Files
tenderpilot/public/IMPLEMENTATION_GUIDE.md

622 lines
16 KiB
Markdown
Raw Normal View History

# TenderRadar Navigation System & Shared Layout
Complete implementation guide for consistent navigation, authentication, and styling across all TenderRadar pages.
## 📁 File Structure
```
/var/www/tenderradar/
├── auth.js # Shared auth utilities
├── app.css # Shared app styles
├── index.html # Landing page (unchanged)
├── login.html # Login page
├── signup.html # Sign up page
├── dashboard.html # Dashboard page
├── profile.html # User profile page
├── alerts.html # Alerts page
├── tenders.html # Tenders page (optional)
├── styles.css # Landing page styles (unchanged)
├── script.js # Landing page script (unchanged)
└── components/
├── nav.js # Navigation component
└── footer.js # Footer component
```
## 🚀 Quick Start
### 1. Add Auth Module to All App Pages
Add this to the `<head>` of every app page (dashboard, profile, alerts, etc.):
```html
<!-- Authentication utilities (must be loaded first) -->
<script src="/auth.js"></script>
```
### 2. Add Navigation & Footer Components
Add these before the closing `</body>` tag on every app page:
```html
<!-- Navigation component -->
<script src="/components/nav.js"></script>
<!-- Footer component -->
<script src="/components/footer.js"></script>
```
### 3. Include App Styles
Add this to the `<head>` of every app page:
```html
<!-- App-specific styles (complements/overrides landing styles) -->
<link rel="stylesheet" href="/app.css">
```
### 4. Protect Pages with Auth Check
Add this immediately after loading auth.js in your page JavaScript:
```javascript
// Require authentication on this page
requireAuth();
```
---
## 📋 Complete Example: dashboard.html
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard | TenderRadar</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="icon" href="/favicon.ico" type="image/x-icon">
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
<!-- Landing page styles -->
<link rel="stylesheet" href="/styles.css">
<!-- App-specific styles -->
<link rel="stylesheet" href="/app.css">
<!-- Authentication utilities (must load first) -->
<script src="/auth.js"></script>
</head>
<body>
<!-- Navigation auto-injects here -->
<!-- Footer auto-injects here -->
<!-- Main content -->
<main class="app-container">
<div class="page-header">
<div>
<h1 class="page-title">Dashboard</h1>
<p class="page-subtitle">Welcome back! Here's your tender overview.</p>
</div>
<div class="page-actions">
<button class="btn btn-primary">New Alert</button>
</div>
</div>
<!-- Your dashboard content here -->
<div class="grid grid-2">
<!-- Stat cards, charts, tables, etc. -->
</div>
</main>
<!-- Component scripts (auto-initialize) -->
<script src="/components/nav.js"></script>
<script src="/components/footer.js"></script>
<!-- Page-specific script -->
<script>
// Require authentication on this page
requireAuth();
// Your dashboard logic here
document.addEventListener('DOMContentLoaded', () => {
// Initialize dashboard
loadDashboardData();
});
async function loadDashboardData() {
const response = await fetchWithAuth('/api/dashboard');
const data = await response.json();
// Update UI with data
}
</script>
</body>
</html>
```
---
## 🔐 Authentication API Reference
### `getToken()`
Retrieves the stored JWT token.
```javascript
const token = getToken();
if (token) {
console.log('User is authenticated');
}
```
### `setToken(token)`
Stores a JWT token in localStorage.
```javascript
// Typically done after login
setToken(response.token);
```
### `clearToken()`
Removes the JWT token from localStorage.
```javascript
clearToken();
```
### `isAuthenticated()`
Checks if user is currently authenticated.
```javascript
if (isAuthenticated()) {
// Show app content
} else {
// Redirect to login
}
```
### `getUserInfo()`
Decodes and returns the JWT payload (user info).
```javascript
const user = getUserInfo();
console.log(user.email); // User's email
console.log(user.id); // User ID
console.log(user.iat); // Issued at
console.log(user.exp); // Expiration time
```
### `requireAuth()`
Redirects to login page if not authenticated. Use this in page initialization.
```javascript
// At top of page script
requireAuth();
```
### `logout()`
Clears token and redirects to login page.
```javascript
// Called when user clicks logout button
logout();
```
### `fetchWithAuth(url, options)`
Wrapper around fetch() that automatically adds Authorization header.
```javascript
// GET request with auth
const response = await fetchWithAuth('/api/tenders');
const data = await response.json();
// POST request with auth
const response = await fetchWithAuth('/api/profile', {
method: 'POST',
body: JSON.stringify({ name: 'John' })
});
```
---
## 🎨 Navigation Component Features
The `NavBar` component automatically:
✅ Injects a sticky navbar at the top of the page
✅ Shows different content based on auth state
✅ Displays user email + avatar for authenticated users
✅ Highlights the current active page
✅ Handles logout with token clearing
✅ Mobile-responsive hamburger menu
✅ Responsive user dropdown menu
### Navigation Links (Authenticated)
- **Dashboard** → `/dashboard.html`
- **Tenders** → `/tenders.html`
- **Alerts** → `/alerts.html`
- **Profile** → `/profile.html`
### Navigation Links (Unauthenticated)
- **Login** → `/login.html`
- **Sign Up** → `/signup.html`
---
## 🎨 Styling System
### Color Variables
```css
--primary: #1e40af; /* Deep Blue */
--primary-dark: #1e3a8a; /* Darker Blue */
--primary-light: #3b82f6; /* Light Blue */
--accent: #f59e0b; /* Orange */
--success: #10b981; /* Green */
--danger: #ef4444; /* Red */
--warning: #f59e0b; /* Orange */
--info: #3b82f6; /* Blue */
```
### Component Classes
#### Cards
```html
<div class="card">
<div class="card-header">
<h2 class="card-title">Tender Details</h2>
</div>
<div class="card-content">
<!-- Content here -->
</div>
<div class="card-footer">
<button class="btn btn-primary">Save</button>
</div>
</div>
<!-- Variants: card-primary, card-success, card-warning, card-danger -->
<div class="card card-primary">...</div>
```
#### Buttons
```html
<!-- Variants -->
<button class="btn btn-primary">Primary</button>
<button class="btn btn-secondary">Secondary</button>
<button class="btn btn-outline">Outline</button>
<button class="btn btn-danger">Danger</button>
<button class="btn btn-success">Success</button>
<!-- Sizes -->
<button class="btn btn-sm">Small</button>
<button class="btn btn-primary">Normal</button>
<button class="btn btn-lg">Large</button>
<!-- Full width -->
<button class="btn btn-primary btn-block">Full Width</button>
<!-- With icon -->
<button class="btn btn-primary btn-icon">
<svg>...</svg>
Action
</button>
```
#### Badges & Tags
```html
<!-- Badges (status indicators) -->
<span class="badge badge-primary">Active</span>
<span class="badge badge-success">Approved</span>
<span class="badge badge-warning">Pending</span>
<span class="badge badge-danger">Rejected</span>
<!-- Tags (with optional close button) -->
<div class="tag">Python <span class="tag-close">×</span></div>
```
#### Alerts & Notifications
```html
<!-- Success alert -->
<div class="alert alert-success">
<div class="alert-icon">✓</div>
<div class="alert-content">
<div class="alert-title">Success!</div>
<div class="alert-message">Your profile has been updated.</div>
</div>
<button class="alert-close">×</button>
</div>
<!-- Error alert -->
<div class="alert alert-error">
<div class="alert-icon">!</div>
<div class="alert-content">
<div class="alert-title">Error</div>
<div class="alert-message">Something went wrong. Please try again.</div>
</div>
</div>
```
#### Tables
```html
<div class="table-wrapper">
<table>
<thead>
<tr>
<th>Tender ID</th>
<th>Title</th>
<th>Status</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td>TR-001</td>
<td>Ministry Website Redesign</td>
<td><span class="badge badge-success">Open</span></td>
<td>
<div class="table-actions">
<button class="table-action-btn" title="View">👁️</button>
<button class="table-action-btn" title="Edit">✏️</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
```
#### Forms
```html
<form>
<!-- Single field -->
<div class="form-group">
<label for="email" class="label-required">Email</label>
<input type="email" id="email" name="email" placeholder="user@example.com" required>
<div class="form-hint">We'll never share your email.</div>
</div>
<!-- Text area -->
<div class="form-group">
<label for="bio">Bio</label>
<textarea id="bio" name="bio" placeholder="Tell us about yourself..."></textarea>
</div>
<!-- Select dropdown -->
<div class="form-group">
<label for="sector">Sector</label>
<select id="sector" name="sector">
<option value="">Select a sector...</option>
<option value="it">IT & Software</option>
<option value="construction">Construction</option>
<option value="consulting">Consulting</option>
</select>
</div>
<!-- Two-column layout -->
<div class="form-row form-row-2">
<div class="form-group">
<label for="first_name">First Name</label>
<input type="text" id="first_name" name="first_name">
</div>
<div class="form-group">
<label for="last_name">Last Name</label>
<input type="text" id="last_name" name="last_name">
</div>
</div>
<!-- Error state -->
<div class="form-group">
<label for="password">Password</label>
<input type="password" id="password" name="password" class="error">
<div class="form-error">Password must be at least 8 characters.</div>
</div>
<!-- Form actions -->
<button type="submit" class="btn btn-primary btn-block">Save Profile</button>
</form>
```
#### Grids & Layouts
```html
<!-- Responsive 2-column grid -->
<div class="grid grid-2">
<div class="card">Column 1</div>
<div class="card">Column 2</div>
<div class="card">Column 3 (wraps to new row)</div>
</div>
<!-- Fixed 3-column grid -->
<div class="grid grid-cols-3">
<div class="stat-card">Stat 1</div>
<div class="stat-card">Stat 2</div>
<div class="stat-card">Stat 3</div>
</div>
<!-- Dashboard layout with sidebar -->
<div class="app-layout">
<aside class="app-sidebar">
<a href="/dashboard.html" class="sidebar-item active">Dashboard</a>
<a href="/tenders.html" class="sidebar-item">All Tenders</a>
<a href="/alerts.html" class="sidebar-item">My Alerts</a>
</aside>
<div class="app-content">
<!-- Main content -->
</div>
</div>
```
#### Loading States
```html
<!-- Spinner -->
<div class="spinner"></div>
<div class="spinner spinner-sm"></div>
<div class="spinner spinner-lg"></div>
<!-- Loading message -->
<div class="loading">
<div class="spinner"></div>
Loading tenders...
</div>
<!-- Skeleton loading -->
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text skeleton-text-sm"></div>
<div class="skeleton skeleton-text skeleton-text-lg"></div>
```
#### Empty States
```html
<div class="empty-state">
<div class="empty-state-icon">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/>
<circle cx="12" cy="7" r="4"/>
</svg>
</div>
<h3 class="empty-state-title">No tenders yet</h3>
<p class="empty-state-desc">Create your first alert to start receiving tender matches.</p>
<div class="empty-state-actions">
<button class="btn btn-primary">Create Alert</button>
<button class="btn btn-secondary">Learn More</button>
</div>
</div>
```
---
## 📱 Responsive Design
All components are fully responsive with mobile-first design:
- **Desktop**: Full navigation with all menu items visible
- **Tablet** (768px): Optimized spacing and layouts
- **Mobile** (480px): Hamburger menu, single-column layouts, optimized touch targets
### Mobile Navigation
On mobile, the navbar automatically switches to a hamburger menu that can be toggled to show/hide navigation items.
### Form Inputs on Mobile
All inputs use `font-size: 1rem` on mobile to prevent iOS auto-zoom.
---
## 🔗 Integration with Existing Pages
### Landing Page (index.html) - **No Changes Needed**
The landing page uses `styles.css` and works independently. No auth required.
### Login Page (login.html)
```javascript
// On successful login, store token:
setToken(response.token);
// Then redirect:
window.location.href = '/dashboard.html';
```
### Sign Up Page (signup.html)
```javascript
// After successful registration:
setToken(response.token);
// Then redirect:
window.location.href = '/dashboard.html';
```
### Protected Pages (dashboard.html, profile.html, alerts.html)
All must:
1. Load `auth.js` first
2. Load `app.css` for styling
3. Load navigation and footer components
4. Call `requireAuth()` to protect the page
5. Use `fetchWithAuth()` for API calls
---
## 🛠️ Utility Classes
### Spacing
```html
<!-- Margin top -->
<div class="mt-1 mt-2 mt-3 mt-4 mt-6 mt-8">...</div>
<!-- Margin bottom -->
<div class="mb-1 mb-2 mb-3 mb-4 mb-6 mb-8">...</div>
<!-- Padding -->
<div class="p-1 p-2 p-3 p-4 p-6">...</div>
```
### Text
```html
<div class="text-center">Centered text</div>
<div class="text-right">Right-aligned text</div>
<div class="text-primary">Blue text</div>
<div class="text-success">Green text</div>
<div class="truncate">Text that truncates...</div>
<div class="line-clamp-2">Text limited to 2 lines...</div>
```
### Display
```html
<div class="hidden">Hidden element</div>
<div class="visible">Visible element</div>
<div class="opacity-50">50% opacity</div>
<div class="opacity-75">75% opacity</div>
```
---
## 📋 Checklist for Page Implementation
- [ ] Add `<script src="/auth.js"></script>` to `<head>`
- [ ] Add `<link rel="stylesheet" href="/app.css">` to `<head>`
- [ ] Add navigation component script before `</body>`
- [ ] Add footer component script before `</body>`
- [ ] Call `requireAuth()` in page script (for protected pages)
- [ ] Wrap content in `<main class="app-container">`
- [ ] Use `fetchWithAuth()` for all API calls
- [ ] Test mobile responsiveness
- [ ] Test logout functionality
- [ ] Verify navigation highlights correct active page
---
## 🐛 Troubleshooting
### Navigation not showing?
- Check that `auth.js` is loaded before `nav.js`
- Verify `nav.js` exists at `/components/nav.js`
- Check browser console for errors
### Styles not applying?
- Ensure `app.css` is loaded after landing `styles.css`
- Clear browser cache
- Check file permissions on server
### Auth checks not working?
- Verify `auth.js` is loaded first
- Check localStorage for `tenderradar_token`
- Look for JS errors in browser console
### API calls failing?
- Verify JWT token is valid and not expired
- Use `fetchWithAuth()` instead of plain `fetch()`
- Check server CORS settings if cross-domain
---
## 📚 Additional Resources
- **Brand Colors**: Primary #1e40af, Accent #f59e0b
- **Font Family**: Inter (from Google Fonts)
- **Layout Width**: Max 1400px container
- **Shadow System**: sm, md, lg, xl variants
- **Responsive Breakpoints**: 768px (tablet), 480px (mobile)
---
**Created**: 2026-02-14
**Last Updated**: 2026-02-14