diff --git a/map/app/middleware/rateLimiter.js b/map/app/middleware/rateLimiter.js index e94a5e6..e0f182f 100644 --- a/map/app/middleware/rateLimiter.js +++ b/map/app/middleware/rateLimiter.js @@ -11,7 +11,7 @@ const keyGenerator = (req) => { // General API rate limiter const apiLimiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes - max: 100, + max: 300, // Increased from 100 to 300 to accommodate auto-refresh and multiple users keyGenerator, standardHeaders: true, legacyHeaders: false, diff --git a/map/app/public/css/REFACTORING_SUMMARY.md b/map/app/public/css/REFACTORING_SUMMARY.md new file mode 100644 index 0000000..ca9826f --- /dev/null +++ b/map/app/public/css/REFACTORING_SUMMARY.md @@ -0,0 +1,63 @@ +# CSS Refactoring Summary + +## 🎯 Objective Completed +Successfully refactored the large `admin.css` file (3,011 lines) into 11 smaller, focused modules for better maintainability. + +## 📁 New Structure Created + +``` +/css/admin/ +├── variables.css (66 lines) - Theme configuration & CSS custom properties +├── layout.css (244 lines) - Admin container, sidebar, navigation components +├── forms.css (251 lines) - Form fields, buttons, and input styling +├── status-messages.css (189 lines) - Toast notifications & status indicators +├── user-management.css (219 lines) - User tables, roles, and management UI +├── walk-sheet.css (302 lines) - Walk sheet preview, QR codes, print styles +├── data-convert.css (215 lines) - CSV upload and data processing interface +├── nocodb-links.css (135 lines) - External database integration cards +├── cuts-shifts.css (197 lines) - Cuts and shifts management interface +├── modals.css (310 lines) - Modal dialogs and email composition +└── responsive.css (717 lines) - Mobile, tablet, and desktop breakpoints +``` + +## ✅ Benefits Achieved + +1. **Better Organization**: Styles logically grouped by functionality +2. **Improved Maintainability**: Each module is focused and self-contained +3. **Faster Development**: Easier to find and modify specific components +4. **Consistent Design**: Centralized variables ensure design consistency +5. **Easier Debugging**: Issues can be quickly traced to relevant modules +6. **Future-Proof**: Modular structure supports easier updates and expansion + +## 🔧 Technical Details + +- **Original File**: 3,011 lines in single `admin.css` +- **Refactored**: 11 modules totaling 2,845 lines + 16-line main import file +- **Backup**: Original file preserved as `admin.css.backup` +- **Compatibility**: No changes needed to existing HTML - refactoring is transparent +- **Import Structure**: Main file uses CSS `@import` for clean module loading + +## 📋 File Breakdown + +| Module | Lines | Purpose | +|--------|--------|---------| +| `variables.css` | 66 | CSS custom properties, theme colors, spacing | +| `layout.css` | 244 | Admin container, sidebar, navigation structure | +| `forms.css` | 251 | Form fields, buttons, validation states | +| `status-messages.css` | 189 | Notifications, progress bars, status icons | +| `user-management.css` | 219 | User tables, roles, volunteer management | +| `walk-sheet.css` | 302 | QR codes, printable layouts, form sections | +| `data-convert.css` | 215 | CSV upload, processing, results preview | +| `nocodb-links.css` | 135 | Database integration cards and status | +| `cuts-shifts.css` | 197 | Geographic cuts, shift scheduling | +| `modals.css` | 310 | Dialog boxes, email composition | +| `responsive.css` | 717 | Mobile and tablet responsive design | + +## 🚀 Next Steps + +The refactoring is complete and ready for use. Consider these future enhancements: + +- **Performance**: Implement critical CSS loading +- **Documentation**: Add component usage examples +- **Testing**: Visual regression testing for UI components +- **Design System**: Expand variables into full design tokens diff --git a/map/app/public/css/admin.css b/map/app/public/css/admin.css index 5fa89dc..d1f17d5 100644 --- a/map/app/public/css/admin.css +++ b/map/app/public/css/admin.css @@ -1,3010 +1,18 @@ -/* Admin Panel Specific Styles */ +/* Admin Panel CSS - Main Entry Point */ +/* Modular CSS architecture for better maintainability */ + +/* Import core admin modules */ +@import url("admin/variables.css"); +@import url("admin/layout.css"); +@import url("admin/forms.css"); +@import url("admin/status-messages.css"); +@import url("admin/user-management.css"); +@import url("admin/walk-sheet.css"); +@import url("admin/data-convert.css"); +@import url("admin/nocodb-links.css"); +@import url("admin/cuts-shifts.css"); +@import url("admin/modals.css"); +@import url("admin/responsive.css"); + +/* Legacy import compatibility */ @import url("modules/dashboard.css"); - -.admin-container { - display: flex; - height: calc(100vh - var(--header-height)); - height: calc(var(--app-height) - var(--header-height)); - background-color: #f5f5f5; - width: 100%; - max-width: 100vw; - overflow: hidden; -} - -.admin-sidebar { - width: 250px; - min-width: 250px; - max-width: 250px; - background-color: white; - border-right: 1px solid #e0e0e0; - padding: 20px; - box-sizing: border-box; - flex-shrink: 0; -} - -.admin-sidebar h2 { - font-size: 18px; - margin-bottom: 20px; - color: var(--dark-color); -} - -.admin-nav { - display: flex; - flex-direction: column; - gap: 5px; -} - -.admin-nav a { - padding: 10px 15px; - color: var(--dark-color); - text-decoration: none; - border-radius: var(--border-radius); - transition: var(--transition); -} - -.admin-nav a:hover { - background-color: var(--light-color); -} - -.admin-nav a.active { - background-color: var(--primary-color); - color: white; -} - -.admin-content { - flex: 1; - padding: 30px; - overflow-y: auto; - overflow-x: hidden; - width: 100%; - min-width: 0; /* Allow flex item to shrink */ - box-sizing: border-box; - max-height: calc(100vh - var(--header-height)); - max-height: calc(var(--app-height) - var(--header-height)); -} - -.admin-section { - background-color: white; - border-radius: var(--border-radius); - padding: 30px; - box-shadow: 0 2px 4px rgba(0,0,0,0.1); - width: 100%; - max-width: 100%; - box-sizing: border-box; -} - -.admin-section h2 { - margin-bottom: 15px; - color: var(--dark-color); -} - -.admin-section p { - color: #666; - margin-bottom: 25px; -} - -.admin-map-container { - display: grid; - grid-template-columns: 1fr 300px; - gap: 20px; - margin-top: 20px; -} - -.admin-map { - height: 500px; - border-radius: var(--border-radius); - border: 1px solid #ddd; -} - -.location-controls { - padding: 20px; - background-color: #f9f9f9; - border-radius: var(--border-radius); -} - -.location-controls .form-group { - margin-bottom: 15px; -} - -.location-controls .form-actions { - display: flex; - flex-direction: column; - gap: 10px; - margin-top: 20px; -} - -.help-text { - margin-top: 20px; - padding: 15px; - background-color: #e3f2fd; - border-radius: var(--border-radius); - font-size: 14px; -} - -.help-text p { - margin: 0; - color: #1976d2; -} - -.admin-info { - display: flex; - align-items: center; - gap: 10px; - color: rgba(255,255,255,0.9); - font-size: 14px; -} - -/* Form styles */ -.form-group { - margin-bottom: 15px; -} - -.form-group label { - display: block; - margin-bottom: 5px; - font-weight: 500; - color: var(--dark-color); -} - -.form-group input { - width: 100%; - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: var(--border-radius); - font-size: 14px; - transition: border-color 0.2s; -} - -.form-group input:focus { - outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); -} - -/* Button styles */ -.btn { - padding: 8px 16px; - border: none; - border-radius: var(--border-radius); - font-size: 14px; - font-weight: 500; - cursor: pointer; - text-decoration: none; - display: inline-flex; - align-items: center; - gap: 5px; - transition: all 0.2s; -} - -.btn-primary { - background-color: var(--primary-color); - color: white; -} - -.btn-primary:hover { - background-color: #45a049; - transform: translateY(-1px); -} - -.btn-secondary { - background-color: #6c757d; - color: white; -} - -.btn-secondary:hover { - background-color: #5a6268; - transform: translateY(-1px); -} - -.btn-sm { - padding: 6px 12px; - font-size: 13px; -} - -/* Status messages */ -.status-container { - position: fixed; - top: 20px; - right: 20px; - z-index: 12300; - max-width: 400px; - pointer-events: none; /* Allow clicks to pass through container */ -} - -/* Mobile status container - center horizontally at top */ -@media (max-width: 768px) { - .status-container { - left: 10px; - right: 10px; - top: 10px; - width: auto; - max-width: 100%; - transform: none; - display: flex; - flex-direction: column; - align-items: center; - } - - .status-message { - width: auto; - max-width: 90vw; - box-sizing: border-box; - font-size: 13px; - text-align: center; - } -} - -/* Very small screens - adjust positioning and sizing */ -@media (max-width: 480px) { - .status-container { - top: 70px; /* Smaller header on mobile */ - max-width: calc(100vw - 20px); - min-width: 260px; - } -} - -/* Extra small screens */ -@media (max-width: 360px) { - .status-container { - top: 65px; - max-width: calc(100vw - 15px); - min-width: auto; - } - - .status-message { - padding: 10px 12px; - font-size: 13px; - } -} - -.status-message { - padding: 12px 16px; - margin-bottom: 10px; - border-radius: var(--border-radius); - font-size: 14px; - animation: slideIn 0.3s ease-out; - pointer-events: auto; /* Allow interaction with individual messages */ - cursor: pointer; /* Show it's clickable for dismissal */ -} - -.status-message.success { - background-color: #d4edda; - color: #155724; - border: 1px solid #c3e6cb; -} - -.status-message.error { - background-color: #f8d7da; - color: #721c24; - border: 1px solid #f5c6cb; -} - -.status-message.info { - background-color: #cce7ff; - color: #004085; - border: 1px solid #b3d7ff; -} - -@keyframes slideIn { - from { - transform: translateX(100%); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } -} - -/* Walk Sheet Styles */ -.walk-sheet-container { - display: grid; - grid-template-columns: 2fr 3fr; - gap: 30px; - margin-top: 20px; - align-items: flex-start; -} - -.walk-sheet-config { - background-color: #f9f9f9; - padding: 20px; - border-radius: var(--border-radius); -} - -.walk-sheet-config h3 { - font-size: 16px; - margin-bottom: 15px; - color: var(--dark-color); -} - -.qr-code-group { - background-color: white; - padding: 15px; - border-radius: var(--border-radius); - margin-bottom: 15px; - border: 1px solid #e0e0e0; - position: relative; -} - -.qr-code-group h4 { - font-size: 14px; - margin-bottom: 10px; - color: #666; -} - -.form-row { - display: grid; - grid-template-columns: 2fr 1fr; - gap: 10px; -} - -.help-text-inline { - font-size: 14px; - color: #666; - margin-bottom: 15px; -} - -/* Walk Sheet Preview */ -.walk-sheet-preview { - background-color: #f5f5f5; - padding: 30px; - border-radius: var(--border-radius); - box-shadow: 0 4px 24px rgba(0,0,0,0.10); - min-width: 350px; - max-width: 100%; - margin: 0 auto; - min-height: 950px; /* Ensure container is tall enough */ - display: flex; - flex-direction: column; - align-items: center; - overflow: auto; -} - -.walk-sheet-preview h3 { - font-size: 16px; - margin-bottom: 10px; - color: var(--dark-color); -} - -.preview-controls { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 15px; -} - -.preview-info { - font-size: 12px; - color: #666; -} - -/* Walk Sheet Page (8.5 x 11 actual size) */ -.walk-sheet-page { - /* Actual paper size in pixels at 96 DPI (standard screen resolution) */ - width: 816px; /* 8.5 inches * 96 DPI */ - height: 1056px; /* 11 inches * 96 DPI */ - background: white; - box-shadow: 0 4px 20px rgba(0,0,0,0.15); - padding: 48px; /* 0.5 inch margins at 96 DPI */ - margin: 0 auto; - overflow: hidden; - font-size: 12pt; /* Standard document font size */ - line-height: 1.6; - position: relative; - transform: none; /* No scaling by default */ - box-sizing: border-box; -} - -/* Walk Sheet Print Styles */ -@media print { - body * { - visibility: hidden; - } - - .walk-sheet-page, .walk-sheet-page * { - visibility: visible; - } - - .walk-sheet-page { - position: fixed; - left: 0; - top: 0; - width: 8.5in; - height: 11in; - max-width: none; - padding: 0.5in; /* 0.5 inch margins */ - margin: 0; - box-shadow: none; - font-size: 12pt; - page-break-after: always; - } - - /* Ensure QR codes print properly */ - .ws-qr-code img { - width: 100px !important; - height: 100px !important; - -webkit-print-color-adjust: exact; - print-color-adjust: exact; - } -} - -/* Remove the preview scaling - show at actual size */ -.walk-sheet-preview .walk-sheet-page { - transform: none; - transform-origin: top center; - margin-bottom: 0; - border-radius: 0; /* Remove rounded corners to look more like paper */ -} - -/* Walk Sheet Content Styles - adjusted for actual size */ -.ws-header { - text-align: center; - margin-bottom: 30px; /* Reduced from 40px */ - border-bottom: 3px solid #333; - padding-bottom: 15px; /* Reduced from 20px */ -} - -.ws-title { - font-size: 24pt; /* Reduced from 28pt */ - font-weight: bold; - margin: 0; - color: #000; -} - -.ws-subtitle { - font-size: 12pt; /* Reduced from 14pt */ - color: #444; - margin: 8px 0 0 0; /* Reduced from 10px */ -} - -.ws-qr-section { - display: flex; - justify-content: space-around; - margin: 30px 0; /* Reduced from 40px */ - padding: 20px; /* Reduced from 30px */ - background-color: #f5f5f5; - border-radius: 8px; - border: 1px solid #ddd; -} - -.ws-qr-item { - text-align: center; -} - -.ws-qr-code { - margin-bottom: 5px; -} - -.ws-qr-code img { - display: block; - margin: 0 auto; - width: 120px; /* Reduced from 140px */ - height: 120px; - image-rendering: crisp-edges; - image-rendering: -webkit-crisp-edges; - image-rendering: pixelated; - image-rendering: -moz-crisp-edges; -} - -.ws-qr-code canvas { - display: block; - margin: 0 auto; - image-rendering: crisp-edges; - image-rendering: -webkit-crisp-edges; - image-rendering: pixelated; - image-rendering: -moz-crisp-edges; -} - -.ws-qr-label { - font-size: 11pt; - font-weight: bold; - margin-top: 10px; - color: #333; -} - -.ws-form-section { - margin-top: 30px; /* Reduced from 40px */ -} - -.ws-form-row { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 20px; - margin-bottom: 18px; /* Reduced from 20px */ -} - -.ws-form-group { - border-bottom: 2px solid #ccc; - padding-bottom: 8px; -} - -.ws-form-label { - font-size: 10pt; - color: #555; - display: block; - margin-bottom: 5px; - font-weight: 500; -} - -.ws-form-field { - height: 30px; - background-color: #fafafa; - border: 1px solid #eee; -} - -/* Special field styles for circles */ -.ws-form-field.circles { - height: auto; - background: none; - border: none; - display: flex; - gap: 15px; - align-items: center; - padding: 5px 0; -} - -.ws-circle-option { - display: flex; - align-items: center; - gap: 5px; - font-size: 11pt; -} - -.ws-circle { - width: 24px; - height: 24px; - border: 2px solid #333; - border-radius: 50%; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 10pt; - font-weight: 500; - background-color: white; -} - -.ws-notes-section { - margin-top: 30px; /* Reduced from 40px */ -} - -.ws-notes-label { - font-size: 11pt; - font-weight: bold; - margin-bottom: 10px; - color: #333; -} - -.ws-notes-area { - width: 100%; - height: 80px; /* Reduced from 100px */ - border: 2px solid #ccc; - background-color: #fafafa; - border-radius: 4px; -} - -.ws-footer { - position: absolute; - bottom: 48px; /* Match padding */ - left: 48px; - right: 48px; - text-align: center; - font-size: 10pt; - color: #666; - padding-top: 15px; /* Reduced from 20px */ - border-top: 1px solid #ccc; - max-height: 80px; /* Ensure footer has enough space */ - overflow: hidden; - line-height: 1.4; -} - -/* QR Code Generation Status */ -.qr-code-group.generating::after { - content: 'Generating QR Code...'; - position: absolute; - top: 0; - right: 0; - font-size: 12px; - color: var(--primary-color); - background: white; - padding: 2px 8px; - border-radius: 3px; - box-shadow: 0 1px 3px rgba(0,0,0,0.1); -} - -/* Loading state for save button */ -.btn:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -/* QR code stored indicator */ -.qr-status { - font-size: 12px; - color: var(--success-color); - margin-top: 5px; -} - -.qr-status.stored { - color: var(--success-color); -} - -.qr-status.pending { - color: var(--warning-color); -} - -/* User Management Styles */ -.users-admin-container { - display: grid; - grid-template-columns: 1fr 2fr; - gap: 30px; - margin-top: 20px; - width: 100%; - box-sizing: border-box; - overflow: hidden; -} - -/* Data Convert Styles */ -.data-convert-container { - max-width: 1000px; -} - -.csv-requirements { - background: #f8f9fa; - border: 1px solid #dee2e6; - border-radius: 8px; - padding: 20px; - margin: 20px 0; -} - -.csv-requirements h4 { - color: #495057; - margin-bottom: 15px; - border-bottom: 2px solid #e9ecef; - padding-bottom: 10px; -} - -.requirements-section { - margin-bottom: 20px; -} - -.requirements-section:last-child { - margin-bottom: 0; -} - -.requirements-section h5 { - color: #6c757d; - margin-bottom: 10px; - font-weight: 600; -} - -.field-mapping-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 15px; - margin-top: 10px; -} - -.field-group { - background: white; - border: 1px solid #e9ecef; - border-radius: 6px; - padding: 12px; -} - -.field-group strong { - color: #495057; - display: block; - margin-bottom: 8px; - font-size: 14px; -} - -.field-group ul { - margin: 0; - padding-left: 15px; - list-style-type: disc; -} - -.field-group li { - font-family: 'Courier New', monospace; - font-size: 12px; - color: #6c757d; - margin-bottom: 3px; -} - -.csv-example { - background: #2d3748; - color: #e2e8f0; - padding: 15px; - border-radius: 6px; - font-family: 'Courier New', monospace; - font-size: 12px; - overflow-x: auto; - margin-top: 10px; - white-space: pre; -} - -.requirements-section ul { - margin: 10px 0; - padding-left: 20px; -} - -.requirements-section > ul > li { - margin-bottom: 5px; - color: #495057; -} - -.user-form, -.users-list { - background: white; - border-radius: var(--border-radius); - padding: 25px; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - border: 1px solid #e0e0e0; - width: 100%; - box-sizing: border-box; -} - -.users-list { - overflow-x: auto; /* Allow horizontal scrolling if needed */ - min-width: 0; /* Allow shrinking */ -} - -.user-form h3, -.users-list h3 { - margin-bottom: 20px; - color: var(--dark-color); - border-bottom: 2px solid var(--primary-color); - padding-bottom: 10px; - font-size: 18px; -} - -.users-table { - width: 100%; - border-collapse: collapse; - margin-top: 15px; - font-size: 14px; - table-layout: auto; - min-width: 600px; /* Ensure minimum width for readability on desktop */ - box-sizing: border-box; -} - -.users-table th, -.users-table td { - padding: 12px; - text-align: left; - border-bottom: 1px solid #e0e0e0; -} - -.users-table th { - background-color: #f8f9fa; - font-weight: 600; - color: var(--dark-color); - font-size: 13px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.users-table tbody tr { - transition: background-color 0.2s; -} - -.users-table tbody tr:hover { - background-color: #f8f9fa; -} - -.user-role { - padding: 4px 8px; - border-radius: 12px; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - display: inline-block; - text-align: center; - min-width: 45px; - line-height: 1.2; -} - -.user-role.admin { - background-color: #dc3545; - color: white; -} - -.user-role.user { - background-color: #28a745; - color: white; -} - -.user-role.temp { - background-color: #ff9800; - color: white; -} - -.expiration-info { - display: inline-block; - margin-left: 8px; - font-size: 0.8em; - color: #666; - background: #f5f5f5; - padding: 2px 6px; - border-radius: 3px; -} - -.expiration-warning { - color: #ff5722; - font-weight: bold; -} - -.expires-soon { - background-color: #fff3cd; - border-left: 4px solid #ffc107; -} - -.expired { - background-color: #f8d7da; - border-left: 4px solid #dc3545; - opacity: 0.7; -} - -.help-text { - font-size: 0.85em; - color: #666; - margin-top: 4px; - display: block; -} - -.user-actions { - display: flex; - gap: 8px; -} - -.user-actions .btn { - padding: 6px 12px; - font-size: 12px; - border-radius: var(--border-radius); - font-weight: 500; -} - -.btn-danger { - background-color: #dc3545; - color: white; - border: 1px solid #dc3545; - transition: var(--transition); -} - -.btn-danger:hover { - background-color: #c82333; - border-color: #c82333; - transform: translateY(-1px); -} - -.loading-message, -.empty-message { - text-align: center; - padding: 40px 20px; - color: #666; - font-style: italic; - background-color: #f8f9fa; - border-radius: var(--border-radius); - margin-top: 15px; -} - -.loading-message { - background-color: #e3f2fd; - color: #1976d2; -} - -/* User form specific styles */ -.user-form .form-group { - margin-bottom: 20px; -} - -.user-form .form-group label { - display: block; - margin-bottom: 8px; - font-weight: 600; - color: var(--dark-color); - font-size: 14px; -} - -.user-form .form-group label input[type="checkbox"] { - margin-right: 8px; - transform: scale(1.1); -} - -.user-form input[type="email"], -.user-form input[type="password"], -.user-form input[type="text"] { - width: 100%; - padding: 12px; - border: 2px solid #ddd; - border-radius: var(--border-radius); - font-size: 14px; - transition: var(--transition); - background-color: #fafafa; -} - -.user-form input:focus { - outline: none; - border-color: var(--primary-color); - background-color: white; - box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); -} - -.user-form .form-actions { - display: flex; - gap: 10px; - margin-top: 25px; -} - -.user-form .form-actions .btn { - flex: 1; - padding: 12px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - font-size: 13px; -} - -/* Responsive design for user management */ -@media (max-width: 768px) { - .users-admin-container { - grid-template-columns: 1fr; - gap: 25px; /* Increased gap for better separation */ - } - - .user-form, - .users-list { - padding: 25px; /* Back to desktop padding for better comfort */ - width: 100%; - box-sizing: border-box; - } - - .users-list { - overflow-x: auto; - width: 100%; - box-sizing: border-box; - } - - /* Data Convert responsive styles */ - .field-mapping-grid { - grid-template-columns: 1fr; - } - - .csv-example { - font-size: 10px; - padding: 10px; - } - - .csv-requirements { - padding: 15px; - } - - .users-table { - font-size: 14px; /* Match desktop font size for better readability */ - min-width: auto; /* Remove minimum width constraint on mobile */ - width: 100%; - table-layout: auto; /* Changed from fixed to auto for better text flow */ - border-collapse: collapse; - display: block; - } - - .users-table thead { - display: none; - } - - .users-table tbody, - .users-table tr, - .users-table td { - display: block; - width: 100% !important; - box-sizing: border-box; - } - - .users-table tr { - margin-bottom: 15px; - border: 1px solid #e0e0e0; - border-radius: var(--border-radius); - padding: 10px; - } - - .users-table td { - padding: 8px 0; - border: none; - display: flex; - align-items: center; - word-wrap: break-word; - overflow-wrap: break-word; - } - - .users-table td::before { - content: attr(data-label); - font-weight: bold; - width: 80px; - min-width: 80px; - margin-right: 10px; - color: #555; - } - - /* Adjust column widths for mobile - less aggressive width constraints */ - .users-table th:nth-child(1), - .users-table td:nth-child(1) { /* Email */ - width: 35%; /* Increased back to give more space for email */ - } - - .users-table th:nth-child(2), - .users-table td:nth-child(2) { /* Name */ - width: 25%; - } - - .users-table th:nth-child(3), - .users-table td:nth-child(3) { /* Admin */ - width: 15%; /* Reduced back to original for more space elsewhere */ - } - - .users-table th:nth-child(4), - .users-table td:nth-child(4) { /* Created */ - width: 0%; - display: none; /* Hide created date on mobile to save space */ - } - - .users-table th:nth-child(5), - .users-table td:nth-child(5) { /* Actions */ - width: 25%; /* Reduced back to give more balanced layout */ - } - - .user-actions { - flex-direction: column; - gap: 8px; /* Increased for better spacing */ - width: 100%; - } - - .user-actions .btn { - font-size: 12px; /* Increased to match more with desktop */ - padding: 8px 10px; /* More comfortable padding */ - width: 100%; - text-align: center; - min-height: 36px; /* Increased minimum height for better touch targets */ - line-height: 1.3; - } - - .user-form .form-actions { - flex-direction: column; - } - - /* Admin container mobile layout */ - .admin-container { - flex-direction: column; - } - - .admin-sidebar { - width: 100%; - border-right: none; - border-bottom: 1px solid #e0e0e0; - padding: 15px; - } - - .admin-nav { - flex-direction: row; - overflow-x: auto; - gap: 10px; - padding-bottom: 10px; - } - - .admin-nav a { - white-space: nowrap; - min-width: auto; - padding: 8px 12px; - font-size: 14px; - } - - .header .header-actions { - display: flex !important; - gap: 10px; - flex-wrap: wrap; - } - - .header .header-actions .btn { - padding: 6px 10px; - font-size: 13px; - } - - .admin-info { - font-size: 12px; - } - - .admin-map-container { - grid-template-columns: 1fr; - gap: 15px; - } - - .admin-map { - height: 250px; - } - - .admin-content { - padding: 15px; - } - - .admin-section { - padding: 15px; - } - - .form-row { - grid-template-columns: 1fr; - } - - /* Shifts admin mobile */ - .shifts-admin-container { - grid-template-columns: 1fr; - gap: 20px; - } - - .shift-admin-item { - flex-direction: column; - align-items: flex-start; - gap: 10px; - } - - .shift-actions { - width: 100%; - justify-content: flex-end; - } - - /* Walk sheet container mobile */ - .walk-sheet-container { - display: flex !important; - flex-direction: column !important; - gap: 20px; - } - - .walk-sheet-config { - order: 1 !important; - margin-bottom: 20px; - } - - /* Walk sheet mobile */ - .walk-sheet-preview { - min-width: 0; - max-width: 100%; - width: 100%; - min-height: 500px; - padding: 10px; - overflow: hidden; /* Change from overflow-x: hidden to overflow: hidden */ - box-sizing: border-box; - display: flex; - flex-direction: column; - align-items: center; - } - - /* Container for the scaled page */ - .walk-sheet-preview #walk-sheet-preview-content { - transform: scale(0.35); - transform-origin: top center; - margin: 0 auto; - width: 816px; - max-width: none; - margin-bottom: -600px; - position: relative; - left: 50%; - margin-left: -408px; /* Half of 816px to center it */ - } - - /* Status container mobile */ - .status-container { - top: 10px; - right: 10px; - left: 10px; - max-width: none; - display: flex; - flex-direction: column; - align-items: center; - } - - .status-message { - font-size: 13px; - padding: 10px 12px; - max-width: 90%; - text-align: center; - width: fit-content; - margin: 0 auto 10px auto; - } -} - -@media (max-width: 480px) { - .users-table { - font-size: 13px; /* Close to desktop, but slightly smaller for small screens */ - min-width: auto; - } - - .users-table th, - .users-table td { - padding: 10px 6px; /* Comfortable padding, not too cramped */ - font-size: 13px; /* Match table font size */ - line-height: 1.4; - } - - /* Adjust column widths for very small screens */ - .users-table th:nth-child(1), - .users-table td:nth-child(1) { /* Email */ - width: 35%; /* Reduced from 40% */ - word-break: break-all; - } - - .users-table th:nth-child(2), - .users-table td:nth-child(2) { /* Name */ - width: 25%; /* Reduced from 30% */ - } - - .users-table th:nth-child(3), - .users-table td:nth-child(3) { /* Admin */ - width: 18%; /* Increased from 15% */ - } - - .users-table th:nth-child(5), - .users-table td:nth-child(5) { /* Actions */ - width: 22%; /* Increased from 15% */ - } - - .user-role { - font-size: 10px; /* Increased from 9px for better readability */ - padding: 4px 6px; /* More comfortable padding */ - line-height: 1.2; - } - - .user-actions .btn { - font-size: 11px; /* Increased from 9px */ - padding: 6px 8px; /* More comfortable padding */ - border-radius: 4px; /* Slightly larger border radius */ - min-height: 32px; /* Comfortable touch target */ - line-height: 1.3; - } - - .user-form input[type="email"], - .user-form input[type="password"], - .user-form input[type="text"] { - padding: 10px; - font-size: 13px; - } - - /* Very small screen adjustments */ - .admin-content { - padding: 10px; - } - - .admin-section { - padding: 10px; - } - - .admin-sidebar { - padding: 10px; - } - - .admin-nav a { - padding: 6px 10px; - font-size: 13px; - } - - .header .header-actions .btn { - padding: 5px 8px; - font-size: 12px; - } - - .admin-info { - display: none; /* Hide on very small screens */ - } - - .admin-map { - height: 200px; - } - - /* Walk sheet very small screens */ - .walk-sheet-preview { - padding: 10px; - width: 100%; - display: flex; - justify-content: center; - align-items: flex-start; - } - - .walk-sheet-preview .walk-sheet-page { - transform: scale(0.25); - transform-origin: center top; - margin-bottom: -750px; - margin-left: auto; - margin-right: auto; - } - - /* Form adjustments */ - .form-group input, - .form-group select { - font-size: 16px; /* Prevent zoom on iOS */ - } - - .btn { - min-height: 44px; /* Touch-friendly button size */ - padding: 10px 16px; - } - - .btn-sm { - min-height: 36px; - padding: 8px 12px; - } - - /* User management specific mobile adjustments */ - .user-container { - padding: 0.5rem; - } - - .user-profile { - padding: 1rem; - } - - .users-table th { - font-size: 10px; - } - - .user-actions .btn { - font-size: 10px; - padding: 3px 6px; - min-height: 32px; - } -} - -/* For very large screens, show at full size without scaling */ -@media (min-width: 1600px) { - .walk-sheet-container { - gap: 40px; - grid-template-columns: 1fr 2fr; /* Give more space to preview */ - } - - .walk-sheet-preview { - max-width: 100%; - min-height: 1100px; - padding: 40px; - } - - .walk-sheet-preview .walk-sheet-page { - transform: none; /* Show at actual size */ - } -} - -/* Container adjustments for actual-size preview */ -.walk-sheet-container { - display: grid; - grid-template-columns: minmax(300px, 400px) 1fr; /* Adjust proportions */ - gap: 30px; - margin-top: 20px; - align-items: flex-start; -} - -/* CSS Variables (define these in style.css if not already defined) */ -:root { - --primary-color: #4CAF50; - --dark-color: #333; - --light-color: #f5f5f5; - --border-radius: 6px; - --transition: all 0.2s ease; - --header-height: 60px; -} - -/* Crosshair styling */ -.crosshair { - pointer-events: none; - z-index: 1000; -} - -.crosshair div { - border-radius: 1px; - opacity: 0.8; -} - -/* Admin map styling */ -.admin-map { - position: relative; - cursor: crosshair; -} - -.admin-map .leaflet-container { - cursor: crosshair; -} - -/* Shifts Admin Styles */ -.shifts-admin-container { - display: grid; - grid-template-columns: 400px 1fr; - gap: 30px; -} - -.shift-form { - background: white; - padding: 20px; - border-radius: var(--border-radius); - border: 1px solid #e0e0e0; -} - -.shifts-list { - background: white; - padding: 20px; - border-radius: var(--border-radius); - border: 1px solid #e0e0e0; -} - -.shift-admin-item { - border: 1px solid #e0e0e0; - border-radius: var(--border-radius); - padding: 15px; - margin-bottom: 15px; - display: flex; - justify-content: space-between; - align-items: center; -} - -.shift-admin-item h4 { - margin: 0 0 10px 0; -} - -.shift-admin-item p { - margin: 5px 0; - color: var(--secondary-color); -} - -.status-open { - color: var(--success-color); - font-weight: bold; -} - -.status-full { - color: var(--warning-color); - font-weight: bold; -} - -.status-cancelled { - color: var(--danger-color); - font-weight: bold; -} - -.shift-actions { - display: flex; - gap: 10px; -} - -@media (max-width: 768px) { - .shifts-admin-container { - grid-template-columns: 1fr; - } -} - -/* Additional mobile styles for better responsiveness */ -@media (max-width: 1200px) { - .walk-sheet-container { - display: flex !important; - flex-direction: column !important; - gap: 20px; - } - - .walk-sheet-config { - order: 1 !important; - margin-bottom: 20px; - } - - .walk-sheet-preview { - order: 2 !important; - padding: 20px; - min-height: auto; - max-width: 100vw; - overflow-x: auto; - display: flex; - justify-content: center; - align-items: flex-start; - } - - .walk-sheet-preview .walk-sheet-page { - transform: scale(0.75); - transform-origin: center top; - margin-bottom: -200px; - max-width: 100%; - margin-left: auto; - margin-right: auto; - } -} - -@media (max-width: 1000px) { - .walk-sheet-preview .walk-sheet-page { - transform: scale(0.5); - transform-origin: center top; - margin-bottom: -400px; - margin-left: auto; - margin-right: auto; - } -} - -/* Tablet and mobile header adjustments */ -@media (max-width: 1024px) { - .header { - padding: 10px 15px; - } - - .header h1 { - font-size: 20px; - } - - .header .header-actions { - gap: 8px; - } -} - -/* Responsive Table Styles for Mobile */ -@media (max-width: 640px) { - .users-table { - border: 0; - } - - .users-table thead { - border: none; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; - } - - .users-table tr { - border-bottom: 3px solid #ddd; - display: block; - margin-bottom: 10px; - padding: 10px; - background: #f8f9fa; - border-radius: 6px; - } - - .users-table td { - border: none; - border-bottom: 1px solid #eee; - display: block; - font-size: 13px; - text-align: right; - padding: 8px 10px; - } - - .users-table td::before { - content: attr(data-label); - float: left; - font-weight: bold; - text-transform: uppercase; - font-size: 11px; - color: #666; - } - - .users-table td:last-child { - border-bottom: 0; - } - - .user-actions { - justify-content: flex-end; - margin-top: 8px; - } - - .user-actions .btn { - width: auto !important; - min-width: 80px; - flex: none; - } -} - -/* Enhanced mobile form styling */ -@media (max-width: 480px) { - .user-form input[type="email"], - .user-form input[type="password"], - .user-form input[type="text"] { - font-size: 16px; /* Prevent zoom on iOS */ - padding: 12px; - } - - .user-form .form-group label { - font-size: 14px; - } - - .user-form .form-actions .btn { - width: 100%; - margin-bottom: 8px; - } - - .users-table td { - font-size: 12px; - padding: 6px 8px; - } - - .users-table td::before { - font-size: 10px; - } -} - -/* Mobile Menu Toggle */ -.mobile-menu-toggle { - display: none; - background: none; - border: none; - padding: 8px; - cursor: pointer; - position: relative; - width: 40px; - height: 40px; -} - -.mobile-menu-toggle span { - display: block; - width: 24px; - height: 3px; - background: white; - margin: 5px auto; - transition: all 0.3s ease; - border-radius: 2px; -} - -.mobile-menu-toggle.active span:nth-child(1) { - transform: rotate(45deg) translate(5px, 5px); -} - -.mobile-menu-toggle.active span:nth-child(2) { - opacity: 0; -} - -.mobile-menu-toggle.active span:nth-child(3) { - transform: rotate(-45deg) translate(7px, -6px); -} - -/* Sidebar Header (mobile) */ -.sidebar-header { - display: none; - justify-content: space-between; - align-items: center; - padding-bottom: 20px; - border-bottom: 1px solid #e0e0e0; - margin-bottom: 20px; -} - -.close-sidebar { - background: none; - border: none; - font-size: 24px; - cursor: pointer; - color: var(--dark-color); - padding: 0; - width: 30px; - height: 30px; - display: flex; - align-items: center; - justify-content: center; -} - -/* Sidebar Footer (mobile) */ -.sidebar-footer { - display: none; - margin-top: auto; - padding-top: 20px; - border-top: 1px solid #e0e0e0; -} - -.mobile-admin-info { - margin-top: 15px; - font-size: 14px; - color: #666; - text-align: center; -} - -/* Navigation Icons */ -.nav-icon { - margin-right: 10px; - font-size: 18px; -} - -/* Utility Classes */ -.desktop-only { - display: flex; -} - -.mobile-only { - display: none; -} - -.btn-block { - width: 100%; - justify-content: center; -} - -/* Mobile Styles */ -@media (max-width: 768px) { - /* Show mobile menu toggle */ - .mobile-menu-toggle { - display: block; - } - - /* Header adjustments */ - .header { - padding: 10px 15px; - display: flex; - align-items: center; - gap: 15px; - } - - .header h1 { - flex: 1; - text-align: center; - margin: 0; - font-size: 18px; - } - - /* Hide desktop elements */ - .desktop-only { - display: none !important; - } - - .mobile-only { - display: flex; - } - - /* Admin container becomes full width */ - .admin-container { - flex-direction: column; - height: calc(100vh - 50px); /* Fallback for older browsers */ - height: calc(var(--app-height) - 50px); /* Reduced header height */ - } - - /* Sidebar as overlay */ - .admin-sidebar { - position: fixed; - top: 0; - left: -280px; - width: 280px; - height: 100vh; /* Fallback for older browsers */ - height: var(--app-height); - background: white; - z-index: 12400; - transition: left 0.3s ease; - box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1); - display: flex; - flex-direction: column; - padding: 20px; - } - - .admin-sidebar.active { - left: 0; - } - - /* Show sidebar header and footer on mobile */ - .sidebar-header { - display: flex; - } - - .sidebar-footer { - display: block; - } - - /* Admin nav mobile styling */ - .admin-nav { - flex: 1; - display: flex; - flex-direction: column; - gap: 8px; - margin: 20px 0; - } - - .admin-nav a { - display: flex; - align-items: center; - padding: 10px 12px; - font-size: 14px; - border-radius: 6px; - background-color: #f5f5f5; - border: 1px solid #e0e0e0; - transition: all 0.2s ease; - } - - .admin-nav a:active { - background-color: #e0e0e0; - transform: scale(0.98); - } - - .admin-nav a.active { - background-color: var(--primary-color); - color: white; - border-color: var(--primary-color); - } - - .admin-nav a:hover { - background-color: #ebebeb; - border-color: #d0d0d0; - } - - .admin-nav a.active:hover { - background-color: #0056b3; - border-color: #0056b3; - } - - /* Smaller nav icons and text */ - .nav-icon { - font-size: 16px; - margin-right: 8px; - flex-shrink: 0; - } - - .nav-text { - font-size: 14px; - font-weight: 500; - } - - /* Compact sidebar header */ - .sidebar-header { - display: flex; - padding-bottom: 15px; - margin-bottom: 0; - } - - .sidebar-header h2 { - font-size: 18px; - margin: 0; - } - - /* Compact sidebar footer */ - .sidebar-footer { - display: block; - margin-top: auto; - padding-top: 15px; - border-top: 1px solid #e0e0e0; - } - - .sidebar-footer .btn { - padding: 10px 12px; - font-size: 14px; - } - - .mobile-admin-info { - margin-top: 10px; - font-size: 13px; - color: #666; - text-align: center; - } - - /* Ensure sidebar fits content without scroll */ - .admin-sidebar { - position: fixed; - top: 0; - left: -280px; - width: 280px; - height: 100vh; - background: white; - z-index: 12400; - transition: left 0.3s ease; - box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1); - display: flex; - flex-direction: column; - padding: 15px; - overflow-y: auto; /* Just in case for very small screens */ - } -} - -/* Small mobile devices */ -@media (max-width: 480px) { - .admin-sidebar { - width: 260px; - left: -260px; - padding: 12px; - } - - .admin-nav { - gap: 6px; - margin: 15px 0; - } - - .admin-nav a { - padding: 8px 10px; - font-size: 13px; - } - - .nav-icon { - font-size: 14px; - margin-right: 6px; - } - - .nav-text { - font-size: 13px; - } - - .sidebar-header h2 { - font-size: 16px; - } - - .sidebar-footer .btn { - padding: 8px 10px; - font-size: 13px; - } - - .mobile-admin-info { - font-size: 12px; - margin-top: 8px; - } -} - -/* Very small screens (under 360px) */ -@media (max-width: 360px) { - .admin-sidebar { - width: 240px; - left: -240px; - } - - .admin-nav a { - padding: 8px; - font-size: 12px; - } - - .nav-icon { - font-size: 13px; - margin-right: 5px; - } - - .nav-text { - font-size: 12px; - } -} - -/* Medium screen adjustments for better content fitting */ -@media (max-width: 1024px) and (min-width: 769px) { - .admin-sidebar { - width: 200px; - min-width: 200px; - max-width: 200px; - padding: 15px; - } - - .admin-content { - padding: 20px; - } - - .admin-section { - padding: 20px; - } - - .users-admin-container { - grid-template-columns: 1fr; - gap: 20px; - } - - .shifts-admin-container { - grid-template-columns: 1fr; - gap: 20px; - } - - .user-form, - .users-list { - padding: 20px; - } - - .users-table { - font-size: 13px; - } - - .users-table th, - .users-table td { - padding: 10px 8px; - } -} - -/* Small laptop adjustments */ -@media (max-width: 1200px) and (min-width: 1025px) { - .admin-sidebar { - width: 220px; - min-width: 220px; - max-width: 220px; - } - - .users-admin-container, - .shifts-admin-container { - grid-template-columns: 1fr 1.5fr; - gap: 25px; - } -} - -/* Convert Data Styles */ -.data-convert-container { - display: flex; - flex-direction: column; - gap: 2rem; -} - -.upload-section { - background: white; - border-radius: 8px; - padding: 2rem; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.upload-area { - border: 2px dashed #ddd; - border-radius: 8px; - padding: 3rem; - text-align: center; - cursor: pointer; - transition: all 0.3s ease; -} - -.upload-area:hover { - border-color: var(--primary-color); - background-color: #f8f9fa; -} - -.upload-area.drag-over { - border-color: var(--primary-color); - background-color: #e3f2fd; -} - -.upload-icon { - font-size: 48px; - margin-bottom: 1rem; -} - -.file-info { - margin-top: 1rem; - padding: 1rem; - background: #f8f9fa; - border-radius: 4px; -} - -.csv-requirements { - margin-top: 1.5rem; - padding: 1rem; - background: #fff3cd; - border: 1px solid #ffeeba; - border-radius: 4px; -} - -.csv-requirements h4 { - margin: 0 0 0.5rem 0; - color: #856404; -} - -.csv-requirements ul { - margin: 0; - padding-left: 1.5rem; -} - -.processing-section { - background: white; - border-radius: 8px; - padding: 2rem; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.progress-bar-container { - margin: 1.5rem 0; -} - -.progress-bar { - height: 24px; - background: #e9ecef; - border-radius: 12px; - overflow: hidden; -} - -.progress-bar-fill { - height: 100%; - background: linear-gradient(90deg, #4CAF50 0%, #45a049 100%); - transition: width 0.3s ease; - width: 0%; -} - -.progress-text { - text-align: center; - margin-top: 0.5rem; - font-weight: 500; -} - -.processing-status { - padding: 1rem; - background: #f8f9fa; - border-radius: 4px; - text-align: center; - margin-bottom: 1.5rem; - animation: pulse 1.5s ease-in-out infinite; -} - -/* Pulsing effect for current address */ -@keyframes pulse { - 0% { opacity: 0.8; } - 50% { opacity: 1; } - 100% { opacity: 0.8; } -} - -/* Success row animation */ -.result-success { - animation: slideIn 0.3s ease-out; -} - -.result-error { - animation: slideIn 0.3s ease-out; -} - -@keyframes slideIn { - from { - transform: translateX(-20px); - opacity: 0; - } - to { - transform: translateX(0); - opacity: 1; - } -} - -.results-preview { - margin-top: 2rem; -} - -.results-map { - height: 400px; - margin-bottom: 1.5rem; - border: 1px solid #ddd; - border-radius: 4px; -} - -.results-table-container { - max-height: 400px; - overflow-y: auto; - border: 1px solid #ddd; - border-radius: 4px; -} - -.results-table { - width: 100%; - border-collapse: collapse; -} - -.results-table th { - background: #f8f9fa; - padding: 0.75rem; - text-align: left; - font-weight: 600; - position: sticky; - top: 0; - z-index: 10; -} - -.results-table td { - padding: 0.75rem; - border-bottom: 1px solid #e9ecef; -} - -.result-success { - background: #d4edda; -} - -.result-error { - background: #f8d7da; -} - -.status-icon { - display: inline-block; - width: 20px; - height: 20px; - line-height: 20px; - text-align: center; - border-radius: 50%; - font-weight: bold; -} - -.status-icon.success { - background: #28a745; - color: white; -} - -.status-icon.error { - background: #dc3545; - color: white; -} - -.error-message { - color: #721c24; - font-style: italic; -} - -.processing-actions { - display: flex; - gap: 1rem; - justify-content: center; - margin-top: 2rem; -} - -/* Mobile responsiveness for Convert Data */ -@media (max-width: 768px) { - .upload-area { - padding: 2rem; - } - - .upload-icon { - font-size: 36px; - } - - .results-map { - height: 300px; - } - - .results-table { - font-size: 14px; - } - - .results-table th, - .results-table td { - padding: 0.5rem; - } - - .processing-actions { - flex-direction: column; - } -} - -/* NocoDB Links Section */ -.nocodb-links-container { - display: flex; - flex-direction: column; - gap: 30px; -} - -.nocodb-cards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); - gap: 20px; -} - -.nocodb-card { - background: white; - border-radius: 12px; - padding: 24px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - border: 1px solid #e0e0e0; - transition: all 0.2s ease; -} - -.nocodb-card:hover { - box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); - transform: translateY(-2px); -} - -.nocodb-card-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 12px; -} - -.nocodb-card-header h3 { - margin: 0; - font-size: 18px; - color: #333; -} - -.nocodb-card-badge { - background: #e3f2fd; - color: #1976d2; - padding: 4px 12px; - border-radius: 20px; - font-size: 12px; - font-weight: 600; - text-transform: uppercase; -} - -.nocodb-card p { - color: #666; - margin: 0 0 20px 0; - line-height: 1.5; -} - -.nocodb-card .btn { - width: 100%; - justify-content: center; -} - -.nocodb-card .btn.btn-disabled, -.nocodb-card .btn[disabled] { - background-color: #6c757d; - border-color: #6c757d; - color: white; - opacity: 0.6; - cursor: not-allowed; - pointer-events: none; -} - -.nocodb-info { - margin-top: 20px; -} - -.info-box { - background: #f8f9fa; - border: 1px solid #e9ecef; - border-radius: 8px; - padding: 20px; -} - -.info-box h4 { - margin: 0 0 12px 0; - color: #495057; -} - -.info-box p { - margin: 0 0 12px 0; - color: #6c757d; - line-height: 1.5; -} - -.info-box ul { - margin: 0; - padding-left: 20px; - color: #6c757d; -} - -.info-box ul li { - margin: 6px 0; -} - -.info-box ul li strong { - color: #495057; -} - -/* Badge color variations */ -.nocodb-card:nth-child(1) .nocodb-card-badge { - background: #e8f5e8; - color: #2e7d32; -} - -.nocodb-card:nth-child(2) .nocodb-card-badge { - background: #fff3e0; - color: #f57c00; -} - -.nocodb-card:nth-child(3) .nocodb-card-badge { - background: #f3e5f5; - color: #7b1fa2; -} - -.nocodb-card:nth-child(4) .nocodb-card-badge { - background: #e1f5fe; - color: #0277bd; -} - -.nocodb-card:nth-child(5) .nocodb-card-badge { - background: #e8f5e8; - color: #388e3c; -} - -/* Responsive design for NocoDB cards */ -@media (max-width: 768px) { - .nocodb-cards { - grid-template-columns: 1fr; - gap: 16px; - } - - .nocodb-card { - padding: 20px; - } - - .nocodb-card-header h3 { - font-size: 16px; - } - - .info-box { - padding: 16px; - } -} - -/* Cuts Section Styles */ -.cuts-container { - display: flex; - flex-direction: column; - gap: 20px; -} - -.cuts-map-section { - position: relative; - height: 500px; - background: #f5f5f5; - border-radius: 8px; - overflow: hidden; - border: 1px solid #ddd; -} - -#cuts-map { - width: 100%; - height: 100%; - position: relative; -} - -.cuts-form-section, -.cuts-list-section { - margin-top: 20px; -} - -.cuts-filters { - display: grid; - grid-template-columns: 1fr 200px; - gap: 10px; - margin-bottom: 15px; -} - -.cuts-filters input, -.cuts-filters select { - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: 4px; -} - -/* Button styles to match admin panel theme */ -.btn-sm { - padding: 6px 12px; - font-size: 14px; -} - -.btn-primary { - background-color: #007bff; - color: white; - border: 1px solid #007bff; -} - -.btn-primary:hover:not(:disabled) { - background-color: #0056b3; - border-color: #0056b3; -} - -.btn-secondary { - background-color: #6c757d; - color: white; - border: 1px solid #6c757d; -} - -.btn-secondary:hover:not(:disabled) { - background-color: #545b62; - border-color: #545b62; -} - -.btn-danger { - background-color: #dc3545; - color: white; - border: 1px solid #dc3545; -} - -.btn-danger:hover:not(:disabled) { - background-color: #c82333; - border-color: #c82333; -} - -.btn-success { - background-color: #28a745; - color: white; - border: 1px solid #28a745; -} - -.btn-success:hover:not(:disabled) { - background-color: #218838; - border-color: #218838; -} - -/* Responsive layout for cuts */ -@media (min-width: 1200px) { - .cuts-container { - display: grid; - grid-template-columns: 1fr; - gap: 20px; - } - - .cuts-form-section, - .cuts-list-section { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; - margin-top: 0; - } - - .cuts-form-section > *, - .cuts-list-section > * { - grid-column: span 1; - } -} - -@media (max-width: 768px) { - .cuts-map-section { - height: 400px; - } - - .cuts-filters { - grid-template-columns: 1fr; - } -} - -/* Disabled form state */ -.cut-form.disabled { - opacity: 0.7; - pointer-events: none; -} - -.cut-form.disabled input:not(#start-drawing-btn):not(#reset-form-btn):not(#cancel-edit-btn), -.cut-form.disabled textarea, -.cut-form.disabled select { - background-color: #f5f5f5; - cursor: not-allowed; -} - -.cut-form.disabled input:not(#start-drawing-btn):not(#reset-form-btn):not(#cancel-edit-btn):focus, -.cut-form.disabled textarea:focus, -.cut-form.disabled select:focus { - outline: none; - border-color: #ddd; - box-shadow: none; -} - -/* Shift User Management Modal */ -.modal { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - display: flex; - justify-content: center; - align-items: center; - z-index: 10000; -} - -.modal-content { - background-color: white; - border-radius: var(--border-radius); - padding: 0; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); - max-width: 600px; - width: 90%; - max-height: 80vh; - overflow: hidden; - display: flex; - flex-direction: column; -} - -.modal-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 20px 25px; - border-bottom: 1px solid #e0e0e0; - background-color: #f8f9fa; -} - -.modal-header h3 { - margin: 0; - color: var(--dark-color); -} - -.modal-body { - padding: 25px; - overflow-y: auto; - flex: 1; -} - -.shift-info { - background-color: #f8f9fa; - padding: 15px; - border-radius: var(--border-radius); - margin-bottom: 20px; -} - -.shift-info h4 { - margin: 0 0 8px 0; - color: var(--dark-color); -} - -.shift-info p { - margin: 0; - color: #666; - font-size: 14px; -} - -.add-user-section { - margin-bottom: 30px; - padding-bottom: 20px; - border-bottom: 1px solid #e0e0e0; -} - -.add-user-section h4 { - margin: 0 0 15px 0; - color: var(--dark-color); -} - -.current-volunteers-section h4 { - margin: 0 0 15px 0; - color: var(--dark-color); -} - -.volunteer-actions-header { - display: flex; - justify-content: flex-end; - align-items: center; - margin-bottom: 15px; - padding-bottom: 10px; - border-bottom: 1px solid #eee; -} - -/* Users list header styling */ -.users-list-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 20px; - padding-bottom: 10px; - border-bottom: 2px solid #eee; -} - -.users-list-header h3 { - margin: 0; - color: var(--dark-color); -} - -/* Email modal styling */ -.modal-large { - max-width: 800px; - width: 90%; -} - -.email-form { - max-width: 100%; -} - -.rich-text-toolbar { - display: flex; - gap: 5px; - margin-bottom: 10px; - padding: 10px; - background: #f8f9fa; - border: 1px solid #ddd; - border-radius: 4px 4px 0 0; - border-bottom: none; -} - -.toolbar-btn { - padding: 8px 12px; - border: 1px solid #ccc; - background: white; - border-radius: 3px; - cursor: pointer; - font-size: 12px; - transition: all 0.2s; -} - -.toolbar-btn:hover { - background: #e9ecef; - border-color: #adb5bd; -} - -.toolbar-btn:active, .toolbar-btn.active { - background: #007bff; - color: white; - border-color: #007bff; -} - -.rich-text-editor { - min-height: 200px; - border: 1px solid #ddd; - padding: 15px; - border-radius: 0 0 4px 4px; - background: white; - font-family: inherit; - font-size: 14px; - line-height: 1.5; - outline: none; -} - -.rich-text-editor:focus { - border-color: var(--primary-color); - box-shadow: 0 0 0 2px rgba(160, 44, 141, 0.2); -} - -.rich-text-editor:empty:before { - content: attr(placeholder); - color: #999; - font-style: italic; -} - -.email-preview { - margin-top: 20px; - padding: 15px; - background: #f8f9fa; - border: 1px solid #ddd; - border-radius: 4px; -} - -.email-preview-content { - background: white; - padding: 20px; - border: 1px solid #ccc; - border-radius: 4px; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; -} - -.preview-header { - margin-bottom: 15px; - padding-bottom: 10px; - border-bottom: 1px solid #eee; -} - -.preview-body { - line-height: 1.6; -} - -.email-recipients-info { - background: #e3f2fd; - padding: 15px; - border-radius: 4px; - margin: 20px 0; - border-left: 4px solid #2196f3; -} - -.email-recipients-info p { - margin: 0; - color: #1565c0; -} - -.volunteer-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px 15px; - background-color: #f8f9fa; - border-radius: var(--border-radius); - margin-bottom: 8px; -} - -.volunteer-info { - flex: 1; -} - -.volunteer-name { - font-weight: bold; - color: var(--dark-color); -} - -.volunteer-email { - font-size: 13px; - color: #666; - margin-top: 2px; -} - -.volunteer-actions { - display: flex; - gap: 8px; -} - -.no-volunteers { - text-align: center; - color: #666; - font-style: italic; - padding: 20px; - background-color: #f8f9fa; - border-radius: var(--border-radius); -} - -/* Enhance existing shift admin items */ -.shift-admin-item { - position: relative; -} - -.manage-volunteers-btn { - margin-left: 8px; -} - -/* User select dropdown */ -#user-select { - padding: 8px 12px; - border: 1px solid #ddd; - border-radius: var(--border-radius); - font-size: 14px; - background-color: white; -} - -#user-select:focus { - outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.1); -} - -/* Responsive modal */ -@media (max-width: 768px) { - .modal-content { - width: 95%; - max-height: 90vh; - } - - .modal-body { - padding: 20px; - } - - .form-row { - flex-direction: column; - gap: 10px; - } - - .volunteer-item { - flex-direction: column; - align-items: flex-start; - gap: 10px; - } - - .volunteer-actions { - align-self: flex-end; - } -} - -/* Email Progress Indicators */ -.email-progress-container { - display: none; - margin-top: 20px; - padding: 20px; - background-color: #f8f9fa; - border-radius: var(--border-radius); - border: 1px solid #dee2e6; -} - -.email-progress-container.show { - display: block; -} - -.progress-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 15px; -} - -.progress-title { - font-weight: 600; - color: var(--dark-color); - font-size: 16px; -} - -.progress-stats { - display: flex; - gap: 15px; - font-size: 14px; -} - -.progress-stat { - display: flex; - align-items: center; - gap: 5px; -} - -.progress-stat.success { - color: var(--success-color); -} - -.progress-stat.error { - color: var(--error-color); -} - -.progress-stat.pending { - color: #6c757d; -} - -.progress-bar-container { - background-color: #e9ecef; - border-radius: 10px; - height: 20px; - overflow: hidden; - margin-bottom: 15px; - position: relative; -} - -.progress-bar { - height: 100%; - background: linear-gradient(90deg, var(--primary-color) 0%, #28a745 100%); - width: 0%; - transition: width 0.3s ease; - position: relative; -} - -.progress-bar.complete { - background: linear-gradient(90deg, #28a745 0%, #20c997 100%); -} - -.progress-bar.error { - background: linear-gradient(90deg, #dc3545 0%, #e74c3c 100%); -} - -.progress-text { - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: white; - font-size: 12px; - font-weight: 600; - text-shadow: 1px 1px 2px rgba(0,0,0,0.5); -} - -.email-status-list { - max-height: 200px; - overflow-y: auto; - border: 1px solid #dee2e6; - border-radius: var(--border-radius); - background-color: white; -} - -.email-status-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 8px 15px; - border-bottom: 1px solid #f8f9fa; - font-size: 14px; -} - -.email-status-item:last-child { - border-bottom: none; -} - -.email-status-recipient { - font-weight: 500; - color: var(--dark-color); -} - -.email-status-result { - display: flex; - align-items: center; - gap: 5px; - font-size: 12px; - font-weight: 600; -} - -.email-status-result.success { - color: var(--success-color); -} - -.email-status-result.error { - color: var(--error-color); -} - -.email-status-result.pending { - color: #6c757d; -} - -.progress-spinner { - display: inline-block; - width: 12px; - height: 12px; - border: 2px solid #f3f3f3; - border-top: 2px solid #6c757d; - border-radius: 50%; - animation: spin 1s linear infinite; -} - -@keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } -} - -.progress-actions { - display: flex; - justify-content: flex-end; - gap: 10px; - margin-top: 15px; -} - -.progress-close-btn { - background: var(--secondary-color); - color: white; - border: none; - padding: 8px 16px; - border-radius: var(--border-radius); - cursor: pointer; - font-size: 14px; - transition: var(--transition); -} - -.progress-close-btn:hover { - background: #5a6268; -} diff --git a/map/app/public/css/admin.css.backup b/map/app/public/css/admin.css.backup new file mode 100644 index 0000000..5fa89dc --- /dev/null +++ b/map/app/public/css/admin.css.backup @@ -0,0 +1,3010 @@ +/* Admin Panel Specific Styles */ +@import url("modules/dashboard.css"); + +.admin-container { + display: flex; + height: calc(100vh - var(--header-height)); + height: calc(var(--app-height) - var(--header-height)); + background-color: #f5f5f5; + width: 100%; + max-width: 100vw; + overflow: hidden; +} + +.admin-sidebar { + width: 250px; + min-width: 250px; + max-width: 250px; + background-color: white; + border-right: 1px solid #e0e0e0; + padding: 20px; + box-sizing: border-box; + flex-shrink: 0; +} + +.admin-sidebar h2 { + font-size: 18px; + margin-bottom: 20px; + color: var(--dark-color); +} + +.admin-nav { + display: flex; + flex-direction: column; + gap: 5px; +} + +.admin-nav a { + padding: 10px 15px; + color: var(--dark-color); + text-decoration: none; + border-radius: var(--border-radius); + transition: var(--transition); +} + +.admin-nav a:hover { + background-color: var(--light-color); +} + +.admin-nav a.active { + background-color: var(--primary-color); + color: white; +} + +.admin-content { + flex: 1; + padding: 30px; + overflow-y: auto; + overflow-x: hidden; + width: 100%; + min-width: 0; /* Allow flex item to shrink */ + box-sizing: border-box; + max-height: calc(100vh - var(--header-height)); + max-height: calc(var(--app-height) - var(--header-height)); +} + +.admin-section { + background-color: white; + border-radius: var(--border-radius); + padding: 30px; + box-shadow: 0 2px 4px rgba(0,0,0,0.1); + width: 100%; + max-width: 100%; + box-sizing: border-box; +} + +.admin-section h2 { + margin-bottom: 15px; + color: var(--dark-color); +} + +.admin-section p { + color: #666; + margin-bottom: 25px; +} + +.admin-map-container { + display: grid; + grid-template-columns: 1fr 300px; + gap: 20px; + margin-top: 20px; +} + +.admin-map { + height: 500px; + border-radius: var(--border-radius); + border: 1px solid #ddd; +} + +.location-controls { + padding: 20px; + background-color: #f9f9f9; + border-radius: var(--border-radius); +} + +.location-controls .form-group { + margin-bottom: 15px; +} + +.location-controls .form-actions { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 20px; +} + +.help-text { + margin-top: 20px; + padding: 15px; + background-color: #e3f2fd; + border-radius: var(--border-radius); + font-size: 14px; +} + +.help-text p { + margin: 0; + color: #1976d2; +} + +.admin-info { + display: flex; + align-items: center; + gap: 10px; + color: rgba(255,255,255,0.9); + font-size: 14px; +} + +/* Form styles */ +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; + font-weight: 500; + color: var(--dark-color); +} + +.form-group input { + width: 100%; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: var(--border-radius); + font-size: 14px; + transition: border-color 0.2s; +} + +.form-group input:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); +} + +/* Button styles */ +.btn { + padding: 8px 16px; + border: none; + border-radius: var(--border-radius); + font-size: 14px; + font-weight: 500; + cursor: pointer; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 5px; + transition: all 0.2s; +} + +.btn-primary { + background-color: var(--primary-color); + color: white; +} + +.btn-primary:hover { + background-color: #45a049; + transform: translateY(-1px); +} + +.btn-secondary { + background-color: #6c757d; + color: white; +} + +.btn-secondary:hover { + background-color: #5a6268; + transform: translateY(-1px); +} + +.btn-sm { + padding: 6px 12px; + font-size: 13px; +} + +/* Status messages */ +.status-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 12300; + max-width: 400px; + pointer-events: none; /* Allow clicks to pass through container */ +} + +/* Mobile status container - center horizontally at top */ +@media (max-width: 768px) { + .status-container { + left: 10px; + right: 10px; + top: 10px; + width: auto; + max-width: 100%; + transform: none; + display: flex; + flex-direction: column; + align-items: center; + } + + .status-message { + width: auto; + max-width: 90vw; + box-sizing: border-box; + font-size: 13px; + text-align: center; + } +} + +/* Very small screens - adjust positioning and sizing */ +@media (max-width: 480px) { + .status-container { + top: 70px; /* Smaller header on mobile */ + max-width: calc(100vw - 20px); + min-width: 260px; + } +} + +/* Extra small screens */ +@media (max-width: 360px) { + .status-container { + top: 65px; + max-width: calc(100vw - 15px); + min-width: auto; + } + + .status-message { + padding: 10px 12px; + font-size: 13px; + } +} + +.status-message { + padding: 12px 16px; + margin-bottom: 10px; + border-radius: var(--border-radius); + font-size: 14px; + animation: slideIn 0.3s ease-out; + pointer-events: auto; /* Allow interaction with individual messages */ + cursor: pointer; /* Show it's clickable for dismissal */ +} + +.status-message.success { + background-color: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; +} + +.status-message.error { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; +} + +.status-message.info { + background-color: #cce7ff; + color: #004085; + border: 1px solid #b3d7ff; +} + +@keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +/* Walk Sheet Styles */ +.walk-sheet-container { + display: grid; + grid-template-columns: 2fr 3fr; + gap: 30px; + margin-top: 20px; + align-items: flex-start; +} + +.walk-sheet-config { + background-color: #f9f9f9; + padding: 20px; + border-radius: var(--border-radius); +} + +.walk-sheet-config h3 { + font-size: 16px; + margin-bottom: 15px; + color: var(--dark-color); +} + +.qr-code-group { + background-color: white; + padding: 15px; + border-radius: var(--border-radius); + margin-bottom: 15px; + border: 1px solid #e0e0e0; + position: relative; +} + +.qr-code-group h4 { + font-size: 14px; + margin-bottom: 10px; + color: #666; +} + +.form-row { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 10px; +} + +.help-text-inline { + font-size: 14px; + color: #666; + margin-bottom: 15px; +} + +/* Walk Sheet Preview */ +.walk-sheet-preview { + background-color: #f5f5f5; + padding: 30px; + border-radius: var(--border-radius); + box-shadow: 0 4px 24px rgba(0,0,0,0.10); + min-width: 350px; + max-width: 100%; + margin: 0 auto; + min-height: 950px; /* Ensure container is tall enough */ + display: flex; + flex-direction: column; + align-items: center; + overflow: auto; +} + +.walk-sheet-preview h3 { + font-size: 16px; + margin-bottom: 10px; + color: var(--dark-color); +} + +.preview-controls { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} + +.preview-info { + font-size: 12px; + color: #666; +} + +/* Walk Sheet Page (8.5 x 11 actual size) */ +.walk-sheet-page { + /* Actual paper size in pixels at 96 DPI (standard screen resolution) */ + width: 816px; /* 8.5 inches * 96 DPI */ + height: 1056px; /* 11 inches * 96 DPI */ + background: white; + box-shadow: 0 4px 20px rgba(0,0,0,0.15); + padding: 48px; /* 0.5 inch margins at 96 DPI */ + margin: 0 auto; + overflow: hidden; + font-size: 12pt; /* Standard document font size */ + line-height: 1.6; + position: relative; + transform: none; /* No scaling by default */ + box-sizing: border-box; +} + +/* Walk Sheet Print Styles */ +@media print { + body * { + visibility: hidden; + } + + .walk-sheet-page, .walk-sheet-page * { + visibility: visible; + } + + .walk-sheet-page { + position: fixed; + left: 0; + top: 0; + width: 8.5in; + height: 11in; + max-width: none; + padding: 0.5in; /* 0.5 inch margins */ + margin: 0; + box-shadow: none; + font-size: 12pt; + page-break-after: always; + } + + /* Ensure QR codes print properly */ + .ws-qr-code img { + width: 100px !important; + height: 100px !important; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } +} + +/* Remove the preview scaling - show at actual size */ +.walk-sheet-preview .walk-sheet-page { + transform: none; + transform-origin: top center; + margin-bottom: 0; + border-radius: 0; /* Remove rounded corners to look more like paper */ +} + +/* Walk Sheet Content Styles - adjusted for actual size */ +.ws-header { + text-align: center; + margin-bottom: 30px; /* Reduced from 40px */ + border-bottom: 3px solid #333; + padding-bottom: 15px; /* Reduced from 20px */ +} + +.ws-title { + font-size: 24pt; /* Reduced from 28pt */ + font-weight: bold; + margin: 0; + color: #000; +} + +.ws-subtitle { + font-size: 12pt; /* Reduced from 14pt */ + color: #444; + margin: 8px 0 0 0; /* Reduced from 10px */ +} + +.ws-qr-section { + display: flex; + justify-content: space-around; + margin: 30px 0; /* Reduced from 40px */ + padding: 20px; /* Reduced from 30px */ + background-color: #f5f5f5; + border-radius: 8px; + border: 1px solid #ddd; +} + +.ws-qr-item { + text-align: center; +} + +.ws-qr-code { + margin-bottom: 5px; +} + +.ws-qr-code img { + display: block; + margin: 0 auto; + width: 120px; /* Reduced from 140px */ + height: 120px; + image-rendering: crisp-edges; + image-rendering: -webkit-crisp-edges; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; +} + +.ws-qr-code canvas { + display: block; + margin: 0 auto; + image-rendering: crisp-edges; + image-rendering: -webkit-crisp-edges; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; +} + +.ws-qr-label { + font-size: 11pt; + font-weight: bold; + margin-top: 10px; + color: #333; +} + +.ws-form-section { + margin-top: 30px; /* Reduced from 40px */ +} + +.ws-form-row { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; + margin-bottom: 18px; /* Reduced from 20px */ +} + +.ws-form-group { + border-bottom: 2px solid #ccc; + padding-bottom: 8px; +} + +.ws-form-label { + font-size: 10pt; + color: #555; + display: block; + margin-bottom: 5px; + font-weight: 500; +} + +.ws-form-field { + height: 30px; + background-color: #fafafa; + border: 1px solid #eee; +} + +/* Special field styles for circles */ +.ws-form-field.circles { + height: auto; + background: none; + border: none; + display: flex; + gap: 15px; + align-items: center; + padding: 5px 0; +} + +.ws-circle-option { + display: flex; + align-items: center; + gap: 5px; + font-size: 11pt; +} + +.ws-circle { + width: 24px; + height: 24px; + border: 2px solid #333; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 10pt; + font-weight: 500; + background-color: white; +} + +.ws-notes-section { + margin-top: 30px; /* Reduced from 40px */ +} + +.ws-notes-label { + font-size: 11pt; + font-weight: bold; + margin-bottom: 10px; + color: #333; +} + +.ws-notes-area { + width: 100%; + height: 80px; /* Reduced from 100px */ + border: 2px solid #ccc; + background-color: #fafafa; + border-radius: 4px; +} + +.ws-footer { + position: absolute; + bottom: 48px; /* Match padding */ + left: 48px; + right: 48px; + text-align: center; + font-size: 10pt; + color: #666; + padding-top: 15px; /* Reduced from 20px */ + border-top: 1px solid #ccc; + max-height: 80px; /* Ensure footer has enough space */ + overflow: hidden; + line-height: 1.4; +} + +/* QR Code Generation Status */ +.qr-code-group.generating::after { + content: 'Generating QR Code...'; + position: absolute; + top: 0; + right: 0; + font-size: 12px; + color: var(--primary-color); + background: white; + padding: 2px 8px; + border-radius: 3px; + box-shadow: 0 1px 3px rgba(0,0,0,0.1); +} + +/* Loading state for save button */ +.btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* QR code stored indicator */ +.qr-status { + font-size: 12px; + color: var(--success-color); + margin-top: 5px; +} + +.qr-status.stored { + color: var(--success-color); +} + +.qr-status.pending { + color: var(--warning-color); +} + +/* User Management Styles */ +.users-admin-container { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 30px; + margin-top: 20px; + width: 100%; + box-sizing: border-box; + overflow: hidden; +} + +/* Data Convert Styles */ +.data-convert-container { + max-width: 1000px; +} + +.csv-requirements { + background: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 8px; + padding: 20px; + margin: 20px 0; +} + +.csv-requirements h4 { + color: #495057; + margin-bottom: 15px; + border-bottom: 2px solid #e9ecef; + padding-bottom: 10px; +} + +.requirements-section { + margin-bottom: 20px; +} + +.requirements-section:last-child { + margin-bottom: 0; +} + +.requirements-section h5 { + color: #6c757d; + margin-bottom: 10px; + font-weight: 600; +} + +.field-mapping-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 15px; + margin-top: 10px; +} + +.field-group { + background: white; + border: 1px solid #e9ecef; + border-radius: 6px; + padding: 12px; +} + +.field-group strong { + color: #495057; + display: block; + margin-bottom: 8px; + font-size: 14px; +} + +.field-group ul { + margin: 0; + padding-left: 15px; + list-style-type: disc; +} + +.field-group li { + font-family: 'Courier New', monospace; + font-size: 12px; + color: #6c757d; + margin-bottom: 3px; +} + +.csv-example { + background: #2d3748; + color: #e2e8f0; + padding: 15px; + border-radius: 6px; + font-family: 'Courier New', monospace; + font-size: 12px; + overflow-x: auto; + margin-top: 10px; + white-space: pre; +} + +.requirements-section ul { + margin: 10px 0; + padding-left: 20px; +} + +.requirements-section > ul > li { + margin-bottom: 5px; + color: #495057; +} + +.user-form, +.users-list { + background: white; + border-radius: var(--border-radius); + padding: 25px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; + width: 100%; + box-sizing: border-box; +} + +.users-list { + overflow-x: auto; /* Allow horizontal scrolling if needed */ + min-width: 0; /* Allow shrinking */ +} + +.user-form h3, +.users-list h3 { + margin-bottom: 20px; + color: var(--dark-color); + border-bottom: 2px solid var(--primary-color); + padding-bottom: 10px; + font-size: 18px; +} + +.users-table { + width: 100%; + border-collapse: collapse; + margin-top: 15px; + font-size: 14px; + table-layout: auto; + min-width: 600px; /* Ensure minimum width for readability on desktop */ + box-sizing: border-box; +} + +.users-table th, +.users-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #e0e0e0; +} + +.users-table th { + background-color: #f8f9fa; + font-weight: 600; + color: var(--dark-color); + font-size: 13px; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.users-table tbody tr { + transition: background-color 0.2s; +} + +.users-table tbody tr:hover { + background-color: #f8f9fa; +} + +.user-role { + padding: 4px 8px; + border-radius: 12px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + display: inline-block; + text-align: center; + min-width: 45px; + line-height: 1.2; +} + +.user-role.admin { + background-color: #dc3545; + color: white; +} + +.user-role.user { + background-color: #28a745; + color: white; +} + +.user-role.temp { + background-color: #ff9800; + color: white; +} + +.expiration-info { + display: inline-block; + margin-left: 8px; + font-size: 0.8em; + color: #666; + background: #f5f5f5; + padding: 2px 6px; + border-radius: 3px; +} + +.expiration-warning { + color: #ff5722; + font-weight: bold; +} + +.expires-soon { + background-color: #fff3cd; + border-left: 4px solid #ffc107; +} + +.expired { + background-color: #f8d7da; + border-left: 4px solid #dc3545; + opacity: 0.7; +} + +.help-text { + font-size: 0.85em; + color: #666; + margin-top: 4px; + display: block; +} + +.user-actions { + display: flex; + gap: 8px; +} + +.user-actions .btn { + padding: 6px 12px; + font-size: 12px; + border-radius: var(--border-radius); + font-weight: 500; +} + +.btn-danger { + background-color: #dc3545; + color: white; + border: 1px solid #dc3545; + transition: var(--transition); +} + +.btn-danger:hover { + background-color: #c82333; + border-color: #c82333; + transform: translateY(-1px); +} + +.loading-message, +.empty-message { + text-align: center; + padding: 40px 20px; + color: #666; + font-style: italic; + background-color: #f8f9fa; + border-radius: var(--border-radius); + margin-top: 15px; +} + +.loading-message { + background-color: #e3f2fd; + color: #1976d2; +} + +/* User form specific styles */ +.user-form .form-group { + margin-bottom: 20px; +} + +.user-form .form-group label { + display: block; + margin-bottom: 8px; + font-weight: 600; + color: var(--dark-color); + font-size: 14px; +} + +.user-form .form-group label input[type="checkbox"] { + margin-right: 8px; + transform: scale(1.1); +} + +.user-form input[type="email"], +.user-form input[type="password"], +.user-form input[type="text"] { + width: 100%; + padding: 12px; + border: 2px solid #ddd; + border-radius: var(--border-radius); + font-size: 14px; + transition: var(--transition); + background-color: #fafafa; +} + +.user-form input:focus { + outline: none; + border-color: var(--primary-color); + background-color: white; + box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); +} + +.user-form .form-actions { + display: flex; + gap: 10px; + margin-top: 25px; +} + +.user-form .form-actions .btn { + flex: 1; + padding: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + font-size: 13px; +} + +/* Responsive design for user management */ +@media (max-width: 768px) { + .users-admin-container { + grid-template-columns: 1fr; + gap: 25px; /* Increased gap for better separation */ + } + + .user-form, + .users-list { + padding: 25px; /* Back to desktop padding for better comfort */ + width: 100%; + box-sizing: border-box; + } + + .users-list { + overflow-x: auto; + width: 100%; + box-sizing: border-box; + } + + /* Data Convert responsive styles */ + .field-mapping-grid { + grid-template-columns: 1fr; + } + + .csv-example { + font-size: 10px; + padding: 10px; + } + + .csv-requirements { + padding: 15px; + } + + .users-table { + font-size: 14px; /* Match desktop font size for better readability */ + min-width: auto; /* Remove minimum width constraint on mobile */ + width: 100%; + table-layout: auto; /* Changed from fixed to auto for better text flow */ + border-collapse: collapse; + display: block; + } + + .users-table thead { + display: none; + } + + .users-table tbody, + .users-table tr, + .users-table td { + display: block; + width: 100% !important; + box-sizing: border-box; + } + + .users-table tr { + margin-bottom: 15px; + border: 1px solid #e0e0e0; + border-radius: var(--border-radius); + padding: 10px; + } + + .users-table td { + padding: 8px 0; + border: none; + display: flex; + align-items: center; + word-wrap: break-word; + overflow-wrap: break-word; + } + + .users-table td::before { + content: attr(data-label); + font-weight: bold; + width: 80px; + min-width: 80px; + margin-right: 10px; + color: #555; + } + + /* Adjust column widths for mobile - less aggressive width constraints */ + .users-table th:nth-child(1), + .users-table td:nth-child(1) { /* Email */ + width: 35%; /* Increased back to give more space for email */ + } + + .users-table th:nth-child(2), + .users-table td:nth-child(2) { /* Name */ + width: 25%; + } + + .users-table th:nth-child(3), + .users-table td:nth-child(3) { /* Admin */ + width: 15%; /* Reduced back to original for more space elsewhere */ + } + + .users-table th:nth-child(4), + .users-table td:nth-child(4) { /* Created */ + width: 0%; + display: none; /* Hide created date on mobile to save space */ + } + + .users-table th:nth-child(5), + .users-table td:nth-child(5) { /* Actions */ + width: 25%; /* Reduced back to give more balanced layout */ + } + + .user-actions { + flex-direction: column; + gap: 8px; /* Increased for better spacing */ + width: 100%; + } + + .user-actions .btn { + font-size: 12px; /* Increased to match more with desktop */ + padding: 8px 10px; /* More comfortable padding */ + width: 100%; + text-align: center; + min-height: 36px; /* Increased minimum height for better touch targets */ + line-height: 1.3; + } + + .user-form .form-actions { + flex-direction: column; + } + + /* Admin container mobile layout */ + .admin-container { + flex-direction: column; + } + + .admin-sidebar { + width: 100%; + border-right: none; + border-bottom: 1px solid #e0e0e0; + padding: 15px; + } + + .admin-nav { + flex-direction: row; + overflow-x: auto; + gap: 10px; + padding-bottom: 10px; + } + + .admin-nav a { + white-space: nowrap; + min-width: auto; + padding: 8px 12px; + font-size: 14px; + } + + .header .header-actions { + display: flex !important; + gap: 10px; + flex-wrap: wrap; + } + + .header .header-actions .btn { + padding: 6px 10px; + font-size: 13px; + } + + .admin-info { + font-size: 12px; + } + + .admin-map-container { + grid-template-columns: 1fr; + gap: 15px; + } + + .admin-map { + height: 250px; + } + + .admin-content { + padding: 15px; + } + + .admin-section { + padding: 15px; + } + + .form-row { + grid-template-columns: 1fr; + } + + /* Shifts admin mobile */ + .shifts-admin-container { + grid-template-columns: 1fr; + gap: 20px; + } + + .shift-admin-item { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + .shift-actions { + width: 100%; + justify-content: flex-end; + } + + /* Walk sheet container mobile */ + .walk-sheet-container { + display: flex !important; + flex-direction: column !important; + gap: 20px; + } + + .walk-sheet-config { + order: 1 !important; + margin-bottom: 20px; + } + + /* Walk sheet mobile */ + .walk-sheet-preview { + min-width: 0; + max-width: 100%; + width: 100%; + min-height: 500px; + padding: 10px; + overflow: hidden; /* Change from overflow-x: hidden to overflow: hidden */ + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + } + + /* Container for the scaled page */ + .walk-sheet-preview #walk-sheet-preview-content { + transform: scale(0.35); + transform-origin: top center; + margin: 0 auto; + width: 816px; + max-width: none; + margin-bottom: -600px; + position: relative; + left: 50%; + margin-left: -408px; /* Half of 816px to center it */ + } + + /* Status container mobile */ + .status-container { + top: 10px; + right: 10px; + left: 10px; + max-width: none; + display: flex; + flex-direction: column; + align-items: center; + } + + .status-message { + font-size: 13px; + padding: 10px 12px; + max-width: 90%; + text-align: center; + width: fit-content; + margin: 0 auto 10px auto; + } +} + +@media (max-width: 480px) { + .users-table { + font-size: 13px; /* Close to desktop, but slightly smaller for small screens */ + min-width: auto; + } + + .users-table th, + .users-table td { + padding: 10px 6px; /* Comfortable padding, not too cramped */ + font-size: 13px; /* Match table font size */ + line-height: 1.4; + } + + /* Adjust column widths for very small screens */ + .users-table th:nth-child(1), + .users-table td:nth-child(1) { /* Email */ + width: 35%; /* Reduced from 40% */ + word-break: break-all; + } + + .users-table th:nth-child(2), + .users-table td:nth-child(2) { /* Name */ + width: 25%; /* Reduced from 30% */ + } + + .users-table th:nth-child(3), + .users-table td:nth-child(3) { /* Admin */ + width: 18%; /* Increased from 15% */ + } + + .users-table th:nth-child(5), + .users-table td:nth-child(5) { /* Actions */ + width: 22%; /* Increased from 15% */ + } + + .user-role { + font-size: 10px; /* Increased from 9px for better readability */ + padding: 4px 6px; /* More comfortable padding */ + line-height: 1.2; + } + + .user-actions .btn { + font-size: 11px; /* Increased from 9px */ + padding: 6px 8px; /* More comfortable padding */ + border-radius: 4px; /* Slightly larger border radius */ + min-height: 32px; /* Comfortable touch target */ + line-height: 1.3; + } + + .user-form input[type="email"], + .user-form input[type="password"], + .user-form input[type="text"] { + padding: 10px; + font-size: 13px; + } + + /* Very small screen adjustments */ + .admin-content { + padding: 10px; + } + + .admin-section { + padding: 10px; + } + + .admin-sidebar { + padding: 10px; + } + + .admin-nav a { + padding: 6px 10px; + font-size: 13px; + } + + .header .header-actions .btn { + padding: 5px 8px; + font-size: 12px; + } + + .admin-info { + display: none; /* Hide on very small screens */ + } + + .admin-map { + height: 200px; + } + + /* Walk sheet very small screens */ + .walk-sheet-preview { + padding: 10px; + width: 100%; + display: flex; + justify-content: center; + align-items: flex-start; + } + + .walk-sheet-preview .walk-sheet-page { + transform: scale(0.25); + transform-origin: center top; + margin-bottom: -750px; + margin-left: auto; + margin-right: auto; + } + + /* Form adjustments */ + .form-group input, + .form-group select { + font-size: 16px; /* Prevent zoom on iOS */ + } + + .btn { + min-height: 44px; /* Touch-friendly button size */ + padding: 10px 16px; + } + + .btn-sm { + min-height: 36px; + padding: 8px 12px; + } + + /* User management specific mobile adjustments */ + .user-container { + padding: 0.5rem; + } + + .user-profile { + padding: 1rem; + } + + .users-table th { + font-size: 10px; + } + + .user-actions .btn { + font-size: 10px; + padding: 3px 6px; + min-height: 32px; + } +} + +/* For very large screens, show at full size without scaling */ +@media (min-width: 1600px) { + .walk-sheet-container { + gap: 40px; + grid-template-columns: 1fr 2fr; /* Give more space to preview */ + } + + .walk-sheet-preview { + max-width: 100%; + min-height: 1100px; + padding: 40px; + } + + .walk-sheet-preview .walk-sheet-page { + transform: none; /* Show at actual size */ + } +} + +/* Container adjustments for actual-size preview */ +.walk-sheet-container { + display: grid; + grid-template-columns: minmax(300px, 400px) 1fr; /* Adjust proportions */ + gap: 30px; + margin-top: 20px; + align-items: flex-start; +} + +/* CSS Variables (define these in style.css if not already defined) */ +:root { + --primary-color: #4CAF50; + --dark-color: #333; + --light-color: #f5f5f5; + --border-radius: 6px; + --transition: all 0.2s ease; + --header-height: 60px; +} + +/* Crosshair styling */ +.crosshair { + pointer-events: none; + z-index: 1000; +} + +.crosshair div { + border-radius: 1px; + opacity: 0.8; +} + +/* Admin map styling */ +.admin-map { + position: relative; + cursor: crosshair; +} + +.admin-map .leaflet-container { + cursor: crosshair; +} + +/* Shifts Admin Styles */ +.shifts-admin-container { + display: grid; + grid-template-columns: 400px 1fr; + gap: 30px; +} + +.shift-form { + background: white; + padding: 20px; + border-radius: var(--border-radius); + border: 1px solid #e0e0e0; +} + +.shifts-list { + background: white; + padding: 20px; + border-radius: var(--border-radius); + border: 1px solid #e0e0e0; +} + +.shift-admin-item { + border: 1px solid #e0e0e0; + border-radius: var(--border-radius); + padding: 15px; + margin-bottom: 15px; + display: flex; + justify-content: space-between; + align-items: center; +} + +.shift-admin-item h4 { + margin: 0 0 10px 0; +} + +.shift-admin-item p { + margin: 5px 0; + color: var(--secondary-color); +} + +.status-open { + color: var(--success-color); + font-weight: bold; +} + +.status-full { + color: var(--warning-color); + font-weight: bold; +} + +.status-cancelled { + color: var(--danger-color); + font-weight: bold; +} + +.shift-actions { + display: flex; + gap: 10px; +} + +@media (max-width: 768px) { + .shifts-admin-container { + grid-template-columns: 1fr; + } +} + +/* Additional mobile styles for better responsiveness */ +@media (max-width: 1200px) { + .walk-sheet-container { + display: flex !important; + flex-direction: column !important; + gap: 20px; + } + + .walk-sheet-config { + order: 1 !important; + margin-bottom: 20px; + } + + .walk-sheet-preview { + order: 2 !important; + padding: 20px; + min-height: auto; + max-width: 100vw; + overflow-x: auto; + display: flex; + justify-content: center; + align-items: flex-start; + } + + .walk-sheet-preview .walk-sheet-page { + transform: scale(0.75); + transform-origin: center top; + margin-bottom: -200px; + max-width: 100%; + margin-left: auto; + margin-right: auto; + } +} + +@media (max-width: 1000px) { + .walk-sheet-preview .walk-sheet-page { + transform: scale(0.5); + transform-origin: center top; + margin-bottom: -400px; + margin-left: auto; + margin-right: auto; + } +} + +/* Tablet and mobile header adjustments */ +@media (max-width: 1024px) { + .header { + padding: 10px 15px; + } + + .header h1 { + font-size: 20px; + } + + .header .header-actions { + gap: 8px; + } +} + +/* Responsive Table Styles for Mobile */ +@media (max-width: 640px) { + .users-table { + border: 0; + } + + .users-table thead { + border: none; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + } + + .users-table tr { + border-bottom: 3px solid #ddd; + display: block; + margin-bottom: 10px; + padding: 10px; + background: #f8f9fa; + border-radius: 6px; + } + + .users-table td { + border: none; + border-bottom: 1px solid #eee; + display: block; + font-size: 13px; + text-align: right; + padding: 8px 10px; + } + + .users-table td::before { + content: attr(data-label); + float: left; + font-weight: bold; + text-transform: uppercase; + font-size: 11px; + color: #666; + } + + .users-table td:last-child { + border-bottom: 0; + } + + .user-actions { + justify-content: flex-end; + margin-top: 8px; + } + + .user-actions .btn { + width: auto !important; + min-width: 80px; + flex: none; + } +} + +/* Enhanced mobile form styling */ +@media (max-width: 480px) { + .user-form input[type="email"], + .user-form input[type="password"], + .user-form input[type="text"] { + font-size: 16px; /* Prevent zoom on iOS */ + padding: 12px; + } + + .user-form .form-group label { + font-size: 14px; + } + + .user-form .form-actions .btn { + width: 100%; + margin-bottom: 8px; + } + + .users-table td { + font-size: 12px; + padding: 6px 8px; + } + + .users-table td::before { + font-size: 10px; + } +} + +/* Mobile Menu Toggle */ +.mobile-menu-toggle { + display: none; + background: none; + border: none; + padding: 8px; + cursor: pointer; + position: relative; + width: 40px; + height: 40px; +} + +.mobile-menu-toggle span { + display: block; + width: 24px; + height: 3px; + background: white; + margin: 5px auto; + transition: all 0.3s ease; + border-radius: 2px; +} + +.mobile-menu-toggle.active span:nth-child(1) { + transform: rotate(45deg) translate(5px, 5px); +} + +.mobile-menu-toggle.active span:nth-child(2) { + opacity: 0; +} + +.mobile-menu-toggle.active span:nth-child(3) { + transform: rotate(-45deg) translate(7px, -6px); +} + +/* Sidebar Header (mobile) */ +.sidebar-header { + display: none; + justify-content: space-between; + align-items: center; + padding-bottom: 20px; + border-bottom: 1px solid #e0e0e0; + margin-bottom: 20px; +} + +.close-sidebar { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: var(--dark-color); + padding: 0; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; +} + +/* Sidebar Footer (mobile) */ +.sidebar-footer { + display: none; + margin-top: auto; + padding-top: 20px; + border-top: 1px solid #e0e0e0; +} + +.mobile-admin-info { + margin-top: 15px; + font-size: 14px; + color: #666; + text-align: center; +} + +/* Navigation Icons */ +.nav-icon { + margin-right: 10px; + font-size: 18px; +} + +/* Utility Classes */ +.desktop-only { + display: flex; +} + +.mobile-only { + display: none; +} + +.btn-block { + width: 100%; + justify-content: center; +} + +/* Mobile Styles */ +@media (max-width: 768px) { + /* Show mobile menu toggle */ + .mobile-menu-toggle { + display: block; + } + + /* Header adjustments */ + .header { + padding: 10px 15px; + display: flex; + align-items: center; + gap: 15px; + } + + .header h1 { + flex: 1; + text-align: center; + margin: 0; + font-size: 18px; + } + + /* Hide desktop elements */ + .desktop-only { + display: none !important; + } + + .mobile-only { + display: flex; + } + + /* Admin container becomes full width */ + .admin-container { + flex-direction: column; + height: calc(100vh - 50px); /* Fallback for older browsers */ + height: calc(var(--app-height) - 50px); /* Reduced header height */ + } + + /* Sidebar as overlay */ + .admin-sidebar { + position: fixed; + top: 0; + left: -280px; + width: 280px; + height: 100vh; /* Fallback for older browsers */ + height: var(--app-height); + background: white; + z-index: 12400; + transition: left 0.3s ease; + box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + padding: 20px; + } + + .admin-sidebar.active { + left: 0; + } + + /* Show sidebar header and footer on mobile */ + .sidebar-header { + display: flex; + } + + .sidebar-footer { + display: block; + } + + /* Admin nav mobile styling */ + .admin-nav { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; + margin: 20px 0; + } + + .admin-nav a { + display: flex; + align-items: center; + padding: 10px 12px; + font-size: 14px; + border-radius: 6px; + background-color: #f5f5f5; + border: 1px solid #e0e0e0; + transition: all 0.2s ease; + } + + .admin-nav a:active { + background-color: #e0e0e0; + transform: scale(0.98); + } + + .admin-nav a.active { + background-color: var(--primary-color); + color: white; + border-color: var(--primary-color); + } + + .admin-nav a:hover { + background-color: #ebebeb; + border-color: #d0d0d0; + } + + .admin-nav a.active:hover { + background-color: #0056b3; + border-color: #0056b3; + } + + /* Smaller nav icons and text */ + .nav-icon { + font-size: 16px; + margin-right: 8px; + flex-shrink: 0; + } + + .nav-text { + font-size: 14px; + font-weight: 500; + } + + /* Compact sidebar header */ + .sidebar-header { + display: flex; + padding-bottom: 15px; + margin-bottom: 0; + } + + .sidebar-header h2 { + font-size: 18px; + margin: 0; + } + + /* Compact sidebar footer */ + .sidebar-footer { + display: block; + margin-top: auto; + padding-top: 15px; + border-top: 1px solid #e0e0e0; + } + + .sidebar-footer .btn { + padding: 10px 12px; + font-size: 14px; + } + + .mobile-admin-info { + margin-top: 10px; + font-size: 13px; + color: #666; + text-align: center; + } + + /* Ensure sidebar fits content without scroll */ + .admin-sidebar { + position: fixed; + top: 0; + left: -280px; + width: 280px; + height: 100vh; + background: white; + z-index: 12400; + transition: left 0.3s ease; + box-shadow: 2px 0 10px rgba(0, 0, 0, 0.1); + display: flex; + flex-direction: column; + padding: 15px; + overflow-y: auto; /* Just in case for very small screens */ + } +} + +/* Small mobile devices */ +@media (max-width: 480px) { + .admin-sidebar { + width: 260px; + left: -260px; + padding: 12px; + } + + .admin-nav { + gap: 6px; + margin: 15px 0; + } + + .admin-nav a { + padding: 8px 10px; + font-size: 13px; + } + + .nav-icon { + font-size: 14px; + margin-right: 6px; + } + + .nav-text { + font-size: 13px; + } + + .sidebar-header h2 { + font-size: 16px; + } + + .sidebar-footer .btn { + padding: 8px 10px; + font-size: 13px; + } + + .mobile-admin-info { + font-size: 12px; + margin-top: 8px; + } +} + +/* Very small screens (under 360px) */ +@media (max-width: 360px) { + .admin-sidebar { + width: 240px; + left: -240px; + } + + .admin-nav a { + padding: 8px; + font-size: 12px; + } + + .nav-icon { + font-size: 13px; + margin-right: 5px; + } + + .nav-text { + font-size: 12px; + } +} + +/* Medium screen adjustments for better content fitting */ +@media (max-width: 1024px) and (min-width: 769px) { + .admin-sidebar { + width: 200px; + min-width: 200px; + max-width: 200px; + padding: 15px; + } + + .admin-content { + padding: 20px; + } + + .admin-section { + padding: 20px; + } + + .users-admin-container { + grid-template-columns: 1fr; + gap: 20px; + } + + .shifts-admin-container { + grid-template-columns: 1fr; + gap: 20px; + } + + .user-form, + .users-list { + padding: 20px; + } + + .users-table { + font-size: 13px; + } + + .users-table th, + .users-table td { + padding: 10px 8px; + } +} + +/* Small laptop adjustments */ +@media (max-width: 1200px) and (min-width: 1025px) { + .admin-sidebar { + width: 220px; + min-width: 220px; + max-width: 220px; + } + + .users-admin-container, + .shifts-admin-container { + grid-template-columns: 1fr 1.5fr; + gap: 25px; + } +} + +/* Convert Data Styles */ +.data-convert-container { + display: flex; + flex-direction: column; + gap: 2rem; +} + +.upload-section { + background: white; + border-radius: 8px; + padding: 2rem; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.upload-area { + border: 2px dashed #ddd; + border-radius: 8px; + padding: 3rem; + text-align: center; + cursor: pointer; + transition: all 0.3s ease; +} + +.upload-area:hover { + border-color: var(--primary-color); + background-color: #f8f9fa; +} + +.upload-area.drag-over { + border-color: var(--primary-color); + background-color: #e3f2fd; +} + +.upload-icon { + font-size: 48px; + margin-bottom: 1rem; +} + +.file-info { + margin-top: 1rem; + padding: 1rem; + background: #f8f9fa; + border-radius: 4px; +} + +.csv-requirements { + margin-top: 1.5rem; + padding: 1rem; + background: #fff3cd; + border: 1px solid #ffeeba; + border-radius: 4px; +} + +.csv-requirements h4 { + margin: 0 0 0.5rem 0; + color: #856404; +} + +.csv-requirements ul { + margin: 0; + padding-left: 1.5rem; +} + +.processing-section { + background: white; + border-radius: 8px; + padding: 2rem; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.progress-bar-container { + margin: 1.5rem 0; +} + +.progress-bar { + height: 24px; + background: #e9ecef; + border-radius: 12px; + overflow: hidden; +} + +.progress-bar-fill { + height: 100%; + background: linear-gradient(90deg, #4CAF50 0%, #45a049 100%); + transition: width 0.3s ease; + width: 0%; +} + +.progress-text { + text-align: center; + margin-top: 0.5rem; + font-weight: 500; +} + +.processing-status { + padding: 1rem; + background: #f8f9fa; + border-radius: 4px; + text-align: center; + margin-bottom: 1.5rem; + animation: pulse 1.5s ease-in-out infinite; +} + +/* Pulsing effect for current address */ +@keyframes pulse { + 0% { opacity: 0.8; } + 50% { opacity: 1; } + 100% { opacity: 0.8; } +} + +/* Success row animation */ +.result-success { + animation: slideIn 0.3s ease-out; +} + +.result-error { + animation: slideIn 0.3s ease-out; +} + +@keyframes slideIn { + from { + transform: translateX(-20px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +.results-preview { + margin-top: 2rem; +} + +.results-map { + height: 400px; + margin-bottom: 1.5rem; + border: 1px solid #ddd; + border-radius: 4px; +} + +.results-table-container { + max-height: 400px; + overflow-y: auto; + border: 1px solid #ddd; + border-radius: 4px; +} + +.results-table { + width: 100%; + border-collapse: collapse; +} + +.results-table th { + background: #f8f9fa; + padding: 0.75rem; + text-align: left; + font-weight: 600; + position: sticky; + top: 0; + z-index: 10; +} + +.results-table td { + padding: 0.75rem; + border-bottom: 1px solid #e9ecef; +} + +.result-success { + background: #d4edda; +} + +.result-error { + background: #f8d7da; +} + +.status-icon { + display: inline-block; + width: 20px; + height: 20px; + line-height: 20px; + text-align: center; + border-radius: 50%; + font-weight: bold; +} + +.status-icon.success { + background: #28a745; + color: white; +} + +.status-icon.error { + background: #dc3545; + color: white; +} + +.error-message { + color: #721c24; + font-style: italic; +} + +.processing-actions { + display: flex; + gap: 1rem; + justify-content: center; + margin-top: 2rem; +} + +/* Mobile responsiveness for Convert Data */ +@media (max-width: 768px) { + .upload-area { + padding: 2rem; + } + + .upload-icon { + font-size: 36px; + } + + .results-map { + height: 300px; + } + + .results-table { + font-size: 14px; + } + + .results-table th, + .results-table td { + padding: 0.5rem; + } + + .processing-actions { + flex-direction: column; + } +} + +/* NocoDB Links Section */ +.nocodb-links-container { + display: flex; + flex-direction: column; + gap: 30px; +} + +.nocodb-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; +} + +.nocodb-card { + background: white; + border-radius: 12px; + padding: 24px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); + border: 1px solid #e0e0e0; + transition: all 0.2s ease; +} + +.nocodb-card:hover { + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); + transform: translateY(-2px); +} + +.nocodb-card-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} + +.nocodb-card-header h3 { + margin: 0; + font-size: 18px; + color: #333; +} + +.nocodb-card-badge { + background: #e3f2fd; + color: #1976d2; + padding: 4px 12px; + border-radius: 20px; + font-size: 12px; + font-weight: 600; + text-transform: uppercase; +} + +.nocodb-card p { + color: #666; + margin: 0 0 20px 0; + line-height: 1.5; +} + +.nocodb-card .btn { + width: 100%; + justify-content: center; +} + +.nocodb-card .btn.btn-disabled, +.nocodb-card .btn[disabled] { + background-color: #6c757d; + border-color: #6c757d; + color: white; + opacity: 0.6; + cursor: not-allowed; + pointer-events: none; +} + +.nocodb-info { + margin-top: 20px; +} + +.info-box { + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 8px; + padding: 20px; +} + +.info-box h4 { + margin: 0 0 12px 0; + color: #495057; +} + +.info-box p { + margin: 0 0 12px 0; + color: #6c757d; + line-height: 1.5; +} + +.info-box ul { + margin: 0; + padding-left: 20px; + color: #6c757d; +} + +.info-box ul li { + margin: 6px 0; +} + +.info-box ul li strong { + color: #495057; +} + +/* Badge color variations */ +.nocodb-card:nth-child(1) .nocodb-card-badge { + background: #e8f5e8; + color: #2e7d32; +} + +.nocodb-card:nth-child(2) .nocodb-card-badge { + background: #fff3e0; + color: #f57c00; +} + +.nocodb-card:nth-child(3) .nocodb-card-badge { + background: #f3e5f5; + color: #7b1fa2; +} + +.nocodb-card:nth-child(4) .nocodb-card-badge { + background: #e1f5fe; + color: #0277bd; +} + +.nocodb-card:nth-child(5) .nocodb-card-badge { + background: #e8f5e8; + color: #388e3c; +} + +/* Responsive design for NocoDB cards */ +@media (max-width: 768px) { + .nocodb-cards { + grid-template-columns: 1fr; + gap: 16px; + } + + .nocodb-card { + padding: 20px; + } + + .nocodb-card-header h3 { + font-size: 16px; + } + + .info-box { + padding: 16px; + } +} + +/* Cuts Section Styles */ +.cuts-container { + display: flex; + flex-direction: column; + gap: 20px; +} + +.cuts-map-section { + position: relative; + height: 500px; + background: #f5f5f5; + border-radius: 8px; + overflow: hidden; + border: 1px solid #ddd; +} + +#cuts-map { + width: 100%; + height: 100%; + position: relative; +} + +.cuts-form-section, +.cuts-list-section { + margin-top: 20px; +} + +.cuts-filters { + display: grid; + grid-template-columns: 1fr 200px; + gap: 10px; + margin-bottom: 15px; +} + +.cuts-filters input, +.cuts-filters select { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: 4px; +} + +/* Button styles to match admin panel theme */ +.btn-sm { + padding: 6px 12px; + font-size: 14px; +} + +.btn-primary { + background-color: #007bff; + color: white; + border: 1px solid #007bff; +} + +.btn-primary:hover:not(:disabled) { + background-color: #0056b3; + border-color: #0056b3; +} + +.btn-secondary { + background-color: #6c757d; + color: white; + border: 1px solid #6c757d; +} + +.btn-secondary:hover:not(:disabled) { + background-color: #545b62; + border-color: #545b62; +} + +.btn-danger { + background-color: #dc3545; + color: white; + border: 1px solid #dc3545; +} + +.btn-danger:hover:not(:disabled) { + background-color: #c82333; + border-color: #c82333; +} + +.btn-success { + background-color: #28a745; + color: white; + border: 1px solid #28a745; +} + +.btn-success:hover:not(:disabled) { + background-color: #218838; + border-color: #218838; +} + +/* Responsive layout for cuts */ +@media (min-width: 1200px) { + .cuts-container { + display: grid; + grid-template-columns: 1fr; + gap: 20px; + } + + .cuts-form-section, + .cuts-list-section { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; + margin-top: 0; + } + + .cuts-form-section > *, + .cuts-list-section > * { + grid-column: span 1; + } +} + +@media (max-width: 768px) { + .cuts-map-section { + height: 400px; + } + + .cuts-filters { + grid-template-columns: 1fr; + } +} + +/* Disabled form state */ +.cut-form.disabled { + opacity: 0.7; + pointer-events: none; +} + +.cut-form.disabled input:not(#start-drawing-btn):not(#reset-form-btn):not(#cancel-edit-btn), +.cut-form.disabled textarea, +.cut-form.disabled select { + background-color: #f5f5f5; + cursor: not-allowed; +} + +.cut-form.disabled input:not(#start-drawing-btn):not(#reset-form-btn):not(#cancel-edit-btn):focus, +.cut-form.disabled textarea:focus, +.cut-form.disabled select:focus { + outline: none; + border-color: #ddd; + box-shadow: none; +} + +/* Shift User Management Modal */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 10000; +} + +.modal-content { + background-color: white; + border-radius: var(--border-radius); + padding: 0; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3); + max-width: 600px; + width: 90%; + max-height: 80vh; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 25px; + border-bottom: 1px solid #e0e0e0; + background-color: #f8f9fa; +} + +.modal-header h3 { + margin: 0; + color: var(--dark-color); +} + +.modal-body { + padding: 25px; + overflow-y: auto; + flex: 1; +} + +.shift-info { + background-color: #f8f9fa; + padding: 15px; + border-radius: var(--border-radius); + margin-bottom: 20px; +} + +.shift-info h4 { + margin: 0 0 8px 0; + color: var(--dark-color); +} + +.shift-info p { + margin: 0; + color: #666; + font-size: 14px; +} + +.add-user-section { + margin-bottom: 30px; + padding-bottom: 20px; + border-bottom: 1px solid #e0e0e0; +} + +.add-user-section h4 { + margin: 0 0 15px 0; + color: var(--dark-color); +} + +.current-volunteers-section h4 { + margin: 0 0 15px 0; + color: var(--dark-color); +} + +.volunteer-actions-header { + display: flex; + justify-content: flex-end; + align-items: center; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #eee; +} + +/* Users list header styling */ +.users-list-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + padding-bottom: 10px; + border-bottom: 2px solid #eee; +} + +.users-list-header h3 { + margin: 0; + color: var(--dark-color); +} + +/* Email modal styling */ +.modal-large { + max-width: 800px; + width: 90%; +} + +.email-form { + max-width: 100%; +} + +.rich-text-toolbar { + display: flex; + gap: 5px; + margin-bottom: 10px; + padding: 10px; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: 4px 4px 0 0; + border-bottom: none; +} + +.toolbar-btn { + padding: 8px 12px; + border: 1px solid #ccc; + background: white; + border-radius: 3px; + cursor: pointer; + font-size: 12px; + transition: all 0.2s; +} + +.toolbar-btn:hover { + background: #e9ecef; + border-color: #adb5bd; +} + +.toolbar-btn:active, .toolbar-btn.active { + background: #007bff; + color: white; + border-color: #007bff; +} + +.rich-text-editor { + min-height: 200px; + border: 1px solid #ddd; + padding: 15px; + border-radius: 0 0 4px 4px; + background: white; + font-family: inherit; + font-size: 14px; + line-height: 1.5; + outline: none; +} + +.rich-text-editor:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(160, 44, 141, 0.2); +} + +.rich-text-editor:empty:before { + content: attr(placeholder); + color: #999; + font-style: italic; +} + +.email-preview { + margin-top: 20px; + padding: 15px; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: 4px; +} + +.email-preview-content { + background: white; + padding: 20px; + border: 1px solid #ccc; + border-radius: 4px; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} + +.preview-header { + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #eee; +} + +.preview-body { + line-height: 1.6; +} + +.email-recipients-info { + background: #e3f2fd; + padding: 15px; + border-radius: 4px; + margin: 20px 0; + border-left: 4px solid #2196f3; +} + +.email-recipients-info p { + margin: 0; + color: #1565c0; +} + +.volunteer-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 15px; + background-color: #f8f9fa; + border-radius: var(--border-radius); + margin-bottom: 8px; +} + +.volunteer-info { + flex: 1; +} + +.volunteer-name { + font-weight: bold; + color: var(--dark-color); +} + +.volunteer-email { + font-size: 13px; + color: #666; + margin-top: 2px; +} + +.volunteer-actions { + display: flex; + gap: 8px; +} + +.no-volunteers { + text-align: center; + color: #666; + font-style: italic; + padding: 20px; + background-color: #f8f9fa; + border-radius: var(--border-radius); +} + +/* Enhance existing shift admin items */ +.shift-admin-item { + position: relative; +} + +.manage-volunteers-btn { + margin-left: 8px; +} + +/* User select dropdown */ +#user-select { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: var(--border-radius); + font-size: 14px; + background-color: white; +} + +#user-select:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.1); +} + +/* Responsive modal */ +@media (max-width: 768px) { + .modal-content { + width: 95%; + max-height: 90vh; + } + + .modal-body { + padding: 20px; + } + + .form-row { + flex-direction: column; + gap: 10px; + } + + .volunteer-item { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + .volunteer-actions { + align-self: flex-end; + } +} + +/* Email Progress Indicators */ +.email-progress-container { + display: none; + margin-top: 20px; + padding: 20px; + background-color: #f8f9fa; + border-radius: var(--border-radius); + border: 1px solid #dee2e6; +} + +.email-progress-container.show { + display: block; +} + +.progress-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} + +.progress-title { + font-weight: 600; + color: var(--dark-color); + font-size: 16px; +} + +.progress-stats { + display: flex; + gap: 15px; + font-size: 14px; +} + +.progress-stat { + display: flex; + align-items: center; + gap: 5px; +} + +.progress-stat.success { + color: var(--success-color); +} + +.progress-stat.error { + color: var(--error-color); +} + +.progress-stat.pending { + color: #6c757d; +} + +.progress-bar-container { + background-color: #e9ecef; + border-radius: 10px; + height: 20px; + overflow: hidden; + margin-bottom: 15px; + position: relative; +} + +.progress-bar { + height: 100%; + background: linear-gradient(90deg, var(--primary-color) 0%, #28a745 100%); + width: 0%; + transition: width 0.3s ease; + position: relative; +} + +.progress-bar.complete { + background: linear-gradient(90deg, #28a745 0%, #20c997 100%); +} + +.progress-bar.error { + background: linear-gradient(90deg, #dc3545 0%, #e74c3c 100%); +} + +.progress-text { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 12px; + font-weight: 600; + text-shadow: 1px 1px 2px rgba(0,0,0,0.5); +} + +.email-status-list { + max-height: 200px; + overflow-y: auto; + border: 1px solid #dee2e6; + border-radius: var(--border-radius); + background-color: white; +} + +.email-status-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 15px; + border-bottom: 1px solid #f8f9fa; + font-size: 14px; +} + +.email-status-item:last-child { + border-bottom: none; +} + +.email-status-recipient { + font-weight: 500; + color: var(--dark-color); +} + +.email-status-result { + display: flex; + align-items: center; + gap: 5px; + font-size: 12px; + font-weight: 600; +} + +.email-status-result.success { + color: var(--success-color); +} + +.email-status-result.error { + color: var(--error-color); +} + +.email-status-result.pending { + color: #6c757d; +} + +.progress-spinner { + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid #f3f3f3; + border-top: 2px solid #6c757d; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.progress-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 15px; +} + +.progress-close-btn { + background: var(--secondary-color); + color: white; + border: none; + padding: 8px 16px; + border-radius: var(--border-radius); + cursor: pointer; + font-size: 14px; + transition: var(--transition); +} + +.progress-close-btn:hover { + background: #5a6268; +} diff --git a/map/app/public/css/admin/README.md b/map/app/public/css/admin/README.md new file mode 100644 index 0000000..6bd2a63 --- /dev/null +++ b/map/app/public/css/admin/README.md @@ -0,0 +1,119 @@ +# Admin CSS Refactoring + +This directory contains the refactored admin panel CSS, broken down into smaller, more manageable modules for better maintainability. + +## File Structure + +``` +/css/ +├── admin.css # Main entry point with imports +├── admin.css.backup # Backup of original file +└── admin/ # Modular CSS files + ├── variables.css # CSS variables and theme configuration + ├── layout.css # Layout components (sidebar, content, navigation) + ├── forms.css # Form styles and button variations + ├── status-messages.css # Toast notifications and status indicators + ├── user-management.css # User tables, forms, and management UI + ├── walk-sheet.css # Walk sheet preview and QR code components + ├── data-convert.css # CSV upload and data processing interface + ├── nocodb-links.css # External database integration cards + ├── cuts-shifts.css # Cuts and shifts management interface + ├── modals.css # Modal dialogs and email composition + └── responsive.css # Mobile and tablet responsive styles +``` + +## Module Breakdown + +### variables.css +- CSS custom properties for colors, spacing, typography +- Theme configuration and z-index layering +- Consistent design tokens across the application + +### layout.css +- Admin container and sidebar layout +- Navigation components and menu items +- Desktop layout structure and crosshair utilities + +### forms.css +- Form field styles and validation states +- Button variations (primary, secondary, danger, etc.) +- Input groups and form actions + +### status-messages.css +- Toast notification system +- Progress bars and loading states +- Status icons and success/error indicators + +### user-management.css +- User tables with sorting and filtering +- User role badges and expiration indicators +- Volunteer management components + +### walk-sheet.css +- QR code configuration and display +- Printable walk sheet layout (8.5x11 paper) +- Form field circles and notes sections + +### data-convert.css +- CSV file upload interface +- Data processing progress indicators +- Results preview with map integration + +### nocodb-links.css +- External database integration cards +- Connection status indicators +- Information boxes and documentation + +### cuts-shifts.css +- Geographic cuts management +- Shift scheduling interface +- Map interaction components + +### modals.css +- Modal dialog base structure +- Email composition with rich text editing +- Progress tracking for email campaigns + +### responsive.css +- Mobile-first responsive design +- Tablet and desktop breakpoints +- Touch-friendly interface adaptations + +## Benefits of Refactoring + +1. **Improved Maintainability**: Each module focuses on a specific area of functionality +2. **Better Organization**: Styles are logically grouped and easy to find +3. **Faster Development**: Developers can work on specific modules without conflicts +4. **Easier Debugging**: Issues can be quickly traced to their relevant module +5. **Better Performance**: Unused CSS can be more easily identified and removed +6. **Consistent Design**: Centralized variables ensure design consistency + +## Usage + +The main `admin.css` file imports all modules automatically. No changes are needed to existing HTML files - the refactoring is transparent to the application. + +## Customization + +To customize the admin interface: + +1. **Colors and Theme**: Modify `variables.css` +2. **Layout Changes**: Edit `layout.css` +3. **Component Styles**: Update the relevant module (e.g., `forms.css` for button styles) +4. **Mobile Experience**: Adjust `responsive.css` + +## File Size Comparison + +- **Original**: ~3,011 lines in single file +- **Refactored**: ~11 focused modules averaging ~200-400 lines each +- **Total Size**: Approximately the same (no functionality removed) +- **Maintainability**: Significantly improved + +## Future Improvements + +Consider these enhancements for continued improvement: + +1. **CSS Variables Expansion**: More granular theming options +2. **Component Documentation**: Add component examples and usage guidelines +3. **Performance Optimization**: Implement critical CSS loading +4. **Design System**: Expand into a full design system with documentation +5. **Automated Testing**: Add visual regression testing for UI components diff --git a/map/app/public/css/admin/cuts-shifts.css b/map/app/public/css/admin/cuts-shifts.css new file mode 100644 index 0000000..2ee9a5e --- /dev/null +++ b/map/app/public/css/admin/cuts-shifts.css @@ -0,0 +1,197 @@ +/* Cuts and Shifts Management Interface */ +/* Map interaction, form states, and administrative controls */ + +/* Cuts Section Styles */ +.cuts-container { + display: flex; + flex-direction: column; + gap: var(--padding-base); +} + +.cuts-map-section { + position: relative; + height: 500px; + background: #f5f5f5; + border-radius: 8px; + overflow: hidden; + border: 1px solid #ddd; +} + +#cuts-map { + width: 100%; + height: 100%; + position: relative; +} + +.cuts-form-section, +.cuts-list-section { + margin-top: var(--padding-base); +} + +.cuts-filters { + display: grid; + grid-template-columns: 1fr 200px; + gap: 10px; + margin-bottom: 15px; +} + +.cuts-filters input, +.cuts-filters select { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: var(--border-radius); +} + +/* Disabled Form State */ +.cut-form.disabled { + opacity: 0.7; + pointer-events: none; +} + +.cut-form.disabled input:not(#start-drawing-btn):not(#reset-form-btn):not(#cancel-edit-btn), +.cut-form.disabled textarea, +.cut-form.disabled select { + background-color: #f5f5f5; + cursor: not-allowed; +} + +.cut-form.disabled input:not(#start-drawing-btn):not(#reset-form-btn):not(#cancel-edit-btn):focus, +.cut-form.disabled textarea:focus, +.cut-form.disabled select:focus { + outline: none; + border-color: #ddd; + box-shadow: none; +} + +/* Shifts Admin Styles */ +.shifts-admin-container { + display: grid; + grid-template-columns: 400px 1fr; + gap: 30px; +} + +.shift-form { + background: white; + padding: var(--padding-base); + border-radius: var(--border-radius); + border: 1px solid #e0e0e0; +} + +.shifts-list { + background: white; + padding: var(--padding-base); + border-radius: var(--border-radius); + border: 1px solid #e0e0e0; +} + +.shift-admin-item { + border: 1px solid #e0e0e0; + border-radius: var(--border-radius); + padding: 15px; + margin-bottom: 15px; + display: flex; + justify-content: space-between; + align-items: center; + position: relative; +} + +.shift-admin-item h4 { + margin: 0 0 10px 0; +} + +.shift-admin-item p { + margin: 5px 0; + color: var(--secondary-color); +} + +/* Shift Status Colors */ +.status-open { + color: var(--success-color); + font-weight: bold; +} + +.status-full { + color: var(--warning-color); + font-weight: bold; +} + +.status-cancelled { + color: var(--error-color); + font-weight: bold; +} + +.shift-actions { + display: flex; + gap: 10px; +} + +.manage-volunteers-btn { + margin-left: 8px; +} + +/* Shift Information Display */ +.shift-info { + background-color: #f8f9fa; + padding: 15px; + border-radius: var(--border-radius); + margin-bottom: var(--padding-base); +} + +.shift-info h4 { + margin: 0 0 8px 0; + color: var(--dark-color); +} + +.shift-info p { + margin: 0; + color: #666; + font-size: var(--font-size-base); +} + +/* Add User Section */ +.add-user-section { + margin-bottom: 30px; + padding-bottom: var(--padding-base); + border-bottom: 1px solid #e0e0e0; +} + +.add-user-section h4 { + margin: 0 0 15px 0; + color: var(--dark-color); +} + +.current-volunteers-section h4 { + margin: 0 0 15px 0; + color: var(--dark-color); +} + +.volunteer-actions-header { + display: flex; + justify-content: flex-end; + align-items: center; + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #eee; +} + +/* Large Screen Layout for Cuts */ +@media (min-width: 1200px) { + .cuts-container { + display: grid; + grid-template-columns: 1fr; + gap: var(--padding-base); + } + + .cuts-form-section, + .cuts-list-section { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--padding-base); + margin-top: 0; + } + + .cuts-form-section > *, + .cuts-list-section > * { + grid-column: span 1; + } +} diff --git a/map/app/public/css/admin/data-convert.css b/map/app/public/css/admin/data-convert.css new file mode 100644 index 0000000..f80b488 --- /dev/null +++ b/map/app/public/css/admin/data-convert.css @@ -0,0 +1,215 @@ +/* Data Conversion Interface Styles */ +/* CSV upload, processing, and results preview components */ + +.data-convert-container { + display: flex; + flex-direction: column; + gap: 2rem; + max-width: 1000px; +} + +/* Upload Section */ +.upload-section { + background: white; + border-radius: 8px; + padding: 2rem; + box-shadow: var(--shadow-sm); +} + +.upload-area { + border: 2px dashed #ddd; + border-radius: 8px; + padding: 3rem; + text-align: center; + cursor: pointer; + transition: all 0.3s ease; +} + +.upload-area:hover { + border-color: var(--primary-color); + background-color: #f8f9fa; +} + +.upload-area.drag-over { + border-color: var(--primary-color); + background-color: #e3f2fd; +} + +.upload-icon { + font-size: 48px; + margin-bottom: 1rem; +} + +.file-info { + margin-top: 1rem; + padding: 1rem; + background: #f8f9fa; + border-radius: var(--border-radius); +} + +/* CSV Requirements Section */ +.csv-requirements { + background: #f8f9fa; + border: 1px solid #dee2e6; + border-radius: 8px; + padding: var(--padding-base); + margin: var(--padding-base) 0; +} + +.csv-requirements h4 { + color: #495057; + margin-bottom: 15px; + border-bottom: 2px solid #e9ecef; + padding-bottom: 10px; +} + +.requirements-section { + margin-bottom: var(--padding-base); +} + +.requirements-section:last-child { + margin-bottom: 0; +} + +.requirements-section h5 { + color: #6c757d; + margin-bottom: 10px; + font-weight: 600; +} + +.field-mapping-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 15px; + margin-top: 10px; +} + +.field-group { + background: white; + border: 1px solid #e9ecef; + border-radius: var(--border-radius); + padding: 12px; +} + +.field-group strong { + color: #495057; + display: block; + margin-bottom: 8px; + font-size: var(--font-size-base); +} + +.field-group ul { + margin: 0; + padding-left: 15px; + list-style-type: disc; +} + +.field-group li { + font-family: 'Courier New', monospace; + font-size: var(--font-size-xs); + color: #6c757d; + margin-bottom: 3px; +} + +.csv-example { + background: #2d3748; + color: #e2e8f0; + padding: 15px; + border-radius: var(--border-radius); + font-family: 'Courier New', monospace; + font-size: var(--font-size-xs); + overflow-x: auto; + margin-top: 10px; + white-space: pre; +} + +.requirements-section ul { + margin: 10px 0; + padding-left: var(--padding-base); +} + +.requirements-section > ul > li { + margin-bottom: 5px; + color: #495057; +} + +/* Processing Section */ +.processing-section { + background: white; + border-radius: 8px; + padding: 2rem; + box-shadow: var(--shadow-sm); +} + +.processing-actions { + display: flex; + gap: 1rem; + justify-content: center; + margin-top: 2rem; +} + +/* Results Preview */ +.results-preview { + margin-top: 2rem; +} + +.results-map { + height: 400px; + margin-bottom: 1.5rem; + border: 1px solid #ddd; + border-radius: var(--border-radius); +} + +.results-table-container { + max-height: 400px; + overflow-y: auto; + border: 1px solid #ddd; + border-radius: var(--border-radius); +} + +.results-table { + width: 100%; + border-collapse: collapse; +} + +.results-table th { + background: #f8f9fa; + padding: 0.75rem; + text-align: left; + font-weight: 600; + position: sticky; + top: 0; + z-index: 10; +} + +.results-table td { + padding: 0.75rem; + border-bottom: 1px solid #e9ecef; +} + +.result-success { + background: #d4edda; + animation: slideIn 0.3s ease-out; +} + +.result-error { + background: #f8d7da; + animation: slideIn 0.3s ease-out; +} + +.error-message { + color: #721c24; + font-style: italic; +} + +/* Animation for results */ +@keyframes slideIn { + from { + transform: translateX(-20px); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} diff --git a/map/app/public/css/admin/forms.css b/map/app/public/css/admin/forms.css new file mode 100644 index 0000000..0a7a758 --- /dev/null +++ b/map/app/public/css/admin/forms.css @@ -0,0 +1,251 @@ +/* Form and Button Styles */ +/* Reusable form components and button variations */ + +/* Form Components */ +.form-group { + margin-bottom: 15px; +} + +.form-group label { + display: block; + margin-bottom: 5px; + font-weight: 500; + color: var(--dark-color); +} + +.form-group input, +.form-group select, +.form-group textarea { + width: 100%; + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: var(--border-radius); + font-size: var(--font-size-base); + transition: border-color 0.2s; +} + +.form-group input:focus, +.form-group select:focus, +.form-group textarea:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2); +} + +.form-row { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 10px; +} + +.form-actions { + display: flex; + gap: 10px; + margin-top: 20px; +} + +.help-text-inline { + font-size: var(--font-size-base); + color: #666; + margin-bottom: 15px; +} + +/* Button Base Styles */ +.btn { + padding: 8px 16px; + border: none; + border-radius: var(--border-radius); + font-size: var(--font-size-base); + font-weight: 500; + cursor: pointer; + text-decoration: none; + display: inline-flex; + align-items: center; + gap: 5px; + transition: var(--transition); + line-height: 1.4; +} + +.btn:disabled { + opacity: 0.6; + cursor: not-allowed; +} + +/* Button Size Variations */ +.btn-sm { + padding: 6px 12px; + font-size: var(--font-size-sm); +} + +.btn-lg { + padding: 12px 24px; + font-size: var(--font-size-lg); +} + +/* Button Color Variations */ +.btn-primary { + background-color: var(--primary-color); + color: white; + border: 1px solid var(--primary-color); +} + +.btn-primary:hover:not(:disabled) { + background-color: #45a049; + transform: translateY(-1px); +} + +.btn-secondary { + background-color: var(--secondary-color); + color: white; + border: 1px solid var(--secondary-color); +} + +.btn-secondary:hover:not(:disabled) { + background-color: #5a6268; + transform: translateY(-1px); +} + +.btn-success { + background-color: var(--success-color); + color: white; + border: 1px solid var(--success-color); +} + +.btn-success:hover:not(:disabled) { + background-color: #218838; + border-color: #1e7e34; +} + +.btn-danger { + background-color: var(--error-color); + color: white; + border: 1px solid var(--error-color); +} + +.btn-danger:hover:not(:disabled) { + background-color: #c82333; + border-color: #bd2130; + transform: translateY(-1px); +} + +.btn-warning { + background-color: var(--warning-color); + color: #212529; + border: 1px solid var(--warning-color); +} + +.btn-warning:hover:not(:disabled) { + background-color: #e0a800; + border-color: #d39e00; +} + +.btn-info { + background-color: var(--info-color); + color: white; + border: 1px solid var(--info-color); +} + +.btn-info:hover:not(:disabled) { + background-color: #138496; + border-color: #117a8b; +} + +/* Button States */ +.btn-disabled, +.btn[disabled] { + background-color: var(--secondary-color); + border-color: var(--secondary-color); + color: white; + opacity: 0.6; + cursor: not-allowed; + pointer-events: none; +} + +/* Enhanced Form Styles for Specific Contexts */ +.user-form input[type="email"], +.user-form input[type="password"], +.user-form input[type="text"] { + width: 100%; + padding: 12px; + border: 2px solid #ddd; + border-radius: var(--border-radius); + font-size: var(--font-size-base); + transition: var(--transition); + background-color: #fafafa; +} + +.user-form input:focus { + outline: none; + border-color: var(--primary-color); + background-color: white; + box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1); +} + +.user-form .form-group { + margin-bottom: 20px; +} + +.user-form .form-group label { + display: block; + margin-bottom: 8px; + font-weight: 600; + color: var(--dark-color); + font-size: var(--font-size-base); +} + +.user-form .form-group label input[type="checkbox"] { + margin-right: 8px; + transform: scale(1.1); +} + +.user-form .form-actions { + display: flex; + gap: 10px; + margin-top: 25px; +} + +.user-form .form-actions .btn { + flex: 1; + padding: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + font-size: var(--font-size-sm); +} + +/* Special Form Field Types */ +.form-field { + height: 30px; + background-color: #fafafa; + border: 1px solid #eee; +} + +.form-field.circles { + height: auto; + background: none; + border: none; + display: flex; + gap: 15px; + align-items: center; + padding: 5px 0; +} + +.circle-option { + display: flex; + align-items: center; + gap: 5px; + font-size: 11pt; +} + +.circle { + width: 24px; + height: 24px; + border: 2px solid #333; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 10pt; + font-weight: 500; + background-color: white; +} diff --git a/map/app/public/css/admin/layout.css b/map/app/public/css/admin/layout.css new file mode 100644 index 0000000..df2e577 --- /dev/null +++ b/map/app/public/css/admin/layout.css @@ -0,0 +1,244 @@ +/* Admin Layout & Navigation Styles */ +/* Core layout components: container, sidebar, content area, navigation */ + +.admin-container { + display: flex; + height: calc(100vh - var(--header-height)); + height: calc(var(--app-height) - var(--header-height)); + background-color: #f5f5f5; + width: 100%; + max-width: 100vw; + overflow: hidden; +} + +.admin-sidebar { + width: var(--sidebar-width); + min-width: var(--sidebar-width); + max-width: var(--sidebar-width); + background-color: white; + border-right: 1px solid #e0e0e0; + padding: var(--padding-base); + box-sizing: border-box; + flex-shrink: 0; +} + +.admin-sidebar h2 { + font-size: var(--font-size-xl); + margin-bottom: var(--padding-base); + color: var(--dark-color); +} + +.admin-nav { + display: flex; + flex-direction: column; + gap: 5px; +} + +.admin-nav a { + padding: 10px 15px; + color: var(--dark-color); + text-decoration: none; + border-radius: var(--border-radius); + transition: var(--transition); +} + +.admin-nav a:hover { + background-color: var(--light-color); +} + +.admin-nav a.active { + background-color: var(--primary-color); + color: white; +} + +.admin-content { + flex: 1; + padding: 30px; + overflow-y: auto; + overflow-x: hidden; + width: 100%; + min-width: 0; /* Allow flex item to shrink */ + box-sizing: border-box; + max-height: calc(100vh - var(--header-height)); + max-height: calc(var(--app-height) - var(--header-height)); +} + +.admin-section { + background-color: white; + border-radius: var(--border-radius); + padding: 30px; + box-shadow: var(--shadow-sm); + width: 100%; + max-width: 100%; + box-sizing: border-box; +} + +.admin-section h2 { + margin-bottom: 15px; + color: var(--dark-color); +} + +.admin-section p { + color: #666; + margin-bottom: 25px; +} + +.admin-map-container { + display: grid; + grid-template-columns: 1fr 300px; + gap: var(--padding-base); + margin-top: var(--padding-base); +} + +.admin-map { + height: 500px; + border-radius: var(--border-radius); + border: 1px solid #ddd; + position: relative; + cursor: crosshair; +} + +.admin-map .leaflet-container { + cursor: crosshair; +} + +.location-controls { + padding: var(--padding-base); + background-color: #f9f9f9; + border-radius: var(--border-radius); +} + +.location-controls .form-group { + margin-bottom: 15px; +} + +.location-controls .form-actions { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: var(--padding-base); +} + +.help-text { + margin-top: var(--padding-base); + padding: 15px; + background-color: #e3f2fd; + border-radius: var(--border-radius); + font-size: var(--font-size-base); +} + +.help-text p { + margin: 0; + color: #1976d2; +} + +.admin-info { + display: flex; + align-items: center; + gap: 10px; + color: rgba(255,255,255,0.9); + font-size: var(--font-size-base); +} + +/* Crosshair styling */ +.crosshair { + pointer-events: none; + z-index: var(--z-dropdown); +} + +.crosshair div { + border-radius: 1px; + opacity: 0.8; +} + +/* Mobile Menu Components */ +.mobile-menu-toggle { + display: none; + background: none; + border: none; + padding: 8px; + cursor: pointer; + position: relative; + width: 40px; + height: 40px; +} + +.mobile-menu-toggle span { + display: block; + width: 24px; + height: 3px; + background: white; + margin: 5px auto; + transition: var(--transition); + border-radius: 2px; +} + +.mobile-menu-toggle.active span:nth-child(1) { + transform: rotate(45deg) translate(5px, 5px); +} + +.mobile-menu-toggle.active span:nth-child(2) { + opacity: 0; +} + +.mobile-menu-toggle.active span:nth-child(3) { + transform: rotate(-45deg) translate(7px, -6px); +} + +/* Sidebar Header & Footer (mobile) */ +.sidebar-header { + display: none; + justify-content: space-between; + align-items: center; + padding-bottom: var(--padding-base); + border-bottom: 1px solid #e0e0e0; + margin-bottom: var(--padding-base); +} + +.close-sidebar { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: var(--dark-color); + padding: 0; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; +} + +.sidebar-footer { + display: none; + margin-top: auto; + padding-top: var(--padding-base); + border-top: 1px solid #e0e0e0; +} + +.mobile-admin-info { + margin-top: 15px; + font-size: var(--font-size-base); + color: #666; + text-align: center; +} + +/* Navigation Icons */ +.nav-icon { + margin-right: 10px; + font-size: var(--font-size-xl); +} + +/* Utility Classes */ +.desktop-only { + display: flex; +} + +.mobile-only { + display: none; +} + +.btn-block { + width: 100%; + justify-content: center; +} diff --git a/map/app/public/css/admin/modals.css b/map/app/public/css/admin/modals.css new file mode 100644 index 0000000..88b18d6 --- /dev/null +++ b/map/app/public/css/admin/modals.css @@ -0,0 +1,310 @@ +/* Modal Dialogs and Email Interface */ +/* Modal layouts, email composition, and interactive dialogs */ + +/* Modal Base Structure */ +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: var(--z-modal); +} + +.modal-content { + background-color: white; + border-radius: var(--border-radius); + padding: 0; + box-shadow: var(--shadow-lg); + max-width: 600px; + width: 90%; + max-height: 80vh; + overflow: hidden; + display: flex; + flex-direction: column; +} + +.modal-large { + max-width: 800px; + width: 90%; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: var(--padding-base) 25px; + border-bottom: 1px solid #e0e0e0; + background-color: #f8f9fa; +} + +.modal-header h3 { + margin: 0; + color: var(--dark-color); +} + +.modal-body { + padding: 25px; + overflow-y: auto; + flex: 1; +} + +/* Email Form Components */ +.email-form { + max-width: 100%; +} + +.rich-text-toolbar { + display: flex; + gap: 5px; + margin-bottom: 10px; + padding: 10px; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: var(--border-radius) var(--border-radius) 0 0; + border-bottom: none; +} + +.toolbar-btn { + padding: 8px 12px; + border: 1px solid #ccc; + background: white; + border-radius: 3px; + cursor: pointer; + font-size: var(--font-size-xs); + transition: var(--transition); +} + +.toolbar-btn:hover { + background: #e9ecef; + border-color: #adb5bd; +} + +.toolbar-btn:active, +.toolbar-btn.active { + background: #007bff; + color: white; + border-color: #007bff; +} + +.rich-text-editor { + min-height: 200px; + border: 1px solid #ddd; + padding: 15px; + border-radius: 0 0 var(--border-radius) var(--border-radius); + background: white; + font-family: inherit; + font-size: var(--font-size-base); + line-height: 1.5; + outline: none; +} + +.rich-text-editor:focus { + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(160, 44, 141, 0.2); +} + +.rich-text-editor:empty:before { + content: attr(placeholder); + color: #999; + font-style: italic; +} + +/* Email Preview */ +.email-preview { + margin-top: var(--padding-base); + padding: 15px; + background: #f8f9fa; + border: 1px solid #ddd; + border-radius: var(--border-radius); +} + +.email-preview-content { + background: white; + padding: var(--padding-base); + border: 1px solid #ccc; + border-radius: var(--border-radius); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; +} + +.preview-header { + margin-bottom: 15px; + padding-bottom: 10px; + border-bottom: 1px solid #eee; +} + +.preview-body { + line-height: 1.6; +} + +.email-recipients-info { + background: #e3f2fd; + padding: 15px; + border-radius: var(--border-radius); + margin: var(--padding-base) 0; + border-left: 4px solid #2196f3; +} + +.email-recipients-info p { + margin: 0; + color: #1565c0; +} + +/* Email Progress Components */ +.email-progress-container { + display: none; + margin-top: var(--padding-base); + padding: var(--padding-base); + background-color: #f8f9fa; + border-radius: var(--border-radius); + border: 1px solid #dee2e6; +} + +.email-progress-container.show { + display: block; +} + +.progress-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} + +.progress-title { + font-weight: 600; + color: var(--dark-color); + font-size: var(--font-size-lg); +} + +.progress-stats { + display: flex; + gap: 15px; + font-size: var(--font-size-base); +} + +.progress-stat { + display: flex; + align-items: center; + gap: 5px; +} + +.progress-stat.success { + color: var(--success-color); +} + +.progress-stat.error { + color: var(--error-color); +} + +.progress-stat.pending { + color: #6c757d; +} + +.progress-bar-container { + background-color: #e9ecef; + border-radius: 10px; + height: var(--padding-base); + overflow: hidden; + margin-bottom: 15px; + position: relative; +} + +.progress-bar { + height: 100%; + background: linear-gradient(90deg, var(--primary-color) 0%, var(--success-color) 100%); + width: 0%; + transition: width 0.3s ease; + position: relative; +} + +.progress-bar.complete { + background: linear-gradient(90deg, var(--success-color) 0%, #20c997 100%); +} + +.progress-bar.error { + background: linear-gradient(90deg, var(--error-color) 0%, #e74c3c 100%); +} + +.progress-text { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: var(--font-size-xs); + font-weight: 600; + text-shadow: 1px 1px 2px rgba(0,0,0,0.5); +} + +.email-status-list { + max-height: 200px; + overflow-y: auto; + border: 1px solid #dee2e6; + border-radius: var(--border-radius); + background-color: white; +} + +.email-status-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 15px; + border-bottom: 1px solid #f8f9fa; + font-size: var(--font-size-base); +} + +.email-status-item:last-child { + border-bottom: none; +} + +.email-status-recipient { + font-weight: 500; + color: var(--dark-color); +} + +.email-status-result { + display: flex; + align-items: center; + gap: 5px; + font-size: var(--font-size-xs); + font-weight: 600; +} + +.email-status-result.success { + color: var(--success-color); +} + +.email-status-result.error { + color: var(--error-color); +} + +.email-status-result.pending { + color: #6c757d; +} + +.progress-actions { + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 15px; +} + +.progress-close-btn { + background: var(--secondary-color); + color: white; + border: none; + padding: 8px 16px; + border-radius: var(--border-radius); + cursor: pointer; + font-size: var(--font-size-base); + transition: var(--transition); +} + +.progress-close-btn:hover { + background: #5a6268; +} diff --git a/map/app/public/css/admin/nocodb-links.css b/map/app/public/css/admin/nocodb-links.css new file mode 100644 index 0000000..4607abe --- /dev/null +++ b/map/app/public/css/admin/nocodb-links.css @@ -0,0 +1,135 @@ +/* NocoDB Links and External System Integration */ +/* Card layouts and integration status for external databases */ + +.nocodb-links-container { + display: flex; + flex-direction: column; + gap: 30px; +} + +.nocodb-cards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: var(--padding-base); +} + +.nocodb-card { + background: white; + border-radius: 12px; + padding: 24px; + box-shadow: var(--shadow-md); + border: 1px solid #e0e0e0; + transition: var(--transition); +} + +.nocodb-card:hover { + box-shadow: var(--shadow-lg); + transform: translateY(-2px); +} + +.nocodb-card-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 12px; +} + +.nocodb-card-header h3 { + margin: 0; + font-size: var(--font-size-xl); + color: #333; +} + +.nocodb-card-badge { + background: #e3f2fd; + color: #1976d2; + padding: 4px 12px; + border-radius: 20px; + font-size: var(--font-size-xs); + font-weight: 600; + text-transform: uppercase; +} + +.nocodb-card p { + color: #666; + margin: 0 0 var(--padding-base) 0; + line-height: 1.5; +} + +.nocodb-card .btn { + width: 100%; + justify-content: center; +} + +.nocodb-card .btn.btn-disabled, +.nocodb-card .btn[disabled] { + background-color: var(--secondary-color); + border-color: var(--secondary-color); + color: white; + opacity: 0.6; + cursor: not-allowed; + pointer-events: none; +} + +/* Info Box */ +.nocodb-info { + margin-top: var(--padding-base); +} + +.info-box { + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 8px; + padding: var(--padding-base); +} + +.info-box h4 { + margin: 0 0 12px 0; + color: #495057; +} + +.info-box p { + margin: 0 0 12px 0; + color: #6c757d; + line-height: 1.5; +} + +.info-box ul { + margin: 0; + padding-left: var(--padding-base); + color: #6c757d; +} + +.info-box ul li { + margin: 6px 0; +} + +.info-box ul li strong { + color: #495057; +} + +/* Badge Color Variations */ +.nocodb-card:nth-child(1) .nocodb-card-badge { + background: #e8f5e8; + color: #2e7d32; +} + +.nocodb-card:nth-child(2) .nocodb-card-badge { + background: #fff3e0; + color: #f57c00; +} + +.nocodb-card:nth-child(3) .nocodb-card-badge { + background: #f3e5f5; + color: #7b1fa2; +} + +.nocodb-card:nth-child(4) .nocodb-card-badge { + background: #e1f5fe; + color: #0277bd; +} + +.nocodb-card:nth-child(5) .nocodb-card-badge { + background: #e8f5e8; + color: #388e3c; +} diff --git a/map/app/public/css/admin/responsive.css b/map/app/public/css/admin/responsive.css new file mode 100644 index 0000000..340fe30 --- /dev/null +++ b/map/app/public/css/admin/responsive.css @@ -0,0 +1,717 @@ +/* Responsive Design and Media Queries */ +/* Mobile, tablet, and desktop layout adaptations */ + +/* Mobile Status Container */ +@media (max-width: 768px) { + .status-container { + left: 10px; + right: 10px; + top: 10px; + width: auto; + max-width: 100%; + transform: none; + display: flex; + flex-direction: column; + align-items: center; + } + + .status-message { + width: auto; + max-width: 90vw; + box-sizing: border-box; + font-size: var(--font-size-sm); + text-align: center; + } +} + +/* Very Small Screens (under 480px) */ +@media (max-width: 480px) { + .status-container { + top: 70px; + max-width: calc(100vw - 20px); + min-width: 260px; + } +} + +@media (max-width: 360px) { + .status-container { + top: 65px; + max-width: calc(100vw - 15px); + min-width: auto; + } + + .status-message { + padding: 10px 12px; + font-size: var(--font-size-sm); + } +} + +/* Mobile Layout Adaptations */ +@media (max-width: 768px) { + /* Show mobile menu toggle */ + .mobile-menu-toggle { + display: block; + } + + /* Header adjustments */ + .header { + padding: 10px 15px; + display: flex; + align-items: center; + gap: 15px; + } + + .header h1 { + flex: 1; + text-align: center; + margin: 0; + font-size: var(--font-size-xl); + } + + /* Hide desktop elements */ + .desktop-only { + display: none !important; + } + + .mobile-only { + display: flex; + } + + /* Admin container becomes full width */ + .admin-container { + flex-direction: column; + height: calc(100vh - 50px); + height: calc(var(--app-height) - 50px); + } + + /* Sidebar as overlay */ + .admin-sidebar { + position: fixed; + top: 0; + left: -280px; + width: var(--sidebar-width-mobile); + height: 100vh; + height: var(--app-height); + background: white; + z-index: var(--z-modal-backdrop); + transition: left 0.3s ease; + box-shadow: var(--shadow-md); + display: flex; + flex-direction: column; + padding: var(--padding-base); + } + + .admin-sidebar.active { + left: 0; + } + + /* Show sidebar header and footer on mobile */ + .sidebar-header { + display: flex; + } + + .sidebar-footer { + display: block; + } + + /* Admin nav mobile styling */ + .admin-nav { + flex: 1; + display: flex; + flex-direction: column; + gap: 8px; + margin: var(--padding-base) 0; + } + + .admin-nav a { + display: flex; + align-items: center; + padding: 10px 12px; + font-size: var(--font-size-base); + border-radius: var(--border-radius); + background-color: #f5f5f5; + border: 1px solid #e0e0e0; + transition: var(--transition); + } + + .admin-nav a:active { + background-color: #e0e0e0; + transform: scale(0.98); + } + + .admin-nav a.active { + background-color: var(--primary-color); + color: white; + border-color: var(--primary-color); + } + + .admin-nav a:hover { + background-color: #ebebeb; + border-color: #d0d0d0; + } + + .admin-nav a.active:hover { + background-color: #0056b3; + border-color: #0056b3; + } + + /* Navigation icons and text */ + .nav-icon { + font-size: var(--font-size-lg); + margin-right: 8px; + flex-shrink: 0; + } + + .nav-text { + font-size: var(--font-size-base); + font-weight: 500; + } + + /* Content adjustments */ + .admin-content { + padding: 15px; + } + + .admin-section { + padding: 15px; + } + + /* Admin container mobile layout */ + .admin-container { + flex-direction: column; + } + + .admin-sidebar { + width: 100%; + border-right: none; + border-bottom: 1px solid #e0e0e0; + padding: 15px; + } + + .admin-nav { + flex-direction: row; + overflow-x: auto; + gap: 10px; + padding-bottom: 10px; + } + + .admin-nav a { + white-space: nowrap; + min-width: auto; + padding: 8px 12px; + font-size: var(--font-size-base); + } + + .header .header-actions { + display: flex !important; + gap: 10px; + flex-wrap: wrap; + } + + .header .header-actions .btn { + padding: 6px 10px; + font-size: var(--font-size-sm); + } + + .admin-info { + font-size: var(--font-size-xs); + } + + .admin-map-container { + grid-template-columns: 1fr; + gap: 15px; + } + + .admin-map { + height: 250px; + } + + .form-row { + grid-template-columns: 1fr; + } + + /* User management mobile */ + .users-admin-container { + grid-template-columns: 1fr; + gap: 25px; + } + + .user-form, + .users-list { + padding: 25px; + width: 100%; + box-sizing: border-box; + } + + .users-list { + overflow-x: auto; + width: 100%; + box-sizing: border-box; + } + + /* Data Convert responsive styles */ + .field-mapping-grid { + grid-template-columns: 1fr; + } + + .csv-example { + font-size: 10px; + padding: 10px; + } + + .csv-requirements { + padding: 15px; + } + + /* NocoDB cards responsive */ + .nocodb-cards { + grid-template-columns: 1fr; + gap: 16px; + } + + .nocodb-card { + padding: var(--padding-base); + } + + .nocodb-card-header h3 { + font-size: var(--font-size-lg); + } + + .info-box { + padding: 16px; + } + + /* Shifts admin mobile */ + .shifts-admin-container { + grid-template-columns: 1fr; + gap: var(--padding-base); + } + + .shift-admin-item { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + .shift-actions { + width: 100%; + justify-content: flex-end; + } + + /* Walk sheet container mobile */ + .walk-sheet-container { + display: flex !important; + flex-direction: column !important; + gap: var(--padding-base); + } + + .walk-sheet-config { + order: 1 !important; + margin-bottom: var(--padding-base); + } + + /* Walk sheet mobile */ + .walk-sheet-preview { + min-width: 0; + max-width: 100%; + width: 100%; + min-height: 500px; + padding: 10px; + overflow: hidden; + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + } + + .walk-sheet-preview #walk-sheet-preview-content { + transform: scale(0.35); + transform-origin: top center; + margin: 0 auto; + width: 816px; + max-width: none; + margin-bottom: -600px; + position: relative; + left: 50%; + margin-left: -408px; + } + + /* Users table mobile card layout */ + .users-table { + font-size: var(--font-size-base); + min-width: auto; + width: 100%; + table-layout: auto; + border-collapse: collapse; + display: block; + } + + .users-table thead { + display: none; + } + + .users-table tbody, + .users-table tr, + .users-table td { + display: block; + width: 100% !important; + box-sizing: border-box; + } + + .users-table tr { + margin-bottom: 15px; + border: 1px solid #e0e0e0; + border-radius: var(--border-radius); + padding: 10px; + } + + .users-table td { + padding: 8px 0; + border: none; + display: flex; + align-items: center; + word-wrap: break-word; + overflow-wrap: break-word; + } + + .users-table td::before { + content: attr(data-label); + font-weight: bold; + width: 80px; + min-width: 80px; + margin-right: 10px; + color: #555; + } + + .user-actions { + flex-direction: column; + gap: 8px; + width: 100%; + } + + .user-actions .btn { + font-size: var(--font-size-xs); + padding: 8px 10px; + width: 100%; + text-align: center; + min-height: 36px; + line-height: 1.3; + } + + .user-form .form-actions { + flex-direction: column; + } + + /* Modal responsive */ + .modal-content { + width: 95%; + max-height: 90vh; + } + + .modal-body { + padding: var(--padding-base); + } + + .form-row { + flex-direction: column; + gap: 10px; + } + + .volunteer-item { + flex-direction: column; + align-items: flex-start; + gap: 10px; + } + + .volunteer-actions { + align-self: flex-end; + } + + .processing-actions { + flex-direction: column; + } +} + +/* Very Small Screens (under 480px) */ +@media (max-width: 480px) { + .admin-sidebar { + width: 260px; + left: -260px; + padding: 12px; + } + + .admin-nav { + gap: 6px; + margin: 15px 0; + } + + .admin-nav a { + padding: 8px 10px; + font-size: var(--font-size-sm); + } + + .nav-icon { + font-size: var(--font-size-base); + margin-right: 6px; + } + + .nav-text { + font-size: var(--font-size-sm); + } + + .admin-content { + padding: 10px; + } + + .admin-section { + padding: 10px; + } + + .admin-sidebar { + padding: 10px; + } + + .admin-nav a { + padding: 6px 10px; + font-size: var(--font-size-sm); + } + + .header .header-actions .btn { + padding: 5px 8px; + font-size: var(--font-size-xs); + } + + .admin-info { + display: none; + } + + .admin-map { + height: 200px; + } + + .walk-sheet-preview .walk-sheet-page { + transform: scale(0.25); + transform-origin: center top; + margin-bottom: -750px; + margin-left: auto; + margin-right: auto; + } + + .form-group input, + .form-group select { + font-size: 16px; /* Prevent zoom on iOS */ + } + + .btn { + min-height: 44px; + padding: 10px 16px; + } + + .btn-sm { + min-height: 36px; + padding: 8px 12px; + } +} + +/* Ultra Small Screens (under 360px) */ +@media (max-width: 360px) { + .admin-sidebar { + width: 240px; + left: -240px; + } + + .admin-nav a { + padding: 8px; + font-size: var(--font-size-xs); + } + + .nav-icon { + font-size: var(--font-size-sm); + margin-right: 5px; + } + + .nav-text { + font-size: var(--font-size-xs); + } +} + +/* Tablet Responsive (768px - 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + .admin-sidebar { + width: var(--sidebar-width-tablet); + min-width: var(--sidebar-width-tablet); + max-width: var(--sidebar-width-tablet); + padding: 15px; + } + + .admin-content { + padding: var(--padding-base); + } + + .admin-section { + padding: var(--padding-base); + } + + .users-admin-container { + grid-template-columns: 1fr; + gap: var(--padding-base); + } + + .shifts-admin-container { + grid-template-columns: 1fr; + gap: var(--padding-base); + } + + .user-form, + .users-list { + padding: var(--padding-base); + } + + .users-table { + font-size: var(--font-size-sm); + } + + .users-table th, + .users-table td { + padding: 10px 8px; + } +} + +/* Small Laptop (1024px - 1200px) */ +@media (max-width: 1200px) and (min-width: 1025px) { + .admin-sidebar { + width: var(--sidebar-width-tablet); + min-width: var(--sidebar-width-tablet); + max-width: var(--sidebar-width-tablet); + } + + .users-admin-container, + .shifts-admin-container { + grid-template-columns: 1fr 1.5fr; + gap: 25px; + } + + .walk-sheet-container { + display: flex !important; + flex-direction: column !important; + gap: var(--padding-base); + } + + .walk-sheet-config { + order: 1 !important; + margin-bottom: var(--padding-base); + } + + .walk-sheet-preview { + order: 2 !important; + padding: var(--padding-base); + min-height: auto; + max-width: 100vw; + overflow-x: auto; + display: flex; + justify-content: center; + align-items: flex-start; + } + + .walk-sheet-preview .walk-sheet-page { + transform: scale(0.75); + transform-origin: center top; + margin-bottom: -200px; + max-width: 100%; + margin-left: auto; + margin-right: auto; + } +} + +@media (max-width: 1000px) { + .walk-sheet-preview .walk-sheet-page { + transform: scale(0.5); + transform-origin: center top; + margin-bottom: -400px; + margin-left: auto; + margin-right: auto; + } + + .cuts-map-section { + height: 400px; + } + + .cuts-filters { + grid-template-columns: 1fr; + } +} + +/* Header Responsive */ +@media (max-width: 1024px) { + .header { + padding: 10px 15px; + } + + .header h1 { + font-size: var(--padding-base); + } + + .header .header-actions { + gap: 8px; + } +} + +/* Responsive Table for Very Small Screens */ +@media (max-width: 640px) { + .users-table { + border: 0; + } + + .users-table thead { + border: none; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; + } + + .users-table tr { + border-bottom: 3px solid #ddd; + display: block; + margin-bottom: 10px; + padding: 10px; + background: #f8f9fa; + border-radius: var(--border-radius); + } + + .users-table td { + border: none; + border-bottom: 1px solid #eee; + display: block; + font-size: var(--font-size-sm); + text-align: right; + padding: 8px 10px; + } + + .users-table td::before { + content: attr(data-label); + float: left; + font-weight: bold; + text-transform: uppercase; + font-size: 11px; + color: #666; + } + + .users-table td:last-child { + border-bottom: 0; + } + + .user-actions { + justify-content: flex-end; + margin-top: 8px; + } + + .user-actions .btn { + width: auto !important; + min-width: 80px; + flex: none; + } +} diff --git a/map/app/public/css/admin/status-messages.css b/map/app/public/css/admin/status-messages.css new file mode 100644 index 0000000..ac95c9f --- /dev/null +++ b/map/app/public/css/admin/status-messages.css @@ -0,0 +1,189 @@ +/* Status Messages and Notifications */ +/* Toast notifications, alerts, and status indicators */ + +.status-container { + position: fixed; + top: 20px; + right: 20px; + z-index: var(--z-toast); + max-width: 400px; + pointer-events: none; /* Allow clicks to pass through container */ +} + +.status-message { + padding: 12px 16px; + margin-bottom: 10px; + border-radius: var(--border-radius); + font-size: var(--font-size-base); + animation: slideIn 0.3s ease-out; + pointer-events: auto; /* Allow interaction with individual messages */ + cursor: pointer; /* Show it's clickable for dismissal */ +} + +.status-message.success { + background-color: #d4edda; + color: #155724; + border: 1px solid #c3e6cb; +} + +.status-message.error { + background-color: #f8d7da; + color: #721c24; + border: 1px solid #f5c6cb; +} + +.status-message.info { + background-color: #cce7ff; + color: #004085; + border: 1px solid #b3d7ff; +} + +.status-message.warning { + background-color: #fff3cd; + color: #856404; + border: 1px solid #ffeeba; +} + +@keyframes slideIn { + from { + transform: translateX(100%); + opacity: 0; + } + to { + transform: translateX(0); + opacity: 1; + } +} + +/* QR Code Generation Status */ +.qr-code-group.generating::after { + content: 'Generating QR Code...'; + position: absolute; + top: 0; + right: 0; + font-size: var(--font-size-xs); + color: var(--primary-color); + background: white; + padding: 2px 8px; + border-radius: 3px; + box-shadow: var(--shadow-sm); +} + +/* QR code stored indicator */ +.qr-status { + font-size: var(--font-size-xs); + margin-top: 5px; +} + +.qr-status.stored { + color: var(--success-color); +} + +.qr-status.pending { + color: var(--warning-color); +} + +/* Loading Messages */ +.loading-message, +.empty-message { + text-align: center; + padding: 40px 20px; + color: #666; + font-style: italic; + background-color: #f8f9fa; + border-radius: var(--border-radius); + margin-top: 15px; +} + +.loading-message { + background-color: #e3f2fd; + color: #1976d2; +} + +/* Processing Status */ +.processing-status { + padding: 1rem; + background: #f8f9fa; + border-radius: var(--border-radius); + text-align: center; + margin-bottom: 1.5rem; + animation: pulse 1.5s ease-in-out infinite; +} + +@keyframes pulse { + 0% { opacity: 0.8; } + 50% { opacity: 1; } + 100% { opacity: 0.8; } +} + +/* Progress Bars */ +.progress-bar-container { + margin: 1.5rem 0; +} + +.progress-bar { + height: 24px; + background: #e9ecef; + border-radius: 12px; + overflow: hidden; +} + +.progress-bar-fill { + height: 100%; + background: linear-gradient(90deg, var(--primary-color) 0%, #45a049 100%); + transition: width 0.3s ease; + width: 0%; +} + +.progress-text { + text-align: center; + margin-top: 0.5rem; + font-weight: 500; +} + +/* Status Icons */ +.status-icon { + display: inline-block; + width: 20px; + height: 20px; + line-height: 20px; + text-align: center; + border-radius: 50%; + font-weight: bold; +} + +.status-icon.success { + background: var(--success-color); + color: white; +} + +.status-icon.error { + background: var(--error-color); + color: white; +} + +.status-icon.warning { + background: var(--warning-color); + color: #212529; +} + +.status-icon.info { + background: var(--info-color); + color: white; +} + +/* Spinner */ +.progress-spinner { + display: inline-block; + width: 12px; + height: 12px; + border: 2px solid #f3f3f3; + border-top: 2px solid #6c757d; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} diff --git a/map/app/public/css/admin/user-management.css b/map/app/public/css/admin/user-management.css new file mode 100644 index 0000000..6f6222b --- /dev/null +++ b/map/app/public/css/admin/user-management.css @@ -0,0 +1,219 @@ +/* User Management Interface Styles */ +/* Tables, user forms, and user-related components */ + +.users-admin-container { + display: grid; + grid-template-columns: 1fr 2fr; + gap: 30px; + margin-top: var(--padding-base); + width: 100%; + box-sizing: border-box; + overflow: hidden; +} + +.user-form, +.users-list { + background: white; + border-radius: var(--border-radius); + padding: 25px; + box-shadow: var(--shadow-sm); + border: 1px solid #e0e0e0; + width: 100%; + box-sizing: border-box; +} + +.users-list { + overflow-x: auto; + min-width: 0; +} + +.user-form h3, +.users-list h3 { + margin-bottom: var(--padding-base); + color: var(--dark-color); + border-bottom: 2px solid var(--primary-color); + padding-bottom: 10px; + font-size: var(--font-size-xl); +} + +/* Users Table */ +.users-table { + width: 100%; + border-collapse: collapse; + margin-top: 15px; + font-size: var(--font-size-base); + table-layout: auto; + min-width: 600px; + box-sizing: border-box; +} + +.users-table th, +.users-table td { + padding: 12px; + text-align: left; + border-bottom: 1px solid #e0e0e0; +} + +.users-table th { + background-color: #f8f9fa; + font-weight: 600; + color: var(--dark-color); + font-size: var(--font-size-sm); + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.users-table tbody tr { + transition: background-color 0.2s; +} + +.users-table tbody tr:hover { + background-color: #f8f9fa; +} + +/* User Role Badges */ +.user-role { + padding: 4px 8px; + border-radius: 12px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + display: inline-block; + text-align: center; + min-width: 45px; + line-height: 1.2; +} + +.user-role.admin { + background-color: var(--error-color); + color: white; +} + +.user-role.user { + background-color: var(--success-color); + color: white; +} + +.user-role.temp { + background-color: var(--warning-color); + color: white; +} + +/* User Expiration Information */ +.expiration-info { + display: inline-block; + margin-left: 8px; + font-size: 0.8em; + color: #666; + background: #f5f5f5; + padding: 2px 6px; + border-radius: 3px; +} + +.expiration-warning { + color: #ff5722; + font-weight: bold; +} + +.expires-soon { + background-color: #fff3cd; + border-left: 4px solid var(--warning-color); +} + +.expired { + background-color: #f8d7da; + border-left: 4px solid var(--error-color); + opacity: 0.7; +} + +/* User Actions */ +.user-actions { + display: flex; + gap: 8px; +} + +.user-actions .btn { + padding: 6px 12px; + font-size: var(--font-size-xs); + border-radius: var(--border-radius); + font-weight: 500; +} + +/* Users List Header */ +.users-list-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: var(--padding-base); + padding-bottom: 10px; + border-bottom: 2px solid #eee; +} + +.users-list-header h3 { + margin: 0; + color: var(--dark-color); +} + +/* Helper Text */ +.help-text { + font-size: 0.85em; + color: #666; + margin-top: 4px; + display: block; +} + +/* Volunteer Management */ +.volunteer-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 15px; + background-color: #f8f9fa; + border-radius: var(--border-radius); + margin-bottom: 8px; +} + +.volunteer-info { + flex: 1; +} + +.volunteer-name { + font-weight: bold; + color: var(--dark-color); +} + +.volunteer-email { + font-size: var(--font-size-sm); + color: #666; + margin-top: 2px; +} + +.volunteer-actions { + display: flex; + gap: 8px; +} + +.no-volunteers { + text-align: center; + color: #666; + font-style: italic; + padding: var(--padding-base); + background-color: #f8f9fa; + border-radius: var(--border-radius); +} + +/* User Select Dropdown */ +#user-select { + padding: 8px 12px; + border: 1px solid #ddd; + border-radius: var(--border-radius); + font-size: var(--font-size-base); + background-color: white; +} + +#user-select:focus { + outline: none; + border-color: var(--primary-color); + box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.1); +} diff --git a/map/app/public/css/admin/variables.css b/map/app/public/css/admin/variables.css new file mode 100644 index 0000000..138da2c --- /dev/null +++ b/map/app/public/css/admin/variables.css @@ -0,0 +1,66 @@ +/* CSS Variables - Admin Panel Theme Configuration */ +/* Centralized theme variables for consistency across admin modules */ + +:root { + /* Colors */ + --primary-color: #4CAF50; + --secondary-color: #6c757d; + --success-color: #28a745; + --error-color: #dc3545; + --warning-color: #ffc107; + --info-color: #17a2b8; + --dark-color: #333; + --light-color: #f5f5f5; + + /* Layout */ + --header-height: 60px; + --sidebar-width: 250px; + --sidebar-width-tablet: 220px; + --sidebar-width-mobile: 280px; + --app-height: 100vh; + + /* Spacing */ + --border-radius: 6px; + --transition: all 0.2s ease; + --padding-base: 20px; + --padding-sm: 15px; + --padding-xs: 10px; + + /* Typography */ + --font-size-base: 14px; + --font-size-sm: 13px; + --font-size-xs: 12px; + --font-size-lg: 16px; + --font-size-xl: 18px; + + /* Shadows */ + --shadow-sm: 0 2px 4px rgba(0,0,0,0.1); + --shadow-md: 0 4px 8px rgba(0,0,0,0.15); + --shadow-lg: 0 8px 24px rgba(0,0,0,0.15); + + /* Z-index layers */ + --z-dropdown: 1000; + --z-sticky: 1020; + --z-fixed: 1030; + --z-modal-backdrop: 1040; + --z-modal: 1050; + --z-popover: 1060; + --z-tooltip: 1070; + --z-toast: 12300; +} + +/* Theme color variations */ +.theme-green { + --primary-color: #4CAF50; + --primary-hover: #45a049; +} + +.theme-blue { + --primary-color: #007bff; + --primary-hover: #0056b3; +} + +.theme-purple { + --primary-color: #6f42c1; + --primary-hover: #5a2d91; +} diff --git a/map/app/public/css/admin/walk-sheet.css b/map/app/public/css/admin/walk-sheet.css new file mode 100644 index 0000000..5209f9e --- /dev/null +++ b/map/app/public/css/admin/walk-sheet.css @@ -0,0 +1,302 @@ +/* Walk Sheet Configuration and Preview Styles */ +/* QR codes, form layouts, and printable walk sheet formatting */ + +.walk-sheet-container { + display: grid; + grid-template-columns: 2fr 3fr; + gap: 30px; + margin-top: var(--padding-base); + align-items: flex-start; +} + +.walk-sheet-config { + background-color: #f9f9f9; + padding: var(--padding-base); + border-radius: var(--border-radius); +} + +.walk-sheet-config h3 { + font-size: var(--font-size-lg); + margin-bottom: 15px; + color: var(--dark-color); +} + +/* QR Code Groups */ +.qr-code-group { + background-color: white; + padding: 15px; + border-radius: var(--border-radius); + margin-bottom: 15px; + border: 1px solid #e0e0e0; + position: relative; +} + +.qr-code-group h4 { + font-size: var(--font-size-base); + margin-bottom: 10px; + color: #666; +} + +/* Walk Sheet Preview */ +.walk-sheet-preview { + background-color: #f5f5f5; + padding: 30px; + border-radius: var(--border-radius); + box-shadow: var(--shadow-lg); + min-width: 350px; + max-width: 100%; + margin: 0 auto; + min-height: 950px; + display: flex; + flex-direction: column; + align-items: center; + overflow: auto; +} + +.walk-sheet-preview h3 { + font-size: var(--font-size-lg); + margin-bottom: 10px; + color: var(--dark-color); +} + +.preview-controls { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 15px; +} + +.preview-info { + font-size: var(--font-size-xs); + color: #666; +} + +/* Walk Sheet Page - Actual Paper Size */ +.walk-sheet-page { + width: 816px; /* 8.5 inches * 96 DPI */ + height: 1056px; /* 11 inches * 96 DPI */ + background: white; + box-shadow: var(--shadow-lg); + padding: 48px; /* 0.5 inch margins */ + margin: 0 auto; + overflow: hidden; + font-size: 12pt; + line-height: 1.6; + position: relative; + transform: none; + box-sizing: border-box; +} + +/* Walk Sheet Content Styles */ +.ws-header { + text-align: center; + margin-bottom: 30px; + border-bottom: 3px solid #333; + padding-bottom: 15px; +} + +.ws-title { + font-size: 24pt; + font-weight: bold; + margin: 0; + color: #000; +} + +.ws-subtitle { + font-size: 12pt; + color: #444; + margin: 8px 0 0 0; +} + +.ws-qr-section { + display: flex; + justify-content: space-around; + margin: 30px 0; + padding: var(--padding-base); + background-color: #f5f5f5; + border-radius: 8px; + border: 1px solid #ddd; +} + +.ws-qr-item { + text-align: center; +} + +.ws-qr-code { + margin-bottom: 5px; +} + +.ws-qr-code img, +.ws-qr-code canvas { + display: block; + margin: 0 auto; + width: 120px; + height: 120px; + image-rendering: crisp-edges; + image-rendering: -webkit-crisp-edges; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; +} + +.ws-qr-label { + font-size: 11pt; + font-weight: bold; + margin-top: 10px; + color: #333; +} + +/* Walk Sheet Form Sections */ +.ws-form-section { + margin-top: 30px; +} + +.ws-form-row { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: var(--padding-base); + margin-bottom: 18px; +} + +.ws-form-group { + border-bottom: 2px solid #ccc; + padding-bottom: 8px; +} + +.ws-form-label { + font-size: 10pt; + color: #555; + display: block; + margin-bottom: 5px; + font-weight: 500; +} + +.ws-form-field { + height: 30px; + background-color: #fafafa; + border: 1px solid #eee; +} + +.ws-form-field.circles { + height: auto; + background: none; + border: none; + display: flex; + gap: 15px; + align-items: center; + padding: 5px 0; +} + +.ws-circle-option { + display: flex; + align-items: center; + gap: 5px; + font-size: 11pt; +} + +.ws-circle { + width: 24px; + height: 24px; + border: 2px solid #333; + border-radius: 50%; + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 10pt; + font-weight: 500; + background-color: white; +} + +/* Notes Section */ +.ws-notes-section { + margin-top: 30px; +} + +.ws-notes-label { + font-size: 11pt; + font-weight: bold; + margin-bottom: 10px; + color: #333; +} + +.ws-notes-area { + width: 100%; + height: 80px; + border: 2px solid #ccc; + background-color: #fafafa; + border-radius: 4px; +} + +/* Walk Sheet Footer */ +.ws-footer { + position: absolute; + bottom: 48px; + left: 48px; + right: 48px; + text-align: center; + font-size: 10pt; + color: #666; + padding-top: 15px; + border-top: 1px solid #ccc; + max-height: 80px; + overflow: hidden; + line-height: 1.4; +} + +/* Print Styles */ +@media print { + body * { + visibility: hidden; + } + + .walk-sheet-page, .walk-sheet-page * { + visibility: visible; + } + + .walk-sheet-page { + position: fixed; + left: 0; + top: 0; + width: 8.5in; + height: 11in; + max-width: none; + padding: 0.5in; + margin: 0; + box-shadow: none; + font-size: 12pt; + page-break-after: always; + } + + .ws-qr-code img { + width: 100px !important; + height: 100px !important; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + } +} + +/* Container Adjustments */ +.walk-sheet-container { + display: grid; + grid-template-columns: minmax(300px, 400px) 1fr; + gap: 30px; + margin-top: var(--padding-base); + align-items: flex-start; +} + +/* Large Screen Optimizations */ +@media (min-width: 1600px) { + .walk-sheet-container { + gap: 40px; + grid-template-columns: 1fr 2fr; + } + + .walk-sheet-preview { + max-width: 100%; + min-height: 1100px; + padding: 40px; + } + + .walk-sheet-preview .walk-sheet-page { + transform: none; + } +} diff --git a/map/app/public/js/cache-manager.js b/map/app/public/js/cache-manager.js index 6d07c8a..f3071ca 100644 --- a/map/app/public/js/cache-manager.js +++ b/map/app/public/js/cache-manager.js @@ -103,7 +103,7 @@ class ClientCacheManager { * Start periodic version checking */ startVersionChecking() { - // Check every 30 seconds + // Check every 60 seconds (reduced from 30 to ease rate limiting) this.versionCheckInterval = setInterval(async () => { try { if (await this.hasVersionChanged()) { @@ -111,8 +111,23 @@ class ClientCacheManager { } } catch (error) { console.warn('Version check failed:', error); + // If we get a rate limit error, slow down checks + if (error.message?.includes('429') || error.message?.includes('Too Many Requests')) { + console.log('Slowing down version checks due to rate limiting'); + clearInterval(this.versionCheckInterval); + // Restart with a longer interval (2 minutes) + this.versionCheckInterval = setInterval(async () => { + try { + if (await this.hasVersionChanged()) { + this.handleVersionChange(); + } + } catch (error) { + console.warn('Version check failed:', error); + } + }, 120000); // 2 minutes + } } - }, 30000); + }, 60000); // 1 minute } /** diff --git a/map/app/public/js/main.js b/map/app/public/js/main.js index 0591ae5..0cbd0d4 100644 --- a/map/app/public/js/main.js +++ b/map/app/public/js/main.js @@ -70,14 +70,42 @@ function setupAutoRefresh() { // Use longer interval for temp users to avoid rate limiting const refreshInterval_ms = currentUser?.userType === 'temp' ? 120000 : // 2 minutes for temp users - CONFIG.REFRESH_INTERVAL; // 30 seconds for regular users + 60000; // 1 minute for regular users (increased from 30 seconds) - refreshInterval = setInterval(() => { - loadLocations(); + let consecutiveErrors = 0; + const maxErrors = 3; + + refreshInterval = setInterval(async () => { + try { + await loadLocations(); + consecutiveErrors = 0; // Reset error count on success + } catch (error) { + consecutiveErrors++; + console.warn(`Location refresh failed (${consecutiveErrors}/${maxErrors}):`, error); + + // If we've had too many consecutive errors, slow down the refresh + if (consecutiveErrors >= maxErrors) { + console.log('Too many consecutive errors, slowing down refresh interval'); + clearInterval(refreshInterval); + + // Restart with a longer interval + const slowInterval = refreshInterval_ms * 2; // Double the interval + refreshInterval = setInterval(async () => { + try { + await loadLocations(); + consecutiveErrors = 0; + } catch (error) { + console.warn('Location refresh failed:', error); + } + }, slowInterval); + } + } }, refreshInterval_ms); if (currentUser?.userType === 'temp') { console.log('Auto-refresh set to 2 minutes for temporary account'); + } else { + console.log('Auto-refresh set to 1 minute for regular account'); } }).catch(error => { // Fallback to default interval if auth module fails to load diff --git a/map/app/server.js b/map/app/server.js index 3c15f6d..228a6bb 100644 --- a/map/app/server.js +++ b/map/app/server.js @@ -145,10 +145,7 @@ app.use(cacheBusting.staticCacheMiddleware()); // Serve static files with proper cache headers app.use(express.static('public')); -// Apply rate limiting to API routes -app.use('/api/', apiLimiter); - -// Cache busting version endpoint +// Cache busting version endpoint (before rate limiting) app.get('/api/version', (req, res) => { res.json({ version: cacheBusting.getVersion(), @@ -156,6 +153,9 @@ app.get('/api/version', (req, res) => { }); }); +// Apply rate limiting to API routes +app.use('/api/', apiLimiter); + // Proxy endpoint for MkDocs search app.get('/api/docs-search', async (req, res) => { try { diff --git a/map/files-explainer.md b/map/files-explainer.md index 10a3fa6..9961aee 100644 --- a/map/files-explainer.md +++ b/map/files-explainer.md @@ -186,7 +186,51 @@ Admin panel HTML page for managing start location, walk sheet, shift management, # app/public/css/admin.css -CSS styles specific to the admin panel UI. +Main entry point for admin panel CSS with modular imports. Refactored from a single large file into focused modules for better maintainability. Imports all admin-specific CSS modules including variables, layout, forms, user management, walk sheets, and responsive design. + +# app/public/css/admin/variables.css + +CSS custom properties and theme configuration for the admin panel. Defines colors, spacing, typography, shadows, and z-index layers used consistently across all admin modules. + +# app/public/css/admin/layout.css + +Layout components for the admin panel including container structure, sidebar navigation, content areas, map controls, and mobile menu components. Handles the overall admin interface layout and navigation. + +# app/public/css/admin/forms.css + +Form and button styles for the admin interface. Includes form field styling, button variations (primary, secondary, danger, etc.), form actions, and special form field types like circles and validation states. + +# app/public/css/admin/status-messages.css + +Status messages, notifications, and progress indicators for the admin panel. Handles toast notifications, progress bars, loading states, QR code generation status, and various status icons with animations. + +# app/public/css/admin/user-management.css + +User management interface styles including user tables, role badges, expiration indicators, volunteer management components, and user form styling. Handles the complete user administration interface. + +# app/public/css/admin/walk-sheet.css + +Walk sheet configuration and preview styles. Handles QR code display, printable walk sheet layout (8.5x11 paper), form field circles, notes sections, and print-specific styling for walk sheet generation. + +# app/public/css/admin/data-convert.css + +Data conversion interface styles for CSV upload and processing. Includes file upload areas, processing progress indicators, results preview tables, drag-and-drop styling, and map integration for geocoding results display. + +# app/public/css/admin/nocodb-links.css + +External database integration card styles for NocoDB connections. Provides styling for integration status cards, connection indicators, information boxes, and database management interface components. + +# app/public/css/admin/cuts-shifts.css + +Geographic cuts and shift management interface styles. Handles cut drawing controls, form states, shift scheduling interface, map interaction components, and administrative controls for cuts and volunteer shifts. + +# app/public/css/admin/modals.css + +Modal dialogs and email composition interface for the admin panel. Includes modal structure, email composition with rich text editing, progress tracking for email campaigns, and interactive dialog components. + +# app/public/css/admin/responsive.css + +Responsive design and media queries for all admin panel components. Handles mobile, tablet, and desktop layout adaptations, touch-friendly interfaces, mobile navigation, and responsive table layouts. # app/public/css/modules/apartment-marker.css