Shared + migration: table user_preference (thème clair/sombre/système) (step 1/4)
- Modèle Prisma UserPreference (1-1 avec UserProfile, onDelete Cascade), enum ThemePreference (LIGHT/DARK/SYSTEM, défaut SYSTEM) - packages/shared: THEME_PREFERENCES/ThemePreference, PreferencesView, updatePreferencesSchema
This commit is contained in:
parent
db427a3f30
commit
e3811dc280
5 changed files with 67 additions and 0 deletions
|
|
@ -0,0 +1,13 @@
|
||||||
|
-- CreateEnum
|
||||||
|
CREATE TYPE "ThemePreference" AS ENUM ('LIGHT', 'DARK', 'SYSTEM');
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "user_preference" (
|
||||||
|
"user_profile_id" INTEGER NOT NULL,
|
||||||
|
"theme" "ThemePreference" NOT NULL DEFAULT 'SYSTEM',
|
||||||
|
|
||||||
|
CONSTRAINT "user_preference_pkey" PRIMARY KEY ("user_profile_id")
|
||||||
|
);
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE "user_preference" ADD CONSTRAINT "user_preference_user_profile_id_fkey" FOREIGN KEY ("user_profile_id") REFERENCES "user_profiles"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||||
|
|
@ -98,10 +98,36 @@ model UserProfile {
|
||||||
/// a time — but Prisma models the admin side of a one-to-many FK as a
|
/// a time — but Prisma models the admin side of a one-to-many FK as a
|
||||||
/// list regardless of that real-world cardinality.
|
/// list regardless of that real-world cardinality.
|
||||||
administeredHouses House[] @relation("HouseAdmin")
|
administeredHouses House[] @relation("HouseAdmin")
|
||||||
|
preferences UserPreference?
|
||||||
|
|
||||||
@@map("user_profiles")
|
@@map("user_profiles")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Not in the original spec doc — personalization settings (theme for now,
|
||||||
|
/// meant to grow), one row per profile, created on demand (see
|
||||||
|
/// `preferences.service.ts`) rather than at signup — same "absent means the
|
||||||
|
/// default" philosophy as `dietId`/allergies.
|
||||||
|
enum ThemePreference {
|
||||||
|
LIGHT
|
||||||
|
DARK
|
||||||
|
/// Follow the OS/browser preference — the default. Not "no row yet" (that
|
||||||
|
/// case is handled in the service layer) but an explicit choice to track
|
||||||
|
/// the system, distinguishable from a user who hasn't decided yet if this
|
||||||
|
/// model ever needs that distinction.
|
||||||
|
SYSTEM
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserPreference {
|
||||||
|
/// Both the primary key and the FK — a strict 1-1 with UserProfile, no
|
||||||
|
/// separate auto-incrementing id (a profile has at most one preferences row).
|
||||||
|
userProfileId Int @id @map("user_profile_id")
|
||||||
|
theme ThemePreference @default(SYSTEM)
|
||||||
|
|
||||||
|
userProfile UserProfile @relation(fields: [userProfileId], references: [id], onDelete: Cascade)
|
||||||
|
|
||||||
|
@@map("user_preference")
|
||||||
|
}
|
||||||
|
|
||||||
/// Explicit join table for the user_profiles <-> allergy association
|
/// Explicit join table for the user_profiles <-> allergy association
|
||||||
/// (documented in the spec as a plain many-to-many, no extra fields).
|
/// (documented in the spec as a plain many-to-many, no extra fields).
|
||||||
model UserProfileAllergy {
|
model UserProfileAllergy {
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ export * from "./schemas/account.js";
|
||||||
export * from "./schemas/auth.js";
|
export * from "./schemas/auth.js";
|
||||||
export * from "./schemas/household.js";
|
export * from "./schemas/household.js";
|
||||||
export * from "./schemas/planning.js";
|
export * from "./schemas/planning.js";
|
||||||
|
export * from "./schemas/preferences.js";
|
||||||
export * from "./schemas/profile.js";
|
export * from "./schemas/profile.js";
|
||||||
export * from "./tools/assert-is-never.js";
|
export * from "./tools/assert-is-never.js";
|
||||||
export * from "./types/household.js";
|
export * from "./types/household.js";
|
||||||
export * from "./types/planning.js";
|
export * from "./types/planning.js";
|
||||||
|
export * from "./types/preferences.js";
|
||||||
export * from "./types/reference.js";
|
export * from "./types/reference.js";
|
||||||
export * from "./types/user-profile.js";
|
export * from "./types/user-profile.js";
|
||||||
|
|
|
||||||
11
packages/shared/src/schemas/preferences.ts
Normal file
11
packages/shared/src/schemas/preferences.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { z } from "zod";
|
||||||
|
import { THEME_PREFERENCES } from "../types/preferences.js";
|
||||||
|
|
||||||
|
// See schemas/auth.ts for the shared client/server validation rationale.
|
||||||
|
|
||||||
|
/** Payload accepted by `PATCH /preferences`. */
|
||||||
|
export const updatePreferencesSchema = z.object({
|
||||||
|
theme: z.enum(THEME_PREFERENCES),
|
||||||
|
});
|
||||||
|
/** Inferred TS type for {@link updatePreferencesSchema}'s validated output. */
|
||||||
|
export type UpdatePreferencesInput = z.infer<typeof updatePreferencesSchema>;
|
||||||
15
packages/shared/src/types/preferences.ts
Normal file
15
packages/shared/src/types/preferences.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* The 3 values a profile's theme preference can take — `SYSTEM` means "no
|
||||||
|
* explicit choice, follow the OS/browser preference" (see `apps/web`'s
|
||||||
|
* `ThemeContext`, which maps this to *not* setting the `data-theme`
|
||||||
|
* attribute at all, letting `_theme.scss`'s `prefers-color-scheme` media
|
||||||
|
* query take over).
|
||||||
|
*/
|
||||||
|
export const THEME_PREFERENCES = ["LIGHT", "DARK", "SYSTEM"] as const;
|
||||||
|
/** Inferred TS type for one {@link THEME_PREFERENCES} member. */
|
||||||
|
export type ThemePreference = (typeof THEME_PREFERENCES)[number];
|
||||||
|
|
||||||
|
/** A profile's personalization preferences, as returned by `GET /preferences` / `PATCH /preferences`. */
|
||||||
|
export interface PreferencesView {
|
||||||
|
theme: ThemePreference;
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue