Commit Graph

51 Commits

Author SHA1 Message Date
schalli b70161717c fix(260907-ipz): Anrede in vier deutschen Texten auf Siezen umstellen
Die Oberflaeche siezt ueberall, ausser an vier Stellen: leere
Gruppenliste, Modul-Aktivierungsdialog, Modul-Sperrseite und
Marktplatz-Hinweis duzten bislang. Alle vier Werte in de.json werden
auf die Sie-Form umgestellt, en.json bleibt unveraendert, da Englisch
die Unterscheidung nicht kennt. Die Mock-Uebersetzer und Zusicherungen
in den drei betroffenen Testdateien werden auf den neuen Wortlaut
nachgezogen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 13:37:12 +02:00
schalli 11c0ec63cb feat(quick-260907-fgv): Anmeldeseite zeigt Logo links, Text rechts daneben
Auf Wunsch des Users stehen Bildmarke und Text auf dem gelben Anmelde-Panel jetzt
nebeneinander statt untereinander — dieselbe Anordnung wie in der gelieferten Datei
tessera-logo-schriftzug.svg: links die Marke, rechts der Name und darunter der
Untertitel.

Dafuer nimmt die horizontale Variante der Marken-Komponente einen optionalen
Untertitel entgegen. Die gestapelte Variante entfaellt — sie war nach dieser
Umstellung nirgends mehr im Einsatz, alle uebrigen fuenf Stellen standen schon vorher
nebeneinander.

Der Test auf die entfallene Variante wurde nicht geloescht, sondern durch zwei Tests
auf das neue Verhalten ersetzt: einer prueft, dass ohne Angabe kein Untertitel
erscheint, der andere die tatsaechliche Reihenfolge im Dokument (Marke vor Name, Name
vor Untertitel) statt nur deren Vorhandensein. Die Erwartungstexte sind von Hand
geschrieben und nicht aus derselben Quelle gebildet, aus der die Komponente sie zieht.

222/222 Web-Tests gruen, Typpruefung sauber, im Browser gegengeprueft.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:41:07 +02:00
schalli c4cc05a942 feat(quick-260907-fgv-02): sechs Marken-Stellen auf TesseraLogo umgestellt, Gelbwert zusammengefuehrt
- header.tsx, sidebar.tsx (Schublade), login/page.tsx (breit + schmal),
  reset-password/page.tsx, reset-password/[token]/page.tsx ziehen jetzt
  alle TesseraLogo statt eines von Hand gesetzten Schriftzugs
- login/page.tsx: handgezeichneter Vier-Quadrate-Platzhalter entfernt,
  gelbes Panel nutzt BRAND_YELLOW per Inline-Style statt eigener
  Tailwind-Klasse, Kopfkommentar ohne Farbwert neu formuliert
- account-settings-form.tsx: DEFAULT_ACCENT liest jetzt BRAND_YELLOW statt
  eines eigenen Literals
- tessera-logo.tsx: Schriftzug setzt keine eigene Textfarbe mehr, sondern
  erbt sie vom umgebenden Container (Rule 1 -- ohne diese Korrektur waere
  der Schriftzug auf dem immer gelben Anmelde-Panel im dunklen
  Erscheinungsbild unlesbar hell auf hell gewesen)
- sidebar.tsx: nicht mehr benoetigtes tCommon entfernt; sidebar.test.tsx
  um common.brand.logoAlt in der Uebersetzungs-Attrappe ergaenzt
- vollstaendige Web-Testsuite (221 Tests) und Typpruefung gruen

Docker-Neubau und Browser-Abnahme (curl-Health-Check gegen den lokalen
Container) bewusst ausgelassen -- der Orchestrator uebernimmt beides in
Task 3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:25:14 +02:00
schalli 49d3df4485 feat(quick-260907-fgv-01): Markenbausteine — Icons, Farbkonstanten, TesseraLogo-Komponente
- favicon.svg und apple-touch-icon.png byteidentisch nach src/app/ kopiert
  (dateibasierte Next.js-Metadaten fuer den Browser-Tab)
- brand.ts: einzige Stelle im Web-Quellcode fuer die drei Markenfarben
  (BRAND_YELLOW, BRAND_OLIVE, BRAND_PLATE), aus favicon.svg uebernommen
- TesseraLogo-Komponente mit den Varianten mark/horizontal/stacked, als
  JSX-SVG von Hand gesetzt (kein dangerouslySetInnerHTML), Kontur-Klasse
  fuer die dunkle Platte per plateOutline-Eigenschaft schaltbar
- common.brand.logoAlt in de.json und en.json ergaenzt, Schluesselparitaet
  per Test abgesichert

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 11:21:40 +02:00
schalli 4a23e13731 feat(quick-260907-e8k-01): gemeinsame 403-Komponente und ModuleAccessGate
Zieht das bisher inline in [category]/[moduleSlug]/page.tsx stehende
403-Markup in ModuleAccessDenied (uebersetzungsfrei, nimmt fertige Texte
als Props) und legt mit ModuleAccessGate eine wiederverwendbare
Server-Component-Pruefung an, die checkModuleAccess aufruft und bei
jeder Ausnahme ebenfalls als "kein Zugriff" wertet (zweite
Verteidigungslinie ueber das bereits geschlossen ausfallende
checkModuleAccess, T-15-29). Vier Testfaelle decken Durchlassen,
Verweigern, Ausnahme und Slug-Weitergabe ab.

Bereitet Task 2 vor: die vier Modul-Layouts und die generische Route
werden auf dieses Gate umgestellt (WINDOWS #10, PERM-04).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
2026-09-07 10:24:30 +02:00
schalli 90dc3981d5 feat(15-06): phase-wide i18n keys + sixth admin nav entry for groups
- de.json/en.json: admin.groups.* (incl. ldapBind, members, deleteConfirm,
  grants sub-namespaces), admin.users.grants.*, adminModules.grantsLink +
  grants.* + activationDialog.*, modules.accessDenied.*, marketplace
  statusNoAccess/toastNoAccess, header.admin.groups -- covers this plan's
  /admin/groups surface plus the Wave 4 surfaces (permission matrix,
  user-detail grants, activation dialog, 403 page, marketplace badge) so
  15-07/15-08 can run in parallel without touching the translation files
- admin-sidebar.tsx: sixth nav entry "Gruppen" -> /admin/groups with a
  roster/list icon (Lucide list glyph, distinct from the users icon)
2026-08-04 18:50:28 +02:00
schalli 8fb92a4e2a fix(favorites): route icon img through same-origin proxy, not hotlink
<img src={fav.iconUrl}> hotlinked the external favicon directly; sites
that send Cross-Origin-Resource-Policy: same-origin (e.g. claude.ai)
get blocked by the browser (net::ERR_BLOCKED_BY_RESPONSE.NotSameOrigin),
leaving only the letter fallback. Points src at the new same-origin
/api-proxy/favorites/:id/icon route instead (same pattern already used
for the user avatar image). Render guard and onError fallback unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-07 15:34:56 +02:00
schalli 384b2409a2 fix(tests): add noCompactor mock + fix note-widget event simulation
Tessera CI/CD / Lint & Type Check (push) Successful in 42s
Tessera CI/CD / Tests (push) Successful in 46s
Tessera CI/CD / Build & Publish Images (push) Successful in 4m3s
- dashboard-grid.test: add noCompactor to react-grid-layout mock
- note-widget.test: enable edit mode before typing (onChange is undefined
  when isEditing=false), replace native dispatchEvent with fireEvent.change

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 10:15:17 +02:00
schalli c8f3361816 fix(dashboard): noCompactor + note edit/preview toggle + widget border
Tessera CI/CD / Tests (push) Has been cancelled
Tessera CI/CD / Build & Publish Images (push) Has been cancelled
Tessera CI/CD / Lint & Type Check (push) Has been cancelled
- dashboard-grid: add noCompactor to prevent auto-compaction on drag
- note-widget: edit/preview toggle button (pencil icon), isEditing state,
  hideToolbar in preview mode
- widget-wrapper: border-primary/20 accent border
- dashboard-store: console.error on widget add/remove/layout-save failures

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 08:57:37 +02:00
schalli be3680d0df feat(user-settings): avatar delete + accent color
- DELETE /users/me/avatar endpoint with file cleanup
- PATCH /users/me/accent-color with hex validation (#rrggbb)
- auth.service.ts: include accentColor in user select
- AccountSettingsForm: delete-avatar button + accent color picker/save/reset
- auth-actions.ts: deleteAvatarAction + updateAccentColorAction

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-02 08:57:31 +02:00
schalli e35276243a fix(calendar): EWS uses NTLM auth + edit form stays open after save
- Replace ews-javascript-api (Basic Auth only) with httpntlm for EWS connections
- testEwsConnection uses GetFolder SOAP via NTLM
- fetchViaEws uses FindItem CalendarView SOAP via NTLM
- Edit form no longer auto-closes on save — shows "Erfolgreich gespeichert" instead
- Test button in edit mode uses saved credentials via /sources/:id/test endpoint
- Add saveSuccess i18n key (de/en)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 14:05:07 +02:00
schalli 51d8c2f14e fix(calendar): SSRF exception for Exchange + error messages + domain in edit
- SSRF check skipped for Exchange type (internal EWS servers are common)
- testConnectionFromConfig catches SSRF/validation errors, returns {success:false,error} instead of throwing 403
- updateSource reads existing.type to determine effective type for SSRF check
- Panel shows saveError/editSaveError on failed add/update
- Edit form initialValues now includes domain field
- i18n: calendar.saveError key added (de+en)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:41:32 +02:00
schalli b719291bdc fix(accent-color): restore color on reload + apply sidebar vars
header.tsx: accentColor was missing from setUser call on mount —
applyAccentColor(undefined) fired on every reload, removing --primary.

auth-store: also set --sidebar-accent (15% opacity) and
--sidebar-accent-foreground so active sidebar items match accent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:30:33 +02:00
schalli 42daa87e5e feat(calendar): add domain field and test-connection button to Exchange sources
- Prisma: domain String? added to CalendarSource model (db push applied)
- DTOs: domain in CreateCalendarSourceDto, UpdateCalendarSourceDto, new TestCalendarSourceConfigDto
- Service: domain in SOURCE_SAFE_SELECT, addSource, updateSource; new testConnectionFromConfig method
- Controller: POST /calendar/sources/test-config (before :id routes to avoid collision)
- ExchangeProvider: domain in all source interfaces; passed as 3rd arg to EWS WebCredentials
- Frontend: domain in CalendarSource/CreateSourcePayload/UpdateSourcePayload; testSourceConfig API fn
- Form: domain field (Exchange-only), "Test connection" button with idle/loading/success/error states
- i18n: de+en keys for formFieldDomain, formFieldDomainHint, formTestConnection, formTesting, formTestSuccess, formTestFailed

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:15:13 +02:00
schalli 2e0e7290ba fix(calendar): remove setState call from render in isFormValid
validateUrl() calls setUrlError() — calling it during render triggers
React error #301 (cannot update component while rendering). Remove it
from the isFormValid computation; onChange/onBlur already keep urlError
in sync so !urlError is sufficient.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 13:01:03 +02:00
schalli e5b76b735a fix(i18n): add missing marketplace.accessDenied and translate calendar form
- Add marketplace.accessDenied to de.json and en.json
- Add 12 calendar form field/action keys to widgets.calendar in both locales
- Replace all hardcoded English strings in calendar-source-form.tsx with t() calls
- Remove locale-detection hack on Cancel button (was comparing t() result to English string)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 12:05:52 +02:00
schalli eebceb298d fix(08): apply code review findings (CR-01, CR-02, WR-01–05, IN-01)
- CR-01: fix SSRF bypass — isPrivateIpv6 now delegates ::ffff:<ipv4> to
  isPrivateIpv4, covering 172.16-31.x and 169.254.x ranges
- CR-02: add ParseUUIDPipe to GET /favorites widgetId param + service guard
  so missing widgetId returns 400 instead of leaking all user favorites
- WR-01: link-widget — replace raw 'link.error' key with t('link.error') (4 sites)
- WR-02: favorites-widget — fix load-path error to use t('favorites.error')
- WR-03: widget-catalog-modal — move aria-hidden from outer wrapper to backdrop
- WR-04: calculator — remove duplicate M button (MR clone); MC/MR/M+/M−/MS remain
- WR-05: schema — add FavoriteLink→WidgetInstance FK with onDelete:Cascade
- IN-01: create-widget.dto.ts — update comment from four to eight supported types

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-01 11:03:16 +02:00
schalli e9914f61cb feat(08-04): LinkWidget implementation + page.tsx wiring (GREEN)
- LinkWidget: single-link widget reusing FavoriteLink backend (D-06)
- Single-link enforcement: add form hidden when link exists
- List (row) and tile (grid) view modes, switchable in edit mode
- Inline add/edit/delete forms in edit mode
- Letter fallback span + icon with onError hide (T-08-11)
- Links with target="_blank" rel="noreferrer" (T-08-12)
- i18n keys added to de.json + en.json (link.*)
- wireLinkWidget(LinkWidget) added to portal page.tsx
- All 7 link-widget tests pass (GREEN); tsc --noEmit clean
2026-07-01 10:48:58 +02:00
schalli 4657e50494 test(08-04): add failing tests for LinkWidget single-link contract (RED)
- fetchFavorites called with instanceId on mount
- Link renders as anchor with target="_blank" rel="noreferrer" (T-08-12)
- Empty + edit mode shows add form; createFavorite called on submit
- Single-link enforcement: add form hidden when link exists (D-06)
- Edit mode: updateFavorite called with id and new title
- View toggle: list default, tile container on gridView click
- Letter fallback: iconUrl null shows first uppercase letter
2026-07-01 10:46:50 +02:00
schalli cc6f5ae893 feat(08-03): FavoritesWidget frontend + API client + page.tsx wiring (GREEN)
- favorites-api.ts: FavoriteLink type + fetchFavorites/createFavorite/updateFavorite/deleteFavorite
  all use credentials: include and API_URL/favorites
- favorites-widget.tsx: list/grid view, inline add/edit/delete in edit mode,
  icon + letter fallback, rel=noreferrer + target=_blank, no dangerouslySetInnerHTML (T-08-07)
- useEffect deps fixed to [instanceId] only — excludes t() to prevent re-fetch on each render
- page.tsx: wireFavoritesWidget(FavoritesWidget) wired
- Full test suite: 81/81 pass (17 test files)
- Web TypeScript: clean
2026-07-01 10:34:11 +02:00
schalli a3bb3f2396 test(08-03): add failing tests for FavoritesWidget CRUD + view toggle + letter fallback (RED)
- fetchFavorites called with instanceId (widgetId scope, Pitfall 3)
- covers add/edit/delete, empty state, list/grid toggle, letter fallback
- tests fail: favorites-widget.tsx and favorites-api.ts do not exist yet
2026-07-01 10:21:49 +02:00
schalli c1c7bff929 feat(08-02): StopwatchWidget with config persistence + reload reconstruction (GREEN)
- stopwatch-widget.tsx: start/stop/reset/lap controls, setInterval tick (100ms)
- Reload reconstruction: Date.now() - startedAt + elapsed (Pitfall 2 fix)
- State persisted via updateWidgetConfig(instanceId, {...}) on each action
- Single interval cleared on unmount and when not running (T-08-04 mitigated)
- Lap times stored newest-first per RESEARCH recommendation
- No CSS modules — Tailwind only (grep -c module.css = 0)
- page.tsx: added wireStopwatchWidget(StopwatchWidget) import + call
- All 7 stopwatch tests pass (GREEN)
2026-07-01 10:10:03 +02:00
schalli d8d008b2c4 test(08-02): add failing tests for Stopwatch behavior and reload reconstruction (RED)
- Tests for start/stop/reset/lap controls
- Reload reconstruction test verifies elapsed from startedAt + stored elapsed
- Tests fail because stopwatch-widget.tsx does not yet exist (expected RED state)
2026-07-01 10:08:44 +02:00
schalli 63ec93bd35 feat(08-01): registry foundation for 4 new widget types + Calculator widget (GREEN)
- widget-registry.tsx: extend WidgetType union with calculator/favorites/link/stopwatch
- widget-registry.tsx: add WIDGET_CONSTRAINTS entries with per-widget grid constraints (DASH-11)
- widget-registry.tsx: add SVG icons (CalculatorIcon, FavoritesIcon, LinkIcon, StopwatchIcon)
- widget-registry.tsx: add WIDGET_REGISTRY entries and wire functions for all 4 new types
- calculator-widget.tsx: full arithmetic implementation ported from personal-dashboard
  (parseDisplay, formatNumber, calculate, keyboard handler with stopPropagation)
- widget-catalog-modal.tsx: extend WIDGET_TYPES to include all 8 types
- create-widget.dto.ts: extend @IsIn to accept 8 widget types (T-08-01 mitigated)
- page.tsx: import CalculatorWidget and call wireCalculatorWidget()
- de.json / en.json: add i18n keys for calculator, favorites, link, stopwatch
- All 16 tests passing (GREEN)
2026-07-01 09:57:44 +02:00
schalli b751ae7453 test(08-01): add failing tests for Calculator widget and registry constraints (RED)
- calculator-widget.test.tsx: 5 behaviour tests (render, arithmetic, div/0, keyboard, decimal)
- widget-registry.test.tsx: DASH-11 structure check for all 8 widget types including new Phase-8 types
- Both suites fail (RED baseline) — implementation does not exist yet
2026-07-01 09:54:22 +02:00
schalli 88db54fa7d fix(account-settings): clear stale pw error on input + live header avatar update
Tessera CI/CD / Lint & Type Check (push) Successful in 44s
Tessera CI/CD / Tests (push) Successful in 37s
Tessera CI/CD / Build & Publish Images (push) Successful in 1m34s
- Password form errors (wrong pw, mismatch) now clear on first keystroke
  in any password field instead of persisting until next submit.
- Avatar upload now bumps avatarVersion in auth store and sets hasAvatar=true,
  so the header avatar switches to the uploaded image immediately without reload.
- Header img src uses ?v={avatarVersion} as cache-buster to force browser to
  fetch the new avatar when version increments.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 15:29:21 +02:00
schalli 85cd17452c refactor(admin): move SMTP settings from user settings to admin area
Tessera CI/CD / Lint & Type Check (push) Successful in 39s
Tessera CI/CD / Tests (push) Successful in 37s
Tessera CI/CD / Build & Publish Images (push) Successful in 3m28s
SMTP configuration is an admin concern, not a per-user setting. Removed
it from the settings sidebar and relocated to /admin/smtp with a link in
the admin sidebar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-30 14:47:44 +02:00
schalli 5ae498fd88 feat(quick-260630-gbh-01): header avatar display with initial fallback
- AuthUser store: add optional hasAvatar? field
- Header: populate hasAvatar from fetchCurrentUser() result
- Header avatar button: shows <img src='/api-proxy/users/me/avatar'> when hasAvatar=true with onError fallback to initial span
- Plain <img> tag used (same-origin /api-proxy rewrite, no next/image remote config needed)
2026-06-30 12:02:02 +02:00
schalli 4482a8ce78 feat(quick-260630-gbh-01): account settings page — avatar upload + conditional password form
- AuthUser interface: add isLocalUser? + hasAvatar? fields
- uploadAvatarAction: server action forwarding file to POST /users/me/avatar
- AccountSettingsForm: avatar preview with initial fallback + upload; password form only for local users; LDAP notice
- /settings/general/account page mirroring smtp page structure
- SettingsSidebar: Konto link above SMTP link
- de.json + en.json: categoryAccount + account.* keys
2026-06-30 12:00:40 +02:00
schalli 28d4fa5c82 fix(ci): fix sidebar tests + add registry publish + prod compose
Tessera CI/CD / Lint & Type Check (push) Successful in 46s
Tessera CI/CD / Tests (push) Successful in 38s
Tessera CI/CD / Build & Publish Images (push) Failing after 8m32s
- sidebar.test: expand category before asserting on module names
  (categories are collapsed by default since UI-Umbau)
- ci.yml: replace build-deploy with publish job that pushes images
  to git.vicolab.de container registry
- docker-compose.prod.yml: pull-only compose for server deployments
  using registry images

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 11:14:34 +02:00
schalli 01ff137f43 fix(07): UAT fixes — SMTP test email, DKV routing, Exchange domain field
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
- SMTP: add test-to field, send real email via sendMail() instead of verify()
- SMTP: fix no_auth warning shown as error for open-relay servers
- DKV: register dkv-fleet in MODULE_REGISTRY (fixes "Modul nicht gefunden")
- DKV: add dynamic [category]/[moduleSlug]/settings + vehicles sub-routes
- DKV: settings/vehicles links use useParams for consistent URLs
- DKV: add gear icon settings link to module main page
- DKV: rename module to "DKV-Rechnung" in seed + translations
- DKV: add optional domain field for Exchange (WebCredentials 3rd arg)
- DKV: hide IMAP-only fields (Port/Verschlüsselung/Ordner) when Exchange selected
- DKV: hide Exchange-only field (Domain) when IMAP selected
- Prisma: add domain column to DkvModuleConfig

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 21:58:26 +02:00
schalli 4de87a8ea2 fix(07): SMTP test warns when connection passes but no auth configured
Tessera CI/CD / Lint & Type Check (push) Waiting to run
Tessera CI/CD / Tests (push) Blocked by required conditions
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Server reachable without credentials (port 25 open relay) returns
{ success: true, warning: 'no_auth' } instead of green success.
Frontend shows red warning: server reachable but emails will fail.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 01:29:53 +02:00
schalli 75aec38dae feat(07-06): extend SettingsSidebar with Allgemein > SMTP category
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 40s
Tessera CI/CD / Tests (push) Waiting to run
- Add 'Allgemein' category section ABOVE existing Dashboard category
- Single SMTP link to /settings/general/smtp with identical active/inactive styling and aria-current
- isActive('/settings/general/smtp') works via pathname.startsWith branch
- Existing Dashboard/Widgets/Calendar items unchanged
2026-06-27 17:04:25 +02:00
schalli fd2dda69cb feat(07-06): settings-api client + SmtpSettingsForm + SMTP page (DKV-05)
Tessera CI/CD / Build & Deploy (push) Blocked by required conditions
Tessera CI/CD / Lint & Type Check (push) Successful in 41s
Tessera CI/CD / Tests (push) Waiting to run
- settings-api.ts: fetchSmtp/saveSmtp/testSmtp with credentials:'include'; SmtpConfig exposes only hasPassword (T-07-17)
- smtp-settings-form.tsx: Surface C form with show/hide password toggle, test-feedback auto-clears 6s
- settings/general/smtp/page.tsx: SmtpSettingsPage heading + SmtpSettingsForm
- de.json + en.json: add smtp.showPassword/hidePassword/testTesting/testSuccess/testFailed keys
- TDD GREEN: 5/5 tests pass; aria-hidden* on required markers removed for correct getByLabelText matching
2026-06-27 17:03:57 +02:00
schalli ba02b25cba refactor(ui): restructure sidebar and admin navigation
Tessera CI/CD / Lint & Type Check (push) Successful in 43s
Tessera CI/CD / Tests (push) Failing after 45s
Tessera CI/CD / Build & Deploy (push) Has been skipped
- Sidebar: remove footer (user info, settings, locale switcher), remove admin section, make category headers static with per-category collapse toggle
- Header dropdown: replace inline admin links with single "Administrator" entry
- Admin section: dedicated layout with AdminSidebar (mirrors Settings pattern) at /admin/*
- Disable self-delete button in user management (backend already rejects with 403)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-26 08:34:19 +02:00
schalli 24de136b9b feat(05-04): calendar settings page with source management and visibility toggle
- calendar-settings-panel.tsx: source list with color dots, type badges, visibility toggle (CAL-02), edit/delete actions, connection test auto-run, delete confirmation dialog
- calendar-source-form.tsx: add/edit form with name/type/URL/credentials/color; Exchange-mode select for exchange type; username/password hidden for ICS; client-side https-only validation (T-05-14)
- settings/dashboard/calendar/page.tsx: route page rendering CalendarSettingsPanel

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:26:06 +02:00
schalli c0f2f4ca51 feat(05-04): calendar API client, calendar widget, registry wiring
- calendar-api.ts: fetchSources/addSource/updateSource/deleteSource/testSource/fetchEvents with credentials:'include'
- calendar-widget.tsx: upcoming-events list with source color dots, three empty states (no sources/no events/loading)
- widget-registry.tsx: wireCalendarWidget() replaces placeholder with real CalendarWidget
- page.tsx: wires CalendarWidget into registry on mount
- i18n: added calendar.loading key to de.json and en.json

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:23:27 +02:00
schalli 2d8699e45c test(05-04): add failing tests for calendar widget
- Three test cases: event rendering with color dots, no-events empty state, no-sources empty state
- Mocks calendar-api and next-intl following existing test patterns

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 15:21:37 +02:00
schalli 7e2592b2af feat(05-02): add widget settings panel with search provider form
- Settings > Dashboard page with per-widget-instance config
- Clock config: timezone select (IANA list) + date toggle (D-12/D-13)
- Note config: editable title field (D-17)
- Search config: SearchProviderForm with add/delete and {query} validation (D-15)
- Calendar config: link to calendar-specific settings
- i18n keys for search provider management (en + de)
- Fix useRef initialization for React 19 strict mode (note-widget)
- Fix unknown type narrowing in widget title display

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 14:58:42 +02:00
schalli dd0209898b feat(05-02): add search and note widgets with tests
- SearchWidget: provider dropdown, text input, button; opens search in new tab via window.open (D-14/D-15)
- NoteWidget: MDEditor with compact toolbar, debounced autosave (1500ms), AbortController for in-flight cancellation (D-16/D-17/D-18)
- rehype-sanitize enabled for Markdown XSS prevention (T-05-05)
- Widget registry updated with wireSearchWidget/wireNoteWidget (no more placeholders)
- dashboard-api.ts: added fetchSearchProviders, addSearchProvider, removeSearchProvider, signal support on updateWidgetConfig
- 9 new tests passing (search: 5, note: 4)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:33:01 +02:00
schalli 97c13809f5 test(05-02): add failing tests for search and note widgets
- SearchWidget: provider selection, window.open with encoded query, Enter key trigger
- NoteWidget: debounced autosave, rapid typing collapse, AbortController usage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:30:45 +02:00
schalli 8a9c1ae6a9 feat(05-01): settings shell, header link, and i18n keys for dashboard phase
- Create settings/layout.tsx with SettingsSidebar and back-to-dashboard link
- Create settings/page.tsx with redirect to /settings/dashboard
- Create settings-sidebar.tsx with Widgets and Calendar nav items, aria-current
- Add Settings link in header user dropdown (gear icon, before logout)
- Add settings namespace (DE+EN) with all category and action keys
- Add widgets namespace (DE+EN) with all widget names, descriptions, error states
- Add header.settings key ("Einstellungen"/"Settings")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:23:47 +02:00
schalli d5e1c42e64 feat(05-01): dashboard grid, clock widget, widget registry, store, and tests
- Install react-grid-layout@2.2.3 and react-resizable
- Create widget-registry.tsx with all 4 widget types, WIDGET_CONSTRAINTS, WidgetProps
- Create dashboard-api.ts with fetch/save layout and widget CRUD functions
- Create dashboard-store.ts (Zustand, NO persist — D-05) with edit mode and auto-save on exit
- Create DashboardGrid with react-grid-layout v2 Responsive, ResizeObserver width
- Create ClockWidget using Intl.DateTimeFormat (no manual UTC offsets)
- Create WidgetWrapper with drag handle and delete button in edit mode
- Create EditModeToggle (pencil/checkmark), WidgetCatalogModal (2x2 grid)
- Rewrite portal page.tsx as dashboard with grid, edit toggle, widget catalog
- Add ResizeObserver polyfill in test setup, CSS mock support in vitest config
- All 5 tests green (dashboard grid + clock widget)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:21:43 +02:00
schalli 792b7661d8 test(05-01): add failing tests for dashboard grid and clock widget
- DashboardGrid test: empty state, widget rendering, edit mode affordances
- ClockWidget test: timezone rendering, showDate toggle behavior

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-24 11:15:18 +02:00
schalli e967ea9660 feat(04-03): migrate sidebar to Link/usePathname, add search and refresh signal
Replace all raw <a> with Next.js Link. Add usePathname-based active
highlighting, SidebarSearch with category/module filtering, and
sidebarRefreshKey subscription for live activation updates. 26 tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 10:21:27 +02:00
schalli dad9a779df fix(03): add module settings page, dynamic sidebar categories
- Admin modules page at /admin/modules with toggle switches
- Sidebar categories now fetch active modules from API dynamically
- Added "Module" link under admin section in sidebar
- Added i18n keys for module management (DE + EN)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-19 14:00:50 +02:00
schalli 6e19591168 feat(02-04): LDAP admin UI with config, mapping editor, and sync trigger 2026-06-19 08:40:29 +02:00
schalli bfb04eac66 feat(02-02): user CRUD API + admin page, tenant CRUD API + admin page
- Create UserController with GET/POST/PATCH/DELETE endpoints at /users
  - ADMIN sees own-tenant users only; SUPER_ADMIN sees all (T-02-10)
  - ADMIN cannot escalate to SUPER_ADMIN role (T-02-08)
  - ADMIN cannot delete self or cross-tenant users
- Create TenantController with GET/POST/PATCH/DELETE at /tenants
  - SUPER_ADMIN-only access (D-10)
  - Tenant deletion blocked if active users exist (T-02-09)
- Create CreateUserDto, UpdateUserDto, CreateTenantDto with class-validator
- Create admin/users page with user table, create/edit/delete modals
- Create admin/tenants page with tenant table, create/edit/deactivate (SUPER_ADMIN only)
- Add admin section to sidebar: Verwaltung > Benutzer + Mandanten
  - Verwaltung visible for ADMIN/SUPER_ADMIN; Tenants link SUPER_ADMIN only
- Install @nestjs/mapped-types for PartialType DTO pattern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:38:54 +02:00
schalli e7b2a70fc9 feat(02-02): login page UI, header/sidebar auth wiring, i18n keys
- Create split-screen login page with branding left (#ffed00) and form right (D-01)
- Login form has username, password, and remember-me checkbox (D-02)
- Wire header user avatar with auth store: shows user initial, dropdown with role badge and logout
- Wire sidebar footer with auth store: shows user name, role, and initial
- Add auth, header role, and admin i18n keys to both de.json and en.json
- All new UI strings use t() function (no hardcoded text)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-18 13:34:43 +02:00
schalli a9018083b2 fix(01): remove traefik, lighten dark mode, fix sidebar accent color, move locale switcher to sidebar footer 2026-06-18 12:41:02 +02:00