Yaltopia-Ticket-Admin/src/pages/admin/settings/index.tsx
2026-06-11 10:48:11 +03:00

143 lines
5.4 KiB
TypeScript

import { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { settingsService, type Setting } from "@/services";
import { toast } from "sonner";
import type { ApiError } from "@/types/error.types";
import { cn } from "@/lib/utils";
export default function SettingsPage() {
const queryClient = useQueryClient();
const [selectedCategory, setSelectedCategory] = useState<string>("GENERAL");
const { data: settings, isLoading } = useQuery({
queryKey: ["admin", "settings", selectedCategory],
queryFn: () => settingsService.getSettings(selectedCategory),
});
const updateSettingMutation = useMutation({
mutationFn: ({ key, value }: { key: string; value: string }) =>
settingsService.updateSetting(key, { value }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["admin", "settings"] });
toast.success("Setting updated successfully");
},
onError: (error) => {
const apiError = error as ApiError;
toast.error(
apiError.response?.data?.message || "Failed to update setting",
);
},
});
const handleSave = (key: string, value: string) => {
updateSettingMutation.mutate({ key, value });
};
const categories = [
"GENERAL",
"EMAIL",
"STORAGE",
"SECURITY",
"API",
"FEATURES",
];
return (
<div className="space-y-8 max-w-7xl mx-auto bg-white p-4 min-h-screen">
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4">
<div>
<h1 className="text-3xl font-bold text-gray-900 tracking-tight">
System Settings
</h1>
<p className="text-gray-500 mt-1">
Configure global application parameters.
</p>
</div>
<div className="flex items-center gap-2">
{/* View only access: Create Setting button removed */}
</div>
</div>
<Tabs
value={selectedCategory}
onValueChange={setSelectedCategory}
className="space-y-6"
>
<TabsList className="bg-gray-100/50 p-1 rounded-none border border-gray-200">
{categories.map((cat) => (
<TabsTrigger
key={cat}
value={cat}
className="rounded-none data-[state=active]:bg-white data-[state=active]:shadow-sm px-6 text-[10px] font-bold uppercase tracking-widest transition-all"
>
{cat}
</TabsTrigger>
))}
</TabsList>
<TabsContent value={selectedCategory} className="outline-none">
<Card className="border shadow-none rounded-none">
<CardHeader className="border-b pb-4 bg-gray-50/30">
<CardTitle className="text-xs font-bold uppercase tracking-widest text-gray-400">
{selectedCategory} Configuration
</CardTitle>
</CardHeader>
<CardContent className="p-0 divide-y">
{isLoading ? (
<div className="p-20 text-center text-gray-400 animate-pulse font-medium uppercase tracking-widest text-[10px]">
Fetching system variables...
</div>
) : settings && settings.length > 0 ? (
settings.map((setting: Setting) => (
<div
key={setting.key}
className="p-6 flex flex-col md:flex-row md:items-center justify-between gap-6 hover:bg-gray-50/50 transition-colors"
>
<div className="max-w-md">
<Label
className="text-sm font-bold text-gray-900 uppercase tracking-tighter"
htmlFor={setting.key}
>
{setting.key.replace(/\./g, " / ")}
</Label>
{setting.description && (
<p className="text-xs text-gray-400 mt-1 leading-relaxed">
{setting.description}
</p>
)}
</div>
<div className="flex-1 md:max-w-sm">
<Input
id={setting.key}
defaultValue={setting.value}
className={cn(
"h-10 rounded-none border-gray-200 text-sm font-medium focus-visible:ring-gray-900",
updateSettingMutation.isPending &&
"opacity-50 pointer-events-none",
)}
onBlur={(e) => {
if (e.target.value !== setting.value) {
handleSave(setting.key, e.target.value);
}
}}
/>
</div>
</div>
))
) : (
<div className="p-20 text-center text-gray-400 italic">
No variables defined for this category.
</div>
)}
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
}