"use client";
import Link from "next/link";
import { useQuery } from "@tanstack/react-query";
import {
PageHeader,
LoadingState,
ErrorState,
Badge,
Card,
CardContent,
DataTable,
EmptyState,
} 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 { StatWidget, BeautyTablePage } from "@/modules/beauty/design-system";
export function BeautyPageLoader({ label = "در حال بارگذاری…" }: { label?: string }) {
return ;
}
export function BeautyPageError({
error,
onRetry,
}: {
error: unknown;
onRetry?: () => void;
}) {
const message = error instanceof Error ? error.message : "خطا در دریافت دادهها";
return ;
}
export function useBeautyServiceMeta() {
const healthQ = useQuery({
queryKey: ["beauty", "health"],
queryFn: () => beautyBusinessApi.health(),
});
const capsQ = useQuery({
queryKey: ["beauty", "capabilities"],
queryFn: () => beautyBusinessApi.capabilities(),
});
const metricsQ = useQuery({
queryKey: ["beauty", "metrics"],
queryFn: () => beautyBusinessApi.metrics(),
});
return { healthQ, capsQ, metricsQ };
}
export function ServiceMetaBadges() {
const { healthQ, capsQ } = useBeautyServiceMeta();
return (
{healthQ.data ? (
سرویس {healthQ.data.version}
) : null}
{capsQ.data ? فاز {capsQ.data.phase} : null}
);
}
export function MetricsGrid() {
const { metricsQ } = useBeautyServiceMeta();
if (metricsQ.isLoading) return null;
if (!metricsQ.data) return null;
return (
متریکهای سرویس
{metricsQ.data.note ? (
{metricsQ.data.note}
) : null}
{Object.entries(metricsQ.data.metrics).map(([k, v]) => (
{k}: {String(v)}
))}
);
}
export function AggregateStatsRow({
stats,
}: {
stats: { label: string; value: string | number; hint?: string }[];
}) {
return (
{stats.map((s) => (
))}
);
}
export function ExternalProvidersByKind({
kind,
title,
description,
accountingLink,
}: {
kind: "accounting" | "crm" | "communication" | "delivery" | "loyalty";
title: string;
description: string;
accountingLink?: boolean;
}) {
const { tenantId } = useTenantId();
const q = useQuery({
queryKey: ["beauty", tenantId, "external-providers", kind],
queryFn: () => beautyBusinessApi.externalProviders.list(tenantId!),
enabled: !!tenantId,
select: (rows) => rows.filter((p) => p.provider_kind === kind),
});
if (!tenantId || q.isLoading) return ;
if (q.error) return q.refetch()} />;
return (
{String(r.status)},
},
]}
rows={(q.data ?? []) as unknown as Record[]}
empty={}
actions={
accountingLink ? (
حسابداری تربتیار
) : undefined
}
/>
);
}
export function EntityListPage({
title,
description,
queryKey,
queryFn,
columns,
renderCard,
emptyTitle,
}: {
title: string;
description?: string;
queryKey: string[];
queryFn: (tenantId: string) => Promise;
columns: { key: string; header: string; render?: (row: T) => React.ReactNode }[];
renderCard?: (row: T) => React.ReactNode;
emptyTitle?: string;
}) {
const { tenantId } = useTenantId();
const q = useQuery({
queryKey,
queryFn: () => queryFn(tenantId!),
enabled: !!tenantId,
});
if (!tenantId || q.isLoading) return ;
if (q.error) return q.refetch()} />;
const rows = q.data ?? [];
return (
{renderCard ? (
{rows.map((r) => (
{renderCard(r)}
))}
) : (
({
...c,
render: c.render ? (row) => c.render!(row as T) : undefined,
}))}
rows={rows as unknown as Record[]}
empty={}
/>
)}
{renderCard && rows.length === 0 ? : null}
);
}
export function AnalyticsPage({ title }: { title: string }) {
const { tenantId } = useTenantId();
const { organizations, branches, customers, staff, services, isLoading } = useBeautyLookups();
const apptsQ = useQuery({
queryKey: ["beauty", tenantId, "analytics-appointments"],
queryFn: () => beautyBusinessApi.appointments.list(tenantId!),
enabled: !!tenantId,
});
const { capsQ } = useBeautyServiceMeta();
if (!tenantId || isLoading || apptsQ.isLoading) return ;
if (apptsQ.error) return apptsQ.refetch()} />;
return (
);
}
export function MonitoringPage() {
const { healthQ, capsQ, metricsQ } = useBeautyServiceMeta();
if (healthQ.isLoading || capsQ.isLoading) return ;
const err = healthQ.error || capsQ.error || metricsQ.error;
if (err) return healthQ.refetch()} />;
return (
Health
{healthQ.data?.status}
{healthQ.data?.service} v{healthQ.data?.version}
Capabilities
فاز {capsQ.data?.phase}
{capsQ.data?.commercial_product}
Independence
{capsQ.data?.independence.integration_mode}
);
}
export function SettingsListPage({ title, description }: { title: string; description: string }) {
const { tenantId } = useTenantId();
const q = useQuery({
queryKey: ["beauty", tenantId, "settings"],
queryFn: () => beautyBusinessApi.settings.list(tenantId!),
enabled: !!tenantId,
});
if (!tenantId || q.isLoading) return ;
if (q.error) return q.refetch()} />;
return (
(
{JSON.stringify(r.value ?? {})}
),
},
{ key: "description", header: "توضیح", render: (r) => String(r.description ?? "—") },
]}
rows={(q.data ?? []) as unknown as Record[]}
empty={}
/>
);
}