From 7d85a18867ca0fe2310218c63cfc696c0f916171 Mon Sep 17 00:00:00 2001 From: Martin Kagamino Lehoux Date: Tue, 8 Sep 2026 09:12:25 +0200 Subject: Consolidate duplicated page CSS into app.css, share TestDb type - Move .page-header/.back-link/.input-invalid/.save-status/.btn-done (plus hover/variant rules) into app.css as shared route scaffolding; delete per-page scoped copies - Unify drifted values: page-header padding 1rem -> 1.25rem (routine page), empty-state spacing 0.75/1.5rem -> 1/2rem (grocery detail), save-status margin -0.25rem applied on both pages, back-link margin 1.5rem -> 1rem (grocery detail); routine page's large Fait button is kept via a .btn-done-lg modifier - Export TestDb from test-helper and use it in the five db/service test files to shrink duplicated fixture scaffolding --- src/app.css | 63 ++++++++++++++++++++++ src/lib/server/db/groceries.test.ts | 7 ++- src/lib/server/db/ondelete.test.ts | 7 ++- src/lib/server/db/test-helper.ts | 2 + src/lib/server/services/categories.test.ts | 7 ++- src/lib/server/services/groceries.test.ts | 7 ++- src/lib/server/services/references.test.ts | 7 ++- src/routes/+page.svelte | 12 ----- src/routes/groceries/+page.svelte | 5 -- src/routes/groceries/catalog/+page.svelte | 5 -- src/routes/groceries/categories/+page.svelte | 5 -- src/routes/grocery/[id]/+page.svelte | 37 ------------- src/routes/projects/[id]/+page.svelte | 33 ------------ .../[projectId]/routine/[routineId]/+page.svelte | 57 +------------------- 14 files changed, 82 insertions(+), 172 deletions(-) diff --git a/src/app.css b/src/app.css index 7b8464f..1c25c93 100644 --- a/src/app.css +++ b/src/app.css @@ -212,6 +212,8 @@ button { background: var(--surface); border-radius: 0.75rem; box-shadow: 0 1px 2px var(--shadow); + margin-top: 1rem; + padding: 2rem; } /* Error */ @@ -398,3 +400,64 @@ button { font-weight: 600; white-space: nowrap; } + +/* Shared route scaffolding */ +.page-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 1.25rem 0; +} + +.back-link { + display: inline-flex; + align-items: center; + gap: 0.375rem; + margin-top: 1rem; + color: var(--text-secondary); + text-decoration: none; + font-size: 0.875rem; + font-weight: 500; + transition: color 0.2s; +} + +.back-link:hover { + color: var(--accent); +} + +.input-invalid { + border-color: var(--danger); +} + +.save-status { + font-size: 0.875rem; + color: var(--success); + font-weight: 500; + margin-top: -0.25rem; + visibility: hidden; + opacity: 0; + transition: opacity 0.2s ease; + min-height: 1.25rem; +} + +.save-status.visible { + visibility: visible; + opacity: 1; +} + +.btn-done { + flex-shrink: 0; + padding: 0.5rem 0.875rem; + border: none; + border-radius: 0.625rem; + background: var(--success); + color: var(--white); + font-size: 0.875rem; + font-weight: 500; + transition: all 0.2s ease; +} + +.btn-done:hover { + filter: brightness(1.05); + transform: translateY(-1px); +} diff --git a/src/lib/server/db/groceries.test.ts b/src/lib/server/db/groceries.test.ts index 2f8baed..143e4da 100644 --- a/src/lib/server/db/groceries.test.ts +++ b/src/lib/server/db/groceries.test.ts @@ -1,14 +1,13 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { groceries, categories } from '$lib/server/db/schema'; import { eq } from 'drizzle-orm'; -import { createTestDb } from './test-helper'; +import { createTestDb, type TestDb } from './test-helper'; describe('groceries add', () => { - let db: Awaited>['db']; + let db: TestDb['db']; beforeEach(async () => { - const testDb = await createTestDb(); - db = testDb.db; + db = (await createTestDb()).db; }); async function addItem(name: string, categoryId?: number | null) { diff --git a/src/lib/server/db/ondelete.test.ts b/src/lib/server/db/ondelete.test.ts index c2c86de..347bb92 100644 --- a/src/lib/server/db/ondelete.test.ts +++ b/src/lib/server/db/ondelete.test.ts @@ -1,14 +1,13 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { groceries, categories } from '$lib/server/db/schema'; import { eq } from 'drizzle-orm'; -import { createTestDb } from './test-helper'; +import { createTestDb, type TestDb } from './test-helper'; describe('category delete behavior', () => { - let db: Awaited>['db']; + let db: TestDb['db']; beforeEach(async () => { - const testDb = await createTestDb(); - db = testDb.db; + db = (await createTestDb()).db; }); it('sets category_id to null when a referenced category is deleted', async () => { diff --git a/src/lib/server/db/test-helper.ts b/src/lib/server/db/test-helper.ts index 5bae681..2b8c173 100644 --- a/src/lib/server/db/test-helper.ts +++ b/src/lib/server/db/test-helper.ts @@ -11,3 +11,5 @@ export async function createTestDb() { return { db, client }; } + +export type TestDb = Awaited>; diff --git a/src/lib/server/services/categories.test.ts b/src/lib/server/services/categories.test.ts index cfde1da..a89b816 100644 --- a/src/lib/server/services/categories.test.ts +++ b/src/lib/server/services/categories.test.ts @@ -1,15 +1,14 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { groceries, categories } from '$lib/server/db/schema'; import { eq } from 'drizzle-orm'; -import { createTestDb } from '$lib/server/db/test-helper'; +import { createTestDb, type TestDb } from '$lib/server/db/test-helper'; import { loadAll, add, deleteCategory } from './categories'; describe('categories service', () => { - let db: Awaited>['db']; + let db: TestDb['db']; beforeEach(async () => { - const testDb = await createTestDb(); - db = testDb.db; + db = (await createTestDb()).db; }); it('loadAll returns categories sorted by name', async () => { diff --git a/src/lib/server/services/groceries.test.ts b/src/lib/server/services/groceries.test.ts index 83f8d95..a704b57 100644 --- a/src/lib/server/services/groceries.test.ts +++ b/src/lib/server/services/groceries.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { groceries, categories, references } from '$lib/server/db/schema'; import { eq } from 'drizzle-orm'; -import { createTestDb } from '$lib/server/db/test-helper'; +import { createTestDb, type TestDb } from '$lib/server/db/test-helper'; import { loadUnchecked, loadAll, @@ -11,11 +11,10 @@ import { } from './groceries'; describe('groceries service', () => { - let db: Awaited>['db']; + let db: TestDb['db']; beforeEach(async () => { - const testDb = await createTestDb(); - db = testDb.db; + db = (await createTestDb()).db; }); it('loadUnchecked returns only unchecked items', async () => { diff --git a/src/lib/server/services/references.test.ts b/src/lib/server/services/references.test.ts index c02f9e6..1ec3fdb 100644 --- a/src/lib/server/services/references.test.ts +++ b/src/lib/server/services/references.test.ts @@ -1,15 +1,14 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { groceries, references } from '$lib/server/db/schema'; import { eq } from 'drizzle-orm'; -import { createTestDb } from '$lib/server/db/test-helper'; +import { createTestDb, type TestDb } from '$lib/server/db/test-helper'; import { loadForGrocery, add, deleteReference } from './references'; describe('references service', () => { - let db: Awaited>['db']; + let db: TestDb['db']; beforeEach(async () => { - const testDb = await createTestDb(); - db = testDb.db; + db = (await createTestDb()).db; }); it('loadForGrocery returns refs for the given grocery', async () => { diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 35ff5d7..5214bd6 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -46,13 +46,6 @@ diff --git a/src/routes/groceries/+page.svelte b/src/routes/groceries/+page.svelte index df1b97b..3646ba7 100644 --- a/src/routes/groceries/+page.svelte +++ b/src/routes/groceries/+page.svelte @@ -177,9 +177,4 @@ .error { margin-bottom: 1rem; } - - .empty { - margin-top: 1rem; - padding: 2rem; - } diff --git a/src/routes/groceries/catalog/+page.svelte b/src/routes/groceries/catalog/+page.svelte index 74a82e8..59e2018 100644 --- a/src/routes/groceries/catalog/+page.svelte +++ b/src/routes/groceries/catalog/+page.svelte @@ -168,11 +168,6 @@ margin-bottom: 1rem; } - .empty { - margin-top: 1rem; - padding: 2rem; - } - .search-input { width: 100%; padding: 0.75rem 1rem; diff --git a/src/routes/groceries/categories/+page.svelte b/src/routes/groceries/categories/+page.svelte index 48de2da..af85adf 100644 --- a/src/routes/groceries/categories/+page.svelte +++ b/src/routes/groceries/categories/+page.svelte @@ -119,9 +119,4 @@ margin-top: -0.25rem; margin-bottom: 0.5rem; } - - .empty { - margin-top: 1rem; - padding: 2rem; - } diff --git a/src/routes/grocery/[id]/+page.svelte b/src/routes/grocery/[id]/+page.svelte index 531fbf7..4a96f32 100644 --- a/src/routes/grocery/[id]/+page.svelte +++ b/src/routes/grocery/[id]/+page.svelte @@ -214,33 +214,12 @@ margin-bottom: 0.5rem; } - .empty { - margin-top: 0.75rem; - padding: 1.5rem; - } - hr { border: none; border-top: 1.5px solid var(--border); margin: 1.5rem 0; } - .back-link { - display: inline-flex; - align-items: center; - gap: 0.375rem; - margin-top: 1.5rem; - color: var(--text-secondary); - text-decoration: none; - font-size: 0.875rem; - font-weight: 500; - transition: color 0.2s; - } - - .back-link:hover { - color: var(--accent); - } - .delete-item-btn { margin-top: 1.5rem; padding: 0.625rem 1.25rem; @@ -258,20 +237,4 @@ background: var(--danger-bg); transform: translateY(-1px); } - - .save-status { - font-size: 0.875rem; - color: var(--success); - font-weight: 500; - margin-top: -0.25rem; - visibility: hidden; - opacity: 0; - transition: opacity 0.2s ease; - min-height: 1.25rem; - } - - .save-status.visible { - visibility: visible; - opacity: 1; - } diff --git a/src/routes/projects/[id]/+page.svelte b/src/routes/projects/[id]/+page.svelte index e324f45..f1c2ab4 100644 --- a/src/routes/projects/[id]/+page.svelte +++ b/src/routes/projects/[id]/+page.svelte @@ -250,13 +250,6 @@ diff --git a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte index 19713ae..bcdca79 100644 --- a/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte +++ b/src/routes/projects/[projectId]/routine/[routineId]/+page.svelte @@ -221,7 +221,7 @@ placeholder="Note (optionnel)" autocomplete="off" /> - + {/if} @@ -244,29 +244,6 @@