feat(quick-260923-ad9): Datenmodell, Migration und Reiter-Grundlage - Task 1
Neues Modell Dashboard (D-01/D-02/D-09): position statt Standard-Feld, kein Unique auf (userId, position) - Umsortieren schreibt spaeter alle Positionen einer Transaktion neu. WidgetInstance/DashboardLayout haengen jetzt am Reiter statt am Benutzer (DashboardLayout.dashboardId @unique ersetzt userId @unique). Migration 20260923120000_dashboard_tabs: Zeilenschutz mit Mandant- UND Benutzerdimension (Form 20260911120000/20260921120000), Bestands- uebernahme fuer jeden Benutzer mit Kacheln oder Anordnung VOR den Fremdschluesseln (D-03) - gemessen: 0 Kacheln/Anordnungen ohne Reiter, genau 2 Reiter auf Position 0. dashboard.service.ts: listDashboards() (Transaktionssperre gegen doppelte Erstanlage, T-AD9-07), Riegel assertOwnedDashboard() (fail- closed gegen fremde Reiter, T-AD9-01/02/03) - getLayout/saveLayout/ getWidgets/addWidget laufen jetzt ueber dashboardId statt userId. GET /dashboard/tabs neu; die vier bestehenden Wege reichen die Reiter- Kennung durch. Verhalten fuer den Benutzer unveraendert (ein Reiter, wie bisher) - Task 2 ergaenzt Anlegen/Umbenennen/Loeschen/Umsortieren. dashboard.service.spec.ts: 43 Tests (31 alte unveraendert + 12 neue fuer Reiter-Anlage, -Reihenfolge und den Fremdreiter-Riegel bei allen vier Wegen). Zugriffsklassifikation nachgerechnet: 75 Paare (+1), Bereich dashboard 21->24 gebunden. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { IsIn, IsObject, IsOptional, IsString } from 'class-validator';
|
||||
import { WIDGET_TYPES } from '@tessera/shared';
|
||||
|
||||
/**
|
||||
* DTO for creating a new widget instance on a user's dashboard.
|
||||
* DTO for creating a new widget instance on one dashboard tab.
|
||||
*
|
||||
* quick-260922-m1h: `widgetType` wird gegen `WIDGET_TYPES` aus
|
||||
* `@tessera/shared` geprüft — dieselbe Liste, aus der das Frontend seine
|
||||
@@ -10,9 +10,15 @@ import { WIDGET_TYPES } from '@tessera/shared';
|
||||
* zweites Mal; vergaß man einen Eintrag, lehnte die API eine im Katalog
|
||||
* angebotene Kachel mit 400 ab.
|
||||
*
|
||||
* quick-260923-ad9: `dashboardId` selects the tab — the service verifies
|
||||
* ownership before writing (`assertOwnedDashboard`).
|
||||
*
|
||||
* config is optional and defaults to {} on the model.
|
||||
*/
|
||||
export class CreateWidgetDto {
|
||||
@IsString()
|
||||
dashboardId!: string;
|
||||
|
||||
@IsString()
|
||||
@IsIn([...WIDGET_TYPES])
|
||||
widgetType!: string;
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
import { IsObject } from 'class-validator';
|
||||
import { IsObject, IsString } from 'class-validator';
|
||||
|
||||
/**
|
||||
* DTO for saving/updating a user's dashboard layout.
|
||||
* DTO for saving/updating the layout of one dashboard tab (quick-260923-ad9).
|
||||
* The layouts object contains responsive breakpoint layouts
|
||||
* (lg, md, sm, xs, xxs) as managed by react-grid-layout.
|
||||
* `dashboardId` selects the tab — the service verifies ownership before
|
||||
* writing (`assertOwnedDashboard`).
|
||||
*/
|
||||
export class SaveLayoutDto {
|
||||
@IsString()
|
||||
dashboardId!: string;
|
||||
|
||||
@IsObject()
|
||||
layouts!: Record<string, unknown>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user