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

1022 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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
```javascript
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
```javascript
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
```javascript
content: [
'./src/**/*.{js,ts,jsx,tsx}',
],
```
#### Dark Mode
```javascript
darkMode: 'class', // Toggle via class (not media query)
```
#### Custom Theme Extensions
```javascript
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
```javascript
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
```javascript
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
```javascript
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
```json
{
"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
```javascript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
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
```typescript
// 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:
```typescript
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
```typescript
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:
```typescript
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
```typescript
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
```typescript
// Good - const assertion for type safety
export const OFFLINE_CONFIG = {
dbName: 'TourBuilderOffline',
// ...
} as const;
export type OfflineConfig = typeof OFFLINE_CONFIG;
```
### 2. Centralize Environment Access
```typescript
// Good - single source of truth
// config.ts
export const apiBaseURL = process.env.NEXT_PUBLIC_BACK_API || baseURLApi;
// Consumer
import { apiBaseURL } from '../config';
```
### 3. Group Related Settings
```typescript
// 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
```typescript
// Good - explain values
storage: {
cacheApiMaxSize: 5 * 1024 * 1024, // 5MB - files below go to Cache API
minFreeBuffer: 50 * 1024 * 1024, // 50MB - minimum free space
},
```
---
## Related Documentation
- [lib-module.md](./lib-module.md) - Offline storage using OFFLINE_CONFIG
- [hooks-module.md](./hooks-module.md) - Preload hooks using PRELOAD_CONFIG
- [layouts-module.md](./layouts-module.md) - Menu rendering
- [stores-module.md](./stores-module.md) - styleSlice using storage keys
---
## Adding New Configuration
### Step 1: Create Config File
```typescript
// 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
```typescript
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.