refactored the admin.css
This commit is contained in:
parent
a96318f19e
commit
26717f89f7
@ -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,
|
||||
|
||||
63
map/app/public/css/REFACTORING_SUMMARY.md
Normal file
63
map/app/public/css/REFACTORING_SUMMARY.md
Normal file
@ -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
|
||||
File diff suppressed because it is too large
Load Diff
3010
map/app/public/css/admin.css.backup
Normal file
3010
map/app/public/css/admin.css.backup
Normal file
File diff suppressed because it is too large
Load Diff
119
map/app/public/css/admin/README.md
Normal file
119
map/app/public/css/admin/README.md
Normal file
@ -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
|
||||
197
map/app/public/css/admin/cuts-shifts.css
Normal file
197
map/app/public/css/admin/cuts-shifts.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
215
map/app/public/css/admin/data-convert.css
Normal file
215
map/app/public/css/admin/data-convert.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
251
map/app/public/css/admin/forms.css
Normal file
251
map/app/public/css/admin/forms.css
Normal file
@ -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;
|
||||
}
|
||||
244
map/app/public/css/admin/layout.css
Normal file
244
map/app/public/css/admin/layout.css
Normal file
@ -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;
|
||||
}
|
||||
310
map/app/public/css/admin/modals.css
Normal file
310
map/app/public/css/admin/modals.css
Normal file
@ -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;
|
||||
}
|
||||
135
map/app/public/css/admin/nocodb-links.css
Normal file
135
map/app/public/css/admin/nocodb-links.css
Normal file
@ -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;
|
||||
}
|
||||
717
map/app/public/css/admin/responsive.css
Normal file
717
map/app/public/css/admin/responsive.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
189
map/app/public/css/admin/status-messages.css
Normal file
189
map/app/public/css/admin/status-messages.css
Normal file
@ -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); }
|
||||
}
|
||||
219
map/app/public/css/admin/user-management.css
Normal file
219
map/app/public/css/admin/user-management.css
Normal file
@ -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);
|
||||
}
|
||||
66
map/app/public/css/admin/variables.css
Normal file
66
map/app/public/css/admin/variables.css
Normal file
@ -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;
|
||||
}
|
||||
302
map/app/public/css/admin/walk-sheet.css
Normal file
302
map/app/public/css/admin/walk-sheet.css
Normal file
@ -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;
|
||||
}
|
||||
}
|
||||
@ -103,7 +103,19 @@ 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()) {
|
||||
this.handleVersionChange();
|
||||
}
|
||||
} 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()) {
|
||||
@ -112,7 +124,10 @@ class ClientCacheManager {
|
||||
} catch (error) {
|
||||
console.warn('Version check failed:', error);
|
||||
}
|
||||
}, 30000);
|
||||
}, 120000); // 2 minutes
|
||||
}
|
||||
}
|
||||
}, 60000); // 1 minute
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user