Sortierung nach Titel und haengendes Overlay beheben
All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 2m3s
All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 2m3s
Sortierung: Erklaerungen erscheinen im Admin wie im oeffentlichen
Bereich alphabetisch nach Titel. Bewusst in JavaScript statt ueber die
Datenbank - SQLite vergleicht binaer, dort landeten "Aenderungs-
mitteilung" und "Uebernahme" hinter "Zaehlerstandsablesung". Intl mit
deutscher Kollation sortiert Ae bei A. Gemeinsame Regel in
lib/policy-sort.ts, benutzt von der Server-Komponente, der API und der
Admin-Tabelle; die Spaltenkoepfe bleiben umschaltbar.
Overlay: @radix-ui/react-select verlangte react-dismissable-layer
1.1.11, alle anderen Radix-Pakete 1.1.19. Dieses Paket setzt und
entfernt pointer-events auf dem body und fuehrt dazu eine Liste offener
Layer im Modulzustand - bei zwei Kopien zwei getrennte Listen. Ein
Select im Dialog liess deshalb beim Schliessen das Overlay des Dialogs
verschwinden und pointer-events: none stehen. Die Seite war danach bis
zum Neuladen tot.
Belegt im Browser mit einer Wegwerfseite (Select im Dialog):
zwei Versionen -> Dialog nach Select-Schliessen nicht mehr bedienbar,
body pointer-events bleibt "none"
eine Version -> body wieder leer, Seite klickbar
react-select, -label, -slot und -tabs auf aktuelle Versionen gehoben;
damit liegt jedes Radix-Kernpaket nur noch einmal im Baum.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
150fd3878c
commit
c951a4123a
@ -65,6 +65,7 @@ import { deletePolicy, usePrivacyPolicies } from "@/hooks/use-privacy-policies"
|
||||
import { usePolicyDefaults } from "@/hooks/use-policy-defaults"
|
||||
import { describeMissing, missingRequiredFields } from "@/lib/policy-completeness"
|
||||
import { formatDateTime, initials } from "@/lib/format"
|
||||
import { compareByTitle } from "@/lib/policy-sort"
|
||||
import { PrivacyPolicy } from "@/types/privacy-policy"
|
||||
|
||||
type SortKey = "title" | "createdAt" | "updatedAt"
|
||||
@ -75,8 +76,9 @@ export default function AdminDashboard() {
|
||||
const { defaults } = usePolicyDefaults()
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState("")
|
||||
const [sortBy, setSortBy] = useState<SortKey>("createdAt")
|
||||
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc")
|
||||
// Vorgabe: alphabetisch nach Titel. Die Spaltenköpfe bleiben umschaltbar.
|
||||
const [sortBy, setSortBy] = useState<SortKey>("title")
|
||||
const [sortOrder, setSortOrder] = useState<"asc" | "desc">("asc")
|
||||
const [isFormOpen, setIsFormOpen] = useState(false)
|
||||
const [editingPolicy, setEditingPolicy] = useState<PrivacyPolicy | null>(null)
|
||||
const [previewPolicy, setPreviewPolicy] = useState<PrivacyPolicy | null>(null)
|
||||
@ -99,9 +101,7 @@ export default function AdminDashboard() {
|
||||
)
|
||||
.sort((a, b) => {
|
||||
if (sortBy === "title") {
|
||||
return sortOrder === "asc"
|
||||
? a.title.localeCompare(b.title, "de")
|
||||
: b.title.localeCompare(a.title, "de")
|
||||
return sortOrder === "asc" ? compareByTitle(a, b) : compareByTitle(b, a)
|
||||
}
|
||||
const aDate = new Date(a[sortBy]).getTime()
|
||||
const bDate = new Date(b[sortBy]).getTime()
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
import { readPolicyDefaults, toApiPolicy } from '@/lib/policy-defaults.server'
|
||||
import { describeMissing, missingRequiredFields } from '@/lib/policy-completeness'
|
||||
import { resolveSlug } from '@/lib/policy-slug.server'
|
||||
import { sortByTitle } from '@/lib/policy-sort'
|
||||
import { PolicyDefaults } from '@/types/privacy-policy'
|
||||
|
||||
// Prisma client instance
|
||||
@ -70,16 +71,14 @@ export async function GET(request: NextRequest) {
|
||||
where: isAdmin ? {} : {
|
||||
isPublic: true
|
||||
},
|
||||
orderBy: {
|
||||
createdAt: 'desc'
|
||||
}
|
||||
}),
|
||||
readPolicyDefaults(prisma),
|
||||
])
|
||||
|
||||
// Standardtexte einsetzen, damit Konsumenten immer den geltenden Text sehen
|
||||
// Standardtexte einsetzen, damit Konsumenten immer den geltenden Text sehen.
|
||||
// Sortiert nach Titel – nicht über die Datenbank, siehe lib/policy-sort.
|
||||
return NextResponse.json(
|
||||
policies.map((policy) => toApiPolicy(policy, defaults))
|
||||
sortByTitle(policies.map((policy) => toApiPolicy(policy, defaults)))
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('Error fetching privacy policies:', error)
|
||||
|
||||
@ -4,6 +4,7 @@ import { PolicyList } from "@/components/policy-list"
|
||||
import { PublicHeader } from "@/components/public-header"
|
||||
import { SiteFooter } from "@/components/site-footer"
|
||||
import { readPolicyDefaults, toApiPolicy } from "@/lib/policy-defaults.server"
|
||||
import { sortByTitle } from "@/lib/policy-sort"
|
||||
|
||||
// Prisma client instance
|
||||
const prisma = new PrismaClient()
|
||||
@ -22,7 +23,6 @@ export default async function PublicDashboard() {
|
||||
const [policies, defaults] = await Promise.all([
|
||||
prisma.privacyPolicy.findMany({
|
||||
where: { isPublic: true },
|
||||
orderBy: { createdAt: "desc" },
|
||||
}),
|
||||
readPolicyDefaults(prisma),
|
||||
])
|
||||
@ -30,7 +30,9 @@ export default async function PublicDashboard() {
|
||||
return (
|
||||
<div className="flex min-h-screen flex-col bg-background">
|
||||
<PublicHeader subtitle="Datenschutz" />
|
||||
<PolicyList initial={policies.map((policy) => toApiPolicy(policy, defaults))} />
|
||||
<PolicyList
|
||||
initial={sortByTitle(policies.map((policy) => toApiPolicy(policy, defaults)))}
|
||||
/>
|
||||
<SiteFooter />
|
||||
</div>
|
||||
)
|
||||
|
||||
19
lib/policy-sort.ts
Normal file
19
lib/policy-sort.ts
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Einheitliche Sortierung der Erklärungen nach Titel.
|
||||
*
|
||||
* Bewusst nicht über die Datenbank: SQLite vergleicht Zeichenketten binär,
|
||||
* dort landete „Änderungsmitteilung“ hinter „Zählerstandsablesung“. Intl
|
||||
* sortiert nach deutschen Regeln, Ä also bei A.
|
||||
*
|
||||
* `numeric` sorgt außerdem für „Nr. 2“ vor „Nr. 10“.
|
||||
*/
|
||||
const collator = new Intl.Collator("de", { numeric: true })
|
||||
|
||||
export function compareByTitle(a: { title: string }, b: { title: string }) {
|
||||
return collator.compare(a.title, b.title)
|
||||
}
|
||||
|
||||
/** Gibt eine nach Titel aufsteigend sortierte Kopie zurück. */
|
||||
export function sortByTitle<T extends { title: string }>(policies: T[]): T[] {
|
||||
return [...policies].sort(compareByTitle)
|
||||
}
|
||||
2555
package-lock.json
generated
2555
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -29,13 +29,13 @@
|
||||
"@radix-ui/react-checkbox": "^1.3.11",
|
||||
"@radix-ui/react-dialog": "^1.1.15",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.24",
|
||||
"@radix-ui/react-label": "^2.1.7",
|
||||
"@radix-ui/react-label": "^2.1.15",
|
||||
"@radix-ui/react-scroll-area": "^1.2.18",
|
||||
"@radix-ui/react-select": "^2.2.6",
|
||||
"@radix-ui/react-select": "^2.3.7",
|
||||
"@radix-ui/react-separator": "^1.1.15",
|
||||
"@radix-ui/react-slot": "^1.2.3",
|
||||
"@radix-ui/react-slot": "^1.3.3",
|
||||
"@radix-ui/react-switch": "^1.3.7",
|
||||
"@radix-ui/react-tabs": "^1.1.13",
|
||||
"@radix-ui/react-tabs": "^1.1.21",
|
||||
"@radix-ui/react-tooltip": "^1.2.16",
|
||||
"@tailwindcss/postcss": "^4.1.13",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user