38 Commits

Author SHA1 Message Date
skysyaz
1ab7cde015 Block PM self-finalization and preserve approval audit trails.
When program-manager approval is off, PM-owned submits stay pending for admin review; notify admins instead of self, align reject messaging, and keep approval history readable after user deletion.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 21:32:46 +08:00
skysyaz
eba3535ff3 Fix PM own-timesheet access and harden approval transitions.
Include approvers' own rows in timesheet scope, lock submit/approve/reject/revert, abort unauthorized approve, and split set-password throttles so GET refreshes are not locked out.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 20:56:58 +08:00
skysyaz
b133de92fa Harden security fixes and repair traffic/route regressions.
Accept legacy route:list --columns, lock timesheet edits, tighten password throttle and session defaults, and fix site traffic date/unique races without Postgres-only SQL.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 20:52:40 +08:00
b4b7041b8e Harden timesheet authz and close audit/security gaps.
Block weekly-hours IDOR and unauthorized rejects, enforce project membership, stop broadcast reset-token spam, and tighten related hardening plus tests.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 17:51:38 +08:00
skysyaz
5bc545b596
Merge pull request #3 from skysyaz/fix/scheduled-activitylog-clean-force
Fix scheduled activitylog:clean failing in production
2026-07-10 10:22:23 +08:00
0c8d652166 Force scheduled activitylog:clean to skip prod confirm prompt
The activitylog:clean command uses ConfirmableTrait, which in production
blocks on an interactive confirm() prompt. Under cron there is no TTY, so
the prompt resolves to its default (false), the command exits 1, and
ScheduleRunCommand throws a generic Exception on every daily run.

Pass --force so the scheduled task actually runs instead of failing.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-10 10:14:22 +08:00
skysyaz
8f25e9e781 Expand Watchtower reporting with queue and scheduled task hooks.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 22:48:10 +08:00
skysyaz
227fb87731 Report unhandled exceptions to Watchtower for centralized error tracking.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 22:32:32 +08:00
skysyaz
350306f1e3
Merge pull request #2 from skysyaz/gate-future-week-submission
Gate future-week timesheet submission and approval
2026-07-08 21:58:26 +08:00
skysyaz
4cadac4caa Gate future-week timesheet submission and approval
Block submission and approval of timesheets whose week_start Monday has
not arrived yet (a future week). Drafting ahead stays allowed; only the
submit and approve gates close, since hours not yet worked cannot be
verified. The current in-progress week remains submittable.

- Timesheet::isFutureWeek() is the single source of truth
- canUserSubmitTimesheet hides the Submit action for future weeks
- submitTimesheet throws a friendly ValidationException before the 403
- canBeApprovedBy hides Approve/Reject for future weeks
- handleApprove defends with the same ValidationException
- cover with submit + approval gate tests

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-08 21:53:48 +08:00
skysyaz
18aa126cde
Merge pull request #1 from skysyaz/security-audit-fixes
Harden transport/headers and credential handling from security audit
2026-07-08 21:33:17 +08:00
skysyaz
0c807c25b7 Restore unsafe-eval in CSP; revert enforcement to report-only
The standard Alpine build bundled by Livewire/Filament evaluates directive
expressions via new Function, so the admin UI fails to initialize when
'unsafe-eval' is dropped from script-src. Restore it and drop only the
unused cdn.jsdelivr.net origin (still blocks the "load arbitrary script from
a public CDN" vector). Revert csp_enforce to false (report-only) — enforcing
the current policy can't fully stop XSS while unsafe-inline/unsafe-eval
remain, and full no-eval enforcement needs Livewire's CSP-safe build
(livewire.csp_safe=true) + nonces as a separate rollout. Update the CSP test
to the corrected policy string.

Co-authored-by-by: Claude <noreply@anthropic.com>
2026-07-08 21:28:59 +08:00
skysyaz
80d311e746 Document approval-history scope as deliberate business rule
Add a comment to TimesheetAccess::scopeTimesheetsForUser noting that the
approvalLogs clause (approvers keep read access to a user's hours on a
project after acting on any of that user's timesheets) is intended, not a
leak to be tightened to the single approved row.

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-08 21:18:35 +08:00
skysyaz
0ff146e0f7 Harden transport/headers and credential handling from security audit
- CSP: drop 'unsafe-eval' and the unused cdn.jsdelivr.net script origin;
  enforce by default (SECURITY_CSP_ENFORCE, env-overridable for rollout).
  Report-Only stays on so violations surface during transition.
- HSTS: emit when APP_URL is https, not only when the current leg is TLS,
  so it survives a TLS-terminating proxy; add preload.
- Cache headers: guard no-store on auth() + GET + text/html instead of the
  dead admin/* prefix (panel is mounted at root); uniform X-Frame-Options: DENY.
- Activation email: stop mailing the cleartext temporary password; the
  set-password token link is the sole onboarding path.
- Uptime heartbeat: read the token from the X-Uptime-Token header instead
  of the query string (leaks via logs/referer); update docs.
- Project aggregates: select only the hour columns to bound memory.
- Password hashing: standardize on the User model's hashed cast; remove
  redundant Hash::make in the reset action and the user form.
- Tests: add login-throttle test (5 failed -> 6th throttled); update
  CSP/header and uptime/activation tests to the new behavior.

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-08 21:16:22 +08:00
fa04d10d7c Fix settings save when clearing optional overtime daily threshold.
Null values violated the settings.value NOT NULL constraint; clearing now deletes the row instead.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 10:20:48 +08:00
755fee72ae Protect project timesheets on delete and improve View Project team UI.
Soft-delete projects with restore support, prevent timesheet cascade loss, and redesign the team members and contributions panels with matched-height scrollable cards.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-07 11:25:11 +08:00
skysyaz
2502601c08 Backfill existing audit_log.log_name to causer role
One-time portable (SQLite + Postgres) migration relabeling the audit log
badge column for existing rows; new entries already write the acting role.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 22:00:04 +08:00
skysyaz
9201184690 Make audit log_name reflect the acting user's role
The Audit Log page's "Log" badge always read "admin" regardless of who
acted, because both manual AuditLogger::log and the LogsAuditableChanges
trait hardcoded log_name='admin'. Default to Auth::user()'s role (or
'system' for console/jobs) in both, and the backfill command. Update the
status-change test to expect the employee's role.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 21:54:00 +08:00
skysyaz
4568c57148 Fix broadcast modal Set typehint to Filament v5 utility class
afterStateUpdated callbacks received Filament\Schemas\Components\Utilities\Set,
not the non-existent Filament\Forms\Set — selecting a template/reuse preset
threw a TypeError. Use the correct class.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 20:55:32 +08:00
skysyaz
d340e74833 Seed Welcome broadcast template and compact broadcast modal
Add BroadcastTemplateSeeder with Welcome + Password reset reminder
templates, wired into DatabaseSeeder. Make the Broadcast email modal
stick its header and trim the body textarea so the title stays visible.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 20:53:48 +08:00
skysyaz
f967857d76 Add broadcast templates
Reusable subject/body pairs managed via a Broadcast templates admin resource
(Filament CRUD, gated to admins and project_admins). The Broadcast email action
gains a "Use a template" picker that fills the subject and body, so common
messages don't need retyping. Complements the existing "Reuse a previous
broadcast" history picker.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 20:47:27 +08:00
skysyaz
8f39b8e4c7 Record broadcast history and reuse past broadcasts
Each broadcast now stores sender, subject, body, and recipient count in a
broadcast_emails table. The Broadcast email action gains a "Reuse a previous
broadcast" dropdown that auto-fills subject and body from a past broadcast, so
admins can re-blast a message without retyping it.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 20:41:03 +08:00
skysyaz
149bb211e9 Lock email field on set-password page
The email is fixed by the activation token; making it read-only stops users
from changing it and breaking the token match.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 20:35:13 +08:00
skysyaz
8fde4eacde Fix Filament facade namespace in activation notification
Use Filament\Facades\Filament (the app's convention) instead of the
non-existent Illuminate\Support\Facades\Filament, which threw in the
queue worker and silently failed every activation email. Also correct the
Notification facade import in UserNotifier (should be
Illuminate\Support\Facades\Notification) so the non-queued send path works.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 20:25:29 +08:00
skysyaz
d33c5f62ca Add user activation email and broadcast feature
Email new users their login URL and typed password plus a set-your-own-password
link on creation; add a broadcast action to message all visible users with a
per-user activation link, and a per-row resend action. Adds a public
set-password route backed by the Laravel password broker.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-03 20:07:43 +08:00
29770d3afe Make timesheet view flex layout span full page width.
Add columnSpanFull() to the two-column Flex container so the timesheet view uses the full available width instead of leaving unused horizontal space.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 17:41:22 +08:00
8e4ca53533 Reorganize timesheet view into a two-column layout.
Place details, approval history, and notes in the left column and the weekly breakdown in the right column so the view page uses vertical space more effectively.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 17:33:49 +08:00
f2e89acb99 Sync composer.lock with PHP 8.4 platform requirement.
Refresh the lockfile metadata so it matches composer.json after raising the declared runtime to PHP 8.4, avoiding install warnings on staging and production.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 17:20:59 +08:00
ca4013d888 Align pending approval counts and PHP runtime requirement.
Scope dashboard pending counts to each approver's assigned projects, harden overtime validation against incomplete weekly arrays, remove legacy PD backfill labels, and declare the PHP 8.4 runtime required by the app.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 17:18:14 +08:00
1833279731 Show project approvers on My Projects and fix member assignment scoping.
Employees see labeled PM and Program Manager contacts on assigned projects; PMs and program managers can pick employees when staffing projects; weekly-hours print/PDF exports include overtime totals; reports summary cards align consistently.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 16:06:56 +08:00
35462b1d77 Add post-deploy permission fix for tar/rsync deploys on PHP 8.4.
Windows tar archives can leave storage read-only; restore writable storage paths and use route:clear instead of route:cache.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 11:41:38 +08:00
d0f416b32e Include inline OT layout styles so staging works without Vite rebuild.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 11:27:25 +08:00
6535c0b4cb Add overtime to Weekly Hours page and align edit-form OT grid with planner.
Shows per-project overtime rows with live totals on Weekly Hours, and matches the day-strip layout and breakpoints so Friday OT lines up with the planner dates.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 11:25:58 +08:00
8f3d56be98 Fix Reports page crash on PHP 8.4 and exclude assigned project members from picker.
Return updated buckets from TimesheetSummaryBuilder instead of passing by reference, and filter member options to users not already selected in other repeater rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 11:04:24 +08:00
97d159aee8 Add Program Manager rename, Project Admin, project types, and overtime support.
Renames Project Director to Program Manager, introduces scoped Project Admin user management, configurable project types, and separate regular/overtime hour tracking in forms, reports, and PDF exports.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 10:53:41 +08:00
1f18cc29fe Fix site traffic recording after Filament panel moved to root.
Traffic counting still matched admin/* after the panel path changed, so today's page views stayed at zero.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 09:50:12 +08:00
831e7f8826 Improve timesheet workflow, access control, and admin UX.
Allow multiple rows per project per week, auto-approve PM submissions when appropriate, show disambiguated project labels, move the Filament panel to the site root, and expand authorization and test coverage.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-29 17:45:54 +08:00
e90c84970f Initial commit: Quatriz timesheet application.
Laravel 13 + Filament admin panel with weekly hours entry, timesheet approval workflow, project management, reports, and PDF export.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-28 22:22:52 +08:00