Documentation Index
Fetch the complete documentation index at: https://grantmaster.dev/llms.txt
Use this file to discover all available pages before exploring further.
Engineering reference: For service contracts, EventBus events, and data-layer details see src/features/journals/journals.md.
Journals
Overview
The journals feature provides time-tracking and time-off management for team members in GrantMaster. Employees submit daily journal entries documenting hours worked on projects, with optional entries for leave (vacation, sick, parental, study). Managers review and approve submissions, and the system supports auto-approval workflows based on organization rules. Additionally, the journal submission workflow allows comprehensive monthly reflection with AI-assisted entry generation and manager validation.
Key characteristics:
- Dual entry types: daily work logs and journal submissions
- Approval workflows for compliance and audit trails
- Auto-approval rules (optional per organization)
- Locking mechanism to prevent editing after approval
- EventBus integration for notifications (internal dispatcher) and audit logging
Firestore collection: journals (legacy name; contains JournalEntry documents)
Monthly submissions collection: journalSubmissions (contains JournalSubmission documents)
Page Organization
The journals feature spans two separate pages, each with their own PageDef in src/features/journals/pages.config.ts:
Journaling Page (/journaling/:tab)
| Tab | Value | Permission | Description |
|---|
| Daily Log | daily-log | SUBMIT_JOURNAL_SUBMISSION | Calendar grid for creating and reviewing daily entries |
| My Submissions | my-submissions | SUBMIT_JOURNAL_SUBMISSION | User’s journal submission history and drafts |
| Team Feed | team-feed | — | Cross-team activity feed |
| Insights | insights | — | Analytics and time-tracking insights |
AI-first features include cross-document RAG for context-aware journal generation, calendar/activity sync, and compliance-first entry generation.
Timesheets Page (/timesheet/:tab)
| Tab | Value | Description |
|---|
| Weekly Matrix | weekly-matrix | 7-day × projects grid editor for structured time entry |
| Submissions | submissions | Submission tracking and history |
| Approvals | approvals | Manager approval queue for timesheet entries |
| Reports | reports | Timesheet reports and exports |
The Timesheets page provides a structured, spreadsheet-like interface for time entry, separate from the narrative-focused Journaling page.
Data Model
Firestore Collections
| Collection | Document Type | Description |
|---|
journals | JournalEntry | Daily journal entries (work logs, leave). Organized by date and user. Includes status (pending → submitted → approved/rejected) and locking for audit. |
journalSubmissions | JournalSubmission | Journal submissions. Parent doc; contains subcollection history for edit audit trail. |
journalSubmissions/{id}/history | JournalSubmissionHistory | Immutable audit log of changes to a monthly submission (draft, submitted, approved, rejected, edited). |
Key TypeScript Types
JournalEntry (src/schemas/journals.schema.ts)
- Core fields:
id, userId, userName, date, projectId, projectName, hours, activityType, description, organizationId
- Status:
'pending', 'submitted', 'approved', 'rejected'
- Controls:
isLocked (prevents editing after approval), isEdited (tracks manager edits)
- Audit:
auditFlag (severity), auditMessage, createdAt, updatedAt, submittedAt
- Leave:
entryType (‘work’, ‘vacation’, ‘sick_leave’, ‘parental_leave’, ‘study_leave’), leaveDetails
- Approval:
approvedBy, approvedAt, approvalComment, rejectedBy, rejectedAt, rejectionReason
- NGO:
volunteerId, volunteerName, isVolunteerTime, inKindValue (market value of volunteer hours)
JournalSubmission (src/schemas/journals.schema.ts)
- Core:
id, organizationId, userId, userName, month (YYYY-MM format)
- Status:
JournalSubmissionStatus enum: DRAFT, SUBMITTED, APPROVED, REJECTED, ESCALATED
- Workflow:
submittedAt, submittedBy, reviewedAt, reviewedBy, reviewerComment, escalatedAt, escalationReason
- Content:
input (user-provided narrative), generatedEntries (AI-generated journal entries)
- Validation:
validationFeedback[], isValid
- Audit:
version, createdAt, lastModifiedAt, aiGenerationMetadata
- Compliance:
complianceScore (auto-calculated)
Key Behaviors
Daily Journal Workflow
-
Entry Creation:
- User creates a journal entry via calendar grid, weekly matrix, or manual entry form
- Entry defaults to
status: 'pending', isLocked: false
- Each entry is auto-saved via
journalService.saveEntries() (batch operation)
-
Submission:
- User submits batch of entries →
journalService.submitJournals()
- System checks organization’s auto-approval rules
- Auto-approve path: If daily hours ≤ threshold, no excluded projects, description meets requirements → status becomes
'approved', locked
- Manual approval path: Otherwise → status becomes
'submitted', awaiting manager review
- Event emitted:
JOURNAL_SUBMITTED (notifies manager via internal notification system) or JOURNAL_APPROVED (if auto-approved)
-
Manager Review:
- Manager dashboard displays pending submissions grouped by employee
- Approve:
journalService.approveJournals() → status: 'approved', isLocked: true, event JOURNAL_APPROVED
- Reject:
journalService.rejectJournals() → status: 'rejected', isLocked: false, event JOURNAL_REJECTED with reason
- Optionally add comment
-
Locking & Audit:
- Approved entries are locked (
isLocked: true) and cannot be edited
- Rejected entries are unlocked and returned for revision
- All status changes logged to audit trail
Journal Submission Workflow
-
Draft Creation:
- User navigates to the journal submission wizard
- Completes interview form (
JournalInput) with narrative questions
- Save as draft →
journalWorkflowService.saveDraft(), status DRAFT
- Auto-drafts are stored; user can return to edit
-
AI Generation:
- User submits draft for AI processing
- Genkit generates daily entries from narrative via
useJournalGeneration hook
- Entries are validated against compliance rules (compliance score calculated)
- User reviews generated entries and validation feedback
- Can edit/delete individual entries before final submission
-
Submission:
- User submits →
journalWorkflowService.submitJournal()
- Status changes to
SUBMITTED
- Event
JOURNAL_SUBMITTED notifies manager
- Entries are marked
isLocked: false (still editable by manager if needed)
-
Manager Approval/Rejection:
- Manager reviews submission on dashboard
- Approve:
journalWorkflowService.approveJournal() → APPROVED, generated entries locked
- Reject:
journalWorkflowService.rejectJournal() → REJECTED, entries remain unlocked for revision
- Manager can edit individual entries; changes logged to history subcollection
-
Audit Trail:
- All changes (draft, edit, submit, approve, reject) immutably logged in
history subcollection
- Each change record includes: timestamp, actor, change type, modified fields, optional comment
Service Contract
| Service | Owns | Key Methods |
|---|
| JournalService | Daily journal CRUD, approval workflow, locking | createJournal(), updateJournal(), deleteJournal(), getJournalsPaginated(), submitJournals(), approveJournals(), rejectJournals(), lockJournals(), saveEntries() |
| JournalWorkflowService | Journal submission CRUD, journal submission workflow, history auditing | saveDraft(), loadDraft(), listDrafts(), deleteDraft(), submitJournal(), loadSubmission(), listSubmissions(), approveJournal(), rejectJournal(), updateEntry(), deleteEntry(), fetchJournalHistory() |
| journalEventService | Event emission for approval workflows | emitJournalSubmittedEvent(), emitJournalApprovedEvent(), emitJournalRejectedEvent(), toJournalEventRecord(), toJournalEventRecordFromEntry() |
| JournalContext | Legacy bridge to new tRPC hooks; filters and selectors | useJournals(), useJournal(), useJournalsByUser(), useJournalsByProject(), usePendingJournals(), useApprovedJournals(), useLockedJournals() + date-range selectors |
Note: JournalService and JournalWorkflowService both extend BaseService, providing error handling, validation, audit logging, and infrastructure dependency injection.
Events
Emitted
| Event | Trigger | Severity | Persisted | Payload |
|---|
JOURNAL_SUBMITTED | User submits a daily journal or journal submission for approval | INFO | Yes | journalId, employeeId, employeeName, projectId, projectName, hours, date, managerId (optional) |
JOURNAL_APPROVED | Manager or auto-approval system approves journal | INFO | Yes | journalId, employeeId, employeeName, projectId, projectName, hours, approvedBy, approvedByName, approvedAt |
JOURNAL_REJECTED | Manager rejects journal (daily or monthly) | WARNING | Yes | journalId, employeeId, employeeName, projectId, projectName, hours, rejectedBy, rejectedByName, rejectedAt, reason |
All events routed via EventBus → persisted to systemEvents collection → listeners (internal event subscribers) notify managers and employees.
Consumed
- Internal notification subscribers: Subscribe to
JOURNAL_SUBMITTED, JOURNAL_APPROVED, JOURNAL_REJECTED to trigger in-app and email notifications
- Audit/compliance handlers: May subscribe to approval events for reporting
Dependencies
Internal:
@/core/BaseService — base class for CRUD and validation
@/core/eventBus — EventBus for approval/rejection notifications
@/core/firebase — Firestore instance
@/core/firestoreCollections — collection path utilities
@/features/compliance/services/journalComplianceService — compliance scoring for journal submissions
@/features/ai/services/ragService (via useJournalGeneration) — AI generation of daily entries
External:
- Firebase (Firestore, Auth)
- Internal notification system (
src/features/notifications/)
- Sentry (error reporting)
File Structure
src/features/journals/
├── JournalContext.tsx # Legacy context bridge; provides useJournals(), selectors
├── JournalGenerationContext.tsx # Context for AI generation state (useJournalGeneration hook)
├── index.ts # Public API barrel export
├── public.ts # (Appears unused; may be legacy)
├── services/
│ ├── JournalService.ts # Daily journal CRUD, approval workflow, locking
│ ├── JournalService.test.ts
│ ├── journalWorkflowService.ts # Journal submission CRUD and workflow
│ ├── journalWorkflowService.test.ts
│ ├── journalEventService.ts # Event emission helpers (JOURNAL_SUBMITTED, JOURNAL_APPROVED, JOURNAL_REJECTED)
│ ├── journalSubmissionService.ts # (May be deprecated; check usage)
│ ├── timerEntryService.ts # Active timer state (in-progress time tracking)
│ ├── timerService.ts # Timer start/stop/track logic
│ ├── bulkApprovalService.ts # Batch approval operations
│ └── bulkJournalApprovalService.ts # (Possible alternate; check overlap)
├── hooks/
│ ├── useJournals.ts # tRPC hook for fetching/filtering journal entries
│ ├── useJournalGeneration.ts # Hook wrapping AI generation (Genkit integration)
│ └── usePendingApprovalsCount.ts # Count of pending submissions for manager dashboard
├── components/
│ ├── JournalDetailsModal.tsx # Modal for viewing/editing a single entry
│ ├── JournalHistory.tsx # Audit trail display + PDF export
│ ├── JournalPreview.tsx # Compact entry preview card
│ ├── TraditionalJournalModal.tsx # Modal for manual daily entry creation
│ ├── journal/
│ │ ├── AuditPanel.tsx # Displays audit flags/compliance warnings
│ │ ├── CalendarGrid.tsx # Month-view calendar entry editor
│ │ ├── EntryList.tsx # Table of daily entries with filters
│ │ └── WeeklyMatrix.tsx # Week-view grid editor (7 days × projects)
│ ├── journalSubmission/
│ │ ├── ActivityPreviewCard.tsx # Card showing generated entry from AI
│ │ ├── DraftsList.tsx # List of draft journal submissions
│ │ ├── EditEntryModal.tsx # Employee edits AI-generated entry
│ │ ├── ManagerEditEntryModal.tsx # Manager edits/reviews entry post-approval
│ │ ├── SubmissionHistory.tsx # Change log (version, actor, timestamp)
│ │ └── ValidationPanel.tsx # Shows validation feedback & compliance score
│ ├── pages/
│ │ ├── ManagerReviewDashboard.tsx # Manager's approval interface
│ │ ├── JournalReview.tsx # (Appears related to journal review; check overlap with Dashboard)
│ │ ├── JournalSubmissions.tsx # Journal submission list/navigation page
│ │ └── Wizard.tsx # Multi-step form for journal submission input
│ └── pdf/
│ └── JournalPDF.tsx # Renders journal entries as PDF
└── README.md # Original README (may be outdated)
Naming Gotchas
⚠️ Firestore collection names differ from feature terminology:
- Firestore collection:
journals (legacy; never rename)
Code terminology: JournalEntry, “journals feature”
- Firestore collection:
journalSubmissions (legacy; never rename)
Code terminology: JournalSubmission, “journal submissions”
- Firestore subcollection:
history (audit log)
Code terminology: JournalSubmissionHistory
Why: Early-stage naming preserved for backward compatibility; collection renames would break production data.
Integration Notes
- Status transitions:
pending → submitted (user action) → approved/rejected (manager action). Rejected journals are unlocked and re-enter pending.
- Locking: Set by
lockJournals() when approved; enforced at update/delete time via business rule check.
- Auto-approval: Configured in
Organization.autoApprovalRules.journals (max daily hours, excluded projects, description length).
- AI generation: Triggered via
useJournalGeneration hook; uses Genkit to expand narrative into structured daily entries.
- Compliance scoring:
journalWorkflowService.updateWithGeneratedEntries() auto-calculates compliance score against organization rules.
- Audit trail: All events persisted to
systemEvents collection; journal submissions have immutable history subcollection per submission.