Commit graph

89 commits

Author SHA1 Message Date
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
1cd60b4bbc
Fix: Load booking constants from shared/constants.json
Created shared_constants.py module that loads constants from the
shared JSON file. Updated availability.py and booking.py to import
from this module instead of hardcoding values.

This ensures backend and frontend stay in sync with the same source
of truth for booking configuration.
2025-12-21 17:29:39 +01:00