4.7 KiB
Next Steps
Items that were out of scope for the initial 6-hour build, with notes on how to approach each.
Features
User creation / deletion UI
Users are seeded via Flyway (V2__seed_demo_data.sql) and the selected user's name/default rate can
be edited from the top bar. A POST /api/users endpoint with a "New User" form, plus optional delete
support, would complete the CRUD cycle. Straightforward to add following the same pattern as project
creation.
Time entry edit smoke coverage
Editing existing time entries is implemented end-to-end (PUT /api/time-entries/{id}, TimeEntryForm
edit mode, and the WeeklyView Edit button). Add explicit frontend and/or MockMvc smoke coverage for
the edit path so regressions are caught.
Notes / description field on time entries
Add a TEXT column to time_entries (new Flyway migration), expose it in the DTOs, and add a
textarea to TimeEntryForm. Straightforward schema change.
Rate history — audit-stable costs
Currently, cost is computed at read time from the current project/user hourly rate. If a rate
changes, historical costs change retroactively. To fix: add hourly_rate_cents_snapshot INTEGER NOT NULL
to time_entries, resolve and snapshot the rate in TimeEntryService.create(). Historical queries
then use the snapshot instead of joining to current rates.
CSV export of reports
Add a GET /api/reports/weekly.csv?userId=&week= and /monthly.csv endpoint that streams
text/csv. Frontend adds a "Download CSV" button that opens the URL in a new tab.
Technical quality
Full backend test pyramid
The current tests cover the repository date-range query, service cost/budget logic, one end-to-end MockMvc flow, and a few validation cases for user/project updates. Gaps to fill:
- Dedicated
@WebMvcTestcontroller-slice tests for validation error responses (400 on invalid fields). - More
@DataJpaTestcases (zero entries, multiple users, rate fallback). @SpringBootTestwith Testcontainers so tests do not depend on an externally-running PostgreSQL.
Frontend end-to-end tests (Playwright)
A Playwright suite covering the golden paths:
- Log a time entry → verify it appears in the weekly view.
- Create a project → verify it appears in the project list.
- Exceed a project budget → verify the budget banner appears.
Pagination of time entries
GET /api/time-entries returns all entries in a date range. For large datasets, add Spring Data
pagination (Pageable parameter) and a "Load more" button or cursor in the frontend.
UX improvements
Delete confirmation modal
Currently confirm() is used for delete. Replace with a small Tailwind dialog / Headless UI
Dialog for a consistent UX.
Bookmarkable week/month URLs
Weekly and monthly views use component state for the selected period. Moving the period to the
URL (e.g., /weekly/2026-W20, /monthly/2026-05) enables bookmarking and browser back/forward.
React Router v7 useParams + useNavigate is the implementation path.
Validation error UI polish
Form errors are shown as a banner above the form. Inline per-field errors (below each input) would be more discoverable.
Architecture
Authentication & per-user data isolation
No auth in the current build — user is selected via a dropdown. For production, add Spring
Security with JWT or session-based auth. The user_id on time_entries already supports
per-user isolation; the API just needs to enforce it from the authenticated principal.
Per-project currency
Add currency CHAR(3) NOT NULL DEFAULT 'EUR' to projects. The aggregation queries then need
currency-aware math; cross-currency totals on the monthly report would require a conversion rate
or report per-currency totals separately.
Time zone awareness
time_entries.entry_date is a LocalDate (no zone). If users span time zones, "today" must
be resolved from the user's local zone rather than the server's. Pass a timeZone parameter or
derive it from the authenticated user's profile.
Dark mode
Tailwind v4 supports prefers-color-scheme media queries via the dark: variant. Add
@media (prefers-color-scheme: dark) styles or a toggle stored in localStorage.
i18n (de/en)
Hardcoded strings are in English; currency formatting uses German locale (de-DE). Introduce
react-intl or a similar library and extract string constants. Language preference lives in user
settings or Accept-Language.
Performance
- N+1 in
ProjectService.findAll(): one aggregate query for all projects avoids it (already done viaaggregateAllByProject). No further action needed at small scale. - Large datasets: consider materialized views or a dedicated reporting aggregate table updated via a database trigger or scheduled job.