131 lines
4.7 KiB
TypeScript
131 lines
4.7 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
|
import { useQuery, useMutation } from '@tanstack/react-query';
|
|
import { api } from '../lib/api';
|
|
import { useToast } from '../components/Toast';
|
|
|
|
export default function Settings() {
|
|
const toast = useToast();
|
|
const [workspaceName, setWorkspaceName] = useState('');
|
|
const [defaultBillable, setDefaultBillable] = useState(false);
|
|
const [weekStart, setWeekStart] = useState('Monday');
|
|
|
|
const { data: settings, isLoading, refetch } = useQuery({
|
|
queryKey: ['settings'],
|
|
queryFn: () => api.getSettings(),
|
|
});
|
|
|
|
const { data: user } = useQuery({
|
|
queryKey: ['me'],
|
|
queryFn: () => api.getMe(),
|
|
});
|
|
|
|
const updateMutation = useMutation({
|
|
mutationFn: async (data: { workspaceName: string; defaultBillable: boolean; weekStart: string }) => {
|
|
return api.updateSettings(data);
|
|
},
|
|
onSuccess: async () => {
|
|
toast.success('Einstellungen erfolgreich aktualisiert');
|
|
await refetch();
|
|
},
|
|
onError: () => {
|
|
toast.error('Fehler beim Aktualisieren der Einstellungen');
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (settings) {
|
|
setWorkspaceName(settings.workspaceName || '');
|
|
setDefaultBillable(settings.defaultBillable ?? false);
|
|
setWeekStart(settings.weekStart || 'Monday');
|
|
}
|
|
}, [settings]);
|
|
|
|
const handleSubmit = async (e: React.FormEvent) => {
|
|
e.preventDefault();
|
|
updateMutation.mutate({
|
|
workspaceName,
|
|
defaultBillable,
|
|
weekStart,
|
|
});
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-[400px]">
|
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-slate-900"></div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (user?.role !== 'admin') {
|
|
return (
|
|
<div className="flex flex-col items-center justify-center min-h-[400px] text-center">
|
|
<h1 className="text-2xl font-bold text-slate-800">Zugriff verweigert</h1>
|
|
<p className="text-slate-500 mt-2">Diese Seite ist nur für Administratoren zugänglich.</p>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-2xl mx-auto py-10 px-4">
|
|
<div className="bg-white border border-slate-200 rounded-xl shadow-sm overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-slate-200 bg-slate-50/50">
|
|
<h1 className="text-xl font-semibold text-slate-800">Workspace Einstellungen</h1>
|
|
</div>
|
|
|
|
<form onSubmit={handleSubmit} className="p-6 space-y-6">
|
|
<div className="grid grid-cols-1 gap-6">
|
|
{/* Workspace Name */}
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-medium text-slate-700">Workspace Name</label>
|
|
<input
|
|
type="text"
|
|
value={workspaceName}
|
|
onChange={(e) => setWorkspaceName(e.target.value)}
|
|
className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all"
|
|
placeholder="z.B. Meine Agentur GmbH"
|
|
/>
|
|
</div>
|
|
|
|
{/* Default Billable */}
|
|
<div className="flex items-center space-x-3 py-2">
|
|
<input
|
|
id="defaultBillable"
|
|
type="checkbox"
|
|
checked={defaultBillable}
|
|
onChange={(e) => setDefaultBillable(e.target.checked)}
|
|
className="h-4 w-4 text-blue-600 focus:ring-blue-500 border-slate-300 rounded cursor-pointer"
|
|
/>
|
|
<label htmlFor="defaultBillable" className="text-sm font-medium text-slate-700 cursor-pointer">
|
|
Neue Einträge standardmäßig als abrechenbar markieren
|
|
</label>
|
|
</div>
|
|
|
|
{/* Week Start */}
|
|
<div className="space-y-2">
|
|
<label className="text-sm font-medium text-slate-700">Wochenstart</label>
|
|
<select
|
|
value={weekStart}
|
|
onChange={(e) => setWeekStart(e.target.value)}
|
|
className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 transition-all bg-white"
|
|
>
|
|
<option value="Monday">Montag</option>
|
|
<option value="Sunday">Sonntag</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-4 flex justify-end">
|
|
<button
|
|
type="submit"
|
|
disabled={updateMutation.isPending}
|
|
className="px-4 py-2 bg-slate-900 text-white rounded-md hover:bg-slate-800 disabled:opacity-50 transition-colors font-medium"
|
|
>
|
|
{updateMutation.isPending ? 'Speichert...' : 'Einstellungen speichern'}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |