Implement complete ANSI art terminal redesign

Transform entire UI into retro terminal aesthetic with ASCII/ANSI art:

Visual Changes:
- Add large ASCII art "TINKER TICKETS" banner with typewriter animation
- Terminal black background (#0a0a0a) with matrix green text (#00ff41)
- ASCII borders throughout using box-drawing characters (┌─┐│└─┘╔═╗║╚╝)
- Monospace fonts (Courier New, Consolas, Monaco) everywhere
- All rounded corners removed (border-radius: 0)
- Text glow effects on important elements
- Terminal prompts (>, $) and brackets ([]) on all UI elements

Dashboard:
- Table with ASCII corner decorations and terminal green borders
- Headers with > prefix and amber glow
- Priority badges: [P1] [P2] format with colored glows
- Status badges: [OPEN] [CLOSED] with borders and glows
- Search box with $ SEARCH prompt
- All buttons in [ BRACKET ] format

Ticket View:
- Ticket container with double ASCII borders (╔╗╚╝)
- Priority-colored corner characters
- UUID display: [UUID: xxx] format
- Comments section: ╔═══ COMMENTS ═══╗ header
- Activity timeline with ASCII tree (├──, │, └──)
- Tabs with [ ] brackets and ▼ active indicator

Components:
- Modals with ╔═══ TITLE ═══╗ headers and ASCII corners
- Hamburger menu with MENU SYSTEM box decoration
- Settings modal with terminal styling
- All inputs with green borders and amber focus glow
- Checkboxes with ✓ characters

Technical:
- New file: ascii-banner.js with banner artwork and typewriter renderer
- Comprehensive responsive design (1024px, 768px, 480px breakpoints)
- Mobile: simplified ASCII, hidden decorations, full-width menu
- Print styles for clean black/white output
- All functionality preserved, purely visual transformation

Colors preserved:
- Priority: P1=red, P2=orange, P3=blue, P4=green, P5=gray
- Status: Open=green, In Progress=yellow, Closed=red
- Accents: Terminal green, amber, cyan

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-06 23:22:25 -05:00
parent eda40a150b
commit 8aa5c39ed8
4 changed files with 1620 additions and 209 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +1,115 @@
/* ===== TICKET PAGE SPECIFIC STYLES ===== */
/* ===== TICKET PAGE SPECIFIC STYLES - TERMINAL EDITION ===== */
/* Status colors for ticket page */
.status-Open,
/* Import terminal variables (should be loaded from dashboard.css globally) */
/* This file uses the same CSS variables defined in dashboard.css */
/* Status colors for ticket page - TERMINAL STYLE */
.status-Open,
[id="statusDisplay"].status-Open {
background-color: var(--status-open) !important;
color: white !important;
background-color: transparent !important;
color: var(--status-open) !important;
padding: 8px 16px;
border-radius: 6px;
border-radius: 0 !important;
border: 2px solid var(--status-open) !important;
font-weight: 500;
text-transform: uppercase;
font-size: 0.9em;
letter-spacing: 0.5px;
font-family: var(--font-mono);
text-shadow: 0 0 5px var(--status-open), 0 0 10px var(--status-open);
}
.status-Open::before,
[id="statusDisplay"].status-Open::before {
content: '[';
margin-right: 4px;
}
.status-Open::after,
[id="statusDisplay"].status-Open::after {
content: ']';
margin-left: 4px;
}
.status-In-Progress,
[id="statusDisplay"].status-In-Progress {
background-color: var(--status-in-progress) !important;
color: #212529 !important;
background-color: transparent !important;
color: var(--status-in-progress) !important;
padding: 8px 16px;
border-radius: 6px;
border-radius: 0 !important;
border: 2px solid var(--status-in-progress) !important;
font-weight: 500;
text-transform: uppercase;
font-size: 0.9em;
letter-spacing: 0.5px;
font-family: var(--font-mono);
text-shadow: 0 0 5px var(--status-in-progress), 0 0 10px var(--status-in-progress);
}
.status-In-Progress::before,
[id="statusDisplay"].status-In-Progress::before {
content: '[';
margin-right: 4px;
}
.status-In-Progress::after,
[id="statusDisplay"].status-In-Progress::after {
content: ']';
margin-left: 4px;
}
.status-Closed,
[id="statusDisplay"].status-Closed {
background-color: var(--status-closed) !important;
color: white !important;
background-color: transparent !important;
color: var(--status-closed) !important;
padding: 8px 16px;
border-radius: 6px;
border-radius: 0 !important;
border: 2px solid var(--status-closed) !important;
font-weight: 500;
text-transform: uppercase;
font-size: 0.9em;
letter-spacing: 0.5px;
font-family: var(--font-mono);
text-shadow: 0 0 5px var(--status-closed), 0 0 10px var(--status-closed);
}
/* Base Layout Components */
.status-Closed::before,
[id="statusDisplay"].status-Closed::before {
content: '[';
margin-right: 4px;
}
.status-Closed::after,
[id="statusDisplay"].status-Closed::after {
content: ']';
margin-left: 4px;
}
.status-Resolved {
background-color: transparent !important;
color: var(--status-open) !important;
padding: 8px 16px;
border-radius: 0 !important;
border: 2px solid var(--status-open) !important;
font-weight: 500;
text-transform: uppercase;
font-size: 0.9em;
letter-spacing: 0.5px;
font-family: var(--font-mono);
text-shadow: 0 0 5px var(--status-open), 0 0 10px var(--status-open);
}
.status-Resolved::before {
content: '[';
margin-right: 4px;
}
.status-Resolved::after {
content: ']';
margin-left: 4px;
}
/* Base Layout Components - TERMINAL STYLE */
.ticket-container {
width: 90%;
height: auto !important;
@@ -45,28 +117,101 @@
min-width: 800px;
max-width: 1800px;
margin: 40px auto;
padding: 20px;
background: var(--bg-secondary);
border-radius: 12px;
box-shadow: var(--shadow);
border-left: 6px solid;
padding: 0;
background: var(--bg-primary);
border: 3px double var(--terminal-green);
border-radius: 0;
box-shadow: none;
transition: border-color 0.3s ease;
position: relative;
font-family: var(--font-mono);
}
/* Priority border colors */
[data-priority="1"] { border-color: var(--priority-1); }
[data-priority="2"] { border-color: var(--priority-2); }
[data-priority="3"] { border-color: var(--priority-3); }
[data-priority="4"] { border-color: var(--priority-4); }
[data-priority="5"] { border-color: var(--priority-5); }
/* ASCII corner decorations */
.ticket-container::before {
content: '╔';
position: absolute;
top: -3px;
left: -3px;
font-size: 1.5rem;
color: var(--terminal-green);
line-height: 1;
z-index: 10;
}
/* Header Components */
.ticket-container::after {
content: '╗';
position: absolute;
top: -3px;
right: -3px;
font-size: 1.5rem;
color: var(--terminal-green);
line-height: 1;
z-index: 10;
}
/* Priority-based border colors */
[data-priority="1"] {
border-color: var(--priority-1);
}
[data-priority="1"]::before,
[data-priority="1"]::after {
color: var(--priority-1);
}
[data-priority="2"] {
border-color: var(--priority-2);
}
[data-priority="2"]::before,
[data-priority="2"]::after {
color: var(--priority-2);
}
[data-priority="3"] {
border-color: var(--priority-3);
}
[data-priority="3"]::before,
[data-priority="3"]::after {
color: var(--priority-3);
}
[data-priority="4"] {
border-color: var(--priority-4);
}
[data-priority="4"]::before,
[data-priority="4"]::after {
color: var(--priority-4);
}
[data-priority="5"] {
border-color: var(--priority-5);
}
[data-priority="5"]::before,
[data-priority="5"]::after {
color: var(--priority-5);
}
/* Header Components - TERMINAL STYLE */
.ticket-header {
display: flex;
flex-direction: column;
margin-bottom: 30px;
width: 100%;
overflow-wrap: break-word;
padding: 20px;
border-bottom: 2px solid var(--terminal-green);
background: var(--bg-secondary);
position: relative;
}
.ticket-header::before {
content: '═══════════════════════════════════════════════════════';
display: block;
color: var(--terminal-green);
font-family: var(--font-mono);
font-size: 0.8rem;
margin-bottom: 10px;
opacity: 0.5;
}
.ticket-subheader {
@@ -79,8 +224,9 @@
.ticket-details {
margin-top: 30px;
padding: 20px;
background: var(--bg-primary);
border-radius: 8px;
background: var(--bg-secondary);
border: 2px solid var(--terminal-green);
border-radius: 0;
}
.header-controls {
@@ -90,8 +236,20 @@
}
.ticket-id {
font-family: 'Courier New', monospace;
font-family: var(--font-mono);
margin-right: 20px;
color: var(--terminal-amber);
text-shadow: var(--glow-amber);
}
.ticket-id::before {
content: '[UUID: ';
color: var(--terminal-green);
}
.ticket-id::after {
content: ']';
color: var(--terminal-green);
}
.status-priority-group {
@@ -103,23 +261,39 @@
.priority-indicator {
padding: 4px 8px;
border-radius: 4px;
border: 2px solid;
border-radius: 0;
font-weight: bold;
font-family: var(--font-mono);
}
/* Title Input Styles */
.priority-indicator::before {
content: '[';
margin-right: 2px;
}
.priority-indicator::after {
content: ']';
margin-left: 2px;
}
/* Title Input Styles - TERMINAL */
.title-input {
font-size: 1em;
font-size: 1.2em;
font-weight: bold;
font-family: var(--font-mono);
width: 100%;
border: 2px solid transparent;
border-radius: 4px;
padding: 12px;
margin: -4px;
border: none;
border-bottom: 2px solid transparent;
border-radius: 0;
padding: 8px 0;
margin: 0;
word-break: break-word;
overflow-wrap: break-word;
white-space: pre-wrap;
display: block;
background: transparent;
color: var(--terminal-green);
line-height: 1.4;
min-height: fit-content;
height: auto;
@@ -220,12 +394,29 @@ textarea.editable {
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
/* Comments Section */
/* Comments Section - TERMINAL STYLE */
.comments-section {
margin-top: 40px;
padding: 25px;
padding: 20px;
background: var(--bg-secondary);
border-radius: 8px;
border: 2px solid var(--terminal-green);
border-radius: 0;
}
.comments-section h2 {
font-family: var(--font-mono);
color: var(--terminal-amber);
text-shadow: var(--glow-amber);
text-transform: uppercase;
letter-spacing: 0.1em;
}
.comments-section h2::before {
content: '╔═══ ';
}
.comments-section h2::after {
content: ' ═══╗';
}
.comment-form {
@@ -234,41 +425,68 @@ textarea.editable {
.comment-form textarea {
width: calc(100% - 20px);
min-height: 80px;
min-height: 100px;
margin-bottom: 10px;
padding: 10px;
border-radius: 6px;
border: 1px solid var(--border-color);
border-radius: 0;
border: 2px solid var(--terminal-green);
background: var(--bg-primary);
color: var(--text-primary);
color: var(--terminal-green);
font-family: var(--font-mono);
}
.comment-form textarea::placeholder {
color: rgba(0, 255, 65, 0.5);
}
.comment {
background: var(--bg-primary);
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 15px;
border: 2px solid var(--terminal-green);
border-radius: 0;
margin-bottom: 15px;
position: relative;
box-shadow: none;
}
.comment-header {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
margin-bottom: 10px;
padding-bottom: 8px;
border-bottom: 1px solid var(--terminal-green);
font-family: var(--font-mono);
font-size: 0.9em;
}
.comment-user {
font-weight: bold;
color: var(--text-primary);
color: var(--terminal-amber);
}
.comment-user::before {
content: '> ';
color: var(--terminal-green);
}
.comment-date {
color: var(--text-secondary);
color: var(--terminal-cyan);
font-size: 0.85em;
}
.comment-date::before {
content: '[';
color: var(--terminal-green);
}
.comment-date::after {
content: ']';
color: var(--terminal-green);
}
.comment-text {
color: var(--text-primary);
color: var(--terminal-green);
font-family: var(--font-mono);
line-height: 1.6;
word-wrap: break-word;
margin: 0;
@@ -295,37 +513,64 @@ textarea.editable {
margin: 10px 0;
}
/* Comment Tabs */
/* Comment Tabs - TERMINAL STYLE */
.ticket-tabs {
display: flex;
gap: 10px;
gap: 0;
margin: 20px 0;
border-bottom: 2px solid var(--terminal-green);
}
.tab-btn {
padding: 12px 24px;
border: 1px solid var(--border-color);
background: var(--bg-secondary);
border-radius: 6px;
border: 2px solid var(--terminal-green);
border-bottom: none;
background: var(--bg-primary);
border-radius: 0;
cursor: pointer;
font-weight: 500;
font-size: 1.1em;
font-weight: bold;
font-size: 1em;
font-family: var(--font-mono);
color: var(--terminal-green);
transition: all 0.3s ease;
position: relative;
margin-right: -2px;
transition: all 0.3s ease;
}
.tab-btn::before {
content: '[ ';
color: var(--terminal-green);
}
.tab-btn::after {
content: ' ]';
color: var(--terminal-green);
}
.tab-btn:hover {
transform: translateY(-2px);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
background: rgba(0, 255, 65, 0.05);
color: var(--terminal-amber);
}
.tab-btn.active {
background: #3b82f6;
color: white;
border-color: #3b82f6;
background: var(--bg-secondary);
color: var(--terminal-amber);
border-color: var(--terminal-amber);
text-shadow: var(--glow-amber);
z-index: 1;
}
.tab-btn.active::after {
content: ' ▼ ]';
}
.tab-content {
display: none;
padding: 20px;
border: 2px solid var(--terminal-green);
border-top: none;
background: var(--bg-secondary);
}
.tab-content.active {
@@ -443,10 +688,11 @@ input:checked + .slider:before {
background: var(--border-color);
}
/* Activity Timeline Styles */
/* Activity Timeline Styles - ASCII TREE */
.timeline-container {
padding: 1rem;
max-width: 800px;
font-family: var(--font-mono);
}
.timeline-event {
@@ -456,32 +702,61 @@ input:checked + .slider:before {
position: relative;
}
/* ASCII tree connector */
.timeline-event:not(:last-child)::before {
content: '';
content: '';
position: absolute;
left: 12px;
top: 30px;
bottom: -24px;
width: 2px;
background: var(--border-color, #ddd);
color: var(--terminal-green);
font-family: var(--font-mono);
font-size: 1.2rem;
line-height: 1.5rem;
}
/* Branch connector */
.timeline-event::after {
content: '├──';
position: absolute;
left: 0;
top: 12px;
color: var(--terminal-green);
font-family: var(--font-mono);
}
.timeline-event:last-child::after {
content: '└──';
}
.timeline-icon {
font-size: 1.5rem;
font-size: 1.2rem;
flex-shrink: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
margin-left: 30px;
}
.timeline-content {
flex: 1;
background: var(--card-bg, #f8f9fa);
padding: 0.75rem 1rem;
border-radius: 8px;
border: 1px solid var(--border-color, #ddd);
background: var(--bg-primary);
padding: 12px 16px;
border: 2px solid var(--terminal-green);
border-radius: 0;
position: relative;
}
/* Terminal box corner */
.timeline-content::before {
content: '┌─';
position: absolute;
top: -2px;
left: -2px;
color: var(--terminal-green);
font-family: var(--font-mono);
}
.timeline-header {
@@ -489,25 +764,40 @@ input:checked + .slider:before {
gap: 0.5rem;
align-items: center;
margin-bottom: 0.5rem;
flex-wrap: wrap;
font-family: var(--font-mono);
}
.timeline-header strong {
color: var(--terminal-amber);
}
.timeline-header strong::before {
content: '[';
color: var(--terminal-green);
}
.timeline-header strong::after {
content: ']';
color: var(--terminal-green);
}
.timeline-action {
color: var(--text-muted, #666);
color: var(--terminal-green);
font-size: 0.9rem;
}
.timeline-date {
margin-left: auto;
color: var(--text-muted, #999);
color: var(--terminal-cyan);
font-size: 0.85rem;
}
.timeline-details {
font-size: 0.9rem;
color: var(--text-secondary, #555);
color: var(--terminal-green);
padding-top: 0.5rem;
border-top: 1px solid var(--border-color, #eee);
border-top: 1px solid var(--terminal-green);
font-family: var(--font-mono);
}
body.dark-mode .timeline-content {
@@ -676,3 +966,108 @@ body.dark-mode .editable {
color: #e2e8f0 !important;
border-color: #4a5568 !important;
}
/* ===== RESPONSIVE DESIGN - TERMINAL EDITION ===== */
/* Tablet and smaller */
@media (max-width: 1024px) {
.ticket-container {
width: 95%;
min-width: 600px;
}
/* Reduce timeline spacing */
.timeline-event {
margin-bottom: 1rem;
}
}
/* Mobile */
@media (max-width: 768px) {
.ticket-container {
width: 98%;
min-width: unset;
margin: 20px auto;
padding: 0;
}
/* Hide ASCII corner decorations on mobile */
.ticket-container::before,
.ticket-container::after {
display: none;
}
/* Simplify ticket header */
.ticket-header::before {
display: none;
}
.ticket-header {
padding: 15px;
}
/* Stack header controls */
.header-controls {
flex-direction: column;
align-items: flex-start;
}
/* Reduce comment padding */
.comment {
padding: 10px;
}
/* Simplify timeline on mobile */
.timeline-icon {
margin-left: 20px;
}
.timeline-event::after {
font-size: 0.9rem;
}
/* Smaller tabs */
.tab-btn {
padding: 8px 16px;
font-size: 0.9em;
}
/* Reduce glow effects */
.glow-text,
[class*="glow-"] {
text-shadow: 0 0 3px currentColor;
}
}
/* Very small mobile */
@media (max-width: 480px) {
.ticket-container {
width: 100%;
margin: 10px 0;
}
/* Remove timeline connectors on very small screens */
.timeline-event::before,
.timeline-event::after {
display: none;
}
.timeline-content::before {
display: none;
}
.timeline-icon {
margin-left: 0;
}
/* Stack tabs vertically */
.ticket-tabs {
flex-direction: column;
}
.tab-btn {
width: 100%;
border: 2px solid var(--terminal-green);
margin-bottom: 5px;
}
}

197
assets/js/ascii-banner.js Normal file
View File

@@ -0,0 +1,197 @@
/**
* ASCII Art Banners for Tinker Tickets - Terminal Edition
*
* This file contains ASCII art banners and rendering functions
* for the retro terminal aesthetic redesign.
*/
// ASCII Art Banner Definitions
const ASCII_BANNERS = {
// Main large banner for desktop
main: `
╔══════════════════════════════════════════════════════════════════════════╗
║ ║
║ ████████╗██╗███╗ ██╗██╗ ██╗███████╗██████╗ ║
║ ╚══██╔══╝██║████╗ ██║██║ ██╔╝██╔════╝██╔══██╗ ║
║ ██║ ██║██╔██╗ ██║█████╔╝ █████╗ ██████╔╝ ║
║ ██║ ██║██║╚██╗██║██╔═██╗ ██╔══╝ ██╔══██╗ ║
║ ██║ ██║██║ ╚████║██║ ██╗███████╗██║ ██║ ║
║ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ║
║ ║
║ ████████╗██╗ ██████╗██╗ ██╗███████╗████████╗███████╗ ║
║ ╚══██╔══╝██║██╔════╝██║ ██╔╝██╔════╝╚══██╔══╝██╔════╝ ║
║ ██║ ██║██║ █████╔╝ █████╗ ██║ ███████╗ ║
║ ██║ ██║██║ ██╔═██╗ ██╔══╝ ██║ ╚════██║ ║
║ ██║ ██║╚██████╗██║ ██╗███████╗ ██║ ███████║ ║
║ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝ ╚═╝ ╚══════╝ ║
║ ║
║ >> RETRO TERMINAL TICKETING SYSTEM v1.0 << ║
║ ║
╚══════════════════════════════════════════════════════════════════════════╝
`,
// Compact version for smaller screens
compact: `
┌──────────────────────────────────────────────────────────┐
│ ▀█▀ █ █▄ █ █▄▀ █▀▀ █▀█ ▀█▀ █ █▀▀ █▄▀ █▀▀ ▀█▀ █▀ │
│ █ █ █ ▀█ █ █ ██▄ █▀▄ █ █ █▄▄ █ █ ██▄ █ ▄█ │
│ Terminal Ticketing System v1.0 │
└──────────────────────────────────────────────────────────┘
`,
// Minimal version for mobile
minimal: `
╔════════════════════════════╗
║ TINKER TICKETS v1.0 ║
╚════════════════════════════╝
`
};
/**
* Renders ASCII banner with optional typewriter effect
*
* @param {string} bannerId - ID of banner to render ('main', 'compact', or 'minimal')
* @param {string} containerSelector - CSS selector for container element
* @param {number} speed - Speed of typewriter effect in milliseconds (0 = instant)
* @param {boolean} addGlow - Whether to add text glow effect
*/
function renderASCIIBanner(bannerId, containerSelector, speed = 5, addGlow = true) {
const banner = ASCII_BANNERS[bannerId];
const container = document.querySelector(containerSelector);
if (!container || !banner) {
console.error('ASCII Banner: Container or banner not found', { bannerId, containerSelector });
return;
}
// Create pre element for ASCII art
const pre = document.createElement('pre');
pre.className = 'ascii-banner';
pre.style.margin = '0';
pre.style.fontFamily = 'var(--font-mono)';
pre.style.color = 'var(--terminal-green)';
if (addGlow) {
pre.style.textShadow = 'var(--glow-green)';
}
pre.style.fontSize = getBannerFontSize(bannerId);
pre.style.lineHeight = '1.2';
pre.style.whiteSpace = 'pre';
pre.style.overflow = 'visible';
pre.style.textAlign = 'center';
container.appendChild(pre);
// Instant render or typewriter effect
if (speed === 0) {
pre.textContent = banner;
} else {
renderWithTypewriter(pre, banner, speed);
}
}
/**
* Get appropriate font size for banner type
*
* @param {string} bannerId - Banner ID
* @returns {string} - CSS font size
*/
function getBannerFontSize(bannerId) {
const width = window.innerWidth;
if (bannerId === 'main') {
if (width < 768) return '0.4rem';
if (width < 1024) return '0.6rem';
return '0.8rem';
} else if (bannerId === 'compact') {
if (width < 768) return '0.6rem';
return '0.8rem';
} else {
return '0.8rem';
}
}
/**
* Renders text with typewriter effect
*
* @param {HTMLElement} element - Element to render into
* @param {string} text - Text to render
* @param {number} speed - Speed in milliseconds per character
*/
function renderWithTypewriter(element, text, speed) {
let index = 0;
const typeInterval = setInterval(() => {
element.textContent = text.substring(0, index);
index++;
if (index > text.length) {
clearInterval(typeInterval);
// Trigger completion event
const event = new CustomEvent('bannerComplete');
element.dispatchEvent(event);
}
}, speed);
}
/**
* Renders responsive banner based on screen size
*
* @param {string} containerSelector - CSS selector for container
* @param {number} speed - Typewriter speed (0 = instant)
*/
function renderResponsiveBanner(containerSelector, speed = 5) {
const width = window.innerWidth;
let bannerId;
if (width < 480) {
bannerId = 'minimal';
} else if (width < 1024) {
bannerId = 'compact';
} else {
bannerId = 'main';
}
renderASCIIBanner(bannerId, containerSelector, speed, true);
}
/**
* Animated welcome sequence
* Shows banner followed by a blinking cursor effect
*
* @param {string} containerSelector - CSS selector for container
*/
function animatedWelcome(containerSelector) {
const container = document.querySelector(containerSelector);
if (!container) return;
// Clear container
container.innerHTML = '';
// Render banner
renderResponsiveBanner(containerSelector, 3);
// Add blinking cursor after banner
const banner = container.querySelector('.ascii-banner');
if (banner) {
banner.addEventListener('bannerComplete', () => {
const cursor = document.createElement('span');
cursor.textContent = '█';
cursor.style.animation = 'blink-caret 0.75s step-end infinite';
cursor.style.marginLeft = '5px';
banner.appendChild(cursor);
});
}
}
// Export functions for use in other scripts
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
ASCII_BANNERS,
renderASCIIBanner,
renderResponsiveBanner,
animatedWelcome
};
}

View File

@@ -10,6 +10,7 @@
<title>Ticket Dashboard</title>
<link rel="icon" type="image/png" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/images/favicon.png">
<link rel="stylesheet" href="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/css/dashboard.css">
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/ascii-banner.js"></script>
<script src="<?php echo $GLOBALS['config']['ASSETS_URL']; ?>/js/dashboard.js"></script>
</head>
<body data-categories='<?php echo json_encode($categories); ?>' data-types='<?php echo json_encode($types); ?>'>
@@ -90,6 +91,16 @@
}
}
</style>
<!-- ASCII Banner Container -->
<div id="ascii-banner-container" style="margin: 2rem auto; text-align: center; max-width: 1200px;"></div>
<script>
// Render ASCII banner on page load with typewriter effect
document.addEventListener('DOMContentLoaded', function() {
renderResponsiveBanner('#ascii-banner-container', 3);
});
</script>
<div class="dashboard-header">
<h1>Ticket Dashboard</h1>
<button onclick="window.location.href='<?php echo $GLOBALS['config']['BASE_URL']; ?>/ticket/create'" class="btn create-ticket">New Ticket</button> </div>