30 KiB
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';
3. Group Related Settings
// 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
},
Related Documentation
- lib-module.md - Offline storage using OFFLINE_CONFIG
- hooks-module.md - Preload hooks using PRELOAD_CONFIG
- layouts-module.md - Menu rendering
- stores-module.md - styleSlice using storage keys
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.