theme
This commit is contained in:
parent
be67268546
commit
fd276b2c07
7
client/libs/shared/i18n/README.md
Normal file
7
client/libs/shared/i18n/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# I18n
|
||||
|
||||
This library provides internationalization and language selection functionality for the application.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test i18n` to execute the unit tests.
|
||||
5
client/libs/shared/i18n/eslint.config.mjs
Normal file
5
client/libs/shared/i18n/eslint.config.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
name: 'i18n',
|
||||
extends: ['../../../eslint.base.config.mjs'],
|
||||
};
|
||||
22
client/libs/shared/i18n/jest.config.ts
Normal file
22
client/libs/shared/i18n/jest.config.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'i18n',
|
||||
preset: '../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../coverage/libs/shared/i18n',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
21
client/libs/shared/i18n/project.json
Normal file
21
client/libs/shared/i18n/project.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "i18n",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/shared/i18n/src",
|
||||
"prefix": "lib",
|
||||
"tags": [],
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/shared/i18n/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
2
client/libs/shared/i18n/src/index.ts
Normal file
2
client/libs/shared/i18n/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './lib/language.service';
|
||||
export * from './lib/language-selector.component';
|
||||
@ -0,0 +1,44 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { LanguageService } from './language.service';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-language-selector',
|
||||
standalone: true,
|
||||
imports: [MatButtonModule, MatIcon, MatMenuModule],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<button
|
||||
mat-icon-button
|
||||
[matMenuTriggerFor]="languageMenu"
|
||||
class="action-button"
|
||||
aria-label="Select language"
|
||||
>
|
||||
<mat-icon>translate</mat-icon>
|
||||
</button>
|
||||
<mat-menu #languageMenu="matMenu">
|
||||
<button mat-menu-item (click)="languageService.setLanguage('en')">
|
||||
<span>English</span>
|
||||
</button>
|
||||
<button mat-menu-item (click)="languageService.setLanguage('sl')">
|
||||
<span>Slovenian</span>
|
||||
</button>
|
||||
</mat-menu>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.action-button {
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.action-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class LanguageSelectorComponent {
|
||||
protected languageService = inject(LanguageService);
|
||||
}
|
||||
39
client/libs/shared/i18n/src/lib/language.service.ts
Normal file
39
client/libs/shared/i18n/src/lib/language.service.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
|
||||
export type SupportedLanguage = 'en' | 'sl';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LanguageService {
|
||||
private readonly LANGUAGE_KEY = 'selectedLanguage';
|
||||
|
||||
currentLanguage = signal<SupportedLanguage>(this.#getInitialLanguage());
|
||||
|
||||
#getInitialLanguage(): SupportedLanguage {
|
||||
// Try to get language from localStorage
|
||||
const storedLang = localStorage.getItem(
|
||||
this.LANGUAGE_KEY
|
||||
) as SupportedLanguage | null;
|
||||
if (storedLang && (storedLang === 'en' || storedLang === 'sl')) {
|
||||
return storedLang;
|
||||
}
|
||||
|
||||
// Try to detect from browser settings
|
||||
const browserLang = navigator.language.substring(0, 2).toLowerCase();
|
||||
if (browserLang === 'sl') {
|
||||
return 'sl';
|
||||
}
|
||||
|
||||
// Default to English
|
||||
return 'en';
|
||||
}
|
||||
|
||||
setLanguage(lang: SupportedLanguage): void {
|
||||
this.currentLanguage.set(lang);
|
||||
localStorage.setItem(this.LANGUAGE_KEY, lang);
|
||||
// This is where you would typically trigger translation changes
|
||||
// in a real implementation
|
||||
console.log(`Language set to: ${lang}`);
|
||||
}
|
||||
}
|
||||
16
client/libs/shared/i18n/tsconfig.json
Normal file
16
client/libs/shared/i18n/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs"
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
15
client/libs/shared/i18n/tsconfig.lib.json
Normal file
15
client/libs/shared/i18n/tsconfig.lib.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"inlineSources": true
|
||||
},
|
||||
"exclude": [
|
||||
"src/test-setup.ts",
|
||||
"**/*.spec.ts",
|
||||
"jest.config.ts",
|
||||
"**/*.test.ts"
|
||||
]
|
||||
}
|
||||
13
client/libs/shared/i18n/tsconfig.spec.json
Normal file
13
client/libs/shared/i18n/tsconfig.spec.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@ -3,10 +3,19 @@ import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { ThemeToggleComponent } from '@org/shared/theme';
|
||||
import { LanguageSelectorComponent } from '@org/shared/i18n';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-navbar',
|
||||
imports: [MatToolbarModule, MatIcon, MatButtonModule, RouterModule],
|
||||
imports: [
|
||||
MatToolbarModule,
|
||||
MatIcon,
|
||||
MatButtonModule,
|
||||
RouterModule,
|
||||
ThemeToggleComponent,
|
||||
LanguageSelectorComponent,
|
||||
],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<mat-toolbar color="primary" class="navbar">
|
||||
@ -15,8 +24,35 @@ import { RouterModule } from '@angular/router';
|
||||
<span class="brand">TaskManager</span>
|
||||
</span>
|
||||
<span class="spacer"></span>
|
||||
<a mat-button routerLink="/users" routerLinkActive="active-link">Users</a>
|
||||
<a mat-button routerLink="/tasks" routerLinkActive="active-link">Tasks</a>
|
||||
|
||||
<div class="nav-links">
|
||||
<a
|
||||
mat-button
|
||||
routerLink="/users"
|
||||
routerLinkActive="active-link"
|
||||
class="nav-button"
|
||||
>
|
||||
<mat-icon>people</mat-icon>
|
||||
<span>Users</span>
|
||||
</a>
|
||||
<a
|
||||
mat-button
|
||||
routerLink="/tasks"
|
||||
routerLinkActive="active-link"
|
||||
class="nav-button"
|
||||
>
|
||||
<mat-icon>task</mat-icon>
|
||||
<span>Tasks</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="action-buttons">
|
||||
<!-- Theme toggle button -->
|
||||
<lib-theme-toggle></lib-theme-toggle>
|
||||
|
||||
<!-- Language selector button -->
|
||||
<lib-language-selector></lib-language-selector>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
`,
|
||||
styles: [
|
||||
@ -24,6 +60,8 @@ import { RouterModule } from '@angular/router';
|
||||
.navbar {
|
||||
padding: 0 2rem;
|
||||
min-height: 64px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.logo {
|
||||
display: flex;
|
||||
@ -39,7 +77,48 @@ import { RouterModule } from '@angular/router';
|
||||
.spacer {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
.nav-links {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
margin-right: 1rem;
|
||||
}
|
||||
.nav-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0 1rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.nav-button.active-link {
|
||||
background-color: rgba(255, 255, 255, 0.15);
|
||||
position: relative;
|
||||
}
|
||||
.nav-button.active-link::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 10%;
|
||||
right: 10%;
|
||||
height: 3px;
|
||||
background-color: white;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
.action-button {
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.action-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class NavbarComponent {}
|
||||
export class NavbarComponent {
|
||||
// All functionality has been moved to separate components
|
||||
}
|
||||
|
||||
7
client/libs/shared/theme/README.md
Normal file
7
client/libs/shared/theme/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
# Theme
|
||||
|
||||
This library provides theme management functionality for the application.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `nx test theme` to execute the unit tests.
|
||||
5
client/libs/shared/theme/eslint.config.mjs
Normal file
5
client/libs/shared/theme/eslint.config.mjs
Normal file
@ -0,0 +1,5 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
name: 'theme',
|
||||
extends: ['../../../eslint.base.config.mjs'],
|
||||
};
|
||||
22
client/libs/shared/theme/jest.config.ts
Normal file
22
client/libs/shared/theme/jest.config.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'theme',
|
||||
preset: '../../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
coverageDirectory: '../../../coverage/libs/shared/theme',
|
||||
transform: {
|
||||
'^.+\\.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
21
client/libs/shared/theme/project.json
Normal file
21
client/libs/shared/theme/project.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "theme",
|
||||
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
|
||||
"sourceRoot": "libs/shared/theme/src",
|
||||
"prefix": "lib",
|
||||
"tags": [],
|
||||
"projectType": "library",
|
||||
"targets": {
|
||||
"test": {
|
||||
"executor": "@nx/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "libs/shared/theme/jest.config.ts"
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nx/eslint:lint",
|
||||
"outputs": ["{options.outputFile}"]
|
||||
}
|
||||
}
|
||||
}
|
||||
2
client/libs/shared/theme/src/index.ts
Normal file
2
client/libs/shared/theme/src/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './lib/theme.service';
|
||||
export * from './lib/theme-toggle.component';
|
||||
39
client/libs/shared/theme/src/lib/theme-toggle.component.ts
Normal file
39
client/libs/shared/theme/src/lib/theme-toggle.component.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIcon } from '@angular/material/icon';
|
||||
import { ThemeToggleService } from './theme.service';
|
||||
|
||||
@Component({
|
||||
selector: 'lib-theme-toggle',
|
||||
standalone: true,
|
||||
imports: [MatButtonModule, MatIcon],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
template: `
|
||||
<button
|
||||
mat-icon-button
|
||||
(click)="themeService.toggleTheme()"
|
||||
class="action-button"
|
||||
[attr.aria-label]="
|
||||
themeService.isDark() ? 'Switch to light theme' : 'Switch to dark theme'
|
||||
"
|
||||
>
|
||||
<mat-icon>{{
|
||||
themeService.isDark() ? 'light_mode' : 'dark_mode'
|
||||
}}</mat-icon>
|
||||
</button>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.action-button {
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
.action-button:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class ThemeToggleComponent {
|
||||
protected themeService = inject(ThemeToggleService);
|
||||
}
|
||||
55
client/libs/shared/theme/src/lib/theme.service.ts
Normal file
55
client/libs/shared/theme/src/lib/theme.service.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { OverlayContainer } from '@angular/cdk/overlay';
|
||||
import { computed, DOCUMENT, effect, inject, Injectable } from '@angular/core';
|
||||
import { stored } from '@mmstack/primitives';
|
||||
|
||||
function prefersDarkMode() {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ThemeToggleService {
|
||||
private readonly overlayCls =
|
||||
inject(OverlayContainer).getContainerElement().classList;
|
||||
private readonly dockument = inject(DOCUMENT);
|
||||
|
||||
private readonly theme = stored<'light' | 'dark'>(
|
||||
prefersDarkMode() ? 'dark' : 'light',
|
||||
{
|
||||
key: 'user-theme',
|
||||
syncTabs: true,
|
||||
}
|
||||
);
|
||||
|
||||
public isDark = computed(() => this.theme() === 'dark');
|
||||
|
||||
constructor() {
|
||||
effect(() =>
|
||||
this.isDark() ? this.addDarkThemeClass() : this.removeDarkThemeClass()
|
||||
);
|
||||
|
||||
effect(() =>
|
||||
this.dockument.body.setAttribute(
|
||||
'style',
|
||||
`color-scheme: ${this.isDark() ? 'dark' : 'light'}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public toggleTheme() {
|
||||
console.log(`Toggling theme to ${this.isDark() ? 'light' : 'dark'}`);
|
||||
|
||||
this.theme.set(this.isDark() ? 'light' : 'dark');
|
||||
}
|
||||
|
||||
private addDarkThemeClass() {
|
||||
this.overlayCls.add('dark-theme');
|
||||
this.dockument.body.classList.add('dark-theme');
|
||||
}
|
||||
|
||||
private removeDarkThemeClass() {
|
||||
this.overlayCls.remove('dark-theme');
|
||||
this.dockument.body.classList.remove('dark-theme');
|
||||
}
|
||||
}
|
||||
30
client/libs/shared/theme/tsconfig.json
Normal file
30
client/libs/shared/theme/tsconfig.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"skipLibCheck": true,
|
||||
"experimentalDecorators": true,
|
||||
"importHelpers": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"angularCompilerOptions": {
|
||||
"enableI18nLegacyMessageIdFormat": false,
|
||||
"strictInjectionParameters": true,
|
||||
"strictInputAccessModifiers": true,
|
||||
"typeCheckHostBindings": true,
|
||||
"strictTemplates": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
16
client/libs/shared/theme/tsconfig.lib.json
Normal file
16
client/libs/shared/theme/tsconfig.lib.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"src/**/*.spec.ts",
|
||||
"src/test-setup.ts",
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts"
|
||||
],
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
17
client/libs/shared/theme/tsconfig.spec.json
Normal file
17
client/libs/shared/theme/tsconfig.spec.json
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../../../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"target": "es2016",
|
||||
"types": ["jest", "node"],
|
||||
"moduleResolution": "node10"
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.d.ts"
|
||||
]
|
||||
}
|
||||
@ -87,13 +87,13 @@ import { TaskCommentComponent } from './task-comment.component';
|
||||
`
|
||||
.task-panel {
|
||||
margin-bottom: 1rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.task-panel:hover {
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.12);
|
||||
box-shadow: var(--mat-sys-level3);
|
||||
}
|
||||
|
||||
.task-panel .mat-expansion-panel-header {
|
||||
@ -147,7 +147,7 @@ import { TaskCommentComponent } from './task-comment.component';
|
||||
|
||||
.task-timestamp {
|
||||
font-size: 0.8rem;
|
||||
color: #757575;
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
}
|
||||
|
||||
.task-details {
|
||||
@ -155,15 +155,15 @@ import { TaskCommentComponent } from './task-comment.component';
|
||||
}
|
||||
|
||||
.task-description {
|
||||
font-size: 1rem;
|
||||
font-size: var(--mat-sys-body-large-size);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 1.5rem;
|
||||
white-space: pre-line;
|
||||
color: #424242;
|
||||
background-color: #f8f9fa;
|
||||
color: var(--mat-sys-on-surface);
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
padding: 1rem;
|
||||
border-radius: 6px;
|
||||
border-left: 4px solid #1976d2;
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
border-left: 4px solid var(--mat-sys-primary);
|
||||
}
|
||||
|
||||
.comments-section {
|
||||
@ -174,9 +174,9 @@ import { TaskCommentComponent } from './task-comment.component';
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 1.2rem;
|
||||
font-size: var(--mat-sys-title-medium-size);
|
||||
margin-bottom: 1rem;
|
||||
color: #424242;
|
||||
color: var(--mat-sys-on-surface);
|
||||
}
|
||||
|
||||
.comments-section h3 mat-icon {
|
||||
|
||||
@ -111,16 +111,16 @@ import { CommentResponse, User } from '@org/shared/api';
|
||||
|
||||
.comment-item {
|
||||
padding: 1rem;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
margin-bottom: 0.75rem;
|
||||
border: 1px solid #e0e0e0;
|
||||
border: 1px solid var(--mat-sys-outline-variant);
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
|
||||
.comment-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.08);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
}
|
||||
|
||||
.comment-header {
|
||||
@ -131,45 +131,45 @@ import { CommentResponse, User } from '@org/shared/api';
|
||||
}
|
||||
|
||||
.comment-author {
|
||||
font-weight: 600;
|
||||
color: #1976d2;
|
||||
font-weight: var(--mat-sys-title-small-weight);
|
||||
color: var(--mat-sys-primary);
|
||||
}
|
||||
|
||||
.comment-time {
|
||||
font-size: 0.8rem;
|
||||
color: #757575;
|
||||
font-size: var(--mat-sys-body-small-size);
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
}
|
||||
|
||||
.comment-content {
|
||||
margin: 0;
|
||||
white-space: pre-line;
|
||||
color: #424242;
|
||||
color: var(--mat-sys-on-surface);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.no-comments {
|
||||
font-style: italic;
|
||||
color: #757575;
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8px;
|
||||
border: 1px dashed #e0e0e0;
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
border: 1px dashed var(--mat-sys-outline-variant);
|
||||
}
|
||||
|
||||
.comment-form {
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
padding: 1rem;
|
||||
margin-top: 1rem;
|
||||
border: 1px solid #e0e0e0;
|
||||
border: 1px solid var(--mat-sys-outline-variant);
|
||||
}
|
||||
|
||||
.comment-form h4 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
font-size: 1.1rem;
|
||||
color: #424242;
|
||||
font-size: var(--mat-sys-title-medium-size);
|
||||
color: var(--mat-sys-on-surface);
|
||||
}
|
||||
|
||||
.form-row {
|
||||
|
||||
@ -81,8 +81,8 @@ import { ToastService } from '@org/shared/toast';
|
||||
}
|
||||
|
||||
.form-title {
|
||||
font-weight: 700;
|
||||
color: #1976d2;
|
||||
font-weight: var(--mat-sys-label-large-weight-prominent);
|
||||
color: var(--mat-sys-primary);
|
||||
}
|
||||
|
||||
.full-width {
|
||||
|
||||
@ -94,8 +94,8 @@ import { TaskCardComponent } from './task-card.component';
|
||||
|
||||
.page-header {
|
||||
margin-bottom: 1.5rem;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
}
|
||||
|
||||
.spacer {
|
||||
@ -114,19 +114,19 @@ import { TaskCardComponent } from './task-card.component';
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 8px;
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
padding: 3rem 1.5rem;
|
||||
margin: 1.5rem 0;
|
||||
text-align: center;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 3rem;
|
||||
height: 3rem;
|
||||
width: 3rem;
|
||||
color: #bdbdbd;
|
||||
color: var(--mat-sys-outline);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
|
||||
@ -56,20 +56,20 @@ import { RouterModule } from '@angular/router';
|
||||
`
|
||||
.user-card {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.07);
|
||||
background: var(--mat-sys-surface-container);
|
||||
border-radius: var(--mat-sys-corner-medium);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
transition: all 0.2s ease;
|
||||
padding-bottom: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.user-card:hover {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: var(--mat-sys-level3);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.user-link {
|
||||
text-decoration: none;
|
||||
color: #1976d2;
|
||||
color: var(--mat-sys-primary);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
@ -145,23 +145,23 @@ import { UserTaskListComponent } from './user-task-list.component';
|
||||
}
|
||||
|
||||
mat-card {
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
padding: 1rem;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
.tasks-section {
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.tasks-section h2 {
|
||||
font-size: 1.25rem;
|
||||
font-size: var(--mat-sys-title-large-size);
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--mat-sys-outline-variant);
|
||||
padding-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -170,34 +170,34 @@ import { UserTaskListComponent } from './user-task-list.component';
|
||||
|
||||
mat-list-item {
|
||||
margin-bottom: 0.5rem;
|
||||
border-left: 3px solid #3f51b5;
|
||||
border-left: 3px solid var(--mat-sys-primary);
|
||||
}
|
||||
|
||||
.task-title {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-weight: var(--mat-sys-title-medium-weight);
|
||||
}
|
||||
|
||||
.task-description {
|
||||
white-space: pre-line;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
}
|
||||
|
||||
.no-tasks {
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
background-color: var(--mat-sys-surface-container);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
}
|
||||
|
||||
lib-task-form {
|
||||
display: block;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem;
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
background-color: var(--mat-sys-surface-container);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
}
|
||||
|
||||
.back-button {
|
||||
|
||||
@ -107,8 +107,8 @@ type UserWithCounts = User & { taskCount: number; commentCount: number };
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 2rem;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
}
|
||||
.user-grid {
|
||||
display: grid;
|
||||
@ -118,20 +118,20 @@ type UserWithCounts = User & { taskCount: number; commentCount: number };
|
||||
}
|
||||
.user-card {
|
||||
position: relative;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08);
|
||||
background: var(--mat-sys-surface-container);
|
||||
border-radius: var(--mat-sys-corner-medium);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
transition: all 0.2s ease;
|
||||
padding-bottom: 0.5rem;
|
||||
overflow: hidden;
|
||||
}
|
||||
.user-card:hover {
|
||||
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: var(--mat-sys-level3);
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.user-link {
|
||||
text-decoration: none;
|
||||
color: #1976d2;
|
||||
color: var(--mat-sys-primary);
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
@ -148,13 +148,13 @@ type UserWithCounts = User & { taskCount: number; commentCount: number };
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
margin-top: 0.5rem;
|
||||
font-size: 0.9rem;
|
||||
color: #555;
|
||||
font-size: var(--mat-sys-body-small-size);
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
}
|
||||
.meta-icon {
|
||||
vertical-align: middle;
|
||||
margin-right: 0.2em;
|
||||
color: #1976d2;
|
||||
color: var(--mat-sys-primary);
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
.container {
|
||||
|
||||
@ -53,16 +53,16 @@ import { TaskFormComponent } from '@org/web/tasks';
|
||||
styles: [
|
||||
`
|
||||
.tasks-section {
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 8px;
|
||||
background-color: var(--mat-sys-surface-container-low);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.tasks-section h2 {
|
||||
font-size: 1.25rem;
|
||||
font-size: var(--mat-sys-title-large-size);
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
border-bottom: 1px solid #eee;
|
||||
border-bottom: 1px solid var(--mat-sys-outline-variant);
|
||||
padding-bottom: 0.5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -70,33 +70,33 @@ import { TaskFormComponent } from '@org/web/tasks';
|
||||
}
|
||||
|
||||
mat-list {
|
||||
border-radius: 4px;
|
||||
border-radius: var(--mat-sys-corner-extra-small);
|
||||
overflow: hidden;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
||||
background-color: var(--mat-sys-surface-container);
|
||||
box-shadow: var(--mat-sys-level1);
|
||||
}
|
||||
|
||||
mat-list-item {
|
||||
margin-bottom: 0.5rem;
|
||||
border-left: 3px solid #3f51b5;
|
||||
border-left: 3px solid var(--mat-sys-primary);
|
||||
}
|
||||
|
||||
.task-title {
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
font-weight: var(--mat-sys-title-medium-weight);
|
||||
}
|
||||
|
||||
.task-description {
|
||||
white-space: pre-line;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
}
|
||||
|
||||
.no-tasks {
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
color: rgba(0, 0, 0, 0.5);
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
color: var(--mat-sys-on-surface-variant);
|
||||
background-color: var(--mat-sys-surface-container);
|
||||
border-radius: var(--mat-sys-corner-small);
|
||||
}
|
||||
|
||||
lib-task-form {
|
||||
|
||||
132
client/package-lock.json
generated
132
client/package-lock.json
generated
@ -19,6 +19,8 @@
|
||||
"@angular/platform-browser": "~20.0.0",
|
||||
"@angular/platform-browser-dynamic": "~20.0.0",
|
||||
"@angular/router": "~20.0.0",
|
||||
"@mmstack/primitives": "^20.0.1",
|
||||
"@mmstack/translate": "^20.0.1",
|
||||
"rxjs": "~7.8.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
@ -2685,6 +2687,84 @@
|
||||
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "2.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.4.tgz",
|
||||
"integrity": "sha512-qrycXDeaORzIqNhBOx0btnhpD1c+/qFIHAN9znofuMJX6QBwtbrmlpWfD4oiUUD2vJUOIYFA/gYtg2KAMGG7sA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@formatjs/fast-memoize": "2.2.7",
|
||||
"@formatjs/intl-localematcher": "0.6.1",
|
||||
"decimal.js": "^10.4.3",
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/fast-memoize": {
|
||||
"version": "2.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz",
|
||||
"integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/icu-messageformat-parser": {
|
||||
"version": "2.11.2",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.2.tgz",
|
||||
"integrity": "sha512-AfiMi5NOSo2TQImsYAg8UYddsNJ/vUEv/HaNqiFjnI3ZFfWihUtD5QtuX6kHl8+H+d3qvnE/3HZrfzgdWpsLNA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.3.4",
|
||||
"@formatjs/icu-skeleton-parser": "1.8.14",
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/icu-skeleton-parser": {
|
||||
"version": "1.8.14",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.14.tgz",
|
||||
"integrity": "sha512-i4q4V4qslThK4Ig8SxyD76cp3+QJ3sAqr7f6q9VVfeGtxG9OhiAk3y9XF6Q41OymsKzsGQ6OQQoJNY4/lI8TcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.3.4",
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl": {
|
||||
"version": "3.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-3.1.6.tgz",
|
||||
"integrity": "sha512-tDkXnA4qpIFcDWac8CyVJq6oW8DR7W44QDUBsfXWIIJD/FYYen0QoH46W7XsVMFfPOVKkvbufjboZrrWbEfmww==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.3.4",
|
||||
"@formatjs/fast-memoize": "2.2.7",
|
||||
"@formatjs/icu-messageformat-parser": "2.11.2",
|
||||
"intl-messageformat": "10.7.16",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5.6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.1.tgz",
|
||||
"integrity": "sha512-ePEgLgVCqi2BBFnTMWPfIghu6FkbZnnBVhO2sSxvLfrdFw7wCHAHiDoM2h4NRgjbaY7+B7HgOLZGkK187pZTZg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@humanfs/core": {
|
||||
"version": "0.19.1",
|
||||
"dev": true,
|
||||
@ -3693,6 +3773,42 @@
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@mmstack/object": {
|
||||
"version": "20.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@mmstack/object/-/object-20.0.0.tgz",
|
||||
"integrity": "sha512-cLhbkhU+S6jNm+7RNJ8DxvprBtX45pE5ymw58Pj3PoB+UOCA7pZPBDiubQQWqKBpoQJO0X/LyT/uTDV1YKs1Jg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mmstack/primitives": {
|
||||
"version": "20.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@mmstack/primitives/-/primitives-20.0.1.tgz",
|
||||
"integrity": "sha512-WAzQS+uf18Fjuz3HkWUnvJaB2nFt78/xV5f7vah9iR3DPe3IsWABngcEMNc1Wc+i4NHfytJQHfPlhSJXFMb3Gw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@mmstack/object": "^20.0.0",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "~20.0.3",
|
||||
"@angular/core": "~20.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@mmstack/translate": {
|
||||
"version": "20.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@mmstack/translate/-/translate-20.0.1.tgz",
|
||||
"integrity": "sha512-kFKRedsLLcU5GdYcJBhmT6vzPYVQMFRKlrVbaP/gawk+U56i9ZWK58o1CWYggf5WslTsBa98fNnEJyu2/3trYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "~20.0.3",
|
||||
"@formatjs/intl": "~3.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@modern-js/node-bundle-require": {
|
||||
"version": "2.67.6",
|
||||
"dev": true,
|
||||
@ -8722,7 +8838,6 @@
|
||||
},
|
||||
"node_modules/decimal.js": {
|
||||
"version": "10.6.0",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/dedent": {
|
||||
@ -11162,6 +11277,19 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/intl-messageformat": {
|
||||
"version": "10.7.16",
|
||||
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.16.tgz",
|
||||
"integrity": "sha512-UmdmHUmp5CIKKjSoE10la5yfU+AYJAaiYLsodbjL4lji83JNvgOQUjGaGhGrpFCb0Uh7sl7qfP1IyILa8Z40ug==",
|
||||
"license": "BSD-3-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.3.4",
|
||||
"@formatjs/fast-memoize": "2.2.7",
|
||||
"@formatjs/icu-messageformat-parser": "2.11.2",
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ip-address": {
|
||||
"version": "9.0.5",
|
||||
"dev": true,
|
||||
@ -17589,7 +17717,7 @@
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.8.3",
|
||||
"dev": true,
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
|
||||
@ -19,6 +19,8 @@
|
||||
"@angular/platform-browser": "~20.0.0",
|
||||
"@angular/platform-browser-dynamic": "~20.0.0",
|
||||
"@angular/router": "~20.0.0",
|
||||
"@mmstack/primitives": "^20.0.1",
|
||||
"@mmstack/translate": "^20.0.1",
|
||||
"rxjs": "~7.8.0",
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
|
||||
@ -21,5 +21,6 @@ export const appConfig: ApplicationConfig = {
|
||||
provide: API,
|
||||
useValue: apiUrls,
|
||||
},
|
||||
// No need to provide ThemeService and LanguageService as they use providedIn: 'root'
|
||||
],
|
||||
};
|
||||
|
||||
@ -7,8 +7,19 @@ import { NavbarComponent } from '@org/shared/navbar';
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<lib-navbar />
|
||||
<div class="content-container">
|
||||
<router-outlet />
|
||||
</div>
|
||||
`,
|
||||
styles: [``],
|
||||
styles: [
|
||||
`
|
||||
.content-container {
|
||||
padding: 1.5rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
min-height: calc(100vh - 64px);
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class App {}
|
||||
|
||||
@ -22,21 +22,6 @@ body.dark-theme {
|
||||
transition: background-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
.toast-success {
|
||||
background-color: #4caf50;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.toast-error {
|
||||
background-color: #f44336;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.toast-info {
|
||||
background-color: #2196f3;
|
||||
color: white;
|
||||
}
|
||||
|
||||
body.dark-theme .mat-mdc-card {
|
||||
--mdc-elevated-card-container-color: #424242;
|
||||
color: rgba(255, 255, 255, 0.87);
|
||||
|
||||
@ -9,8 +9,10 @@
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@org/shared/api": ["libs/shared/api/src/index.ts"],
|
||||
"@org/shared/i18n": ["libs/shared/i18n/src/index.ts"],
|
||||
"@org/shared/navbar": ["libs/shared/navbar/src/index.ts"],
|
||||
"@org/shared/stores": ["libs/shared/stores/src/index.ts"],
|
||||
"@org/shared/theme": ["libs/shared/theme/src/index.ts"],
|
||||
"@org/shared/toast": ["libs/shared/toast/src/index.ts"],
|
||||
"@org/web/tasks": ["libs/web/tasks/src/index.ts"],
|
||||
"@org/web/users": ["libs/web/users/src/index.ts"]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user