Commit Graph

32 Commits

Author SHA1 Message Date
9ce5b45b79 Delete three pockets of dead code, on evidence
1. App\Mail\LoginCodeMail + its Blade view. Login codes go through
   SystemEmail::send('login_code', …), whose TEMPLATES entry carries its own
   plain-text body — the `emails.login-code` view was referenced ONLY from
   LoginCodeMail::content(). Checked all 30 repos: the remaining hits were the
   class itself, two now-unused imports in LoginCodeService, its own render test,
   and one historical plan doc (prose, left).

2. CustomFieldsController::index(). It rendered 'Admin/Tables', and **no
   Tables.vue exists anywhere in the workspace** — so it would have blank-screened
   even if something routed to it, and nothing does. The live editor is
   Admin → User Settings → Fields (UserSettingsController::index builds a strict
   superset of that payload), and /admin/settings/tables already redirects there.
   All four registered actions (store/update/destroy/reorder) still route and were
   re-verified by name after the change. A docblock now records where the UI lives
   so the method does not get re-added.

3. Thirteen stale Vue duplicates in dashboard-expenses and dashboard-ticketing.
   Those pages are served FROM THE SHELL — app.js resolves ./Pages/** before any
   packages/* glob, Ticketing was never globbed at all, and the built manifest
   carried the packages/expenses copies only as shadowed dead weight. All 13 were
   diffed against the shell before deletion: every one is the April original plus
   the July full-width sweep. Two were not merely stale but BROKEN —
   Expenses/Budgets called route('expenses.budgets.index'), a name that does not
   exist, and BudgetEdit still used fiscal_year after the controller moved to
   schoolYears. One genuine difference is noted rather than lost: the snap-in's
   Ticketing/Create gated its "Go to Settings" link on isSiteAdmin while the
   shell's shows it unconditionally, so a non-admin sees a link that 403s — a
   pre-existing shell behaviour (Index.vue does the same), not caused by this.

   This matters because config/inertia.php now registers snap-in page roots for
   tests: a page added ONLY to a snap-in copy would have PASSED tests while 404ing
   in a browser. resources/js/app.js drops the dangling packages/expenses glob,
   with a comment tying its list to config/inertia.php so the two cannot drift.

Bonus: the same trap existed in dashboard-unifi, whose resources/js/Pages was an
empty untracked scaffold that app.js never globbed. Removed. The glob now returns
exactly the four roots app.js resolves — admissions, forms, tuition, verification.

Tests: 83 (was 84; -1 is the deleted mailable render test), doctor exit 0, and a
browser pass over all seven Expenses/Ticketing pages plus /admin/user-settings/fields
and /admin/settings/tables — 200, mounted, zero console errors. /app/ticketing/create
renders the attachments input, proving the shell copy is what serves.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 19:05:30 -04:00
0be06694b2 Read the attachment disk from config, not env (secrets S3)
The three reads were `config('ticketing.storage_disk', env('TICKETING_STORAGE_DISK', 'local'))`,
but this package had no config file at all — so the config() lookup was always
null and env() was the only reader.

Once `config:cache` runs in production, a runtime env() returns NULL. Every
ticket attachment would have silently gone to the 'local' disk, ignoring
whatever the operator had configured — and previously-stored attachments would
404 because show() looked on the wrong disk.

Verified rather than assumed: with TICKETING_STORAGE_DISK=s3proof set,
config() returned 's3proof' both cached and uncached, while env() returned
's3proof' uncached and NULL cached.

Adds config/ticketing.php + mergeConfigFrom so the value is captured at
cache time, and drops the env() fallbacks.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-26 14:00:15 -04:00
Claude
a54c01453d Declare tier/stability in the manifest
Ship-dark and the dashboard:doctor deploy gate only protect apps that DECLARE
what they are; this app was falling through to defaults. Pure metadata, additive
only, no version bump (repo convention: manifest-only changes do not bump).

bosch is BETA deliberately: production last deployed 2026-07-16 and this app was
built on the 19th, so the next prod deploy is its FIRST install — it talks to a
real alarm panel and its supervised cutover day has not happened. Ship-dark means
it lands disabled until an operator turns it on. signinout is beta for the same
reason (planned, scaffold only). Everything long-live in prod is stable, because
wrongly tagging a live app beta could dark it, while stable never disables
anything.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-25 19:26:05 -04:00
dcfbf20ab0 UI: full-width pages (shell standard 2026-07-24)
Page wrappers drop their max-w cap in line with the shell's style guide: the
page uses the whole content area. Single-column create/edit forms keep a capped
column (w-full max-w-2xl) so their inputs stay readable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 19:42:55 -04:00
e49a1e2ac9 Ticketing: settings tabs are deep-linkable routes (/settings/{tab})
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Jh8RnYXrC8E6z79LWs8ggd
pre-lxc-migration-2026-08-09
2026-06-21 20:19:04 -04:00
Joel Wedemire
dd0a458250 feat: ticket views, statuses, participants, merge; mobile layout fixes (#5)
- New migrations: ticket views, statuses, participants, merge support
- New models: TicketView, TicketStatus, TicketParticipant
- New seeder: EmailTemplatesSeeder
- Console commands for ticketing
- Mobile: sidebar min-w-0/overflow-hidden, tab nav overflow-x-auto

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 22:22:45 -07:00
Joel Wedemire
ffb64078d8 fix(mobile): add min-w-0/overflow-hidden to sidebar panes, truncate header text
Closes dashboard-ticketing #5: sidebar and group switcher overflowed on 375px viewport.
Adds min-w-0 to aside and inner content div, truncates header labels. Settings tab nav
already had overflow-x-auto with shrink-0 tabs; no additional changes needed there.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-19 22:12:49 -07:00
Joel Wedemire
9527147c32 fix: replace role check with permission check in TicketingSettingsController
isSiteAdmin() used the dropped role field. Now checks is_super_admin
or ticketing.settings permission. Closes #2.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:33:47 -07:00
Joel Wedemire
1bc34ff16f fix: move /settings route before /{ticket} wildcard; fix null tickets on bootstrap
Route ordering caused /settings to be captured by the /{ticket} wildcard,
returning 404. Also pass safe empty pagination object when no groups exist
to prevent TypeError reading .total on null. Closes #2 #3.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:31:50 -07:00
Joel Wedemire
a66f61c638 feat: update to new dashboard permissions format
Replace flat nav[] with nav_folder/pages/permissions structure.
Update routes to use app.access + permission: middleware groups.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 21:22:27 -07:00
Joel Wedemire
bce98c0d4b fix(ticketing): restrict settings link to admins and protect global priorities
- Show 'Go to Settings' bootstrap link only for admin/super_admin users
- Pass isSiteAdmin prop to Create.vue to control settings CTA visibility
- Require site admin for updatePriority/destroyPriority when priority is global (group_id = null)
- Closes: non-admin users seeing forbidden settings link; agents mutating global priorities
2026-04-09 14:32:19 -07:00
Joel Wedemire
45b019dad2 feat(ticketing): add version and dashboard nav config to composer.json 2026-04-09 14:18:36 -07:00
Joel Wedemire
a32f6590f4 fix(ui): patch responsive and tailwind bugs 2026-04-09 10:40:23 -07:00
Joel Wedemire
5ac4a3e771 fix(ticketing): resolve 500 error on ticket creation
nextTicketNumber() was using count()+1 which collides when tickets
have been deleted. Now queries MAX number via SUBSTRING ordering so
the generated ticket number is always unique.
2026-04-09 09:30:50 -07:00
Joel Wedemire
54aff1ee1b feat(ticketing): implement mobile-responsive inbox UX 2026-04-09 08:44:29 -07:00
Joel Wedemire
f1d161793d Add email connections to ticketing settings 2026-04-08 22:01:50 -07:00
Joel Wedemire
24dae5837d Accept attachments on ticket create 2026-04-08 21:58:33 -07:00
Joel Wedemire
4f65724f90 Allow site admins into ticketing settings 2026-04-08 21:56:17 -07:00
Joel Wedemire
5ffb9f1eed Reset ticketing demo data before seeding 2026-04-08 21:34:30 -07:00
Joel Wedemire
28a18a9cad Move ticketing demo seeder into src 2026-04-08 21:10:30 -07:00
Joel Wedemire
5a695ce9ee Autoload ticketing seeders 2026-04-08 21:09:56 -07:00
Joel Wedemire
13cff98c67 Fix ticketing service provider 2026-04-08 21:04:53 -07:00
Joel Wedemire
990d1dbb1b Add ticketing demo seeder 2026-04-08 20:39:53 -07:00
Joel Wedemire
f2ca83d2a7 Add ticketing project settings 2026-04-08 20:05:47 -07:00
Joel Wedemire
3c65f9a4fd Improve ticketing settings 2026-04-08 19:48:32 -07:00
Joel Wedemire
f2b614abb7 fix: scope create form priorities to selected group; reset on group change 2026-04-08 19:06:38 -07:00
Joel Wedemire
652829ab90 fix: bootstrap blocker + 4 security bugs
- Bootstrap (critical): settings/create/index no longer 403 on fresh install.
  Site admins (admin/super_admin) can access settings when 0 groups exist.
  First group creation seeds default priorities (Low/Medium/High/Urgent).
  Index shows friendly first-run splash. Create shows warning + settings link.

- Internal notes leak (high): submitters can no longer receive is_internal
  messages via ticket show, index detail panel, or any Inertia prop.
  filterMessagesForRole() strips internal notes for non-agents.

- Arbitrary assignee (med/high): update() now validates assigned_to against
  actual agent-access users for the ticket's group server-side.

- Cross-group priority/project forgery (medium): store() and update() now
  verify priority_id and project_id belong to the ticket's own group (or
  are global for priorities).

- Foreign message_id on attachment upload (medium): message_id is now
  validated to belong to the current ticket, not just any message row.
2026-04-08 18:31:51 -07:00
Joel Wedemire
615c091f88 Build hybrid ticketing inbox 2026-04-08 17:46:38 -07:00
Joel Wedemire
aa6177cf62 fix: orWhereIn typo; broaden illuminate/support and inertia-laravel version constraints 2026-04-08 17:16:29 -07:00
Joel Wedemire
391699220f feat: full dashboard-ticketing scaffold with data model, controllers, Vue pages 2026-04-08 17:10:30 -07:00
Joel Wedemire
81d0d54f50 feat: initial dashboard-ticketing scaffold 2026-04-08 14:17:26 -07:00
Joel Wedemire
1448eb7cf4 initial commit 2026-04-08 14:12:03 -07:00