Add account hierarchy framework with migrations, RLS policies, and UI components
This commit is contained in:
@@ -1,15 +1,14 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useAction } from 'next-safe-action/hooks';
|
||||
|
||||
import { Plus, Pencil, Trash2, Settings } from 'lucide-react';
|
||||
import { useAction } from 'next-safe-action/hooks';
|
||||
|
||||
import { Button } from '@kit/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@kit/ui/card';
|
||||
import { Input } from '@kit/ui/input';
|
||||
import { toast } from '@kit/ui/sonner';
|
||||
|
||||
import {
|
||||
createRole,
|
||||
updateRole,
|
||||
@@ -93,18 +92,27 @@ function SettingsSection({
|
||||
>
|
||||
Erstellen
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => setShowAdd(false)}>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setShowAdd(false)}
|
||||
>
|
||||
Abbrechen
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{items.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">Keine Einträge vorhanden.</p>
|
||||
<p className="text-muted-foreground text-sm">
|
||||
Keine Einträge vorhanden.
|
||||
</p>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{items.map((item) => (
|
||||
<div key={String(item.id)} className="flex items-center justify-between rounded-lg border p-3">
|
||||
<div
|
||||
key={String(item.id)}
|
||||
className="flex items-center justify-between rounded-lg border p-3"
|
||||
>
|
||||
{editingId === String(item.id) ? (
|
||||
<div className="flex flex-1 gap-2">
|
||||
<Input
|
||||
@@ -122,7 +130,11 @@ function SettingsSection({
|
||||
>
|
||||
Speichern
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onClick={() => setEditingId(null)}>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setEditingId(null)}
|
||||
>
|
||||
Abbrechen
|
||||
</Button>
|
||||
</div>
|
||||
@@ -131,7 +143,9 @@ function SettingsSection({
|
||||
<div>
|
||||
<span className="font-medium">{String(item.name)}</span>
|
||||
{item.description && (
|
||||
<p className="text-xs text-muted-foreground">{String(item.description)}</p>
|
||||
<p className="text-muted-foreground text-xs">
|
||||
{String(item.description)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
@@ -150,7 +164,7 @@ function SettingsSection({
|
||||
size="sm"
|
||||
onClick={() => onDelete(String(item.id))}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-destructive" />
|
||||
<Trash2 className="text-destructive h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
@@ -171,42 +185,60 @@ export default function SettingsContent({
|
||||
feeTypes,
|
||||
}: SettingsContentProps) {
|
||||
// Roles
|
||||
const { execute: execCreateRole, isPending: isCreatingRole } = useAction(createRole, {
|
||||
onSuccess: () => toast.success('Funktion erstellt'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
const { execute: execUpdateRole, isPending: isUpdatingRole } = useAction(updateRole, {
|
||||
onSuccess: () => toast.success('Funktion aktualisiert'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
const { execute: execCreateRole, isPending: isCreatingRole } = useAction(
|
||||
createRole,
|
||||
{
|
||||
onSuccess: () => toast.success('Funktion erstellt'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
},
|
||||
);
|
||||
const { execute: execUpdateRole, isPending: isUpdatingRole } = useAction(
|
||||
updateRole,
|
||||
{
|
||||
onSuccess: () => toast.success('Funktion aktualisiert'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
},
|
||||
);
|
||||
const { execute: execDeleteRole } = useAction(deleteRole, {
|
||||
onSuccess: () => toast.success('Funktion gelöscht'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
|
||||
// Types
|
||||
const { execute: execCreateType, isPending: isCreatingType } = useAction(createAssociationType, {
|
||||
onSuccess: () => toast.success('Vereinstyp erstellt'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
const { execute: execUpdateType, isPending: isUpdatingType } = useAction(updateAssociationType, {
|
||||
onSuccess: () => toast.success('Vereinstyp aktualisiert'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
const { execute: execCreateType, isPending: isCreatingType } = useAction(
|
||||
createAssociationType,
|
||||
{
|
||||
onSuccess: () => toast.success('Vereinstyp erstellt'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
},
|
||||
);
|
||||
const { execute: execUpdateType, isPending: isUpdatingType } = useAction(
|
||||
updateAssociationType,
|
||||
{
|
||||
onSuccess: () => toast.success('Vereinstyp aktualisiert'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
},
|
||||
);
|
||||
const { execute: execDeleteType } = useAction(deleteAssociationType, {
|
||||
onSuccess: () => toast.success('Vereinstyp gelöscht'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
|
||||
// Fee Types
|
||||
const { execute: execCreateFeeType, isPending: isCreatingFee } = useAction(createFeeType, {
|
||||
onSuccess: () => toast.success('Beitragsart erstellt'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
const { execute: execUpdateFeeType, isPending: isUpdatingFee } = useAction(updateFeeType, {
|
||||
onSuccess: () => toast.success('Beitragsart aktualisiert'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
});
|
||||
const { execute: execCreateFeeType, isPending: isCreatingFee } = useAction(
|
||||
createFeeType,
|
||||
{
|
||||
onSuccess: () => toast.success('Beitragsart erstellt'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
},
|
||||
);
|
||||
const { execute: execUpdateFeeType, isPending: isUpdatingFee } = useAction(
|
||||
updateFeeType,
|
||||
{
|
||||
onSuccess: () => toast.success('Beitragsart aktualisiert'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
},
|
||||
);
|
||||
const { execute: execDeleteFeeType } = useAction(deleteFeeType, {
|
||||
onSuccess: () => toast.success('Beitragsart gelöscht'),
|
||||
onError: ({ error }) => toast.error(error.serverError ?? 'Fehler'),
|
||||
@@ -224,7 +256,9 @@ export default function SettingsContent({
|
||||
<SettingsSection
|
||||
title="Funktionen (Rollen)"
|
||||
items={roles}
|
||||
onAdd={(name, description) => execCreateRole({ accountId, name, description, sortOrder: 0 })}
|
||||
onAdd={(name, description) =>
|
||||
execCreateRole({ accountId, name, description, sortOrder: 0 })
|
||||
}
|
||||
onUpdate={(id, name) => execUpdateRole({ roleId: id, name })}
|
||||
onDelete={(id) => execDeleteRole({ roleId: id })}
|
||||
isAdding={isCreatingRole}
|
||||
@@ -234,7 +268,9 @@ export default function SettingsContent({
|
||||
<SettingsSection
|
||||
title="Vereinstypen"
|
||||
items={types}
|
||||
onAdd={(name, description) => execCreateType({ accountId, name, description, sortOrder: 0 })}
|
||||
onAdd={(name, description) =>
|
||||
execCreateType({ accountId, name, description, sortOrder: 0 })
|
||||
}
|
||||
onUpdate={(id, name) => execUpdateType({ typeId: id, name })}
|
||||
onDelete={(id) => execDeleteType({ typeId: id })}
|
||||
isAdding={isCreatingType}
|
||||
@@ -244,7 +280,9 @@ export default function SettingsContent({
|
||||
<SettingsSection
|
||||
title="Beitragsarten"
|
||||
items={feeTypes}
|
||||
onAdd={(name, description) => execCreateFeeType({ accountId, name, description, isActive: true })}
|
||||
onAdd={(name, description) =>
|
||||
execCreateFeeType({ accountId, name, description, isActive: true })
|
||||
}
|
||||
onUpdate={(id, name) => execUpdateFeeType({ feeTypeId: id, name })}
|
||||
onDelete={(id) => execDeleteFeeType({ feeTypeId: id })}
|
||||
isAdding={isCreatingFee}
|
||||
|
||||
@@ -2,10 +2,10 @@ import { getSupabaseServerClient } from '@kit/supabase/server-client';
|
||||
import { createVerbandApi } from '@kit/verbandsverwaltung/api';
|
||||
import { VerbandTabNavigation } from '@kit/verbandsverwaltung/components';
|
||||
|
||||
import { AccountNotFound } from '~/components/account-not-found';
|
||||
import { CmsPageShell } from '~/components/cms-page-shell';
|
||||
|
||||
import SettingsContent from './_components/settings-content';
|
||||
import { AccountNotFound } from '~/components/account-not-found';
|
||||
|
||||
interface Props {
|
||||
params: Promise<{ account: string }>;
|
||||
|
||||
Reference in New Issue
Block a user