feat: update Dockerfiles to use explicit image paths and enhance localStorage handling in ThemeService
This commit is contained in:
1 parent
030d00ef67
commit
3dca10b56f
7 files changed
+59
-10
No files matched your search
+3
-3
@@ -1,7 +1,7 @@
|
||||
# ==========================================
|
||||
# Stage 1: Build using Node.js + Bun
|
||||
# ==========================================
|
||||
FROM node:26-alpine AS build
|
||||
FROM docker.io/library/node:26-alpine AS build
|
||||
WORKDIR /app
|
||||
|
||||
RUN npm install -g bun
|
||||
@@ -19,10 +19,10 @@ RUN bun run build
|
||||
# ==========================================
|
||||
# Stage 2: Serve the app using Nginx
|
||||
# ==========================================
|
||||
FROM nginx:alpine
|
||||
FROM docker.io/library/nginx:alpine
|
||||
RUN rm -rf /usr/share/nginx/html/*
|
||||
COPY --from=build /app/dist/lmnp-frontend/browser /usr/share/nginx/html
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
@@ -83,7 +83,7 @@
|
||||
"lint": {
|
||||
"builder": "@angular-eslint/builder:lint",
|
||||
"options": {
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html"]
|
||||
"lintFilePatterns": ["src/**/*.ts", "src/**/*.html", "e2e/**/*.ts"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,13 @@ test('persists a dark appearance preference from Settings', async ({ page }) =>
|
||||
await page.getByRole('radio', { name: 'Sombre' }).check();
|
||||
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
expect(await page.evaluate(() => localStorage.getItem('lmnp-theme'))).toBe('dark');
|
||||
expect(
|
||||
await page.evaluate(
|
||||
// This callback runs in the browser page, where localStorage is guaranteed by the test.
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
() => localStorage.getItem('lmnp-theme'),
|
||||
),
|
||||
).toBe('dark');
|
||||
|
||||
await page.reload();
|
||||
await expect(page.locator('html')).toHaveAttribute('data-theme', 'dark');
|
||||
@@ -192,7 +198,11 @@ test('keeps the secured API check and logout available', async ({ page }) => {
|
||||
});
|
||||
|
||||
test('follows operating-system changes for the System theme', async ({ page }) => {
|
||||
await page.addInitScript(() => localStorage.setItem('lmnp-theme', 'system'));
|
||||
await page.addInitScript(
|
||||
// This callback runs in the browser page, where localStorage is guaranteed by the test.
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
() => localStorage.setItem('lmnp-theme', 'system'),
|
||||
);
|
||||
await page.emulateMedia({ colorScheme: 'dark' });
|
||||
await mockUser(page, true);
|
||||
await mockEmptyApartmentList(page);
|
||||
|
||||
@@ -16,11 +16,36 @@ module.exports = defineConfig([
|
||||
processor: angular.processInlineTemplates,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json', './tsconfig.app.json', './tsconfig.spec.json'],
|
||||
project: [
|
||||
'./tsconfig.json',
|
||||
'./tsconfig.app.json',
|
||||
'./tsconfig.spec.json',
|
||||
'./tsconfig.e2e.json',
|
||||
],
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-restricted-globals': [
|
||||
'error',
|
||||
{
|
||||
name: 'localStorage',
|
||||
message: 'Use getBrowserStorage() for storage that works outside a browser.',
|
||||
},
|
||||
],
|
||||
'no-restricted-properties': [
|
||||
'error',
|
||||
{
|
||||
object: 'window',
|
||||
property: 'localStorage',
|
||||
message: 'Use getBrowserStorage() for storage that works outside a browser.',
|
||||
},
|
||||
{
|
||||
object: 'globalThis',
|
||||
property: 'localStorage',
|
||||
message: 'Use getBrowserStorage() for storage that works outside a browser.',
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/no-deprecated': 'error',
|
||||
'@angular-eslint/directive-selector': [
|
||||
'error',
|
||||
@@ -40,6 +65,12 @@ module.exports = defineConfig([
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/services/browser-storage.ts'],
|
||||
rules: {
|
||||
'no-restricted-properties': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.html'],
|
||||
extends: [angular.configs.templateRecommended, angular.configs.templateAccessibility],
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export function getBrowserStorage(): Storage | null {
|
||||
try {
|
||||
return typeof globalThis.localStorage === 'undefined' ? null : globalThis.localStorage;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DOCUMENT } from '@angular/common';
|
||||
import { effect, inject, Injectable, signal } from '@angular/core';
|
||||
import { getBrowserStorage } from './browser-storage';
|
||||
|
||||
export type ThemePreference = 'light' | 'dark' | 'system';
|
||||
|
||||
@@ -8,7 +9,7 @@ const THEME_STORAGE_KEY = 'lmnp-theme';
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class ThemeService {
|
||||
private readonly document = inject(DOCUMENT);
|
||||
private readonly storage = typeof localStorage === 'undefined' ? null : localStorage;
|
||||
private readonly storage = getBrowserStorage();
|
||||
private readonly colorSchemeQuery =
|
||||
typeof window.matchMedia === 'function'
|
||||
? window.matchMedia('(prefers-color-scheme: dark)')
|
||||
|
||||
Reference in new issue
Block a user