Files
Job-Info/instructions/auth/VIEWS.md
T
2026-01-09 04:08:06 +00:00

725 lines
17 KiB
Markdown

# View Architecture & Reference
**Version:** 1.0
**Status:** PRODUCTION
**Last Updated:** January 2026
---
## Overview
This document defines the professional view architecture for Job Info. All UI should follow these patterns for consistency, maintainability, and professional appearance.
---
## View Hierarchy
```
JobInfo Application
├── AuthView
│ └── Sign In Form
├── LandingView
│ ├── Header
│ ├── JobCardView
│ │ ├── Search & Filters
│ │ └── Job Cards (Grid)
│ └── Footer
├── ManagerInfoView
│ ├── Job Header
│ ├── Job Info Panel
│ ├── FileListView
│ │ ├── File Search
│ │ ├── File Categories
│ │ │ ├── Manager Info
│ │ │ ├── Contracts / Estimates
│ │ │ ├── Submittals
│ │ │ ├── Plans
│ │ │ └── Other
│ │ └── File Actions
│ ├── NotesSection
│ └── Footer
├── NotesView
│ ├── Notes Header
│ ├── Notes Content (Read-only)
│ └── Back to Job Detail
└── FilePreviewModal
├── PDF Viewer
├── Image Viewer
└── Word Document Handler
```
---
## 1. AuthView (Sign In Page)
**Purpose:** Authenticate user via PocketBase
**Components:**
- Logo/Header
- Email input
- Password input
- Sign In button
- Error messages
- Remember Me checkbox (optional)
**State:**
- `isLoading`: boolean
- `errorMessage`: string | null
- `email`: string
- `password`: string
**Key Functions:**
```javascript
async function handleSignIn(email, password) {
// 1. Show loading state
// 2. Call pb.collection('users').authWithPassword()
// 3. Store token in localStorage
// 4. Redirect to index.html (LandingView)
// 5. Handle errors gracefully
}
```
**HTML Structure:**
```html
<div class="auth-container">
<div class="auth-card">
<div class="auth-header">
<img src="assets/logo.png" alt="Logo" />
<h1>Job Info</h1>
</div>
<form id="signinForm" class="auth-form">
<div class="form-group">
<label for="email">Email</label>
<input
id="email"
type="email"
placeholder="you@example.com"
class="form-input"
required
/>
</div>
<div class="form-group">
<label for="password">Password</label>
<input
id="password"
type="password"
placeholder="••••••••"
class="form-input"
required
/>
</div>
<div id="errorMsg" class="error-message hidden"></div>
<button type="submit" class="btn btn-primary btn-lg w-full">
Sign In
</button>
</form>
<div class="auth-footer">
<p class="text-sm text-gray-600">
Secure login via PocketBase
</p>
</div>
</div>
</div>
```
**CSS Classes:**
```css
.auth-container { /* Full screen container */ }
.auth-card { /* Centered white card */ }
.auth-header { /* Logo + title */ }
.auth-form { /* Form styling */ }
.form-group { /* Input group */ }
.form-input { /* Input field */ }
.error-message { /* Red error text */ }
.btn { /* Button base */ }
.btn-primary { /* Blue button */ }
.btn-lg { /* Large button */ }
```
---
## 2. LandingView (Home / Jobs List)
**Purpose:** Display list of jobs with search and filters
**Components:**
- Header (with user info & sign out)
- Search input
- Filter controls (status, date range)
- Job cards in grid
- Pagination
- Loading skeleton
- Empty state
**State:**
```javascript
const landingState = {
jobs: [], // Job list
page: 1,
perPage: 10,
total: 0,
sort: '-Job_Number', // Default sort
searchTerm: '',
filters: {
status: '',
dateRange: { start: null, end: null }
},
isLoading: false,
error: null,
cache: {} // For pagination memory
};
```
**Key Functions:**
```javascript
async function loadJobs(page = 1, searchTerm = '', filters = {}) {
// 1. Show loading skeleton
// 2. Build cache key
// 3. Check Redis cache
// 4. If miss, fetch from /api/jobs
// 5. Display jobs
// 6. Handle pagination
}
function handleSearch(term) {
// 1. Debounce (300ms)
// 2. Update state.searchTerm
// 3. Reset to page 1
// 4. Call loadJobs()
}
function handleFilterChange(filter) {
// 1. Update state.filters
// 2. Reset to page 1
// 3. Call loadJobs()
}
async function handleJobClick(job) {
// 1. Navigate to ManagerInfoView
// 2. Store current job in state
// 3. Fetch job files
}
```
**HTML Structure:**
```html
<div class="landing-container">
<!-- Header -->
<header class="landing-header">
<div class="header-left">
<img src="assets/logo.png" alt="Logo" class="logo" />
<h1>Job Info</h1>
</div>
<div class="header-right">
<span class="user-name" id="userNameDisplay">John Doe</span>
<button class="btn btn-secondary" onclick="handleSignOut()">
Sign Out
</button>
</div>
</header>
<!-- Search & Filters -->
<div class="search-filters-bar">
<div class="search-group">
<input
type="search"
id="jobSearch"
placeholder="Search jobs by name or number..."
class="search-input"
/>
<button class="btn-search">🔍</button>
</div>
<div class="filters-group">
<select id="statusFilter" class="filter-select">
<option value="">All Statuses</option>
<option value="Active">Active</option>
<option value="Completed">Completed</option>
<option value="Pending">Pending</option>
</select>
<input
type="date"
id="startDate"
class="filter-input"
/>
<input
type="date"
id="endDate"
class="filter-input"
/>
</div>
</div>
<!-- Job Cards Grid -->
<div class="jobs-grid" id="jobsGrid">
<!-- JobCard components inserted here -->
</div>
<!-- Pagination -->
<div class="pagination">
<button class="btn-pagination" onclick="previousPage()">← Previous</button>
<span id="pageInfo">Page 1 of 10</span>
<button class="btn-pagination" onclick="nextPage()">Next →</button>
</div>
<!-- Loading Skeleton -->
<div id="loadingSkeleton" class="skeleton-grid hidden">
<!-- 10 skeleton cards -->
</div>
<!-- Empty State -->
<div id="emptyState" class="empty-state hidden">
<div class="empty-state-icon">📭</div>
<h3>No jobs found</h3>
<p>Try adjusting your filters or search term</p>
</div>
</div>
```
**JobCard Component:**
```html
<div class="job-card" onclick="handleJobClick(jobData)">
<div class="card-header">
<h3 class="card-title">Job# 4757</h3>
<span class="card-badge badge-active">Active</span>
</div>
<div class="card-body">
<p class="card-subtitle">Construction Project Name</p>
<div class="card-details">
<div class="detail-row">
<span class="detail-label">Manager:</span>
<span class="detail-value">John Smith</span>
</div>
<div class="detail-row">
<span class="detail-label">Start Date:</span>
<span class="detail-value">Jan 15, 2025</span>
</div>
<div class="detail-row">
<span class="detail-label">Files:</span>
<span class="detail-value">42</span>
</div>
</div>
</div>
<div class="card-footer">
<button class="btn-card-action">View Details →</button>
</div>
</div>
```
**CSS Tailwind Classes:**
```css
.landing-container /* Full page layout */
.landing-header /* Sticky header with logo */
.header-left /* Logo + title */
.header-right /* User info + sign out */
.search-filters-bar /* Search + filter controls */
.search-group /* Search input + button */
.filters-group /* Filter dropdowns */
.jobs-grid /* Grid of cards (responsive) */
.job-card /* Individual card */
.card-header /* Card title + badge */
.card-body /* Card content */
.card-details /* Key-value details */
.pagination /* Page navigation */
.skeleton-grid /* Loading placeholders */
.empty-state /* No results message */
```
---
## 3. ManagerInfoView (Job Detail)
**Purpose:** Display job details, files, and notes
**Components:**
- Job header with info
- File list (categorized)
- Notes section
- Back button
**State:**
```javascript
const managerInfoState = {
job: null, // Current job object
files: [], // Files for this job
fileFilter: '', // Search within files
notes: [], // Sticky notes
isLoadingFiles: false,
error: null
};
```
**HTML Structure:**
```html
<div class="manager-info-container">
<!-- Back Button -->
<button class="btn btn-secondary" onclick="goBackToLanding()">
← Back to Jobs
</button>
<!-- Job Header -->
<div class="job-header">
<div class="header-title">
<h1>Job# <span id="jobNumber">4757</span></h1>
<p class="job-name" id="jobName">Construction Project Name</p>
</div>
<div class="header-badges">
<span class="badge badge-active">Active</span>
<span class="badge">42 Files</span>
</div>
</div>
<!-- Job Info Panel -->
<div class="job-info-panel">
<div class="info-group">
<label>Manager</label>
<p class="info-value" id="jobManager">John Smith</p>
</div>
<div class="info-group">
<label>Contact</label>
<p class="info-value" id="jobContact">john@example.com</p>
</div>
<div class="info-group">
<label>Start Date</label>
<p class="info-value" id="jobStartDate">Jan 15, 2025</p>
</div>
<div class="info-group">
<label>Status</label>
<p class="info-value" id="jobStatus">Active</p>
</div>
</div>
<!-- File List Section -->
<div class="file-list-section">
<h2>Files</h2>
<div class="file-search-row">
<input
id="fileSearchInput"
type="search"
placeholder="Search files..."
class="search-input"
/>
<a href="#" target="_blank" rel="noopener" class="btn btn-secondary">
Open Folder
</a>
</div>
<!-- File Groups (categorized) -->
<div id="fileGroups" class="file-groups">
<!-- FileGroup components inserted here -->
</div>
<!-- Loading State -->
<div id="fileLoading" class="loading-spinner hidden">
<div class="spinner"></div>
<p>Loading files...</p>
</div>
<!-- Empty State -->
<div id="filesEmpty" class="empty-state hidden">
<p>No files found</p>
</div>
</div>
<!-- Notes Section -->
<div class="notes-section">
<h2>Notes</h2>
<div class="notes-grid" id="notesGrid">
<!-- StickyNote components inserted here -->
</div>
<button class="btn btn-primary" onclick="expandNotes()">
Expand Notes
</button>
</div>
</div>
```
**FileGroup Component:**
```html
<div class="file-group">
<div class="group-header">
<h3>Manager Info <span class="file-count">(5)</span></h3>
</div>
<div class="file-list">
<div class="file-item">
<div class="file-icon">📄</div>
<div class="file-info">
<p class="file-name">manager_info.pdf</p>
<p class="file-meta">1.2 MB · Jan 20, 2025</p>
</div>
<div class="file-actions">
<button class="btn btn-sm btn-primary" onclick="previewFile(...)">
Preview
</button>
<a href="#" target="_blank" class="btn btn-sm btn-secondary">
Download
</a>
</div>
</div>
<!-- More files... -->
</div>
</div>
```
**StickyNote Component:**
```html
<div class="sticky-note note-yellow">
<div class="note-content">
<p>Project kickoff meeting scheduled for Monday</p>
</div>
<div class="note-date">Jan 20, 2025</div>
</div>
```
---
## 4. NotesView (Expanded Notes)
**Purpose:** Full-screen view of job notes
**Components:**
- Notes list (full content)
- Back button
**HTML Structure:**
```html
<div class="notes-view-container">
<button class="btn btn-secondary" onclick="goBackToJobDetail()">
← Back to Job
</button>
<h1>Job# <span id="noteJobNumber">4757</span> - Notes</h1>
<div class="notes-expanded-grid" id="notesExpandedGrid">
<!-- Full StickyNote components with larger content -->
</div>
<div id="notesEmpty" class="empty-state hidden">
<p>No notes for this job</p>
</div>
</div>
```
---
## 5. FilePreviewModal
**Purpose:** Preview files (PDF, images, documents)
**Components:**
- Modal overlay
- Viewer (PDF, image, or iframe for documents)
- File info
- Close button
**HTML Structure:**
```html
<div id="iframeViewerModal" class="modal hidden">
<div class="modal-content">
<!-- Header -->
<div class="modal-header">
<h3 id="iframeTitle">File Preview</h3>
<button class="btn-close" onclick="closeModal()"></button>
</div>
<!-- PDF Viewer -->
<div id="pdfViewer" class="viewer hidden">
<!-- Canvas for PDF rendering -->
</div>
<!-- Image Viewer -->
<div id="imageViewer" class="viewer hidden">
<img id="imageContent" src="" alt="" />
</div>
<!-- iFrame for Documents -->
<div id="iframeViewer" class="viewer hidden">
<iframe id="fileFrame" src=""></iframe>
</div>
<!-- Loading -->
<div id="iframeLoader" class="loader hidden">
<div class="spinner"></div>
<p>Loading file...</p>
</div>
<!-- Footer -->
<div class="modal-footer">
<a id="downloadLink" href="" target="_blank" class="btn btn-primary">
Download
</a>
</div>
</div>
</div>
```
---
## 6. Component Standards
### Button Styles
```html
<!-- Primary (Blue) -->
<button class="btn btn-primary">Action</button>
<!-- Secondary (Gray) -->
<button class="btn btn-secondary">Cancel</button>
<!-- Danger (Red) -->
<button class="btn btn-danger">Delete</button>
<!-- Disabled -->
<button class="btn btn-primary" disabled>Disabled</button>
<!-- Sizes -->
<button class="btn btn-sm">Small</button>
<button class="btn">Normal</button>
<button class="btn btn-lg">Large</button>
```
### Card Styles
```html
<div class="card">
<div class="card-header">
<h3>Title</h3>
</div>
<div class="card-body">
<p>Content</p>
</div>
<div class="card-footer">
<button>Action</button>
</div>
</div>
```
### Badge Styles
```html
<span class="badge badge-active">Active</span>
<span class="badge badge-pending">Pending</span>
<span class="badge badge-completed">Completed</span>
```
### Input Styles
```html
<input type="text" class="input-field" placeholder="..." />
<input type="email" class="input-field" placeholder="..." />
<input type="date" class="input-field" />
<select class="select-field">
<option>Option 1</option>
</select>
<textarea class="textarea-field"></textarea>
```
---
## 7. Responsive Design
### Breakpoints (Tailwind)
```
Mobile: < 640px (sm: 640px)
Tablet: 768px (md: 768px)
Desktop: 1024px (lg: 1024px)
Wide: 1280px (xl: 1280px)
```
### Grid Layouts
```html
<!-- Job Cards: 1 col mobile, 2 col tablet, 3 col desktop -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<!-- Cards -->
</div>
<!-- File List: Full width on mobile, 2 col on tablet+ -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- Files -->
</div>
```
---
## 8. Accessibility Standards
### WCAG 2.1 Level AA
- ✅ Color contrast ≥ 4.5:1 for text
- ✅ Semantic HTML (use `<button>`, not `<div onclick>`)
- ✅ ARIA labels for icons
- ✅ Keyboard navigation (Tab, Enter)
- ✅ Screen reader friendly
**Example:**
```html
<!-- ✅ CORRECT -->
<button class="btn" aria-label="Close modal"></button>
<label for="email">Email</label>
<input id="email" type="email" />
<!-- ❌ WRONG -->
<div onclick="close()" class="cursor-pointer"></div>
<input type="email" placeholder="Email" />
```
---
## 9. Internationalization (i18n)
### Labels & Messages
All user-facing text should be:
- English
- Professional tone
- Clear and concise
- Consistent across views
**Example:**
```javascript
const LABELS = {
SIGN_IN: 'Sign In',
SEARCH_JOBS: 'Search jobs by name or number...',
NO_FILES_FOUND: 'No files found',
LOADING: 'Loading...',
ERROR: 'An error occurred. Please try again.'
};
```
---
## Summary
| View | Purpose | Key Components |
|------|---------|-----------------|
| AuthView | User login | Form, inputs, validation |
| LandingView | Job list | Cards, search, filters, pagination |
| ManagerInfoView | Job detail | Header, files, notes, actions |
| NotesView | Expanded notes | Full-screen notes display |
| FilePreviewModal | File preview | Viewer (PDF/image/document) |
**All UI must follow these patterns for consistency and professionalism.**