diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bc37ad78a..2c58b28e2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -3,9 +3,10 @@ default_install_hook_types: [commit-msg, pre-commit] default_stages: [commit, merge-commit] +minimum_pre_commit_version: 3.2.0 repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: check-added-large-files - id: check-builtin-literals @@ -68,12 +69,16 @@ repos: files: "^backend/capellacollab" exclude: "^backend/capellacollab/alembic/" - repo: https://github.com/pre-commit/mirrors-prettier - rev: v2.7.1 + rev: v3.0.3 hooks: - id: prettier types_or: [ts, css, html, markdown] + files: "^frontend" + additional_dependencies: + - "prettier@^3.0.3" + - "prettier-plugin-tailwindcss@^0.5.5" - repo: https://github.com/Lucas-C/pre-commit-hooks - rev: v1.4.2 + rev: v1.5.4 hooks: - id: insert-license name: Insert license headers (shell-style comments) @@ -125,11 +130,11 @@ repos: - --comment-style - "..| |" - repo: https://github.com/fsfe/reuse-tool - rev: v1.1.0 + rev: v2.1.0 hooks: - id: reuse - repo: https://github.com/pre-commit/mirrors-eslint - rev: v8.33.0 + rev: v8.51.0 hooks: - id: eslint additional_dependencies: @@ -143,16 +148,17 @@ repos: - "@typescript-eslint/parser@^6.7.4" - "eslint-plugin-unused-imports@^3.0.0" - "eslint-plugin-deprecation@^2.0.0" + - "eslint-plugin-tailwindcss@^3.13.0" args: ["--fix"] types: [] exclude: '.+\.spec(-helper)?\.ts$' types_or: ["html", "ts"] - repo: https://github.com/qoomon/git-conventional-commits - rev: v2.5.0 + rev: v2.6.5 hooks: - id: conventional-commits - repo: https://github.com/asottile/pyupgrade - rev: v3.3.1 + rev: v3.15.0 hooks: - id: pyupgrade args: ["--py311-plus"] diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 2ee6a05f4..f5fb2ed36 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -4,6 +4,18 @@ */ module.exports = { + settings: { + tailwindcss: { + config: "frontend/tailwind.config.js", + cssFiles: [ + "frontend/**/*.css", + "!**/node_modules", + "!**/.*", + "!**/dist", + "!**/build", + ], + }, + }, overrides: [ { files: ["*.ts"], @@ -27,15 +39,6 @@ module.exports = { style: "camelCase", }, ], - /* Find a proper naming strategy - "@angular-eslint/component-selector": [ - "error", - { - type: "element", - prefix: "app", - style: "kebab-case", - }, - ], */ "import/order": [ "error", { @@ -60,8 +63,18 @@ module.exports = { }, { files: ["*.html"], - extends: ["plugin:@angular-eslint/template/recommended"], - rules: {}, + extends: [ + "plugin:@angular-eslint/template/recommended", + "plugin:tailwindcss/recommended", + ], + parser: "@angular-eslint/template-parser", + rules: { + "tailwindcss/classnames-order": "off", + "tailwindcss/no-custom-classname": "error", + "tailwindcss/enforces-negative-arbitrary-values": "error", + "tailwindcss/enforces-shorthand": "error", + "tailwindcss/no-contradicting-classname": "error", + }, }, ], }; diff --git a/frontend/.prettierrc.js b/frontend/.prettierrc.js new file mode 100644 index 000000000..eae506ad8 --- /dev/null +++ b/frontend/.prettierrc.js @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: Copyright DB Netz AG and the capella-collab-manager contributors + * SPDX-License-Identifier: CC0-1.0 + */ + +module.exports = { + plugins: [require.resolve("prettier-plugin-tailwindcss")], +}; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 5865ca344..73c89cd4d 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -56,6 +56,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-deprecation": "^2.0.0", "eslint-plugin-import": "^2.28.1", + "eslint-plugin-tailwindcss": "^3.13.0", "eslint-plugin-unused-imports": "^3.0.0", "jasmine-core": "~5.1.1", "karma": "~6.4.2", @@ -65,6 +66,8 @@ "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "^2.1.0", "postcss": "^8.4.31", + "prettier": "^3.0.3", + "prettier-plugin-tailwindcss": "^0.5.5", "tailwindcss": "^3.3.3", "typescript": "^5.1.6", "webpack": "^5.88.2" @@ -8927,6 +8930,22 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-tailwindcss": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-tailwindcss/-/eslint-plugin-tailwindcss-3.13.0.tgz", + "integrity": "sha512-Fcep4KDRLWaK3KmkQbdyKHG0P4GdXFmXdDaweTIPcgOP60OOuWFbh1++dufRT28Q4zpKTKaHwTsXPJ4O/EjU2Q==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.5", + "postcss": "^8.4.4" + }, + "engines": { + "node": ">=12.13.0" + }, + "peerDependencies": { + "tailwindcss": "^3.3.2" + } + }, "node_modules/eslint-plugin-unused-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.0.0.tgz", @@ -17945,6 +17964,93 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", + "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "dev": true, + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-tailwindcss": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.5.5.tgz", + "integrity": "sha512-voy0CjWv/CM8yeaduv5ZwovovpTGMR5LbzlhGF+LtEvMJt9wBeVTVnW781hL38R/RcDXCJwN2rolsgr94B/n0Q==", + "dev": true, + "engines": { + "node": ">=14.21.3" + }, + "peerDependencies": { + "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-pug": "*", + "@shopify/prettier-plugin-liquid": "*", + "@shufo/prettier-plugin-blade": "*", + "@trivago/prettier-plugin-sort-imports": "*", + "prettier": "^3.0", + "prettier-plugin-astro": "*", + "prettier-plugin-css-order": "*", + "prettier-plugin-import-sort": "*", + "prettier-plugin-jsdoc": "*", + "prettier-plugin-organize-attributes": "*", + "prettier-plugin-organize-imports": "*", + "prettier-plugin-style-order": "*", + "prettier-plugin-svelte": "*" + }, + "peerDependenciesMeta": { + "@ianvs/prettier-plugin-sort-imports": { + "optional": true + }, + "@prettier/plugin-pug": { + "optional": true + }, + "@shopify/prettier-plugin-liquid": { + "optional": true + }, + "@shufo/prettier-plugin-blade": { + "optional": true + }, + "@trivago/prettier-plugin-sort-imports": { + "optional": true + }, + "prettier-plugin-astro": { + "optional": true + }, + "prettier-plugin-css-order": { + "optional": true + }, + "prettier-plugin-import-sort": { + "optional": true + }, + "prettier-plugin-jsdoc": { + "optional": true + }, + "prettier-plugin-marko": { + "optional": true + }, + "prettier-plugin-organize-attributes": { + "optional": true + }, + "prettier-plugin-organize-imports": { + "optional": true + }, + "prettier-plugin-style-order": { + "optional": true + }, + "prettier-plugin-svelte": { + "optional": true + }, + "prettier-plugin-twig-melody": { + "optional": true + } + } + }, "node_modules/pretty-bytes": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", diff --git a/frontend/package.json b/frontend/package.json index 3a73be8fb..dc4f217db 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -61,6 +61,7 @@ "eslint-config-prettier": "^9.0.0", "eslint-plugin-deprecation": "^2.0.0", "eslint-plugin-import": "^2.28.1", + "eslint-plugin-tailwindcss": "^3.13.0", "eslint-plugin-unused-imports": "^3.0.0", "jasmine-core": "~5.1.1", "karma": "~6.4.2", @@ -70,6 +71,8 @@ "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "^2.1.0", "postcss": "^8.4.31", + "prettier": "^3.0.3", + "prettier-plugin-tailwindcss": "^0.5.5", "tailwindcss": "^3.3.3", "typescript": "^5.1.6", "webpack": "^5.88.2" diff --git a/frontend/src/app/app.component.ts b/frontend/src/app/app.component.ts index d9c5b4539..320654831 100644 --- a/frontend/src/app/app.component.ts +++ b/frontend/src/app/app.component.ts @@ -18,7 +18,7 @@ export class AppComponent implements AfterViewInit { constructor( public pageLayoutService: PageLayoutService, public fullscreenService: FullscreenService, - private navBarService: NavBarService + private navBarService: NavBarService, ) {} @ViewChild('sidenav') private sidenav?: MatSidenav; diff --git a/frontend/src/app/events/events.component.ts b/frontend/src/app/events/events.component.ts index bff7b6d1e..ab9c63ac6 100644 --- a/frontend/src/app/events/events.component.ts +++ b/frontend/src/app/events/events.component.ts @@ -29,7 +29,7 @@ export class EventsComponent implements OnInit, AfterViewInit { historyEventData: HistoryEvent[] = []; historyEventDataSource = new MatTableDataSource( - this.historyEventData + this.historyEventData, ); constructor(private eventService: EventsService) {} diff --git a/frontend/src/app/events/service/events.service.ts b/frontend/src/app/events/service/events.service.ts index b8947e0bf..9c2ef2bee 100644 --- a/frontend/src/app/events/service/events.service.ts +++ b/frontend/src/app/events/service/events.service.ts @@ -29,7 +29,7 @@ export class EventsService { customSortingDataAccessor( data: HistoryEvent, - sortHeaderId: string + sortHeaderId: string, ): string | number { switch (sortHeaderId) { case 'eventType': diff --git a/frontend/src/app/general/auth/auth-guard/auth-guard.service.ts b/frontend/src/app/general/auth/auth-guard/auth-guard.service.ts index 2a4d1a443..26b32c0b2 100644 --- a/frontend/src/app/general/auth/auth-guard/auth-guard.service.ts +++ b/frontend/src/app/general/auth/auth-guard/auth-guard.service.ts @@ -21,7 +21,7 @@ export class AuthGuardService implements CanActivate { canActivate( _route: ActivatedRouteSnapshot, - _state: RouterStateSnapshot + _state: RouterStateSnapshot, ): | Observable | Promise diff --git a/frontend/src/app/general/auth/auth-redirect/auth-redirect.component.ts b/frontend/src/app/general/auth/auth-redirect/auth-redirect.component.ts index b4c52937c..0efec6afc 100644 --- a/frontend/src/app/general/auth/auth-redirect/auth-redirect.component.ts +++ b/frontend/src/app/general/auth/auth-redirect/auth-redirect.component.ts @@ -18,7 +18,7 @@ export class AuthRedirectComponent implements OnInit { private route: ActivatedRoute, private authService: AuthService, private userService: UserService, - private router: Router + private router: Router, ) {} ngOnInit(): void { @@ -30,7 +30,7 @@ export class AuthRedirectComponent implements OnInit { .map((key) => ['error', 'error_description', 'error_uri'].includes(key) ? [key, params[key]].join('=') - : '' + : '', ) .join('&'); this.router.navigateByUrl(redirect_url); diff --git a/frontend/src/app/general/auth/auth/auth.component.ts b/frontend/src/app/general/auth/auth/auth.component.ts index a21e5272f..e9a98b2aa 100644 --- a/frontend/src/app/general/auth/auth/auth.component.ts +++ b/frontend/src/app/general/auth/auth/auth.component.ts @@ -28,7 +28,7 @@ export class AuthComponent implements OnInit { public metadataService: MetadataService, private authService: AuthService, private pageLayoutService: PageLayoutService, - private route: ActivatedRoute + private route: ActivatedRoute, ) { this.pageLayoutService.disableAll(); } diff --git a/frontend/src/app/general/auth/http-interceptor/auth.interceptor.ts b/frontend/src/app/general/auth/http-interceptor/auth.interceptor.ts index a0375bb50..54fc36694 100644 --- a/frontend/src/app/general/auth/http-interceptor/auth.interceptor.ts +++ b/frontend/src/app/general/auth/http-interceptor/auth.interceptor.ts @@ -21,24 +21,27 @@ import { @Injectable() export class AuthInterceptor implements HttpInterceptor { - constructor(private router: Router, private authService: AuthService) {} + constructor( + private router: Router, + private authService: AuthService, + ) {} intercept( request: HttpRequest, - next: HttpHandler + next: HttpHandler, ): Observable> { const req = this.injectAccessToken(request); return next.handle(req).pipe( catchError((err: HttpErrorResponse) => { return this.handleTokenExpired(err, request, next); - }) + }), ); } handleTokenExpired( err: HttpErrorResponse, request: HttpRequest, - next: HttpHandler + next: HttpHandler, ) { if (err.status === 401) { if (err.error.detail.err_code == 'token_exp') { @@ -50,7 +53,7 @@ export class AuthInterceptor implements HttpInterceptor { catchError(() => { this.router.navigateByUrl('/logout?reason=session-expired'); throw err; - }) + }), ); } else { this.router.navigateByUrl('/logout?reason=unauthorized'); diff --git a/frontend/src/app/general/auth/logout/logout-redirect/logout-redirect.component.ts b/frontend/src/app/general/auth/logout/logout-redirect/logout-redirect.component.ts index 3c28dd019..a7a7b98c3 100644 --- a/frontend/src/app/general/auth/logout/logout-redirect/logout-redirect.component.ts +++ b/frontend/src/app/general/auth/logout/logout-redirect/logout-redirect.component.ts @@ -13,7 +13,10 @@ import { AuthService } from 'src/app/services/auth/auth.service'; styleUrls: ['./logout-redirect.component.css'], }) export class LogoutRedirectComponent implements OnInit { - constructor(private authService: AuthService, private router: Router) {} + constructor( + private authService: AuthService, + private router: Router, + ) {} ngOnInit(): void { this.authService.logOut(); diff --git a/frontend/src/app/general/auth/logout/logout/logout.component.ts b/frontend/src/app/general/auth/logout/logout/logout.component.ts index 03c6254c8..1db4c2ce9 100644 --- a/frontend/src/app/general/auth/logout/logout/logout.component.ts +++ b/frontend/src/app/general/auth/logout/logout/logout.component.ts @@ -18,7 +18,7 @@ export class LogoutComponent implements OnInit { constructor( private route: ActivatedRoute, - private pageLayoutService: PageLayoutService + private pageLayoutService: PageLayoutService, ) { this.pageLayoutService.disableAll(); } diff --git a/frontend/src/app/general/breadcrumbs/breadcrumbs.component.html b/frontend/src/app/general/breadcrumbs/breadcrumbs.component.html index e943c601a..cec94d5fe 100644 --- a/frontend/src/app/general/breadcrumbs/breadcrumbs.component.html +++ b/frontend/src/app/general/breadcrumbs/breadcrumbs.component.html @@ -6,7 +6,7 @@