Erscheinungsbild im Admin-Bereich anpassbar
Name, Zusatz in der Fusszeile, Akzentfarbe, Logo und Favicon lassen sich unter Verwaltung -> Erscheinungsbild einstellen. Sie gelten fuer die ganze Installation und liegen in einer eigenen Tabelle mit genau einer Zeile. Zur Farbe: Tailwind 4 uebersetzt bg-indigo-600 zu background-color: var(--color-indigo-600). Die Anwendung schreibt diese Variablen zur Laufzeit neu — damit folgen alle rund 60 eingefaerbten Stellen der gewaehlten Farbe, ohne dass eine einzige Klasse angefasst werden muss. Uebernommen werden nur Farbton und Saettigung, nicht die Helligkeit. Die Helligkeitskurve stammt aus der Tailwind-Vorlage. Sonst wuerde ein helles Gelb die Kopfzeile unlesbar machen, denn auf Stufe 600 steht weisse Schrift. Die Umrechnung laeuft ueber OKLCH und trifft Tailwinds eigenes Indigo exakt. Logo und Favicon sind Uploads mit eigener Groessengrenze (2 MB bzw. 512 KB) und eigener Rechtepruefung: instance:manage statt codes:create. Uploads haben jetzt ein Feld "purpose" — sonst haette die Aufraeumroutine fuer herrenlose Dateien Logo und Favicon nach einem Tag geloescht. Ein ersetztes Bild wird gelaescht, und eine Datei aus einem QR-Code laesst sich nicht als Logo einhaengen. Auf den Landingpages gelten Farbe und Favicon, aber weder Logo noch Navigation — wer scannt, soll die Seite des Inhalts sehen. Ein erster Entwurf hielt das Erscheinungsbild 15 Sekunden im Speicher, um die Abfrage je Seitenaufruf zu sparen. Das ging schief: API-Route und Layout laufen in getrennten Modulinstanzen, das Verwerfen beim Speichern erreichte die andere Instanz nicht — nach dem Speichern blieb die Oberflaeche unveraendert. Jetzt ohne Puffer.
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
-- Erscheinungsbild: Name, Logo, Favicon und Akzentfarbe der Installation.
|
||||
|
||||
-- Wozu eine hochgeladene Datei gehört. Bestehende Dateien hängen alle an
|
||||
-- einem Code, deshalb ist die Voreinstellung richtig.
|
||||
ALTER TABLE "Upload" ADD COLUMN "purpose" TEXT NOT NULL DEFAULT 'code';
|
||||
CREATE INDEX "Upload_purpose_idx" ON "Upload"("purpose");
|
||||
|
||||
CREATE TABLE "Branding" (
|
||||
"id" TEXT NOT NULL DEFAULT 'branding',
|
||||
"appName" TEXT NOT NULL DEFAULT 'QR-Code-Generator',
|
||||
"footerText" TEXT NOT NULL DEFAULT 'statische und dynamische QR-Codes',
|
||||
"accentColor" TEXT NOT NULL DEFAULT '#4F46E5',
|
||||
"logoUploadId" TEXT,
|
||||
"faviconUploadId" TEXT,
|
||||
"updatedAt" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Branding_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
CREATE UNIQUE INDEX "Branding_logoUploadId_key" ON "Branding"("logoUploadId");
|
||||
CREATE UNIQUE INDEX "Branding_faviconUploadId_key" ON "Branding"("faviconUploadId");
|
||||
|
||||
ALTER TABLE "Branding" ADD CONSTRAINT "Branding_logoUploadId_fkey"
|
||||
FOREIGN KEY ("logoUploadId") REFERENCES "Upload"("id")
|
||||
ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
ALTER TABLE "Branding" ADD CONSTRAINT "Branding_faviconUploadId_fkey"
|
||||
FOREIGN KEY ("faviconUploadId") REFERENCES "Upload"("id")
|
||||
ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- Die eine Zeile gleich anlegen, damit die Anwendung sie nur noch lesen muss.
|
||||
INSERT INTO "Branding" ("id", "updatedAt") VALUES ('branding', CURRENT_TIMESTAMP);
|
||||
@@ -219,14 +219,45 @@ model Upload {
|
||||
data Bytes
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
/// „code" gehört zu einem QR-Code, „branding" zur Oberfläche. Nur die
|
||||
/// erste Art wird als herrenlos weggeräumt.
|
||||
purpose String @default("code")
|
||||
|
||||
/// Der Code, zu dem die Datei gehört. Leer, solange sie nur hochgeladen,
|
||||
/// der Code aber noch nicht gespeichert wurde; solche Reste räumt
|
||||
/// `src/lib/uploads.ts` nach einer Weile weg.
|
||||
qrCodeId String?
|
||||
qrCode QrCode? @relation(fields: [qrCodeId], references: [id], onDelete: Cascade)
|
||||
|
||||
brandingLogo Branding? @relation("BrandingLogo")
|
||||
brandingFavicon Branding? @relation("BrandingFavicon")
|
||||
|
||||
@@index([qrCodeId])
|
||||
@@index([createdAt])
|
||||
@@index([purpose])
|
||||
}
|
||||
|
||||
/// Erscheinungsbild der Installation — Name, Logo, Favicon, Akzentfarbe.
|
||||
///
|
||||
/// Genau eine Zeile mit fester Id. Ein eigenes Modell statt einzelner
|
||||
/// Schlüssel-Wert-Paare, damit die Felder ihren Typ behalten und die
|
||||
/// Beziehungen zu den hochgeladenen Dateien echte Fremdschlüssel sind.
|
||||
model Branding {
|
||||
id String @id @default("branding")
|
||||
appName String @default("QR-Code-Generator")
|
||||
/// Zeile in der Fußzeile, hinter dem Namen.
|
||||
footerText String @default("statische und dynamische QR-Codes")
|
||||
/// Akzentfarbe als Hex. Übernommen werden daraus Farbton und Sättigung;
|
||||
/// die Helligkeitsstufen stammen aus der Vorlage — siehe
|
||||
/// src/lib/branding-colors.ts.
|
||||
accentColor String @default("#4F46E5")
|
||||
|
||||
logoUploadId String? @unique
|
||||
logoUpload Upload? @relation("BrandingLogo", fields: [logoUploadId], references: [id], onDelete: SetNull)
|
||||
faviconUploadId String? @unique
|
||||
faviconUpload Upload? @relation("BrandingFavicon", fields: [faviconUploadId], references: [id], onDelete: SetNull)
|
||||
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
|
||||
/// Eine Rückmeldung, die jemand über die Landingpage eines Feedback-Codes
|
||||
|
||||
Reference in New Issue
Block a user