Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
dce0fcd
Add debugging guidelines to AGENTS.md and copilot-instructions.md for…
rsmithlal Sep 7, 2025
2256373
Refactor ElasticsearchIndexJob and GeocodingJob to improve error hand…
rsmithlal Sep 7, 2025
1f5be64
Enhance geocoder configuration for development by adding test stubs f…
rsmithlal Sep 7, 2025
5bfd3ee
Add queue configuration for notifications in various notifiers
rsmithlal Sep 7, 2025
76c35b1
Improve alt and title attributes in image helper methods to use entit…
rsmithlal Sep 7, 2025
4377eb9
Increase token column limit for invitations from 24 to 64 characters
rsmithlal Sep 7, 2025
9ade31f
Update platform membership creation to use PersonPlatformMembership m…
rsmithlal Sep 7, 2025
36b2e9e
Redirect unauthenticated users to login page instead of fallback loca…
rsmithlal Sep 7, 2025
4ebd64b
Fix AJAX search parameter name and improve localization strings for b…
rsmithlal Sep 7, 2025
6eba191
Add person search functionality for new event invitations
rsmithlal Sep 7, 2025
4078e65
Implement invitation token authorization and enhance event invitation…
rsmithlal Sep 7, 2025
732bf33
Fix invitation resend logic to correctly check last sent timestamp
rsmithlal Sep 7, 2025
e0ee11d
Enhance invitation handling by processing event invitation tokens and…
rsmithlal Sep 7, 2025
e062049
Refactor community membership check in EventInvitation and add primar…
rsmithlal Sep 7, 2025
3173a6a
Refactor community membership creation to use find_or_create_by for e…
rsmithlal Sep 8, 2025
46636a2
Add enhanced event invitation features and tests
rsmithlal Sep 8, 2025
75f3283
Rubocop fixes
rsmithlal Sep 8, 2025
f7a7839
Rubocop fixes
rsmithlal Sep 8, 2025
508e03a
Update locale files for event invitations and notifications in Englis…
rsmithlal Sep 8, 2025
42388f7
Enhance automatic configuration tests with user authentication checks…
rsmithlal Sep 8, 2025
f0aab73
Reset locale to English after each test to prevent test isolation issues
rsmithlal Sep 8, 2025
6981836
Enhance invitation token authorization by ensuring policy scope is re…
rsmithlal Sep 8, 2025
9989c4d
Fix recently sent invitation logic to correctly check if the invitati…
rsmithlal Sep 8, 2025
efd049f
Update config/locales/en.yml
rsmithlal Sep 8, 2025
2f60e93
Add event invitation flow diagrams and documentation
rsmithlal Sep 8, 2025
bd5553d
Merge branch 'enchance/event-invites' of github.com:better-together-s…
rsmithlal Sep 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ This repository contains the **Better Together Community Engine** (an isolated R

## Coding Guidelines

### Debugging and Development Practices
- **Never use Rails console or runner for debugging** - These commands don't support our test-driven development approach
- **Debug through comprehensive tests**: Write detailed test scenarios to reproduce, understand, and verify fixes for issues
- **Use test-driven debugging workflow**:
- Create specific tests that reproduce the problematic behavior
- Add debugging assertions in tests to verify intermediate state
- Trace through code by reading files and using grep search
- Validate fixes by ensuring tests pass
- **Leverage RSpec debugging tools**: Use `--format documentation` for detailed output, `fit` for focused testing, `puts` for temporary debug output in tests
- **Analyze logs and error messages**: Examine Rails logs, test output, and stack traces for debugging information
- **Read code systematically**: Use file reading tools to understand code paths and data flow
- **Temporary debug output**: Add debug statements in application code if needed, but remove before committing

### Docker Environment Usage
- **All database-dependent commands must use `bin/dc-run`**: This includes tests, generators, and any command that connects to PostgreSQL, Redis, or Elasticsearch
- **Dummy app commands use `bin/dc-run-dummy`**: For Rails commands that need the dummy app context (console, migrations specific to dummy app)
Expand All @@ -102,10 +115,11 @@ This repository contains the **Better Together Community Engine** (an isolated R
- RuboCop: `bin/dc-run bundle exec rubocop`
- **IMPORTANT**: Never use `rspec -v` - this displays version info, not verbose output. Use `--format documentation` for detailed output.
- **Examples of commands requiring `bin/dc-run-dummy`**:
- Rails console: `bin/dc-run-dummy rails console`
- Rails console: `bin/dc-run-dummy rails console` (for administrative tasks only, NOT for debugging)
- Dummy app migrations: `bin/dc-run-dummy rails db:migrate`
- Dummy app database operations: `bin/dc-run-dummy rails db:seed`
- **Commands that don't require bin/dc-run**: File operations, documentation generation (unless database access needed), static analysis tools that don't connect to services
- **CRITICAL**: Rails console and runner are NOT debugging tools in this project - use comprehensive test suites instead

### Security Requirements
- **Run Brakeman before generating code**: `bin/dc-run bundle exec brakeman --quiet --no-pager`
Expand Down
13 changes: 13 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,21 @@ plugins:
- rubocop-rspec_rails
- rubocop-capybara
- rubocop-factory_bot

# Disable all RSpec cops that affect test quality but not production code
RSpec/ExampleLength:
Enabled: false
RSpec/MultipleExpectations:
Enabled: false
RSpec/MultipleMemoizedHelpers:
Enabled: false
RSpec/ContextWording:
Enabled: false
RSpec/MessageSpies:
Enabled: false
RSpec/LetSetup:
Enabled: false

Style/StringLiterals:
Exclude:
- 'db/migrate/*'
Expand Down
13 changes: 12 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ Instructions for GitHub Copilot and other automated contributors working in this
- test: `community_engine_test`
- Use `DATABASE_URL` to connect (overrides fallback host in `config/database.yml`).

## Debugging Guidelines
- **Never use Rails console or runner for debugging** - These commands don't align with our test-driven development approach
- **Use comprehensive test suites instead**: Write detailed tests to understand and verify system behavior
- **Debug through tests**: Create specific test scenarios to reproduce and validate fixes for issues
- **Use log analysis**: Examine Rails logs, test output, and error messages for debugging information
- **Add temporary debugging assertions in tests**: Use `expect()` statements to verify intermediate state in tests
- **Use RSpec debugging tools**: Use `--format documentation` for detailed test output, `fit` for focused testing
- **Trace through code by reading files**: Use file reading and grep search to understand code paths
- **Add debug output in application code temporarily** if needed, but remove before committing
- **Validate fixes through test success**: Confirm that issues are resolved by having tests pass

## Commands
- **Tests:** `bin/dc-run bin/ci`
(Equivalent: `bin/dc-run bash -c "cd spec/dummy && bundle exec rspec"`)
Expand All @@ -29,7 +40,7 @@ Instructions for GitHub Copilot and other automated contributors working in this
- Multiple specific lines: `bin/dc-run bundle exec rspec spec/file1_spec.rb:123 spec/file2_spec.rb:456`
- **Important**: RSpec does NOT support hyphenated line numbers (e.g., `spec/file_spec.rb:123-456` is INVALID)
- **Do NOT use `-v` flag**: The `-v` flag displays RSpec version information, NOT verbose output. Use `--format documentation` for detailed test descriptions.
- **Rails Console:** `bin/dc-run-dummy rails console` (runs console in the dummy app context)
- **Rails Console:** `bin/dc-run-dummy rails console` (for administrative tasks only - NOT for debugging. Use comprehensive tests for debugging instead)
- **Rails Commands in Dummy App:** `bin/dc-run-dummy rails [command]` for any Rails commands that need the dummy app environment
- **Lint:** `bin/dc-run bundle exec rubocop`
- **Security:** `bin/dc-run bundle exec brakeman --quiet --no-pager` and `bin/dc-run bundle exec bundler-audit --update`
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
static targets = ["select", "input"]
static values = {
searchUrl: String,
searchDelay: { type: Number, default: 300 }
}

connect() {
this.setupPersonSearch()
}

setupPersonSearch() {
const select = this.selectTarget

// Convert select to a searchable input
this.createSearchInput(select)

// Hide the original select
select.style.display = 'none'
}

createSearchInput(select) {
const searchContainer = select.parentElement

// Create search input
const searchInput = document.createElement('input')
searchInput.type = 'text'
searchInput.className = 'form-control person-search-input'
searchInput.placeholder = select.options[0]?.text || 'Search for people...'
searchInput.setAttribute('data-person-search-target', 'input')

// Create results dropdown
const resultsDropdown = document.createElement('div')
resultsDropdown.className = 'person-search-results'
resultsDropdown.style.cssText = `
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #ced4da;
border-top: none;
border-radius: 0 0 0.375rem 0.375rem;
max-height: 200px;
overflow-y: auto;
z-index: 1000;
display: none;
`

// Insert elements
searchContainer.style.position = 'relative'
searchContainer.insertBefore(searchInput, select)
searchContainer.appendChild(resultsDropdown)

// Setup event listeners
let searchTimeout
searchInput.addEventListener('input', (e) => {
clearTimeout(searchTimeout)
searchTimeout = setTimeout(() => {
this.performSearch(e.target.value, resultsDropdown, select)
}, this.searchDelayValue)
})

searchInput.addEventListener('focus', () => {
if (searchInput.value) {
this.performSearch(searchInput.value, resultsDropdown, select)
}
})

// Hide dropdown when clicking outside
document.addEventListener('click', (e) => {
if (!searchContainer.contains(e.target)) {
resultsDropdown.style.display = 'none'
}
})
}

async performSearch(query, resultsDropdown, select) {
if (query.length < 2) {
resultsDropdown.style.display = 'none'
return
}

try {
const response = await fetch(`${this.searchUrlValue}?search=${encodeURIComponent(query)}`, {
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
})

if (!response.ok) throw new Error('Search failed')

const people = await response.json()
this.displayResults(people, resultsDropdown, select)
} catch (error) {
console.error('Person search error:', error)
resultsDropdown.innerHTML = '<div class="p-2 text-danger">Search failed</div>'
resultsDropdown.style.display = 'block'
}
}

displayResults(people, resultsDropdown, select) {
if (people.length === 0) {
resultsDropdown.innerHTML = '<div class="p-2 text-muted">No people found</div>'
resultsDropdown.style.display = 'block'
return
}

const resultsHtml = people.map(person => `
<div class="person-result p-2 border-bottom"
style="cursor: pointer; display: flex; align-items: center;"
data-person-id="${person.id}"
data-person-name="${person.name}">
<div class="me-2" style="width: 32px; height: 32px; background-color: #dee2e6; border-radius: 50%; display: flex; align-items: center; justify-content: center;">
<i class="fas fa-user text-muted"></i>
</div>
<div>
<div class="fw-medium">${person.name}</div>
<small class="text-muted">@${person.slug}</small>
</div>
</div>
`).join('')

resultsDropdown.innerHTML = resultsHtml
resultsDropdown.style.display = 'block'

// Add click handlers to results
resultsDropdown.querySelectorAll('.person-result').forEach(result => {
result.addEventListener('click', () => {
this.selectPerson(result, select)
resultsDropdown.style.display = 'none'
})
})
}

selectPerson(resultElement, select) {
const personId = resultElement.dataset.personId
const personName = resultElement.dataset.personName

// Update the hidden select
select.innerHTML = `<option value="${personId}" selected>${personName}</option>`
select.value = personId

// Update the search input
const searchInput = this.inputTarget
searchInput.value = personName

// Trigger change event for form handling
select.dispatchEvent(new Event('change', { bubbles: true }))
}
}
1 change: 1 addition & 0 deletions app/assets/stylesheets/better_together/application.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
@use 'conversations';
@use 'forms';
@use 'image-galleries';
@use 'invitations';
@use 'maps';
@use 'metrics';
@use 'navigation';
Expand Down
5 changes: 5 additions & 0 deletions app/assets/stylesheets/better_together/invitations.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

.profile-image.invitee {
width: 32px;
height: 32px;
}
8 changes: 7 additions & 1 deletion app/controllers/better_together/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,13 @@ def user_not_authorized(exception) # rubocop:todo Metrics/AbcSize, Metrics/Metho
]
else
flash[:error] = message # Use flash for regular redirects
redirect_back(fallback_location: home_page_path)

# For unauthenticated users, redirect to login
if current_user.nil?
redirect_to new_user_session_path(locale: I18n.locale)
else
redirect_back(fallback_location: home_page_path)
end
end
end
# rubocop:enable Metrics/MethodLength
Expand Down
Loading
Loading