# 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, variant: { thumbnail: 50, preview: 40, webp: 35, mp4_low: 20, mp4_high: 10, original: 5, } as Record, 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.