39948-vm/frontend/docs/config-module.md
2026-07-03 16:11:24 +02:00

30 KiB
Raw Blame History

Frontend Config Module

Overview

The Config module provides centralized configuration for the frontend application across multiple layers: build tooling, runtime settings, navigation menus, offline/PWA functionality, and asset preloading.

Location:

  • Root configs: frontend/*.config.*
  • Runtime configs: frontend/src/config/, frontend/src/config.ts, frontend/src/menuAside.ts, frontend/src/menuNavBar.ts

Total Files: 12 config-related files


Architecture

frontend/
├── next.config.mjs        (44 LOC)   # Next.js configuration
├── tailwind.config.js     (114 LOC)  # Tailwind CSS theming
├── postcss.config.js      (8 LOC)    # PostCSS plugins
├── prettier.config.js     (12 LOC)   # Code formatting
├── tsconfig.json          (30 LOC)   # TypeScript compiler
├── .eslintrc.cjs          (50 LOC)   # ESLint rules
├── .eslintignore          (3 LOC)    # ESLint exclusions
├── package.json           (84 LOC)   # NPM scripts & deps
│
└── src/
    ├── config.ts           (20 LOC)   # Runtime app config
    ├── menuAside.ts        (42 LOC)   # Sidebar navigation
    ├── menuNavBar.ts       (51 LOC)   # Top navbar menus
    ├── interfaces/index.ts (123 LOC)  # Menu type definitions
    │
    └── config/
        ├── offline.config.ts   (52 LOC)   # PWA/offline settings
        └── preload.config.ts   (94 LOC)   # Asset preloading settings

Root Configuration Files

1. Next.js Configuration (next.config.mjs)

Purpose: Configure Next.js build and runtime behavior.

Lines: 44 LOC

import withSerwistInit from '@serwist/next';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const output = process.env.NEXT_OUTPUT || undefined;
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const isDevelopment = process.env.NODE_ENV === 'development';

const withSerwist = withSerwistInit({
  swSrc: 'src/sw.ts',
  swDest: 'public/sw.js',
  disable: isDevelopment,
  maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
});

const nextConfig = {
  trailingSlash: true,
  distDir: isDevelopment ? '.next' : 'build',
  outputFileTracingRoot: __dirname,
  output,
  basePath: '',
  devIndicators: {
    position: 'bottom-left',
  },
  typescript: {
    ignoreBuildErrors: false,
  },
  eslint: {
    ignoreDuringBuilds: false,
  },
  images: {
    unoptimized: true,
    remotePatterns: [
      { protocol: 'https', hostname: '**' },
    ],
    localPatterns: [
      { pathname: '/api/**' },
    ],
  },
};

export default withSerwist(nextConfig);

Configuration Options

Setting Value Purpose
trailingSlash true URLs end with / for consistent routing
distDir .next in development, build in production Keeps Turbopack dev artifacts separate from production next build output
output undefined by default, env override supported Default VM build runs as a Next.js server
images.unoptimized true Disable image optimization (custom asset handling)
typescript.ignoreBuildErrors false Fail build on TS errors
eslint.ignoreDuringBuilds false Fail build on lint errors

Serwist (PWA) Integration

withSerwistInit({
  swSrc: 'src/sw.ts',      // Service worker source
  swDest: 'public/sw.js',  // Compiled output
  disable: isDevelopment,  // Dev: no SW
})

public/sw.js is generated by the build and is ignored by git. The source of truth is src/sw.ts. next-env.d.ts is also generated by Next.js and ignored to avoid local diffs when build/type generation updates its route type reference.


2. Tailwind CSS Configuration (tailwind.config.js)

Purpose: Custom theme, colors, animations, and plugins.

Lines: 114 LOC

Content Scanning

content: [
  './src/**/*.{js,ts,jsx,tsx}',
],

Dark Mode

darkMode: 'class',  // Toggle via class (not media query)

Custom Theme Extensions

theme: {
  extend: {
    // Custom fonts
    fontFamily: {
      'instrument': ['"Instrument Sans Variable"', 'sans-serif'],
      'instrument-condensed': ['"Instrument Sans Variable"', 'sans-serif'],
    },

    // Z-index for overlays
    zIndex: { '-1': '-1' },

    // Flex utilities
    flexGrow: { 5: '5' },

    // Max heights for menus/modals
    maxHeight: {
      'screen-menu': 'calc(100vh - 3.5rem)',
      modal: 'calc(100vh - 160px)',
    },

    // Transition properties
    transitionProperty: {
      position: 'right, left, top, bottom, margin, padding',
      textColor: 'color',
    },

    // Animations
    keyframes: {
      'fade-out': { from: { opacity: 1 }, to: { opacity: 0 } },
      'fade-in': { from: { opacity: 0 }, to: { opacity: 1 } },
    },
    animation: {
      'fade-out': 'fade-out 250ms ease-in-out',
      'fade-in': 'fade-in 250ms ease-in-out',
    },

    // Custom color palette
    colors: {
      dark: {
        900: '#131618',
        800: '#21242A',
        700: '#2C2F36',
        600: '#9CA3AF',
        500: '#CBD5E1',
      },
      green: { text: '#45B26B' },
      'pavitra': {
        'blue': '#0162FD',
        'green': '#00B448',
        'orange': '#FFAA00',
        'red': '#F20041',
        900: '#14142A',
        800: '#4E4B66',
        700: '#6E7191',
        600: '#A0A3BD',
        500: '#D9DBE9',
        400: '#EFF0F6',
        300: '#F7F7FC',
      },
    },

    // Extended border radius
    borderRadius: {
      '3xl': '2rem',
    },
  },
}

Custom Plugins

plugins: [
  require('@tailwindcss/forms'),        // Form styling
  require('@tailwindcss/typography'),   // Prose content

  // Custom scrollbar utility
  plugin(function ({ matchUtilities, theme }) {
    matchUtilities({
      'aside-scrollbars': (value) => ({
        scrollbarWidth: 'thin',
        scrollbarColor: `${theme(`colors.${color}.${thumb}`)} ${theme(`colors.${color}.${track}`)}`,
        '&::-webkit-scrollbar': { width: '8px', height: '8px' },
        '&::-webkit-scrollbar-track': { backgroundColor: theme(`colors.${color}.${track}`) },
        '&::-webkit-scrollbar-thumb': { borderRadius: '0.25rem', backgroundColor: theme(`colors.${color}.${thumb}`) },
      }),
    }, { values: theme('asideScrollbars') });
  }),
]

3. PostCSS Configuration (postcss.config.js)

Purpose: CSS processing pipeline.

Lines: 8 LOC

module.exports = {
  plugins: {
    'postcss-import': {},       // @import resolution
    'tailwindcss/nesting': {},  // CSS nesting support
    tailwindcss: {},            // Tailwind processing
    autoprefixer: {},           // Vendor prefixes
  },
}

4. Prettier Configuration (prettier.config.js)

Purpose: Code formatting rules.

Lines: 12 LOC

module.exports = {
  semi: false,              // No semicolons
  singleQuote: true,        // Single quotes
  printWidth: 100,          // Line width
  trailingComma: 'es5',     // ES5-compatible trailing commas
  arrowParens: 'always',    // Always wrap arrow params: (x) => x
  tabWidth: 2,              // 2-space indentation
  useTabs: false,           // Spaces, not tabs
  quoteProps: 'as-needed',  // Quote object keys only when needed
  jsxSingleQuote: false,    // Double quotes in JSX
  bracketSpacing: true,     // Spaces in object literals
  bracketSameLine: false,   // JSX closing bracket on new line
}

5. TypeScript Configuration (tsconfig.json)

Purpose: TypeScript compiler options.

Lines: 30 LOC

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": false,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

Key Settings

Setting Value Purpose
strict false Relaxed type checking (legacy)
noEmit true Next.js handles compilation
incremental true Faster rebuilds
isolatedModules true Required for Next.js

6. ESLint Configuration (.eslintrc.cjs)

Purpose: Code linting rules.

Lines: 50 LOC

module.exports = {
  extends: [
    'next/core-web-vitals',
    'plugin:@typescript-eslint/recommended',
    'eslint-config-prettier',
  ],
  plugins: ['import'],
  settings: {
    'import/resolver': {
      typescript: { project: ['./tsconfig.json'] },
    },
  },
  rules: {
    'react/no-children-prop': 'off',
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/no-unused-vars': 'off',
    '@typescript-eslint/ban-types': 'off',
    'react-hooks/exhaustive-deps': 'off',
    'import/named': 'error',
    'import/no-duplicates': 'error',
    'import/no-unresolved': 'error',
    'no-console': 'error',  // Use logger instead
  },
  overrides: [
    // Allow console in logger utility
    { files: ['src/lib/logger.ts'], rules: { 'no-console': 'off' } },
    // Allow console in service worker
    { files: ['src/sw.ts'], rules: { 'no-console': 'off' } },
    // Allow console in API routes
    { files: ['src/pages/api/**/*.ts'], rules: { 'no-console': 'off' } },
  ],
};

Key Rules

Rule Setting Purpose
no-console error Force use of logger utility
import/no-unresolved error Catch missing imports
import/no-duplicates error No duplicate imports

Source Configuration Files

1. Runtime App Config (src/config.ts)

Purpose: Application-wide runtime settings.

Lines: 20 LOC

export const hostApi =
  process.env.NODE_ENV === 'development' && !process.env.NEXT_PUBLIC_BACK_API
    ? 'http://localhost'
    : '';

export const portApi =
  process.env.NODE_ENV === 'development' && !process.env.NEXT_PUBLIC_BACK_API
    ? 3000
    : '';

export const baseURLApi = `${hostApi}${portApi ? `:${portApi}` : ``}/api`;

export const localStorageDarkModeKey = 'darkMode';

export const localStorageStyleKey = 'style';

export const containerMaxW = 'xl:max-w-full xl:mx-auto 2xl:mx-20';

export const appTitle = 'Shimahara Visual';

export const getPageTitle = (currentPageTitle: string) =>
  `${currentPageTitle}${appTitle}`;

Exports

Export Type Purpose Used By
baseURLApi string API base URL _app.tsx, UploadService.js
localStorageDarkModeKey string Dark mode storage key styleSlice.ts
localStorageStyleKey string Style storage key styleSlice.ts
containerMaxW string Container max-width classes NavBar.tsx, SectionMain.tsx, FooterBar.tsx
appTitle string Application name Various pages
getPageTitle function Generate page title All pages (30+ files)

2. Sidebar Menu Config (src/menuAside.ts)

Purpose: Sidebar navigation items.

Lines: 42 LOC

import * as icon from '@mdi/js';
import { MenuAsideItem } from './interfaces';

const menuAside: MenuAsideItem[] = [
  {
    href: '/dashboard',
    icon: icon.mdiViewDashboardOutline,
    label: 'Dashboard',
  },
  {
    href: '/projects/projects-list',
    label: 'Projects',
    icon: icon.mdiFolder,
    permissions: 'READ_PROJECTS',
  },
  {
    href: '/element-type-defaults',
    label: 'Element Defaults',
    icon: icon.mdiPaletteSwatch,
    permissions: 'READ_PAGE_ELEMENTS',
  },
  {
    href: '/users/users-list',
    label: 'Users',
    icon: icon.mdiAccountGroup,
    permissions: 'READ_USERS',
  },
  {
    href: '/profile',
    label: 'Profile',
    icon: icon.mdiAccountCircle,
  },
  {
    href: swaggerDocsUrl,
    target: '_blank',
    label: 'Swagger',
    icon: icon.mdiFileCode,
    permissions: 'READ_API_DOCS',
  },
];

export default menuAside;

Menu Item Structure

type MenuAsideItem = {
  label: string;                    // Display text
  icon?: string;                    // MDI icon path
  href?: string;                    // Navigation URL
  target?: string;                  // Link target (_blank, etc.)
  color?: ColorButtonKey;           // Button color
  isLogout?: boolean;               // Logout action
  withDevider?: boolean;            // Show divider
  menu?: MenuAsideItem[];           // Sub-menu items
  permissions?: string | string[];  // Required permission(s)
};

3. Navbar Menu Config (src/menuNavBar.ts)

Purpose: Top navigation bar items.

Lines: 51 LOC

import { mdiAccount, mdiLogout, mdiThemeLightDark } from '@mdi/js';
import { MenuNavBarItem } from './interfaces';

const menuNavBar: MenuNavBarItem[] = [
  {
    isCurrentUser: true,
    menu: [
      { icon: mdiAccount, label: 'My Profile', href: '/profile' },
      { isDivider: true },
      { icon: mdiLogout, label: 'Log Out', isLogout: true },
    ],
  },
  {
    icon: mdiThemeLightDark,
    label: 'Light/Dark',
    isDesktopNoLabel: true,
    isToggleLightDark: true,
  },
  {
    icon: mdiLogout,
    label: 'Log out',
    isDesktopNoLabel: true,
    isLogout: true,
  },
];

export const webPagesNavBar = [];

export default menuNavBar;

Menu Item Structure

type MenuNavBarItem = {
  label?: string;               // Display text
  icon?: string;                // MDI icon path
  href?: string;                // Navigation URL
  target?: string;              // Link target
  isDivider?: boolean;          // Render as divider
  isLogout?: boolean;           // Logout action
  isDesktopNoLabel?: boolean;   // Icon-only on desktop
  isToggleLightDark?: boolean;  // Dark mode toggle
  isCurrentUser?: boolean;      // User dropdown
  menu?: MenuNavBarItem[];      // Sub-menu items
};

4. Offline Config (src/config/offline.config.ts)

Purpose: PWA offline mode and IndexedDB settings.

Lines: 52 LOC

export const OFFLINE_CONFIG = {
  // IndexedDB
  dbName: 'TourBuilderOffline',
  dbVersion: 1,

  // Cache names (for Cache API)
  cacheNames: {
    static: 'tour-builder-static-v1',
    dynamic: 'tour-builder-dynamic-v1',
    assets: 'tour-builder-assets-v1',
  },

  // Events (EventEmitter event names)
  events: {
    preloadStart: 'asset-preload-start',
    preloadProgress: 'asset-preload-progress',
    preloadComplete: 'asset-preload-complete',
    preloadError: 'asset-preload-error',
    projectDownloadProgress: 'project-download-progress',
    projectDownloadComplete: 'project-download-complete',
    queueUpdate: 'queue-update',
  },

  // Service worker settings
  serviceWorker: {
    scope: '/',
    updateInterval: 60 * 60 * 1000, // 1 hour
  },

  // Storage settings
  storage: {
    cacheApiMaxSize: 5 * 1024 * 1024,   // 5MB - use Cache API
    indexedDbMinSize: 5 * 1024 * 1024,  // 5MB+ - use IndexedDB
  },

  // Retry settings
  retry: {
    maxRetries: 3,
    backoffMs: 1000,
    maxBackoffMs: 30000,
  },
} as const;

export type OfflineConfig = typeof OFFLINE_CONFIG;

Usage

import { OFFLINE_CONFIG } from '../config/offline.config';

// Service worker cache name
const cache = await caches.open(OFFLINE_CONFIG.cacheNames.assets);

// Storage size threshold
if (fileSize < OFFLINE_CONFIG.storage.cacheApiMaxSize) {
  // Store in Cache API
} else {
  // Store in IndexedDB
}

Consumers

File Usage
sw.ts Cache names, storage settings
StorageManager.ts Storage thresholds
DownloadManager.ts Retry settings
DownloadEventBus.ts Event names
usePreloadOrchestrator.ts Event names
usePreloadProgress.ts Event names
useOfflineMode.ts Cache names
DownloadContext.tsx Events, retries
offlineDb/schema.ts DB name, version

5. Preload Config (src/config/preload.config.ts)

Purpose: Asset preloading priorities and queue settings.

Lines: 94 LOC

export const PRELOAD_CONFIG = {
  // Queue settings
  maxConcurrentDownloads: 3,
  maxRetries: 3,
  retryDelayMs: 1000,

  // Size thresholds
  largeFileThreshold: 5 * 1024 * 1024,    // 5MB -> use IndexedDB
  videoChunkSize: 5 * 1024 * 1024,        // 5MB chunks
  initialVideoBufferSeconds: 5,

  // Priority weights (higher = load first)
  priority: {
    currentPage: 1000,
    neighborBase: 500,
    assetType: {
      transition: 150,  // Highest - needed on navigation click
      image: 100,       // Backgrounds load during transition
      audio: 50,
      video: 30,
    } as Record<string, number>,
    variant: {
      thumbnail: 50,
      preview: 40,
      webp: 35,
      mp4_low: 20,
      mp4_high: 10,
      original: 5,
    } as Record<string, number>,
    linkCountMultiplier: 10,
    maxLinkBonus: 50,
  },

  // Storage
  storage: {
    warningPercent: 80,
    criticalPercent: 95,
    minFreeBuffer: 50 * 1024 * 1024,  // 50MB
  },

  // Auto-cleanup timeouts
  autoRemove: {
    completedMs: 3000,
    errorMs: 10000,
  },

  // Neighbor graph traversal
  neighborGraph: {
    maxDepth: 1,  // Only preload immediate neighbors
    constructorMaxDepth: 1,
  },

  // Asset URL field names in element content_json
  assetFields: {
    all: [
      'iconUrl', 'imageUrl', 'mediaUrl', 'videoUrl', 'audioUrl',
      'transitionVideoUrl', 'backgroundImageUrl', 'reverseVideoUrl',
      'carouselPrevIconUrl', 'carouselNextIconUrl', 'src', 'url',
      'poster', 'thumbnail',
    ] as const,
    images: [
      'iconUrl', 'imageUrl', 'backgroundImageUrl',
      'carouselPrevIconUrl', 'carouselNextIconUrl', 'src',
    ] as const,
    nested: ['galleryCards', 'carouselSlides'] as const,
    nestedUrlFields: ['imageUrl', 'videoUrl'] as const,
  },
} as const;

export type PreloadConfig = typeof PRELOAD_CONFIG;

Priority System

┌─────────────────────────────────────────────────────────────┐
│                    Priority Calculation                     │
│                                                             │
│  Base Priority:                                             │
│    currentPage: 1000 (loading current page assets)          │
│    neighborBase: 500 (adjacent pages)                       │
│                                                             │
│  Asset Type Bonus:                                          │
│    transition: +150  (needed immediately)                   │
│    image: +100       (backgrounds)                          │
│    audio: +50        (background audio)                     │
│    video: +30        (content videos)                       │
│                                                             │
│  Variant Bonus:                                             │
│    thumbnail: +50    (quick previews)                       │
│    preview: +40      (medium quality)                       │
│    webp: +35         (optimized images)                     │
│    mp4_low: +20      (mobile video)                         │
│    mp4_high: +10     (HD video)                             │
│    original: +5      (full quality)                         │
│                                                             │
│  Link Bonus:                                                │
│    linkCountMultiplier × numLinks (max: maxLinkBonus)       │
└─────────────────────────────────────────────────────────────┘

Consumers

File Usage
usePreloadOrchestrator.ts Queue, priorities, fields
useNeighborGraph.ts Graph traversal depth
extractPageLinks.ts Asset field names
imagePreDecode.ts Image fields
StorageManager.ts Size thresholds
DownloadManager.ts Queue settings
useStorageQuota.ts Storage limits
usePreloadProgress.ts Auto-cleanup
DownloadContext.tsx Queue settings

Environment Variables

Build-Time Variables

Variable Used In Purpose
NODE_ENV next.config.mjs, config.ts Build/runtime mode

Runtime Variables (NEXT_PUBLIC_*)

Variable Default Purpose
NEXT_PUBLIC_BACK_API - Backend API URL
NEXT_PUBLIC_LOG_LEVEL 'info' Logger verbosity

Usage in Code

// API URL detection
export const apiBaseURL = process.env.NEXT_PUBLIC_BACK_API || baseURLApi;
axios.defaults.baseURL = apiBaseURL;

// Logger level
export const frontendLogLevel = process.env.NEXT_PUBLIC_LOG_LEVEL || 'info';

// Development mode
export const isDevelopment = process.env.NODE_ENV === 'development';
export const isProduction = process.env.NODE_ENV === 'production';

Feature modules, hooks, components, and app initialization should import these exports from frontend/src/config.ts instead of reading process.env directly.


Service Worker Configuration

The service worker (src/sw.ts) uses OFFLINE_CONFIG for caching:

import { OFFLINE_CONFIG } from './config/offline.config';

// Cache strategies
const serwist = new Serwist({
  runtimeCaching: [
    // Static assets - Cache First
    {
      matcher: ({ request }) => request.destination === 'image',
      handler: new CacheFirst({
        cacheName: OFFLINE_CONFIG.cacheNames.assets,
      }),
    },
    // API requests - Network First
    {
      matcher: ({ url }) => url.pathname.startsWith('/api/'),
      handler: new NetworkFirst({
        cacheName: 'api-cache',
        networkTimeoutSeconds: 10,
      }),
    },
  ],
});

Cacheable Extensions

const CACHEABLE_EXTENSIONS = [
  // Images
  '.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg', '.ico',
  // Video
  '.mp4', '.webm', '.mov',
  // Audio
  '.mp3', '.wav', '.ogg', '.m4a',
  // Fonts
  '.woff', '.woff2', '.ttf', '.eot',
  // Code
  '.css', '.js',
];

Logger Configuration

The logger (src/lib/logger.ts) provides structured logging:

interface LoggerConfig {
  level: LogLevel;          // 'debug' | 'info' | 'warn' | 'error'
  isDevelopment: boolean;   // Colored vs JSON output
  service: string;          // Service name for log aggregation
}

// Configuration from environment
this.config = {
  level: toLogLevel(frontendLogLevel),
  isDevelopment,
  service: 'tour-builder-frontend',
};

Usage

import { logger } from '@/lib/logger';

logger.info('User logged in', { userId: '123' });
logger.error('Failed to fetch', error);

Configuration Dependencies

┌─────────────────────────────────────────────────────────────┐
│                    Configuration Flow                       │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│              next.config.mjs (Build Config)                 │
│  ┌─────────────────────────────────────────────────────────┐│
│  │ - Enables Serwist (PWA)                                 ││
│  │ - Sets output mode (export/standalone)                  ││
│  │ - Configures TypeScript/ESLint checking                 ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                 config.ts (Runtime Config)                  │
│  ┌─────────────────────────────────────────────────────────┐│
│  │ - API URLs (baseURLApi, apiBaseURL)                     ││
│  │ - Runtime flags (isDevelopment, isProduction)           ││
│  │ - Public log level                                      ││
│  │ - App title, page titles                                ││
│  │ - Storage keys                                          ││
│  │ - External service keys (TinyMCE)                       ││
│  └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
                              │
               ┌──────────────┼──────────────┐
               ▼              ▼              ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│  offline.config.ts  │ │  preload.config.ts  │ │ menuAside/NavBar.ts │
│  ┌────────────────┐ │ │  ┌────────────────┐ │ │ ┌────────────────┐  │
│  │ Cache names    │ │ │  │ Queue settings │ │ │ │ Navigation     │  │
│  │ Event names    │ │ │  │ Priorities     │ │ │ │ items          │  │
│  │ Storage limits │ │ │  │ Asset fields   │ │ │ │ Permissions    │  │
│  │ Retry config   │ │ │  │ Graph depth    │ │ │ │ Icons          │  │
│  └────────────────┘ │ │  └────────────────┘ │ │ └────────────────┘  │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘
          │                       │                       │
          ▼                       ▼                       ▼
┌─────────────────────┐ ┌─────────────────────┐ ┌─────────────────────┐
│ Service Worker      │ │ Preload Hooks       │ │ Layout Components   │
│ StorageManager      │ │ DownloadManager     │ │ AsideMenu           │
│ DownloadEventBus    │ │ NeighborGraph       │ │ NavBar              │
└─────────────────────┘ └─────────────────────┘ └─────────────────────┘

Config Inventory

File LOC Purpose Consumers
next.config.mjs 44 Build configuration Next.js build
tailwind.config.js 114 CSS theming All components
postcss.config.js 8 CSS processing Build pipeline
prettier.config.js 12 Code formatting Development
tsconfig.json 30 TypeScript compiler Build, IDE
.eslintrc.cjs 50 Linting rules Build, IDE
config.ts 24 Runtime settings and public env access 30+ files
menuAside.ts 42 Sidebar navigation AsideMenuLayer.tsx
menuNavBar.ts 51 Navbar navigation NavBar.tsx
offline.config.ts 52 PWA/offline settings 9 files
preload.config.ts 94 Preloading settings 10 files
Total 517

Best Practices

1. Use Typed Constants

// Good - const assertion for type safety
export const OFFLINE_CONFIG = {
  dbName: 'TourBuilderOffline',
  // ...
} as const;

export type OfflineConfig = typeof OFFLINE_CONFIG;

2. Centralize Environment Access

// Good - single source of truth
// config.ts
export const apiBaseURL = process.env.NEXT_PUBLIC_BACK_API || baseURLApi;

// Consumer
import { apiBaseURL } from '../config';
// Good - logical grouping
export const PRELOAD_CONFIG = {
  queue: { maxConcurrent: 3, maxRetries: 3 },
  priority: { currentPage: 1000, neighborBase: 500 },
  storage: { warningPercent: 80, criticalPercent: 95 },
};

4. Document Magic Numbers

// Good - explain values
storage: {
  cacheApiMaxSize: 5 * 1024 * 1024,  // 5MB - files below go to Cache API
  minFreeBuffer: 50 * 1024 * 1024,   // 50MB - minimum free space
},


Adding New Configuration

Step 1: Create Config File

// src/config/feature.config.ts
export const FEATURE_CONFIG = {
  enabled: true,
  settings: {
    maxItems: 100,
    timeout: 5000,
  },
} as const;

export type FeatureConfig = typeof FEATURE_CONFIG;

Step 2: Import Where Needed

import { FEATURE_CONFIG } from '../config/feature.config';

if (FEATURE_CONFIG.enabled) {
  // Use settings
}

Step 3: Document in This File

Add entry to Config Inventory table and explain usage.