{{ theme.skipToContentLabel || 'Skip to content' }}

System Design Document (SDD)

Dustac Environmental Monitoring Dashboard

Version: 1.0 Date: December 2, 2025 Status: Draft


Document Control

VersionDateAuthorDescription
1.02025-12-02Dustac Development TeamInitial draft

Table of Contents

  1. Introduction
  2. General Overview and Design Guidelines/Approach
  3. Design Considerations
  4. System Architecture and Architecture Design
  5. System Design
  6. Operational Scenarios
  7. Detailed Design
  8. System Integrity Controls
  9. External Interfaces
  10. Appendices

List of Figures

Figure #TitlePage
Figure 4-1High-Level System Architecture DiagramTBD
Figure 4-2Logical View DiagramTBD
Figure 4-3Hardware Architecture DiagramTBD
Figure 4-4Software Architecture DiagramTBD
Figure 4-5Security Architecture DiagramTBD
Figure 4-6System Architecture DiagramTBD
Figure 5-1Entity-Relationship DiagramTBD
Figure 5-2User Interface Navigation FlowTBD
Figure 6-1User Registration Flow DiagramTBD
Figure 6-2Data Ingestion Flow DiagramTBD
Figure 6-3Report Generation Flow DiagramTBD
Figure 7-1Component Interaction DiagramTBD
Figure 7-2Sequence Diagram - AuthenticationTBD
Figure 9-1External Interface ArchitectureTBD

List of Tables

Table #TitlePage
Table 2-1Risk Assessment MatrixTBD
Table 4-1Hardware SpecificationsTBD
Table 4-2Software ComponentsTBD
Table 5-1Database Tables OverviewTBD
Table 5-2Data DictionaryTBD
Table 5-3Input Data FormatsTBD
Table 5-4Output Data FormatsTBD
Table 9-1External Interface InventoryTBD
Table A-1Record of ChangesTBD
Table B-1AcronymsTBD
Table C-1GlossaryTBD
Table D-1Referenced DocumentsTBD
Table E-1ApprovalsTBD

1. Introduction

1.1 Purpose of the SDD

This System Design Document (SDD) provides a comprehensive technical specification for the Dustac Environmental Monitoring Dashboard system. The document serves multiple critical purposes:

  1. Technical Blueprint: Provides detailed architecture and design specifications to guide the development team in implementing system components, ensuring consistency and adherence to established patterns.

  2. Stakeholder Communication: Enables clear communication between technical and non-technical stakeholders by documenting system capabilities, constraints, and design decisions in a structured format.

  3. Quality Assurance Foundation: Establishes the baseline for testing strategies, validation procedures, and acceptance criteria, ensuring the delivered system meets specified requirements.

  4. Maintenance and Evolution: Serves as authoritative reference documentation for system maintenance, troubleshooting, and future enhancements, reducing knowledge transfer friction.

  5. Risk Mitigation: Documents assumptions, constraints, and risk mitigation strategies to proactively address potential issues during development and deployment.

This document bridges the gap between high-level business requirements and low-level implementation details, providing sufficient information for developers to build the system while maintaining alignment with business objectives.

1.2 Project Background

The Dustac Environmental Monitoring Dashboard addresses critical needs in the mining industry for real-time environmental compliance monitoring and data management. Mining operations must continuously monitor airborne particulate matter (dust) to comply with environmental regulations, protect worker health, and maintain community relations.

Business Context:

  • Mining sites deploy DustRanger monitoring devices that collect environmental measurements including PM1.0, PM2.5, PM4.0, and PM10 particulate concentrations, along with temperature, humidity, and location data
  • Regulatory requirements mandate accurate record-keeping, reporting, and trend analysis of environmental metrics
  • Operations teams need accessible, actionable insights from collected data to make informed decisions about dust suppression activities

Historical Evolution: The system has evolved through five major development phases:

  • Phase 1 (Q1 2025): Foundation - User authentication, database schema, and automated data scraping capabilities
  • Phase 2 (Q1 2025): Core features - PDF report generation, dashboard analytics, and data visualization
  • Phase 3 (Q2 2025): Report management - Calendar views, bulk operations, and report templates
  • Phase 4 (Q2 2025): Data management - Multi-site support, device tracking, and advanced filtering
  • Phase 5+ (Q3-Q4 2025): Extended features - Real-time dust level monitoring, weekly field reports, flow meter tracking, climate integration, and AI-powered chart descriptions

Current State: The system is deployed in production, serving multiple mining sites with daily active users managing environmental compliance data. The architecture leverages modern cloud technologies (Supabase, Cloudflare Pages) to provide scalable, secure, and performant services.

1.3 Intended Audience

This document is intended for the following audiences, each with specific interests:

Primary Audiences:

  • Software Developers: Detailed technical specifications for implementation, including database schemas, API designs, component architecture, and coding standards
  • System Architects: High-level architecture patterns, technology stack decisions, integration approaches, and scalability considerations
  • DevOps Engineers: Deployment architecture, infrastructure requirements, CI/CD pipelines, monitoring strategies, and operational procedures

Secondary Audiences:

  • Project Managers: Project scope, constraints, risks, timelines, and resource requirements for planning and tracking
  • Quality Assurance Engineers: Testing strategies, validation scenarios, acceptance criteria, and quality metrics
  • Security Officers: Security architecture, authentication mechanisms, data protection strategies, and compliance considerations
  • Business Stakeholders: System capabilities, business value delivery, operational scenarios, and user experience design

Supporting Audiences:

  • Technical Support Teams: System operation, troubleshooting procedures, and maintenance guidelines
  • External Auditors: Compliance verification, security controls, and audit trail mechanisms
  • Future Development Teams: System understanding for maintenance and enhancement activities

Each audience should focus on sections most relevant to their role while maintaining awareness of the complete system context.

1.4 Document Scope

Inclusions:

This document comprehensively covers:

  1. System Architecture: Complete hardware, software, information, and security architecture specifications
  2. Database Design: Physical data models, table schemas, indexing strategies, and Row-Level Security (RLS) policies
  3. Feature Modules: Detailed design for nine feature modules including data ingestion, reports, dust levels, weekly reports, flow meter, climate, report templates, mine sites, and dashboard
  4. User Interface Design: UI framework, component structure, navigation patterns, and accessibility compliance
  5. API Design: REST endpoints, RPC functions, Edge Functions, and external integrations
  6. Security Design: Authentication flows, authorization models, encryption standards, and audit logging
  7. Performance Design: Optimization strategies, caching mechanisms, scalability patterns, and performance targets
  8. Operational Scenarios: Step-by-step user workflows for key system functions
  9. Deployment Architecture: Development, staging, and production environments with CI/CD pipelines

Exclusions:

This document explicitly does not cover:

  1. Project Management: Project plans, sprint schedules, resource allocation, or budget details (covered in separate project management documentation)
  2. Business Requirements: Detailed business requirements specification (referenced from Requirements Document)
  3. User Training: End-user training materials, tutorials, or help documentation
  4. Marketing Materials: Product positioning, sales collateral, or customer-facing materials
  5. Third-Party System Internal Design: Internal implementation details of Supabase, Cloudflare, or external APIs (only integration interfaces documented)
  6. Future Enhancements: Speculative features beyond Phase 5+ scope (documented separately in product roadmap)
  7. Source Code: Actual implementation code (maintained in version control repository)

Document Boundaries:

This SDD focuses on the "how" of system implementation rather than the "why" of business decisions. Business justifications and requirement specifications are referenced from the Requirements Document and SYSTEM_DESIGN.md. Implementation-level details such as specific code patterns and algorithms are provided at an appropriate level of abstraction for technical understanding without duplicating source code.


2. General Overview and Design Guidelines/Approach

2.1 General Overview

The Dustac Environmental Monitoring Dashboard is a cloud-native web application that enables mining operations to collect, analyze, visualize, and report on environmental dust monitoring data. The system provides a comprehensive platform for managing regulatory compliance while delivering actionable insights for operational decision-making.

System Purpose:

The system serves three primary functions:

  1. Data Ingestion and Management: Automatically collect data from DustRanger monitoring devices via Edge Function scrapers, validate and store measurements in a centralized database, and maintain data integrity through comprehensive validation rules.

  2. Analysis and Visualization: Transform raw measurement data into meaningful insights through interactive dashboards, statistical summaries, trend analysis, and comparative visualizations across multiple sites and time periods.

  3. Reporting and Compliance: Generate professional PDF reports for regulatory submission, maintain audit trails for compliance verification, and provide structured weekly field reports for operational documentation.

Key System Capabilities:

  • Multi-Site Management: Support for multiple mining sites with independent data streams and reporting
  • Real-Time Monitoring: Live dust level tracking with automated data collection from external scrapers
  • Flexible Reporting: Customizable report templates with AI-generated chart descriptions
  • Water Management: Flow meter tracking for dust suppression water usage monitoring
  • Climate Integration: Weather data correlation for environmental context
  • Role-Based Access: Secure, user-specific data access with administrative oversight capabilities
  • Audit Compliance: Comprehensive activity logging for regulatory audit trails

High-Level Context Diagram:

┌─────────────────────────────────────────────────────────────────────────┐
│                          External Entities                               │
└─────────────────────────────────────────────────────────────────────────┘

                     ┌──────────────┼──────────────┐
                     │              │              │
                     ▼              ▼              ▼
            ┌─────────────┐  ┌──────────┐  ┌──────────────┐
            │  End Users  │  │ DustRanger│  │   External   │
            │  (Browser)  │  │  Devices  │  │  Data APIs   │
            └──────┬──────┘  └────┬─────┘  └──────┬───────┘
                   │              │                │
                   │              │                │
                   ▼              ▼                ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                       Dustac Dashboard System                            │
│  ┌────────────────────────────────────────────────────────────────┐    │
│  │                    Frontend (React 19 SPA)                      │    │
│  │  • Dashboard  • Reports  • Weekly Reports  • Dust Levels        │    │
│  └────────────────────┬────────────────────────────────────────────┘    │
│                       │                                                  │
│                       │ Supabase Client SDK                              │
│                       │                                                  │
│  ┌────────────────────┴────────────────────────────────────────────┐   │
│  │              Supabase Backend-as-a-Service                       │   │
│  │  ┌──────────────┐  ┌─────────────┐  ┌─────────────────────┐    │   │
│  │  │ PostgreSQL   │  │  Auth       │  │  Storage (S3-like)  │    │   │
│  │  │  Database    │  │  (JWT)      │  │  • CSV Files        │    │   │
│  │  │  • RLS       │  │  • Email    │  │  • PDF Reports      │    │   │
│  │  └──────────────┘  └─────────────┘  └─────────────────────┘    │   │
│  │  ┌──────────────────────────────────────────────────────────┐  │   │
│  │  │           Edge Functions (Deno Runtime)                   │  │   │
│  │  │  • Chart Description AI  • Scraper Triggers               │  │   │
│  │  └──────────────────────────────────────────────────────────┘  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────┘

                     ┌──────────────┼──────────────┐
                     │              │              │
                     ▼              ▼              ▼
            ┌─────────────┐  ┌──────────┐  ┌──────────────┐
            │   Dustac    │  │  Google  │  │  BOM Weather │
            │   Scraper   │  │  Gemini  │  │     API      │
            │     API     │  │    AI    │  │              │
            └─────────────┘  └──────────┘  └──────────────┘

System Boundaries:

  • Internal: User interface, business logic, data storage, report generation, authentication, and authorization
  • External: DustRanger device data (CSV), external scraper APIs (Dustac, Flow Meter), AI services (Google Gemini), weather data (Bureau of Meteorology)

2.2 Assumptions/Constraints/Risks

2.2.1 Assumptions

The following assumptions underpin the system design:

Technical Assumptions:

  1. Browser Support: Users access the system via modern web browsers (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+) with JavaScript enabled
  2. Network Connectivity: Users have stable internet connectivity with minimum 1 Mbps download speed for dashboard functionality
  3. CSV Data Format: DustRanger devices produce CSV files with consistent column structure as defined in system specifications
  4. Supabase Availability: Supabase platform maintains 99.9% uptime SLA with automatic failover capabilities
  5. Cloudflare CDN: Cloudflare Pages provides global CDN distribution with acceptable latency (<200ms) for target user locations

Operational Assumptions:

  1. User Training: Users receive basic training on system navigation and data management before production use
  2. Data Volume: Automated scrapers collect data at regular intervals; system handles up to 500MB of daily data per site
  3. Concurrent Users: System supports up to 50 concurrent users during peak operational hours
  4. Report Generation: PDF generation completes within 60 seconds for standard reports (up to 30 pages)
  5. Data Retention: Users require access to historical data for minimum 12 months for compliance purposes

Business Assumptions:

  1. Single Tenant Model: Each organization operates independently with isolated data; no cross-organization data sharing required
  2. English Language: Primary user interface and reports in English; internationalization not required in Phase 5
  3. Email Verification: Users have access to corporate email for account verification and notifications
  4. Regulatory Stability: Environmental reporting requirements remain stable during system lifecycle

2.2.2 Constraints

The following constraints influence system design decisions:

Technical Constraints:

  1. Supabase Limitations:

    • Free tier: 500MB database storage, 1GB file storage
    • Pro tier: 8GB database storage, 100GB file storage
    • Row-Level Security overhead impacts query performance for complex policies
    • PostgreSQL connection pool limited to 60 concurrent connections (Pro tier)
  2. Browser Constraints:

    • LocalStorage limited to 10MB for session data
    • Memory constraints for client-side PDF generation (html2canvas/jsPDF)
  3. External API Constraints:

    • Google Gemini API: 60 requests/minute rate limit
    • Dustac Scraper API: 10 requests/minute per user
    • BOM Weather API: Public data access, subject to throttling under heavy load

Business Constraints:

  1. Budget Limitations:

    • Development budget constrains third-party service selections to cost-effective solutions
    • Hosting costs must remain under $500/month for production deployment
  2. Timeline Constraints:

    • Phase 5+ features delivered incrementally over 6-month period
    • No breaking changes to existing data structures during active development

Regulatory Constraints:

  1. Data Privacy:

    • Must comply with general data protection principles
    • User data must remain within specified geographical regions (if required by local regulations)
    • Audit trails required for all data modifications
  2. Environmental Reporting:

    • PDF reports must meet formatting standards for regulatory submission
    • Data retention policies must align with environmental compliance requirements (typically 5-7 years)

Resource Constraints:

  1. Development Team: Core team of 2-3 developers; limited parallel development capacity
  2. Testing Infrastructure: Manual testing supplemented by automated unit/integration tests; limited E2E test coverage
  3. Documentation: Documentation maintained alongside development; dedicated technical writer not available

2.2.3 Risks

Risk IDDescriptionProbabilityImpactMitigation Strategy
R-001Supabase Platform Outage: Extended Supabase service disruption affecting system availabilityLow (5%)High• Implement comprehensive error handling with user-friendly messages
• Monitor Supabase status page
• Maintain backup CSV exports for critical data
• Document manual data access procedures
• Consider multi-region deployment for critical customers
R-002Data Loss: Database corruption or accidental deletion leading to data lossLow (5%)Critical• Leverage Supabase automatic backups (daily snapshots)
• Implement soft-delete patterns for user data
• Maintain audit logs for data recovery
• Regular backup verification testing
• Point-in-time recovery capability (Supabase Pro)
R-003Performance Degradation: System slowdown under high concurrent user loadMedium (30%)Medium• Implement database query optimization and indexing
• Use Supabase connection pooling
• Add client-side caching for static data
• Monitor query performance with slow query logs
• Implement pagination for large datasets
• Consider read replicas for scaling
R-004CSV Format Inconsistency: DustRanger devices output inconsistent CSV formats causing import failuresMedium (25%)Medium• Implement robust CSV validation with detailed error messages
• Provide CSV preview before import
• Maintain format version detection
• Create CSV format documentation for users
• Build flexible parser with configurable column mapping
R-005Security Breach: Unauthorized access to sensitive environmental dataLow (10%)High• Enforce Supabase Row-Level Security policies
• Implement JWT token expiration and refresh
• Regular security audits of RLS policies
• Monitor suspicious activity via activity logs
• Apply HTTPS everywhere with HSTS headers
• Regular dependency vulnerability scanning
R-006PDF Generation Failure: Report generation timeouts or failures under complex reportsMedium (20%)Medium• Implement retry logic with exponential backoff
• Optimize chart rendering performance
• Add timeout handling with partial report generation
• Provide alternative export formats (CSV, Excel)
• Queue system for background report generation
R-007External API Dependency: Dustac scraper or weather API unavailabilityMedium (25%)Low• Implement graceful degradation when external APIs unavailable
• Cache recently fetched data
• Provide manual data entry alternatives
• Display clear status indicators for external service health
• Document known service windows
R-008Browser Compatibility: Issues with specific browser versions or configurationsLow (15%)Low• Test on major browser versions during development
• Provide browser compatibility documentation
• Implement feature detection and polyfills
• Display browser compatibility warnings
• Monitor user-agent analytics for problem patterns
R-009Scalability Limitations: System unable to scale beyond 100+ concurrent usersLow (10%)Medium• Architecture designed for horizontal scaling via Supabase
• Monitor usage metrics and performance
• Plan for database scaling (vertical or read replicas)
• Implement rate limiting to prevent abuse
• Load testing before major user onboarding
R-010AI Service Costs: Google Gemini API costs exceed budget due to high usageMedium (20%)Low• Implement AI description caching
• Rate limit AI generation requests per user
• Provide option to disable AI descriptions
• Monitor API usage dashboards
• Set billing alerts and quotas

2.3 Alignment with Enterprise Architecture Standards

The Dustac Environmental Monitoring Dashboard aligns with modern enterprise architecture principles and industry best practices:

1. Cloud-Native Architecture:

  • Twelve-Factor App Principles: System adheres to 12-factor methodology including configuration management via environment variables, stateless processes, disposable infrastructure, and development/production parity
  • Microservices Pattern: Backend services decomposed into focused, independently deployable components (authentication, storage, edge functions)
  • API-First Design: All data access through well-defined APIs (Supabase REST/GraphQL, Edge Functions), enabling future mobile apps or third-party integrations

2. Security Best Practices:

  • Zero Trust Security Model: No implicit trust; every request authenticated and authorized via JWT tokens and RLS policies
  • Defense in Depth: Multiple security layers including transport encryption (TLS 1.3), database-level RLS, application-level authorization, and audit logging
  • OWASP Top 10 Compliance: Design addresses common vulnerabilities including injection attacks (parameterized queries), broken authentication (JWT with expiration), XSS (React's built-in escaping), and security misconfiguration (least privilege access)
  • Data Encryption: Data encrypted at rest (AES-256) and in transit (TLS 1.3); sensitive data in database encrypted at column level where required

3. Data Architecture Standards:

  • Single Source of Truth: PostgreSQL database serves as authoritative data store; no data duplication across services
  • Data Normalization: Database schema follows third normal form (3NF) to minimize redundancy while maintaining query performance
  • Audit Trail: Comprehensive activity logging for compliance and forensic analysis
  • Data Lifecycle Management: Defined retention policies, archival strategies, and GDPR-compliant data deletion procedures

4. Integration Patterns:

  • RESTful APIs: External integrations follow REST principles with proper HTTP verbs, status codes, and versioning
  • Asynchronous Processing: Long-running operations (report generation, data imports) handled asynchronously with status polling
  • Circuit Breaker Pattern: External API calls wrapped with timeout and retry logic to prevent cascading failures
  • Event-Driven Updates: Real-time subscriptions (Supabase Realtime) for collaborative features and live data updates

5. Development Standards:

  • Infrastructure as Code: Infrastructure configuration managed in version control (Supabase migrations, Cloudflare configuration)
  • Continuous Integration: Automated testing (unit, integration, E2E) in CI pipeline before deployment
  • Version Control: Git-based version control with conventional commits and semantic versioning
  • Code Quality: ESLint rules, Prettier formatting, TypeScript strict mode, and code review requirements

6. Accessibility and Usability:

  • WCAG 2.1 AA Compliance: User interface designed for accessibility with keyboard navigation, screen reader support, and sufficient color contrast
  • Responsive Design: Mobile-first design approach with responsive layouts for desktop, tablet, and mobile viewports
  • Progressive Enhancement: Core functionality works without JavaScript; enhanced features layer progressively
  • Internationalization Ready: Architecture supports future multi-language expansion (i18next integration)

7. Performance Standards:

  • Core Web Vitals: Target metrics include LCP <2.5s, FID <100ms, CLS <0.1
  • API Response Times: 95th percentile response time <500ms for read operations, <2s for write operations
  • Scalability: Architecture supports horizontal scaling to handle 10x current user load without redesign
  • Caching Strategy: Multi-level caching (browser, CDN, database) to minimize redundant data transfers

8. Compliance and Governance:

  • Environmental Compliance: System supports regulatory reporting requirements for mining environmental monitoring
  • Audit Readiness: Complete audit trails with tamper-evident logging for compliance verification
  • Data Sovereignty: Deployment configuration supports data residency requirements for different jurisdictions
  • Disaster Recovery: RPO <24 hours, RTO <4 hours via automated backups and recovery procedures

Industry Framework Alignment:

  • NIST Cybersecurity Framework: Security controls mapped to NIST CSF categories (Identify, Protect, Detect, Respond, Recover)
  • ISO 27001 Principles: Information security management practices aligned with ISO 27001 standards
  • SOC 2 Type II: Supabase infrastructure maintains SOC 2 Type II compliance for service provider assurance

This alignment ensures the system is built on proven patterns, maintainable by industry-standard practices, and positioned for long-term sustainability and growth.


3. Design Considerations

3.1 Goals and Guidelines

The system design is governed by a set of prioritized goals and guidelines that ensure consistency, maintainability, and alignment with business objectives.

Design Priority Hierarchy:

  1. Security First: Data protection and access control take precedence over convenience features
  2. Reliability: System availability and data integrity prioritized over new feature velocity
  3. User Experience: Intuitive, accessible interfaces preferred over feature density
  4. Performance: Acceptable response times maintained even under load
  5. Maintainability: Code clarity and documentation valued for long-term sustainability
  6. Scalability: Architecture supports growth without fundamental redesign

Coding Standards and Conventions:

TypeScript Standards:

  • Strict Mode Enabled: Full TypeScript strict mode enforced (strict: true in tsconfig.json)
  • Explicit Types: Function parameters, return types, and complex objects explicitly typed
  • Type Imports: Use import type for type-only imports (enforced by ESLint)
  • No Any: any type prohibited except for documented exceptional cases
  • Null Safety: noUncheckedIndexedAccess enabled; all array/object access checked for undefined

Import Organization (ESLint enforced):

typescript
// 1. Type imports
import type { Database } from '@/lib/supabaseTypes';

// 2. External dependencies
import { useState, useEffect } from 'react';
import { supabase } from '@supabase/supabase-js';

// 3. Internal imports (@ alias)
import { cn } from '@/lib/utils';
import { Button } from '@/components/ui/button';

React Component Standards:

  • Functional Components: Use function declarations, not arrow functions for components
  • Named Exports: No default exports for components (improves refactoring)
  • Props Interface: Define explicit interface for all component props
  • JSX Prop Ordering (ESLint enforced): reserved props → shorthand → callbacks → multiline
  • Hooks Rules: Follow hooks rules (only at top level, only in React functions)

File Naming Conventions:

  • Components: PascalCase (e.g., ReportTemplate.tsx, DataUploadForm.tsx)
  • Hooks: camelCase with use prefix (e.g., useReportGenerator.tsx, useAuth.tsx)
  • Services: camelCase (e.g., csvParser.ts, uploadService.ts)
  • Types: PascalCase for interfaces/types, camelCase for files (e.g., types.ts exports UploadStatus)
  • Tests: Same as source file with .test.ts or .spec.ts suffix

Git Commit Conventions (Commitlint enforced):

<type>(<scope>): <subject>

feat(upload): add support for multi-file CSV upload
fix(reports): correct date range filtering in PDF generation
chore(deps): update TanStack Router to v1.134.9
docs(phase4): add report management implementation notes
  • Types: feat, fix, chore, docs, test, refactor, style, perf
  • Scopes: upload, reports, dashboard, auth, dust-levels, flow-meter, etc.
  • Subject: Imperative mood, lowercase, no period, max 72 characters

UI/UX Guidelines:

Design System:

  • Component Library: shadcn/ui (Radix UI primitives + Tailwind CSS)
  • Utility-First CSS: Tailwind CSS for styling; avoid custom CSS unless necessary
  • Class Name Merging: Use cn() utility for conditional class names
  • Spacing Scale: Tailwind's default spacing scale (4px increments)
  • Color System: Consistent color palette defined in Tailwind config

Accessibility Requirements:

  • Keyboard Navigation: All interactive elements accessible via keyboard
  • Focus Indicators: Visible focus states for all interactive elements
  • ARIA Labels: Descriptive labels for screen readers where visual labels insufficient
  • Color Contrast: WCAG AA contrast ratios (4.5:1 for normal text, 3:1 for large text)
  • Semantic HTML: Use appropriate HTML elements (buttons, links, headings)

Responsive Design:

  • Mobile-First: Design for mobile viewports first, enhance for larger screens
  • Breakpoints: Tailwind default breakpoints (sm: 640px, md: 768px, lg: 1024px, xl: 1280px)
  • Touch Targets: Minimum 44x44px for touch interactive elements
  • Layout Patterns: Flexbox and Grid for responsive layouts

User Feedback:

  • Loading States: Show loading indicators for operations >300ms
  • Error Messages: Clear, actionable error messages with recovery guidance
  • Success Confirmations: Toast notifications for successful operations
  • Progress Indicators: Progress bars/percentages for long-running operations

API Design Principles:

Supabase REST API Usage:

  • Parameterized Queries: Always use parameterized queries to prevent SQL injection
  • Selective Columns: Select only needed columns to minimize data transfer
  • Pagination: Use range() for large result sets; default page size 50 items
  • Error Handling: Wrap all Supabase calls in try-catch with user-friendly error messages

RPC Function Design:

  • Single Responsibility: Each RPC function performs one clear operation
  • Input Validation: Validate parameters within function; return descriptive errors
  • Transaction Safety: Use transactions for multi-table operations
  • Performance: Optimize queries; avoid N+1 patterns

Edge Function Standards:

  • Stateless: No server-side state between requests
  • Timeout Handling: Operations complete within 60s timeout (Supabase limit)
  • Error Response Format: Consistent JSON error format with status codes
  • Logging: Structured logging for debugging and monitoring

3.2 Development Methods & Contingencies

Development Methodology:

The project follows an Agile iterative approach with continuous delivery:

Sprint Structure:

  • Sprint Duration: 2-week sprints
  • Planning: Sprint planning at start; story pointing with Fibonacci sequence
  • Daily Standups: Asynchronous updates in project management tool
  • Sprint Reviews: Demo completed features to stakeholders
  • Retrospectives: Team reflection and process improvement

Development Workflow:

  1. Feature Branch: Create feature branch from main (e.g., feat/upload-validation)
  2. Development: Implement feature with tests; commit frequently with conventional commits
  3. Testing: Run unit tests, integration tests, and manual testing
  4. Code Review: Pull request reviewed by at least one team member
  5. CI Pipeline: Automated tests and linting in GitHub Actions
  6. Merge: Squash and merge to main after approval
  7. Deploy: Automatic deployment to staging; manual promotion to production

Quality Gates:

  • All tests passing (unit, integration, E2E for critical paths)
  • No new TypeScript errors
  • ESLint warnings below threshold (500 max)
  • Code review approval
  • Security scan passes (dependency vulnerabilities)

Contingency Plans:

1. Third-Party Service Failure (Supabase/Cloudflare):

  • Detection: Monitor service status pages and health check endpoints
  • Response: Display user-friendly error messages with estimated recovery time
  • Mitigation: Implement circuit breakers; graceful degradation where possible
  • Recovery: Automatic retry with exponential backoff; manual escalation if persists >30 minutes

2. Design Ambiguity Discovered During Development:

  • Detection: Developer identifies unclear requirement or conflicting specifications
  • Response: Document ambiguity in issue tracker with specific questions
  • Resolution: Schedule clarification session with product owner/stakeholders
  • Timeline: Block development on ambiguous feature; pivot to other sprint items
  • Documentation: Update requirements document with clarified decisions

3. Performance Issues During Development:

  • Detection: Identify slow queries, high latency, or poor user experience during testing
  • Analysis: Use browser DevTools, Supabase query analyzer, and performance profiling
  • Response Options:
    • Quick win: Add database indexes, optimize queries, add caching
    • Architectural: Redesign data access patterns, introduce background jobs
    • Scope: Reduce feature complexity or split into phases
  • Decision: Evaluate effort vs. impact; escalate if affects core functionality

4. Breaking Changes in Dependencies:

  • Prevention: Pin dependency versions; review changelogs before upgrading
  • Detection: Automated dependency update PRs (Dependabot) with CI checks
  • Response: Test thoroughly in development environment before merging
  • Rollback: Revert to previous version if breaking changes discovered post-deployment

5. Database Schema Changes Required:

  • Process: Create Supabase migration file; test on local database first
  • Backward Compatibility: Prefer additive changes; avoid breaking changes to production data
  • Deployment: Apply migration during low-traffic window; monitor for errors
  • Rollback: Prepare rollback migration in advance; test rollback procedure

3.3 Architectural Strategies

Technology Selection Rationale:

Frontend Stack:

TechnologyRationaleTrade-offs
React 19Industry-standard UI library; large ecosystem; excellent TypeScript support; concurrent features for performanceLearning curve for new developers; bundle size larger than lightweight alternatives
ViteFastest build tool; excellent DX with HMR; native ESM; smaller production bundles than WebpackLess mature plugin ecosystem than Webpack
React Router v7Type-safe routing; excellent TypeScript integration; nested layouts; data loading patternsMore opinionated than alternatives like React Navigation
Tailwind CSSUtility-first rapid development; consistent design system; excellent tree-shaking; responsive design utilitiesLarge class names in JSX; less intuitive for CSS experts
shadcn/uiAccessible components (Radix UI); full customization; copy-paste approach (not dependency); Tailwind integrationRequires manual updates for component improvements
ZustandLightweight state management; minimal boilerplate; TypeScript support; no Provider wrappingLess structured than Redux; fewer dev tools
React Hook FormPerformant form validation; excellent UX; Zod integration; minimal re-rendersAPI different from traditional controlled forms

Backend Stack:

TechnologyRationaleTrade-offs
SupabaseBackend-as-a-Service; PostgreSQL; built-in auth; RLS security; real-time subscriptions; S3-compatible storage; cost-effectiveVendor lock-in; less control than self-hosted; RLS complexity
PostgreSQLProduction-grade RDBMS; JSON support; full-text search; excellent query optimizer; mature ecosystemMore complex than SQLite; requires understanding of SQL optimization
Edge FunctionsServerless compute; Deno runtime; TypeScript native; integrated with Supabase; pay-per-use pricingCold start latency; 60s timeout limit; limited CPU/memory
Supabase StorageS3-compatible object storage; CDN integration; access control; image transformationsLess feature-rich than AWS S3; 5GB free tier limit

Infrastructure Stack:

TechnologyRationaleTrade-offs
Cloudflare PagesGlobal CDN; automatic deployments from Git; preview deployments; free tier generous; edge cachingLimited server-side rendering options; configuration less flexible than Vercel
GitHub ActionsIntegrated with repository; generous free tier; extensive marketplace; easy configurationSlower than dedicated CI services; limited concurrent jobs on free tier

Design Patterns Employed:

1. Feature-Driven Architecture:

  • Pattern: Organize code by feature/domain rather than technical layer
  • Implementation: /src/features/ with self-contained modules (components, services, hooks, types)
  • Benefits: Better code organization, easier to find related code, supports team scalability
  • Trade-off: Some code duplication across features; shared utilities in /src/lib/

2. Service Layer Pattern:

  • Pattern: Separate business logic from UI components
  • Implementation: Service files handle data fetching, transformations, and business rules
  • Benefits: Testable business logic, reusable across components, clearer separation of concerns
  • Example: uploadService.ts handles CSV processing independently of React components

3. Repository Pattern (via Supabase):

  • Pattern: Abstract data access layer behind service interfaces
  • Implementation: Services encapsulate Supabase queries; components don't call Supabase directly
  • Benefits: Easier to test (mock services), potential to swap data source, consistent error handling
  • Example: reportDataService.ts provides getReportData() without exposing SQL details

4. Hooks Pattern:

  • Pattern: Encapsulate stateful logic in reusable custom hooks
  • Implementation: Custom hooks for data fetching, form management, authentication state
  • Benefits: Reusable logic, easier testing, cleaner components
  • Example: useUser() hook provides authentication state to any component

5. Composition Pattern:

  • Pattern: Build complex components from smaller, focused components
  • Implementation: shadcn/ui components composed into feature-specific components
  • Benefits: Reusable building blocks, easier testing, consistent UI
  • Example: DataUploadForm composes Button, Input, Label, Card primitives

6. Error Boundary Pattern:

  • Pattern: Catch React errors and display fallback UI
  • Implementation: Error boundaries wrap route components
  • Benefits: Prevents white screen of death, graceful degradation, error logging opportunity
  • Example: App-level error boundary catches uncaught errors, displays friendly message

Trade-off Decisions and Justifications:

Decision 1: Client-Side PDF Generation vs. Server-Side:

  • Options: (A) Server-side with Puppeteer, (B) Client-side with jsPDF/html2canvas
  • Decision: Client-side generation (B)
  • Rationale: Simpler architecture, no server infrastructure, leverages user's CPU, better for Supabase serverless
  • Trade-off: Limited to client memory, slower on low-end devices, less control over rendering
  • Mitigation: Optimize charts before rendering, chunk large reports, provide loading indicators

Decision 2: Row-Level Security vs. Application-Layer Authorization:

  • Options: (A) RLS policies in PostgreSQL, (B) Authorization checks in application code
  • Decision: RLS policies (A)
  • Rationale: Defense in depth, prevents data leaks even if application logic buggy, Supabase best practice
  • Trade-off: Complex RLS policies impact query performance, harder to debug, requires SQL expertise
  • Mitigation: Optimize RLS policies, add indexes on user_id columns, document policies thoroughly

Decision 3: Monolithic SPA vs. Micro-frontends:

  • Options: (A) Single React SPA, (B) Micro-frontends per feature module
  • Decision: Monolithic SPA (A)
  • Rationale: Simpler deployment, shared state easier, team size doesn't require splitting, consistent UX
  • Trade-off: Larger bundle size, all features deployed together, potential merge conflicts
  • Mitigation: Code splitting per route, lazy loading, modular architecture within monolith

Decision 4: Real-time Updates vs. Polling:

  • Options: (A) Supabase Realtime subscriptions, (B) Polling with setInterval
  • Decision: Polling for most features, Realtime for collaborative editing (B with selective A)
  • Rationale: Simpler implementation, fewer open connections, polling sufficient for non-critical updates
  • Trade-off: Less immediate updates, higher server load, potential stale data
  • Mitigation: Refresh on user action, optimistic UI updates, use Realtime for weekly reports collaboration

Decision 5: TypeScript Strict Mode:

  • Options: (A) TypeScript strict mode, (B) Permissive mode
  • Decision: Strict mode (A)
  • Rationale: Catch errors at compile time, better IDE support, maintainability, industry best practice
  • Trade-off: More verbose code, steeper learning curve, migration effort for existing code
  • Mitigation: Gradual migration, helpful error messages, team training on TypeScript patterns

3.4 Performance Engineering

Performance Requirements and Targets:

MetricTargetMeasurement MethodPriority
Page Load Time (LCP)< 2.5s (75th percentile)Lighthouse, Core Web VitalsHigh
First Input Delay (FID)< 100msCore Web VitalsHigh
Cumulative Layout Shift (CLS)< 0.1Core Web VitalsMedium
Time to Interactive (TTI)< 3.5sLighthouseMedium
API Response Time (Read)< 500ms (95th percentile)Supabase logsHigh
API Response Time (Write)< 2s (95th percentile)Supabase logsMedium
PDF Generation< 60s for 30-page reportApplication logsHigh
CSV Upload Processing< 30s for 10K rowsApplication logsHigh
Dashboard Query< 1s for 90-day dataSupabase query analyzerHigh
Concurrent Users50 users without degradationLoad testing (Playwright)Medium

Performance Design Strategies:

1. Frontend Performance:

Code Splitting and Lazy Loading:

typescript
// Route-based code splitting
const DustLevels = lazy(() => import('@/features/dust-levels'));
const WeeklyReports = lazy(() => import('@/features/weekly-reports'));

// Component-level lazy loading for heavy components
const PDFViewer = lazy(() => import('@/components/PDFViewer'));

Benefits: Smaller initial bundle, faster page loads, load features on demand Trade-off: Slight delay when navigating to new routes (mitigated with Suspense)

Asset Optimization:

  • Image Optimization: Use modern formats (WebP), responsive images with srcset, lazy loading below fold
  • Bundle Size: Tree-shaking unused code, analyze bundle with Vite rollup visualizer
  • Minification: Automatic minification in production build (Terser)
  • Compression: Brotli compression via Cloudflare CDN

React Performance Patterns:

  • Memoization: Use useMemo for expensive calculations, useCallback for callbacks passed to children
  • Virtualization: Virtual scrolling for large lists (react-window) if needed
  • Debouncing: Debounce search inputs and filter changes (usehooks-ts)
  • Optimistic Updates: Update UI immediately, sync with server in background

2. Database Performance:

Indexing Strategy:

sql
-- Composite indexes for common query patterns
CREATE INDEX idx_measurements_site_time ON measurements(site, time);
CREATE INDEX idx_measurements_upload_time ON measurements(upload_id, time);

-- Partial indexes for filtered queries
CREATE INDEX idx_uploads_active ON uploads(user_id, status)
  WHERE status IN ('pending', 'importing');

-- GIN indexes for JSONB columns
CREATE INDEX idx_uploads_sites ON uploads USING GIN(sites);

Query Optimization:

  • Selective Columns: Select only needed columns; avoid SELECT *
  • Limit Results: Always paginate large result sets; default limit 50
  • Avoid N+1: Use joins or batch queries instead of multiple round trips
  • Analyze Queries: Use EXPLAIN ANALYZE to identify slow queries

Connection Pooling:

  • Supabase provides connection pooling (PgBouncer)
  • Application uses single Supabase client instance
  • Connections automatically managed and reused

3. Caching Strategy:

Multi-Level Caching:

┌─────────────┐
│   Browser   │  (60s stale-while-revalidate for static assets)
└──────┬──────┘

┌──────▼──────┐
│ Cloudflare  │  (CDN cache for HTML/JS/CSS/images)
│     CDN     │  (Edge caching rules)
└──────┬──────┘

┌──────▼──────┐
│  Supabase   │  (Database query cache, prepared statements)
│  Database   │
└─────────────┘

Client-Side Caching:

  • React Query: Cache API responses with stale-time and cache-time configurations
  • LocalStorage: Cache user preferences, session data (max 10MB)
  • Service Worker: Cache static assets for offline support (future enhancement)

CDN Caching:

  • Static Assets: Cache-Control headers for long-term caching (1 year for fingerprinted assets)
  • HTML: Short cache (5 minutes) with stale-while-revalidate
  • API Responses: No caching (dynamic, user-specific)

4. Load Handling Strategies:

Request Rate Limiting:

  • Supabase built-in rate limiting (per IP)
  • Client-side request throttling for expensive operations
  • Exponential backoff for failed requests

Background Processing:

  • Heavy Operations: PDF generation, large CSV imports in background
  • Status Polling: Client polls for completion rather than blocking
  • Queue System: Future enhancement for job queue (Supabase pg_cron or external service)

Graceful Degradation:

  • Reduce chart complexity if rendering takes too long
  • Paginate or sample data for very large datasets
  • Show simplified view if full dashboard takes >3s

5. Performance Monitoring:

Monitoring Tools:

  • Supabase Dashboard: Query performance, connection pool stats, database size
  • Vercel Analytics: Core Web Vitals, real user metrics (if Cloudflare doesn't provide)
  • Application Logs: Performance timing logs for key operations
  • Error Tracking: Sentry or similar for error monitoring and performance issues

Performance Budget:

  • Initial Bundle Size: < 300KB gzipped
  • Route Chunks: < 100KB gzipped per route
  • Total JavaScript: < 1MB gzipped
  • Images: < 500KB per page
  • Database Queries: < 10 queries per page load

Alerting Thresholds:

  • Page load time > 5s for 3+ consecutive measurements
  • API error rate > 5% over 5-minute window
  • Database connection pool > 80% utilization
  • PDF generation failure rate > 10%

Performance Testing:

  • Lighthouse CI: Run Lighthouse in CI pipeline; fail build if scores drop below thresholds
  • Load Testing: Playwright or k6 for simulating concurrent users
  • Database Load Testing: pg_bench for database stress testing
  • Real User Monitoring: Track Core Web Vitals from production users

This performance engineering approach ensures the system meets user expectations while remaining maintainable and cost-effective at scale.


4. System Architecture and Architecture Design

4.1 Logical View

The system follows a three-tier architecture pattern with clear separation of concerns between presentation, application logic, and data layers. The logical view abstracts away physical deployment details to focus on component responsibilities and interactions.

Logical Architecture Layers:

┌─────────────────────────────────────────────────────────────────────┐
│                      PRESENTATION LAYER                              │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │                    React 19 Application                     │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │    │
│  │  │  Upload  │  │Dashboard │  │ Reports  │  │   Dust    │  │    │
│  │  │  Module  │  │  Module  │  │  Module  │  │  Levels   │  │    │
│  │  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │    │
│  │  │  Weekly  │  │   Flow   │  │ Climate  │  │  Mine     │  │    │
│  │  │ Reports  │  │  Meter   │  │  Module  │  │  Sites    │  │    │
│  │  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │    │
│  └────────────────────────────────────────────────────────────┘    │
│                           │                                          │
│                  (Props, Events, State)                              │
│                           │                                          │
│  ┌────────────────────────┴───────────────────────────────────┐    │
│  │              UI Component Library (shadcn/ui)               │    │
│  │          (Button, Card, Form, Modal, Table, etc.)           │    │
│  └──────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘

                          (HTTP/REST, WebSocket)

┌─────────────────────────────────┼───────────────────────────────────┐
│                    APPLICATION LOGIC LAYER                           │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │                  Business Logic Services                    │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │    │
│  │  │   CSV    │  │  Report  │  │   PDF    │  │   Chart   │  │    │
│  │  │  Parser  │  │  Data    │  │Generator │  │   Data    │  │    │
│  │  │ Service  │  │ Service  │  │ Service  │  │Transformer│  │    │
│  │  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │    │
│  │  │ Upload   │  │ Weather  │  │Flow Meter│  │  Scraper  │  │    │
│  │  │ Service  │  │ Service  │  │ Service  │  │  Service  │  │    │
│  │  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │    │
│  └────────────────────────────────────────────────────────────┘    │
│                           │                                          │
│  ┌────────────────────────┴───────────────────────────────────┐    │
│  │              Supabase Client SDK (API Gateway)              │    │
│  │       (REST API, RPC Functions, Realtime Subscriptions)     │    │
│  └──────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘

                          (PostgreSQL Protocol)

┌─────────────────────────────────┼───────────────────────────────────┐
│                         DATA LAYER                                   │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │                  PostgreSQL Database                        │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │    │
│  │  │  users/  │  │  mine_   │  │ uploads/ │  │ measure-  │  │    │
│  │  │ profiles │  │  sites   │  │csv_files │  │  ments    │  │    │
│  │  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │    │
│  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌───────────┐  │    │
│  │  │ reports  │  │ weather_ │  │flow_meter│  │  weekly_  │  │    │
│  │  │          │  │   data   │  │   data   │  │  reports  │  │    │
│  │  └──────────┘  └──────────┘  └──────────┘  └───────────┘  │    │
│  │                                                              │    │
│  │  ┌──────────────────────────────────────────────────────┐  │    │
│  │  │       Row-Level Security Policies (RLS)              │  │    │
│  │  │  • User isolation  • Role-based access  • Auditing   │  │    │
│  │  └──────────────────────────────────────────────────────┘  │    │
│  └────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │                   Supabase Storage                          │    │
│  │  • CSV Files Bucket  • PDF Reports Bucket                  │    │
│  └────────────────────────────────────────────────────────────┘    │
│                                                                      │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │               Supabase Auth (User Management)               │    │
│  │  • JWT Tokens  • Email Verification  • Session Management  │    │
│  └────────────────────────────────────────────────────────────┘    │
└─────────────────────────────────────────────────────────────────────┘

Component Responsibilities:

LayerComponentResponsibility
PresentationFeature ModulesRender UI, handle user interactions, manage local UI state
UI ComponentsReusable presentational components with no business logic
ApplicationServicesEncapsulate business logic, data transformations, API calls
Supabase SDKAbstract data access, handle authentication, manage connections
DataPostgreSQLPersistent data storage, enforce data integrity, execute queries
RLS PoliciesEnforce row-level security, isolate user data
Supabase StorageStore binary files (CSV, PDF), serve via signed URLs
Supabase AuthUser authentication, JWT token management, session handling

Cross-Cutting Concerns:

  • Logging: Application-level logging in frontend, database triggers for audit logs
  • Error Handling: Centralized error boundaries (React), consistent error responses (Supabase)
  • Configuration: Environment variables (Vite), feature flags (future)
  • Monitoring: Performance metrics, error tracking, usage analytics

4.2 Hardware Architecture

The system leverages cloud infrastructure with no on-premise hardware. All components run on managed services provided by Supabase (Backend) and Cloudflare (Frontend CDN).

Infrastructure Provider Architecture:

┌───────────────────────────────────────────────────────────────────┐
│                      CLOUDFLARE GLOBAL NETWORK                     │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │              Cloudflare Pages (Static Hosting)              │  │
│  │  • 300+ Edge Locations  • Automatic HTTPS  • DDoS Protection│  │
│  └────────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │                    Cloudflare CDN                           │  │
│  │  • Asset Caching  • Brotli Compression  • Image Optimization│  │
│  └────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────┘

                                │ (HTTPS)

┌───────────────────────────────┼───────────────────────────────────┐
│                       SUPABASE CLOUD                               │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │              Supabase Managed PostgreSQL                    │  │
│  │  Instance: AWS RDS (us-east-1)                              │  │
│  │  Specs: 2 vCPU, 8GB RAM, 50GB SSD (Pro Tier)               │  │
│  │  Features: Automated backups, Point-in-time recovery        │  │
│  └────────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │              Supabase Storage (S3-Compatible)               │  │
│  │  Provider: AWS S3 (us-east-1)                               │  │
│  │  Capacity: 100GB (Pro Tier)                                 │  │
│  │  CDN: Integrated CloudFront distribution                    │  │
│  └────────────────────────────────────────────────────────────┘  │
│  ┌────────────────────────────────────────────────────────────┐  │
│  │              Supabase Edge Functions                        │  │
│  │  Runtime: Deno v1.40+ on Fly.io infrastructure             │  │
│  │  Regions: Multi-region deployment (automatic)               │  │
│  │  Limits: 60s timeout, 512MB memory per invocation          │  │
│  └────────────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────────────┘

Hardware Specifications:

ComponentSpecificationJustification
Database Server2 vCPU, 8GB RAM, 50GB SSD (AWS RDS)Sufficient for 50 concurrent users, 10GB data storage
Connection Pool60 max connections (PgBouncer)Handles concurrent queries from 50 users
Storage Capacity100GB object storage (AWS S3)Accommodates 20,000 CSV files + PDFs (avg 2MB each)
CDN Edge Nodes300+ locations (Cloudflare)<100ms latency for global users
Edge Function Compute512MB RAM, 60s timeout (Fly.io)Sufficient for AI API calls, scraper triggers

4.2.1 Security Hardware Architecture

Network Security Components:

  1. DDoS Protection: Cloudflare's built-in DDoS mitigation at network edge (automatic)
  2. Web Application Firewall (WAF): Cloudflare WAF rules (managed ruleset)
  3. TLS Termination: Cloudflare edge terminates TLS 1.3, re-encrypts to origin
  4. Database Network Isolation: Supabase PostgreSQL accessible only via authenticated connections
  5. VPC Configuration: Supabase runs in isolated VPC (AWS), no public database access

Security Infrastructure:

┌───────────────────────────────────────────────────────────────┐
│                      Internet (Users)                          │
└──────────────────────────┬────────────────────────────────────┘

                  ┌────────▼────────┐
                  │  Cloudflare WAF │  <- DDoS Protection, Bot Detection
                  └────────┬────────┘
                           │ (TLS 1.3)
                  ┌────────▼────────┐
                  │  Cloudflare CDN │  <- HTTPS Enforcement, HSTS
                  └────────┬────────┘

              ┌────────────┼────────────┐
              │            │            │
      ┌───────▼──────┐     │    ┌──────▼─────────┐
      │ Static Assets│     │    │  API Requests  │
      │  (Cached)    │     │    │ (Pass-through) │
      └──────────────┘     │    └──────┬─────────┘
                           │           │
                           │  ┌────────▼─────────┐
                           │  │  Supabase Auth   │ <- JWT Verification
                           │  └────────┬─────────┘
                           │           │
                           │  ┌────────▼─────────┐
                           │  │   RLS Policies   │ <- Row-level filtering
                           │  └────────┬─────────┘
                           │           │
                           │  ┌────────▼─────────┐
                           └──│  PostgreSQL DB   │
                              └──────────────────┘
                                 (VPC Isolated)

SSL/TLS Configuration:

  • Protocol: TLS 1.3 (minimum TLS 1.2)
  • Cipher Suites: Modern ciphers only (ECDHE-RSA-AES256-GCM-SHA384, etc.)
  • Certificate: Cloudflare-managed SSL certificate (auto-renewal)
  • HSTS: Strict-Transport-Security header with 1-year max-age

4.2.2 Performance Hardware Architecture

High Availability Configuration:

  • Database: Supabase managed PostgreSQL with automated failover (Pro tier)
  • Storage: AWS S3 with 99.99% availability SLA, multi-AZ replication
  • CDN: Cloudflare global anycast network (automatic failover to healthy nodes)
  • Edge Functions: Multi-region deployment on Fly.io (automatic routing)

Load Balancing:

  • Frontend: Cloudflare anycast IP routes users to nearest edge location
  • Database: PgBouncer connection pooling distributes connections
  • API: Supabase REST API auto-scales within infrastructure limits

Scaling Characteristics:

ComponentScaling MethodLimitsUpgrade Path
FrontendAutomatic (CDN)No practical limitN/A (inherently scalable)
DatabaseVertical (upgrade instance)64 vCPU, 256GB RAMAdd read replicas (Enterprise)
StorageAutomatic10TB (practical)Unlimited with higher tier
Edge FunctionsHorizontal (auto-scale)Concurrent invocationsNo limit (pay-per-use)

Resource Allocation:

Production Load Profile (50 concurrent users):
┌─────────────────────────────────────────────────────────┐
│ Database                                                 │
│ ├─ CPU Usage:      30-40% average, 70% peak            │
│ ├─ Memory:         4GB / 8GB (50%)                      │
│ ├─ Connections:    15-25 / 60 (40%)                     │
│ └─ Storage:        12GB / 50GB (24%)                    │
├─────────────────────────────────────────────────────────┤
│ Object Storage                                           │
│ ├─ Used:           25GB / 100GB (25%)                   │
│ ├─ Request Rate:   ~500 req/min (CSV downloads, PDFs)  │
│ └─ Bandwidth:      ~2GB/day egress                      │
├─────────────────────────────────────────────────────────┤
│ Edge Functions                                           │
│ ├─ Invocations:    ~50/hour (AI descriptions, scrapers) │
│ ├─ Duration:       2-5s average                         │
│ └─ Concurrency:    1-3 concurrent executions            │
└─────────────────────────────────────────────────────────┘

4.3 Software Architecture

The software architecture follows a modular, feature-driven design with clear boundaries between features and shared infrastructure.

Software Stack Overview:

┌───────────────────────────────────────────────────────────────┐
│                     FRONTEND SOFTWARE STACK                    │
├───────────────────────────────────────────────────────────────┤
│ Runtime Environment                                            │
│ └─ Browser (Chrome 90+, Firefox 88+, Safari 14+, Edge 90+)   │
├───────────────────────────────────────────────────────────────┤
│ UI Framework & Libraries                                       │
│ ├─ React 19.2.0          (UI rendering, component model)      │
│ ├─ React Router v7.9.1   (Client-side routing)                │
│ ├─ Tailwind CSS 4.1.16   (Utility-first styling)              │
│ └─ shadcn/ui             (Accessible component library)        │
├───────────────────────────────────────────────────────────────┤
│ State Management                                               │
│ ├─ Zustand 5.0.8         (Global state)                        │
│ ├─ React Hook Form 7.66  (Form state)                          │
│ └─ usehooks-ts 3.1.1     (Common hooks)                        │
├───────────────────────────────────────────────────────────────┤
│ Data Visualization                                             │
│ ├─ Recharts 3.3.0        (Charts for reports)                  │
│ ├─ Nivo 0.99.0           (Dashboard analytics charts)          │
│ └─ ApexCharts 1.7.0      (Additional chart types)              │
├───────────────────────────────────────────────────────────────┤
│ Document Generation                                            │
│ ├─ jsPDF 3.0.3           (PDF generation)                      │
│ ├─ html2canvas 1.4.1     (DOM to canvas conversion)            │
│ └─ docx 9.5.1            (Word document generation)            │
├───────────────────────────────────────────────────────────────┤
│ Data Processing                                                │
│ ├─ PapaParse 5.5.3       (CSV parsing)                         │
│ ├─ JSZip 3.10.1          (ZIP file handling)                   │
│ ├─ date-fns 4.1.0        (Date manipulation)                   │
│ └─ Zod 4.1.12            (Runtime validation)                  │
├───────────────────────────────────────────────────────────────┤
│ HTTP & API Client                                              │
│ ├─ @supabase/supabase-js 2.80.0  (Supabase SDK)               │
│ └─ Native Fetch API                  (HTTP requests)           │
├───────────────────────────────────────────────────────────────┤
│ Development Tools                                              │
│ ├─ TypeScript 5.9.3      (Type system)                         │
│ ├─ Vite 7.1.12           (Build tool, dev server)              │
│ ├─ ESLint 9.39.1         (Linting)                             │
│ └─ Prettier 3.6.2        (Code formatting)                     │
└───────────────────────────────────────────────────────────────┘

┌───────────────────────────────────────────────────────────────┐
│                     BACKEND SOFTWARE STACK                     │
├───────────────────────────────────────────────────────────────┤
│ Database                                                       │
│ ├─ PostgreSQL 15.x       (Primary database)                    │
│ ├─ PgBouncer             (Connection pooling)                  │
│ └─ PostgREST             (Auto-generated REST API)             │
├───────────────────────────────────────────────────────────────┤
│ Authentication                                                 │
│ └─ Supabase Auth         (JWT-based authentication)            │
├───────────────────────────────────────────────────────────────┤
│ Object Storage                                                 │
│ └─ Supabase Storage      (S3-compatible API)                   │
├───────────────────────────────────────────────────────────────┤
│ Serverless Functions                                           │
│ ├─ Deno Runtime v1.40+   (Edge function runtime)               │
│ └─ TypeScript            (Function implementation language)    │
├───────────────────────────────────────────────────────────────┤
│ External Integrations                                          │
│ ├─ Google Gemini API     (AI chart descriptions)               │
│ ├─ Dustac Scraper API    (Dust level data collection)          │
│ └─ BOM Weather API       (Weather data fetching)               │
└───────────────────────────────────────────────────────────────┘

Software Component Diagram:

Frontend Components:
┌─────────────────────────────────────────────────────────────┐
│                   React Application                          │
│  ┌────────────────────────────────────────────────────┐     │
│  │ Feature Modules (src/features/)                    │     │
│  │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │     │
│  │ │  upload  │ │ reports  │ │  dust-   │ │weekly- │ │     │
│  │ │          │ │          │ │ levels   │ │reports │ │     │
│  │ │ ├─comp.  │ │ ├─comp.  │ │ ├─comp.  │ │├─comp. │ │     │
│  │ │ ├─serv.  │ │ ├─serv.  │ │ ├─serv.  │ │├─serv. │ │     │
│  │ │ ├─hooks  │ │ ├─hooks  │ │ ├─hooks  │ │├─hooks │ │     │
│  │ │ └─types  │ │ └─types  │ │ └─types  │ │└─types │ │     │
│  │ └──────────┘ └──────────┘ └──────────┘ └────────┘ │     │
│  │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌────────┐ │     │
│  │ │flow-meter│ │ climate  │ │ report-  │ │ mine-  │ │     │
│  │ │          │ │          │ │templates │ │ sites  │ │     │
│  │ └──────────┘ └──────────┘ └──────────┘ └────────┘ │     │
│  └────────────────────────────────────────────────────┘     │
│  ┌────────────────────────────────────────────────────┐     │
│  │ Shared Infrastructure (src/lib/, src/components/)  │     │
│  │ ├─ supabase.ts      (Supabase client singleton)   │     │
│  │ ├─ supabaseTypes.ts (Generated DB types)          │     │
│  │ ├─ utils.ts         (Common utilities)            │     │
│  │ └─ components/ui/   (shadcn/ui components)        │     │
│  └────────────────────────────────────────────────────┘     │
│  ┌────────────────────────────────────────────────────┐     │
│  │ Routing (src/routes/)                              │     │
│  │ ├─ Routes.tsx       (Route definitions)           │     │
│  │ └─ _layout/         (Layout components)           │     │
│  └────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────┘

Backend Components:
┌─────────────────────────────────────────────────────────────┐
│                   Supabase Platform                          │
│  ┌────────────────────────────────────────────────────┐     │
│  │ PostgreSQL Database                                │     │
│  │ ├─ Tables (profiles, uploads, measurements, etc.) │     │
│  │ ├─ RLS Policies (user isolation, role-based)      │     │
│  │ ├─ Functions (log_report_read, etc.)              │     │
│  │ ├─ Triggers (auto-logging, updated_at)            │     │
│  │ └─ Indexes (performance optimization)             │     │
│  └────────────────────────────────────────────────────┘     │
│  ┌────────────────────────────────────────────────────┐     │
│  │ Edge Functions (supabase/functions/)               │     │
│  │ ├─ generate-chart-descriptions (Gemini AI)        │     │
│  │ ├─ trigger-dust-level-scraper (Dustac API)        │     │
│  │ └─ trigger-flow-meter-scraper (Flow Meter API)    │     │
│  └────────────────────────────────────────────────────┘     │
│  ┌────────────────────────────────────────────────────┐     │
│  │ Supabase Auth                                      │     │
│  │ ├─ User management (signup, login, reset)         │     │
│  │ ├─ JWT token generation/verification              │     │
│  │ └─ Email verification                              │     │
│  └────────────────────────────────────────────────────┘     │
│  ┌────────────────────────────────────────────────────┐     │
│  │ Supabase Storage (Buckets)                         │     │
│  │ ├─ csv-uploads (CSV files)                         │     │
│  │ ├─ pdf-reports (Generated PDF reports)            │     │
│  │ └─ Storage policies (user-level access)           │     │
│  └────────────────────────────────────────────────────┘     │
└─────────────────────────────────────────────────────────────┘

Component Interaction Patterns:

  1. Data Flow (Read):

    User Action → React Component → Service Layer → Supabase Client SDK
    → REST API → RLS Policy Check → PostgreSQL Query → Response
  2. Data Flow (Write):

    User Input → Form Validation (Zod) → Service Layer → Supabase Client SDK
    → REST API → RLS Policy Check → PostgreSQL INSERT/UPDATE → Trigger → Response
  3. File Upload Flow:

    File Selection → Upload Service → Supabase Storage API
    → S3 Upload → Metadata to PostgreSQL → Status Update
  4. External API Flow:

    User Action → Frontend → Edge Function Invocation → External API Call
    → Response Processing → Database Update → Frontend Notification

4.3.1 Security Software Architecture

Authentication Flow:

┌──────────┐                                    ┌─────────────┐
│  User    │                                    │  Supabase   │
│ Browser  │                                    │    Auth     │
└────┬─────┘                                    └──────┬──────┘
     │                                                 │
     │ 1. POST /auth/signup                           │
     │    { email, password, full_name }              │
     ├───────────────────────────────────────────────>│
     │                                                 │
     │                              2. Validate input │
     │                              3. Hash password  │
     │                              4. Create user    │
     │                              5. Send verify email
     │                                                 │
     │ 6. { user, session: null }                     │
     │<───────────────────────────────────────────────┤
     │                                                 │
     │ 7. Click verification link in email            │
     │    GET /auth/confirm?token=...                 │
     ├───────────────────────────────────────────────>│
     │                                                 │
     │                              8. Verify token   │
     │                              9. Mark confirmed │
     │                                                 │
     │ 10. Redirect to /login                         │
     │<───────────────────────────────────────────────┤
     │                                                 │
     │ 11. POST /auth/signin                          │
     │     { email, password }                        │
     ├───────────────────────────────────────────────>│
     │                                                 │
     │                              12. Verify password
     │                              13. Generate JWT │
     │                              14. Create session
     │                                                 │
     │ 15. { user, session: { access_token, refresh_token }}
     │<───────────────────────────────────────────────┤
     │                                                 │
     │ 16. Store tokens in localStorage               │
     │                                                 │
     │ 17. Subsequent requests include:               │
     │     Authorization: Bearer <access_token>       │
     ├───────────────────────────────────────────────>│
     │                                                 │
     │                              18. Verify JWT    │
     │                              19. Extract user_id
     │                              20. Apply RLS     │

Authorization Model (Row-Level Security):

sql
-- Example RLS Policy for measurements table
CREATE POLICY "Users can view own measurements"
  ON measurements FOR SELECT
  USING (
    EXISTS (
      SELECT 1 FROM uploads
      WHERE uploads.id = measurements.upload_id
      AND uploads.user_id = auth.uid()
    )
  );

-- Admin override policy
CREATE POLICY "Admins can view all measurements"
  ON measurements FOR SELECT
  USING (
    EXISTS (
      SELECT 1 FROM profiles
      WHERE id = auth.uid() AND role = 'admin'
    )
  );

Security Software Components:

ComponentTechnologyFunction
JWT Token GenerationSupabase AuthCreates signed JWT with user_id, role, expiration
JWT Token VerificationSupabase PostgRESTVerifies token signature, checks expiration
Password Hashingbcrypt (Supabase Auth)One-way hash with salt, cost factor 10
Session ManagementSupabase AuthTracks active sessions, handles refresh tokens
RLS Policy EnginePostgreSQLFilters query results based on auth.uid()
Email VerificationSupabase AuthGenerates verification tokens, sends emails
CSRF ProtectionSameSite cookiesPrevents cross-site request forgery
XSS PreventionReact (automatic escaping)Prevents script injection in rendered content
SQL Injection PreventionParameterized queriesAll queries use bound parameters

Encryption:

  • Data in Transit: TLS 1.3 for all HTTPS connections
  • Data at Rest: AES-256 encryption for database and storage (provider-managed)
  • JWT Signing: HS256 algorithm with secret key
  • Password Storage: bcrypt with salt (managed by Supabase Auth)

4.3.2 Performance Software Architecture

Caching Layers:

Request Flow with Caching:

User Request


┌─────────────────┐
│ Browser Cache   │  <-- Cache-Control: max-age=31536000 (static assets)
└────────┬────────┘      ETag validation for HTML
         │ (Cache Miss)

┌─────────────────┐
│ Cloudflare CDN  │  <-- Edge cache with stale-while-revalidate
└────────┬────────┘      Brotli compression
         │ (Cache Miss)

┌─────────────────┐
│ Supabase API    │  <-- No response caching (dynamic, user-specific)
└────────┬────────┘      Connection pooling (PgBouncer)


┌─────────────────┐
│ PostgreSQL      │  <-- Query result caching (internal)
└─────────────────┘      Prepared statement cache

Performance Optimization Techniques:

TechniqueImplementationImpact
Code SplittingVite automatic chunking, React.lazy()Reduces initial bundle from 1.2MB to 300KB
Tree ShakingVite production buildRemoves unused code, saves ~40% bundle size
Image OptimizationWebP format, lazy loading, srcsetReduces image payload by 60-80%
Database IndexingComposite indexes on common queriesReduces query time from 500ms to <50ms
Connection PoolingPgBouncer with 60 connectionsEliminates connection overhead (~100ms)
Debouncingusehooks-ts debounce (300ms)Reduces API calls by 80% for search/filter
PaginationLimit 50 items per pageReduces query time and data transfer
Lazy LoadingReact.lazy() for route componentsDefers loading until route accessed

Bundle Optimization:

typescript
// vite.config.ts - Build optimization
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          'vendor-react': ['react', 'react-dom', 'react-router-dom'],
          'vendor-supabase': ['@supabase/supabase-js'],
          'vendor-charts': ['recharts', '@nivo/core', '@nivo/bar'],
          'vendor-ui': ['@radix-ui/react-dialog', '@radix-ui/react-dropdown-menu'],
        },
      },
    },
    minify: 'terser',
    terserOptions: {
      compress: {
        drop_console: true, // Remove console.log in production
      },
    },
  },
});

Async Processing:

  • PDF Generation: Client-side rendering with loading indicator, no blocking
  • CSV Upload: Chunked processing with progress updates
  • External API Calls: Async/await with timeout handling
  • Database Writes: Batch inserts for multiple records

4.4 Information Architecture

The information architecture defines how data is organized, categorized, and accessed within the system.

Data Classification:

Data CategoryExamplesSensitivityRetention
User Identity DataEmail, full_name, password hashHighAccount lifetime + 30 days
Environmental MeasurementsPM10, PM2.5, temperature, humidityMedium7 years (regulatory)
Operational DataCSV files, upload metadataMedium2 years
Generated ReportsPDF reportsLowPermanent (user managed)
Audit LogsActivity logs, access logsHigh3 years (compliance)
System ConfigurationUser preferences, templatesLowAccount lifetime
Weather DataBOM weather observationsLow1 year
Flow Meter DataWater usage measurementsMedium2 years

4.4.1 Records Management

Data Lifecycle Management:

┌──────────────────────────────────────────────────────────────┐
│                   Data Lifecycle Stages                       │
└──────────────────────────────────────────────────────────────┘

1. CREATION
   ├─ CSV Upload: User uploads file → Supabase Storage → Metadata in DB
   ├─ Measurements: Parsed from CSV → Batch insert to PostgreSQL
   ├─ Reports: Generated from measurements → PDF to Storage → Metadata in DB
   └─ Audit Logs: Triggered automatically on CRUD operations

2. ACTIVE USE (0-12 months)
   ├─ Full access via dashboard and reports
   ├─ Indexed for fast queries
   ├─ Cached at multiple levels
   └─ Available for export

3. ARCHIVAL (12+ months)
   ├─ Data remains in database (searchable but less frequently accessed)
   ├─ CSV files retained in storage (user can download)
   ├─ PDF reports retained permanently
   └─ Query performance may degrade for very old data

4. DISPOSAL
   ├─ User-initiated deletion: Soft delete (mark as deleted, retain 30 days)
   ├─ Account deletion: User data deleted after 30-day grace period
   ├─ Regulatory retention met: Audit logs deleted after 3 years
   └─ Automatic cleanup: Temporary files deleted after 24 hours

Retention Policies:

Data TypeRetention PeriodJustificationDisposal Method
Measurements7 yearsEnvironmental compliance (typical regulatory requirement)Hard delete from database
CSV Files2 yearsOperational need for re-processingDelete from Storage bucket
PDF ReportsPermanent (user-controlled)User maintains for recordsUser-initiated deletion
Audit Logs3 yearsSecurity/compliance requirementsHard delete from database
User Accounts (inactive)2 yearsReasonable retention for inactive usersFull account deletion
Deleted Data (soft delete)30 daysAllow recovery from accidental deletionHard delete after grace period

Archival Procedures:

  1. Automated Archival (Future Enhancement):

    • PostgreSQL partitioning by year for measurements table
    • Older partitions moved to cheaper storage tier (AWS Glacier)
    • Metadata remains in main database for searchability
  2. User-Initiated Export:

    • Users can export all data as ZIP file (CSV + PDFs)
    • Export includes data dictionary and README
    • Export functionality available via dashboard
  3. Backup and Recovery:

    • Supabase automatic daily backups (retained 7 days)
    • Point-in-time recovery available (Pro tier)
    • User can request manual backup before major changes

4.4.2 Data

All data entering the system is electronic; no paper-based inputs.

Data TypeFormatSourceDescriptionVolume
Environmental MeasurementsCSV (UTF-8)DustRanger devices (via user upload)PM measurements, temperature, humidity, location, timestamp~10K rows/file, 50MB max
User RegistrationJSON (Web form)User manual entry via browserEmail, full name, password1 record per user
PDF ReportsBinary (PDF/A)System-generated (jsPDF)Formatted report with charts and statistics1-5MB per report
Weather DataJSON (API response)BOM API (automated fetch)Temperature, rainfall, wind, pressure~30 records/day/station
Dust Level DataJSON (API response)Dustac Scraper API (automated)Real-time dust measurements from external system~100 records/hour
Flow Meter DataJSON (API response)Flow Meter Scraper API (automated)Water usage measurements~50 records/day
Weekly ReportsJSON (Web form)User manual entry via dashboardStructured weekly field report (8 sections)1 report per user per week
Report TemplatesMarkdown (text)User manual entry via editorCustomizable report template<10KB per template
Mine Site InformationJSON (Web form)User manual entrySite name, location, device list<1KB per site

Data Validation at Entry:

  • CSV Files: Column validation, data type checking, date format parsing, range validation
  • User Input: Zod schema validation, React Hook Form validation, backend validation
  • API Responses: JSON schema validation, error handling for malformed data

4.4.3 Manual/Electronic Inputs

Input Processing Flow:

  1. CSV File Upload:

    User selects CSV → Client validation → Upload to Storage →
    Parse CSV → Validate measurements → Batch insert to DB →
    Create upload record → Generate PDF → Update status
  2. Web Form Input (e.g., Weekly Report):

    User enters data → React Hook Form validation →
    Zod schema validation → Submit to Supabase →
    RLS policy check → Insert/Update in DB →
    Trigger updated_at → Return success
  3. External API Data (e.g., Weather):

    Edge Function invoked → Call BOM API →
    Parse JSON response → Transform to DB schema →
    Insert with user_id → Return status

Post-Entry Processing:

  • CSV Measurements: After import, indexed for queries, available for dashboard, trigger PDF generation
  • Weekly Reports: Auto-saved as draft, marked complete when user submits, available for viewing
  • Weather Data: Stored with station_id + date, available for climate charts
  • User Edits: Trigger updated_at timestamp, create audit log entry

4.4.4 Master Files

Primary Data Tables (Master Files):

TableDescriptionKey DataUpdate Frequency
profilesUser profile informationfull_name, email, roleRarely (user edits profile)
mine_sitesMine site locationssite_name, location, statusOccasionally (add/edit sites)
devicesMonitoring devicesdevice_id, device_name, site_idOccasionally (add/edit devices)
uploadsCSV upload trackingupload_type, period, status, file countFrequently (daily/weekly uploads)
csv_filesIndividual CSV filesfilename, site_name, period, record_countFrequently (one per CSV upload)
measurementsEnvironmental readingsPM10, PM2.5, temperature, humidity, timeVery frequently (10K+ rows per upload)
reportsGenerated PDF reportspdf_storage_path, generation_time, statusFrequently (one per CSV file)
weekly_reportsStructured weekly reports8 JSONB sections, status, period_endWeekly (one per user per week)
report_templatesCustom report templatesname, content (markdown), is_defaultRarely (user creates/edits)
weather_dataWeather observationstemperature, rainfall, wind, station_idDaily (automated import)
flow_meter_dataWater usage datavolume, timestamp, site_nameDaily (automated import)
activity_logsAudit trailuser_id, action, resource_type, detailsContinuously (all CRUD operations)

Data Relationships:

profiles (1) ──< (many) uploads
uploads (1) ──< (many) csv_files
csv_files (1) ──< (many) measurements
csv_files (1) ──< (1) reports
mine_sites (1) ──< (many) devices
profiles (1) ──< (many) weekly_reports
profiles (1) ──< (many) report_templates
weather_stations (1) ──< (many) weather_data
profiles (1) ──< (many) flow_meter_data
profiles (1) ──< (many) activity_logs

4.5 Internal Communications Architecture

Communication Protocols:

Communication TypeProtocolUse Case
Frontend ↔ Supabase APIHTTPS/RESTAll data queries, mutations
Frontend ↔ Supabase AuthHTTPS/RESTAuthentication, session management
Frontend ↔ Supabase StorageHTTPS/RESTFile uploads, downloads
Frontend ↔ Edge FunctionsHTTPS/RESTAI generation, scraper triggers
Edge Functions ↔ External APIsHTTPS/RESTGoogle Gemini, Dustac, BOM
Supabase API ↔ PostgreSQLPostgreSQL Wire ProtocolDatabase queries
Frontend ↔ Supabase RealtimeWebSocketLive updates (future)

API Communication Patterns:

typescript
// Example: Data Query Pattern
const { data, error } = await supabase
  .from('measurements')
  .select('pm10, pm25, temperature, time')
  .eq('site', 'Boddington')
  .gte('time', '2025-01-01')
  .lte('time', '2025-01-31')
  .order('time', { ascending: true })
  .limit(1000);

// Example: File Upload Pattern
const { data, error } = await supabase.storage
  .from('csv-uploads')
  .upload(`${userId}/daily/2025-01-15_${uploadId}.csv`, file, {
    contentType: 'text/csv',
    upsert: false,
  });

// Example: Edge Function Invocation
const { data, error } = await supabase.functions.invoke(
  'generate-chart-descriptions',
  {
    body: { chartType: 'dustLevelsTemp', chartData, siteName, dateRange },
  }
);

Message Formats:

  1. REST API Requests:

    • Content-Type: application/json
    • Authorization: Bearer
    • Body: JSON-encoded payload
  2. REST API Responses:

    • Content-Type: application/json
    • Status: 200 (success), 400 (validation error), 401 (unauthorized), 500 (server error)
    • Body: JSON-encoded response or error
  3. File Upload:

    • Content-Type: multipart/form-data (browser upload)
    • Content-Type: text/csv, application/pdf (storage upload)
  4. WebSocket (Realtime):

    • Protocol: WebSocket over TLS
    • Message Format: JSON with event type and payload

Network Topology:

┌──────────────┐
│ User Browser │
└──────┬───────┘
       │ (HTTPS)

   ┌───▼────────────────────┐
   │ Cloudflare CDN (Edge)  │
   └───┬────────────────────┘

       ├─── (Static Assets) ─── Cached at Edge

       ├─── (API Requests) ────┐
       │                        │
       │                   ┌────▼──────────┐
       │                   │ Supabase API  │
       │                   │  (us-east-1)  │
       │                   └────┬──────────┘
       │                        │
       │                   ┌────▼──────────┐
       │                   │  PostgreSQL   │
       │                   │   Database    │
       │                   └───────────────┘

       └─── (Edge Functions) ──┐

                           ┌────▼────────────┐
                           │ Deno Runtime    │
                           │   (Fly.io)      │
                           └────┬────────────┘

                      ┌─────────┼─────────┐
                      │         │         │
                  ┌───▼───┐ ┌──▼───┐ ┌──▼────┐
                  │Gemini │ │Dustac│ │  BOM  │
                  │  API  │ │ API  │ │  API  │
                  └───────┘ └──────┘ └───────┘

4.6 Security Architecture

Defense-in-Depth Strategy:

Layer 1: Network Security
├─ Cloudflare WAF (blocks malicious requests)
├─ DDoS protection (rate limiting, bot detection)
└─ TLS 1.3 encryption (transport security)

Layer 2: Application Security
├─ JWT authentication (validates user identity)
├─ CORS policy (prevents unauthorized origins)
├─ Input validation (prevents injection attacks)
└─ CSRF protection (SameSite cookies)

Layer 3: Data Security
├─ Row-Level Security (isolates user data)
├─ Parameterized queries (prevents SQL injection)
├─ Data encryption at rest (AES-256)
└─ Signed URLs (time-limited file access)

Layer 4: Audit & Monitoring
├─ Activity logs (tracks all data access)
├─ Error logging (detects anomalies)
├─ Session monitoring (tracks active users)
└─ Security alerts (notifies on suspicious activity)

Security Controls Matrix:

ThreatControlImplementation
Unauthorized AccessAuthenticationSupabase Auth with JWT, email verification
Privilege EscalationAuthorizationRLS policies based on user role
Data LeakageData IsolationRLS policies filter by user_id
SQL InjectionInput SanitizationParameterized queries, ORM-style API
XSS AttacksOutput EncodingReact automatic escaping
CSRF AttacksCSRF TokensSameSite cookies, CORS policy
Brute Force LoginRate LimitingSupabase built-in rate limiting
Session HijackingSecure SessionsJWT with expiration, HttpOnly cookies
Man-in-the-MiddleEncryptionTLS 1.3, HSTS headers
Data TamperingAudit LoggingActivity logs with trigger-based logging

4.7 Performance

Performance Architecture Principles:

  1. Minimize Round Trips: Batch queries, use joins instead of N+1 queries
  2. Cache Aggressively: Multi-level caching (browser, CDN, database)
  3. Optimize Assets: Code splitting, image optimization, compression
  4. Index Strategically: Composite indexes on common query patterns
  5. Parallelize When Possible: Concurrent API calls, async processing

Performance Monitoring Dashboard:

Real-Time Performance Metrics:
┌─────────────────────────────────────────────────────────────┐
│ Frontend Metrics (User Experience)                          │
│ ├─ LCP (Largest Contentful Paint):     1.8s ✓ (<2.5s)     │
│ ├─ FID (First Input Delay):            45ms ✓ (<100ms)    │
│ ├─ CLS (Cumulative Layout Shift):      0.05 ✓ (<0.1)      │
│ └─ Bundle Size:                         285KB ✓ (<300KB)   │
├─────────────────────────────────────────────────────────────┤
│ Backend Metrics (API Performance)                           │
│ ├─ Database Query Time (p95):          120ms ✓ (<500ms)   │
│ ├─ API Response Time (p95):            180ms ✓ (<500ms)   │
│ ├─ Database Connection Pool:           18/60 ✓ (<80%)     │
│ └─ Storage Bandwidth:                   1.2GB/day          │
├─────────────────────────────────────────────────────────────┤
│ Business Metrics (User Operations)                          │
│ ├─ CSV Upload Processing:              12s ✓ (<30s)       │
│ ├─ PDF Generation:                      22s ✓ (<60s)       │
│ ├─ Dashboard Load:                      0.8s ✓ (<1s)       │
│ └─ Concurrent Users:                    32/50 ✓            │
└─────────────────────────────────────────────────────────────┘

Bottleneck Analysis:

Potential BottleneckSymptomsMitigation
Database ConnectionsConnection pool exhausted, timeoutsOptimize query count, use connection pooling, upgrade tier
Large CSV ProcessingUpload timeouts, UI freezingChunk processing, Web Workers, progress indicators
PDF GenerationLong wait times, memory errorsOptimize chart rendering, reduce image quality, paginate
Dashboard QueriesSlow load times for large date rangesAdd indexes, implement pagination, cache aggregations
Storage BandwidthSlow downloads for large filesUse CDN, compress files, implement progressive loading

4.8 System Architecture Diagram

Integrated System Architecture:

                                    USERS

                                      │ (HTTPS)

┌─────────────────────────────────────────────────────────────────┐
│                      CLOUDFLARE GLOBAL EDGE                      │
│  ┌────────────────────────────────────────────────────────────┐ │
│  │                    Cloudflare CDN                           │ │
│  │  • DDoS Protection  • WAF Rules  • TLS Termination          │ │
│  │  • Static Asset Caching  • Brotli Compression               │ │
│  └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────┬───────────────────────────────────┘

                 ┌────────────┴────────────┐
                 │                         │
                 │ (Static)                │ (API)
                 ▼                         ▼
┌────────────────────────────┐  ┌───────────────────────────────┐
│  REACT 19 SPA (Frontend)   │  │    SUPABASE PLATFORM          │
│  ┌──────────────────────┐  │  │  ┌─────────────────────────┐ │
│  │ Feature Modules:     │  │  │  │  PostgreSQL Database    │ │
│  │ • Upload             │  │  │  │  • Tables (15+)         │ │
│  │ • Reports            │◄─┼──┼──┤  • RLS Policies         │ │
│  │ • Dust Levels        │  │  │  │  • Triggers & Functions │ │
│  │ • Weekly Reports     │  │  │  │  • Indexes              │ │
│  │ • Flow Meter         │  │  │  └─────────────────────────┘ │
│  │ • Climate            │  │  │  ┌─────────────────────────┐ │
│  │ • Mine Sites         │  │  │  │  Supabase Auth          │ │
│  │ • Dashboard          │  │  │  │  • JWT Generation       │ │
│  └──────────────────────┘  │  │  │  • Email Verification   │ │
│  ┌──────────────────────┐  │  │  │  • Session Management   │ │
│  │ UI Component Library │  │  │  └─────────────────────────┘ │
│  │ • shadcn/ui (Radix)  │  │  │  ┌─────────────────────────┐ │
│  │ • Tailwind CSS       │  │  │  │  Supabase Storage       │ │
│  └──────────────────────┘  │  │  │  • csv-uploads bucket   │ │
│  ┌──────────────────────┐  │  │  │  • pdf-reports bucket   │ │
│  │ State Management     │  │  │  │  • Signed URLs          │ │
│  │ • Zustand            │  │  │  └─────────────────────────┘ │
│  │ • React Hook Form    │  │  │  ┌─────────────────────────┐ │
│  └──────────────────────┘  │  │  │  Edge Functions (Deno)  │ │
│  ┌──────────────────────┐  │  │  │  • Chart Descriptions   │ │
│  │ Visualization        │  │  │  │  • Dust Scraper Trigger │ │
│  │ • Recharts           │  │  │  │  • Flow Meter Trigger   │ │
│  │ • Nivo               │  │  │  └─────────────────────────┘ │
│  └──────────────────────┘  │  └───────────────────────────────┘
│  ┌──────────────────────┐  │
│  │ PDF Generation       │  │
│  │ • jsPDF              │  │
│  │ • html2canvas        │  │
│  └──────────────────────┘  │
└────────────────────────────┘

                              │ (HTTPS)

┌─────────────────────────────────────────────────────────────────┐
│                      EXTERNAL SERVICES                           │
│  ┌─────────────┐  ┌─────────────┐  ┌──────────────────────┐    │
│  │   Google    │  │   Dustac    │  │  Bureau of           │    │
│  │   Gemini    │  │   Scraper   │  │  Meteorology (BOM)   │    │
│  │     AI      │  │     API     │  │  Weather API         │    │
│  └─────────────┘  └─────────────┘  └──────────────────────┘    │
└─────────────────────────────────────────────────────────────────┘

DEPLOYMENT TOPOLOGY:
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Pages:       Production (main branch)                │
│                         Staging (staging branch)                 │
│                         Preview (feature branches)               │
├─────────────────────────────────────────────────────────────────┤
│ Supabase:               Project: hwogexspejrzvadfjmce           │
│                         Region: us-east-1                        │
│                         Tier: Pro ($25/month)                    │
├─────────────────────────────────────────────────────────────────┤
│ GitHub:                 Repository with GitHub Actions CI/CD    │
│                         Auto-deploy to Cloudflare Pages          │
└─────────────────────────────────────────────────────────────────┘

This comprehensive architecture diagram shows all major components, their interactions, and the deployment topology. The system is designed for scalability, security, and performance while maintaining simplicity through managed services.


5. System Design

5.1 Business Requirements

The system design directly addresses the following key business requirements:

Primary Business Requirements:

  1. Environmental Compliance Management

    • Requirement: Enable mining operations to collect, store, and report on dust monitoring data to meet environmental regulations
    • Design Response: PostgreSQL database stores all measurements with 7-year retention; PDF reports generated for regulatory submission
    • Success Criteria: 100% of uploaded measurements retained and queryable; PDF reports meet regulatory formatting standards
  2. Multi-Site Operations Support

    • Requirement: Support multiple mining sites with independent data collection and reporting
    • Design Response: Site-based data organization; users can manage multiple sites with isolated data streams
    • Success Criteria: Users can upload and view data for 10+ distinct sites without performance degradation
  3. Real-Time Operational Insights

    • Requirement: Provide dashboard analytics and visualizations for operational decision-making
    • Design Response: Interactive dashboards with Recharts/Nivo; real-time dust level monitoring via scraper integration
    • Success Criteria: Dashboard loads within 1 second for 90-day data range; charts update within 500ms
  4. Automated Reporting

    • Requirement: Generate professional PDF reports automatically from uploaded CSV data
    • Design Response: Client-side PDF generation with jsPDF; customizable templates; AI-generated chart descriptions
    • Success Criteria: PDF generation completes within 60 seconds for 30-page reports
  5. Water Usage Tracking

    • Requirement: Monitor water consumption for dust suppression activities
    • Design Response: Flow meter data integration; automated collection via scraper API; usage analytics
    • Success Criteria: Flow meter data synchronized hourly; usage trends visualized in dashboard
  6. Weather Correlation

    • Requirement: Correlate environmental measurements with weather conditions
    • Design Response: BOM weather data integration; climate module with combined visualizations
    • Success Criteria: Weather data available for all measurement dates; correlation charts display both datasets
  7. Audit and Compliance

    • Requirement: Maintain complete audit trail for regulatory compliance
    • Design Response: Comprehensive activity logging via database triggers; immutable audit logs
    • Success Criteria: All data modifications logged with user_id, timestamp, and action details

Secondary Business Requirements:

  1. User Access Control: Row-Level Security ensures users only access their own data; admin role for oversight
  2. Weekly Field Reporting: Structured weekly reports capture operational activities in standardized format
  3. Data Export: Users can export all data (CSV, PDF) for external analysis or archival

Non-Functional Requirements Driving Design:

  • Performance: Sub-second dashboard load times, <30s CSV processing for 10K rows
  • Scalability: Support 50 concurrent users; architecture supports 10x growth
  • Security: Zero-trust model with JWT authentication, RLS policies, encrypted data
  • Reliability: 99.9% uptime target; automated backups; point-in-time recovery
  • Usability: Intuitive UI requiring minimal training; accessible to users with varying technical skills

5.2 Database Design

The database follows a normalized relational model (third normal form) optimized for transactional integrity and query performance.

5.2.1 Data Objects and Resultant Data Structures

Core Entity Definitions:

1. profiles (User Profiles):

  • Purpose: Extended user information beyond Supabase Auth
  • Key Attributes: id (UUID, PK), full_name, organization, role (user/admin)
  • Relationships: 1:many with uploads, weekly_reports, weather_data
  • Business Rules:
    • Auto-created when user signs up (trigger)
    • Role defaults to 'user'; only admins can elevate to 'admin'
    • Profile deletion cascades from auth.users deletion

2. uploads (Upload Batches):

  • Purpose: Track CSV upload operations with batch-level metadata
  • Key Attributes: id (UUID, PK), user_id (FK), upload_type, period_start/end, status, file counts
  • Relationships: 1:many with csv_files; many:1 with profiles
  • Business Rules:
    • Status transitions: pending → validating → importing → generating_reports → completed/failed
    • Cannot delete upload with status 'importing' or 'generating_reports'
    • Soft delete retains data for 30 days

3. csv_files (Individual CSV Files):

  • Purpose: Metadata for each CSV file in an upload batch
  • Key Attributes: id (UUID, PK), upload_id (FK), site_name, period_start/end, record_count, devices (JSONB)
  • Relationships: many:1 with uploads; 1:many with measurements; 1:1 with reports
  • Business Rules:
    • Unique constraint on (upload_id, filename)
    • Period must be within upload batch period
    • Device list extracted during CSV parsing

4. measurements (Environmental Readings):

  • Purpose: Individual dust measurement records from DustRanger devices
  • Key Attributes: id (UUID, PK), time (timestamptz), pm10/pm25/pm40 (numeric), temperature, humidity, site
  • Relationships: many:1 with csv_files and uploads
  • Business Rules:
    • PM values must be non-negative
    • Temperature range: -50°C to 60°C
    • Humidity range: 0% to 100%
    • Time stored in UTC; converted to local for display

5. reports (Generated PDF Reports):

  • Purpose: Metadata for generated PDF reports
  • Key Attributes: id (UUID, PK), upload_id/csv_file_id (FKs), pdf_storage_path, generation_timestamp
  • Relationships: many:1 with uploads and csv_files
  • Business Rules:
    • Reports are publicly viewable (all authenticated users)
    • Version increments on regeneration
    • PDF file stored in Supabase Storage

6. weekly_reports (Structured Weekly Reports):

  • Purpose: Structured field reports with 8 sections (JSONB)
  • Key Attributes: id (UUID, PK), user_id (FK), report_period_end, status (draft/completed), 8 JSONB sections
  • Relationships: many:1 with profiles
  • Business Rules:
    • Auto-save drafts every 30 seconds
    • Cannot submit incomplete sections
    • One report per user per week

7. weather_data (Weather Observations):

  • Purpose: Daily weather data from Bureau of Meteorology
  • Key Attributes: id (UUID, PK), station_id (FK), date, temperature/rainfall/wind measurements
  • Relationships: many:1 with weather_stations and profiles
  • Business Rules:
    • Unique constraint on (user_id, station_id, date)
    • Measurements at 9am and 3pm
    • Auto-fetched from BOM API

8. activity_logs (Audit Trail):

  • Purpose: Immutable audit log for compliance
  • Key Attributes: id (UUID, PK), user_id (FK), action, resource_type/id, details (JSONB), created_at
  • Relationships: many:1 with profiles
  • Business Rules:
    • No updates or deletes (append-only)
    • Auto-generated by triggers for report CRUD
    • Manually logged for read/download operations

Data Type Specifications:

PostgreSQL TypeUsagePrecisionRationale
UUIDAll primary keys128-bitGlobally unique, non-sequential (security), standard in distributed systems
TIMESTAMPTZAll timestampsMicrosecondTimezone-aware, stores UTC internally, converts to local for display
NUMERIC(p,s)MeasurementsVariableArbitrary precision, no floating-point rounding errors (critical for compliance)
JSONBSemi-structuredN/ABinary JSON, indexable with GIN, flexible schema for evolving data structures
TEXTStringsUnlimitedVariable length, no artificial 255-char limits, efficient storage
INTEGERCounts, IDs32-bit signedRange: -2B to +2B, sufficient for counts and small IDs
BIGINTFile sizes64-bit signedRange: -9 quintillion to +9 quintillion, handles large file sizes

Relationships and Cardinality:

profiles (1) ──< (many) uploads
  │                 │
  │                 └──< (many) csv_files
  │                         │
  │                         ├──< (many) measurements
  │                         └──< (1) reports

  ├──< (many) weekly_reports
  ├──< (many) weather_data
  ├──< (many) flow_meter_data (future)
  └──< (many) activity_logs

weather_stations (1) ──< (many) weather_data
mine_sites (1) ──< (many) devices (future)

Constraints and Validation:

  • Foreign Key Constraints: All FKs with ON DELETE CASCADE (parent deletion removes children) or ON DELETE SET NULL (preserve orphans)
  • Check Constraints: Enum-like fields (status, role, upload_type) use CHECK (field IN (...))
  • Unique Constraints: Natural keys (upload_id + filename, user_id + station_id + date)
  • Not Null Constraints: Required business fields enforced at database level
  • Default Values: Timestamps default to NOW(), UUIDs auto-generated, status fields default to initial state

5.2.2 File and Database Structures

5.2.2.1 Database Management System Files

PostgreSQL Configuration (Supabase Managed):

ParameterValuePurposeImpact
max_connections60 (PgBouncer pool)Concurrent connection limitPrevents connection exhaustion; scales to 50 concurrent users
shared_buffers~2GB (25% RAM)Database cache sizeFrequently accessed data cached in memory
effective_cache_size~6GB (75% RAM)Query planner assumptionOptimizer prefers index scans when data likely cached
work_mem32MBPer-operation memorySufficient for sorting 10K-row result sets
maintenance_work_mem512MBVACUUM, INDEX creationFaster index builds and maintenance operations
checkpoint_timeout15minWAL checkpoint frequencyBalance durability vs. write performance
wal_buffers16MBWrite-ahead log bufferReduces I/O for transaction logging
random_page_cost1.1SSD cost factorEncourages index usage on SSD storage

Complete Table Schemas (Summary):

See supabase/migrations/20250105000001_initial_schema.sql for full DDL. Key tables:

TableColumnsIndexesRLS PoliciesPurpose
profiles513 (SELECT own/all, UPDATE own)User management
uploads1554 (CRUD own) + 1 (admin all)Upload tracking
csv_files1742 (SELECT/UPDATE own) + 1 (admin)File metadata
measurements2272 (SELECT own/all)Environmental data
reports1253 (SELECT all, DELETE/UPDATE own) + 1 (admin)Report metadata
weekly_reports1234 (CRUD own)Field reports
weather_data2340 (public read via station policy)Weather observations
activity_logs942 (SELECT own/all)Audit trail

Stored Procedures and Functions:

sql
-- Function: handle_new_user()
-- Trigger: AFTER INSERT ON auth.users
-- Purpose: Auto-create profile when user signs up
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.profiles (id, full_name, role)
  VALUES (
    NEW.id,
    COALESCE(NEW.raw_user_meta_data->>'full_name', 'User'),
    'user'
  );
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- Function: log_report_activity()
-- Trigger: AFTER INSERT/UPDATE/DELETE ON reports
-- Purpose: Automatically log report CRUD operations
CREATE OR REPLACE FUNCTION log_report_activity()
RETURNS TRIGGER AS $$
DECLARE
  action_name TEXT;
  report_name TEXT;
BEGIN
  CASE TG_OP
    WHEN 'INSERT' THEN action_name := 'create';
    WHEN 'UPDATE' THEN action_name := 'update';
    WHEN 'DELETE' THEN action_name := 'delete';
  END CASE;

  report_name := COALESCE(NEW.site_name, OLD.site_name) || ' Report ' ||
                 TO_CHAR(COALESCE(NEW.generation_timestamp, OLD.generation_timestamp), 'YYYY-MM-DD');

  INSERT INTO activity_logs (user_id, action, resource_type, resource_id, resource_name, details)
  VALUES (
    (SELECT user_id FROM uploads WHERE id = COALESCE(NEW.upload_id, OLD.upload_id)),
    action_name,
    'report',
    COALESCE(NEW.id, OLD.id),
    report_name,
    jsonb_build_object('site', COALESCE(NEW.site_name, OLD.site_name), 'version', COALESCE(NEW.version, OLD.version))
  );

  RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- RPC Function: log_report_read()
-- Called from: Frontend when user views/downloads report
-- Purpose: Manually log read operations (not captured by triggers)
CREATE OR REPLACE FUNCTION log_report_read(
  p_report_id UUID,
  p_action TEXT  -- 'view' or 'download'
)
RETURNS VOID AS $$
DECLARE
  v_report reports%ROWTYPE;
  v_user_id UUID;
  v_report_name TEXT;
BEGIN
  SELECT * INTO v_report FROM reports WHERE id = p_report_id;
  IF NOT FOUND THEN
    RAISE EXCEPTION 'Report not found';
  END IF;

  v_user_id := auth.uid();
  v_report_name := v_report.site_name || ' Report ' || TO_CHAR(v_report.generation_timestamp, 'YYYY-MM-DD');

  INSERT INTO activity_logs (user_id, action, resource_type, resource_id, resource_name, details)
  VALUES (
    v_user_id,
    p_action,
    'report',
    p_report_id,
    v_report_name,
    jsonb_build_object('site', v_report.site_name, 'file_size_bytes', v_report.file_size_bytes)
  );
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

Database Triggers:

sql
-- Auto-create profile on user signup
CREATE TRIGGER on_auth_user_created
  AFTER INSERT ON auth.users
  FOR EACH ROW EXECUTE FUNCTION public.handle_new_user();

-- Auto-log report CRUD operations
CREATE TRIGGER report_activity_logger
  AFTER INSERT OR UPDATE OR DELETE ON reports
  FOR EACH ROW EXECUTE FUNCTION log_report_activity();

-- Auto-update updated_at timestamp
CREATE TRIGGER weekly_reports_updated_at
  BEFORE UPDATE ON public.weekly_reports
  FOR EACH ROW EXECUTE FUNCTION update_weekly_reports_updated_at();

-- (Similar updated_at triggers for weather_data, weather_stations)

Indexing Strategy (Performance-Critical Indexes):

sql
-- Composite indexes for common query patterns
CREATE INDEX idx_measurements_site_time ON measurements(site, time);
-- Supports: WHERE site = 'X' ORDER BY time (dashboard queries)

CREATE INDEX idx_measurements_upload_time ON measurements(upload_id, time);
-- Supports: WHERE upload_id = 'X' ORDER BY time (report generation)

CREATE INDEX idx_uploads_user_status ON uploads(user_id, status);
-- Supports: WHERE user_id = 'X' AND status IN ('pending', 'importing')

-- Partial indexes for filtered queries
CREATE INDEX idx_uploads_active ON uploads(user_id, status)
  WHERE status IN ('pending', 'importing', 'generating_reports');
-- Smaller index for active uploads only

-- GIN indexes for JSONB columns
CREATE INDEX idx_uploads_files_summary ON uploads USING GIN(files_summary);
-- Supports: WHERE files_summary @> '{"status": "completed"}'

-- B-tree indexes for timestamp ordering
CREATE INDEX idx_activity_logs_created_desc ON activity_logs(created_at DESC);
-- Optimizes: ORDER BY created_at DESC LIMIT 100 (recent activity)
5.2.2.2 Non-Database Management System Files

Supabase Storage Bucket Structure:

csv-uploads/
  {user_id}/
    daily/
      2025-01-15_{upload_id}_boddington.csv
      2025-01-16_{upload_id}_weipa.csv
    weekly/
      2025-01-W03_{upload_id}_combined.csv
    monthly/
      2025-01_{upload_id}_all_sites.csv

pdf-reports/
  {user_id}/
    Boddington/
      2025-01-01_2025-01-31_daily_report.pdf
      2025-02-01_2025-02-28_daily_report.pdf
    Weipa/
      2025-01_monthly_report.pdf

File Type Specifications:

File TypePurposeLocationAccess MethodRetentionMax SizeBackup
CSV Upload FilesRaw environmental datacsv-uploads/{user_id}/{type}/Signed URLs (1-hour expiry)2 years50MBS3 multi-AZ replication
PDF ReportsGenerated compliance reportspdf-reports/{user_id}/{site}/Signed URLs (24-hour expiry)Permanent (user-managed)10MBS3 multi-AZ replication
ConfigurationVite, TypeScript, ESLintGit repository rootFile systemN/A<100KBGit version control
Environment VariablesAPI keys, URLs.env.local (gitignored)Process environmentN/A<10KBNot backed up (secrets)
Database MigrationsSchema definitionssupabase/migrations/Supabase CLIN/A<1MB per fileGit version control
Application LogsRuntime errors, performanceSupabase platformDashboard API90 daysN/APlatform-managed

CSV File Structure Detail:

DustRanger CSV Format:
Header Row: loc,time,speed,massconcentration1p0,massconcentration2p5,...

Sample Data Row:
A,2025-01-15 10:30:00,5.2,12.3,45.6,78.9,123.4,567.8,234.5,89.2,34.1,12.7,1.8,28.5,35.2,65.3,5

Column Mapping:
  loc                      → measurements.loc (TEXT)
  time                     → measurements.time (TIMESTAMPTZ, converted to UTC)
  speed                    → measurements.speed (NUMERIC)
  massconcentration1p0     → measurements.massconcentration1p0 (NUMERIC)
  massconcentration2p5     → measurements.massconcentration2p5 (NUMERIC)
  massconcentration4p0     → measurements.massconcentration4p0 (NUMERIC)
  massconcentration10p0    → measurements.massconcentration10p0 (NUMERIC)
  ... (additional columns)

Validation Rules:
  - Header row required (exact column names)
  - UTF-8 encoding (BOM stripped if present)
  - Comma delimiter (RFC 4180 compliant)
  - Date/time parsing: flexible formats (ISO 8601, DD/MM/YYYY, etc.)
  - Numeric validation: non-negative for PM values, range checks for temp/humidity
  - Missing values: NULL for optional columns, error for required columns

Estimated File Characteristics:
  - Row count: 1,000 - 50,000 (typical daily file: 10,000 rows)
  - Row size: ~200-300 bytes (20 columns × 10-15 bytes avg)
  - File size: 2-15 MB (10K rows × 250 bytes ≈ 2.5MB)
  - Compression: Not applied (small files, processing overhead not justified)

PDF Report Structure Detail:

PDF/A Format Specification:
  - Standard: PDF/A-1b (ISO 19005-1:2005) for long-term archival
  - Page Size: A4 (210mm × 297mm)
  - Orientation: Portrait (switchable to Landscape for wide charts)
  - Color Space: sRGB IEC61966-2.1
  - Fonts: Embedded subset (Helvetica, Courier for tables)
  - Compression: JPEG for charts (quality 85), Flate for text
  - Metadata: XMP metadata with title, author, creation date, keywords

Typical Report Structure (8-12 pages):
  Page 1: Cover
    - Site name (36pt bold)
    - Report period (24pt)
    - Generation date and time
    - Company logo (if configured)
    - Regulatory compliance statement

  Page 2: Executive Summary
    - Total measurements: 10,234
    - Average PM2.5: 45.6 μg/m³
    - Average PM10: 87.3 μg/m³
    - Max PM2.5: 234.5 μg/m³ (timestamp)
    - Exceedance events: 12 (if thresholds defined)
    - Weather summary (if weather data available)

  Pages 3-8: Visualizations
    - PM2.5 Time Series (line chart, full page)
    - PM10 Time Series (line chart, full page)
    - Temperature & Humidity (dual-axis line chart)
    - Dust Level Heatmap (day × hour matrix)
    - Daily Averages (bar chart)
    - Hourly Distribution (stacked area chart)

  Pages 9-10: Statistical Tables
    - Daily summary (date, count, avg, max, min)
    - Hourly summary (hour, count, avg, max)
    - Percentile analysis (p50, p75, p90, p95, p99)

  Page 11: Compliance & Appendices
    - Regulatory standards referenced
    - Compliance status (pass/fail with criteria)
    - Data quality notes
    - Signature block (digital signature future enhancement)

  Page 12: Footer on All Pages
    - Page number (center)
    - Generation timestamp (right)
    - "Generated by Dustac Dashboard" (left)

Estimated File Characteristics:
  - Base PDF structure: ~100KB
  - Chart images (6 charts × 250KB avg): ~1.5MB
  - Tables and text: ~100KB
  - Total typical size: 1.7MB
  - Maximum size (30-page report with high-res charts): 8-10MB

Backup and Recovery Specifications:

Backup TypeFrequencyRetentionRecovery Time (RTO)Recovery Point (RPO)Tested
Database SnapshotDaily (3am UTC)7 days<1 hour (full restore)<24 hours (last snapshot)Monthly
Point-in-Time RecoveryContinuous (WAL)7 days<2 hours (restore to timestamp)<5 minutes (last WAL)Quarterly
Storage Files (S3)Continuous (replication)Permanent (unless deleted)<30 min (S3 11-9s durability)Near-zero (multi-AZ)N/A (AWS SLA)
User Data ExportOn-demandUser-managedImmediate (ZIP download)Current statePer export
Configuration/CodeGit commitsPermanent (Git history)Minutes (git checkout)Last commitPer deploy

5.3 Data Conversion

Current State: Greenfield deployment with no legacy data migration required.

Future Data Conversion Scenarios:

Scenario 1: CSV Format Evolution (High Probability)

DustRanger devices may update CSV format with new measurement types or renamed columns.

AspectSpecification
TriggerDevice firmware update adds new particle size measurements (PM0.3, PM0.5)
Source FormatExisting CSV files with 20 columns
Target FormatNew schema with 22 columns (2 additional PM measurements)
DetectionCSV parser detects missing/extra columns; format version inferred from column set
TransformationVersion-aware parser maps old column names to database; new columns set to NULL for old files
ValidationRow count matches; spot-check converted values against originals; statistical comparison (averages, max/min)
TestingImport 10 sample files (5 old format, 5 new format); verify both formats load correctly
RollbackKeep original CSV files in storage; re-import with corrected parser if issues detected

Implementation:

typescript
// CSV Parser with format version detection
function detectFormatVersion(headers: string[]): 'v1' | 'v2' {
  if (headers.includes('massconcentration0p3') && headers.includes('massconcentration0p5')) {
    return 'v2';  // New format with additional PM measurements
  }
  return 'v1';  // Original format
}

function parseCSV(file: File, formatVersion: 'v1' | 'v2') {
  const columnMapping = formatVersion === 'v1'
    ? V1_COLUMN_MAPPING  // Maps old columns to database
    : V2_COLUMN_MAPPING; // Maps new columns to database

  // Parse with version-specific mapping
  // V1 files: pm0p3 and pm0p5 set to NULL
  // V2 files: all columns populated
}

Scenario 2: Measurement Unit Conversion (Medium Probability)

Regulatory requirements change from μg/m³ to mg/m³ for certain measurements.

AspectSpecification
TriggerRegulatory authority updates standards to mg/m³ for PM10 and above
Sourcemeasurements table with PM values in μg/m³
TargetSame table with PM10+ columns in mg/m³ (divide by 1000)
TransformationSQL UPDATE statement with division: pm10 = pm10 / 1000
Affected DataHistorical measurements (future measurements imported in new units)
ValidationStatistical checks: ensure averages shift by factor of 1000; spot-check conversions; verify regulatory thresholds still pass
RollbackDatabase backup before conversion; multiply by 1000 to revert if issues found

SQL Script:

sql
-- Backup: Performed automatically via Supabase daily snapshot
-- Conversion script (run in transaction)
BEGIN;

-- Add column to track conversion
ALTER TABLE measurements ADD COLUMN unit_conversion_applied BOOLEAN DEFAULT FALSE;

-- Update PM10 and above to mg/m³
UPDATE measurements
SET
  massconcentration10p0 = massconcentration10p0 / 1000,
  unit_conversion_applied = TRUE
WHERE time < '2025-06-01'  -- Only historical data
  AND massconcentration10p0 IS NOT NULL;

-- Verify conversion
SELECT
  COUNT(*) as total_converted,
  AVG(massconcentration10p0) as new_avg,
  AVG(massconcentration10p0 * 1000) as old_avg_check
FROM measurements
WHERE unit_conversion_applied = TRUE;
-- Expect: old_avg_check ≈ historical average

COMMIT;  -- Only commit if validation passes

Scenario 3: User Data Import from External Systems (Future Enhancement)

Users migrating from competitor dashboards or Excel-based tracking.

AspectSpecification
Source FormatsExcel (.xlsx), CSV, competitor database exports
Process1. User uploads file via import wizard
2. System auto-detects format (file extension + content analysis)
3. Column mapping UI presented (auto-match by name similarity)
4. User confirms mappings and validation rules
5. Dry-run import with validation report
6. User approves; data imported with user_id tagging
Data QualityDuplicate detection (timestamp + site), date range validation, measurement range checks
Error HandlingPartial import supported; failed rows logged to error CSV; user can review and re-import
ValidationCompare row counts, statistical summaries (avg, max, min), spot-check random samples

5.4 User Machine-Readable Interface

5.4.1 Inputs

1. CSV File Upload:

AttributeSpecification
FormatCSV (RFC 4180), UTF-8 encoding with optional BOM
Required Columnstime, massconcentration2p5, massconcentration10p0, site
Optional Columnsloc, speed, temperature, humidity, numberconcentrations, typicalparticlesize, roadbumpscount
Header RowRequired (first row, exact column names case-insensitive)
Date/Time FormatFlexible: ISO 8601 (YYYY-MM-DD HH:MM:SS), Australian (DD/MM/YYYY HH:MM:SS), Unix timestamp
Decimal SeparatorPeriod (.) only
Row DelimiterCRLF (\r\n) or LF (\n)
BOM HandlingUTF-8 BOM (\uFEFF) automatically stripped during parsing
Maximum File Size50MB (client-side check), enforced by Supabase Storage (100MB hard limit)
Maximum Rows100,000 rows per file (performance limit for browser parsing)
Validation Rules- PM values ≥ 0
- Temperature: -50°C to 60°C
- Humidity: 0% to 100%
- Dates: within last 10 years, not future
- Site name: non-empty, max 100 chars

Validation Process:

Client-Side (Immediate Feedback):
1. Check file size < 50MB
2. Parse first 100 rows with PapaParse
3. Detect column headers (case-insensitive match)
4. Verify required columns present
5. Check data types (date parsing, numeric conversion)
6. Display preview table with first 10 rows
7. Show validation warnings (if any)

Server-Side (During Import):
1. Re-validate file size and format
2. Parse entire file
3. For each row:
   a. Validate non-null required fields
   b. Parse and validate date/time
   c. Validate numeric ranges
   d. Check data type compatibility
4. Aggregate validation errors
5. Reject file if error rate > 5% (configurable)
6. Log validation errors to processing_log (JSONB)

Error Handling:

Error TypeDetectionUser FeedbackResolution
Invalid FormatFile extension ≠ .csv or not parseable"File must be valid CSV format"Upload correct file
Missing ColumnsRequired columns not in header"Missing columns: time, pm25"Add columns or use correct file
Parse ErrorsRow cannot be parsed (unquoted commas, etc.)"Row 234: Unable to parse"Fix CSV formatting
Value Out of RangePM < 0 or temp > 60°C"Row 456: PM2.5 = -5 (must be ≥ 0)"Correct data in source
Date FormatCannot parse date"Row 789: Invalid date '13/45/2025'"Use supported format
File Too LargeSize > 50MB"File exceeds 50MB limit"Split file or compress

2. Web Form Inputs (Weekly Reports, Settings):

Input TypeValidationError Handling
Text FieldsZod schema: z.string().min(1).max(255)
Required vs. optional marked with asterisk
Inline error below field;
Red border on invalid;
Submit button disabled until valid
Date PickersFlatpickr library
Date range validation
Future dates blocked (for reports)
Calendar prevents invalid dates;
Manual entry validated on blur;
Error message: "Date must be in past"
Number Inputsz.number().min(0).max(1000000)
Decimal places limited (2 for measurements)
Input mask forces numeric;
Increment/decrement buttons;
Error shows acceptable range
Dropdown Selectsz.enum(['option1', 'option2'])
Required unless explicitly optional
Placeholder "Select...";
Error if unchanged on submit;
Searchable for long lists
JSONB DataNested Zod schemas for weekly report sectionsField-level validation;
Auto-save draft every 30s;
Visual indicators for incomplete sections

3. External API Inputs:

APIInput FormatValidationRate Limiting
Dustac ScraperPOST /api/scraper/dustlevels
{ siteName: string, startDate: string, endDate: string }
Site name in allowed list
Date range < 90 days
Dates in YYYY-MM-DD format
10 req/min per user
BOM WeatherGET https://bom.gov.au/climate/data/...?station=027045&date=YYYYMMDDStation ID from valid list
Date format YYYYMMDD
Date within last 5 years
60 req/min (BOM limit)
Google GeminiPOST /functions/v1/generate-chart-descriptions
{ chartType, chartData, siteName, dateRange }
Chart type from enum
Chart data is valid JSON
Token count < 30K
60 req/min (Gemini limit)

5.4.2 Outputs

1. PDF Reports:

AttributeSpecification
FormatPDF/A-1b (ISO 19005-1) for archival compliance
File Naming{site_name}_{start_date}_{end_date}_report.pdf
Example: Boddington_2025-01-01_2025-01-31_report.pdf
Typical Size1-3MB (6-8 charts)
Maximum Size10MB (30-page report with high-res charts)
Resolution150 DPI for charts (balance quality vs. size)
Color ProfilesRGB IEC61966-2.1
MetadataTitle: "{Site} Environmental Report {Date Range}"
Author: "Dustac Dashboard"
Keywords: site, compliance, dust, monitoring
Creation Date: Generation timestamp (ISO 8601)
AccessibilitySearchable text (no image-only PDFs)
Logical reading order
Tagged PDF (future enhancement)
DeliveryBrowser download (Content-Disposition: attachment)
Stored in Supabase Storage (permanent)
Signed URL (24-hour expiry) for sharing

2. CSV Data Exports:

AttributeSpecification
FormatCSV (RFC 4180), UTF-8 with BOM for Excel compatibility
File Naming{site}_{start_date}_{end_date}_export.csv
ColumnsAll measurement columns + metadata (upload_id, csv_file_id, site, time)
Header RowYes (descriptive column names)
Date FormatISO 8601 (YYYY-MM-DD HH:MM:SS) in user's local timezone
Number FormatMaximum 2 decimal places, period decimal separator
Row LimitUnlimited (streaming for large datasets via client-side generation)
DeliveryClient-side generation (FileSaver.js), no server roundtrip for performance

3. JSON API Responses:

typescript
// Success Response
{
  "data": [...] | {...},     // Array or single object
  "count": number,           // Total count (for pagination)
  "status": "success",
  "timestamp": "2025-01-15T10:30:00.000Z"
}

// Error Response
{
  "error": {
    "code": "VALIDATION_ERROR",  // ENUM: VALIDATION_ERROR, AUTH_ERROR, NOT_FOUND, SERVER_ERROR
    "message": "Invalid date range",  // User-friendly
    "details": {                      // Optional debugging info
      "field": "start_date",
      "value": "2025-13-01",
      "reason": "Month must be 1-12"
    }
  },
  "status": "error",
  "timestamp": "2025-01-15T10:30:00.000Z"
}

// HTTP Status Codes
200 OK               - Successful GET/POST/PUT
201 Created          - Resource created (POST)
204 No Content       - Successful DELETE
400 Bad Request      - Validation error
401 Unauthorized     - Missing/invalid authentication
403 Forbidden        - Insufficient permissions (RLS)
404 Not Found        - Resource not found
409 Conflict         - Duplicate resource
429 Too Many Requests - Rate limit exceeded
500 Internal Server Error - Server error

4. Real-Time Notifications (Toast Messages):

EventMessageDurationAction Button
Upload Success"CSV files uploaded successfully (3 files, 25,430 records)"5sView Reports
Upload Error"Upload failed: Invalid date format in row 234"10sView Details
PDF Generated"Report generated: Boddington Jan 2025 (2.3 MB)"5sView PDF
Draft Saved"Weekly report auto-saved"2s-
Permission Denied"Access denied: You don't have permission to view this resource"5s-

5.5 User Interface Design

UI Framework: React 19 with Tailwind CSS and shadcn/ui component library

Navigation Structure:

Main Navigation (Sidebar - Always Visible):
  Dashboard (/)
  Upload (/upload)
  Reports (/reports)
  Dust Levels (/dust-levels)
  Weekly Reports (/weekly-reports)
  Flow Meter (/flow-meter)
  Climate (/climate)
  Report Templates (/report-templates)
  Settings (/settings)

User Profile Dropdown (Top-Right):
  View Profile
  Settings
  Help & Documentation
  Sign Out

Screen Layouts (ASCII mockups):

Dashboard Layout:

┌────────────────────────────────────────────────────────────────┐
│ Dustac Dashboard              [Search]        [👤 User Menu ▼] │
├─────────┬──────────────────────────────────────────────────────┤
│         │ Home > Dashboard                                      │
│         ├───────────────────────────────────────────────────────┤
│  Dash   │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐│
│ Upload  │ │  Total   │ │  Total   │ │  Total   │ │ Active   ││
│ Reports │ │ Uploads  │ │Measuremts│ │ Reports  │ │  Sites   ││
│  Dust   │ │   142    │ │ 1.2M     │ │    87    │ │    5     ││
│ Weekly  │ └──────────┘ └──────────┘ └──────────┘ └──────────┘│
│  Flow   │                                                       │
│ Climate │ Recent Activity                         View All >   │
│ Templates│ ┌───────────────────────────────────────────────┐  │
│ Settings│ │Date      │Action     │Site      │Status      │  │
│         │ ├───────────────────────────────────────────────┤  │
│         │ │2025-01-15│CSV Upload │Boddington│✓ Complete  │  │
│         │ │2025-01-14│Report Gen │Weipa     │✓ Complete  │  │
│         │ └───────────────────────────────────────────────┘  │
│         │                                                      │
│         │ PM2.5 Trends (Last 30 Days)                         │
│         │ ┌──────────────────────────────────────────────┐   │
│         │ │        [Line Chart with multiple sites]      │   │
│         │ │                                               │   │
│         │ └──────────────────────────────────────────────┘   │
└─────────┴──────────────────────────────────────────────────────┘

User Interaction Patterns:

PatternImplementationUse Case
Drag & Dropreact-dropzoneCSV file upload
Inline EditingClick-to-edit with auto-saveWeekly report sections
Modal DialogsRadix UI DialogPDF preview, delete confirmations
Toast Notificationsreact-hot-toastSuccess/error messages (top-right)
Loading StatesSkeleton loaders + spinnersData fetching placeholders
Infinite ScrollIntersection ObserverActivity feed (future)
FilteringDropdown + date range pickersReports by site/date
SortingTable header clicksSort by date, size, status
TooltipsRadix UI TooltipHelp text on icons/abbreviations

5.5.1 Section 508 Compliance

WCAG 2.1 AA Compliance Strategy:

1. Perceivable:

  • Text Alternatives: Alt text for all images; chart descriptions via AI
  • Color Contrast: Minimum 4.5:1 for normal text, 3:1 for large text (Tailwind enforced)
  • Resize Text: Responsive typography (rem units), scalable to 200% without horizontal scroll
  • Reflow: Content reflows at 320px width, no 2D scrolling (except data tables)

2. Operable:

  • Keyboard Accessible: All interactive elements in tab order, logical sequence
  • Focus Visible: 2px outline on focus (Tailwind focus:ring-2)
  • No Keyboard Trap: Modals close on Escape, return focus on dismiss
  • Timing Adjustable: No time limits except session timeout (1 hour, extendable)

3. Understandable:

  • Language: <html lang="en">, consistent terminology
  • Predictable: Consistent navigation, no unexpected context changes
  • Input Assistance: Labels for all fields, error messages describe problem and solution

4. Robust:

  • Valid HTML: Semantic HTML5, no duplicate IDs
  • ARIA: Landmarks (role="navigation"), labels for icon buttons, live regions for status updates

Testing Approach:

  1. Automated: Lighthouse (score > 90), axe DevTools, ESLint jsx-a11y
  2. Manual: Keyboard navigation, screen reader (NVDA/JAWS), color contrast tools
  3. User Testing: Quarterly review with assistive technology users

This completes Section 5 with comprehensive system design specifications.


6. Operational Scenarios

This section describes detailed operational scenarios that illustrate how users interact with the Dustac Environmental Monitoring Dashboard. Each scenario follows a structured format including actors, preconditions, main flow, postconditions, alternative flows, and exception handling.

6.1 Scenario: User Registration and Authentication

6.1.1 Scenario Overview

Description: A new user registers for access to the Dustac system and subsequently authenticates to access the dashboard.

Primary Actor: Environmental Officer / Mine Site Manager

Supporting Actors:

  • Supabase Authentication Service
  • Email Service Provider
  • System Administrator (for role assignment)

Business Goal: Enable secure access to the environmental monitoring system while maintaining audit trails and enforcing role-based access control.

6.1.2 Preconditions

PreconditionDescription
PRE-01User has a valid corporate email address
PRE-02System is accessible via network (internet connectivity)
PRE-03Supabase authentication service is operational
PRE-04User's organization has active subscription
PRE-05User has received invitation or authorization to register

6.1.3 Main Success Flow

Phase 1: Registration

StepActorActionSystem Response
1UserNavigates to registration page (https://app.dustac.com/register)System displays registration form
2UserEnters email address, full name, password (min 12 characters)System validates input format in real-time
3UserSelects organization from dropdownSystem loads organization metadata
4UserClicks "Register" buttonSystem validates all fields
5SystemChecks email uniqueness in auth.users tableNo duplicate found
6SystemCreates user record in auth.users with status='unconfirmed'UUID generated, encrypted password stored
7SystemTriggers handle_new_user() functionProfile record created in user_profiles table
8SystemSends confirmation email via Supabase AuthEmail contains magic link (valid 1 hour)
9SystemDisplays confirmation message"Check your email to verify your account"
10SystemLogs activity in activity_logs tableAction: 'user_registration', includes IP address

Phase 2: Email Verification

StepActorActionSystem Response
11UserChecks email inbox and clicks verification linkBrowser opens verification URL
12SystemValidates token from URL parameterToken is valid and not expired
13SystemUpdates auth.users.email_confirmed_at timestampStatus changes to 'confirmed'
14SystemCreates JWT access token (expires 1 hour) and refresh token (expires 30 days)Tokens stored in localStorage
15SystemRedirects to dashboard home pageUser sees welcome message and onboarding tour
16SystemLogs activityAction: 'email_verified'

Phase 3: Subsequent Authentication

StepActorActionSystem Response
17UserNavigates to login page (https://app.dustac.com/login)System displays login form
18UserEnters email and passwordSystem validates format
19UserClicks "Sign In" buttonSystem submits credentials to Supabase Auth
20SystemVerifies credentials against auth.users tablePassword hash matches
21SystemChecks account statusStatus is 'confirmed' and not locked
22SystemGenerates new JWT access and refresh tokensTokens returned to client
23SystemStores tokens in localStorageTokens encrypted in browser storage
24SystemFetches user profile from user_profiles tableIncludes role, organization, preferences
25SystemRedirects to last visited page or dashboard homeUser sees personalized dashboard
26SystemLogs activityAction: 'user_login', timestamp, IP address, user agent

6.1.4 Postconditions

PostconditionDescription
POST-01User record exists in auth.users with confirmed email
POST-02User profile exists in user_profiles with default role='user'
POST-03Valid JWT tokens stored in browser localStorage
POST-04User session active with 1-hour expiry
POST-05Activity log entries created for registration, verification, and login
POST-06User can access dashboard and upload data

6.1.5 Alternative Flows

A1: Email Already Registered

StepConditionAlternative Action
5aEmail exists in auth.usersSystem displays error: "Email already registered. Please sign in or reset password."
5bUser clicks "Forgot Password" linkSystem redirects to password reset flow

A2: Weak Password

StepConditionAlternative Action
4aPassword fails complexity requirementsSystem displays inline error: "Password must contain 12+ characters, uppercase, lowercase, number, and special character"
4bUser updates passwordSystem re-validates and allows submission

A3: Email Verification Expired

StepConditionAlternative Action
12aToken expired (>1 hour old)System displays error: "Verification link expired"
12bSystem provides "Resend Verification Email" buttonUser clicks button
12cSystem generates new token and sends emailNew email sent with fresh link

A4: Invalid Credentials at Login

StepConditionAlternative Action
20aPassword does not match hashSystem increments failed_login_attempts counter
20bFailed attempts < 5System displays: "Invalid email or password. X attempts remaining"
20cFailed attempts >= 5System locks account for 30 minutes, sends security alert email

A5: Session Expired

StepConditionAlternative Action
-User accesses system with expired access tokenSystem attempts to refresh using refresh token
-Refresh token validSystem issues new access token, user continues session
-Refresh token expiredSystem redirects to login page with message: "Session expired. Please sign in again."

6.1.6 Exception Handling

ExceptionCauseSystem BehaviorRecovery Action
EX-01Supabase Auth service downDisplay error page with retry buttonUser waits and retries; system logs incident
EX-02Email service unavailableRegistration succeeds but email failsSystem queues email for retry; user can request resend
EX-03Network connection lost during registrationTransaction rolled backUser refreshes page and resubmits form
EX-04Browser blocks third-party cookiesToken storage failsSystem displays warning to enable cookies or use different browser
EX-05Database constraint violationRegistration failsSystem logs error, displays generic message, notifies admin

6.1.7 Business Rules

Rule IDBusiness Rule
BR-01Only users with confirmed email addresses can access the system
BR-02Passwords must meet minimum complexity: 12 characters, mixed case, numbers, special characters
BR-03Failed login attempts > 5 within 30 minutes trigger account lockout
BR-04User sessions expire after 1 hour of inactivity (access token TTL)
BR-05All authentication events must be logged for audit compliance
BR-06Users must belong to an active organization subscription

6.1.8 Performance Requirements

MetricTargetMeasurement
Registration form load time< 2 secondsTime to interactive
Registration submission processing< 3 secondsServer response time
Email delivery time< 60 secondsSMTP queue to inbox
Login processing time< 1.5 secondsCredential validation to token issuance
Token refresh time< 500msBackground refresh without user interruption

6.2 Scenario: CSV Data Upload

6.2.1 Scenario Overview

Description: An environmental officer uploads CSV files containing dust monitoring data from field devices to the Dustac platform for processing and analysis.

Primary Actor: Environmental Officer / Field Technician

Supporting Actors:

  • CSV Parser Service
  • Supabase Storage Service
  • Database Import Service
  • Notification Service

Business Goal: Ingest environmental monitoring data efficiently and accurately to enable compliance reporting and analysis.

6.2.2 Preconditions

PreconditionDescription
PRE-01User is authenticated with valid session
PRE-02User has at least one mine site configured in the system
PRE-03CSV files are available locally (exported from Dustac devices)
PRE-04CSV files conform to expected format (18 columns, UTF-8 encoding)
PRE-05User has sufficient storage quota remaining
PRE-06Network bandwidth adequate for file transfer

6.2.3 Main Success Flow

Phase 1: Upload Initiation

StepActorActionSystem Response
1UserNavigates to Upload page (/upload)System displays upload interface with drag-drop zone
2SystemFetches user's mine sites from databaseDisplays site selection dropdown
3UserSelects target mine site from dropdownSystem caches selection
4UserSelects upload type (Daily/Weekly/Monthly)System adjusts date range picker
5UserSets period start and end datesSystem validates date range is logical

Phase 2: File Selection and Validation

StepActorActionSystem Response
6UserDrags and drops 3 CSV files into upload zone OR clicks "Browse" and selects filesSystem displays file list with names and sizes
7SystemPerforms client-side pre-validationChecks file extensions (.csv), sizes (< 50MB each), count (< 10 files)
8SystemReads first 5 rows of each file for previewDisplays data preview table
9SystemDetects and strips UTF-8 BOM if presentNormalizes encoding
10SystemParses CSV headersValidates 18 expected columns exist
11SystemValidates data types in sample rowsChecks numeric fields, date formats, required fields
12SystemDisplays validation resultsShows ✓ for valid files, ⚠ for warnings, ✗ for errors

Phase 3: Upload Execution

StepActorActionSystem Response
13UserReviews validation results and clicks "Upload" buttonSystem disables button, shows progress modal
14SystemCreates upload record in uploads tableStatus='pending', generates UUID
15SystemFor each CSV file, creates csv_files recordLinks to upload_id, status='pending'
16SystemInitiates parallel upload to Supabase StorageFiles uploaded to bucket: csv-uploads/{user_id}/{upload_id}/
17SystemDisplays progress bar per fileUpdates % completion in real-time
18SystemOn successful upload, updates csv_files.storage_pathStores full path for retrieval
19SystemUpdates upload.status='validating'Triggers validation phase

Phase 4: Data Processing

StepActorActionSystem Response
20SystemInvokes CSV parser for each fileParses all rows, extracts measurements
21SystemValidates each measurement recordChecks: time format, numeric ranges, required fields
22SystemUpdates csv_files.record_countStores total valid records per file
23SystemUpdates upload.status='importing'Begins database import
24SystemBatch inserts measurements into measurements tableUses batch size of 1000 records per query
25SystemUpdates progress indicatorShows "Importing: X of Y records"
26SystemOn completion, updates csv_files.status='completed'Marks individual file as processed
27SystemUpdates upload.status='completed'All files processed successfully
28SystemUpdates summary statisticstotal_record_count, successful_files, files_summary JSON
29SystemLogs activity in activity_logsAction: 'data_upload', details include file count and record count
30SystemDisplays success notification"Upload completed: 3 files, 4,523 records imported"

6.2.4 Postconditions

PostconditionDescription
POST-01Upload record exists in uploads table with status='completed'
POST-02CSV files stored in Supabase Storage with unique paths
POST-03csv_files records created for each file with metadata
POST-04Measurement records inserted into measurements table
POST-05User can query uploaded data immediately
POST-06Activity logged for compliance audit trail
POST-07User receives confirmation notification

6.2.5 Alternative Flows

A1: Invalid File Format

StepConditionAlternative Action
10aCSV missing required columnsSystem displays error: "File missing columns: [list]. Please export from Dustac device."
10bUser clicks "Remove" to exclude invalid fileSystem removes file from upload queue
10cUser proceeds with valid files onlySystem uploads remaining valid files

A2: Network Interruption During Upload

StepConditionAlternative Action
17aNetwork connection lost mid-uploadSystem pauses upload, displays "Connection lost. Retrying..."
17bSystem attempts retry every 5 secondsUses exponential backoff (5s, 10s, 20s)
17cConnection restored within 2 minutesSystem resumes upload from last checkpoint
17dConnection not restored after 2 minutesSystem marks upload as 'failed', allows user to retry

A3: Duplicate Data Detected

StepConditionAlternative Action
24aMeasurement records overlap with existing dataSystem detects duplicate (site + timestamp)
24bSystem displays warning dialog"X records already exist. Skip duplicates or cancel upload?"
24cUser selects "Skip Duplicates"System inserts only new records, updates summary with skipped count

A4: Partial File Failure

StepConditionAlternative Action
21aFile 2 of 3 has validation errors in 10% of rowsSystem marks those rows as invalid
21bSystem continues processing valid rowsImports 90% of data from File 2
21cUpdates csv_files.status='partial_success'Stores validation_errors JSON with row numbers
21dDisplays warning notification"File 2: 450 of 500 records imported. 50 records had errors."
21eSystem provides "Download Error Report" buttonUser downloads CSV with error details

A5: Storage Quota Exceeded

StepConditionAlternative Action
16aUser storage quota exceededSystem aborts upload before starting
16bDisplays error message"Storage quota exceeded. Delete old uploads or contact admin."
16cProvides link to Manage Storage pageUser can delete old CSV files and reports

6.2.6 Exception Handling

ExceptionCauseSystem BehaviorRecovery Action
EX-01Supabase Storage unavailableUpload fails immediatelySystem displays error, user retries later
EX-02Database connection timeout during importTransaction rolled backSystem marks upload as 'failed', allows retry
EX-03Malformed CSV (encoding issues)Parser throws exceptionSystem logs error, marks file as 'failed', displays helpful error message
EX-04File size exceeds 50MB limitClient-side validation blocks uploadSystem displays error: "File too large. Split data into multiple files."
EX-05Browser crashes during uploadUpload abandonedOn restart, system shows "Resume Upload?" prompt if partial data exists

6.2.7 Business Rules

Rule IDBusiness Rule
BR-01CSV files must contain exactly 18 columns in specified order
BR-02Maximum file size: 50MB per file, maximum 10 files per upload
BR-03Duplicate measurements (same site + timestamp) are skipped by default
BR-04Data retention: Uploaded CSV files retained for 90 days, measurements retained indefinitely
BR-05Users can only upload data for mine sites they have access to
BR-06Failed uploads can be retried; system cleans up partial data after 24 hours

6.2.8 Data Validation Rules

FieldValidation RuleError Handling
timeISO 8601 format, not future dateReject row, log error
massconcentration*Numeric, >= 0, <= 10000 μg/m³Reject row if required field invalid
temperatureNumeric, -50 to 80 °CAccept with warning if out of range
humidityNumeric, 0 to 100 %Reject row if out of range
speedNumeric, >= 0 km/hAccept null, reject negative
locText, max 255 charactersTruncate if longer

6.2.9 Performance Requirements

MetricTargetMeasurement
File upload speed> 5 MB/sNetwork transfer rate
CSV parsing throughput> 10,000 rows/secondParser processing rate
Database insert throughput> 5,000 records/secondBatch insert performance
Total upload time (10MB file)< 30 secondsEnd-to-end from file selection to completion
Progress update frequencyEvery 500msReal-time UI refresh rate

6.3 Scenario: PDF Report Generation

6.3.1 Scenario Overview

Description: A user generates a comprehensive PDF report containing dust monitoring data, charts, and analysis for a specified date range and set of monitoring devices.

Primary Actor: Environmental Manager / Compliance Officer

Supporting Actors:

  • Report Data Service
  • Chart Rendering Service (Recharts)
  • PDF Generation Service (jsPDF + html2canvas)
  • Supabase Storage Service

Business Goal: Produce professional, compliant environmental reports for regulatory submission and stakeholder communication.

6.3.2 Preconditions

PreconditionDescription
PRE-01User is authenticated with valid session
PRE-02Measurement data exists in database for selected period
PRE-03User has selected at least one mine site and device
PRE-04Browser supports HTML5 Canvas API
PRE-05User has sufficient storage quota for report
PRE-06Adequate browser memory (recommended 4GB+ available)

6.3.3 Main Success Flow

Phase 1: Report Configuration

StepActorActionSystem Response
1UserNavigates to Reports page (/reports)System displays report generation interface
2SystemLoads user's mine sites from databasePopulates site dropdown
3UserSelects mine site from dropdownSystem fetches devices for selected site
4SystemQueries devices table filtered by site_idDisplays device multiselect with device names
5UserSelects 2 devices (e.g., "Device-A1", "Device-B3")System updates selection state
6UserSelects date range using calendar pickerSystem validates: start < end, range <= 90 days
7UserSelects report template (Standard/Detailed/Summary)System loads template configuration
8UserClicks "Preview Data" buttonSystem triggers data preview

Phase 2: Data Retrieval and Preview

StepActorActionSystem Response
9SystemConstructs SQL query with filtersWHERE site=X AND device IN (A,B) AND time BETWEEN start AND end
10SystemExecutes query against measurements tableRetrieves matching records (assume 4,523 records)
11SystemAggregates data for previewCalculates: record count, date range, avg/max PM2.5, PM10
12SystemDisplays data summary tableShows: Device
13SystemRenders preview chart (line chart)Shows PM2.5 trend over time for both devices
14UserReviews preview, adjusts filters if neededSystem updates preview dynamically
15UserSatisfied with data, clicks "Generate Report"System initiates PDF generation

Phase 3: PDF Generation

StepActorActionSystem Response
16SystemCreates report record in reports tableStatus='generating', stores parameters
17SystemDisplays modal: "Generating report..." with progress barUser cannot navigate away
18SystemRenders React components for report in hidden divComponents include: cover page, summary, charts, data tables
19SystemWaits for all charts to complete renderingUses useEffect hooks to detect completion
20SystemCaptures cover page as image using html2canvasResolution: 1240x1754px (A4 portrait at 150 DPI)
21SystemUpdates progress: "Rendering page 1 of 12"Progress bar: 8%
22SystemCaptures summary pageContains: site info, date range, device list, key statistics
23SystemUpdates progress: "Rendering page 2 of 12"Progress bar: 16%
24SystemCaptures chart pages (6 pages)PM2.5 trend, PM10 trend, PM2.5 vs PM10, Temperature, Humidity, Daily averages
25SystemUpdates progress after each chartProgress bar: 24%, 32%, 40%, 48%, 56%, 64%
26SystemCaptures data table pages (4 pages)Paginated tables with 100 records per page
27SystemUpdates progress after each tableProgress bar: 72%, 80%, 88%, 96%
28SystemInitializes jsPDF instanceFormat: A4, orientation: portrait
29SystemAdds each captured image to PDFMaintains aspect ratio, adds page breaks
30SystemGenerates PDF blobFile size: ~3.2 MB
31SystemUpdates progress: "Saving report..."Progress bar: 100%

Phase 4: Storage and Download

StepActorActionSystem Response
32SystemGenerates filenameFormat: "{site}_{start-date}to_Report.pdf"
33SystemUploads PDF to Supabase StorageBucket: reports/{user_id}/{upload_id}/
34SystemUpdates reports tablepdf_storage_path, file_size_bytes, page_count, status='completed'
35SystemCalculates generation durationStores generation_duration_ms (e.g., 45,300 ms = 45.3 seconds)
36SystemLogs activity in activity_logsAction: 'report_generated', includes report_id and parameters
37SystemTriggers browser downloadUses FileSaver.js to download PDF
38SystemCloses progress modalDisplays success notification: "Report generated successfully!"
39SystemDisplays report card in Reports listCard shows: filename, date, size, download/share buttons

6.3.4 Postconditions

PostconditionDescription
POST-01PDF report file saved to user's local downloads folder
POST-02Report record exists in reports table with status='completed'
POST-03PDF file stored in Supabase Storage with unique path
POST-04User can re-download report from Reports page
POST-05Activity logged for audit trail
POST-06Report appears in user's report history
POST-07Report metadata includes generation timestamp and parameters

6.3.5 Alternative Flows

A1: No Data for Selected Criteria

StepConditionAlternative Action
10aQuery returns 0 recordsSystem displays warning: "No data found for selected criteria"
10bSystem disables "Generate Report" buttonUser must adjust filters (date range, devices)
10cSystem provides suggestions"Try expanding date range or selecting different devices"

A2: Browser Memory Insufficient

StepConditionAlternative Action
20ahtml2canvas fails due to memory errorSystem catches exception
20bSystem displays error modal"Report too large to generate in browser. Please reduce date range or contact support."
20cUpdates reports.status='failed'Stores error_message in database
20dLogs error with detailsIncludes browser info, data size, memory usage

A3: User Cancels Generation

StepConditionAlternative Action
25aUser clicks "Cancel" button in progress modalSystem stops rendering process
25bSystem deletes partial report filesCleans up temporary data
25cUpdates reports.status='cancelled'Marks report as cancelled
25dDisplays notification"Report generation cancelled"

A4: Large Dataset (>10,000 records)

StepConditionAlternative Action
10aQuery returns >10,000 recordsSystem displays warning: "Large dataset detected (15,423 records)"
10bSystem offers options"Generate full report (may take 2-3 minutes) OR Generate summary report (faster)"
10cUser selects "Summary"System aggregates data by day instead of individual records
10dPDF contains summary tablesDaily averages instead of raw data

A5: Report Already Exists

StepConditionAlternative Action
16aIdentical report parameters exist in last 24 hoursSystem detects duplicate based on site, devices, date range
16bSystem displays prompt"Similar report exists. Re-generate or download existing?"
16cUser selects "Download Existing"System retrieves existing PDF from storage and downloads
16dUser selects "Re-generate"System proceeds with new generation

6.3.6 Exception Handling

ExceptionCauseSystem BehaviorRecovery Action
EX-01Database query timeoutQuery exceeds 30-second limitSystem displays error, suggests reducing date range
EX-02Chart rendering failureRecharts component throws errorSystem logs error, generates report without that chart, includes placeholder
EX-03Storage upload failureSupabase Storage unavailableReport generated but not saved; offers "Save to Device Only"
EX-04PDF generation crashjsPDF throws exceptionSystem catches error, logs details, displays user-friendly error message
EX-05Network interruption during uploadStorage upload fails mid-streamSystem retries upload 3 times with exponential backoff

6.3.7 Business Rules

Rule IDBusiness Rule
BR-01Reports can span maximum 90 days to ensure reasonable file size
BR-02PDF reports must include: cover page, summary, charts, data tables, footer with generation timestamp
BR-03All reports are stored for 90 days, then automatically archived
BR-04Users can re-generate reports with same parameters; new version increments version number
BR-05Report filenames must be unique and descriptive (include site, date range)
BR-06Generated reports are viewable by all users in the same organization (RLS policy)

6.3.8 Report Content Specification

SectionPage #Content Description
Cover Page1Organization logo, report title, site name, date range, generation date
Executive Summary2Key findings, compliance status, exceedance events, recommendations
Site Information3Site details, device list, monitoring parameters, data completeness
PM2.5 Analysis4-5Time series chart, statistical summary, exceedance analysis, regulatory comparison
PM10 Analysis6-7Time series chart, statistical summary, exceedance analysis, regulatory comparison
Environmental Conditions8-9Temperature and humidity charts, correlation analysis
Daily Averages10Table with daily average values for all parameters
Raw Data Tables11-12Paginated tables with individual measurements (sample or full data)
Footer (all pages)-Page number, report ID, confidentiality notice, generation timestamp

6.3.9 Performance Requirements

MetricTargetMeasurement
Data query time< 5 secondsFor up to 10,000 records
Chart rendering time< 2 seconds per chartTime to complete useEffect hook
html2canvas capture time< 3 seconds per pageImage generation duration
PDF assembly time< 5 secondsjsPDF processing time
Total generation time (standard report)< 60 secondsEnd-to-end for 5,000 records, 12 pages
PDF file size< 5 MBFor standard 90-day report

6.4 Scenario: Dust Level Data Query and Export

6.4.1 Scenario Overview

Description: A user queries historical dust monitoring data using filters and exports the results to CSV format for external analysis or reporting.

Primary Actor: Data Analyst / Environmental Scientist

Supporting Actors:

  • Query Service
  • Data Export Service
  • Supabase Database

Business Goal: Enable flexible data access and integration with external analysis tools and regulatory reporting systems.

6.4.2 Preconditions

PreconditionDescription
PRE-01User is authenticated with valid session
PRE-02Historical measurement data exists in database
PRE-03User has access to at least one mine site
PRE-04Browser supports Blob API for file download

6.4.3 Main Success Flow

Phase 1: Query Configuration

StepActorActionSystem Response
1UserNavigates to Dust Levels page (/dust-levels)System displays data query interface
2SystemLoads user's accessible mine sitesPopulates site filter dropdown
3UserSelects mine site(s) from multiselectSystem fetches devices for selected sites
4UserSelects specific devices (optional)System updates device filter state
5UserSets date range (start and end dates)System validates range (max 365 days)
6UserConfigures additional filters: PM2.5 range, temperature rangeSystem stores filter criteria
7UserClicks "Apply Filters" buttonSystem constructs and executes query

Phase 2: Data Retrieval and Display

StepActorActionSystem Response
8SystemConstructs SQL query with WHERE clausesApplies all user-selected filters
9SystemExecutes paginated query (limit 100 records)Retrieves first page of results
10SystemCalculates total result countCOUNT(*) query with same filters
11SystemDisplays results in data gridColumns: Time, Site, Device, PM2.5, PM10, Temp, Humidity
12SystemShows pagination controls"Showing 1-100 of 2,456 results"
13SystemRenders summary statisticsMin, Max, Average, Median for PM2.5 and PM10
14SystemDisplays interactive chart previewLine chart showing PM2.5 trend over time

Phase 3: Data Interaction

StepActorActionSystem Response
15UserClicks column header to sortSystem re-queries with ORDER BY clause
16UserNavigates to page 3 using paginationSystem fetches records 201-300 (OFFSET 200 LIMIT 100)
17UserClicks on specific rowSystem displays detail modal with all 18 measurements
18UserCloses detail modalSystem returns to data grid view

Phase 4: Data Export

StepActorActionSystem Response
19UserClicks "Export to CSV" buttonSystem displays export configuration modal
20UserSelects export scope: "Current Page" or "All Results"System updates export settings
21UserSelects columns to include (default: all)System updates column selection
22UserClicks "Download CSV" buttonSystem initiates export process
23SystemFetches all data matching filters (no pagination)Executes query without LIMIT
24SystemConverts data to CSV format using PapaParseIncludes headers, formats timestamps, handles nulls
25SystemGenerates CSV blobFile size: ~850 KB for 2,456 records
26SystemGenerates filenameFormat: "DustLevels_{site}_{start-date}to.csv"
27SystemTriggers browser downloadUses FileSaver.js
28SystemLogs activity in activity_logsAction: 'data_export', includes record count
29SystemDisplays success notification"Exported 2,456 records to CSV"

6.4.4 Postconditions

PostconditionDescription
POST-01CSV file downloaded to user's local machine
POST-02Export activity logged for audit compliance
POST-03User can immediately open CSV in Excel or analysis tools
POST-04Original query filters remain active for further exploration

6.4.5 Alternative Flows

A1: Large Dataset Warning

StepConditionAlternative Action
23aQuery matches >50,000 recordsSystem displays warning: "Large export (50K+ records). This may take 1-2 minutes."
23bUser confirms or cancelsIf confirmed, system proceeds with progress indicator
23cSystem streams data in batchesFetches 10,000 records at a time to avoid memory issues

A2: No Results Found

StepConditionAlternative Action
9aQuery returns 0 resultsSystem displays message: "No data matches your filters"
9bSystem provides suggestions"Try expanding date range or removing filters"
9cExport button disabledUser must adjust filters to get results

A3: Export All vs. Current Page

StepConditionAlternative Action
20aUser selects "Current Page"System exports only visible 100 records
20bCSV contains only current page dataFilename includes "_Page3" suffix

6.4.6 Exception Handling

ExceptionCauseSystem BehaviorRecovery Action
EX-01Database query timeoutQuery exceeds 30-second limitDisplay error suggesting to reduce date range or add more filters
EX-02Browser memory exceeded during exportLarge dataset exhausts available memorySplit export into multiple files (by date range)
EX-03Network interruption during data fetchConnection lost mid-queryRetry with exponential backoff (3 attempts)
EX-04Invalid date rangeStart date after end dateClient-side validation prevents submission

6.4.7 Business Rules

Rule IDBusiness Rule
BR-01Query date range cannot exceed 365 days
BR-02Maximum 100,000 records per export (split into multiple files if needed)
BR-03Exported data retains full precision (no rounding)
BR-04All data exports are logged for audit compliance
BR-05Users can only query data for mine sites they have access to (RLS enforced)

6.4.8 Performance Requirements

MetricTargetMeasurement
Initial query execution< 3 secondsFor typical 100-record page
Pagination response time< 1 secondFetching next page
CSV generation (10K records)< 5 secondsData fetch + conversion + download
Chart rendering< 2 secondsLine chart with 1,000 data points

6.5 Scenario: Weekly Report Creation

6.5.1 Scenario Overview

Description: A field supervisor creates and submits a structured weekly report documenting site observations, flow meter usage, dashboard updates, and other operational activities.

Primary Actor: Field Supervisor / Site Manager

Supporting Actors:

  • Report Auto-Save Service
  • Validation Service
  • Notification Service

Business Goal: Capture structured field data for operational tracking, compliance documentation, and stakeholder communication.

6.5.2 Preconditions

PreconditionDescription
PRE-01User is authenticated with valid session
PRE-02User has field supervisor or manager role
PRE-03Week to be reported has ended (report period end date <= today)
PRE-04User has access to relevant mine sites and equipment data

6.5.3 Main Success Flow

Phase 1: Report Initiation

StepActorActionSystem Response
1UserNavigates to Weekly Reports page (/weekly-reports)System displays report dashboard
2SystemFetches user's existing reportsDisplays list of draft and completed reports
3UserClicks "Create New Report" buttonSystem displays week selection modal
4UserSelects report period end date (Saturday)System calculates period start (7 days prior)
5SystemChecks for duplicate reportsValidates no completed report exists for this week
6SystemCreates report record in weekly_reports tableStatus='draft', initializes empty JSONB sections
7SystemRedirects to report editorDisplays 8-section form interface

Phase 2: Section 1 - Site Observations

StepActorActionSystem Response
8UserClicks "Add Observation" in Section 1System displays observation entry form
9UserSelects site from dropdownSystem loads site details
10UserEnters: Ranger ID, Asset Location, Status, Issue, ActionSystem validates required fields
11UserClicks "Save Observation"System adds entry to site_observations JSONB array
12SystemAuto-saves report (debounced 2 seconds)Updates weekly_reports.updated_at
13UserAdds 3 more observations (repeat steps 8-12)Each saved to JSONB array

Phase 3: Section 2 - Flow Meter Usage

StepActorActionSystem Response
14UserNavigates to Section 2 tabSystem displays flow meter usage form
15UserClicks "Add Usage Record"System displays usage entry form
16UserSelects site, enters usage dates, volume used, notesSystem validates numeric fields
17UserClicks "Save Record"System adds entry to flow_meter_usage JSONB array
18SystemAuto-saves reportUpdates database with new data

Phase 4: Sections 3-8 (Continued Entry)

StepActorActionSystem Response
19UserCompletes Section 3: Dashboard UpdatesAdds 2 update entries (category, description)
20UserCompletes Section 4: Vendor ActivitiesAdds 1 vendor activity (name, type, description)
21UserCompletes Section 5: Water Truck TestingFills structured form (sites, hardware, test_summary)
22UserCompletes Section 6: Hardware & InstallationsAdds 2 installation records (type, location, details)
23UserCompletes Section 7: Admin & ReportingAdds travel and reporting entries
24UserCompletes Section 8: Other TasksAdds site support, stakeholder, internal task entries
25SystemAuto-saves after each sectionIncrementally updates weekly_reports record

Phase 5: Validation and Submission

StepActorActionSystem Response
26UserClicks "Review Report" buttonSystem performs validation checks
27SystemValidates all required sections have dataChecks at least 1 entry in critical sections
28SystemDisplays validation summaryShows ✓ Complete or ⚠ Missing for each section
29UserReviews summary, clicks "Submit Report"System displays confirmation modal
30UserConfirms submissionSystem updates status='completed'
31SystemTimestamps completionSets updated_at to current timestamp
32SystemLogs activity in activity_logsAction: 'weekly_report_submitted'
33SystemSends notification to managerEmail with report summary and link
34SystemRedirects to report list pageDisplays success message: "Weekly report submitted!"

Phase 6: Export to DOCX

StepActorActionSystem Response
35UserClicks "Export to Word" buttonSystem retrieves completed report data
36SystemGenerates DOCX using docx libraryCreates formatted document with 8 sections
37SystemIncludes tables, lists, formattingProfessional report layout
38SystemGenerates blob and triggers downloadFilename: "WeeklyReport_{date}_{user}.docx"
39SystemLogs export activityAction: 'weekly_report_exported'

6.5.4 Postconditions

PostconditionDescription
POST-01Weekly report record exists with status='completed'
POST-02All 8 sections populated with structured JSONB data
POST-03Manager notified of report submission
POST-04Activity logged for audit trail
POST-05Report available for export to DOCX
POST-06Draft report cannot be edited after submission (read-only)

6.5.5 Alternative Flows

A1: Save as Draft

StepConditionAlternative Action
26aUser clicks "Save as Draft" instead of SubmitSystem saves current state, status remains 'draft'
26bUser can exit and return laterReport appears in "Draft Reports" section
26cUser clicks "Continue Editing" on draftSystem loads report editor with saved data

A2: Duplicate Week Detected

StepConditionAlternative Action
5aCompleted report exists for selected weekSystem displays warning: "Report already exists for this week"
5bSystem offers options"View existing report" or "Create revision"
5cUser selects "Create revision"System creates new report with version number incremented

A3: Auto-Save Failure

StepConditionAlternative Action
12aNetwork connection lost during auto-saveSystem displays warning banner: "Offline - changes not saved"
12bSystem stores changes in localStorageTemporary local backup
12cConnection restoredSystem syncs local changes to database, displays "Synced" confirmation

A4: Incomplete Sections at Submission

StepConditionAlternative Action
27aCritical sections (1, 2) are emptySystem displays error: "Sections 1 and 2 are required"
27bSubmit button remains disabledUser must add at least one entry to each required section
27cNon-critical sections emptySystem allows submission with warning: "Section 4 is empty"

6.5.6 Exception Handling

ExceptionCauseSystem BehaviorRecovery Action
EX-01Browser crash during editingChanges lost if not auto-saved recentlylocalStorage backup attempts recovery on restart
EX-02Database update conflictTwo users editing same report simultaneouslyLast write wins; system warns of potential conflict
EX-03JSONB data corruptionMalformed JSON in JSONB fieldValidation catches error; system rejects save, logs issue
EX-04DOCX generation failuredocx library throws exceptionSystem displays error, offers CSV export alternative

6.5.7 Business Rules

Rule IDBusiness Rule
BR-01Weekly reports must cover a 7-day period ending on Saturday
BR-02Only one completed report allowed per week per user
BR-03Reports can be saved as draft unlimited times before submission
BR-04Completed reports are read-only; revisions create new report record
BR-05Auto-save occurs every 2 seconds after user input (debounced)
BR-06Sections 1 and 2 (Site Observations, Flow Meter Usage) are mandatory
BR-07Managers receive notification within 5 minutes of report submission

6.5.8 Data Structure Specification

Section 1: Site Observations (JSONB Array)

json
[
  {
    "site_name": "Mine Site A",
    "ranger_id": "R-123",
    "asset_location": "North Pit",
    "status": "Operational",
    "issue": "Dust monitor battery low",
    "action": "Replaced battery, verified operation"
  }
]

Section 2: Flow Meter Usage (JSONB Array)

json
[
  {
    "site_name": "Mine Site B",
    "usage_dates": "2025-12-01 to 2025-12-07",
    "volume_used": "125000",
    "notes": "Increased usage due to dry conditions"
  }
]

Section 5: Water Truck Testing (JSONB Object)

json
{
  "sites": ["Site A", "Site B"],
  "hardware": ["GPS Tracker", "Flow Sensor"],
  "test_summary": "All systems operational. GPS accuracy within 5m."
}

6.5.9 Performance Requirements

MetricTargetMeasurement
Form load time< 2 secondsTime to display editor interface
Auto-save latency< 500msTime from input to database update
Section navigation< 200msTab switching response time
DOCX generation time< 8 secondsFor typical 8-section report
Report list page load< 3 secondsDisplaying 50 recent reports

6.6 Scenario: Flow Meter Data Management

6.6.1 Scenario Overview

Description: A system administrator configures and manages flow meter data sources, including refreshing data from external scraper services and viewing water usage analytics.

Primary Actor: System Administrator / Operations Manager

Supporting Actors:

  • Flow Meter Scraper Edge Function
  • External Flow Meter API
  • Notification Service

Business Goal: Maintain accurate water usage tracking for mine site operations and compliance reporting.

6.6.2 Preconditions

PreconditionDescription
PRE-01User is authenticated with admin or manager role
PRE-02Flow meter devices configured in system
PRE-03Scraper edge function deployed to Supabase
PRE-04External flow meter API accessible
PRE-05Database table flow_meter_data exists and is accessible

6.6.3 Main Success Flow

Phase 1: View Current Data

StepActorActionSystem Response
1UserNavigates to Flow Meters page (/flow-meters)System displays flow meter dashboard
2SystemFetches latest flow meter readings from databaseQueries flow_meter_data table
3SystemDisplays data in table viewColumns: Site, Meter ID, Last Reading, Volume (L), Timestamp, Status
4SystemShows last refresh timestamp"Last updated: 2 hours ago"
5SystemRenders usage chartBar chart showing daily water usage by site
6UserFilters by site or date rangeSystem updates table and chart

Phase 2: Manual Data Refresh

StepActorActionSystem Response
7UserClicks "Refresh Data" buttonSystem displays confirmation modal
8UserConfirms refresh actionSystem disables button, shows spinner
9SystemInvokes Supabase Edge Function trigger-flow-meter-scraperHTTP POST to function endpoint
10Edge FunctionValidates authenticationChecks service role key
11Edge FunctionCalls external flow meter APIFetches latest readings for all devices
12External APIReturns JSON with meter readingsExample: [{meter_id, volume, timestamp}]
13Edge FunctionParses and validates response dataChecks data types, required fields
14Edge FunctionBatch inserts data into flow_meter_data tableUses upsert to handle duplicates
15Edge FunctionReturns success responseHTTP 200 with summary: "45 records updated"
16SystemDisplays success notification"Flow meter data refreshed: 45 records updated"
17SystemReloads dashboard dataDisplays updated readings and chart
18SystemUpdates last refresh timestamp"Last updated: Just now"
19SystemLogs activity in activity_logsAction: 'flow_meter_refresh', includes record count

Phase 3: Configure Flow Meter Settings

StepActorActionSystem Response
20UserClicks "Settings" buttonSystem displays configuration modal
21UserViews current settingsAPI endpoint, refresh frequency, timeout settings
22UserUpdates refresh frequency (from 6 hours to 4 hours)System validates input
23UserClicks "Save Settings"System updates configuration in database
24SystemValidates new settingsChecks frequency is between 1-24 hours
25SystemUpdates system configurationStores in configuration table
26SystemSchedules next automatic refreshCalculates next run time based on new frequency
27SystemDisplays confirmation"Settings updated. Next refresh at 14:00"

Phase 4: View Usage Analytics

StepActorActionSystem Response
28UserNavigates to Analytics tabSystem displays usage analytics dashboard
29SystemAggregates usage data by site and dateSQL queries with GROUP BY clauses
30SystemDisplays KPI cardsTotal usage (week), Average daily, Peak day, Number of sites
31SystemRenders trend chartLine chart showing 30-day usage trend
32SystemDisplays site comparison chartHorizontal bar chart: usage by site
33UserSelects date range "Last 7 days"System updates all charts and KPIs
34UserClicks "Export Report"System generates CSV with usage data

Phase 5: Handle Anomalies

StepActorActionSystem Response
35SystemDetects anomaly (usage spike > 200% of average)Triggers anomaly detection algorithm
36SystemCreates alert recordStores in flow_meter_alerts table
37SystemDisplays alert banner on dashboard"Alert: Site B usage 250% above normal"
38UserClicks "View Details" on alertSystem displays alert detail modal
39SystemShows anomaly analysisChart comparing current vs historical usage
40UserMarks alert as "Investigated" with notesSystem updates alert status
41SystemLogs investigationStores notes and status change

6.6.4 Postconditions

PostconditionDescription
POST-01Flow meter data in database reflects latest readings from external API
POST-02Dashboard displays up-to-date usage information
POST-03Last refresh timestamp updated
POST-04Activity logged for audit trail
POST-05Alerts generated for usage anomalies
POST-06Configuration changes applied for next scheduled refresh

6.6.5 Alternative Flows

A1: Scraper Function Timeout

StepConditionAlternative Action
10aExternal API does not respond within 30 secondsEdge function times out
10bEdge function returns error responseHTTP 504 Gateway Timeout
10cSystem displays error notification"Data refresh failed: API timeout. Please try again."
10dSystem logs error with detailsIncludes timestamp, API endpoint, timeout duration
10eUser waits and retriesSystem attempts refresh again

A2: Partial Data Fetch Failure

StepConditionAlternative Action
12aExternal API returns data for only 30 of 45 metersSome meters offline or unresponsive
13aEdge function validates partial dataIdentifies missing meters
14aEdge function updates available dataInserts 30 successful readings
15aEdge function returns partial successHTTP 207 Multi-Status with details
16aSystem displays warning notification"Partial refresh: 30 of 45 meters updated. 15 meters unavailable."
16bSystem marks unavailable meters with warning iconDashboard shows status: "Offline"

A3: Scheduled Auto-Refresh

StepConditionAlternative Action
-Scheduled refresh time reached (e.g., every 4 hours)System triggers auto-refresh
-System invokes edge function automaticallyNo user interaction required
-Edge function executes refresh processSame as steps 10-15
-On success, system logs refreshAction: 'flow_meter_auto_refresh'
-On failure, system retries once after 5 minutesIf still fails, alerts admin

A4: Invalid Configuration

StepConditionAlternative Action
24aUser enters refresh frequency < 1 hourValidation fails
24bSystem displays inline error"Refresh frequency must be between 1 and 24 hours"
24cSave button remains disabledUser must correct input

A5: No New Data Available

StepConditionAlternative Action
12aExternal API returns same data as last fetchNo changes detected
14aEdge function skips database updateNo upsert operations performed
15aEdge function returns "no changes" responseHTTP 200 with message: "0 records updated"
16aSystem displays info notification"Data refresh complete: No new data available"

6.6.6 Exception Handling

ExceptionCauseSystem BehaviorRecovery Action
EX-01External API authentication failureAPI returns 401 UnauthorizedSystem displays error, alerts admin to check API credentials
EX-02Database connection failure during insertCannot write to flow_meter_data tableEdge function retries 3 times, then logs error and alerts admin
EX-03Malformed JSON from external APIAPI returns invalid data structureEdge function logs error with raw response, returns error to frontend
EX-04Edge function deployment failureFunction not accessible or misconfiguredSystem displays: "Service temporarily unavailable. Contact support."
EX-05Rate limit exceeded on external APIAPI returns 429 Too Many RequestsSystem waits for rate limit reset, then retries automatically

6.6.7 Business Rules

Rule IDBusiness Rule
BR-01Flow meter data is automatically refreshed every 4 hours (configurable)
BR-02Manual refresh can only be triggered once per 15 minutes to avoid API rate limits
BR-03Historical flow meter data retained for 2 years, then archived
BR-04Anomalies are detected if usage exceeds 200% of 30-day rolling average
BR-05Only admin and manager roles can trigger manual refresh or change settings
BR-06Failed refresh attempts are automatically retried once after 5 minutes

6.6.8 External API Specification

Endpoint: https://api.flowmeter-provider.com/v1/readings

Authentication: Bearer token (stored in Supabase secrets)

Request Example:

http
GET /v1/readings?site_id=all&since=2025-12-01T00:00:00Z
Authorization: Bearer {token}

Response Example:

json
{
  "status": "success",
  "data": [
    {
      "meter_id": "FM-001",
      "site_id": "SITE-A",
      "volume_liters": 125000,
      "timestamp": "2025-12-02T14:30:00Z",
      "status": "online"
    },
    {
      "meter_id": "FM-002",
      "site_id": "SITE-B",
      "volume_liters": 98500,
      "timestamp": "2025-12-02T14:30:00Z",
      "status": "online"
    }
  ],
  "total_meters": 45,
  "fetched_at": "2025-12-02T14:30:15Z"
}

6.6.9 Performance Requirements

MetricTargetMeasurement
Dashboard load time< 3 secondsTime to display table and charts
Manual refresh duration< 15 secondsEdge function execution + UI update
External API response time< 10 secondsAPI call latency
Chart rendering time< 2 secondsFor 30 days of data across 10 sites
Anomaly detection latency< 5 secondsPost-refresh analysis

This completes Section 6: Operational Scenarios with comprehensive coverage of all six key user workflows.


7. Detailed Design

This section provides comprehensive detailed design specifications for all system components, including hardware infrastructure, software architecture, security implementation, performance optimization, and internal communications.

7.1 Hardware Detailed Design

7.1.1 Infrastructure Overview

The Dustac Environmental Monitoring Dashboard utilizes a cloud-native infrastructure with no on-premises hardware requirements. All hardware resources are provisioned and managed by third-party cloud providers.

Infrastructure Architecture:

┌─────────────────────────────────────────────────────────────┐
│                    Client Devices                            │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
│  │ Desktop  │  │  Laptop  │  │  Tablet  │  │  Mobile  │   │
│  │ Windows  │  │   macOS  │  │   iOS    │  │  Android │   │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └────┬─────┘   │
└───────┼─────────────┼─────────────┼─────────────┼──────────┘
        │             │             │             │
        └─────────────┴─────────────┴─────────────┘
                        │ HTTPS

┌─────────────────────────────────────────────────────────────┐
│            Cloudflare Global Network (CDN)                   │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Edge Locations: 300+ cities worldwide              │  │
│  │  - DDoS Protection (Unmetered)                       │  │
│  │  - Web Application Firewall (WAF)                    │  │
│  │  - SSL/TLS Termination                               │  │
│  │  - Static Asset Caching                              │  │
│  │  - Bot Management                                     │  │
│  └──────────────────────────────────────────────────────┘  │
└────────────────────────┬────────────────────────────────────┘
                         │ HTTPS

┌─────────────────────────────────────────────────────────────┐
│              Supabase Infrastructure (AWS)                   │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Compute (Deno Edge Functions)                       │  │
│  │  - Runtime: Deno 1.x                                 │  │
│  │  - Auto-scaling: 0-1000 concurrent executions       │  │
│  │  - Memory: 512MB per function                        │  │
│  │  - Timeout: 60 seconds                               │  │
│  └──────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Database (AWS RDS - PostgreSQL 15.x)                │  │
│  │  - Instance Type: db.t4g.medium                      │  │
│  │  - vCPU: 2 cores (ARM Graviton2)                     │  │
│  │  - Memory: 4 GB                                       │  │
│  │  - Storage: 100 GB SSD (gp3)                         │  │
│  │  - IOPS: 3000 baseline                               │  │
│  │  - Backup: Automated daily snapshots (7-day retain) │  │
│  └──────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────┐  │
│  │  Storage (AWS S3)                                     │  │
│  │  - Bucket: supabase-storage-[project-id]            │  │
│  │  - Redundancy: S3 Standard (99.999999999% durable)  │  │
│  │  - Versioning: Enabled                               │  │
│  │  - Lifecycle: 90-day retention → Glacier            │  │
│  └──────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

7.1.2 Client Device Requirements

Minimum Specifications:

ComponentMinimum RequirementRecommended
ProcessorDual-core 1.6 GHzQuad-core 2.4 GHz+
RAM4 GB8 GB+
Storage500 MB free space2 GB+
Display1280x720 resolution1920x1080+
Network5 Mbps download, 1 Mbps upload25 Mbps+ download, 5 Mbps+ upload
BrowserChrome 90+, Firefox 88+, Safari 14+, Edge 90+Latest stable versions
Operating SystemWindows 10, macOS 10.15, iOS 13, Android 10Latest OS versions

Browser Requirements:

  • JavaScript enabled
  • Cookies and local storage enabled
  • HTML5 Canvas API support
  • WebSocket support (for real-time features)
  • Minimum viewport: 320px width (mobile responsive)

7.1.3 Cloud Infrastructure Specifications

Cloudflare Pages (Frontend Hosting):

ResourceSpecification
ComputeServerless (V8 Isolates)
Memory128 MB per request
Execution Time30 seconds per request
Concurrent RequestsUnlimited (auto-scaling)
BandwidthUnlimited
SSL/TLSAutomatic HTTPS, TLS 1.3
HTTP VersionHTTP/2, HTTP/3 (QUIC) support
Cache TTL4 hours (static assets)
Geographic Distribution300+ edge locations

Supabase Database (AWS RDS PostgreSQL):

ResourceSpecificationScaling Plan
Instance Classdb.t4g.mediumUpgrade to db.m6g.large at 10K users
CPU2 vCPU (ARM Graviton2)2 → 4 vCPU at scale
Memory4 GB4 → 16 GB at scale
Storage100 GB SSD (gp3)Auto-scaling enabled (max 1 TB)
IOPS3000 baseline, 16000 burstScales with storage
Max Connections100 concurrent100 → 500 with connection pooling
Backup Retention7 days automated30 days for production
Multi-AZDisabled (dev), Enabled (prod)HA for production workloads
EncryptionAES-256 at restAWS KMS managed keys

Supabase Storage (AWS S3):

ResourceSpecificationLimits
Storage ClassS3 StandardHot data access
Redundancy99.999999999% durabilityMulti-AZ replication
Availability99.99% SLA
Max Object Size5 TBPractical limit: 50 MB per file
Throughput3,500 PUT/COPY/POST/DELETE per prefix per secondAuto-scaling
Throughput5,500 GET/HEAD per prefix per secondAuto-scaling
EncryptionAES-256 (SSE-S3)Server-side encryption
VersioningEnabled90-day retention
Lifecycle Policy90 days → GlacierArchive old uploads

Supabase Edge Functions (Deno Runtime):

ResourceSpecification
RuntimeDeno 1.38+ (TypeScript native)
Memory512 MB per execution
Timeout60 seconds
Cold Start~100-300ms
Warm Execution~5-20ms
Concurrent Executions1000 per function
Environment VariablesStored in Supabase Secrets (encrypted)
External API CallsAllowed (fetch API)

7.1.4 Network Architecture

Network Topology:

Internet

   ├─── Cloudflare Edge (Layer 7)
   │     ├─── DDoS Protection (Unmetered)
   │     ├─── WAF Rules (OWASP Core Ruleset)
   │     ├─── Rate Limiting (100 req/min per IP)
   │     └─── SSL/TLS Termination (TLS 1.3)

   ├─── Cloudflare Pages (Static Assets)
   │     ├─── React SPA (Vite Build)
   │     ├─── Cache-Control: public, max-age=14400
   │     └─── Brotli Compression

   └─── Supabase API Gateway (AWS API Gateway)
         ├─── Authentication (JWT Verification)
         ├─── Authorization (RLS Enforcement)
         ├─── Rate Limiting (1000 req/min per user)

         ├─── Database Connections (PostgreSQL)
         │     ├─── Connection Pool (PgBouncer)
         │     ├─── SSL/TLS Required (sslmode=require)
         │     └─── Private Subnet (VPC Isolated)

         ├─── Storage API (S3 Presigned URLs)
         │     ├─── Time-limited URLs (15 min expiry)
         │     ├─── CORS Headers
         │     └─── Private Buckets

         └─── Edge Functions (Deno Workers)
               ├─── HTTP/HTTPS Only
               ├─── JWT Auth Required
               └─── Service Role Key for Internal Calls

Network Security:

LayerSecurity MeasureImplementation
DNSDNSSECCloudflare DNS with DNSSEC validation
TransportTLS 1.3Mandatory HTTPS, HSTS enabled (max-age=31536000)
ApplicationWAFCloudflare WAF with OWASP rules
DDoSLayer 3/4/7 ProtectionCloudflare unmetered DDoS protection
Rate LimitingPer-IP and Per-User100 req/min (IP), 1000 req/min (user)
FirewallGeo-blockingOptional: Allow-list specific countries

7.1.5 Disaster Recovery and Business Continuity

Backup Strategy:

ComponentBackup FrequencyRetentionRecovery Time Objective (RTO)Recovery Point Objective (RPO)
DatabaseDaily automated snapshots7 days (dev), 30 days (prod)< 4 hours< 24 hours
DatabasePoint-in-time recovery7 days< 1 hour< 15 minutes
Storage (S3)Continuous (versioning)90 days< 1 hourReal-time
Application CodeGit commitsIndefinite< 30 minutes< 1 hour
ConfigurationInfrastructure-as-CodeVersion controlled< 30 minutesReal-time

High Availability Configuration:

ComponentHA StrategyFailover Method
Frontend (Cloudflare)Multi-region edge cachingAutomatic (anycast routing)
Database (RDS)Multi-AZ deployment (prod)Automatic failover (60-120 seconds)
Storage (S3)Multi-AZ replicationAutomatic (transparent to application)
Edge FunctionsMulti-region deploymentAutomatic (Supabase routing)

Disaster Recovery Plan:

  1. Database Failure:

    • Detection: Automated health checks every 30 seconds
    • Alerting: PagerDuty notification to on-call engineer
    • Action: Automatic failover to standby replica (Multi-AZ)
    • Recovery Time: 60-120 seconds
  2. Storage Failure:

    • Detection: S3 service health monitoring
    • Action: S3 automatically handles failover (transparent)
    • Recovery Time: < 1 minute
  3. Complete Region Failure:

    • Detection: Multi-region health checks
    • Action: Manual DNS update to secondary region
    • Recovery Time: 2-4 hours (manual intervention required)
  4. Data Corruption:

    • Detection: Application monitoring, user reports
    • Action: Restore from point-in-time backup
    • Recovery Time: 1-4 hours depending on data volume

7.2 Software Detailed Design

7.2.1 Frontend Component Architecture

Component Hierarchy:

App (Root)

├── Routes (React Router v7)
│   ├── PublicLayout
│   │   ├── LandingPage
│   │   ├── LoginPage
│   │   └── RegisterPage
│   │
│   └── ProtectedLayout (requires auth)
│       ├── Header
│       │   ├── Logo
│       │   ├── MainNavigation
│       │   ├── UserMenu
│       │   └── NotificationBell
│       │
│       ├── Sidebar
│       │   ├── NavigationMenu
│       │   ├── QuickActions
│       │   └── UserProfile
│       │
│       └── MainContent (Outlet)
│           ├── DashboardPage
│           │   ├── KPICards (4)
│           │   ├── UsageChart (Nivo Line)
│           │   ├── SiteStatusTable
│           │   └── RecentActivityFeed
│           │
│           ├── UploadPage
│           │   ├── UploadWizard
│           │   │   ├── Step1_FileSelection
│           │   │   ├── Step2_SiteConfiguration
│           │   │   ├── Step3_Validation
│           │   │   └── Step4_Upload
│           │   ├── DropZone (react-dropzone)
│           │   ├── FileList
│           │   ├── ValidationResults
│           │   └── ProgressBar
│           │
│           ├── ReportsPage
│           │   ├── ReportGenerator
│           │   │   ├── DateRangePicker
│           │   │   ├── DeviceMultiSelect
│           │   │   ├── TemplateSelector
│           │   │   └── GenerateButton
│           │   ├── ReportPreview
│           │   │   ├── DataSummaryTable
│           │   │   └── PreviewChart (Recharts)
│           │   ├── ReportList
│           │   └── ReportCard (per report)
│           │
│           ├── DustLevelsPage
│           │   ├── FilterPanel
│           │   │   ├── SiteFilter
│           │   │   ├── DeviceFilter
│           │   │   ├── DateRangeFilter
│           │   │   └── ThresholdFilter
│           │   ├── DataGrid (100 rows paginated)
│           │   ├── PaginationControls
│           │   ├── SummaryStatistics
│           │   ├── TrendChart (Recharts Line)
│           │   └── ExportButton
│           │
│           ├── WeeklyReportsPage
│           │   ├── ReportListView
│           │   ├── ReportEditor
│           │   │   ├── SectionTabs (8 sections)
│           │   │   ├── Section1_SiteObservations
│           │   │   │   └── ObservationForm (repeatable)
│           │   │   ├── Section2_FlowMeterUsage
│           │   │   │   └── UsageForm (repeatable)
│           │   │   ├── Section3_DashboardUpdates
│           │   │   ├── Section4_VendorActivities
│           │   │   ├── Section5_WaterTruckTesting
│           │   │   ├── Section6_HardwareInstallations
│           │   │   ├── Section7_AdminReporting
│           │   │   └── Section8_OtherTasks
│           │   ├── AutoSaveIndicator
│           │   ├── ValidationSummary
│           │   └── SubmitButton
│           │
│           └── FlowMetersPage
│               ├── FlowMeterDashboard
│               │   ├── LastRefreshTimestamp
│               │   ├── RefreshButton
│               │   ├── MeterDataTable
│               │   └── UsageChart (Nivo Bar)
│               ├── AnalyticsTab
│               │   ├── KPICards
│               │   ├── TrendChart (30 days)
│               │   └── SiteComparisonChart
│               ├── AlertsTab
│               │   ├── AlertBanner
│               │   └── AlertList
│               └── SettingsModal

└── GlobalProviders
    ├── AuthProvider (Supabase Auth)
    ├── QueryClientProvider (React Query - future)
    └── ToastProvider (react-hot-toast)

7.2.2 Key Component Specifications

Component: UploadWizard

Responsibility: Orchestrate multi-step CSV file upload process with validation

Props Interface:

typescript
interface UploadWizardProps {
  onUploadComplete: (uploadId: string) => void;
  onCancel: () => void;
  maxFiles?: number; // default: 10
  maxFileSize?: number; // default: 50MB
}

State Management:

typescript
interface UploadWizardState {
  currentStep: 1 | 2 | 3 | 4;
  selectedFiles: File[];
  siteConfig: {
    siteId: string;
    siteName: string;
    uploadType: 'daily' | 'weekly' | 'monthly';
    periodStart: Date;
    periodEnd: Date;
  };
  validationResults: ValidationResult[];
  uploadProgress: Map<string, number>; // filename -> progress %
  errors: UploadError[];
}

Key Methods:

typescript
handleFileSelection(files: File[]): void
validateFiles(): Promise<ValidationResult[]>
configureSite(config: SiteConfig): void
executeUpload(): Promise<UploadResult>
handleUploadProgress(filename: string, progress: number): void

Component Lifecycle:

  1. Mount: Initialize wizard state, fetch user's mine sites
  2. Step 1: User selects files → Validate file types and sizes
  3. Step 2: User configures site and date range → Store configuration
  4. Step 3: System validates CSV structure → Display validation results
  5. Step 4: Execute upload → Show progress bars → Complete
  6. Unmount: Clean up file references

Error Handling:

  • Client-side validation errors: Display inline with retry option
  • Network errors: Implement exponential backoff retry (3 attempts)
  • Server errors: Display user-friendly message with support contact

Component: ReportGenerator

Responsibility: Configure and generate PDF reports from dust monitoring data

Props Interface:

typescript
interface ReportGeneratorProps {
  onReportGenerated: (reportId: string) => void;
  onCancel: () => void;
  preselectedSite?: string;
  preselectedDateRange?: { start: Date; end: Date };
}

State Management:

typescript
interface ReportGeneratorState {
  selectedSite: string | null;
  selectedDevices: string[];
  dateRange: { start: Date; end: Date };
  template: 'standard' | 'detailed' | 'summary';
  dataPreview: {
    recordCount: number;
    dateRange: { min: Date; max: Date };
    statistics: {
      avgPM25: number;
      maxPM25: number;
      avgPM10: number;
      maxPM10: number;
    };
  } | null;
  isGenerating: boolean;
  generationProgress: number; // 0-100
  currentPage: number;
  totalPages: number;
}

Key Methods:

typescript
fetchDataPreview(): Promise<DataPreview>
validateSelections(): boolean
generateReport(): Promise<ReportResult>
captureReportComponent(component: ReactElement): Promise<ImageBlob>
assemblePDF(images: ImageBlob[]): Promise<PDFBlob>
uploadToStorage(pdf: PDFBlob): Promise<StoragePath>
saveReportMetadata(metadata: ReportMetadata): Promise<ReportId>

PDF Generation Algorithm:

typescript
async function generateReport(): Promise<ReportResult> {
  // Phase 1: Fetch data
  const data = await fetchMeasurements(filters);

  // Phase 2: Render React components (hidden)
  const components = [
    <CoverPage {...data} />,
    <SummaryPage {...data} />,
    <PM25ChartPage data={data.pm25} />,
    <PM10ChartPage data={data.pm10} />,
    <DataTablePage data={data.measurements} />
  ];

  // Phase 3: Capture as images
  const images = await Promise.all(
    components.map(comp => html2canvas(comp, {
      scale: 2, // 2x for high quality
      useCORS: true,
      logging: false
    }))
  );

  // Phase 4: Assemble PDF
  const pdf = new jsPDF('portrait', 'mm', 'a4');
  images.forEach((img, index) => {
    if (index > 0) pdf.addPage();
    pdf.addImage(img, 'PNG', 0, 0, 210, 297); // A4 dimensions
  });

  // Phase 5: Save
  const blob = pdf.output('blob');
  const storagePath = await uploadToStorage(blob);
  const reportId = await saveMetadata({
    storagePath,
    fileSize: blob.size,
    pageCount: images.length,
    ...filters
  });

  return { reportId, storagePath, fileSize: blob.size };
}

Performance Optimization:

  • Use React.memo for chart components to avoid re-renders
  • Render charts with fixed dimensions to prevent layout thrashing
  • Process images in batches of 3 to manage memory usage
  • Use Web Workers for heavy computation (future enhancement)

7.2.3 State Management Architecture

Zustand Store Structure:

typescript
// stores/authStore.ts
interface AuthState {
  user: User | null;
  session: Session | null;
  isAuthenticated: boolean;
  isLoading: boolean;

  // Actions
  setUser: (user: User) => void;
  setSession: (session: Session) => void;
  signOut: () => Promise<void>;
  refreshSession: () => Promise<void>;
}

// stores/uploadStore.ts
interface UploadState {
  currentUpload: Upload | null;
  uploads: Upload[];
  isUploading: boolean;

  // Actions
  createUpload: (files: File[]) => Promise<string>;
  updateUploadProgress: (uploadId: string, progress: number) => void;
  fetchUploads: () => Promise<void>;
  cancelUpload: (uploadId: string) => void;
}

// stores/filterStore.ts
interface FilterState {
  dustLevels: {
    sites: string[];
    devices: string[];
    dateRange: { start: Date; end: Date };
    pm25Range: { min: number; max: number };
  };

  // Actions
  updateFilters: (filters: Partial<FilterState['dustLevels']>) => void;
  resetFilters: () => void;
}

Store Usage Pattern:

typescript
// In component
import { useAuthStore } from '@/stores/authStore';

function ProtectedPage() {
  const { user, isAuthenticated, signOut } = useAuthStore();

  if (!isAuthenticated) {
    return <Navigate to="/login" />;
  }

  return (
    <div>
      <h1>Welcome, {user.full_name}</h1>
      <button onClick={signOut}>Sign Out</button>
    </div>
  );
}

7.2.4 Backend Service Architecture

Supabase Edge Functions:

Function: trigger-flow-meter-scraper

Purpose: Fetch latest flow meter readings from external API and import to database

Invocation: HTTP POST or scheduled cron job

Implementation:

typescript
// supabase/functions/trigger-flow-meter-scraper/index.ts
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'

interface FlowMeterReading {
  meter_id: string;
  site_id: string;
  volume_liters: number;
  timestamp: string;
  status: 'online' | 'offline';
}

serve(async (req) => {
  try {
    // 1. Authenticate
    const authHeader = req.headers.get('Authorization');
    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return new Response('Unauthorized', { status: 401 });
    }

    // 2. Fetch from external API
    const apiUrl = Deno.env.get('FLOW_METER_API_URL')!;
    const apiToken = Deno.env.get('FLOW_METER_API_TOKEN')!;

    const response = await fetch(`${apiUrl}/readings?site_id=all`, {
      headers: {
        'Authorization': `Bearer ${apiToken}`,
        'Content-Type': 'application/json'
      },
      signal: AbortSignal.timeout(30000) // 30 second timeout
    });

    if (!response.ok) {
      throw new Error(`API error: ${response.status}`);
    }

    const { data } = await response.json();
    const readings: FlowMeterReading[] = data;

    // 3. Validate data
    const validReadings = readings.filter(r =>
      r.meter_id && r.volume_liters >= 0 && r.timestamp
    );

    // 4. Upsert to database
    const supabase = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
    );

    const { data: inserted, error } = await supabase
      .from('flow_meter_data')
      .upsert(
        validReadings.map(r => ({
          meter_id: r.meter_id,
          site_id: r.site_id,
          volume_liters: r.volume_liters,
          timestamp: r.timestamp,
          status: r.status,
          updated_at: new Date().toISOString()
        })),
        { onConflict: 'meter_id,timestamp' }
      );

    if (error) throw error;

    // 5. Log activity
    await supabase
      .from('activity_logs')
      .insert({
        user_id: null,
        action: 'flow_meter_auto_refresh',
        resource_type: 'flow_meter',
        details: {
          records_fetched: readings.length,
          records_inserted: inserted?.length || 0,
          timestamp: new Date().toISOString()
        }
      });

    return new Response(
      JSON.stringify({
        success: true,
        records_updated: inserted?.length || 0,
        skipped: readings.length - validReadings.length
      }),
      { status: 200, headers: { 'Content-Type': 'application/json' } }
    );

  } catch (error) {
    console.error('Flow meter scraper error:', error);

    return new Response(
      JSON.stringify({ error: error.message }),
      { status: 500, headers: { 'Content-Type': 'application/json' } }
    );
  }
})

Deployment Configuration:

bash
# Deploy function
supabase functions deploy trigger-flow-meter-scraper

# Set secrets
supabase secrets set FLOW_METER_API_URL=https://api.flowmeter-provider.com/v1
supabase secrets set FLOW_METER_API_TOKEN=<secret-token>

# Configure cron schedule (every 4 hours)
# In Supabase Dashboard: Functions → trigger-flow-meter-scraper → Cron
# Schedule: 0 */4 * * * (every 4 hours)

7.3 Security Detailed Design

7.3.1 Authentication Implementation

Supabase Auth Configuration:

typescript
// lib/supabase.ts
import { createClient } from '@supabase/supabase-js';

const supabaseUrl = import.meta.env.VITE_SUPABASE_URL;
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY;

export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
  auth: {
    autoRefreshToken: true,
    persistSession: true,
    detectSessionInUrl: true,
    storage: window.localStorage, // or AsyncStorage for React Native
    flowType: 'pkce' // PKCE flow for enhanced security
  },
  global: {
    headers: {
      'X-Client-Info': 'dustac-dashboard@1.0.0'
    }
  }
});

JWT Token Structure:

json
{
  "aud": "authenticated",
  "exp": 1733184000,
  "iat": 1733180400,
  "iss": "https://hwogexspejrzvadfjmce.supabase.co/auth/v1",
  "sub": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "email": "user@example.com",
  "phone": "",
  "app_metadata": {
    "provider": "email",
    "providers": ["email"]
  },
  "user_metadata": {
    "full_name": "John Doe",
    "organization": "Mining Corp"
  },
  "role": "authenticated",
  "aal": "aal1",
  "amr": [
    {
      "method": "password",
      "timestamp": 1733180400
    }
  ],
  "session_id": "x9y8z7w6-v5u4-t3s2-r1q0-p9o8n7m6l5k4"
}

Password Policy Enforcement:

RequirementValidation RuleError Message
Minimum Length>= 12 characters"Password must be at least 12 characters long"
Uppercase>= 1 uppercase letter"Password must contain at least one uppercase letter"
Lowercase>= 1 lowercase letter"Password must contain at least one lowercase letter"
Digit>= 1 number"Password must contain at least one number"
Special Character>= 1 of !@#$%^&*()_+-=[]{};':"|,.<>/?"Password must contain at least one special character"
No Common PasswordsNot in HIBP database"Password is too common. Please choose a stronger password"

Implementation:

typescript
import zxcvbn from 'zxcvbn';

function validatePassword(password: string): ValidationResult {
  const errors: string[] = [];

  if (password.length < 12) {
    errors.push("Password must be at least 12 characters long");
  }

  if (!/[A-Z]/.test(password)) {
    errors.push("Password must contain at least one uppercase letter");
  }

  if (!/[a-z]/.test(password)) {
    errors.push("Password must contain at least one lowercase letter");
  }

  if (!/[0-9]/.test(password)) {
    errors.push("Password must contain at least one number");
  }

  if (!/[!@#$%^&*()_+\-=\[\]{}\\|;:'",.<>/?]/.test(password)) {
    errors.push("Password must contain at least one special character");
  }

  // Check password strength using zxcvbn
  const strength = zxcvbn(password);
  if (strength.score < 3) {
    errors.push(`Password is too weak. ${strength.feedback.suggestions.join(' ')}`);
  }

  return {
    isValid: errors.length === 0,
    errors,
    strength: strength.score
  };
}

7.3.2 Authorization and Row-Level Security (RLS)

RLS Policy Design Pattern:

sql
-- Pattern 1: User owns the resource directly
CREATE POLICY "Users can view own uploads"
  ON uploads FOR SELECT
  USING (auth.uid() = user_id);

-- Pattern 2: User owns resource through relationship
CREATE POLICY "Users can view own measurements"
  ON measurements FOR SELECT
  USING (
    EXISTS (
      SELECT 1 FROM uploads
      WHERE uploads.id = measurements.upload_id
      AND uploads.user_id = auth.uid()
    )
  );

-- Pattern 3: Role-based access (Admin sees all)
CREATE POLICY "Admins can view all uploads"
  ON uploads FOR ALL
  USING (
    EXISTS (
      SELECT 1 FROM user_profiles
      WHERE id = auth.uid()
      AND role = 'admin'
    )
  );

-- Pattern 4: Public read access (reports)
CREATE POLICY "All authenticated users can view reports"
  ON reports FOR SELECT
  USING (auth.uid() IS NOT NULL);

-- Pattern 5: Organization-level access
CREATE POLICY "Users can view organization data"
  ON uploads FOR SELECT
  USING (
    EXISTS (
      SELECT 1 FROM user_profiles up1
      JOIN user_profiles up2 ON up1.organization = up2.organization
      WHERE up1.id = auth.uid()
      AND up2.id = uploads.user_id
    )
  );

RLS Policy Testing:

sql
-- Test as specific user
SET LOCAL role = authenticated;
SET LOCAL request.jwt.claims = '{"sub": "user-uuid-here"}';

-- Verify user can only see their own uploads
SELECT * FROM uploads; -- Should return only user's uploads

-- Test as admin
SET LOCAL request.jwt.claims = '{"sub": "admin-uuid-here"}';
SELECT * FROM uploads; -- Should return all uploads

7.3.3 Data Encryption

Encryption at Rest:

Data TypeEncryption MethodKey Management
DatabaseAES-256 (RDS encryption)AWS KMS managed keys
File StorageAES-256 (S3 SSE-S3)AWS S3 managed keys
BackupsAES-256 (automated)AWS KMS managed keys
SecretsAES-256-GCMSupabase Vault

Encryption in Transit:

ConnectionProtocolCipher Suite
Client → CloudflareTLS 1.3TLS_AES_256_GCM_SHA384
Cloudflare → SupabaseTLS 1.2+TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Application → DatabaseTLS 1.2+ (sslmode=require)TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
Application → S3TLS 1.2+TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384

Sensitive Data Handling:

typescript
// Never log sensitive data
function uploadFile(file: File, authToken: string) {
  // ❌ BAD
  console.log('Uploading file with token:', authToken);

  // ✅ GOOD
  console.log('Uploading file:', file.name);

  // Use environment variables for secrets
  const apiKey = import.meta.env.VITE_API_KEY; // Client-side (public)
  const serviceKey = Deno.env.get('SERVICE_ROLE_KEY'); // Server-side (secret)
}

// Sanitize user input before logging
function logActivity(userInput: string) {
  const sanitized = userInput.replace(/[^\w\s-]/g, '');
  console.log('User input:', sanitized);
}

7.3.4 Input Validation and Sanitization

Client-Side Validation (Zod Schema):

typescript
import { z } from 'zod';

// Upload form validation
const uploadSchema = z.object({
  siteId: z.string().uuid('Invalid site ID'),
  siteName: z.string().min(1, 'Site name is required').max(255),
  uploadType: z.enum(['daily', 'weekly', 'monthly']),
  periodStart: z.date().max(new Date(), 'Start date cannot be in the future'),
  periodEnd: z.date(),
  files: z.array(
    z.instanceof(File)
      .refine(file => file.size <= 50 * 1024 * 1024, 'File size must be less than 50MB')
      .refine(file => file.name.endsWith('.csv'), 'Only CSV files are allowed')
  ).min(1, 'At least one file is required').max(10, 'Maximum 10 files allowed')
}).refine(
  data => data.periodEnd >= data.periodStart,
  { message: 'End date must be after start date', path: ['periodEnd'] }
);

// Usage
try {
  const validated = uploadSchema.parse(formData);
  // Proceed with upload
} catch (error) {
  if (error instanceof z.ZodError) {
    // Display validation errors
    error.errors.forEach(err => {
      console.error(`${err.path.join('.')}: ${err.message}`);
    });
  }
}

Server-Side Validation (PostgreSQL Constraints):

sql
-- Check constraints
ALTER TABLE uploads
ADD CONSTRAINT check_upload_dates
CHECK (period_end >= period_start);

ALTER TABLE measurements
ADD CONSTRAINT check_pm25_range
CHECK (massconcentration2p5 >= 0 AND massconcentration2p5 <= 10000);

-- Domain constraints
CREATE DOMAIN email_address AS TEXT
CHECK (VALUE ~ '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$');

ALTER TABLE user_profiles
ALTER COLUMN email TYPE email_address;

SQL Injection Prevention:

typescript
// ❌ BAD: String concatenation (vulnerable to SQL injection)
async function getUserByEmail(email: string) {
  const { data } = await supabase.rpc('get_user', {
    query: `SELECT * FROM users WHERE email = '${email}'`
  });
  return data;
}

// ✅ GOOD: Parameterized queries (Supabase automatically escapes)
async function getUserByEmail(email: string) {
  const { data } = await supabase
    .from('user_profiles')
    .select('*')
    .eq('email', email)
    .single();
  return data;
}

XSS Prevention:

typescript
// React automatically escapes JSX expressions
function UserProfile({ user }: { user: User }) {
  // ✅ SAFE: React escapes HTML entities
  return <h1>Welcome, {user.full_name}</h1>;

  // ❌ DANGEROUS: dangerouslySetInnerHTML bypasses escaping
  return <div dangerouslySetInnerHTML={{ __html: user.bio }} />;

  // ✅ SAFE: Sanitize HTML before rendering
  import DOMPurify from 'dompurify';
  const sanitizedBio = DOMPurify.sanitize(user.bio);
  return <div dangerouslySetInnerHTML={{ __html: sanitizedBio }} />;
}

7.3.5 Security Testing Requirements

Automated Security Testing:

Test TypeToolFrequencyThreshold
Dependency Vulnerability Scannpm audit, SnykEvery commit (CI/CD)0 high/critical vulnerabilities
Static Application Security Testing (SAST)ESLint security pluginsEvery commit0 errors
Secrets DetectionGitGuardianEvery commit0 secrets exposed
SQL Injection TestingSQLMap (penetration test)Monthly0 vulnerabilities
XSS TestingBurp Suite (penetration test)Monthly0 vulnerabilities

Manual Security Review Checklist:

  • [ ] All API endpoints require authentication
  • [ ] RLS policies tested for each table
  • [ ] File uploads validated for type and size
  • [ ] User input sanitized before database insertion
  • [ ] Sensitive data (passwords, tokens) never logged
  • [ ] HTTPS enforced (HSTS header set)
  • [ ] CORS configured with specific origins (no wildcard)
  • [ ] Rate limiting enabled for all public endpoints
  • [ ] Error messages don't leak sensitive information
  • [ ] Session timeout configured (1 hour)

7.4 Performance Detailed Design

7.4.1 Frontend Performance Optimization

Code Splitting Strategy:

typescript
// routes/Routes.tsx
import { lazy, Suspense } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

// Eager load (included in main bundle)
import LoginPage from '@/pages/LoginPage';
import RegisterPage from '@/pages/RegisterPage';

// Lazy load (separate chunks)
const DashboardPage = lazy(() => import('@/pages/DashboardPage'));
const UploadPage = lazy(() => import('@/pages/UploadPage'));
const ReportsPage = lazy(() => import('@/pages/ReportsPage'));
const DustLevelsPage = lazy(() => import('@/pages/DustLevelsPage'));
const WeeklyReportsPage = lazy(() => import('@/pages/WeeklyReportsPage'));
const FlowMetersPage = lazy(() => import('@/pages/FlowMetersPage'));

function AppRoutes() {
  return (
    <BrowserRouter>
      <Suspense fallback={<LoadingSpinner />}>
        <Routes>
          <Route path="/login" element={<LoginPage />} />
          <Route path="/register" element={<RegisterPage />} />
          <Route path="/dashboard" element={<DashboardPage />} />
          <Route path="/upload" element={<UploadPage />} />
          <Route path="/reports" element={<ReportsPage />} />
          <Route path="/dust-levels" element={<DustLevelsPage />} />
          <Route path="/weekly-reports" element={<WeeklyReportsPage />} />
          <Route path="/flow-meters" element={<FlowMetersPage />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}

Bundle Size Optimization:

OptimizationImplementationSavings
Tree ShakingVite automatic~15% bundle size
Code SplittingReact.lazy()Load time: 40% faster
Dynamic Importsimport() for large libraries~200 KB initial bundle reduction
MinificationVite (esbuild)~30% size reduction
CompressionBrotli (Cloudflare)~70% transfer size reduction

Expected Bundle Sizes:

ChunkSize (uncompressed)Size (Brotli)Load Time (4G)
Main (vendor)450 KB135 KB~1.2s
Dashboard80 KB24 KB~200ms
Reports120 KB36 KB~300ms
Upload60 KB18 KB~150ms

React Component Optimization:

typescript
// Use React.memo for expensive components
const DataGrid = React.memo(({ data }: { data: Measurement[] }) => {
  return (
    <table>
      {data.map(row => <DataRow key={row.id} row={row} />)}
    </table>
  );
}, (prevProps, nextProps) => {
  // Custom comparison function
  return prevProps.data.length === nextProps.data.length
    && prevProps.data[0]?.id === nextProps.data[0]?.id;
});

// Use useMemo for expensive calculations
function DashboardKPIs({ measurements }: { measurements: Measurement[] }) {
  const statistics = useMemo(() => {
    return {
      avgPM25: measurements.reduce((sum, m) => sum + m.massconcentration2p5, 0) / measurements.length,
      maxPM25: Math.max(...measurements.map(m => m.massconcentration2p5)),
      avgPM10: measurements.reduce((sum, m) => sum + m.massconcentration10p0, 0) / measurements.length,
      maxPM10: Math.max(...measurements.map(m => m.massconcentration10p0))
    };
  }, [measurements]);

  return (
    <div>
      <KPICard title="Avg PM2.5" value={statistics.avgPM25} />
      <KPICard title="Max PM2.5" value={statistics.maxPM25} />
    </div>
  );
}

// Use useCallback for event handlers passed as props
function ParentComponent() {
  const [count, setCount] = useState(0);

  const handleClick = useCallback(() => {
    setCount(c => c + 1);
  }, []); // Empty deps: function never changes

  return <ChildComponent onClick={handleClick} />;
}

7.4.2 Database Performance Optimization

Query Optimization:

sql
-- ❌ BAD: N+1 query problem
-- Frontend fetches uploads, then for each upload fetches csv_files
SELECT * FROM uploads WHERE user_id = $1;
-- Then for each upload:
SELECT * FROM csv_files WHERE upload_id = $2;

-- ✅ GOOD: Single query with JOIN
SELECT
  u.id,
  u.upload_timestamp,
  u.status,
  json_agg(
    json_build_object(
      'id', cf.id,
      'filename', cf.filename,
      'status', cf.status,
      'record_count', cf.record_count
    )
  ) AS csv_files
FROM uploads u
LEFT JOIN csv_files cf ON cf.upload_id = u.id
WHERE u.user_id = $1
GROUP BY u.id
ORDER BY u.upload_timestamp DESC
LIMIT 50;

Index Strategy:

sql
-- Single-column indexes (already created in schema)
CREATE INDEX idx_measurements_time ON measurements(time);
CREATE INDEX idx_measurements_site ON measurements(site);

-- Composite indexes for common query patterns
CREATE INDEX idx_measurements_site_time
  ON measurements(site, time DESC);
-- Supports: WHERE site = 'X' ORDER BY time DESC

CREATE INDEX idx_measurements_user_time
  ON measurements(upload_id, time DESC);
-- Supports: JOIN with uploads WHERE user_id = 'X' ORDER BY time DESC

-- Partial indexes for common filters
CREATE INDEX idx_measurements_high_pm25
  ON measurements(massconcentration2p5)
  WHERE massconcentration2p5 > 35; -- EPA PM2.5 threshold
-- Supports: WHERE massconcentration2p5 > 35 (exceedance queries)

-- Expression indexes for computed columns
CREATE INDEX idx_user_profiles_lower_email
  ON user_profiles(LOWER(email));
-- Supports case-insensitive email lookup

Query Performance Monitoring:

sql
-- Enable query statistics (in PostgreSQL config)
shared_preload_libraries = 'pg_stat_statements'

-- Find slow queries
SELECT
  query,
  calls,
  total_exec_time,
  mean_exec_time,
  max_exec_time
FROM pg_stat_statements
WHERE mean_exec_time > 1000 -- > 1 second
ORDER BY mean_exec_time DESC
LIMIT 20;

-- Analyze query plan
EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
SELECT * FROM measurements
WHERE site = 'Site A'
  AND time BETWEEN '2025-01-01' AND '2025-01-31'
ORDER BY time DESC
LIMIT 100;

7.4.3 Caching Strategy

Multi-Level Caching Architecture:

┌─────────────────────────────────────────────────────────────┐
│ Level 1: Browser Cache (Cache-Control headers)              │
│ - Static assets: 7 days                                      │
│ - HTML: no-cache (always revalidate)                        │
│ - API responses: no-store (never cache)                     │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Level 2: Cloudflare CDN Cache                                │
│ - Static assets (JS, CSS, images): 4 hours                  │
│ - Purge on deploy                                            │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Level 3: Application Memory Cache (React State)             │
│ - User profile: session duration                             │
│ - Mine sites list: 5 minutes                                 │
│ - Reports list: 1 minute                                     │
└─────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────┐
│ Level 4: Database Query Cache (PostgreSQL)                   │
│ - Shared buffers: 1 GB                                       │
│ - Effective cache: 3 GB                                      │
└─────────────────────────────────────────────────────────────┘

Cache-Control Headers:

typescript
// Cloudflare Pages configuration (_headers file)
/*
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  X-XSS-Protection: 1; mode=block
  Referrer-Policy: strict-origin-when-cross-origin

/assets/*
  Cache-Control: public, max-age=604800, immutable

/index.html
  Cache-Control: no-cache, must-revalidate

/api/*
  Cache-Control: no-store, no-cache, must-revalidate

Application-Level Caching:

typescript
// Simple in-memory cache with TTL
class Cache<T> {
  private cache = new Map<string, { value: T; expiry: number }>();

  set(key: string, value: T, ttlMs: number): void {
    this.cache.set(key, {
      value,
      expiry: Date.now() + ttlMs
    });
  }

  get(key: string): T | null {
    const entry = this.cache.get(key);
    if (!entry) return null;

    if (Date.now() > entry.expiry) {
      this.cache.delete(key);
      return null;
    }

    return entry.value;
  }

  clear(): void {
    this.cache.clear();
  }
}

// Usage
const mineSitesCache = new Cache<MineSite[]>();

async function getMineSites(): Promise<MineSite[]> {
  const cached = mineSitesCache.get('mine_sites');
  if (cached) return cached;

  const { data } = await supabase.from('mine_sites').select('*');
  mineSitesCache.set('mine_sites', data, 5 * 60 * 1000); // 5 min TTL

  return data;
}

7.4.4 Performance Monitoring

Core Web Vitals Targets:

MetricTargetMeasurement Tool
Largest Contentful Paint (LCP)< 2.5sLighthouse, Chrome DevTools
First Input Delay (FID)< 100msReal User Monitoring (RUM)
Cumulative Layout Shift (CLS)< 0.1Lighthouse, Chrome DevTools
Time to First Byte (TTFB)< 600msChrome DevTools Network tab
First Contentful Paint (FCP)< 1.8sLighthouse
Total Blocking Time (TBT)< 300msLighthouse

Performance Monitoring Implementation:

typescript
// Track Web Vitals
import { onCLS, onFID, onLCP } from 'web-vitals';

function sendToAnalytics({ name, value, id }: Metric) {
  // Send to analytics service
  console.log(`${name}: ${value}ms (ID: ${id})`);

  // Optionally send to Supabase for logging
  supabase.from('performance_metrics').insert({
    metric_name: name,
    value,
    metric_id: id,
    user_agent: navigator.userAgent,
    timestamp: new Date().toISOString()
  });
}

onCLS(sendToAnalytics);
onFID(sendToAnalytics);
onLCP(sendToAnalytics);

7.5 Internal Communications Detailed Design

7.5.1 API Communication Patterns

Supabase Client-Server Communication:

┌─────────────────┐                          ┌─────────────────┐
│  React Frontend │                          │  Supabase API   │
│  (Browser)      │                          │  Gateway        │
└────────┬────────┘                          └────────┬────────┘
         │                                            │
         │  1. Initial Request                        │
         │  GET /rest/v1/uploads                      │
         │  Authorization: Bearer <JWT>               │
         ├───────────────────────────────────────────>│
         │                                            │
         │                                            │  2. Verify JWT
         │                                            │     & RLS check
         │                                            │
         │  3. Response                               │
         │  200 OK                                    │
         │  Content-Type: application/json            │
         │  [{id, user_id, status, ...}]             │
         │<───────────────────────────────────────────┤
         │                                            │

API Request/Response Format:

typescript
// Standard API Request
interface APIRequest {
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
  endpoint: string;
  headers: {
    'Authorization': `Bearer ${accessToken}`;
    'Content-Type': 'application/json';
    'apikey': string; // Supabase anon key
    'Prefer'?: 'return=representation' | 'return=minimal';
  };
  body?: Record<string, unknown>;
  params?: Record<string, string>;
}

// Standard API Response
interface APIResponse<T> {
  data: T | null;
  error: {
    message: string;
    details?: string;
    hint?: string;
    code?: string;
  } | null;
  count?: number; // For paginated responses
  status: number;
  statusText: string;
}

Error Response Format:

json
{
  "error": {
    "message": "Permission denied",
    "details": "Row-level security policy violation",
    "hint": "User does not have access to this resource",
    "code": "42501"
  },
  "status": 403,
  "statusText": "Forbidden"
}

7.5.2 Real-Time Communication (WebSocket)

Supabase Realtime Subscriptions:

typescript
// Subscribe to changes on uploads table
const subscription = supabase
  .channel('uploads-channel')
  .on(
    'postgres_changes',
    {
      event: '*', // INSERT, UPDATE, DELETE
      schema: 'public',
      table: 'uploads',
      filter: `user_id=eq.${userId}` // Only user's uploads
    },
    (payload) => {
      console.log('Change received:', payload);

      switch (payload.eventType) {
        case 'INSERT':
          // New upload created
          addUploadToList(payload.new);
          break;
        case 'UPDATE':
          // Upload status changed
          updateUploadInList(payload.new);
          break;
        case 'DELETE':
          // Upload deleted
          removeUploadFromList(payload.old);
          break;
      }
    }
  )
  .subscribe((status) => {
    if (status === 'SUBSCRIBED') {
      console.log('Realtime subscription active');
    }
  });

// Cleanup on unmount
useEffect(() => {
  return () => {
    supabase.removeChannel(subscription);
  };
}, []);

WebSocket Message Format:

json
{
  "event": "postgres_changes",
  "schema": "public",
  "table": "uploads",
  "commit_timestamp": "2025-12-02T14:30:00.000Z",
  "eventType": "UPDATE",
  "new": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "user_id": "x9y8z7w6-v5u4-t3s2-r1q0-p9o8n7m6l5k4",
    "status": "completed",
    "total_record_count": 4523,
    "updated_at": "2025-12-02T14:30:00.000Z"
  },
  "old": {
    "status": "importing"
  }
}

7.5.3 File Upload Communication

Multipart Upload Flow:

typescript
async function uploadCSVFile(file: File, uploadId: string): Promise<StoragePath> {
  const filePath = `${userId}/${uploadId}/${file.name}`;

  // 1. Upload to Supabase Storage
  const { data, error } = await supabase.storage
    .from('csv-uploads')
    .upload(filePath, file, {
      cacheControl: '3600',
      upsert: false,
      contentType: 'text/csv'
    });

  if (error) throw error;

  // 2. Get public URL (if needed)
  const { data: urlData } = supabase.storage
    .from('csv-uploads')
    .getPublicUrl(filePath);

  return {
    path: data.path,
    fullPath: data.fullPath,
    publicUrl: urlData.publicUrl
  };
}

// With progress tracking
async function uploadWithProgress(
  file: File,
  onProgress: (progress: number) => void
): Promise<StoragePath> {
  const xhr = new XMLHttpRequest();

  return new Promise((resolve, reject) => {
    xhr.upload.addEventListener('progress', (e) => {
      if (e.lengthComputable) {
        const progress = (e.loaded / e.total) * 100;
        onProgress(progress);
      }
    });

    xhr.addEventListener('load', () => {
      if (xhr.status === 200) {
        resolve(JSON.parse(xhr.responseText));
      } else {
        reject(new Error(`Upload failed: ${xhr.statusText}`));
      }
    });

    xhr.addEventListener('error', () => {
      reject(new Error('Network error'));
    });

    const formData = new FormData();
    formData.append('file', file);

    xhr.open('POST', `${supabaseUrl}/storage/v1/object/csv-uploads/${filePath}`);
    xhr.setRequestHeader('Authorization', `Bearer ${accessToken}`);
    xhr.setRequestHeader('apikey', supabaseAnonKey);
    xhr.send(formData);
  });
}

7.5.4 Inter-Service Communication

Edge Function to Database:

typescript
// Edge function authenticates with service role key
const supabase = createClient(
  Deno.env.get('SUPABASE_URL')!,
  Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! // Bypasses RLS
);

// Insert data (bypasses RLS policies)
const { data, error } = await supabase
  .from('flow_meter_data')
  .insert([
    { meter_id: 'FM-001', volume_liters: 125000, timestamp: '2025-12-02T14:30:00Z' }
  ]);

Frontend to Edge Function:

typescript
// Frontend calls edge function with user JWT
async function triggerFlowMeterRefresh(): Promise<RefreshResult> {
  const { data: { session } } = await supabase.auth.getSession();

  const response = await fetch(
    `${supabaseUrl}/functions/v1/trigger-flow-meter-scraper`,
    {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${session.access_token}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ siteIds: ['SITE-A', 'SITE-B'] })
    }
  );

  if (!response.ok) {
    throw new Error(`Function error: ${response.statusText}`);
  }

  return response.json();
}

7.5.5 Error Handling and Retry Logic

Exponential Backoff Retry:

typescript
async function fetchWithRetry<T>(
  fetcher: () => Promise<T>,
  maxRetries: number = 3,
  baseDelay: number = 1000
): Promise<T> {
  let lastError: Error;

  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fetcher();
    } catch (error) {
      lastError = error as Error;

      // Don't retry on 4xx errors (client errors)
      if (error.status >= 400 && error.status < 500) {
        throw error;
      }

      // Calculate delay with exponential backoff
      const delay = baseDelay * Math.pow(2, attempt);
      const jitter = Math.random() * 0.3 * delay; // Add 0-30% jitter

      console.log(`Retry attempt ${attempt + 1} after ${delay + jitter}ms`);
      await new Promise(resolve => setTimeout(resolve, delay + jitter));
    }
  }

  throw lastError!;
}

// Usage
const data = await fetchWithRetry(async () => {
  const response = await supabase.from('uploads').select('*');
  if (response.error) throw response.error;
  return response.data;
});

Circuit Breaker Pattern:

typescript
class CircuitBreaker {
  private failures = 0;
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
  private nextAttempt = 0;

  constructor(
    private threshold: number = 5,
    private timeout: number = 60000 // 60 seconds
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      if (Date.now() < this.nextAttempt) {
        throw new Error('Circuit breaker is OPEN');
      }
      this.state = 'HALF_OPEN';
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess(): void {
    this.failures = 0;
    this.state = 'CLOSED';
  }

  private onFailure(): void {
    this.failures++;
    if (this.failures >= this.threshold) {
      this.state = 'OPEN';
      this.nextAttempt = Date.now() + this.timeout;
    }
  }
}

// Usage
const externalAPIBreaker = new CircuitBreaker(5, 60000);

async function callExternalAPI(): Promise<Data> {
  return externalAPIBreaker.execute(async () => {
    const response = await fetch('https://external-api.com/data');
    return response.json();
  });
}

This completes Section 7: Detailed Design with comprehensive specifications for hardware, software, security, performance, and communications.


8. System Integrity Controls

This section documents the controls and mechanisms implemented to ensure system integrity across data, application, and security layers.

8.1 Data Integrity Controls

8.1.1 Input Validation

Multi-Layer Validation Strategy:

Validation LayerImplementationPurpose
Client-SideZod schema validation, React Hook FormImmediate user feedback, reduce server load
API LayerSupabase REST API validationReject malformed requests before database
Database LayerCHECK constraints, triggers, domain typesLast line of defense, enforce data rules

Validation Rules by Data Type:

typescript
// CSV Upload Validation
const csvValidationRules = {
  fileSize: { max: 50 * 1024 * 1024, message: 'File exceeds 50MB limit' },
  fileType: { allowed: ['.csv'], message: 'Only CSV files accepted' },
  columnCount: { expected: 18, message: 'CSV must have 18 columns' },
  encodingFormat: { expected: 'UTF-8', autoConvert: true },
  dateFormat: { pattern: 'ISO 8601', example: '2025-12-02T14:30:00Z' },
  numericRange: {
    pm25: { min: 0, max: 10000 },
    pm10: { min: 0, max: 10000 },
    temperature: { min: -50, max: 80 },
    humidity: { min: 0, max: 100 }
  }
};

// User Input Validation
const userInputRules = {
  email: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
  password: { minLength: 12, requireUppercase: true, requireLowercase: true, requireNumber: true, requireSpecial: true },
  siteName: { minLength: 1, maxLength: 255, pattern: /^[\w\s\-]+$/ },
  dateRange: { maxDays: 365, noFutureDates: true }
};

8.1.2 Data Consistency Checks

Database Constraints:

sql
-- Referential integrity
ALTER TABLE measurements
ADD CONSTRAINT fk_measurements_upload
FOREIGN KEY (upload_id) REFERENCES uploads(id) ON DELETE CASCADE;

ALTER TABLE csv_files
ADD CONSTRAINT fk_csv_files_upload
FOREIGN KEY (upload_id) REFERENCES uploads(id) ON DELETE CASCADE;

-- Data consistency constraints
ALTER TABLE uploads
ADD CONSTRAINT check_dates CHECK (period_end >= period_start);

ALTER TABLE uploads
ADD CONSTRAINT check_file_counts
CHECK (successful_files + failed_files = total_csv_files);

-- Status transitions
CREATE TYPE upload_status AS ENUM (
  'pending', 'validating', 'importing',
  'generating_reports', 'completed', 'failed', 'partial_success'
);

-- Prevent invalid status transitions
CREATE OR REPLACE FUNCTION validate_upload_status_transition()
RETURNS TRIGGER AS $$
BEGIN
  IF OLD.status = 'completed' AND NEW.status NOT IN ('completed', 'failed') THEN
    RAISE EXCEPTION 'Cannot transition from completed to %', NEW.status;
  END IF;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER check_upload_status_transition
  BEFORE UPDATE ON uploads
  FOR EACH ROW
  EXECUTE FUNCTION validate_upload_status_transition();

Application-Level Consistency Checks:

typescript
async function validateUploadConsistency(uploadId: string): Promise<ConsistencyReport> {
  const upload = await fetchUpload(uploadId);
  const csvFiles = await fetchCSVFiles(uploadId);

  const issues: string[] = [];

  // Check file count consistency
  if (csvFiles.length !== upload.total_csv_files) {
    issues.push(`File count mismatch: expected ${upload.total_csv_files}, found ${csvFiles.length}`);
  }

  // Check record count totals
  const totalRecords = csvFiles.reduce((sum, file) => sum + file.record_count, 0);
  if (totalRecords !== upload.total_record_count) {
    issues.push(`Record count mismatch: expected ${upload.total_record_count}, found ${totalRecords}`);
  }

  // Check status consistency
  const completedFiles = csvFiles.filter(f => f.status === 'completed').length;
  const failedFiles = csvFiles.filter(f => f.status === 'failed').length;
  if (completedFiles !== upload.successful_files || failedFiles !== upload.failed_files) {
    issues.push('File status counts inconsistent with upload record');
  }

  return {
    isConsistent: issues.length === 0,
    issues
  };
}

8.1.3 Transaction Management

ACID Transaction Guarantees:

sql
-- Upload with file records transaction
BEGIN;

-- 1. Create upload record
INSERT INTO uploads (user_id, upload_type, period_start, period_end, total_csv_files)
VALUES ($1, $2, $3, $4, $5)
RETURNING id;

-- 2. Create CSV file records
INSERT INTO csv_files (upload_id, filename, file_size_bytes, site_name)
VALUES ($6, $7, $8, $9), ($6, $10, $11, $12), ($6, $13, $14, $15);

-- 3. If any step fails, rollback entire transaction
COMMIT;

-- Handle errors
EXCEPTION WHEN OTHERS THEN
  ROLLBACK;
  RAISE;

Distributed Transaction Handling (Storage + Database):

typescript
async function uploadFilesWithRollback(files: File[], uploadId: string): Promise<UploadResult> {
  const uploadedFiles: string[] = [];
  const dbRecords: string[] = [];

  try {
    // Phase 1: Upload files to storage
    for (const file of files) {
      const path = await supabase.storage
        .from('csv-uploads')
        .upload(`${userId}/${uploadId}/${file.name}`, file);
      uploadedFiles.push(path.data.path);
    }

    // Phase 2: Create database records
    const { data, error } = await supabase
      .from('csv_files')
      .insert(files.map((f, i) => ({
        upload_id: uploadId,
        filename: f.name,
        storage_path: uploadedFiles[i]
      })))
      .select();

    if (error) throw error;
    dbRecords.push(...data.map(r => r.id));

    return { success: true, uploadId };

  } catch (error) {
    // Rollback: Delete uploaded files
    for (const path of uploadedFiles) {
      await supabase.storage.from('csv-uploads').remove([path]);
    }

    // Rollback: Delete database records
    if (dbRecords.length > 0) {
      await supabase.from('csv_files').delete().in('id', dbRecords);
    }

    throw error;
  }
}

8.1.4 Backup and Recovery

Backup Strategy:

ComponentMethodFrequencyRetentionAutomation
DatabaseRDS automated snapshotsDaily at 03:00 UTC7 days (dev), 30 days (prod)Automatic
DatabasePoint-in-time recoveryContinuous (WAL logs)7 daysAutomatic
StorageS3 versioningOn every change90 daysAutomatic
StorageS3 replicationReal-timeCross-region (prod only)Automatic
CodeGit repositoryOn every commitIndefiniteManual
ConfigurationInfrastructure-as-CodeOn every changeVersion controlledManual

Recovery Procedures:

sql
-- Restore database from snapshot
-- CLI command:
aws rds restore-db-instance-from-db-snapshot \
  --db-instance-identifier dustac-restored \
  --db-snapshot-identifier dustac-snapshot-20251202

-- Point-in-time recovery to specific timestamp
aws rds restore-db-instance-to-point-in-time \
  --source-db-instance-identifier dustac-prod \
  --target-db-instance-identifier dustac-recovered \
  --restore-time 2025-12-02T14:30:00Z

-- Restore S3 object to previous version
aws s3api list-object-versions \
  --bucket supabase-storage-hwogexspejrzvadfjmce \
  --prefix csv-uploads/user123/upload456/data.csv

aws s3api get-object \
  --bucket supabase-storage-hwogexspejrzvadfjmce \
  --key csv-uploads/user123/upload456/data.csv \
  --version-id abc123xyz \
  data-restored.csv

8.2 Application Integrity Controls

8.2.1 Error Handling

Error Classification:

Error TypeHTTP StatusUser MessageAction
Validation Error400"Invalid input: {field} {reason}"Show inline error, allow correction
Authentication Error401"Session expired. Please sign in again."Redirect to login
Authorization Error403"Access denied. Contact administrator."Show error page
Not Found404"Resource not found"Show not found page
Server Error500"An error occurred. Our team has been notified."Show error page, log incident
Network Error-"Network connection lost. Retrying..."Auto-retry with backoff

Error Handling Implementation:

typescript
class ApplicationError extends Error {
  constructor(
    public code: string,
    public message: string,
    public statusCode: number = 500,
    public userMessage?: string,
    public details?: unknown
  ) {
    super(message);
    this.name = 'ApplicationError';
  }
}

// Error boundary component
class ErrorBoundary extends React.Component<Props, State> {
  state = { hasError: false, error: null };

  static getDerivedStateFromError(error: Error) {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
    // Log to error tracking service
    console.error('Error boundary caught:', error, errorInfo);

    // Send to backend logging
    supabase.from('error_logs').insert({
      error_message: error.message,
      error_stack: error.stack,
      component_stack: errorInfo.componentStack,
      timestamp: new Date().toISOString()
    });
  }

  render() {
    if (this.state.hasError) {
      return (
        <ErrorFallback
          error={this.state.error}
          onReset={() => this.setState({ hasError: false, error: null })}
        />
      );
    }

    return this.props.children;
  }
}

// Global error handler
window.addEventListener('unhandledrejection', (event) => {
  console.error('Unhandled promise rejection:', event.reason);

  // Log to backend
  supabase.from('error_logs').insert({
    error_type: 'unhandled_rejection',
    error_message: event.reason?.message || String(event.reason),
    timestamp: new Date().toISOString()
  });
});

8.2.2 Logging and Monitoring

Log Levels and Usage:

LevelUsageExampleStorage
ERRORUnhandled exceptions, critical failuresDatabase connection failedSupabase logs, 30-day retention
WARNRecoverable issues, deprecated featuresAPI rate limit approachingSupabase logs, 7-day retention
INFOSignificant events, user actionsUser logged in, Report generatedSupabase logs, 7-day retention
DEBUGDevelopment debugging (prod disabled)Function X called with paramsBrowser console only

Structured Logging:

typescript
interface LogEntry {
  level: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG';
  message: string;
  timestamp: string;
  user_id?: string;
  context?: Record<string, unknown>;
  stack_trace?: string;
}

async function log(entry: LogEntry): Promise<void> {
  // Console log for development
  console[entry.level.toLowerCase()](entry.message, entry.context);

  // Send to Supabase for production
  if (import.meta.env.PROD && entry.level !== 'DEBUG') {
    await supabase.from('application_logs').insert({
      ...entry,
      user_agent: navigator.userAgent,
      url: window.location.href
    });
  }
}

// Usage
log({
  level: 'INFO',
  message: 'PDF report generated',
  user_id: user.id,
  context: { reportId, fileSize, pageCount, duration: '45.3s' }
});

Performance Monitoring:

typescript
// Track performance metrics
function trackPerformance(metricName: string, duration: number, metadata?: object) {
  supabase.from('performance_metrics').insert({
    metric_name: metricName,
    duration_ms: duration,
    metadata,
    user_id: auth.user?.id,
    timestamp: new Date().toISOString()
  });
}

// Usage with performance API
const startTime = performance.now();
await generatePDFReport(data);
const endTime = performance.now();
trackPerformance('pdf_generation', endTime - startTime, { recordCount: data.length });

8.2.3 Health Checks

Health Check Endpoints:

EndpointCheckFrequencyTimeoutAlert Threshold
/api/healthBasic livenessEvery 30s5s3 consecutive failures
/api/health/dbDatabase connectionEvery 60s10s2 consecutive failures
/api/health/storageStorage accessEvery 60s10s2 consecutive failures
/api/health/detailedFull diagnosticOn-demand30sManual review

Implementation:

typescript
// Edge function: health-check
serve(async (req) => {
  const checks: HealthCheck[] = [];

  // Database check
  try {
    const start = Date.now();
    const { error } = await supabase.from('uploads').select('id').limit(1);
    checks.push({
      name: 'database',
      status: error ? 'unhealthy' : 'healthy',
      latency: Date.now() - start,
      error: error?.message
    });
  } catch (error) {
    checks.push({
      name: 'database',
      status: 'unhealthy',
      error: error.message
    });
  }

  // Storage check
  try {
    const start = Date.now();
    const { error } = await supabase.storage.from('csv-uploads').list('', { limit: 1 });
    checks.push({
      name: 'storage',
      status: error ? 'unhealthy' : 'healthy',
      latency: Date.now() - start,
      error: error?.message
    });
  } catch (error) {
    checks.push({
      name: 'storage',
      status: 'unhealthy',
      error: error.message
    });
  }

  const allHealthy = checks.every(c => c.status === 'healthy');

  return new Response(JSON.stringify({
    status: allHealthy ? 'healthy' : 'degraded',
    timestamp: new Date().toISOString(),
    checks
  }), {
    status: allHealthy ? 200 : 503,
    headers: { 'Content-Type': 'application/json' }
  });
});

8.3 Security Integrity Controls

8.3.1 Access Controls

Role-Based Access Control (RBAC):

RolePermissionsRLS Policy
AnonymousView public pagesNo database access
Authenticated UserView own data, upload CSV, generate reportsauth.uid() = user_id
ManagerView organization data, manage team uploadsorganization = user.organization
AdminFull system access, user managementrole = 'admin'

Permission Matrix:

ResourceAnonymousUserManagerAdmin
View Public Pages
Register/Login
Upload CSV
Generate Reports
View Own Data
View Team Data
Delete Others' Data
Manage Users
System Configuration

8.3.2 Audit Trails

Audit Log Structure:

sql
CREATE TABLE activity_logs (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id UUID REFERENCES auth.users(id),
  action TEXT NOT NULL, -- 'create', 'read', 'update', 'delete'
  resource_type TEXT NOT NULL, -- 'upload', 'report', 'user', etc.
  resource_id UUID,
  resource_name TEXT,
  details JSONB,
  ip_address INET,
  user_agent TEXT,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX idx_activity_logs_user ON activity_logs(user_id);
CREATE INDEX idx_activity_logs_created ON activity_logs(created_at DESC);
CREATE INDEX idx_activity_logs_action ON activity_logs(action);

Audited Actions:

ActionLogged DetailsRetention
User LoginUser ID, IP, timestamp, user agent90 days
User RegistrationEmail, IP, timestamp365 days
Password ResetUser ID, IP, timestamp90 days
Data UploadUpload ID, file count, record count, site365 days
Report GenerationReport ID, date range, devices365 days
Data ExportRecord count, filters, format365 days
User Role ChangeOld role, new role, changed by365 days
Data DeletionResource type, ID, deleted by365 days

8.3.3 Intrusion Detection

Detection Mechanisms:

ThreatDetection MethodResponse
Brute Force Login5+ failed attempts in 30 minAccount lockout (30 min), CAPTCHA
SQL InjectionPattern matching in logsBlock request, alert admin
XSS AttemptContent Security Policy violationBlock script, log incident
Abnormal TrafficCloudflare WAF rulesRate limit, challenge, block
Suspicious File UploadFile type validation, size checkReject upload, log attempt
Session HijackingToken fingerprinting, IP changeInvalidate session, require re-auth

Automated Response:

typescript
// Detect and respond to brute force
async function handleFailedLogin(email: string, ip: string): Promise<void> {
  const failedAttempts = await getFailedAttempts(email, ip, 30); // Last 30 minutes

  if (failedAttempts >= 5) {
    // Lock account
    await supabase.from('user_profiles').update({
      account_locked_until: new Date(Date.now() + 30 * 60 * 1000)
    }).eq('email', email);

    // Log security event
    await supabase.from('security_events').insert({
      event_type: 'brute_force_detected',
      email,
      ip_address: ip,
      failed_attempts: failedAttempts,
      action_taken: 'account_locked_30min'
    });

    // Alert admin
    await sendSecurityAlert({
      type: 'brute_force',
      severity: 'high',
      details: { email, ip, failedAttempts }
    });
  }
}

8.3.4 Vulnerability Management

Security Scanning Schedule:

Scan TypeToolFrequencyRemediation SLA
Dependency Vulnerabilitiesnpm audit, SnykEvery commitCritical: 24h, High: 7 days
Static Code AnalysisESLint security rulesEvery commitHigh: 7 days, Medium: 30 days
Container ScanningTrivy (if using Docker)WeeklyCritical: 48h, High: 14 days
Penetration TestingManual + automatedQuarterlyCritical: Immediate, High: 7 days
Security ReviewManual code reviewPer major releaseAddress before release

Patch Management:

bash
# Weekly dependency audit
npm audit --audit-level=moderate

# Update dependencies with known vulnerabilities
npm audit fix

# Review and apply patches
npm update --save

# Test after updates
npm run test
npm run build

# Deploy patched version
git commit -m "chore(security): apply security patches"
git push

9. External Interfaces

9.1 Interface Architecture

The Dustac system integrates with four primary external services to enrich functionality and automate data collection.

Interface Inventory:

InterfacePurposeProtocolAuthenticationData ExchangeFrequency
Dustac Dust Level Scraper APIFetch dust monitoring sensor dataHTTP RESTAPI KeyJSONOn-demand
Flow Meter Scraper APIFetch water usage dataHTTP RESTBearer TokenJSONEvery 4 hours (scheduled)
Google Gemini AI APIGenerate chart descriptions for accessibilityHTTP RESTAPI KeyJSONOn report generation
BOM Weather Data InterfaceFetch weather station dataHTTP RESTPublic (no auth)CSVOn-demand

Integration Pattern:

┌──────────────────────────────────────────────────────────────┐
│                    Dustac Dashboard                           │
│  ┌────────────────────────────────────────────────────────┐  │
│  │         Supabase Edge Functions (Middleware)           │  │
│  │  - Authentication, rate limiting, error handling       │  │
│  │  - Data transformation, validation                      │  │
│  │  - Caching, retry logic                                 │  │
│  └───┬──────────────┬──────────────┬──────────────┬───────┘  │
└──────┼──────────────┼──────────────┼──────────────┼──────────┘
       │              │              │              │
       ▼              ▼              ▼              ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│   Dustac   │ │Flow Meter  │ │  Gemini AI │ │ BOM Weather│
│  API       │ │  API       │ │   API      │ │    API     │
└────────────┘ └────────────┘ └────────────┘ └────────────┘

9.2 Interface Detailed Design

9.2.1 Dustac Dust Level Scraper API

Purpose: Fetch real-time and historical dust monitoring data from Dustac sensors deployed at mine sites.

Interface Specifications:

AttributeValue
Base URLhttps://api.dustac.com/v1 (example)
ProtocolHTTPS (TLS 1.2+)
AuthenticationAPI Key in header (X-API-Key)
Data FormatJSON
Rate Limit100 requests/minute
Timeout30 seconds

API Endpoints:

GET /sites

Retrieve list of available monitoring sites.

Request:

http
GET /v1/sites HTTP/1.1
Host: api.dustac.com
X-API-Key: {api_key}
Accept: application/json

Response:

json
{
  "sites": [
    {
      "site_id": "SITE-A",
      "site_name": "Mine Site Alpha",
      "location": { "lat": -23.6980, "lon": 133.8807 },
      "status": "active"
    }
  ]
}

GET /measurements

Fetch dust measurements for specified site and date range.

Request:

http
GET /v1/measurements?site_id=SITE-A&start_date=2025-12-01&end_date=2025-12-07 HTTP/1.1
Host: api.dustac.com
X-API-Key: {api_key}
Accept: application/json

Response:

json
{
  "site_id": "SITE-A",
  "measurements": [
    {
      "timestamp": "2025-12-01T00:00:00Z",
      "pm2_5": 12.5,
      "pm10": 25.3,
      "temperature": 28.5,
      "humidity": 45.2
    }
  ],
  "total_count": 10080
}

Error Handling:

Error CodeMeaningResponse Action
401Invalid API keyAlert admin to update credentials
404Site not foundNotify user, check site configuration
429Rate limit exceededImplement exponential backoff
500Server errorRetry up to 3 times, then log incident

Implementation (Edge Function):

typescript
async function fetchDustacData(siteId: string, startDate: string, endDate: string) {
  const apiKey = Deno.env.get('DUSTAC_API_KEY')!;

  const response = await fetch(
    `https://api.dustac.com/v1/measurements?site_id=${siteId}&start_date=${startDate}&end_date=${endDate}`,
    {
      headers: {
        'X-API-Key': apiKey,
        'Accept': 'application/json'
      },
      signal: AbortSignal.timeout(30000)
    }
  );

  if (!response.ok) {
    throw new Error(`Dustac API error: ${response.status}`);
  }

  return response.json();
}

9.2.2 Flow Meter Scraper API

Purpose: Automatically fetch water flow meter readings from water trucks at mine sites for usage tracking and compliance reporting.

Interface Specifications:

AttributeValue
Base URLhttps://api.flowmeter-provider.com/v1
ProtocolHTTPS (TLS 1.3)
AuthenticationBearer Token (JWT)
Data FormatJSON
Rate Limit60 requests/hour
Timeout30 seconds
InvocationScheduled (every 4 hours via cron) + Manual trigger

API Endpoint:

GET /readings

Request:

http
GET /v1/readings?site_id=all&since=2025-12-01T00:00:00Z HTTP/1.1
Host: api.flowmeter-provider.com
Authorization: Bearer {token}
Accept: application/json

Response:

json
{
  "status": "success",
  "data": [
    {
      "meter_id": "FM-001",
      "site_id": "SITE-A",
      "volume_liters": 125000,
      "timestamp": "2025-12-02T14:30:00Z",
      "status": "online"
    },
    {
      "meter_id": "FM-002",
      "site_id": "SITE-B",
      "volume_liters": 98500,
      "timestamp": "2025-12-02T14:30:00Z",
      "status": "online"
    }
  ],
  "total_meters": 45,
  "fetched_at": "2025-12-02T14:30:15Z"
}

Data Transformation:

Source FieldTarget FieldTransformation
meter_idmeter_idDirect mapping
site_idsite_idDirect mapping
volume_litersvolume_litersValidate >= 0
timestamptimestampConvert to ISO 8601 if needed
statusstatusMap to enum ('online', 'offline')

Implementation: See Section 7.2.4 for complete edge function code.


9.2.3 Google Gemini AI API

Purpose: Generate natural language descriptions of charts for screen readers and accessibility compliance (WCAG 2.1 AA).

Interface Specifications:

AttributeValue
Base URLhttps://generativelanguage.googleapis.com/v1beta
ProtocolHTTPS (TLS 1.3)
AuthenticationAPI Key (query parameter)
Data FormatJSON
Rate Limit60 requests/minute (free tier)
Timeout60 seconds
InvocationDuring PDF report generation

API Endpoint:

POST /models/gemini-pro:generateContent

Request:

http
POST /v1beta/models/gemini-pro:generateContent?key={api_key} HTTP/1.1
Host: generativelanguage.googleapis.com
Content-Type: application/json

{
  "contents": [{
    "parts": [{
      "text": "Describe this chart for a screen reader user: The chart shows PM2.5 levels over 7 days. Values range from 12 to 45 μg/m³. There was a spike to 45 μg/m³ on Dec 3rd, exceeding the EPA 24-hour standard of 35 μg/m³. Other days remained below the threshold."
    }]
  }],
  "generationConfig": {
    "temperature": 0.4,
    "maxOutputTokens": 150
  }
}

Response:

json
{
  "candidates": [{
    "content": {
      "parts": [{
        "text": "This chart displays particulate matter (PM2.5) concentrations measured over a seven-day period. Most readings stayed below 35 micrograms per cubic meter, which is the EPA's 24-hour air quality standard. However, on December 3rd, levels peaked at 45 micrograms per cubic meter, exceeding safe limits. This spike may indicate increased dust activity or adverse weather conditions on that day."
      }]
    }
  }]
}

Implementation:

typescript
async function generateChartDescription(chartData: ChartData): Promise<string> {
  const apiKey = Deno.env.get('GEMINI_API_KEY')!;

  const prompt = `Describe this chart for a screen reader user: ${JSON.stringify(chartData)}`;

  const response = await fetch(
    `https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=${apiKey}`,
    {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        contents: [{ parts: [{ text: prompt }] }],
        generationConfig: { temperature: 0.4, maxOutputTokens: 150 }
      }),
      signal: AbortSignal.timeout(60000)
    }
  );

  const data = await response.json();
  return data.candidates[0].content.parts[0].text;
}

9.2.4 BOM Weather Data Interface

Purpose: Fetch historical weather data from Australian Bureau of Meteorology (BOM) for correlation analysis with dust levels.

Interface Specifications:

AttributeValue
Base URLhttp://www.bom.gov.au/climate/dwo/
ProtocolHTTP (public data)
AuthenticationNone (public API)
Data FormatCSV
Rate LimitRespect robots.txt, ~1 request/second
Timeout30 seconds
InvocationOn-demand (user-initiated weather data import)

Data Retrieval Pattern:

URL Structure:

http://www.bom.gov.au/climate/dwo/{YYYYMM}/text/{STATION_CODE}.{YYYYMM}.csv

Example:

http://www.bom.gov.au/climate/dwo/202512/text/IDCJDW4133.202512.csv

CSV Structure:

csv
Date,Minimum temperature (°C),Maximum temperature (°C),Rainfall (mm),Evaporation (mm),Sunshine (hours),Direction of maximum wind gust,Speed of maximum wind gust (km/h),Time of maximum wind gust,9am Temperature (°C),9am relative humidity (%),9am cloud amount (oktas),9am wind direction,9am wind speed (km/h),9am MSL pressure (hPa),3pm Temperature (°C),3pm relative humidity (%),3pm cloud amount (oktas),3pm wind direction,3pm wind speed (km/h),3pm MSL pressure (hPa)
2025-12-01,24.5,32.1,0,8.2,10.5,NW,41,14:23,28.2,65,2,NE,15,1012.3,31.5,42,1,W,22,1010.8

Parsing Implementation:

typescript
async function fetchBOMWeatherData(stationCode: string, year: number, month: number) {
  const yyyymm = `${year}${String(month).padStart(2, '0')}`;
  const url = `http://www.bom.gov.au/climate/dwo/${yyyymm}/text/${stationCode}.${yyyymm}.csv`;

  const response = await fetch(url, { signal: AbortSignal.timeout(30000) });

  if (!response.ok) {
    throw new Error(`BOM API error: ${response.status}`);
  }

  const csvText = await response.text();

  // Parse CSV using PapaParse
  const parsed = Papa.parse(csvText, {
    header: true,
    skipEmptyLines: true,
    transformHeader: (header) => header.trim()
  });

  // Transform to database format
  const weatherData = parsed.data.map(row => ({
    station_id: stationCode,
    date: row['Date'],
    min_temp: parseFloat(row['Minimum temperature (°C)']),
    max_temp: parseFloat(row['Maximum temperature (°C)']),
    rainfall: parseFloat(row['Rainfall (mm)']),
    evaporation: parseFloat(row['Evaporation (mm)']),
    sunshine: parseFloat(row['Sunshine (hours)']),
    wind_gust_direction: row['Direction of maximum wind gust'],
    wind_gust_speed: parseFloat(row['Speed of maximum wind gust (km/h)']),
    // ... map other fields
  }));

  return weatherData;
}

Error Handling:

ErrorCauseResponse
404Station or date not availableNotify user, suggest alternative station
TimeoutSlow network or serverRetry with exponential backoff (3 attempts)
Parse errorMalformed CSVLog error, skip invalid rows, import valid data

This completes Section 8 and Section 9 with comprehensive integrity controls and external interface specifications.


Appendices

Appendix A: Record of Changes

VersionDateAuthorChange Description
1.02025-12-02Dustac TeamInitial document creation

Appendix B: Acronyms

AcronymDefinition
APIApplication Programming Interface
BaaSBackend-as-a-Service
BOMBureau of Meteorology
CDNContent Delivery Network
CI/CDContinuous Integration/Continuous Deployment
CSVComma-Separated Values
E2EEnd-to-End
JWTJSON Web Token
PDFPortable Document Format
PMParticulate Matter
RLSRow-Level Security
SDDSystem Design Document
SPASingle Page Application
SQLStructured Query Language
SSRServer-Side Rendering
UIUser Interface
UUIDUniversally Unique Identifier
WCAGWeb Content Accessibility Guidelines

Appendix C: Glossary

TermDefinition
Dust LevelConcentration of particulate matter in the air, measured in μg/m³
Edge FunctionServerless function executed at the network edge
Flow MeterDevice measuring water flow rate and consumption
Mine SitePhysical location of mining operations being monitored
Row-Level SecurityDatabase security feature that restricts data access per row
SupabaseOpen-source Backend-as-a-Service platform
Weekly ReportStructured field report submitted on a weekly basis

Appendix D: Referenced Documents

Document IDDocument TitleVersionDate
REF-001CLAUDE.md (Project Instructions)1.02025-12-02
REF-002SYSTEM_DESIGN.md (Technical Design)1.02025-12-02
REF-003Supabase Documentation-Current
REF-004React 19 Documentation-Current
REF-005Cloudflare Pages Documentation-Current

Appendix E: Approvals

RoleNameSignatureDate
Project Manager
Technical Lead
Security Officer
Quality Assurance Lead

Appendix F: Additional Appendices

[Reserved for additional supporting documentation as needed.]


End of Document