Commit graph

90 commits

Author SHA1 Message Date
18284c5e63
Use explicit join in random-jobs endpoint to avoid potential N+1 query
- Changed from using scalars().all() with lazy='joined' relationship
- Now uses explicit join similar to other audit endpoints
- Guarantees single query regardless of SQLAlchemy async behavior
2025-12-21 23:14:08 +01:00
6b572aa81b
Use connection pool for job enqueueing instead of per-request
- Added get_job_pool() for lazy pool initialization
- Added close_job_pool() for graceful shutdown
- Hooked pool shutdown into FastAPI lifespan
- Reuses connections instead of creating new ones per enqueue
2025-12-21 23:13:22 +01:00
7ec987c78d
Phase 6: E2E Test
- Update scripts/e2e.sh to start worker alongside backend
- Create frontend/e2e/random-jobs.spec.ts with 3 tests:
  - Counter increment creates random job outcome visible to admin
  - Admin can view empty random jobs list
  - Regular user cannot access random jobs page
2025-12-21 23:00:54 +01:00
b8470b77a7
Phase 5: Frontend Admin Page
- Create /admin/random-jobs/page.tsx with outcomes table
- Add 'admin-random-jobs' to PageId type in Header
- Add 'Random Jobs' nav item to ADMIN_NAV_ITEMS
- Display: ID, Job ID, Triggered By, Value, Duration, Status, Created At
- Uses VIEW_AUDIT permission
2025-12-21 22:55:56 +01:00
b3ed81e8fd
Phase 4: API Endpoint
- Add RandomNumberOutcomeResponse schema to schemas.py
- Add GET /api/audit/random-jobs endpoint to routes/audit.py
- Returns list of outcomes (newest first, no pagination)
- Requires VIEW_AUDIT permission
- Add tests: admin can access, regular user forbidden, unauthenticated 401
2025-12-21 22:53:54 +01:00
7beb213cf5
Phase 3: Outcome storage
- Add RandomNumberOutcome model to models.py
- Update worker.py to execute job logic:
  - Generate random number 0-100
  - Record execution duration
  - Store outcome in database
- Add test_jobs.py with unit tests for job handler logic
2025-12-21 22:50:35 +01:00
6ca0ae88dd
Phase 2: Job enqueueing from counter
- Add backend/jobs.py with enqueue_random_number_job function
- Modify counter increment endpoint to enqueue job after incrementing
- Add mock_enqueue_job fixture to conftest.py for all tests
- Add test_increment_enqueues_job_with_user_id to verify correct user_id
- Job is enqueued synchronously; failure causes request to fail
2025-12-21 22:44:31 +01:00
10c0316603
Phase 1: Add pgqueuer infrastructure
- Add pgqueuer dependency to pyproject.toml
- Create worker.py with schema installation and job handler registration
- Add make worker command to Makefile
- Update make dev to run worker alongside backend/frontend
- Use has_table() check for idempotent schema installation
- Register 'random_number' job handler (placeholder that logs for now)
2025-12-21 22:37:04 +01:00
15bae15731
Phase 1: Add pgqueuer infrastructure and worker skeleton
- Add pgqueuer dependency to pyproject.toml
- Create worker.py with basic setup:
  - Independent database connection using asyncpg
  - Install pgqueuer schema on startup
  - Register dummy job handler
  - Start consumer loop
- Add 'make worker' command
- Update 'make dev' to run worker alongside backend/frontend

Validation:
- Worker starts successfully
- pgqueuer tables exist in database
- All existing tests pass
2025-12-21 22:25:37 +01:00
607f872c71
fix pre-commit hook and code quality fixes 2025-12-21 22:14:48 +01:00
d2fc802ef9
code quality in place 2025-12-21 22:12:43 +01:00
d55382d16f
Add pre-commit framework
- Install pre-commit
- Configure .pre-commit-config.yaml with:
  - ruff (lint + format) for Python
  - bandit for Python security
  - eslint for TypeScript
  - prettier for TypeScript
  - shared constants validation
- Add Makefile targets: pre-commit, lint
2025-12-21 22:01:38 +01:00
521848217d
Add Vitest coverage for frontend tests
- Install @vitest/coverage-v8
- Configure coverage in vitest.config.ts
- Add npm script: test:coverage
- Add coverage/ to gitignore
2025-12-21 22:00:47 +01:00
37de6f70e0
Add Prettier for TypeScript formatting
- Install prettier
- Configure .prettierrc.json and .prettierignore
- Add npm scripts: format, format:check
- Add Makefile target: format-frontend
- Format all frontend files
2025-12-21 21:59:26 +01:00
4b394b0698
Add ESLint for TypeScript/React linting
- Install eslint, typescript-eslint, eslint-plugin-react-hooks
- Configure eslint.config.js with flat config format
- Add type: module to package.json
- Fix unused variable issues (prefix with underscore)
- Add Makefile targets: lint-frontend, fix-frontend
2025-12-21 21:58:41 +01:00
30583805cd
Add bandit for Python security linting
- Add bandit as dev dependency
- Configure in pyproject.toml (exclude venv/tests)
- Skip B101 (assert) and B311 (random for non-crypto)
- Add Makefile target: security-backend
2025-12-21 21:56:46 +01:00
6a2d7155cb
Add pytest-cov for test coverage
- Add pytest-cov as dev dependency
- Configure coverage in pyproject.toml
- Exclude tests, __pycache__, seed.py from coverage
- Enable branch coverage
- Add .coverage to gitignore
2025-12-21 21:55:19 +01:00
6c218130e9
Add ruff linter/formatter for Python
- Add ruff as dev dependency
- Configure ruff in pyproject.toml with strict 88-char line limit
- Ignore B008 (FastAPI Depends pattern is standard)
- Allow longer lines in tests for readability
- Fix all lint issues in source files
- Add Makefile targets: lint-backend, format-backend, fix-backend
2025-12-21 21:54:26 +01:00
69bc8413e0
remove 2025-12-21 21:39:48 +01:00
621e12cdb5
fix tests 2025-12-21 21:35:35 +01:00
bf1e42a498
fixes 2025-12-21 19:04:38 +01:00
f81e7a88bd
Fix React CSS property conflict in availability page
- Replaced borderColor with full border property in dayCardActive
- Replaced borderColor with full border property in dayCardSource
- Replaced borderColor with full border property in dayCardTarget
- Prevents React warning about mixing shorthand and non-shorthand properties
- Fixes console error when using 'Clear all' button on availability page
2025-12-21 18:23:31 +01:00
74ed6f0c11
Revert to local timezone display for user-facing times
- Changed formatTime back to use toLocaleTimeString (local time)
- Changed formatDateTime back to use toLocaleString (local time)
- App now displays all times in user's local timezone
- Backend still stores times in UTC, frontend converts for display
2025-12-21 18:20:22 +01:00
a5b941e748
Fix timezone issue in formatDateTime for appointments page
- Changed formatDateTime to use UTC methods instead of toLocaleString
- Prevents timezone conversion when displaying appointment times
- Now booking at 11:45 shows as 11:45 in appointments page, not 12:45
- Consistent with formatTime which already uses UTC
- Manual formatting to match previous format: 'Mon, Jan 15, 11:45'
2025-12-21 18:18:40 +01:00
b900b52b3c
Fix infinite loop in booking page availability fetching
- Memoized dates array using useMemo to prevent recreation on every render
- Removed dates from useEffect dependency array since it's now stable
- Prevents cascade of API requests when opening booking page
- Dates only recalculate when minAdvanceDays or maxAdvanceDays change
2025-12-21 18:15:57 +01:00
63f40433cc
Disable dates with no availability on booking page
- Fetch availability for all dates on page load
- Track which dates have available slots in state
- Disable date buttons that have no availability
- Add visual styling for disabled dates (reduced opacity, not-allowed cursor)
- Prevent clicking on dates with no availability
- Improves UX by showing which dates are bookable at a glance
2025-12-21 18:13:12 +01:00
7926e3ae4c
Fix timezone issue: display booking slots in UTC
- Changed formatTime() to use UTC methods instead of toLocaleTimeString()
- Prevents timezone conversion when displaying booking slots
- Now admin sets 9-17 and user sees 9-17, regardless of timezone
- Fixes 1-hour offset issue when user timezone differs from UTC
2025-12-21 18:10:15 +01:00
38b5f2a0a7
Fix React CSS property conflict: use border instead of borderColor
- Replaced borderColor with full border property in dateButtonSelected
- Replaced borderColor with full border property in slotButtonSelected
- Prevents React warning about mixing shorthand and non-shorthand properties
- Fixes console error when clicking elements on booking page
2025-12-21 18:08:49 +01:00
46c3c2073a
Fix test assertion for updated error message
- Updated test to match new descriptive error message format
- Changed from 'not within available' to 'not within any available time ranges'
- All tests now passing
2025-12-21 18:06:50 +01:00
b9f605d7b3
Extract common errorBanner style to shared styles
- Added errorBanner to sharedStyles for consistent error display
- Removed duplicate errorBanner definitions from all pages
- Updated appointments, booking, admin/appointments, and admin/availability pages
- Reduced code duplication while maintaining component-specific styles
2025-12-21 18:01:30 +01:00
3d83472b97
Extract duplicate test helper functions to shared module
- Created frontend/e2e/helpers/auth.ts with shared auth utilities
- Extracted getRequiredEnv, REGULAR_USER, ADMIN_USER, clearAuth, loginUser
- Updated all three e2e test files to use shared helpers
- Reduced code duplication across test files
2025-12-21 17:59:48 +01:00
3369a71271
Improve availability error messages with date context
- Added date to slot overlap error message
- Added date to invalid time range error message
- Makes errors more actionable for users
2025-12-21 17:59:18 +01:00
131477b7f3
Make error messages more descriptive
- Added specific slot time and date to availability error message
- Added appointment ID and context to 'not found' errors
- Added formatted appointment time to past appointment cancellation errors
- Added date context to slot overlap error messages
- All errors now provide actionable information to users
2025-12-21 17:59:08 +01:00
4d5673f181
Standardize timezone usage to timezone.utc
- Replaced all UTC imports with timezone imports
- Changed all datetime.now(UTC) to datetime.now(timezone.utc)
- Consistent with booking.py and more explicit about timezone usage
- Updated models.py, routes/auth.py, and routes/invites.py
2025-12-21 17:58:43 +01:00
1a478f7583
Make copy operation atomic with explicit transaction handling
- Wrapped copy operation in try/except with explicit rollback
- Added comments explaining atomicity
- Ensures all-or-nothing behavior for copying to multiple dates
2025-12-21 17:57:42 +01:00
c24597edb4
Be explicit about eager loading in queries
- Added explicit joinedload(Appointment.user) to admin appointment queries
- Makes the eager loading intention clear and explicit
- Replaced comment-based documentation with actual query options
2025-12-21 17:57:23 +01:00
64eeaadd28
Extract weekend detection logic to shared utility
- Created isWeekend() function in utils/date.ts
- Replaced inline weekend detection logic in availability page
- More reusable and testable
2025-12-21 17:57:06 +01:00
1497a81cd5
Add backend validation for note length using constant
- Updated BookingRequest validator to use NOTE_MAX_LENGTH constant
- Replaced hardcoded 144 with constant for consistency
- Error message now includes the actual max length value
2025-12-21 17:56:38 +01:00
3c13270bc0
Standardize time formatting and avoid string slicing
- Created formatTimeString() utility to properly parse time strings
- Replaced all .slice(0, 5) calls with proper time formatting
- Handles both 'HH:MM:SS' and 'HH:MM' formats safely
- More robust than string slicing
2025-12-21 17:56:20 +01:00
a25e84e197
Fix copy button symbol to use more standard Unicode character
- Replaced ⎘ (U+2398) with 📋 (clipboard emoji) for better cross-platform compatibility
- More recognizable and widely supported symbol
2025-12-21 17:55:06 +01:00
5c396e62ec
Extract duplicate date range helpers to shared utility
- Created getDateRange() function in utils/date.ts
- Replaced getBookableDates() and getDateRange() duplicates
- Both booking and availability pages now use shared function
- Function accepts minAdvanceDays and maxAdvanceDays as parameters
2025-12-21 17:54:49 +01:00
208278bddb
Use MIN_ADVANCE_DAYS constant globally instead of hardcoded value
- Updated availability.py to use MIN_ADVANCE_DAYS constant instead of hardcoded timedelta(days=1)
- Ensures consistency between booking and availability date ranges
- Both now use the same constant from shared_constants
2025-12-21 17:53:47 +01:00
a14405a998
Derive slot validation from config instead of hardcoded values
- Created _get_valid_minute_boundaries() helper that derives valid minutes from SLOT_DURATION_MINUTES
- Replaced hardcoded (0, 15, 30, 45) with dynamic calculation
- Error message now includes valid minute values for better clarity
2025-12-21 17:53:35 +01:00
40b193238e
Move TIME_OPTIONS outside component
- TIME_OPTIONS is now computed at module level, not inside component
- generateTimeOptions now accepts slotDurationMinutes as parameter
- Prevents unnecessary recomputation on every render
2025-12-21 17:53:17 +01:00
02212ba478
Make error handling consistent across frontend
- Standardized all error handling to use ternary pattern
- Changed if/else blocks to ternary operators for consistency
- Updated booking, appointments, and admin appointments pages
2025-12-21 17:50:52 +01:00
8cc2cfa2e4
Extract duplicate status display logic to shared utility
- Created frontend/app/utils/appointment.ts with getStatusDisplay()
- Supports context-aware text (isOwnView parameter)
- Updated both appointments pages to use shared utility
2025-12-21 17:50:24 +01:00
d24acfd322
Extract duplicate AppointmentResponse construction to helper
- Created _to_appointment_response() helper function
- Replaced 5 duplicate AppointmentResponse constructions with helper calls
- Helper handles both explicit user_email and eager-loaded user relationship cases
2025-12-21 17:49:37 +01:00
6ff3c0a133
Extract duplicate date formatting functions to shared utilities
- Created frontend/app/utils/date.ts with formatDate, formatTime, formatDateTime, formatDisplayDate
- Created frontend/e2e/helpers/date.ts with formatDateLocal, getTomorrowDateStr
- Updated all frontend pages and e2e tests to use shared utilities
- Removed duplicate date formatting code from 6 files
2025-12-21 17:48:17 +01:00
eefdfd714f
Fix: useCallback dependency array in availability page
Compute dates inside fetchAvailability callback to avoid
dependency on external variable that changes on every render.
2025-12-21 17:32:55 +01:00
77e7f98e1e
Fix: Add pagination to admin appointments endpoint
- Added pagination with page/per_page query params
- Fixed N+1 query by using eager-loaded user relationship
- Removed unused _get_user_email helper function
- Updated frontend to handle paginated response
- Regenerated API types
2025-12-21 17:32:25 +01:00