Files
TimeTracker/app/static/kiosk-mode.css
T
Dries Peeters bdf9249edc refactor: comprehensive application improvements and architecture enhancements
This commit implements all critical improvements from the application review,
establishing modern architecture patterns and significantly improving performance,
security, and maintainability.

## Architecture Improvements

- Implement service layer pattern: Migrated routes (projects, tasks, invoices, reports)
  to use dedicated service classes with business logic separation
- Add repository pattern: Enhanced repositories with comprehensive docstrings and
  type hints for better data access abstraction
- Create base CRUD service: BaseCRUDService reduces code duplication across services
- Implement API versioning structure: Created app/routes/api/ package with v1
  subpackage for future versioning support

## Performance Optimizations

- Fix N+1 query problems: Added eager loading (joinedload) to all migrated routes,
  reducing database queries by 80-90%
- Add query logging: Implemented query_logging.py for performance monitoring and
  slow query detection
- Create caching foundation: Added cache_redis.py utilities ready for Redis integration

## Security Enhancements

- Enhanced API token management: Created ApiTokenService with token rotation,
  expiration management, and scope validation
- Add environment validation: Implemented startup validation for critical
  environment variables with production checks
- Improve error handling: Standardized error responses with route_helpers.py utilities

## Code Quality

- Add comprehensive type hints: All service and repository methods now have
  complete type annotations
- Add docstrings: Comprehensive documentation added to all services, repositories,
  and public APIs
- Standardize error handling: Consistent error response patterns across all routes

## Testing

- Add unit tests: Created test suites for ProjectService, TaskService,
  InvoiceService, ReportingService, ApiTokenService, and BaseRepository
- Test coverage: Added tests for CRUD operations, eager loading, filtering,
  and error cases

## Documentation

- Add API versioning documentation: Created docs/API_VERSIONING.md with
  versioning strategy and migration guidelines
- Add implementation documentation: Comprehensive review and progress
  documentation files

## Files Changed

### New Files (20+)
- app/services/base_crud_service.py
- app/services/api_token_service.py
- app/utils/env_validation.py
- app/utils/query_logging.py
- app/utils/route_helpers.py
- app/utils/cache_redis.py
- app/routes/api/__init__.py
- app/routes/api/v1/__init__.py
- tests/test_services/*.py (5 files)
- tests/test_repositories/test_base_repository.py
- docs/API_VERSIONING.md
- Documentation files (APPLICATION_REVIEW_2025.md, etc.)

### Modified Files (15+)
- app/services/project_service.py
- app/services/task_service.py
- app/services/invoice_service.py
- app/services/reporting_service.py
- app/routes/projects.py
- app/routes/tasks.py
- app/routes/invoices.py
- app/routes/reports.py
- app/repositories/base_repository.py
- app/repositories/task_repository.py
- app/__init__.py

## Impact

- Performance: 80-90% reduction in database queries
- Code Quality: Modern architecture patterns, type hints, comprehensive docs
- Security: Enhanced API token management, environment validation
- Maintainability: Service layer separation, consistent error handling
- Testing: Foundation for comprehensive test coverage

All changes are backward compatible and production-ready.
2025-11-24 20:58:22 +01:00

667 lines
11 KiB
CSS

/**
* Kiosk Mode Styles
* Touch-optimized, high contrast, fullscreen-friendly
*/
/* Base Styles */
.kiosk-mode {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
margin: 0;
padding: 0;
background: #f5f5f5;
color: #333;
min-height: 100vh;
overflow-x: hidden;
}
.kiosk-container {
max-width: 100%;
margin: 0 auto;
padding: 0;
}
/* Login Page */
.kiosk-login {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.kiosk-login-card {
background: white;
border-radius: 16px;
padding: 3rem;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 600px;
width: 90%;
}
.kiosk-header {
text-align: center;
margin-bottom: 2rem;
}
.kiosk-title {
font-size: 2.5rem;
font-weight: bold;
color: #333;
margin: 0 0 0.5rem 0;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.kiosk-title i {
color: #667eea;
}
.kiosk-subtitle {
font-size: 1.1rem;
color: #666;
margin: 0;
}
.kiosk-user-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 1rem;
margin-bottom: 2rem;
}
.kiosk-user-button {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 12px;
padding: 1.5rem 1rem;
cursor: pointer;
transition: all 0.2s;
min-height: 120px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.kiosk-user-button:hover {
background: #e9ecef;
border-color: #667eea;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.2);
}
.kiosk-user-avatar {
font-size: 2.5rem;
color: #667eea;
}
.kiosk-user-name {
font-size: 0.9rem;
font-weight: 500;
color: #333;
}
.kiosk-username-input-wrapper {
margin-bottom: 1.5rem;
}
.kiosk-label {
display: block;
font-weight: 500;
margin-bottom: 0.5rem;
color: #333;
font-size: 0.95rem;
}
.kiosk-label-large {
display: block;
font-weight: 600;
margin-bottom: 1rem;
color: #333;
font-size: 1.2rem;
text-align: center;
}
.kiosk-input {
width: 100%;
padding: 1rem;
font-size: 1.1rem;
border: 2px solid #e9ecef;
border-radius: 8px;
transition: all 0.2s;
box-sizing: border-box;
}
.kiosk-input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.kiosk-input-large {
font-size: 1.3rem;
padding: 1.2rem;
}
.kiosk-input-barcode {
font-size: 1.5rem;
padding: 1.5rem;
text-align: center;
letter-spacing: 2px;
font-weight: 500;
}
.kiosk-button {
background: #667eea;
color: white;
border: none;
border-radius: 8px;
padding: 1rem 2rem;
font-size: 1.1rem;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
display: inline-flex;
align-items: center;
gap: 0.5rem;
text-decoration: none;
min-height: 44px; /* Touch target minimum */
min-width: 44px;
}
.kiosk-button:hover {
background: #5568d3;
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
}
.kiosk-button:active {
transform: translateY(0);
}
.kiosk-button-primary {
background: #667eea;
}
.kiosk-button-danger {
background: #dc3545;
}
.kiosk-button-danger:hover {
background: #c82333;
}
.kiosk-button-large {
padding: 1.5rem 3rem;
font-size: 1.3rem;
width: 100%;
justify-content: center;
}
.kiosk-button-small {
padding: 0.75rem 1rem;
font-size: 0.9rem;
}
.kiosk-button-quantity {
padding: 0.75rem;
min-width: 50px;
font-size: 1.2rem;
}
.kiosk-link {
color: #667eea;
text-decoration: none;
font-size: 0.9rem;
}
.kiosk-link:hover {
text-decoration: underline;
}
.kiosk-footer {
text-align: center;
margin-top: 2rem;
padding-top: 2rem;
border-top: 1px solid #e9ecef;
}
.kiosk-messages {
margin-bottom: 1.5rem;
}
.kiosk-alert {
padding: 1rem;
border-radius: 8px;
margin-bottom: 1rem;
}
.kiosk-alert-error {
background: #fee;
color: #c33;
border: 1px solid #fcc;
}
.kiosk-alert-success {
background: #efe;
color: #3c3;
border: 1px solid #cfc;
}
/* Dashboard */
.kiosk-header-bar {
background: white;
padding: 1rem 2rem;
display: flex;
align-items: center;
justify-content: space-between;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
position: sticky;
top: 0;
z-index: 100;
}
.kiosk-header-left,
.kiosk-header-right {
display: flex;
align-items: center;
gap: 1rem;
}
.kiosk-header-center {
flex: 1;
text-align: center;
}
.kiosk-user-info {
display: flex;
align-items: center;
gap: 0.5rem;
font-weight: 500;
color: #333;
}
.kiosk-timer-display {
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
font-size: 1.2rem;
font-weight: 600;
color: #667eea;
}
.kiosk-timer-project {
font-size: 0.9rem;
color: #666;
font-weight: normal;
}
.kiosk-main {
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
}
.kiosk-section {
background: white;
border-radius: 12px;
padding: 2rem;
margin-bottom: 2rem;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
}
.kiosk-section-title {
font-size: 1.3rem;
font-weight: 600;
margin-bottom: 1rem;
color: #333;
}
/* Barcode Section */
.kiosk-barcode-section {
text-align: center;
}
.kiosk-barcode-input-wrapper {
max-width: 600px;
margin: 0 auto;
}
.kiosk-barcode-status {
margin-top: 1rem;
padding: 0.75rem;
border-radius: 8px;
font-size: 0.95rem;
min-height: 20px;
}
.kiosk-status-loading {
background: #e3f2fd;
color: #1976d2;
}
.kiosk-status-success {
background: #e8f5e9;
color: #2e7d32;
}
.kiosk-status-error {
background: #ffebee;
color: #c62828;
}
/* Item Display */
.kiosk-item-card {
background: #f8f9fa;
border-radius: 12px;
padding: 2rem;
}
.kiosk-item-header {
margin-bottom: 1.5rem;
padding-bottom: 1rem;
border-bottom: 2px solid #e9ecef;
}
.kiosk-item-name {
font-size: 1.8rem;
font-weight: 700;
color: #333;
margin: 0 0 0.5rem 0;
}
.kiosk-item-sku {
font-size: 1.1rem;
color: #666;
font-family: monospace;
}
.kiosk-item-details {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 1rem;
margin-bottom: 1.5rem;
}
.kiosk-item-detail {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.kiosk-detail-label {
font-size: 0.85rem;
color: #666;
font-weight: 500;
}
.kiosk-detail-value {
font-size: 1rem;
color: #333;
font-weight: 600;
}
/* Stock Levels */
.kiosk-stock-levels {
margin-top: 1.5rem;
}
.kiosk-stock-levels h4 {
font-size: 1.1rem;
margin-bottom: 1rem;
color: #333;
}
.kiosk-stock-level {
background: white;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 1rem;
margin-bottom: 1rem;
}
.kiosk-stock-warehouse {
font-weight: 600;
font-size: 1.1rem;
color: #333;
margin-bottom: 0.75rem;
}
.kiosk-stock-quantity {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
}
.kiosk-stock-label {
color: #666;
}
.kiosk-stock-value {
font-weight: 600;
color: #333;
}
.kiosk-stock-low {
color: #dc3545;
}
.kiosk-stock-location {
font-size: 0.9rem;
color: #666;
margin-top: 0.5rem;
font-style: italic;
}
.kiosk-stock-empty {
text-align: center;
color: #999;
padding: 2rem;
}
/* Operations */
.kiosk-operations-tabs {
display: flex;
gap: 0.5rem;
margin-bottom: 2rem;
border-bottom: 2px solid #e9ecef;
}
.kiosk-tab {
background: none;
border: none;
padding: 1rem 2rem;
font-size: 1.1rem;
font-weight: 500;
color: #666;
cursor: pointer;
border-bottom: 3px solid transparent;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
min-height: 44px;
}
.kiosk-tab:hover {
color: #667eea;
background: #f8f9fa;
}
.kiosk-tab-active {
color: #667eea;
border-bottom-color: #667eea;
}
.kiosk-tab-content {
display: none;
}
.kiosk-tab-content-active {
display: block;
}
.kiosk-form {
max-width: 500px;
}
.kiosk-form-group {
margin-bottom: 1.5rem;
}
.kiosk-select {
width: 100%;
padding: 1rem;
font-size: 1.1rem;
border: 2px solid #e9ecef;
border-radius: 8px;
background: white;
cursor: pointer;
min-height: 44px;
}
.kiosk-select:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
.kiosk-quantity-controls {
display: flex;
align-items: center;
gap: 1rem;
}
.kiosk-input-quantity {
flex: 1;
text-align: center;
font-size: 1.5rem;
font-weight: 600;
}
.kiosk-timer-info {
background: #f8f9fa;
border-radius: 8px;
padding: 1.5rem;
margin-bottom: 1.5rem;
text-align: center;
}
.kiosk-timer-info p {
margin: 0.5rem 0;
}
/* Recent Items */
.kiosk-recent-items {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
gap: 1rem;
}
.kiosk-recent-item {
background: #f8f9fa;
border: 2px solid #e9ecef;
border-radius: 8px;
padding: 1rem;
cursor: pointer;
transition: all 0.2s;
text-align: left;
min-height: 80px;
}
.kiosk-recent-item:hover {
background: #e9ecef;
border-color: #667eea;
transform: translateY(-2px);
}
.kiosk-recent-item-name {
font-weight: 600;
color: #333;
margin-bottom: 0.25rem;
}
.kiosk-recent-item-sku {
font-size: 0.85rem;
color: #666;
font-family: monospace;
}
/* Notifications */
.kiosk-notification {
position: fixed;
top: 20px;
right: 20px;
padding: 1rem 1.5rem;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 1000;
display: none;
max-width: 400px;
font-weight: 500;
animation: slideIn 0.3s ease-out;
}
.kiosk-notification-success {
background: #4caf50;
color: white;
}
.kiosk-notification-error {
background: #f44336;
color: white;
}
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
/* Responsive */
@media (max-width: 768px) {
.kiosk-header-bar {
flex-direction: column;
gap: 1rem;
padding: 1rem;
}
.kiosk-header-center {
order: -1;
}
.kiosk-main {
padding: 1rem;
}
.kiosk-section {
padding: 1.5rem;
}
.kiosk-user-grid {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}
.kiosk-operations-tabs {
flex-direction: column;
}
.kiosk-tab {
width: 100%;
justify-content: center;
}
}
/* Note: Dark mode is now handled by Tailwind's dark: prefix classes */
/* This file is kept for backward compatibility but most styles should use Tailwind */