TorbatYar/frontend/modules/beauty/pages/staff-portal.tsx
Mortezakoohjani 6f4a484051 Migrate Beauty, Healthcare, and Accounting frontend to modular src/modules architecture.
Move business logic out of App Router into module packages, add boundary validation scripts, and keep all routes as thin re-exports without changing URLs or API behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-26 22:28:27 +03:30

159 lines
5.3 KiB
TypeScript

"use client";
import { useQuery } from "@tanstack/react-query";
import { PageHeader, EmptyState, Badge, DataTable } from "@/components/ds";
import { beautyBusinessApi } from "@/modules/beauty/services/beauty-business-api";
import { useTenantId } from "@/hooks/useTenantId";
import { useBeautyLookups } from "@/modules/beauty/hooks/useBeautyLookups";
import { AppointmentCard } from "@/modules/beauty/design-system";
import {
AggregateStatsRow,
BeautyPageError,
BeautyPageLoader,
SettingsListPage,
} from "./shared";
export function StaffPortalDashboard() {
const { tenantId } = useTenantId();
const { staff, isLoading } = useBeautyLookups();
const schedulesQ = useQuery({
queryKey: ["beauty", tenantId, "staff-schedules"],
queryFn: () => beautyBusinessApi.staffSchedules.list(tenantId!),
enabled: !!tenantId,
});
const apptsQ = useQuery({
queryKey: ["beauty", tenantId, "staff-appts"],
queryFn: () => beautyBusinessApi.appointments.list(tenantId!),
enabled: !!tenantId,
});
if (!tenantId || isLoading || schedulesQ.isLoading) return <BeautyPageLoader />;
return (
<div>
<PageHeader title="داشبورد پرسنل" description="برنامه و نوبت‌های امروز." />
<AggregateStatsRow
stats={[
{ label: "پرسنل", value: staff.length },
{ label: "برنامه‌ها", value: schedulesQ.data?.length ?? 0 },
{ label: "نوبت‌ها", value: apptsQ.data?.length ?? 0 },
]}
/>
</div>
);
}
export function StaffPortalSchedule() {
const { tenantId } = useTenantId();
const { staffName } = useBeautyLookups();
const q = useQuery({
queryKey: ["beauty", tenantId, "staff-portal-schedule"],
queryFn: () => beautyBusinessApi.staffSchedules.list(tenantId!),
enabled: !!tenantId,
});
if (!tenantId || q.isLoading) return <BeautyPageLoader />;
if (q.error) return <BeautyPageError error={q.error} onRetry={() => q.refetch()} />;
return (
<div>
<PageHeader title="برنامه کاری" description="staff-schedules API." />
<DataTable
columns={[
{ key: "code", header: "کد" },
{
key: "staff_ref",
header: "پرسنل",
render: (r) => staffName(String(r.staff_ref)),
},
{ key: "day_of_week", header: "روز" },
{ key: "start_time", header: "شروع" },
{ key: "end_time", header: "پایان" },
]}
rows={(q.data ?? []) as unknown as Record<string, unknown>[]}
empty={<EmptyState title="برنامه‌ای ثبت نشده" />}
/>
</div>
);
}
export function StaffPortalCommission() {
const { tenantId } = useTenantId();
const q = useQuery({
queryKey: ["beauty", tenantId, "staff-commission"],
queryFn: () => beautyBusinessApi.commissionRules.list(tenantId!),
enabled: !!tenantId,
});
if (!tenantId || q.isLoading) return <BeautyPageLoader />;
if (q.error) return <BeautyPageError error={q.error} onRetry={() => q.refetch()} />;
return (
<div>
<PageHeader title="کمیسیون" description="commission-rules API." />
<DataTable
columns={[
{ key: "code", header: "کد" },
{ key: "name", header: "نام" },
{ key: "rule_type", header: "نوع" },
{
key: "rate_percent",
header: "درصد",
render: (r) => (r.rate_percent != null ? `${r.rate_percent}%` : "—"),
},
{
key: "status",
header: "وضعیت",
render: (r) => <Badge tone="default">{String(r.status)}</Badge>,
},
]}
rows={(q.data ?? []) as unknown as Record<string, unknown>[]}
empty={<EmptyState title="قانون کمیسیونی ثبت نشده" />}
/>
</div>
);
}
export function StaffPortalAppointments() {
const { tenantId } = useTenantId();
const { staffName, serviceName } = useBeautyLookups();
const q = useQuery({
queryKey: ["beauty", tenantId, "staff-portal-appts"],
queryFn: () => beautyBusinessApi.appointments.list(tenantId!),
enabled: !!tenantId,
});
if (!tenantId || q.isLoading) return <BeautyPageLoader />;
if (q.error) return <BeautyPageError error={q.error} onRetry={() => q.refetch()} />;
return (
<div>
<PageHeader title="نوبت‌های من" description="appointments API." />
<div className="mt-6 space-y-3">
{(q.data ?? []).map((a) => (
<AppointmentCard
key={a.id}
appointment={a}
staffName={staffName(a.staff_ref)}
serviceName={serviceName(a.service_ref)}
/>
))}
</div>
{(q.data ?? []).length === 0 ? <EmptyState title="نوبتی نیست" /> : null}
</div>
);
}
export function StaffPortalNotifications() {
return (
<div>
<PageHeader title="اعلان‌ها" description="communication-integrations." />
<EmptyState title="اعلانی وجود ندارد" />
</div>
);
}
export function StaffPortalSettings() {
return <SettingsListPage title="تنظیمات پرسنل" description="settings API." />;
}