tenant admin startseite schicker gestaltet und super-admin und tenant admin (filament) aufgesplittet.
Es gibt nun task collections und vordefinierte tasks für alle. Onboarding verfeinert und webseite-carousel gefixt (logging später entfernen!)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CheckCircle2, Circle, Loader2, Pencil, Plus, Trash2 } from 'lucide-react';
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
@@ -23,7 +24,7 @@ import {
|
||||
updateTask,
|
||||
} from '../api';
|
||||
import { isAuthError } from '../auth/tokens';
|
||||
import { ADMIN_EVENTS_PATH } from '../constants';
|
||||
import { ADMIN_EVENTS_PATH, ADMIN_TASK_COLLECTIONS_PATH } from '../constants';
|
||||
|
||||
type TaskFormState = {
|
||||
title: string;
|
||||
@@ -43,6 +44,7 @@ const INITIAL_FORM: TaskFormState = {
|
||||
|
||||
export default function TasksPage() {
|
||||
const navigate = useNavigate();
|
||||
const { t } = useTranslation('common');
|
||||
const [tasks, setTasks] = React.useState<TenantTask[]>([]);
|
||||
const [meta, setMeta] = React.useState<PaginationMeta | null>(null);
|
||||
const [page, setPage] = React.useState(1);
|
||||
@@ -150,6 +152,9 @@ export default function TasksPage() {
|
||||
}
|
||||
|
||||
async function toggleCompletion(task: TenantTask) {
|
||||
if (task.tenant_id === null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const updated = await updateTask(task.id, { is_completed: !task.is_completed });
|
||||
setTasks((prev) => prev.map((entry) => (entry.id === updated.id ? updated : entry)));
|
||||
@@ -165,13 +170,18 @@ export default function TasksPage() {
|
||||
title="Task Bibliothek"
|
||||
subtitle="Weise Aufgaben zu und tracke Fortschritt rund um deine Events."
|
||||
actions={
|
||||
<Button
|
||||
className="bg-gradient-to-r from-pink-500 via-fuchsia-500 to-purple-500 text-white shadow-lg shadow-pink-500/20"
|
||||
onClick={openCreate}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Neuer Task
|
||||
</Button>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Button variant="outline" onClick={() => navigate(ADMIN_TASK_COLLECTIONS_PATH)}>
|
||||
{t('navigation.collections')}
|
||||
</Button>
|
||||
<Button
|
||||
className="bg-gradient-to-r from-pink-500 via-fuchsia-500 to-purple-500 text-white shadow-lg shadow-pink-500/20"
|
||||
onClick={openCreate}
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Neu
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
{error && (
|
||||
@@ -269,25 +279,34 @@ function TaskRow({
|
||||
}) {
|
||||
const assignedCount = task.assigned_events_count ?? task.assigned_events?.length ?? 0;
|
||||
const completed = task.is_completed;
|
||||
const isGlobal = task.tenant_id === null;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3 rounded-2xl border border-slate-100 bg-white/90 p-4 shadow-sm sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="flex flex-1 items-start gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggle}
|
||||
className="rounded-full border border-pink-200 bg-white/80 p-2 text-pink-600 shadow-sm transition hover:bg-pink-50"
|
||||
onClick={isGlobal ? undefined : onToggle}
|
||||
aria-disabled={isGlobal}
|
||||
className={`rounded-full border border-pink-200 bg-white/80 p-2 text-pink-600 shadow-sm transition ${
|
||||
isGlobal ? 'opacity-50 cursor-not-allowed' : 'hover:bg-pink-50'
|
||||
}`}
|
||||
>
|
||||
{completed ? <CheckCircle2 className="h-5 w-5" /> : <Circle className="h-5 w-5" />}
|
||||
</button>
|
||||
<div className="space-y-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<p className={`text-sm font-semibold ${completed ? 'text-slate-500 line-through' : 'text-slate-900'}`}>
|
||||
{task.title}
|
||||
</p>
|
||||
<Badge variant="outline" className="border-pink-200 text-pink-600">
|
||||
{mapPriority(task.priority)}
|
||||
</Badge>
|
||||
{isGlobal && (
|
||||
<Badge variant="secondary" className="bg-slate-100 text-slate-600">
|
||||
Global
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{task.description && <p className="text-xs text-slate-600">{task.description}</p>}
|
||||
<div className="flex flex-wrap items-center gap-3 text-xs text-slate-500">
|
||||
@@ -297,10 +316,16 @@ function TaskRow({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button variant="outline" size="sm" onClick={onEdit}>
|
||||
<Button variant="outline" size="sm" onClick={onEdit} disabled={isGlobal} className={isGlobal ? 'opacity-50' : ''}>
|
||||
<Pencil className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={onDelete} className="text-rose-600">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onDelete}
|
||||
className={`text-rose-600 ${isGlobal ? 'opacity-50' : ''}`}
|
||||
disabled={isGlobal}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user