Commit Graph

118 Commits

Author SHA1 Message Date
jared 85afec64ac Add responsive .lt-main.lt-container overrides to match production base.css
Production base.css has per-breakpoint .lt-main.lt-container rules that
explicitly set padding-top with tighter spacing at SM/XS viewports. Adding
these to beta to match — ensures header clearance is bulletproof at all sizes.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 11:13:16 -04:00
jared 0eab5d40e6 Restore .lt-main.lt-container combined selector — proper cascade fix
The TDS v1.2 sync removed the .lt-main.lt-container combined selector that
was already in the project's base.css. That selector has specificity (0,2,0)
vs single-class (0,1,0), so it always wins over .lt-container padding
shorthand at every breakpoint without needing per-breakpoint overrides.

Also restored flex:1, width:100%, min-width:0 on .lt-main that were dropped.
Removed the incorrect per-breakpoint .lt-main and #main-content hacks added
today which were the wrong approach to the same problem.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 17:49:00 -04:00
jared 3cfe46050b Fix header overlap with ID selector — unambiguous highest specificity
Use #main-content (specificity 1,0,0,0) to set padding-top at each breakpoint.
This cannot be overridden by any class-based rule regardless of cascade order,
permanently fixing the fixed header overlapping page content.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 17:45:14 -04:00
jared 6102985f92 Fix header overlap at all breakpoints — restore lt-main padding-top
Every media query that overrides .lt-container { padding } with a shorthand
was clobbering .lt-main { padding-top } because both selectors have equal
specificity and the container rule came later in the file. Added .lt-main
padding-top restores after each affected breakpoint (LG 1024-1279px, MD
768-1023px, 1920px+). The laptop range (LG) was the likely culprit on desktop.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 17:41:33 -04:00
jared e91709798b Fix header overlapping content at mobile breakpoints
In the SM (≤767px) and XS (≤479px) media queries, .lt-container { padding }
shorthand appeared after .lt-main { padding-top } with equal specificity,
causing the shorthand to clobber the header-clearance padding-top. Swap order
so .lt-main always wins.

Also remove redundant lt-scanlines div — body::before in base.css already
renders the scanline overlay globally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 17:19:22 -04:00
jared cfdc9e0f37 Sync TDS v1.2 additions: scanlines, cursor, radar, display-field, VT323
- Sync base.css + base.js from web_template (adds lt-scanlines,
  lt-cursor, lt-radar, lt-display-field, --font-crt/VT323 token)
- Add VT323 to Google Fonts link in layout_header.php
- Add lt-scanlines to <body> — CRT scanline overlay, light-mode suppressed
- Replace custom .editable-metadata:disabled CSS override in ticket.css
  with the canonical .lt-display-field class from base.css
- Switch Priority/Category/Type/Visibility selects and visibility-group
  checkboxes in TicketView.php from disabled attribute to lt-display-field
- Update toggleEditMode() in ticket.js to add/remove lt-display-field
  instead of toggling the disabled attribute

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 16:55:12 -04:00
jared fdc6d3d463 Fix ASCII art alignment, readonly input opacity, api key visibility
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>
2026-03-31 19:43:18 -04:00
jared 72d5061867 Fix description line breaks and disabled-field readability
Ticket descriptions are plain text — renderDescriptionView() now always
uses nl2br instead of parseMarkdown(), preventing markdown from mangling
single newlines into run-on paragraphs.

Override base.css opacity:0.45 on disabled .editable-metadata selects
(Priority, Category, Type) so they remain legible at full contrast on
dark/OLED screens in read mode.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 19:36:10 -04:00
jared 1d721eecb4 fix: description unreadable in dark mode / OLED — swap disabled textarea for lt-markdown div
Root cause: disabled textarea gets opacity:0.45 + color:var(--text-muted) from
base.css, making it near-invisible on OLED (true-black background).

Fix:
- TicketView: add #ticketDescriptionView (div.lt-markdown) alongside the textarea;
  textarea is now hidden by default (style="display:none"), view div is shown
- ticket.js: renderDescriptionView() renders raw text via parseMarkdown() or nl2br;
  showDescriptionView() / showDescriptionEdit() swap between them;
  toggleEditMode() calls showDescriptionEdit() when entering edit, and
  renderDescriptionView() + showDescriptionView() when returning to read mode
- ticket.css: .ticket-description-view sets full-contrast text-primary/secondary
  colors, min-height, and line-height for comfortable reading

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 18:10:39 -04:00
jared 2e450dc01d Apply web_template gap analysis improvements (P1-P3)
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>
2026-03-29 17:02:40 -04:00
jared d8e6dcf7fa fix: CSS nesting conflict, dashboard.js dead code removal, admin view escaping
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>
2026-03-28 21:34:34 -04:00
jared 18bf1fde0e feat: LDAP avatar support via lldap
- Create tinker-tickets service account in lldap (lldap_strict_readonly)
- Add /api/user_avatar.php: binds to lldap, fetches avatar attribute,
  caches JPEG to uploads/avatars/, returns 404 sentinel for missing photos
- Install php8.2-ldap on LXC 132 (beta) and LXC coding server
- Update layout_header.php: show lt-avatar with photo overlay + initials fallback
- Update TicketView.php: comment avatars use photo overlay pattern
- Add .lt-avatar-img / .lt-avatar-initials CSS for photo-over-initials layout
- Add LDAP_* config keys to config.php and .env.example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 20:47:08 -04:00
jared 3bb4792635 Fix header overlap, is-hidden missing globally, and CreateTicketView CSS
- base.css: add .lt-main.lt-container combined selector (specificity 0,2,0)
  to prevent responsive .lt-container padding shorthand from overriding
  the fixed-header clearance padding-top — affected all viewports < 1280px
- base.css: add .is-hidden { display: none !important } globally; it was
  only defined in ticket.css so dashboard ticketPreview popup rendered
  as a green box at 0,0 on page load instead of being hidden
- CreateTicketView.php: add dashboard.css to pageStyles so create-ticket-
  meta-grid, lt-form-hint, visibility-groups-list, duplicate-list classes
  are available (they were undefined when only ticket.css was loaded)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:30:00 -04:00
jared b42597c927 Fix CSS variables, missing utility classes, API hardening, and audit log UX
- 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>
2026-03-28 13:22:12 -04:00
jared e721b33911 Align UI with web_template TDS v1.2 standards
- Replace lt-chip priority badges with lt-badge lt-badge-p[1-4] across
  DashboardView, TemplatesView (matches web_template sticky table pattern)
- Add lt-theme-btn theme toggle to header-right; wire lt.theme.toggle()
- Replace ASCII art empty state with lt-empty-state component in dashboard
- Standardize tab wrapper lt-tabs → lt-tab-bar in Dashboard and TicketView
- Add missing lt-keys-help modal to layout_footer (fixes ? key doing nothing)
- Add lt-cmd-overlay command palette container + lt.cmdPalette.init() nav
- Add .lt-timeline-action CSS rule (used in TicketView, was undefined)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-28 13:06:40 -04:00
jared d7775e62ec Fix layout regressions, nav drawer structure, and security issues
- 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>
2026-03-28 12:43:24 -04:00
jared 51f6991f9d feat: nano-style footer bar, missing utility classes, CSS semantic vars
- 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>
2026-03-27 20:16:05 -04:00
jared 9bdeaf7731 fix: deep audit — wire TDS v1.2 components, fix kanban/tabs/bulk/avatar
- ticket.js: fix showTab() early return preventing attachments/deps from loading
- ticket.js: fix performStatusChange() overwriting lt-status-* classes
- dashboard.js: fix updateSelectionCount() using is-visible instead of style.display
- dashboard.js: fix populateKanbanCards() to use #kanban-col-* IDs (TDS v1.2)
- dashboard.js: fix setViewMode() removing references to old non-TDS elements
- dashboard.js: remove mobile-bottom-nav injection (no CSS existed for it)
- dashboard.css: add full lt-kanban-card component styles with priority accents
- dashboard.css: add mobile sidebar overlay, filter toggle, ticket preview popup CSS
- DashboardView.php: replace priority badges with lt-chip component
- TicketView.php: add lt-avatar with initials to comment author display
- ApiKeysView.php: enhance API usage section with lt-code-block component + curl example

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 19:58:14 -04:00
jared 79c2d2b513 feat: complete TDS v1.2 redesign across all views
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>
2026-03-27 19:05:42 -04:00
jared ce95e555d5 CSS class migrations: admin views and boot overlay fade-out
- 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>
2026-03-20 21:20:55 -04:00
jared e35401d54e CSS class migrations for ticket page: tabs, visibility, markdown preview, uploads
- Switch tab show/hide from style.display to .tab-content.active CSS class
- Convert visibilityGroupsField, markdownPreview, uploadProgress to use .is-hidden class
- Replace comment text div style.display with classList.add/remove('is-hidden')
- Add .is-hidden utility class to ticket.css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:13:55 -04:00
jared 913e294f9d CSS class migrations: stat-card cursor, view toggle, bulk actions visibility
- Replace stat-card cursor:pointer inline style with CSS rule
- Convert view toggle (table/card) to use .is-hidden CSS class
- Convert bulk-actions and export-dropdown to use .is-visible class
- Add .is-hidden/.is-visible utility rules to dashboard.css
- Remove duplicate lt.keys.initDefaults() call from dashboard.js
- Remove redundant setTimeout from view mode restore
- Add lt.keys.initDefaults() to dashboard.js (was missing entirely)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 21:08:28 -04:00
jared 31aa7d1b81 Fix JS SyntaxError breaking tabs, textarea scrolling, and XSS escaping
Bug fixes:
- ticket.js: Remove duplicate const textarea declaration inside showMentionSuggestions()
  (was redeclaring a parameter, causing SyntaxError that broke all tab switching)
- ticket.css: Add overflow:hidden + resize:none to disabled textarea so description
  shows full height without internal scrollbar (page scrolls instead)
- ticket.js: Trigger height recalculation when entering edit mode on description

XSS/escaping fixes:
- TicketView.php: htmlspecialchars() on description textarea content (closes </textarea> injection risk)
- TicketView.php: htmlspecialchars() on ticket status and workflow transition status strings
- DashboardView.php: htmlspecialchars() on $cat/$type in input value= attributes
- RecurringTicketsView.php: htmlspecialchars() on composed schedule string

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:34:55 -04:00
jared 7695c6134c Accessibility pass: ARIA roles, label associations, CSS class migrations
- Add role=dialog/aria-modal/aria-labelledby to all 12 modal overlays (JS + PHP)
- Add aria-label="Close" to all 14 modal close buttons
- Add full ARIA combobox pattern to @mention autocomplete (listbox, option, aria-selected, aria-expanded)
- Add for= attributes to admin filter form labels (AuditLog, UserActivity, ApiKeys)
- Remove dead closeOnAdvancedSearchBackdropClick() from advanced-search.js

CSS/JS style cleanup:
- Move .ascii-banner static styles from JS inline to CSS class; add .ascii-banner--glow
- Add .ascii-banner-cursor, .loading-overlay--hiding, .has-overlay, tr[data-clickable]
- Add .animate-fadein/.animate-fadeout/.comment--deleting to ticket.css
- Add .lt-toast--hiding to base.css; remove opacity/transition inline JS
- Remove redundant cursor:pointer JS (already in th{} CSS rule)
- Remove trailing space in lt-select class attributes

Bug fixes:
- base.js: boot overlay opacity inline style was overriding .fade-out class opacity via
  specificity (1000 vs 20), preventing the fade-out animation — removed
- ascii-banner.js: cursor used blink-caret (border-color only) instead of blink-cursor
  (opacity-based), so the █ cursor never actually blinked — fixed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 20:29:58 -04:00
jared 11f75fd823 Migrate all raw fetch() calls to lt.api, fix CSS fallback values
- Replace all 23 raw fetch() calls in dashboard.js and ticket.js with
  lt.api.get/post/delete — removes manual CSRF header injection,
  manual JSON parsing boilerplate, and response.ok checks throughout
- dashboard.js: 10 calls (inline save x2, template GET, 5x bulk ops,
  quick-status, quick-assign)
- ticket.js: 13 calls (main save, add/update/delete comment x3, reply,
  assign, metadata update, status change, deps GET/POST/DELETE,
  attachments GET, delete attachment)
- Remove stale csrf_token from deleteAttachment body (lt.api sends the
  X-CSRF-Token header automatically)
- Fix CSS variable fallbacks in ticket.css: replace
  var(--text-primary, #f7fafc) and var(--bg-secondary, #1a202c)
  with plain var(--text-primary) and var(--bg-secondary)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:27:46 -04:00
jared e179709fc3 Add lt.autoRefresh, fix showToast in admin, clean up inline styles
- 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>
2026-03-20 11:16:18 -04:00
jared b03a9cfc8c Extract hardcoded rgba colors and inline styles to CSS classes
- 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>
2026-03-20 11:08:52 -04:00
jared d44a530018 Extend lt.time.ago() to ticket view, replace showToast with lt.toast
- Add data-ts attributes to TicketView.php: ticket created/updated
  header, comment dates (inner span to preserve edited indicator),
  and all activity timeline dates
- Add initRelativeTimes() to ticket.js using lt.time.ago(); runs on
  DOMContentLoaded and every 60s to keep relative times current
- Attachment dates now use lt.time.ago() with full date in title attr
  and ts-cell span for periodic refresh
- Replace all 11 showToast() calls in ticket.js with lt.toast.* directly,
  removing reliance on the backwards-compat shim for these paths
- Add span.ts-cell and td.ts-cell CSS to both dashboard.css and ticket.css:
  dotted underline + cursor:help signals the title tooltip is available

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 11:03:34 -04:00
jared 1046537429 Move ASCII banner into boot sequence, fix remaining UI issues
- 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>
2026-03-20 10:41:57 -04:00
jared d8220da1e0 Make dashboard table horizontally scrollable at smaller screen widths
- Set .table-wrapper to overflow-x: auto with touch scrolling support
- Add min-width: 900px to table to trigger scroll before columns collapse
- Set .ascii-frame-outer overflow-x: visible to avoid clipping conflict

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 10:35:45 -04:00
jared 90c5b3ff71 UI/UX polish: terminal design system alignment pass
Views:
- DashboardView.php: remove hardcoded [ ] from admin-badge button (CSS adds them)
- DashboardView.php: view toggle ≡/▦ → [ = ]/[ # ] (view-btn suppresses auto-brackets)
- DashboardView.php: clear-search ✗ → [ X ] (plain text, no auto-brackets on <a>)
- DashboardView.php: remove ↓ arrow emoji from export button text
- TicketView.php: tab labels → UPPERCASE (tab-btn CSS adds [ ] around text)
- TicketView.php: Edit Ticket/Clone/Add Comment/Add → title-case → UPPERCASE
- TicketView.php: reply button ↩ → [ << ] (comment-action-btn has no auto-brackets)

JavaScript:
- dashboard.js: modal/action button text all → UPPERCASE (CONFIRM/CANCEL/SAVE/ASSIGN/UPDATE/DELETE PERMANENTLY)
- dashboard.js: null guard in loadTemplate(), toggleSelectAll()
- ticket.js: null guards in addDependency(), handleFileUpload()

CSS:
- dashboard.css: z-index 1001/1002 magic numbers → var(--z-modal)/var(--z-popover)
- ticket.css: status-select hover/focus border rgba(white) → terminal palette

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:50:59 -04:00
jared 84bea80abd Fix PHP parse error and CSS/JS follow-on fixes
- DashboardView.php: fix PHP parse error on line 456/472/473/474 caused by
  escaped double-quotes {$row[\"key\"]} inside double-quoted echo strings;
  replaced with safe string concatenation . $row['key'] .
- ticket.css: fix status-select hover/focus border rgba(white) → terminal palette
- ticket.js: add null guards to addComment, togglePreview, updatePreview,
  toggleMarkdownMode, and addDependency element lookups

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:44:08 -04:00
jared 2f9af856dc Fix design system violations: replace off-brand colors with terminal palette
- dashboard.css: replace all hardcoded Tailwind hex colors (#2d3748, #1a202c,
  #e2e8f0, #4a5568, #007cba, #3b82f6 etc.) in dark-mode sections and component
  styles with terminal CSS variables (--bg-*, --text-*, --border-color,
  --terminal-green/amber)
- dashboard.css: fix card-priority colors white/black → var(--bg-primary)
- dashboard.css: fix card-assignee border-radius: 50% → 0 (no circles rule)
- dashboard.css: fix mobile bottom-sheet border-radius: 12px → 0
- dashboard.css: fix search-box focus border (#007cba → var(--terminal-green))
- dashboard.css: fix save-filter button blue (#3b82f6) → terminal green
- dashboard.css: fix search-results-info blue highlight → terminal green
- dashboard.css: fix btn-bulk/btn-secondary dark-mode bootstrap colors → terminal
- ticket.css: replace comprehensive dark-mode Tailwind hex block with CSS vars
- ticket.css: fix status-select white/black text → var(--bg-primary)
- ticket.css: fix status-select.status-resolved hardcoded #28a745 → var(--status-open)
- ticket.css: fix timeline dark-mode hardcoded colors → CSS vars
- ticket.css: fix .slider:before background white → var(--bg-primary)
- ticket.css: fix .btn-danger:hover color white → var(--bg-primary)
- ticket.css: fix visibility-groups-list label border-radius: 4px → 0
- ticket.css: add will-change: opacity to age-warning/age-critical animations
- views: bump CSS version strings to v=20260319c
- views/DashboardView.php: add aria-labels to card view quick action buttons

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 22:37:19 -04:00
jared 27075a62ee Fix bracket buttons rendering below text + UI/security improvements
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>
2026-03-19 22:20:43 -04:00
jared dd8833ee2f Restore visual effects using GPU-safe techniques (no repaint triggers)
Rules: transform/opacity = GPU composited (fine). box-shadow/text-shadow on
hover = CPU repaint (removed). Static box-shadow/text-shadow = painted once (fine).

- Buttons (.btn, .btn-base, button, .btn-primary): add will-change:transform
  for pre-promotion, add transform:translateY(-1px) on hover (GPU, no repaint),
  scope transition to include transform, remove box-shadow/text-shadow from hover
- Stat cards: add will-change:transform, add transform:translateY(-2px) on hover
- Priority badges: replace filter:blur(6px) ::after pseudo-element (permanent GPU
  layer per badge, ~20 on screen at once) with static box-shadow:0 0 6px currentColor
  on the badge itself — painted once, never changes, zero compositor overhead
- Links: replace opacity-transition ::after underline (lazy GPU layer creation on
  hover) with text-decoration:underline on hover (pure CPU paint, no GPU layer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 19:28:28 -04:00
jared ab3e77a9ba Fix blink root cause: eliminate position:fixed GPU compositing layers
Chrome promotes ALL position:fixed elements to GPU compositing layers for scroll
performance, regardless of whether they have animations. The body::before scanline
overlay (position:fixed, z-index:9999, full-viewport) and body::after watermark
(position:fixed) were both on GPU layers. Every CPU repaint from any hover state
change required a compositor re-blend pass → one-frame blink at compositor sync.

Fixes:
- Move scanlines from body::before (position:fixed) into body { background-image }
  — same visual, no separate element, no GPU layer promotion
- Set body::before { display:none } and body::after { display:none } in both
  dashboard.css and base.css
- Remove animation:matrix-rain from .stat-card:hover::before — background-position
  animation is not GPU-composited, caused CPU repaints every frame while hovered
  plus GPU texture uploads when animation started/stopped on cursor enter/exit
- Scope a { transition: all } → transition: color in base.css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 12:23:30 -04:00
jared 68ff89b48c Fix persistent blink: scanline animation still active via base.css cascade
Root cause: removing 'animation' from dashboard.css body::before did NOT disable
the scanline — it just stopped overriding base.css which still had
'animation: scanline 8s linear infinite'. CSS cascade means the base.css value
remained active. Fixed by setting 'animation: none' explicitly in dashboard.css.

Also fix base.css (used by all pages including ticket page):
- Set body::before animation: none (removes GPU compositing layer from scanline)
- Change corner-pulse/subtle-pulse/pulse-glow/pulse-red keyframes from text-shadow
  and box-shadow animations to opacity (GPU composited, zero CPU repaint overhead)
- Change exec-running-pulse from box-shadow to opacity
- Remove box-shadow from .lt-table tr:hover, .lt-card:hover, .lt-stat-card:hover
- Remove text-shadow/box-shadow/transform from .lt-btn:hover and variants
- Remove text-shadow from a:hover

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 12:12:44 -04:00
jared 328c103460 Fix ascii-frame-outer blink: eliminate all repaint-causing hover effects
- Change pulse-glow keyframes from text-shadow animation to opacity (GPU composited,
  eliminates 60fps CPU repaint that was the likely root cause of the persistent blink)
- Remove box-shadow from .quick-action-btn:hover; scope transition: all → background/color
- Remove box-shadow + background gradient + transform:translateY from .stat-card:hover;
  scope transition: all → border-color only
- Remove .stat-card::after transition and hover background change
- Remove duplicate .stat-card:hover transform:translateY block
- Remove box-shadow from .clear-search-btn:hover; scope transition: all → background/color
- Remove text-shadow from th transition (th:hover never changes text-shadow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 12:06:39 -04:00
jared 21ef9154e9 Fix ascii-frame-outer blink: remove scanline GPU layer and remaining repaint triggers
- Remove body::before scanline animation (transform: translateY promoted it to a
  GPU compositing layer; CPU repaints from hover states required compositor re-blend,
  causing one-frame blink at compositor sync boundary)
- Remove text-shadow and transform: translateY(-2px) from .btn-primary:hover/.create-ticket:hover
- Scope .btn-primary transition from 'all' to specific composited properties
- Remove box-shadow: inset from .banner-toggle:hover

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 11:57:47 -04:00
jared 4ecd72bc04 Strip all box-shadow/text-shadow from hover states inside ascii-frame-outer
These non-composited properties force CPU repaints of the surrounding
paint region (the section itself) every time hover enters or exits.
With the fixed overlay (body::before scanline), each such repaint
requires the compositor to re-blend the layer, visible as a blink.

Removed from dashboard.css:
- btn/button/btn-base:hover: box-shadow + text-shadow
- th:hover: text-shadow
- ticket-link:hover: text-shadow
- pagination button:hover: box-shadow + transform + transition:all
- ticket-card-row:hover: box-shadow + transition:all -> background only
- .btn ripple rule: transition:all -> specific properties
- ascii-frame-outer: removed will-change/translateZ (GPU upload worse)

Removed from ticket.css:
- metadata-select:hover: box-shadow; transition:all -> border-color
- comment:hover: box-shadow
- btn:hover: box-shadow
- mention:hover: text-shadow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 11:50:06 -04:00
jared 368ad9b48e Promote ascii-frame-outer to GPU layer to stop hover blink
The body::before scanline overlay (position:fixed, z-index:9999) requires
the compositor to re-blend over the section every time a CPU repaint
happens inside it. Hover state entry/exit triggers these repaints, causing
a visible blink as the compositor flushes.

Fixes:
- Add will-change:transform + transform:translateZ(0) to ascii-frame-outer
  to promote it to its own GPU compositing layer, isolating its repaints
  from the scanline compositing pass
- Convert corner-pulse and subtle-pulse from text-shadow (CPU repaint)
  to opacity (GPU composited) to eliminate continuous repaint pressure
  inside the section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 11:35:38 -04:00
jared 3497c4cb47 Fix ascii-frame-outer blink: remove compositor layer thrashing on hover
Four root causes removed:
- body { transition: all } — forced browser to check all CSS properties
  on every hover event across the entire page
- a:not(.btn)::after underline: width+box-shadow transition replaced with
  opacity transition — width repaints paint layer, box-shadow forced parent
  section repaint; opacity is GPU-composited and doesn't repaint ancestors
- .ticket-link:hover { transform: translateX } — created/destroyed GPU
  compositor layer on every ticket ID hover; removed, scoped transition
  to specific non-layout properties
- .btn:hover { transform: translateY } in ticket.css — same layer issue

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 11:25:18 -04:00
jared e756f8e0bb Fix ascii-frame-outer blink caused by JS/CSS hover conflict
JS mouseenter/mouseleave handlers were setting row.style.backgroundColor
inline, fighting with the CSS tr:hover rule. On mouseleave both fired
simultaneously causing a double repaint / blink. Removed the redundant
JS handlers — the CSS tr:hover transition already handles this cleanly.

Also removed body flicker animation from base.css (was still present
after being removed from dashboard.css).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 11:14:05 -04:00
jared fea7575ac8 Fix dashboard blink when cursor leaves ticket table section
- Remove pulse-glow-box animation and translateY from button:hover
  (infinite animation stopping abruptly caused a flash on mouse-leave)
- Scope button transition from 'all' to specific visual properties
  (prevents transform/layout changes from triggering on hover exit)
- Scope th transition from 'all' to background-color + text-shadow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 11:10:27 -04:00
jared 6fbba3939f Remove page-blinking CSS animations and old deploy script
- Remove .ascii-frame-outer:hover flicker animation (caused article to
  shake/blink every time cursor entered the ticket container)
- Remove body flicker animation (caused full page blink every 30s)
- Remove deploy.sh (deployment now handled by Gitea CI/CD)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 11:04:03 -04:00
jared f3c15e2582 Fix table row blink when cursor leaves the table
transition:all was firing on every row simultaneously when the cursor
left the table. Scoped it to background-color only. Also removed the
inset box-shadow from tr:hover which forced repaint layer thrashing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-19 10:55:50 -04:00
jared 4a838b68ca Move base.js/base.css into assets to fix auth proxy 404
/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>
2026-03-17 23:44:46 -04:00
jared 92544d60ce Fix lt-modal-overlay not hidden without base.css
Add lt-modal-overlay, lt-modal, lt-btn fallback styles to dashboard.css
so modals are properly hidden (display:none) and styled even when
/web_template/base.css is not yet served. Mirrors the rules from base.css.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-17 23:29:20 -04:00
jared a34ca51223 Apply LotusGuild design system convergence (aesthetic_diff.md)
- §10: Filter sidebar labels color green→amber with glow-amber,
  matching unified amber-for-labels convention from base.css

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-14 21:40:43 -04:00
jared e8b2f670b9 Fix mobile bottom nav consistency and ticket view width
Mobile bottom nav:
- Added nav-label class to all text labels in JS
- Fixed icon sizing (20px fixed height)
- Fixed label sizing (10px for all)
- Equal width columns (25% each)
- Changed gear emoji from ⚙️ to ⚙ for consistency

Ticket view mobile:
- Removed all borders from ticket container
- Removed decorative corners on mobile
- Reduced nested padding significantly
- ascii-frame-inner now 0.75rem padding (was 1rem)
- Nested ascii-frame-inner only 0.5rem
- detail-group full-width has no padding
- Content goes edge-to-edge

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 11:59:31 -05:00