Files
tenderpilot/public/IMPLEMENTATION_GUIDE.md
Peter Foster f969ecae04 feat: visual polish, nav login link, pricing badge fix, cursor fix, button contrast
- Hero mockup: enhanced 3D perspective and shadow
- Testimonials: illustrated SVG avatars
- Growth pricing card: visual prominence (scale, gradient, badge)
- Most Popular badge: repositioned to avoid overlapping heading
- Nav: added Log In link next to Start Free Trial
- Fixed btn-primary text colour on anchor tags (white on blue)
- Fixed cursor: default on all non-interactive elements
- Disabled user-select on non-form content to prevent text caret
2026-02-14 14:17:15 +00:00

622 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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