# Feature Spec: Organization Timeline **Priority:** P1 (Core Demo Feature) **Estimated Time:** 2-3 hours **User Story:** "As a boat owner, I want to see a chronological timeline of all activities (uploads, maintenance, warranty events) for my organization, with most recent at top." --- ## Requirements ### Functional Requirements 1. **Timeline View** - Dedicated page showing chronological activity feed 2. **Organization Scoped** - Only show events for current user's organization 3. **Reverse Chronological** - Most recent first (top), oldest last (bottom) 4. **Event Types:** - Document uploads (PDFs, images, manuals) - Maintenance records - Warranty claims/alerts - Service provider contacts - Settings changes (future) - User invitations (future) ### UI Requirements - **Timeline URL:** `/timeline` or `/organization/:orgId/timeline` - **Visual Design:** Vertical timeline with date markers - **Grouping:** Group events by date (Today, Yesterday, Last Week, etc.) - **Infinite Scroll:** Load more as user scrolls down - **Filters:** Filter by event type, date range, entity (specific boat) --- ## Database Schema ### Option 1: Unified Activity Log Table (Recommended) **New Migration:** `010_activity_timeline.sql` ```sql CREATE TABLE activity_log ( id TEXT PRIMARY KEY, organization_id TEXT NOT NULL, entity_id TEXT, -- Optional: boat/yacht ID if event is entity-specific user_id TEXT NOT NULL, event_type TEXT NOT NULL, -- 'document_upload', 'maintenance_log', 'warranty_claim', 'settings_change' event_action TEXT, -- 'created', 'updated', 'deleted', 'viewed' event_title TEXT NOT NULL, event_description TEXT, metadata TEXT, -- JSON blob for event-specific data reference_id TEXT, -- ID of related resource (document_id, maintenance_id, etc.) reference_type TEXT, -- 'document', 'maintenance', 'warranty', etc. created_at INTEGER NOT NULL, FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE, FOREIGN KEY (entity_id) REFERENCES boats(id) ON DELETE CASCADE, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ); CREATE INDEX idx_activity_org_created ON activity_log(organization_id, created_at DESC); CREATE INDEX idx_activity_entity ON activity_log(entity_id, created_at DESC); CREATE INDEX idx_activity_type ON activity_log(event_type); ``` **Example Records:** ```json { "id": "evt_abc123", "organization_id": "6ce0dfc7-f754-4122-afde-85154bc4d0ae", "entity_id": "boat_azimut55s", "user_id": "bef71b0c-3427-485b-b4dd-b6399f4d4c45", "event_type": "document_upload", "event_action": "created", "event_title": "Owner Manual - Azimut 55S", "event_description": "PDF uploaded: Liliane1_Prestige_Manual_EN.pdf (6.7MB, 100 pages)", "metadata": "{\"fileSize\": 6976158, \"pageCount\": 100, \"mimeType\": \"application/pdf\"}", "reference_id": "efb25a15-7d84-4bc3-b070-6bd7dec8d59a", "reference_type": "document", "created_at": 1760903255 } { "id": "evt_def456", "organization_id": "6ce0dfc7-f754-4122-afde-85154bc4d0ae", "entity_id": "boat_azimut55s", "user_id": "bef71b0c-3427-485b-b4dd-b6399f4d4c45", "event_type": "maintenance_log", "event_action": "created", "event_title": "Bilge Pump Service", "event_description": "Replaced bilge pump filter, tested operation", "metadata": "{\"cost\": 245.50, \"provider\": \"Marine Services Ltd\", \"nextServiceDue\": 1776903255}", "reference_id": "maint_xyz789", "reference_type": "maintenance", "created_at": 1761007118 } ``` ### Option 2: Event Sourcing (More Complex, Skip for MVP) - Separate tables per event type - Aggregate into timeline via JOIN - Better for audit trails, overkill for MVP --- ## API Endpoints ### GET /api/organizations/:orgId/timeline **Query Parameters:** - `limit` (default: 50, max: 200) - `offset` (default: 0) - For pagination - `eventType` (optional) - Filter: document_upload, maintenance_log, warranty_claim - `entityId` (optional) - Filter by specific boat - `startDate` (optional) - Unix timestamp - `endDate` (optional) - Unix timestamp **Response:** ```json { "events": [ { "id": "evt_abc123", "eventType": "document_upload", "eventAction": "created", "title": "Owner Manual - Azimut 55S", "description": "PDF uploaded: Liliane1_Prestige_Manual_EN.pdf", "createdAt": 1761007118620, "user": { "id": "bef71b0c-3427-485b-b4dd-b6399f4d4c45", "name": "Test User 2", "email": "test2@navidocs.test" }, "entity": { "id": "boat_azimut55s", "name": "Liliane I", "type": "boat" }, "metadata": { "fileSize": 6976158, "pageCount": 100 }, "referenceId": "efb25a15-7d84-4bc3-b070-6bd7dec8d59a", "referenceType": "document" } ], "pagination": { "total": 156, "limit": 50, "offset": 0, "hasMore": true }, "groupedByDate": { "today": 5, "yesterday": 12, "thisWeek": 23, "thisMonth": 45, "older": 71 } } ``` ### POST /api/activity (Internal - Auto-called by services) **Body:** ```json { "organizationId": "6ce0dfc7-f754-4122-afde-85154bc4d0ae", "entityId": "boat_azimut55s", "userId": "bef71b0c-3427-485b-b4dd-b6399f4d4c45", "eventType": "document_upload", "eventAction": "created", "eventTitle": "Owner Manual Uploaded", "eventDescription": "Liliane1_Prestige_Manual_EN.pdf", "metadata": {}, "referenceId": "efb25a15-7d84-4bc3-b070-6bd7dec8d59a", "referenceType": "document" } ``` --- ## Frontend Implementation ### Route: `/timeline` **File:** `client/src/views/Timeline.vue` ```vue ``` --- ## Backend Service Layer **File:** `server/services/activity-logger.js` (NEW) ```javascript /** * Activity Logger Service * Automatically logs events to organization timeline */ import { getDb } from '../config/db.js'; import { v4 as uuidv4 } from 'uuid'; export async function logActivity({ organizationId, entityId = null, userId, eventType, eventAction, eventTitle, eventDescription = '', metadata = {}, referenceId = null, referenceType = null }) { const db = getDb(); const activity = { id: `evt_${uuidv4()}`, organization_id: organizationId, entity_id: entityId, user_id: userId, event_type: eventType, event_action: eventAction, event_title: eventTitle, event_description: eventDescription, metadata: JSON.stringify(metadata), reference_id: referenceId, reference_type: referenceType, created_at: Date.now() }; db.prepare(` INSERT INTO activity_log ( id, organization_id, entity_id, user_id, event_type, event_action, event_title, event_description, metadata, reference_id, reference_type, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `).run( activity.id, activity.organization_id, activity.entity_id, activity.user_id, activity.event_type, activity.event_action, activity.event_title, activity.event_description, activity.metadata, activity.reference_id, activity.reference_type, activity.created_at ); return activity; } ``` **Usage Example (in upload route):** ```javascript // server/routes/upload.js (after successful upload) import { logActivity } from '../services/activity-logger.js'; // ... after document saved to DB ... await logActivity({ organizationId: organizationId, entityId: entityId, userId: req.user.id, eventType: 'document_upload', eventAction: 'created', eventTitle: title, eventDescription: `Uploaded ${sanitizedFilename} (${(file.size / 1024).toFixed(1)}KB)`, metadata: { fileSize: file.size, fileName: sanitizedFilename, documentType: documentType }, referenceId: documentId, referenceType: 'document' }); ``` --- ## Implementation Checklist ### Phase 1: Database & Backend (1 hour) - [ ] Create migration: `010_activity_timeline.sql` - [ ] Run migration: `node scripts/run-migration.js 010` - [ ] Create `services/activity-logger.js` - [ ] Create route: `routes/timeline.js` with GET endpoint - [ ] Add activity logging to upload route - [ ] Test API: `curl /api/organizations/:id/timeline` ### Phase 2: Frontend (1.5 hours) - [ ] Create `views/Timeline.vue` - [ ] Add route to `router.js`: `/timeline` - [ ] Add navigation link in header - [ ] Implement infinite scroll - [ ] Add event type filters - [ ] Style timeline with date grouping ### Phase 3: Backfill (30 min) - [ ] Write script to backfill existing documents into activity_log - [ ] Run: `node scripts/backfill-timeline.js` - [ ] Verify timeline shows historical data --- ## Demo Talking Points **"Here's the organization timeline - it shows everything that's happened with your boat:"** - "Today: You uploaded the bilge pump manual (6.7MB, fully searchable)" - "Last week: Maintenance service logged for engine oil change" - "This month: 12 documents uploaded, 3 maintenance records" - "Scroll down to see older activity - everything is tracked automatically" **Value Proposition:** - Never lose track of what's been done - See maintenance history at a glance - Useful for warranty claims ("when did we service that?") - Audit trail for multi-user organizations --- **Ready to implement?** This can be built in parallel with OCR optimization. Timeline doesn't block demo, but adds significant perceived value.