- NotificationHelper::notifyWatchers: excludeUserId parameter was
accepted but never used; actors were notified of their own actions.
Fix: add AND tw.user_id != ? clause to watcher query when exclusion
is requested.
- TicketView.php: formatAction() default case returned raw
$event['action_type'] unescaped into HTML context. Fix: wrap with
htmlspecialchars().
- Admin views: field_id, recurring_id, template_id, transition_id
in data-id attributes were uncast; field_type was unescaped in
CustomFieldsView; from/to_status slugs derived from DB values were
used directly in class attributes in WorkflowDesignerView.
Fix: (int) cast for IDs, htmlspecialchars for field_type,
preg_replace to sanitize DB-derived CSS class slugs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- TicketView.php: Show 3 lt-skeleton-card placeholders in the comment list
while "Load more" fetches; skeletons are removed on resolve or error
- ticket.css: Add .comment-skeleton margin spacing
- WorkflowDesignerView.php + manage_workflows.php: Prevent creating/editing
status transitions where from_status === to_status (client + server check)
- RecurringTicketsView.php: Expand monthly day picker from 28 to 31 days
(days 29-31 labelled "last day in short months")
- RecurringTicketModel.php: Clamp monthly schedule day to last day of target
month using format('t') instead of hard-capping at 28
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace style="text-align:center" with .lt-text-center utility class in
WorkflowDesignerView, CustomFieldsView, error_403, error_404, DashboardView JS string
- Replace style="margin-top:..." with .lt-mt-sm utility in WorkflowDesignerView
- Switch comment-edit-raw data-store textareas to .is-hidden class (TicketView PHP
+ JS-rendered; ticket.js template literal) — these are never shown, only read via .value
- Add aria-describedby="visibilityGroupsHint" + id on hint <p> in CreateTicketView
- Fix bind_param type string bug in manage_workflows.php PUT handler: 'ssiiiii' → 'ssiiii'
(7 type chars for 6 params caused binding error on workflow transition updates)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- updateTicketField() now targets .lt-frame-ticket[data-priority] (TDS v1.2)
instead of old .priority-indicator / .ticket-container selectors
- All 7 admin views: keyboard-shortcuts.js now uses dynamic ?v={$_v}
instead of hardcoded unversioned path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- watch_ticket.php GET now returns watcher list (up to 6 users) for avatar group
- TicketView: watcher avatar group rendered next to WATCH button, refreshes on toggle
- Rewrite renderDependencies/renderDependents to use TDS lt-kv-grid/lt-badge/lt-btn classes
- renderDependencies: show lt-alert--warning blocker banner when blocked_by has open tickets
- Fix ALL hardcoded ?v=20260327 asset version strings in CreateTicketView + all admin views
- base.css: fix .lt-nav-dropdown-menu hardcoded background → var(--bg-overlay)
- base.css: add light-theme overrides for nav dropdown menu (background, links, hover)
- ticket.css: add .lt-avatar-group and .lt-avatar--overflow styles for watcher display
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Use white-space:pre-wrap on description view div so newlines and multiple
spaces are preserved natively — no <br> replacement, ASCII art aligns
correctly since body is already monospace (JetBrains Mono).
Override opacity:1 on readonly API key input so generated keys are fully
readable instead of being faded to 0.45 by base.css [readonly] rule.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
P1-A: Fix CSP - add fonts.googleapis.com to style-src, fonts.gstatic.com to font-src
P1-B: CSRF token rotation - add rotateToken() to CsrfMiddleware; bootstrap.php rotates
after successful validation and stores in $GLOBALS['_new_csrf_token']; add
apiRespond() helper to append token to responses; lt.api interceptor in
layout_footer.php auto-updates window.CSRF_TOKEN from responses
P1-C: Styled 403/404 error views with TDS layout instead of raw text; index.php now
uses requireAdmin() helper eliminating 7 duplicated guard blocks (P3-D)
P2-A: Remove duplicate JS-generated keyboard help modal from keyboard-shortcuts.js;
'?' key now routes to static #lt-keys-help modal in footer
P2-B: Asset versioning driven by config ASSET_VERSION key; base.css and base.js get
?v= cache-busting in layout_header.php
P2-C: Add data-theme="dark" to <html> tag to prevent FOUC on light-mode users
P2-E: Escape status value in dashboard.js hover preview class attribute via lt.escHtml()
P2-F: Replace bespoke showLoadingOverlay() with lt-spinner / lt-loading-text from
base.css; add .lt-loading-overlay wrapper CSS to dashboard.css
P2-G: Add keyboard-shortcuts.js to all 7 admin views so J/K nav and ? help work
P3-A: APP_NAME, APP_SUBTITLE, APP_VERSION driven from config.php; layout header/footer
use config values instead of hardcoded strings
P3-G: Replace custom initTableSorting() with lt.sortTable.init() which manages aria-sort
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CSS:
- ticket.css: use combined .comment.thread-depth-N selectors to resolve the
margin-left conflict between .comment-reply and .thread-depth-N classes
dashboard.js:
- Remove legacy initStatusFilter() (superseded by TDS v1.2 sidebar filters)
- Remove initTableSorting() call (client-side sort conflicts with server ?sort=)
- Remove quickSave() + saveTicket() (old hamburger-menu ticket page functions)
- Remove global loadTemplate() (duplicate of IIFE-scoped version in CreateTicketView)
- Remove generateSkeletonRows/Comments/Stats helpers (never called, used
unregistered CSS class names like .skeleton-row-tr)
- Remove "force dark mode" lines that overrode the user theme preference
- Fix non-TDS CSS classes in modal templates: text-center → style, text-green →
lt-text-cyan, mb-half → lt-mb-xs, modal-warning-text → lt-text-danger
Admin views:
- RecurringTicketsView: replace innerHTML += loop with createElement/appendChild
(avoids serial DOM re-parsing on each iteration)
- AuditLogView: add htmlspecialchars() to action_type option values (consistency)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- base.css: add --lt-border/--lt-surface aliases so dashboard.css respects
theme instead of using hardcoded fallback colors
- base.css: add lt-select-sm/lt-input-sm compact size variants (used in 15+
places), lt-msg-danger alias for lt-msg-error, lt-form-hint--warn,
lt-font-mono utility class
- audit_log.php: cap ?limit= at 500 to prevent DoS via oversized queries
- ApiKeysView.php: replace deprecated execCommand('copy') with lt.copy();
add integer casts on api_key_id in id attr and data-id
- AuditLogView.php: rebuild pagination with windowed prev/next/ellipsis
pattern matching DashboardView; integer cast on user_id select option
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- base.css: add width:100%+min-width:0 to .lt-main so flex column body
doesn't shrink content due to margin:0 auto from .lt-container
- layout_header.php: restructure mobile nav drawer to match web_template
exactly (nav-drawer-links nav, direct <a> links, section div, no ul/li
wrapper, overlay after drawer); fix lt-nav-overlay id mismatch with
base.js; rename lt-header-username -> lt-header-user (matches CSS);
add JSON_HEX_TAG to all inline json_encode calls (closes </script> XSS)
- base.css: add lt-kv-row/label/value aliases (display:contents pattern
used in web_template v1.2 kv-grid); add lt-badge-sm variant
- Admin views: add missing .catch() on editField/editRecurring/loadUsers;
add JSON_HEX_TAG to json_encode in TemplatesView/WorkflowDesignerView
- TicketView: add JSON_HEX_TAG to all ticket-data json_encode calls
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- layout_footer.php: add lt-footer with context-sensitive keyboard hint bar
([ ~ ] HOME | [ / ] SEARCH | [ + ] NEW | [ * ] CFG | [ ? ] HELP)
Context adapts for dashboard, ticket, and admin pages
- layout_footer.php: wire show-keyboard-help and open-settings for all pages
- base.css: body { display:flex; flex-direction:column } + lt-main { flex:1 }
so footer sticks to bottom of viewport on short pages
- base.css: add lt-flex-gap-xs/sm/md/lg and lt-flex-align-start/center/end
(were used across all views but never defined — causing broken layouts)
- base.css: add --lt-danger/amber/cyan/success/text-primary CSS variables
(referenced in ticket.css and dashboard.css fallbacks but never declared)
- base.css: add lt-text-danger/warning/success/info/primary utility classes
(used in TicketView, DashboardView, admin views but not defined in base.css)
- DashboardView.php: remove ascii-banner.js (loaded but never called)
- TemplatesView.php: fix priority badge from lt-p* to lt-chip component
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Full application redesign using Terminal Design System v1.2 (lt-* class
system). Introduces shared layout_header/footer partials, upgrades
base.css/base.js to TDS v1.2, and rewrites all views (Dashboard, Ticket,
CreateTicket, and all 7 admin views) with lt-frame, lt-table, lt-modal,
lt-stats-grid, lt-kv-grid, and data-action event delegation patterns.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace style.display with .is-hidden classList in ApiKeysView, CustomFieldsView, RecurringTicketsView
- Convert boot overlay fade-out from style.opacity to .boot-overlay--fade-out CSS class
- Add .boot-overlay--fade-out rule to dashboard.css
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- htmlspecialchars() on category, type, status in table rows
- htmlspecialchars() on data-status attributes in quick-action buttons
- Restrict $currentDir to 'asc'|'desc' to prevent class injection
- htmlspecialchars() on all http_build_query URLs in pagination and sort headers
- htmlspecialchars() on AuditLogView pagination URLs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace all 8 showToast() calls in ApiKeysView.php with lt.toast.*
— all toast calls in the codebase now use lt.toast directly
- Add .duplicate-list, .duplicate-meta, .duplicate-hint CSS classes to
dashboard.css; replace inline styles in duplicate detection JS with them
- Add dashboardAutoRefresh() using lt.autoRefresh — reloads page every
5 minutes, skipping if a modal is open or user is typing in an input
- Add REFRESH button to dashboard header that triggers lt.autoRefresh.now()
for immediate manual refresh with timer restart
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add .inline-error and .inline-warning utility classes to dashboard.css
with correctly-matched terminal palette rgba values (replaces off-palette
rgba(231,76,60,0.1) and rgba(241,196,15,0.1))
- Add .key-generated-alert class for the new API key display frame
- Add base .dependency-item, .dependency-group h4, .dependency-item a,
.dependency-title, .btn-small overrides to ticket.css
- Remove all inline styles from the dependency list template in ticket.js
— layout, colors, and sizing now come from CSS classes
- Update CreateTicketView.php and ApiKeysView.php to use the new classes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove collapsible ASCII banner from dashboard (was cluttering the UI)
- Show ASCII banner in the boot overlay on first session visit, above
the boot messages, with a 400ms pause before messages begin
- Add scroll fade indicator (green-tinted gradient edges) to .table-wrapper
so users can see when the table is horizontally scrollable
- Fix null guards for tab switcher in ticket.js (tabEl, activeBtn)
- Fix Reset → RESET uppercase in AuditLogView and UserActivityView
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
CSS fixes:
- Fix [ ] brackets appearing below button text by replacing display:inline-flex
with display:inline-block + white-space:nowrap on .btn — removes cross-browser
flex pseudo-element inconsistency as root cause
- Remove conflicting .btn::before ripple block (position:absolute was overriding
bracket content positioning)
- Remove overflow:hidden from .btn which was clipping bracket content
- Fix body::after duplicate rule causing GPU layer blink (second position:fixed
rule re-created compositor layer, overriding display:none suppression)
- Replace all transition:all with scoped property transitions in dashboard.css,
ticket.css, base.css (prevents full CSS property evaluation on every hover)
- Convert pulse-warning/pulse-critical keyframes from box-shadow to opacity
animation (GPU-composited, eliminates CPU repaints at 60fps)
- Fix mobile *::before/*::after blanket content:none rule — now targets only
decorative frame glyphs, preserving button brackets and status indicators
- Remove --terminal-green-dim override that broke .lt-btn hover backgrounds
JS fixes:
- Fix all lt.lt.toast.* double-prefix instances in dashboard.js
- Add null guard before .appendChild() on bulkAssignUser select
- Replace all remaining emoji with terminal bracket notation (dashboard.js,
ticket.js, markdown.js)
- Migrate all toast.*() shim calls to lt.toast.* across all JS files
View fixes:
- Remove hardcoded [ ] brackets from .btn buttons (CSS now adds them)
- Replace all emoji with terminal bracket notation in all views and admin views
- Add missing CSP nonces to AuditLogView.php and UserActivityView.php script tags
- Bump CSS version strings to ?v=20260319b for cache busting
Security fixes:
- update_ticket.php: add authorization check (non-admins can only edit their own
or assigned tickets)
- add_comment.php: validate and cast ticket_id to integer with 400 response
- clone_ticket.php: fix unconditional session_start(), add ticket ID validation,
add internal ticket access check
- bulk_operation.php: add HTTP 401/403 status codes on auth failures
- upload_attachment.php: fix missing $conn arg in AttachmentModel constructor
- assign_ticket.php: add ticket existence check and permission verification
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
/web_template/ path was being intercepted by the auth proxy at
t.lotusguild.org returning HTML instead of the actual files. Moving
base.js and base.css into /assets/js/ and /assets/css/ where static
assets are already served correctly. Updated all 10 view files and
deploy.sh accordingly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wraps all lt.keys.initDefaults() calls in `if (window.lt)` guards across
6 view files. Adds `if (!window.lt) return` bail-out in keyboard-shortcuts.js
and `if (window.lt)` guard in settings.js DOMContentLoaded handler.
This prevents TypeError crashes when /web_template/base.js returns 404,
which was causing the admin menu click delegation to never register.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Refactored all inline event handlers (onclick, onchange, onsubmit) to use
addEventListener with data-action attributes and event delegation pattern.
Changes:
- views/*.php: Replaced inline handlers with data-action attributes
- views/admin/*.php: Same refactoring for all admin views
- assets/js/dashboard.js: Added event delegation for bulk/quick action modals
- assets/js/ticket.js: Added event delegation for dynamic elements
- assets/js/markdown.js: Refactored toolbar button handlers
- assets/js/keyboard-shortcuts.js: Refactored modal close button
- SecurityHeadersMiddleware.php: Enabled strict CSP with nonces
The CSP now uses script-src 'self' 'nonce-{nonce}' instead of 'unsafe-inline',
significantly improving XSS protection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Added .setting-row-compact class for stacked label/input layout
- Updated TemplatesView.php grid to use compact rows (3 columns)
- Updated RecurringTicketsView.php grid to use compact rows (2 columns)
- Removed inline style="width: 100%" (handled by CSS now)
- Labels now stack above inputs in grid context for clarity
- Updated cache version to 20260126b
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Template fixes:
- Fixed column name mismatch: use 'default_priority' instead of 'priority'
- Updated manage_templates.php API INSERT and UPDATE queries
- Updated TemplatesView.php to use correct field name in PHP and JS
CSS improvements for .setting-row:
- Better flexbox layout with flex-wrap for responsiveness
- Proper styling for inputs, selects, and textareas in setting rows
- Labels now align to top (better for textareas)
- Added focus states with amber glow effect
- Improved checkbox styling within setting rows
- Better mobile responsive behavior (stacked layout)
- Updated cache version to 20260126a
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add admin dropdown menu in dashboard header with links to all admin pages
- Fix template modal: larger size (800px), responsive grid, type/priority dropdowns
- Fix recurring tickets modal: add Type and Assign To fields, larger size
- Make dashboard stat cards clickable for quick filtering
- Fix user-activity query (remove is_active requirement)
- Add table existence check in ticket_dependencies API
- Fix table overflow on dashboard
- Update Claude.md and README.md with current project status
- Remove migrations directory (all migrations completed)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>