Startseite: „Beliebt" ersetzt durch „nur dynamisch"
Das Abzeichen stammte aus Version 1, als genau ein Typ nutzbar war. Seit alle 15 gebaut sind, stand es an jeder Kachel und sagte damit nichts. An seiner Stelle steht jetzt nur noch dort etwas, wo es die Wahl beeinflusst: sechs Typen brauchen zwingend eine Kurz-URL und funktionieren deshalb nicht ohne diesen Server. Die uebrigen neun Kacheln bleiben unbeschriftet. Die Badge-Variante hiess "popular" und wird an drei weiteren Stellen als Hervorhebung benutzt — sie heisst jetzt "accent", weil sie mit Beliebtheit nichts zu tun hat. Dazu die Roadmap neu geordnet: Typen je Gruppe, Statistik, Papierkorb, Logo im Code, Ablaufdatum — in dieser Reihenfolge, jeweils mit dem, was dabei zu entscheiden ist.
This commit is contained in:
+90
-41
@@ -1,9 +1,7 @@
|
|||||||
# Roadmap
|
# Roadmap
|
||||||
|
|
||||||
Was gebaut ist, und was danach sinnvoll wäre. Der zweite Teil ist eine
|
Was gebaut ist, was als Nächstes kommt und was danach denkbar wäre. Die
|
||||||
Sammlung von Vorschlägen mit einer groben Einschätzung des Aufwands — keine
|
Aufwände sind grobe Einschätzungen, keine Zusagen.
|
||||||
Zusage und keine Reihenfolge. Was zuerst kommt, entscheidet, was im Betrieb
|
|
||||||
fehlt.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -71,34 +69,97 @@ Die Bilder erzeugt ein Skript, damit sie nicht veralten.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Denkbar als Nächstes
|
## Als Nächstes
|
||||||
|
|
||||||
Die Einschätzungen sind grobe Hausnummern für eine Person, die den Code kennt.
|
In dieser Reihenfolge. Die Einschätzungen sind grobe Hausnummern für eine
|
||||||
|
Person, die den Code kennt.
|
||||||
|
|
||||||
### Naheliegend
|
### 1. QR-Typen je Gruppe freigeben · mittel
|
||||||
|
|
||||||
**Statistik über die Zeit** · mittel
|
Heute gilt die Freigabe eines Typs für die ganze Installation: Was
|
||||||
Der Zähler weiß heute nur, wie oft und wann zuletzt. Ein Verlauf je Tag wäre
|
abgeschaltet ist, ist für alle weg. Bei mehreren Gruppen ist das zu grob — die
|
||||||
eine eigene Tabelle plus ein Diagramm in der Codeansicht. Die eigentliche
|
Marketingabteilung braucht Social Media, die Werkstatt nur Website und PDF.
|
||||||
Frage ist nicht die Technik, sondern wie lange man so etwas aufbewahrt — und
|
|
||||||
was man dabei über die Scannenden erfährt. Ein reiner Tageszähler ohne
|
Geplant: Die Freigabe bekommt eine zweite Dimension. Es bleibt eine globale
|
||||||
Herkunft und Gerät ist unbedenklich und würde für „läuft die Kampagne?“
|
Einstellung als Grundlage, und je Gruppe lässt sich davon abweichen. Wer in
|
||||||
genügen.
|
mehreren Gruppen ist, sieht auf der Startseite die Vereinigung; beim Anlegen
|
||||||
|
entscheidet die gewählte Gruppe.
|
||||||
|
|
||||||
|
Zu klären ist der Fall, in dem ein Typ global aus, in einer Gruppe aber an
|
||||||
|
ist — entweder die Gruppe darf das überstimmen, oder die globale Abschaltung
|
||||||
|
gewinnt immer. Die zweite Lesart ist die weniger überraschende und
|
||||||
|
wahrscheinlich die richtige.
|
||||||
|
|
||||||
|
Berührt: `QrTypeSetting` (zusammengesetzter Schlüssel aus Typ und Gruppe),
|
||||||
|
`src/lib/qr/availability.ts`, die Startseite und die Typ-Verwaltung, die dann
|
||||||
|
eine Gruppenauswahl bekommt.
|
||||||
|
|
||||||
|
### 2. Statistik über die Zeit · mittel
|
||||||
|
|
||||||
|
Der Zähler weiß heute nur, wie oft und wann zuletzt. Gebraucht wird der
|
||||||
|
Verlauf: Wie lief die Kampagne in den ersten Tagen, wann ist Ruhe
|
||||||
|
eingekehrt?
|
||||||
|
|
||||||
|
Geplant: eine Tabelle mit einer Zeile je Code und Tag, dazu ein Diagramm in
|
||||||
|
der Codeansicht und die Zahlen der letzten 7, 30 und 365 Tage.
|
||||||
|
|
||||||
|
Bewusst **ohne** Herkunft, Gerät oder Verweisseite. Ein reiner Tageszähler
|
||||||
|
sagt nichts über einzelne Menschen aus und braucht deshalb weder Einwilligung
|
||||||
|
noch Löschfristen. Wer mehr will, muss sich mit beidem befassen — das ist eine
|
||||||
|
andere Entscheidung als „ein Diagramm wäre schön“.
|
||||||
|
|
||||||
|
### 3. Papierkorb statt Löschen · klein
|
||||||
|
|
||||||
|
Heute ist ein gedruckter Code nach einem Fehlklick unwiederbringlich tot,
|
||||||
|
samt seiner Dateien und Rückmeldungen.
|
||||||
|
|
||||||
|
Geplant: Gelöschte Codes 30 Tage aufheben und dann endgültig entfernen. In
|
||||||
|
„Meine Codes“ ein Reiter „Papierkorb“ mit Wiederherstellen. Solange ein Code
|
||||||
|
im Papierkorb liegt, bleibt sein Slug belegt — sonst könnte ihn ein neuer Code
|
||||||
|
bekommen und die gedruckten Codes zeigten plötzlich woandershin.
|
||||||
|
|
||||||
|
### 4. Logo im QR-Code · mittel
|
||||||
|
|
||||||
|
Ein kleines Bild in die Mitte setzen, wie man es von Firmen-Codes kennt.
|
||||||
|
|
||||||
|
Geplant: Upload eines Logos je Code, mittig eingesetzt, mit erzwungener
|
||||||
|
Fehlerkorrektur **H** und einer Warnung, sobald zu viel Fläche verdeckt wird.
|
||||||
|
|
||||||
|
Das ist der Punkt mit dem größten Risiko, etwas Kaputtes auszuliefern: Ein
|
||||||
|
Code, der auf dem eigenen Handy noch funktioniert, kann bei anderen
|
||||||
|
Scanner-Apps durchfallen. Gehört deshalb nicht ohne eine ehrliche Prüfung mit
|
||||||
|
mehreren Geräten und Apps ausgeliefert — und mit einer klaren Obergrenze für
|
||||||
|
die verdeckte Fläche.
|
||||||
|
|
||||||
|
### 5. Ablaufdatum und Zeitsteuerung · mittel
|
||||||
|
|
||||||
|
Ein Code, der ab einem Datum woandershin führt oder abläuft — für
|
||||||
|
Veranstaltungen, Aktionen und Aushänge mit Verfallsdatum.
|
||||||
|
|
||||||
|
Geplant, in zwei Stufen:
|
||||||
|
|
||||||
|
- **Ablauf:** ab einem Zeitpunkt zeigt der Code eine erklärende Seite statt
|
||||||
|
des Ziels. Wichtig ist der Text — wer davorsteht, soll erfahren, dass die
|
||||||
|
Aktion vorbei ist, nicht dass „etwas nicht gefunden“ wurde.
|
||||||
|
- **Zeitplan:** mehrere Ziele mit Gültigkeitsfenstern, damit derselbe
|
||||||
|
Aufsteller montags auf die Mittagskarte und samstags auf die Veranstaltung
|
||||||
|
zeigt.
|
||||||
|
|
||||||
|
Zu bedenken: Zeitzonen und die Frage, was beim Bearbeiten mit vergangenen
|
||||||
|
Fenstern geschieht.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Später denkbar
|
||||||
|
|
||||||
**Codes exportieren** · klein
|
**Codes exportieren** · klein
|
||||||
Eine CSV aller Codes einer Gruppe mit Name, Kurz-URL, Ziel und Scans — für
|
Eine CSV aller Codes einer Gruppe mit Name, Kurz-URL, Ziel und Scans — für
|
||||||
die Übergabe an eine Druckerei oder die Ablage.
|
die Übergabe an eine Druckerei oder die Ablage.
|
||||||
|
|
||||||
**Papierkorb statt Löschen** · klein
|
|
||||||
Gelöschte Codes 30 Tage aufheben, dann endgültig entfernen. Heute ist ein
|
|
||||||
gedruckter Code nach einem Fehlklick unwiederbringlich tot.
|
|
||||||
|
|
||||||
**Codes duplizieren** · klein
|
**Codes duplizieren** · klein
|
||||||
„Wie dieser, aber mit anderem Ziel“ — bei vielen ähnlichen Projekten spart
|
„Wie dieser, aber mit anderem Ziel“ — bei vielen ähnlichen Projekten spart
|
||||||
das jedes Mal ein paar Minuten.
|
das jedes Mal ein paar Minuten.
|
||||||
|
|
||||||
### Wenn der Bedarf da ist
|
|
||||||
|
|
||||||
**Stapelverarbeitung** · mittel
|
**Stapelverarbeitung** · mittel
|
||||||
Eine CSV hochladen und daraus 200 Codes erzeugen, als ZIP zurück. Sinnvoll,
|
Eine CSV hochladen und daraus 200 Codes erzeugen, als ZIP zurück. Sinnvoll,
|
||||||
sobald jedes Produkt oder jeder Tisch einen eigenen Code bekommt.
|
sobald jedes Produkt oder jeder Tisch einen eigenen Code bekommt.
|
||||||
@@ -109,38 +170,26 @@ von Verzeichnisgruppen zu Gruppen dieser Anwendung ist möglich, braucht aber
|
|||||||
eine klare Regel dafür, was mit von Hand vergebenen Rollen geschieht — sonst
|
eine klare Regel dafür, was mit von Hand vergebenen Rollen geschieht — sonst
|
||||||
verliert jemand beim nächsten Anmelden stillschweigend seine Rechte.
|
verliert jemand beim nächsten Anmelden stillschweigend seine Rechte.
|
||||||
|
|
||||||
**Logo im QR-Code** · mittel
|
|
||||||
Ein kleines Bild in die Mitte setzen, mit erzwungener Fehlerkorrektur H und
|
|
||||||
einer Warnung, wenn zu viel verdeckt wird. Braucht eine ehrliche Prüfung mit
|
|
||||||
mehreren Scanner-Apps, sonst entstehen Codes, die nur auf dem eigenen Handy
|
|
||||||
funktionieren.
|
|
||||||
|
|
||||||
**Ablaufdatum und Zeitsteuerung** · mittel
|
|
||||||
Ein Code, der ab einem Datum woandershin führt oder abläuft — für
|
|
||||||
Veranstaltungen und Aktionen.
|
|
||||||
|
|
||||||
**Passwortgeschützte Landingpages** · mittel
|
**Passwortgeschützte Landingpages** · mittel
|
||||||
Für Unterlagen, die nicht jeder sehen soll, der den Aushang fotografiert.
|
Für Unterlagen, die nicht jeder sehen soll, der den Aushang fotografiert.
|
||||||
Heute ist die Empfehlung klar: Vertrauliches gehört nicht hinter einen
|
Heute ist die Empfehlung klar: Vertrauliches gehört nicht hinter einen
|
||||||
offenen QR-Code.
|
offenen QR-Code.
|
||||||
|
|
||||||
### Größere Brocken
|
|
||||||
|
|
||||||
**Mehrsprachige Oberfläche** · groß
|
|
||||||
Die Anwendung ist durchgehend deutsch, auch in den Fehlermeldungen und den
|
|
||||||
Landingpages. Eine zweite Sprache berührt jede Datei.
|
|
||||||
|
|
||||||
**Eigene Gestaltung der Landingpages** · groß
|
|
||||||
Logo, Farben und Schrift je Gruppe, damit die Seite nach der eigenen Firma
|
|
||||||
aussieht. Technisch überschaubar, gestalterisch eine eigene Aufgabe — und es
|
|
||||||
braucht Grenzen, damit nichts Unlesbares entsteht.
|
|
||||||
|
|
||||||
**Dateien außerhalb der Datenbank** · mittel
|
**Dateien außerhalb der Datenbank** · mittel
|
||||||
Heute liegen Uploads in PostgreSQL, damit es bei einem Volumen bleibt. Wer
|
Heute liegen Uploads in PostgreSQL, damit es bei einem Volumen bleibt. Wer
|
||||||
sehr viele oder sehr große Dateien ablegt, will sie irgendwann auf einem
|
sehr viele oder sehr große Dateien ablegt, will sie irgendwann auf einem
|
||||||
Objektspeicher (S3) haben. Der Wechsel betrifft nur `src/lib/uploads.ts` und
|
Objektspeicher (S3) haben. Der Wechsel betrifft nur `src/lib/uploads.ts` und
|
||||||
die Auslieferungsroute — aber auch die Sicherungsstrategie.
|
die Auslieferungsroute — aber auch die Sicherungsstrategie.
|
||||||
|
|
||||||
|
**Eigene Gestaltung der Landingpages** · groß
|
||||||
|
Logo, Farben und Schrift je Gruppe, damit die Seite nach der eigenen Firma
|
||||||
|
aussieht. Technisch überschaubar, gestalterisch eine eigene Aufgabe — und es
|
||||||
|
braucht Grenzen, damit nichts Unlesbares entsteht.
|
||||||
|
|
||||||
|
**Mehrsprachige Oberfläche** · groß
|
||||||
|
Die Anwendung ist durchgehend deutsch, auch in den Fehlermeldungen und den
|
||||||
|
Landingpages. Eine zweite Sprache berührt jede Datei.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Wie ein neuer Typ dazukommt
|
## Wie ein neuer Typ dazukommt
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export default async function AccountPage() {
|
|||||||
<dt className="text-slate-500">Rolle:</dt>
|
<dt className="text-slate-500">Rolle:</dt>
|
||||||
<dd>
|
<dd>
|
||||||
{user.isInstanceAdmin ? (
|
{user.isInstanceAdmin ? (
|
||||||
<Badge tone="popular">Administrator der Installation</Badge>
|
<Badge tone="accent">Administrator der Installation</Badge>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-slate-900">Benutzer</span>
|
<span className="text-slate-900">Benutzer</span>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -67,11 +67,16 @@ export default async function HomePage() {
|
|||||||
>
|
>
|
||||||
{qrType.title}
|
{qrType.title}
|
||||||
</span>
|
</span>
|
||||||
{usable ? (
|
{/* Kein Abzeichen „Beliebt“ mehr: Es stand an jedem
|
||||||
<Badge tone="popular">Beliebt</Badge>
|
nutzbaren Typ und sagte damit nichts. Hier steht nur
|
||||||
) : (
|
noch, was die Wahl beeinflusst — nämlich wenn ein Typ
|
||||||
|
zwingend eine Kurz-URL braucht und deshalb nicht ohne
|
||||||
|
diesen Server funktioniert. */}
|
||||||
|
{!usable ? (
|
||||||
<Badge tone="soon">Bald verfügbar</Badge>
|
<Badge tone="soon">Bald verfügbar</Badge>
|
||||||
)}
|
) : !qrType.supportsStatic ? (
|
||||||
|
<Badge tone="neutral">nur dynamisch</Badge>
|
||||||
|
) : null}
|
||||||
</span>
|
</span>
|
||||||
<span
|
<span
|
||||||
className={
|
className={
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ export function DomainAdmin({ baseUrlHostname }: { baseUrlHostname: string }) {
|
|||||||
{domain.hostname}
|
{domain.hostname}
|
||||||
</h2>
|
</h2>
|
||||||
{domain.hostname === baseUrlHostname ? (
|
{domain.hostname === baseUrlHostname ? (
|
||||||
<Badge tone="popular">aus BASE_URL</Badge>
|
<Badge tone="accent">aus BASE_URL</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
{!domain.isActive ? (
|
{!domain.isActive ? (
|
||||||
<Badge tone="soon">abgeschaltet</Badge>
|
<Badge tone="soon">abgeschaltet</Badge>
|
||||||
|
|||||||
@@ -236,12 +236,14 @@ export function Badge({
|
|||||||
tone = "neutral",
|
tone = "neutral",
|
||||||
children,
|
children,
|
||||||
}: {
|
}: {
|
||||||
tone?: "neutral" | "popular" | "soon" | "success";
|
tone?: "neutral" | "accent" | "soon" | "success";
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}) {
|
}) {
|
||||||
const tones: Record<string, string> = {
|
const tones: Record<string, string> = {
|
||||||
neutral: "bg-slate-100 text-slate-600",
|
neutral: "bg-slate-100 text-slate-600",
|
||||||
popular: "bg-indigo-100 text-indigo-700",
|
// Hervorhebung — hieß früher "popular", als sie nur für das Abzeichen
|
||||||
|
// „Beliebt" auf der Startseite gedacht war.
|
||||||
|
accent: "bg-indigo-100 text-indigo-700",
|
||||||
soon: "bg-slate-100 text-slate-500",
|
soon: "bg-slate-100 text-slate-500",
|
||||||
success: "bg-emerald-100 text-emerald-700",
|
success: "bg-emerald-100 text-emerald-700",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ export function UserAdmin({
|
|||||||
{user.name?.trim() || user.email}
|
{user.name?.trim() || user.email}
|
||||||
</span>
|
</span>
|
||||||
{user.isInstanceAdmin ? (
|
{user.isInstanceAdmin ? (
|
||||||
<Badge tone="popular">Administrator</Badge>
|
<Badge tone="accent">Administrator</Badge>
|
||||||
) : null}
|
) : null}
|
||||||
{!user.isActive ? <Badge tone="soon">deaktiviert</Badge> : null}
|
{!user.isActive ? <Badge tone="soon">deaktiviert</Badge> : null}
|
||||||
{user.isLocked ? (
|
{user.isLocked ? (
|
||||||
|
|||||||
Reference in New Issue
Block a user