Files
bwitek/apps/web/src/app/[locale]/admin/navigation/page.tsx

385 lines
11 KiB
TypeScript

"use client"
import { trpc } from "@/utils/trpc";
import { useQuery } from "@tanstack/react-query";
import { toast } from "sonner";
import {useTranslations} from 'next-intl';
import { Card, CardContent } from "@/components/ui/card";
import { useMutation } from "@tanstack/react-query";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ResponsiveDataTable, type Column } from "@/components/admin/data-table/index";
import { NavigationForm } from "@/components/admin/navigation-form";
import { TopBarForm } from "@/components/admin/topbar-form";
export default function AdminPanelNavigation() {
const t = useTranslations();
const navigationItems = useQuery(trpc.content.getNavigation.queryOptions());
const topBarItems = useQuery(trpc.content.getTopBar.queryOptions());
const { mutate: createNavItem } = useMutation(trpc.content.createNavItem.mutationOptions({
onMutate: () => {
toast.loading(t("common.saving"));
},
onSuccess: () => {
toast.success(t("common.saved"));
navigationItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const { mutate: updateNavItem } = useMutation(trpc.content.updateNavItem.mutationOptions({
onMutate: () => {
toast.loading(t("common.updating"));
},
onSuccess: () => {
toast.success(t("common.updated"));
navigationItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const { mutate: deleteNavItem } = useMutation(trpc.content.deleteNavItem.mutationOptions({
onMutate: () => {
toast.loading(t("common.deleting"));
},
onSuccess: () => {
toast.success(t("common.deleted"));
navigationItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const { mutate: changeNavigationOrder } = useMutation(trpc.content.changeNavigationOrder.mutationOptions({
onMutate: () => {
toast.loading(t("common.updating"));
},
onSuccess: () => {
toast.success(t("common.updated"));
navigationItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const { mutate: createTopBarItem } = useMutation(trpc.content.createTopBarItem.mutationOptions({
onMutate: () => {
toast.loading(t("common.saving"));
},
onSuccess: () => {
toast.success(t("common.saved"));
topBarItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const { mutate: updateTopBarItem } = useMutation(trpc.content.updateTopBarItem.mutationOptions({
onMutate: () => {
toast.loading(t("common.updating"));
},
onSuccess: () => {
toast.success(t("common.updated"));
topBarItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const { mutate: deleteTopBarItem } = useMutation(trpc.content.deleteTopBarItem.mutationOptions({
onMutate: () => {
toast.loading(t("common.deleting"));
},
onSuccess: () => {
toast.success(t("common.deleted"));
topBarItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const { mutate: changeTopBarOrder } = useMutation(trpc.content.changeTopBarOrder.mutationOptions({
onMutate: () => {
toast.loading(t("common.updating"));
},
onSuccess: () => {
toast.success(t("common.updated"));
topBarItems.refetch();
},
onError: (error: any) => {
toast.error(error.message);
},
onSettled: () => {
toast.dismiss();
}
}));
const handleCreateNavItem = (data: any) => {
const maxOrder = navigationItems.data?.reduce((max, item) => Math.max(max, item.order), 0) || 0;
createNavItem({
...data,
url: data.url || null,
order: maxOrder + 1
});
};
const handleUpdateNavItem = (id: string, data: any) => {
const item = navigationItems.data?.find(item => item.id === id);
if (!item) return;
updateNavItem({
id,
label: data.label,
url: data.url || null,
external: data.external,
newTab: data.newTab,
isActive: data.isActive,
order: item.order
});
};
const handleDeleteNavItem = (id: string) => {
deleteNavItem(id);
};
const handleChangeNavigationOrder = (id: string, direction: "up" | "down") => {
changeNavigationOrder({
id,
direction
});
};
const handleCreateTopBarItem = (data: any) => {
const maxOrder = topBarItems.data?.reduce((max, item) => Math.max(max, item.order), 0) || 0;
createTopBarItem({
...data,
url: data.url || null,
iconName: data.iconName || null,
...(data.iconProvider ? { iconProvider: data.iconProvider } : {}),
order: maxOrder + 1
});
};
const handleUpdateTopBarItem = (id: string, data: any) => {
const item = topBarItems.data?.find(item => item.id === id);
if (!item) return;
updateTopBarItem({
id,
name: data.name,
url: data.url || null,
iconName: data.iconName || null,
...(data.iconProvider ? { iconProvider: data.iconProvider } : {}),
external: data.external,
newTab: data.newTab,
order: item.order
});
};
const handleDeleteTopBarItem = (id: string) => {
deleteTopBarItem(id);
};
const handleChangeTopBarOrder = (id: string, direction: "up" | "down") => {
changeTopBarOrder({
id,
direction
});
};
// Column definitions
const navigationColumns: Column[] = [
{
key: "label.pl",
header: t("admin.navigation.labelPl"),
render: (item) => (
<div className="max-w-[180px] truncate" title={item.label.pl}>
{item.label.pl}
</div>
),
sortable: true,
searchable: true,
priority: 'high'
},
{
key: "label.en",
header: t("admin.navigation.labelEn"),
render: (item) => (
<div className="max-w-[180px] truncate" title={item.label.en}>
{item.label.en}
</div>
),
sortable: true,
searchable: true,
priority: 'medium'
},
{
key: "url",
header: t("admin.navigation.url"),
render: (item) => (
<div className="max-w-[200px] truncate" title={item.url || "-"}>
{item.url || "-"}
</div>
),
sortable: true,
searchable: true,
priority: 'medium'
},
{
key: "isActive",
header: t("admin.navigation.active"),
render: (item) => item.isActive ? t("common.yes") : t("common.no"),
sortable: true,
priority: 'low'
},
{
key: "order",
header: t("admin.navigation.order"),
render: (item) => item.order,
sortable: true,
priority: 'low'
}
];
const topBarColumns: Column[] = [
{
key: "name.pl",
header: t("admin.topBar.namePl"),
render: (item) => (
<div className="max-w-[180px] truncate" title={item.name.pl}>
{item.name.pl}
</div>
),
sortable: true,
searchable: true,
priority: 'high'
},
{
key: "name.en",
header: t("admin.topBar.nameEn"),
render: (item) => (
<div className="max-w-[180px] truncate" title={item.name.en}>
{item.name.en}
</div>
),
sortable: true,
searchable: true,
priority: 'medium'
},
{
key: "iconName",
header: t("admin.topBar.iconName"),
render: (item) => item.iconName || "-",
sortable: true,
searchable: true,
priority: 'medium'
},
{
key: "url",
header: t("admin.topBar.url"),
render: (item) => (
<div className="max-w-[200px] truncate" title={item.url || "-"}>
{item.url || "-"}
</div>
),
sortable: true,
searchable: true,
priority: 'medium'
},
{
key: "order",
header: t("admin.topBar.order"),
render: (item) => item.order,
sortable: true,
priority: 'low'
}
];
if (navigationItems.isLoading || topBarItems.isLoading) {
return <div>{t("common.loading")}</div>;
}
return (
<Card>
<CardContent className="p-6">
<Tabs defaultValue="navigation">
<TabsList className="w-full mb-6">
<TabsTrigger value="navigation" className="flex-1">{t("admin.navigation.title")}</TabsTrigger>
<TabsTrigger value="topbar" className="flex-1">{t("admin.topBar.title")}</TabsTrigger>
</TabsList>
<TabsContent value="navigation">
<ResponsiveDataTable
title={t("admin.navigation.title")}
addButtonText={t("admin.navigation.addNew")}
columns={navigationColumns}
data={navigationItems.data || []}
FormComponent={NavigationForm}
onAdd={handleCreateNavItem}
onEdit={handleUpdateNavItem}
onDelete={handleDeleteNavItem}
onChangeOrder={handleChangeNavigationOrder}
addDialogTitle={t("admin.navigation.addNewTitle")}
editDialogTitle={t("admin.navigation.editTitle")}
deleteDialogTitle={t("admin.navigation.deleteTitle")}
deleteDialogConfirmText={t("admin.navigation.deleteConfirm")}
showOrderButtons={true}
/>
</TabsContent>
<TabsContent value="topbar">
<ResponsiveDataTable
title={t("admin.topBar.title")}
addButtonText={t("admin.topBar.addNew")}
columns={topBarColumns}
data={topBarItems.data || []}
FormComponent={TopBarForm}
onAdd={handleCreateTopBarItem}
onEdit={handleUpdateTopBarItem}
onDelete={handleDeleteTopBarItem}
onChangeOrder={handleChangeTopBarOrder}
addDialogTitle={t("admin.topBar.addNewTitle")}
editDialogTitle={t("admin.topBar.editTitle")}
deleteDialogTitle={t("admin.topBar.deleteTitle")}
deleteDialogConfirmText={t("admin.topBar.deleteConfirm")}
showOrderButtons={true}
/>
</TabsContent>
</Tabs>
</CardContent>
</Card>
);
}