Files
2026-04-21 06:30:48 +07:00

113 lines
3.7 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { getModules, toggleModule } from "@/services/modules";
import { usePermissions } from "@/hooks/usePermissions";
import { useApiStore, useLocaleStore } from "@/store/uiStore";
import DataTable from "@/components/ui/Table";
import { ModuleItem } from "@/types/api";
import { t } from "@/lib/locale";
import PageHeader from "@/components/ui/PageHeader";
import ConfirmDialog from "@/components/ui/ConfirmDialog";
export default function ModulesPage() {
const locale = useLocaleStore((s) => s.locale);
const permissions = usePermissions();
const addToast = useApiStore((s) => s.addToast);
const [modules, setModules] = useState<ModuleItem[]>([]);
const [pendingModule, setPendingModule] = useState<ModuleItem | null>(null);
const [confirmLoading, setConfirmLoading] = useState(false);
useEffect(() => {
const loadModules = async () => {
if (!permissions.isAdmin) return;
try {
const rows = await getModules();
setModules(rows);
} catch (error) {
addToast((error as { message?: string })?.message || t("loadFailed", locale), "error");
}
};
loadModules();
}, [permissions.isAdmin, addToast, locale]);
const onToggle = (row: ModuleItem) => {
setPendingModule(row);
};
const onConfirmToggle = async () => {
if (!pendingModule) return;
setConfirmLoading(true);
try {
await toggleModule(pendingModule.code, { enabled: !pendingModule.enabled });
setModules((prev) =>
prev.map((item) =>
item.code === pendingModule.code ? { ...item, enabled: !pendingModule.enabled } : item
)
);
addToast(t("moduleToggled", locale), "success");
} catch (error) {
addToast((error as { message?: string })?.message || t("actionFailed", locale), "error");
} finally {
setPendingModule(null);
setConfirmLoading(false);
}
};
if (!permissions.isAdmin) {
return <div className="alert alert-warning">{t("forbidden", locale)}</div>;
}
return (
<main className="vstack gap-3">
<PageHeader
title={t("modules", locale)}
breadcrumb={[
{ label: t("dashboard", locale), href: "/dashboard" },
{ label: t("modules", locale), href: "/dashboard/modules" }
]}
/>
<div className="card page-card">
<div className="card-body">
<DataTable
columns={[
{ key: "code", header: "Code" },
{ key: "name", header: "Name" },
{
key: "enabled",
header: "Enabled",
render: (row) => (
<span className="badge bg-azure">
{row.enabled ? t("enabled", locale) : t("disabled", locale)}
</span>
)
},
{
key: "actions",
header: t("actions", locale),
render: (row) => (
<button className="btn btn-outline-primary btn-sm" onClick={() => onToggle(row)}>
{row.enabled ? t("disable", locale) : t("enable", locale)}
</button>
)
}
]}
data={modules}
/>
</div>
</div>
<ConfirmDialog
open={!!pendingModule}
title={pendingModule ? `${pendingModule.enabled ? t("disable", locale) : t("enable", locale)} ${pendingModule.code}` : ""}
message={t("moduleToggled", locale)}
onConfirm={onConfirmToggle}
onCancel={() => setPendingModule(null)}
confirmLabel={t("save", locale)}
cancelLabel="Cancel"
loading={confirmLoading}
/>
</main>
);
}