@iconsulting-dev/forgekit 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +203 -0
- package/dist/commands/new.d.ts +3 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +120 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +1 -0
- package/dist/generators/backend/index.d.ts +4 -0
- package/dist/generators/backend/index.d.ts.map +1 -0
- package/dist/generators/backend/index.js +69 -0
- package/dist/generators/backend/index.js.map +1 -0
- package/dist/generators/base-generator.d.ts +9 -0
- package/dist/generators/base-generator.d.ts.map +1 -0
- package/dist/generators/base-generator.js +13 -0
- package/dist/generators/base-generator.js.map +1 -0
- package/dist/generators/ci/index.d.ts +3 -0
- package/dist/generators/ci/index.d.ts.map +1 -0
- package/dist/generators/ci/index.js +18 -0
- package/dist/generators/ci/index.js.map +1 -0
- package/dist/generators/claude-code/index.d.ts +4 -0
- package/dist/generators/claude-code/index.d.ts.map +1 -0
- package/dist/generators/claude-code/index.js +47 -0
- package/dist/generators/claude-code/index.js.map +1 -0
- package/dist/generators/docker/index.d.ts +3 -0
- package/dist/generators/docker/index.d.ts.map +1 -0
- package/dist/generators/docker/index.js +14 -0
- package/dist/generators/docker/index.js.map +1 -0
- package/dist/generators/frontend/index.d.ts +4 -0
- package/dist/generators/frontend/index.d.ts.map +1 -0
- package/dist/generators/frontend/index.js +60 -0
- package/dist/generators/frontend/index.js.map +1 -0
- package/dist/generators/git.d.ts +2 -0
- package/dist/generators/git.d.ts.map +1 -0
- package/dist/generators/git.js +16 -0
- package/dist/generators/git.js.map +1 -0
- package/dist/generators/root/index.d.ts +4 -0
- package/dist/generators/root/index.d.ts.map +1 -0
- package/dist/generators/root/index.js +29 -0
- package/dist/generators/root/index.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/prompts/project.d.ts +3 -0
- package/dist/prompts/project.d.ts.map +1 -0
- package/dist/prompts/project.js +70 -0
- package/dist/prompts/project.js.map +1 -0
- package/dist/templates/backend/ApiError.java.hbs +4 -0
- package/dist/templates/backend/Application.java.hbs +12 -0
- package/dist/templates/backend/ApplicationTests.java.hbs +12 -0
- package/dist/templates/backend/GlobalExceptionHandler.java.hbs +27 -0
- package/dist/templates/backend/OpenApiConfig.java.hbs +19 -0
- package/dist/templates/backend/PageResponse.java.hbs +12 -0
- package/dist/templates/backend/SecurityConfig.java.hbs +43 -0
- package/dist/templates/backend/application-dev.yml.hbs +7 -0
- package/dist/templates/backend/application.yml.hbs +37 -0
- package/dist/templates/backend/gitignore.hbs +16 -0
- package/dist/templates/backend/maven-wrapper.properties.hbs +2 -0
- package/dist/templates/backend/mvnw.cmd.hbs +4 -0
- package/dist/templates/backend/mvnw.hbs +17 -0
- package/dist/templates/backend/pom.xml.hbs +136 -0
- package/dist/templates/ci/ci.yml.hbs +87 -0
- package/dist/templates/claude-code/CLAUDE.md.hbs +79 -0
- package/dist/templates/claude-code/settings.json.hbs +9 -0
- package/dist/templates/docker/docker-compose.yml.hbs +34 -0
- package/dist/templates/frontend/angular.json.hbs +71 -0
- package/dist/templates/frontend/app.component.ts.hbs +11 -0
- package/dist/templates/frontend/app.config.ts.hbs +16 -0
- package/dist/templates/frontend/app.routes.ts.hbs +14 -0
- package/dist/templates/frontend/auth.guard.ts.hbs +14 -0
- package/dist/templates/frontend/auth.interceptor.ts.hbs +13 -0
- package/dist/templates/frontend/auth.service.ts.hbs +18 -0
- package/dist/templates/frontend/environment.development.ts.hbs +4 -0
- package/dist/templates/frontend/environment.ts.hbs +4 -0
- package/dist/templates/frontend/error.interceptor.ts.hbs +14 -0
- package/dist/templates/frontend/gitignore.hbs +4 -0
- package/dist/templates/frontend/index.html.hbs +13 -0
- package/dist/templates/frontend/layout.component.ts.hbs +42 -0
- package/dist/templates/frontend/main.ts.hbs +6 -0
- package/dist/templates/frontend/package.json.hbs +35 -0
- package/dist/templates/frontend/sidebar.component.ts.hbs +58 -0
- package/dist/templates/frontend/styles.scss.hbs +8 -0
- package/dist/templates/frontend/topbar.component.ts.hbs +59 -0
- package/dist/templates/frontend/tsconfig.app.json.hbs +13 -0
- package/dist/templates/frontend/tsconfig.json.hbs +24 -0
- package/dist/templates/root/README.md.hbs +37 -0
- package/dist/templates/root/gitignore.hbs +12 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/__tests__/validation.test.d.ts +2 -0
- package/dist/utils/__tests__/validation.test.d.ts.map +1 -0
- package/dist/utils/__tests__/validation.test.js +59 -0
- package/dist/utils/__tests__/validation.test.js.map +1 -0
- package/dist/utils/template-engine.d.ts +5 -0
- package/dist/utils/template-engine.d.ts.map +1 -0
- package/dist/utils/template-engine.js +23 -0
- package/dist/utils/template-engine.js.map +1 -0
- package/dist/utils/validation.d.ts +3 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +19 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/versions.d.ts +18 -0
- package/dist/versions.d.ts.map +1 -0
- package/dist/versions.js +91 -0
- package/dist/versions.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
changes:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
pull-requests: read
|
|
14
|
+
outputs:
|
|
15
|
+
{{#if backend}}
|
|
16
|
+
backend: $\{{ steps.filter.outputs.backend }}
|
|
17
|
+
{{/if}}
|
|
18
|
+
{{#if frontend}}
|
|
19
|
+
frontend: $\{{ steps.filter.outputs.frontend }}
|
|
20
|
+
{{/if}}
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: dorny/paths-filter@v3
|
|
24
|
+
id: filter
|
|
25
|
+
with:
|
|
26
|
+
filters: |
|
|
27
|
+
{{#if backend}}
|
|
28
|
+
backend:
|
|
29
|
+
- 'backend/**'
|
|
30
|
+
{{/if}}
|
|
31
|
+
{{#if frontend}}
|
|
32
|
+
frontend:
|
|
33
|
+
- 'frontend/**'
|
|
34
|
+
{{/if}}
|
|
35
|
+
|
|
36
|
+
{{#if backend}}
|
|
37
|
+
backend:
|
|
38
|
+
needs: changes
|
|
39
|
+
if: needs.changes.outputs.backend == 'true'
|
|
40
|
+
runs-on: ubuntu-latest
|
|
41
|
+
defaults:
|
|
42
|
+
run:
|
|
43
|
+
working-directory: backend
|
|
44
|
+
steps:
|
|
45
|
+
- uses: actions/checkout@v4
|
|
46
|
+
|
|
47
|
+
- name: Set up Java 21
|
|
48
|
+
uses: actions/setup-java@v4
|
|
49
|
+
with:
|
|
50
|
+
distribution: corretto
|
|
51
|
+
java-version: '21'
|
|
52
|
+
cache: maven
|
|
53
|
+
|
|
54
|
+
- name: Build & Test
|
|
55
|
+
run: ./mvnw verify --batch-mode --no-transfer-progress
|
|
56
|
+
{{/if}}
|
|
57
|
+
|
|
58
|
+
{{#if frontend}}
|
|
59
|
+
frontend:
|
|
60
|
+
needs: changes
|
|
61
|
+
if: needs.changes.outputs.frontend == 'true'
|
|
62
|
+
runs-on: ubuntu-latest
|
|
63
|
+
defaults:
|
|
64
|
+
run:
|
|
65
|
+
working-directory: frontend
|
|
66
|
+
steps:
|
|
67
|
+
- uses: actions/checkout@v4
|
|
68
|
+
|
|
69
|
+
- name: Set up Node 22
|
|
70
|
+
uses: actions/setup-node@v4
|
|
71
|
+
with:
|
|
72
|
+
node-version: '22'
|
|
73
|
+
cache: npm
|
|
74
|
+
cache-dependency-path: frontend/package-lock.json
|
|
75
|
+
|
|
76
|
+
- name: Install dependencies
|
|
77
|
+
run: npm ci
|
|
78
|
+
|
|
79
|
+
- name: Lint
|
|
80
|
+
run: npm run lint
|
|
81
|
+
|
|
82
|
+
- name: Test
|
|
83
|
+
run: npm run test -- --watch=false --browsers=ChromeHeadless
|
|
84
|
+
|
|
85
|
+
- name: Build
|
|
86
|
+
run: npm run build
|
|
87
|
+
{{/if}}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# {{name}}
|
|
2
|
+
|
|
3
|
+
{{description}}
|
|
4
|
+
|
|
5
|
+
## Project Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
{{name}}/
|
|
9
|
+
{{#if backend}}
|
|
10
|
+
├── backend/ # Spring Boot API
|
|
11
|
+
{{/if}}
|
|
12
|
+
{{#if frontend}}
|
|
13
|
+
├── frontend/ # Angular SPA
|
|
14
|
+
{{/if}}
|
|
15
|
+
{{#if docker}}
|
|
16
|
+
├── docker-compose.yml
|
|
17
|
+
{{/if}}
|
|
18
|
+
└── README.md
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
{{#if backend}}
|
|
22
|
+
## Backend — Spring Boot {{versions.springBoot}}
|
|
23
|
+
|
|
24
|
+
- **Java 21** with records, pattern matching, sealed classes
|
|
25
|
+
- **Architecture:** Feature-based package structure
|
|
26
|
+
- **DB:** PostgreSQL, Flyway migrations in `backend/src/main/resources/db/migration/`
|
|
27
|
+
- **Conventions:** Records for DTOs, Lombok where useful, MapStruct for mappings
|
|
28
|
+
- **Validation:** Jakarta Bean Validation
|
|
29
|
+
- **Logging:** SLF4J
|
|
30
|
+
|
|
31
|
+
### Commands
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cd backend
|
|
35
|
+
./mvnw spring-boot:run # Start dev server
|
|
36
|
+
./mvnw test # Run tests
|
|
37
|
+
./mvnw package -DskipTests # Build JAR
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
{{/if}}
|
|
41
|
+
{{#if frontend}}
|
|
42
|
+
## Frontend — Angular
|
|
43
|
+
|
|
44
|
+
- **Angular {{versions.angular}}** with strict mode, standalone components, signals
|
|
45
|
+
- **UI:** PrimeNG {{versions.primeng}}, theme Aura, PrimeFlex
|
|
46
|
+
- **State:** NgRx SignalStore
|
|
47
|
+
- **Conventions:** OnPush change detection, `input()`/`output()` functions, `inject()` for DI
|
|
48
|
+
- **Control flow:** `@if`, `@for` (not *ngIf/*ngFor)
|
|
49
|
+
- **Indentation:** 2 spaces
|
|
50
|
+
|
|
51
|
+
### Commands
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
cd frontend
|
|
55
|
+
npm install # Install dependencies
|
|
56
|
+
ng serve # Start dev server (port 4200)
|
|
57
|
+
ng build # Production build
|
|
58
|
+
ng test # Run tests
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
{{/if}}
|
|
62
|
+
{{#if docker}}
|
|
63
|
+
## Infrastructure
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
docker compose up -d # Start PostgreSQL + pgAdmin
|
|
67
|
+
docker compose down # Stop services
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- **PostgreSQL:** localhost:5432
|
|
71
|
+
- **pgAdmin:** localhost:5050 (admin@admin.com / admin)
|
|
72
|
+
|
|
73
|
+
{{/if}}
|
|
74
|
+
## Conventions
|
|
75
|
+
|
|
76
|
+
- **Git:** Conventional commits (`feat:`, `fix:`, `refactor:`, `chore:`)
|
|
77
|
+
- **Branches:** `feature/`, `fix/`, `refactor/`, `chore/`
|
|
78
|
+
- **DB:** snake_case naming
|
|
79
|
+
- **Security:** Never commit secrets, use environment variables
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
services:
|
|
2
|
+
postgres:
|
|
3
|
+
image: postgres:17
|
|
4
|
+
container_name: {{dbName}}_postgres
|
|
5
|
+
restart: unless-stopped
|
|
6
|
+
environment:
|
|
7
|
+
POSTGRES_DB: {{dbName}}
|
|
8
|
+
POSTGRES_USER: postgres
|
|
9
|
+
POSTGRES_PASSWORD: postgres
|
|
10
|
+
ports:
|
|
11
|
+
- "5432:5432"
|
|
12
|
+
volumes:
|
|
13
|
+
- postgres_data:/var/lib/postgresql/data
|
|
14
|
+
healthcheck:
|
|
15
|
+
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
16
|
+
interval: 10s
|
|
17
|
+
timeout: 5s
|
|
18
|
+
retries: 5
|
|
19
|
+
|
|
20
|
+
pgadmin:
|
|
21
|
+
image: dpage/pgadmin4:latest
|
|
22
|
+
container_name: {{dbName}}_pgadmin
|
|
23
|
+
restart: unless-stopped
|
|
24
|
+
environment:
|
|
25
|
+
PGADMIN_DEFAULT_EMAIL: admin@admin.com
|
|
26
|
+
PGADMIN_DEFAULT_PASSWORD: admin
|
|
27
|
+
ports:
|
|
28
|
+
- "5050:80"
|
|
29
|
+
depends_on:
|
|
30
|
+
postgres:
|
|
31
|
+
condition: service_healthy
|
|
32
|
+
|
|
33
|
+
volumes:
|
|
34
|
+
postgres_data:
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"newProjectRoot": "projects",
|
|
5
|
+
"projects": {
|
|
6
|
+
"{{projectName}}": {
|
|
7
|
+
"projectType": "application",
|
|
8
|
+
"root": "",
|
|
9
|
+
"sourceRoot": "src",
|
|
10
|
+
"prefix": "app",
|
|
11
|
+
"architect": {
|
|
12
|
+
"build": {
|
|
13
|
+
"builder": "@angular/build:application",
|
|
14
|
+
"options": {
|
|
15
|
+
"outputPath": "dist/{{projectName}}",
|
|
16
|
+
"index": "src/index.html",
|
|
17
|
+
"browser": "src/main.ts",
|
|
18
|
+
"tsConfig": "tsconfig.app.json",
|
|
19
|
+
"inlineStyleLanguage": "scss",
|
|
20
|
+
"styles": [
|
|
21
|
+
"node_modules/primeicons/primeicons.css",
|
|
22
|
+
"node_modules/primeflex/primeflex.css",
|
|
23
|
+
"src/styles.scss"
|
|
24
|
+
]
|
|
25
|
+
},
|
|
26
|
+
"configurations": {
|
|
27
|
+
"production": {
|
|
28
|
+
"budgets": [
|
|
29
|
+
{
|
|
30
|
+
"type": "initial",
|
|
31
|
+
"maximumWarning": "500kB",
|
|
32
|
+
"maximumError": "1MB"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"type": "anyComponentStyle",
|
|
36
|
+
"maximumWarning": "4kB",
|
|
37
|
+
"maximumError": "8kB"
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"outputHashing": "all"
|
|
41
|
+
},
|
|
42
|
+
"development": {
|
|
43
|
+
"optimization": false,
|
|
44
|
+
"extractLicenses": false,
|
|
45
|
+
"sourceMap": true,
|
|
46
|
+
"fileReplacements": [
|
|
47
|
+
{
|
|
48
|
+
"replace": "src/environments/environment.ts",
|
|
49
|
+
"with": "src/environments/environment.development.ts"
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"defaultConfiguration": "production"
|
|
55
|
+
},
|
|
56
|
+
"serve": {
|
|
57
|
+
"builder": "@angular/build:dev-server",
|
|
58
|
+
"configurations": {
|
|
59
|
+
"production": {
|
|
60
|
+
"buildTarget": "{{projectName}}:build:production"
|
|
61
|
+
},
|
|
62
|
+
"development": {
|
|
63
|
+
"buildTarget": "{{projectName}}:build:development"
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"defaultConfiguration": "development"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Component, ChangeDetectionStrategy } from '@angular/core';
|
|
2
|
+
import { RouterOutlet } from '@angular/router';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'app-root',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [RouterOutlet],
|
|
8
|
+
template: '<router-outlet />',
|
|
9
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
10
|
+
})
|
|
11
|
+
export class AppComponent {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
|
|
2
|
+
import { provideRouter, withComponentInputBinding } from '@angular/router';
|
|
3
|
+
import { provideHttpClient, withInterceptors } from '@angular/common/http';
|
|
4
|
+
import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
|
|
5
|
+
import { routes } from './app.routes';
|
|
6
|
+
import { authInterceptor } from './core/interceptors/auth.interceptor';
|
|
7
|
+
import { errorInterceptor } from './core/interceptors/error.interceptor';
|
|
8
|
+
|
|
9
|
+
export const appConfig: ApplicationConfig = {
|
|
10
|
+
providers: [
|
|
11
|
+
provideZoneChangeDetection({ eventCoalescing: true }),
|
|
12
|
+
provideRouter(routes, withComponentInputBinding()),
|
|
13
|
+
provideHttpClient(withInterceptors([authInterceptor, errorInterceptor])),
|
|
14
|
+
provideAnimationsAsync(),
|
|
15
|
+
],
|
|
16
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Routes } from '@angular/router';
|
|
2
|
+
import { LayoutComponent } from './layout/layout.component';
|
|
3
|
+
|
|
4
|
+
export const routes: Routes = [
|
|
5
|
+
{
|
|
6
|
+
path: '',
|
|
7
|
+
component: LayoutComponent,
|
|
8
|
+
children: [
|
|
9
|
+
// Add feature routes here with lazy loading:
|
|
10
|
+
// { path: 'dashboard', loadComponent: () => import('./features/dashboard/dashboard.component').then(m => m.DashboardComponent) },
|
|
11
|
+
{ path: '', redirectTo: 'home', pathMatch: 'full' },
|
|
12
|
+
],
|
|
13
|
+
},
|
|
14
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { inject } from '@angular/core';
|
|
2
|
+
import { CanActivateFn, Router } from '@angular/router';
|
|
3
|
+
import { AuthService } from '../services/auth.service';
|
|
4
|
+
|
|
5
|
+
export const authGuard: CanActivateFn = () => {
|
|
6
|
+
const authService = inject(AuthService);
|
|
7
|
+
const router = inject(Router);
|
|
8
|
+
|
|
9
|
+
if (authService.isAuthenticated()) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return router.createUrlTree(['/login']);
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { HttpInterceptorFn } from '@angular/common/http';
|
|
2
|
+
|
|
3
|
+
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
|
4
|
+
const token = localStorage.getItem('access_token');
|
|
5
|
+
|
|
6
|
+
if (token) {
|
|
7
|
+
req = req.clone({
|
|
8
|
+
setHeaders: { Authorization: `Bearer ${token}` },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
return next(req);
|
|
13
|
+
};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Injectable, signal, computed } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Injectable({ providedIn: 'root' })
|
|
4
|
+
export class AuthService {
|
|
5
|
+
private readonly token = signal<string | null>(localStorage.getItem('access_token'));
|
|
6
|
+
|
|
7
|
+
readonly isAuthenticated = computed(() => !!this.token());
|
|
8
|
+
|
|
9
|
+
login(token: string): void {
|
|
10
|
+
localStorage.setItem('access_token', token);
|
|
11
|
+
this.token.set(token);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
logout(): void {
|
|
15
|
+
localStorage.removeItem('access_token');
|
|
16
|
+
this.token.set(null);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { HttpInterceptorFn } from '@angular/common/http';
|
|
2
|
+
import { catchError, throwError } from 'rxjs';
|
|
3
|
+
|
|
4
|
+
export const errorInterceptor: HttpInterceptorFn = (req, next) => {
|
|
5
|
+
return next(req).pipe(
|
|
6
|
+
catchError((error) => {
|
|
7
|
+
if (error.status === 401) {
|
|
8
|
+
localStorage.removeItem('access_token');
|
|
9
|
+
window.location.href = '/login';
|
|
10
|
+
}
|
|
11
|
+
return throwError(() => error);
|
|
12
|
+
}),
|
|
13
|
+
);
|
|
14
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="fr">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>{{name}}</title>
|
|
6
|
+
<base href="/">
|
|
7
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
8
|
+
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<app-root></app-root>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Component, ChangeDetectionStrategy, signal } from '@angular/core';
|
|
2
|
+
import { RouterOutlet } from '@angular/router';
|
|
3
|
+
import { SidebarComponent } from './sidebar/sidebar.component';
|
|
4
|
+
import { TopbarComponent } from './topbar/topbar.component';
|
|
5
|
+
|
|
6
|
+
@Component({
|
|
7
|
+
selector: 'app-layout',
|
|
8
|
+
standalone: true,
|
|
9
|
+
imports: [RouterOutlet, SidebarComponent, TopbarComponent],
|
|
10
|
+
template: `
|
|
11
|
+
<div class="layout-wrapper">
|
|
12
|
+
<app-topbar (toggleSidebar)="sidebarVisible.set(!sidebarVisible())" />
|
|
13
|
+
<div class="layout-content-wrapper">
|
|
14
|
+
<app-sidebar [visible]="sidebarVisible()" />
|
|
15
|
+
<main class="layout-main">
|
|
16
|
+
<router-outlet />
|
|
17
|
+
</main>
|
|
18
|
+
</div>
|
|
19
|
+
</div>
|
|
20
|
+
`,
|
|
21
|
+
styles: `
|
|
22
|
+
.layout-wrapper {
|
|
23
|
+
display: flex;
|
|
24
|
+
flex-direction: column;
|
|
25
|
+
height: 100vh;
|
|
26
|
+
}
|
|
27
|
+
.layout-content-wrapper {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex: 1;
|
|
30
|
+
overflow: hidden;
|
|
31
|
+
}
|
|
32
|
+
.layout-main {
|
|
33
|
+
flex: 1;
|
|
34
|
+
padding: 1.5rem;
|
|
35
|
+
overflow-y: auto;
|
|
36
|
+
}
|
|
37
|
+
`,
|
|
38
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
39
|
+
})
|
|
40
|
+
export class LayoutComponent {
|
|
41
|
+
sidebarVisible = signal(true);
|
|
42
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}-frontend",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"scripts": {
|
|
5
|
+
"ng": "ng",
|
|
6
|
+
"start": "ng serve",
|
|
7
|
+
"build": "ng build",
|
|
8
|
+
"watch": "ng build --watch --configuration development",
|
|
9
|
+
"test": "ng test"
|
|
10
|
+
},
|
|
11
|
+
"private": true,
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@angular/animations": "^{{versions.angular}}",
|
|
14
|
+
"@angular/common": "^{{versions.angular}}",
|
|
15
|
+
"@angular/compiler": "^{{versions.angular}}",
|
|
16
|
+
"@angular/core": "^{{versions.angular}}",
|
|
17
|
+
"@angular/forms": "^{{versions.angular}}",
|
|
18
|
+
"@angular/platform-browser": "^{{versions.angular}}",
|
|
19
|
+
"@angular/platform-browser-dynamic": "^{{versions.angular}}",
|
|
20
|
+
"@angular/router": "^{{versions.angular}}",
|
|
21
|
+
"@ngrx/signals": "^{{versions.ngrxSignals}}",
|
|
22
|
+
"primeng": "^{{versions.primeng}}",
|
|
23
|
+
"primeicons": "^{{versions.primeicons}}",
|
|
24
|
+
"primeflex": "^{{versions.primeflex}}",
|
|
25
|
+
"rxjs": "~{{versions.rxjs}}",
|
|
26
|
+
"tslib": "^2.8.0",
|
|
27
|
+
"zone.js": "~{{versions.zoneJs}}"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@angular/build": "^{{versions.angular}}",
|
|
31
|
+
"@angular/cli": "^{{versions.angular}}",
|
|
32
|
+
"@angular/compiler-cli": "^{{versions.angular}}",
|
|
33
|
+
"typescript": "~{{versions.typescript}}"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Component, ChangeDetectionStrategy, input } from '@angular/core';
|
|
2
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
3
|
+
|
|
4
|
+
@Component({
|
|
5
|
+
selector: 'app-sidebar',
|
|
6
|
+
standalone: true,
|
|
7
|
+
imports: [RouterLink, RouterLinkActive],
|
|
8
|
+
template: `
|
|
9
|
+
@if (visible()) {
|
|
10
|
+
<aside class="layout-sidebar">
|
|
11
|
+
<nav>
|
|
12
|
+
<ul class="sidebar-menu">
|
|
13
|
+
<li>
|
|
14
|
+
<a routerLink="/home" routerLinkActive="active-link">
|
|
15
|
+
<i class="pi pi-home"></i>
|
|
16
|
+
<span>Accueil</span>
|
|
17
|
+
</a>
|
|
18
|
+
</li>
|
|
19
|
+
</ul>
|
|
20
|
+
</nav>
|
|
21
|
+
</aside>
|
|
22
|
+
}
|
|
23
|
+
`,
|
|
24
|
+
styles: `
|
|
25
|
+
.layout-sidebar {
|
|
26
|
+
width: 250px;
|
|
27
|
+
background: var(--surface-card);
|
|
28
|
+
border-right: 1px solid var(--surface-border);
|
|
29
|
+
padding: 1rem;
|
|
30
|
+
}
|
|
31
|
+
.sidebar-menu {
|
|
32
|
+
list-style: none;
|
|
33
|
+
padding: 0;
|
|
34
|
+
margin: 0;
|
|
35
|
+
}
|
|
36
|
+
.sidebar-menu a {
|
|
37
|
+
display: flex;
|
|
38
|
+
align-items: center;
|
|
39
|
+
gap: 0.75rem;
|
|
40
|
+
padding: 0.75rem 1rem;
|
|
41
|
+
border-radius: var(--border-radius);
|
|
42
|
+
color: var(--text-color);
|
|
43
|
+
text-decoration: none;
|
|
44
|
+
transition: background 0.2s;
|
|
45
|
+
}
|
|
46
|
+
.sidebar-menu a:hover {
|
|
47
|
+
background: var(--surface-hover);
|
|
48
|
+
}
|
|
49
|
+
.active-link {
|
|
50
|
+
background: var(--primary-color) !important;
|
|
51
|
+
color: var(--primary-color-text) !important;
|
|
52
|
+
}
|
|
53
|
+
`,
|
|
54
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
55
|
+
})
|
|
56
|
+
export class SidebarComponent {
|
|
57
|
+
visible = input(true);
|
|
58
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Component, ChangeDetectionStrategy, output } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
@Component({
|
|
4
|
+
selector: 'app-topbar',
|
|
5
|
+
standalone: true,
|
|
6
|
+
template: `
|
|
7
|
+
<header class="layout-topbar">
|
|
8
|
+
<div class="topbar-left">
|
|
9
|
+
<button class="topbar-menu-btn" (click)="toggleSidebar.emit()">
|
|
10
|
+
<i class="pi pi-bars"></i>
|
|
11
|
+
</button>
|
|
12
|
+
<span class="topbar-title">ForgeKit App</span>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="topbar-right">
|
|
15
|
+
<button class="topbar-avatar">
|
|
16
|
+
<i class="pi pi-user"></i>
|
|
17
|
+
</button>
|
|
18
|
+
</div>
|
|
19
|
+
</header>
|
|
20
|
+
`,
|
|
21
|
+
styles: `
|
|
22
|
+
.layout-topbar {
|
|
23
|
+
display: flex;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: space-between;
|
|
26
|
+
padding: 0 1.5rem;
|
|
27
|
+
height: 60px;
|
|
28
|
+
background: var(--surface-card);
|
|
29
|
+
border-bottom: 1px solid var(--surface-border);
|
|
30
|
+
}
|
|
31
|
+
.topbar-left {
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
gap: 1rem;
|
|
35
|
+
}
|
|
36
|
+
.topbar-menu-btn, .topbar-avatar {
|
|
37
|
+
background: none;
|
|
38
|
+
border: none;
|
|
39
|
+
cursor: pointer;
|
|
40
|
+
font-size: 1.25rem;
|
|
41
|
+
color: var(--text-color);
|
|
42
|
+
padding: 0.5rem;
|
|
43
|
+
border-radius: 50%;
|
|
44
|
+
transition: background 0.2s;
|
|
45
|
+
}
|
|
46
|
+
.topbar-menu-btn:hover, .topbar-avatar:hover {
|
|
47
|
+
background: var(--surface-hover);
|
|
48
|
+
}
|
|
49
|
+
.topbar-title {
|
|
50
|
+
font-size: 1.25rem;
|
|
51
|
+
font-weight: 600;
|
|
52
|
+
color: var(--text-color);
|
|
53
|
+
}
|
|
54
|
+
`,
|
|
55
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
56
|
+
})
|
|
57
|
+
export class TopbarComponent {
|
|
58
|
+
toggleSidebar = output();
|
|
59
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compileOnSave": false,
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"outDir": "./dist/out-tsc",
|
|
5
|
+
"strict": true,
|
|
6
|
+
"noUnusedLocals": true,
|
|
7
|
+
"noUnusedParameters": true,
|
|
8
|
+
"noImplicitReturns": true,
|
|
9
|
+
"noFallthroughCasesInSwitch": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"declaration": false,
|
|
12
|
+
"downlevelIteration": true,
|
|
13
|
+
"experimentalDecorators": true,
|
|
14
|
+
"moduleResolution": "node",
|
|
15
|
+
"importHelpers": true,
|
|
16
|
+
"target": "ES2022",
|
|
17
|
+
"module": "ES2022",
|
|
18
|
+
"lib": [
|
|
19
|
+
"ES2022",
|
|
20
|
+
"dom"
|
|
21
|
+
],
|
|
22
|
+
"useDefineForClassFields": false
|
|
23
|
+
}
|
|
24
|
+
}
|