This is the frontend application of Narvik, a comprehensive web application for managing French associations, particularly sporting associations. The application is built using modern Vue.js and Nuxt technologies.
Official Website: https://about.narvik.app/ Backend API: https://github.com/Narvik-app/backend
- Nuxt - Modern Vue.js meta-framework
- Vue - Progressive JavaScript framework
- TypeScript - Type-safe JavaScript
- Nuxt UI - Modern UI component library
- Tailwind CSS - Utility-first CSS framework
- Heroicons - Beautiful hand-crafted SVG icons
- Pinia - Vue.js state management
- Pinia Plugin Persistedstate - State persistence
- nuxt-api-party - API integration
- Chart.js - Flexible charting library
- vue-chartjs - Vue.js wrapper for Chart.js
- TipTap - Extensible rich text editor
- TipTap Starter Kit - Basic TipTap extensions
- Day.js - Date manipulation
- JWT Decode - JWT token handling
- Vue QR Code Reader - QR code scanning
- V-Calendar - Date picker component
app/
├── components/ # Vue components organized by feature
│ ├── Chart/ # Chart components (Bar, Doughnut, Line)
│ ├── Club/ # Club management components
│ ├── Generic/ # Reusable generic components
│ ├── Inventory/ # Inventory management
│ ├── MemberSeason/ # Member season components
│ ├── Metric/ # Metrics and analytics
│ ├── Modal/ # Modal dialog components
│ └── User/ # User management components
├── composables/ # Vue composables and utilities
│ ├── api/ # API integration composables
│ │ ├── query/ # Query abstractions
│ │ └── clubDependent/ # Club-specific API queries
│ └── image.ts # Image handling utilities
├── layouts/ # Application layouts
│ ├── admin.vue # Admin layout
│ ├── default.vue # Default layout
│ ├── email.vue # Email-specific layout
│ ├── empty.vue # Empty layout
│ ├── member.vue # Member layout
│ ├── pos.vue # Point of sale layout
│ └── super-admin.vue # Super admin layout
├── middleware/ # Route middleware
│ └── auth.global.ts # Global authentication middleware
├── pages/ # Application routes
│ ├── admin/ # Admin pages
│ ├── login/ # Authentication pages
│ ├── self.vue # Self-service page
│ └── super-admin/ # Super admin pages
├── stores/ # Pinia state stores
│ ├── useAppConfig.ts # Application configuration
│ ├── useMetricStore.ts # Metrics state
│ └── useSelfUser.ts # User profile state
├── types/ # TypeScript type definitions
│ ├── api/ # API-related types
│ ├── date.ts # Date type utilities
│ ├── groupedNavigationLinks.ts # Navigation types
│ ├── jwtTokens.ts # JWT token types
│ ├── select.ts # Select component types
│ └── table.ts # Table component types
└── utils/ # Utility functions
├── browser.ts # Browser utilities
├── chart.ts # Chart utilities
├── colors.ts # Color utilities
├── date.ts # Date manipulation
├── error.ts # Error handling
├── file.ts # File handling
├── resource.ts # Resource management
├── string.ts # String utilities
└── table.ts # Table utilities
├── nuxt.config.ts # Nuxt configuration
├── package.json # Dependencies and scripts
├── pnpm-lock.yaml # PNPM lock file
├── pnpm-workspace.yaml # PNPM workspace config
├── .env.example # Environment variables template
├── .gitignore # Git ignore rules
└── .dockerignore # Docker ignore rules
├── Dockerfile # Docker image definition
├── docker/ # Docker configuration
│ └── docker-entrypoint.sh # Container entrypoint
└── Makefile # Development commands
When working with agents, certain files and directories should be excluded from the agent's context to ensure security and performance:
.env*- Environment configuration files containing secrets, API keys, and client credentialsruntimeConfig- Runtime configuration containing sensitive data innuxt.config.ts
.output/- Nuxt build output directory.nuxt/- Nuxt development cache.vite/- Vite build cache and temp filesnode_modules/- Node.js dependencies (handled by PNPM).pnpm/- PNPM cache directory
.git/- Git repository data and history*.log- Log files*.tmp- Temporary files*.bak- Backup fileslocalhost.pem&localhost-key.pem- SSL certificates
docker/- Docker configuration and scriptsDockerfile- Container definition
Agents should primarily focus on:
app/- Application source code and componentsnuxt.config.ts- Core configuration (excluding sensitive parts)package.json- Dependencies and scriptsdocs/- DocumentationREADME.md- Project overview- Project root files - TypeScript configs, ESLint configs, etc.
This ensures agents work with the actual application code while avoiding sensitive data, generated files, and dependencies that could slow down processing.
-
Prerequisites:
- Docker and Docker Compose installed
- PNPM package manager
-
Quick Start:
# Development server with SSL pnpm dev -
Development Server:
- Development URL: https://localhost:3000
- Uses SSL certificate for local development
- Auto-reload on file changes
-
Build and Production:
# Build for production pnpm build # Preview production build pnpm preview
CRITICAL REQUIREMENT: All package manager operations should be run on the host system using PNPM.
1. Using PNPM directly on host (Recommended):
# ✅ CORRECT - Run on host system
pnpm install
pnpm dev
pnpm build
pnpm lint
pnpm lint:fixUnlike the backend which requires container execution for PHP commands, the frontend can run package managers and build tools directly on the host system because:
- Node.js Availability: Modern development systems typically have Node.js installed
- Development Experience: Direct host execution provides better development experience with hot reload
- Tool Compatibility: Frontend build tools work reliably across different host environments
- Performance: Direct execution is often faster than containerized development
| Operation | Correct Command | Notes |
|---|---|---|
| Install dependencies | pnpm install |
Run on host system |
| Development server | pnpm dev |
Runs with SSL on localhost:3000 |
| Build production | pnpm build |
Generate optimized build |
| Preview build | pnpm preview |
Preview production build locally |
| Lint code | pnpm lint |
Check code quality with ESLint |
| Fix lint issues | pnpm lint:fix |
Automatically fix ESLint issues |
- Modular Vue.js components organized by feature
- Reusable generic components in
components/Generic/ - Clear separation between presentational and business logic components
- Pinia stores for centralized state management
- Persistent state for user preferences and cart data
- Club-dependent state management for multi-tenant architecture
- nuxt-api-party for type-safe API calls
- Abstract query classes for consistent data fetching
- Real-time updates via Mercure integration
- Single source of truth: don't duplicate backend default/fallback logic here. When an optional field has no value, omit it from the request payload instead of filling in a hardcoded default — let the backend's own default apply.
- Comprehensive TypeScript usage throughout the application
- Strict typing for API responses and component props
- Type definitions for all major entities and data structures
- Mobile-first approach with Tailwind CSS
- Nuxt UI components for consistent design system
- Dark/light mode support via colorMode configuration
- JWT token-based authentication
- CSRF protection and input validation
- Secure API communication with backend
The application uses multiple layouts for different user roles:
- Admin Layout - Full administrative interface
- Super Admin Layout - Elevated administrative privileges
- Member Layout - Simplified interface for members
- POS Layout - Point of sale specific interface
- Email Layout - Email template layout
- Default Layout - General purpose layout
- Empty Layout - Minimal layout for specific pages
- Pinia Stores - Centralized state management
useAppConfig.ts- Application configurationuseSelfUser.ts- Current user profileuseCartStore.ts- Shopping cart functionalityusePresenceStore.ts- Member presence trackinguseSaleStore.ts- Sales managementuseMetricStore.ts- Analytics and metricsuseExternalPresence.ts- External presence tracking
- Abstract Query Classes - Reusable API query logic
- Club-Dependent Queries - Multi-tenant data fetching
- Plugin-Specific Queries - Modular feature support
- Emailing plugin queries
- Presence plugin queries
- Sale plugin queries
The frontend supports granular permission checks via useSelfUser.can(Permission):
import {Permission} from '~/types/api/permissions';
const selfStore = useSelfUserStore();
// Check if user can access a feature
if (selfStore.can(Permission.EmailAccess)) { /* ... */ }
// EDIT implies ACCESS - this returns true for both
if (selfStore.can(Permission.EmailEdit)) { /* ... */ }Hierarchy: If user has *_EDIT, they automatically have *_ACCESS.
Admins: can() returns true for all permissions when user is admin or super-admin.
Component-level can() checks control what's rendered, but routes must also be gated in app/middleware/auth.global.ts — otherwise a non-admin supervisor without the right permission can still navigate to the page (and only find out it's forbidden once an in-page action fails, or worse, see a page they shouldn't).
The middleware checks /admin/* routes in this order:
supervisorOnlyPaths- route patterns any supervisor can reach, no specific permission required (e.g./admin,/admin/members).permissionPaths- route patterns mapped to thePermissionrequired to view them. Each entry is{ pattern, permission }, wherepermissioncan be a singlePermissionor an array (the user needs any one of them - use an array when a page serves more than one feature, e.g./admin/sales/newacceptsSaleNewORLoanEditbecause it hosts both sale creation and loan creation/return).- Anything matching
/admin/*but not listed in either array is denied (redirects to/admin) for non-admin supervisors, even if the page itself checks permissions correctly.
When adding a new admin page gated by a Permission, add a matching entry to permissionPaths in the same change - it's easy to gate the page's own UI with can() and forget the route is still unreachable for non-admin supervisors. Order matters: more specific patterns (e.g. /admin/sales/new, /admin/loans/items/[id]) must come before more generic ones (e.g. /admin/sales/[id], /admin/loans/items) since patterns are tested in array order and the first match wins.
- Admin, Super Admin, Member, and Badger interfaces
- Role-based navigation and component visibility
- Secure authentication flow
- Presence tracking with real-time updates
- QR code generation and scanning
- Image upload and management
- Chart visualizations with Chart.js
- Rich text editing with TipTap
- Comprehensive CRUD operations
- Advanced filtering and sorting
- Data import/export functionality
- Bulk operations support
- Template-based email creation
- Dynamic content generation
- Email list management
- Automated email workflows
While not explicitly configured in the current setup, the recommended testing approach would include:
- Vitest - Fast unit testing framework
- Vue Test Utils - Vue.js testing utilities
- Playwright - Modern web testing
tests/
├── unit/ # Unit tests for components and utilities
├── integration/ # Integration tests for composables and stores
├── e2e/ # End-to-end tests for user workflows
└── fixtures/ # Test data and mocks
- Code Splitting - Automatic route-based code splitting
- Tree Shaking - Unused code elimination
- Asset Optimization - Image and static asset optimization
- Bundle Analysis - Regular bundle size monitoring
- Lazy Loading - Component and route lazy loading
- Caching Strategy - Intelligent caching for API responses
- Real-Time Updates - Efficient Mercure integration
- State Persistence - Smart state management and persistence
- Local development server with SSL
- Hot reload for immediate feedback
- Docker container for consistent environment
- Optimized Docker builds with multi-stage builds
- Cloud deployment via Docker registries
- Progressive Web App (PWA) capabilities
- Static asset optimization
- Error tracking and logging
- Performance monitoring
- User analytics integration (configurable)
For detailed development guidelines, see the project documentation:
- ESLint for code linting and formatting
- TypeScript for type safety
- Conventional commit messages recommended
- Component composition patterns
GNU AGPLv3 License - See LICENSE file for details.
Created by: Benoît VIGNAL Version: 3.15 Last Updated: 2026-01-03