Sessions 6-10 Feature Specs: - Session 6: Inventory & Warranty Tracking (equipment management) - Session 7: Maintenance Scheduler (recurring tasks with alerts) - Session 8: Crew & Contact Management (marine operations directory) - Session 9: Compliance & Certification Tracker (regulatory compliance) - Session 10: Fuel Log & Expense Tracker (financial management) Deployment Documentation: - STACKCP_DEPLOYMENT_GUIDE.md (complete deployment process) - DEPLOYMENT_SUMMARY.md (executive overview) - DEPLOYMENT_ARCHITECTURE.md (technical deep dive) - DEPLOYMENT_INDEX.md (navigation hub) - README_DEPLOYMENT.txt (quick start) - STACKCP_QUICK_COMMANDS.sh (copy-paste commands) Session Prompts: - 4 new prompts with step-by-step build instructions Total: ~450-600 min build time across 5 features Demo value: Complete boat management platform
16 KiB
Cloud Session Prompt: Compliance & Certification Tracker
Feature: Regulatory Compliance and Certification Management with Expiration Alerts
Duration: 75-90 minutes
Priority: P1 (Core Feature)
Branch: feature/compliance-certification
Your Mission
Build a compliance tracking system for managing regulatory requirements, safety inspections, licenses, registrations, and certifications. Marine vessels require numerous time-sensitive certifications to remain legal and insured.
What you're building:
- Compliance item tracking with expiration dates
- Automated renewal alerts
- Compliance dashboard with status overview
- Document attachment for certificates
- Renewal history tracking
- Visual status indicators (valid, expiring soon, expired)
- Critical alerts for mandatory expired items
Quick Start
cd /home/setup/navidocs
git checkout navidocs-cloud-coordination
git pull origin navidocs-cloud-coordination
git checkout -b feature/compliance-certification
Step 1: Read the Spec (5 min)
Read this file: /home/setup/navidocs/FEATURE_SPEC_COMPLIANCE_CERTIFICATION.md
This spec contains:
- Complete database schema (3 tables)
- All 8 API endpoints with request/response examples
- Frontend component designs
- Status calculation logic
- Compliance categories
- Demo data (12-15 compliance items)
Step 2: Database Migration (12 min)
Create: server/migrations/014_compliance_certification.sql
Tables to create:
compliance_items- Certifications, licenses, inspectionscompliance_renewals- Renewal history per itemcompliance_documents- Linked certificates and receipts
Copy schema from: FEATURE_SPEC_COMPLIANCE_CERTIFICATION.md (lines 31-80)
Run migration:
cd server
node run-migration.js 014_compliance_certification.sql
Verify:
sqlite3 db/navidocs.db "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'compliance%';"
Step 3: Backend Service (25 min)
Create: server/services/compliance-service.js
Key functions:
// Compliance item CRUD
async function createComplianceItem(orgId, itemData)
async function getComplianceList(orgId, filters)
async function getComplianceById(itemId)
async function updateComplianceItem(itemId, updates)
async function deleteComplianceItem(itemId)
// Renewal workflow
async function renewComplianceItem(itemId, renewalData) {
// 1. Create renewal record
// 2. Update compliance_item.expiration_date = new_expiration_date
// 3. Update compliance_item.status = 'valid'
// 4. Update compliance_item.certificate_number (if changed)
// 5. Link document if provided
// 6. Log to activity timeline
}
// Status calculation
function calculateComplianceStatus(item) {
const now = Date.now();
const expirationDate = item.expiration_date;
if (!expirationDate) return 'pending_renewal';
const daysUntilExpiry = Math.floor((expirationDate - now) / (1000 * 60 * 60 * 24));
if (daysUntilExpiry < 0) return 'expired';
if (daysUntilExpiry <= item.alert_days_before) return 'expiring_soon';
return 'valid';
}
// Alert level calculation
function getAlertLevel(item) {
const daysUntilExpiry = Math.floor((item.expiration_date - Date.now()) / (1000 * 60 * 60 * 24));
if (daysUntilExpiry < 0 && item.is_mandatory) return 'critical';
if (daysUntilExpiry < 0) return 'expired';
if (daysUntilExpiry <= 7) return 'urgent';
if (daysUntilExpiry <= 30) return 'warning';
return 'info';
}
// Alerts
async function getComplianceAlerts(orgId) {
// Get items where status = 'expired' OR 'expiring_soon'
// Prioritize: mandatory expired > expired > expiring soon
}
// Dashboard
async function getComplianceDashboard(orgId) {
// Return stats: total, valid, expiring_soon, expired, mandatory_expired
// Urgent items list
// Upcoming renewals (next 90 days)
// Total renewal cost (next 90 days)
}
// Documents
async function attachDocument(itemId, documentId, documentType)
Step 4: Backend Routes (18 min)
Create: server/routes/compliance.js
const express = require('express');
const router = express.Router({ mergeParams: true });
const complianceService = require('../services/compliance-service');
const authMiddleware = require('../middleware/auth');
router.use(authMiddleware);
// GET /api/organizations/:orgId/compliance
router.get('/', async (req, res) => {
const { orgId } = req.params;
const { type, status, mandatory_only } = req.query;
// Call complianceService.getComplianceList()
});
// POST /api/organizations/:orgId/compliance
router.post('/', async (req, res) => {
// Create compliance item
});
// GET /api/organizations/:orgId/compliance/:itemId
router.get('/:itemId', async (req, res) => {
// Get item details + renewal history + documents
});
// PUT /api/organizations/:orgId/compliance/:itemId
router.put('/:itemId', async (req, res) => {
// Update item
});
// DELETE /api/organizations/:orgId/compliance/:itemId
router.delete('/:itemId', async (req, res) => {
// Delete item
});
// POST /api/organizations/:orgId/compliance/:itemId/renew
router.post('/:itemId/renew', async (req, res) => {
// Renew compliance item
// Update expiration date, create renewal record
});
// GET /api/organizations/:orgId/compliance/alerts
router.get('/alerts', async (req, res) => {
// Get expired/expiring items
});
// GET /api/organizations/:orgId/compliance/dashboard
router.get('/dashboard', async (req, res) => {
// Get dashboard statistics
});
module.exports = router;
Register route in server/index.js:
app.use('/api/organizations/:orgId/compliance', require('./routes/compliance'));
Step 5: Frontend - Compliance Dashboard (30 min)
Create: client/src/views/Compliance.vue
Features:
- Status overview cards (Valid, Expiring Soon, Expired)
- Alert banner for critical items
- Filterable table
- "Add Compliance Item" button
- Quick actions (Renew, View, Edit)
Template structure:
<template>
<div class="compliance-view">
<!-- Alert Banner -->
<div v-if="criticalAlerts.length > 0" class="alert-banner critical">
<strong>🚨 URGENT: {{ mandatoryExpiredCount }} mandatory certification expired</strong>
<strong v-if="expiringCount > 0"> | ⚠️ {{ expiringCount }} items expiring within 30 days</strong>
<div v-for="alert in criticalAlerts.slice(0, 3)" :key="alert.item_id">
└─ {{ alert.item_name }}: {{ alert.days_until_expiry < 0 ? `EXPIRED ${Math.abs(alert.days_until_expiry)} days ago` : `Expires in ${alert.days_until_expiry} days` }}
</div>
<button @click="viewAlerts">View All Compliance</button>
</div>
<div class="header">
<h1>Compliance & Certifications</h1>
<button @click="showAddModal = true">+ Add Compliance Item</button>
</div>
<!-- Status Overview Cards -->
<div class="status-cards">
<div class="card valid">
<div class="count">{{ stats.valid }}</div>
<div class="label">✅ Valid</div>
<div class="sublabel">All up to date</div>
</div>
<div class="card expiring">
<div class="count">{{ stats.expiring_soon }}</div>
<div class="label">⚠️ Expiring Soon</div>
<div class="sublabel">Within 30 days</div>
</div>
<div class="card expired">
<div class="count">{{ stats.expired }}</div>
<div class="label">❌ Expired</div>
<div class="sublabel">Renew now</div>
</div>
</div>
<!-- Filters -->
<div class="filters">
<select v-model="filters.type">
<option value="">All Types</option>
<option value="vessel_registration">Vessel Registration</option>
<option value="safety_inspection">Safety Inspection</option>
<option value="crew_certification">Crew Certification</option>
<option value="insurance">Insurance</option>
<!-- ... more types -->
</select>
<select v-model="filters.status">
<option value="">All Status</option>
<option value="valid">Valid</option>
<option value="expiring_soon">Expiring Soon</option>
<option value="expired">Expired</option>
</select>
<label>
<input type="checkbox" v-model="filters.mandatory_only">
Mandatory Only
</label>
</div>
<!-- Compliance Table -->
<table class="compliance-table">
<thead>
<tr>
<th>Item Name</th>
<th>Category</th>
<th>Issuing Authority</th>
<th>Certificate #</th>
<th>Expiration Date</th>
<th>Days Until Expiry</th>
<th>Status</th>
<th>Cost</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="item in filteredItems" :key="item.id" :class="statusRowClass(item.status, item.is_mandatory)">
<td>
{{ item.item_name }}
<span v-if="item.is_mandatory" class="mandatory-badge">MANDATORY</span>
</td>
<td>{{ item.item_type }}</td>
<td>{{ item.issuing_authority }}</td>
<td>{{ item.certificate_number || '-' }}</td>
<td>{{ formatDate(item.expiration_date) }}</td>
<td :class="daysClass(item.days_until_expiry)">
{{ item.days_until_expiry > 0 ? item.days_until_expiry : `${Math.abs(item.days_until_expiry)} ago` }}
</td>
<td>
<span :class="statusBadge(item.status)">
{{ item.status.toUpperCase() }}
</span>
</td>
<td>{{ item.cost ? `$${item.cost.toFixed(2)}` : '-' }}</td>
<td>
<button @click="renewItem(item)" class="btn-renew">Renew</button>
<button @click="viewItem(item)">View</button>
<button @click="editItem(item)">Edit</button>
</td>
</tr>
</tbody>
</table>
<AddComplianceItemModal v-if="showAddModal" @close="showAddModal = false" @saved="loadCompliance" />
<RenewComplianceModal v-if="renewingItem" :item="renewingItem" @close="renewingItem = null" @renewed="loadCompliance" />
</div>
</template>
<script>
export default {
data() {
return {
items: [],
stats: {},
alerts: [],
filters: {
type: '',
status: '',
mandatory_only: false
},
showAddModal: false,
renewingItem: null
};
},
computed: {
filteredItems() {
return this.items.filter(item => {
if (this.filters.type && item.item_type !== this.filters.type) return false;
if (this.filters.status && item.status !== this.filters.status) return false;
if (this.filters.mandatory_only && !item.is_mandatory) return false;
return true;
});
},
criticalAlerts() {
return this.alerts.filter(a => a.alert_level === 'critical' || a.alert_level === 'urgent');
},
mandatoryExpiredCount() {
return this.alerts.filter(a => a.alert_level === 'critical').length;
},
expiringCount() {
return this.alerts.filter(a => a.days_until_expiry >= 0 && a.days_until_expiry <= 30).length;
}
},
async mounted() {
await this.loadCompliance();
await this.loadAlerts();
},
methods: {
async loadCompliance() {
const response = await fetch(`/api/organizations/${this.orgId}/compliance`, {
headers: { 'Authorization': `Bearer ${this.token}` }
});
const data = await response.json();
this.items = data.items;
this.stats = data.stats;
},
async loadAlerts() {
const response = await fetch(`/api/organizations/${this.orgId}/compliance/alerts`, {
headers: { 'Authorization': `Bearer ${this.token}` }
});
const data = await response.json();
this.alerts = data.alerts;
},
statusRowClass(status, isMandatory) {
if (status === 'expired' && isMandatory) return 'row-critical';
if (status === 'expired') return 'row-expired';
if (status === 'expiring_soon') return 'row-expiring';
return '';
},
daysClass(days) {
if (days < 0) return 'days-expired';
if (days <= 7) return 'days-urgent';
if (days <= 30) return 'days-warning';
return '';
},
statusBadge(status) {
return `badge badge-${status}`;
},
// ... other methods
}
};
</script>
<style scoped>
.status-cards {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin: 2rem 0;
}
.card {
padding: 2rem;
border-radius: 8px;
text-align: center;
}
.card.valid { background: #e8f5e9; }
.card.expiring { background: #fff3cd; }
.card.expired { background: #ffe0e0; }
.row-critical { background-color: #ffebee; font-weight: bold; }
.row-expired { background-color: #ffe0e0; }
.row-expiring { background-color: #fff3cd; }
.days-expired { color: red; font-weight: bold; }
.days-urgent { color: orange; font-weight: bold; }
.days-warning { color: #ff9800; }
.mandatory-badge {
background: red;
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.75rem;
margin-left: 8px;
}
</style>
Step 6: Add Compliance Item Modal (15 min)
Create: client/src/components/AddComplianceItemModal.vue
Form fields:
- Item Type* (dropdown)
- Item Name*
- Description
- Issuing Authority
- Certificate Number
- Issue Date
- Expiration Date*
- Renewal Frequency (days)
- Is Mandatory (checkbox - default: true)
- Alert Days Before (default: 30)
- Cost
- Renewal Process (textarea)
- Contact for Renewal
- Notes
Step 7: Renew Compliance Modal (12 min)
Create: client/src/components/RenewComplianceModal.vue
Form fields:
- Renewal Date* (default: today)
- New Expiration Date*
- New Certificate Number
- Cost
- Upload Certificate Document
- Notes
Auto-fill logic:
mounted() {
// If item has renewal_frequency_days, auto-calculate new expiration
if (this.item.renewal_frequency_days) {
const newExpiry = new Date();
newExpiry.setDate(newExpiry.getDate() + this.item.renewal_frequency_days);
this.formData.new_expiration_date = newExpiry.getTime();
}
// Pre-fill cost from previous renewal
this.formData.cost = this.item.cost;
}
Step 8: Alert Banner Component (10 min)
Create: client/src/components/ComplianceAlertBanner.vue
Add to HomeView.vue dashboard
Step 9: Navigation & Router (8 min)
Update: client/src/router.js
{
path: '/compliance',
component: () => import('./views/Compliance.vue'),
meta: { requiresAuth: true }
}
Update navigation: Add "Compliance" link
Step 10: Demo Data (10 min)
Create: server/seed-compliance-demo-data.js
Sample items:
- 2 expired (1 mandatory: Vessel Registration)
- 3 expiring within 30 days (Hull Insurance: 5 days)
- 7 valid items
- Mix of categories
Run:
node server/seed-compliance-demo-data.js
Step 11: Testing (12 min)
Test checklist:
- Can add compliance items
- Can renew items
- Status calculated correctly
- Alert banner shows critical items
- Dashboard cards show correct counts
- Can filter by type and status
- Mandatory badge displays
- Renewal history tracked
Step 12: Completion (5 min)
git add .
git commit -m "[SESSION-9] Add compliance & certification tracker
Features:
- Compliance item tracking with expiration dates
- Automated renewal alerts
- Compliance dashboard with status overview
- Document attachment for certificates
- Renewal history tracking
- Visual status indicators (valid, expiring soon, expired)
- Critical alerts for mandatory expired items
Database: 3 new tables
API: 8 new endpoints
Frontend: Compliance view + 2 modals + alert banner"
git push origin feature/compliance-certification
Create: SESSION-9-COMPLETE.md
Success Criteria
✅ Database migration creates 3 tables ✅ All 8 API endpoints working ✅ Can add compliance items ✅ Can renew items (updates expiration, creates renewal record) ✅ Status calculated correctly ✅ Alert banner shows urgent items ✅ Dashboard shows overview statistics ✅ Can filter by type and status ✅ Documents can be attached ✅ Demo data loads successfully
Go build! 🚀