dummy data removed

This commit is contained in:
brooktewabe 2026-04-01 11:29:10 +03:00
parent 4d229d0b94
commit 4ed7161a33
3 changed files with 107 additions and 8 deletions

View File

@ -24,9 +24,11 @@ import {
} from "@/components/ui/table";
import { Progress } from "@/components/ui/progress";
import { apiGet } from "@/lib/api";
import type { Booking, DashboardPayload } from "@/lib/types";
import { formatDate, formatMoney } from "@/lib/format";
import { roomDisplayName } from "@/lib/room-utils";
import { Spinner } from "@/components/ui/spinner";
import { useAuthStore } from "@/store/authStore";
import type { Booking, DashboardPayload } from "@/lib/types";
const tooltipStyle = {
backgroundColor: "var(--navy)",
@ -35,18 +37,111 @@ const tooltipStyle = {
color: "#fff",
};
type HotelSummaryResponse = {
arrivalsToday?: number;
arrivals?: number;
departuresToday?: number;
departures?: number;
unpaidHolds?: number;
revenueMonth?: string | number;
bookingsByStatus?: Record<string, number>;
};
function isDashboardPayload(d: unknown): d is DashboardPayload {
return typeof d === "object" && d !== null && "bookingSeries" in d;
}
function isHotelSummary(d: unknown): d is HotelSummaryResponse {
if (typeof d !== "object" || d === null) return false;
const x = d as HotelSummaryResponse;
return (
typeof x.arrivalsToday === "number" ||
typeof x.arrivals === "number" ||
x.bookingsByStatus !== undefined
);
}
export function DashboardPage() {
const [data, setData] = useState<DashboardPayload | null>(null);
const selectedPropertyId = useAuthStore((s) => s.selectedPropertyId);
const [data, setData] = useState<DashboardPayload | HotelSummaryResponse | null>(
null
);
const [err, setErr] = useState<string | null>(null);
useEffect(() => {
apiGet<DashboardPayload>("/dashboard")
setErr(null);
setData(null);
apiGet<DashboardPayload | HotelSummaryResponse>(`/dashboard/summary`)
.then(setData)
.catch((e) => setErr(String(e)));
}, []);
}, [selectedPropertyId]);
if (err) return <p className="text-destructive">{err}</p>;
if (!data) return <p className="text-muted-foreground">Loading</p>;
if (!data) return (
<div className="flex min-h-[400px] items-center justify-center">
<Spinner size={32} />
</div>
);
if (isHotelSummary(data) && !isDashboardPayload(data)) {
const arrivals = data.arrivalsToday ?? data.arrivals ?? 0;
const departures = data.departuresToday ?? data.departures ?? 0;
const revenueRaw = data.revenueMonth ?? 0;
const revenueNum =
typeof revenueRaw === "string" ? parseFloat(revenueRaw) : Number(revenueRaw);
return (
<div className="space-y-6">
<div>
<h1 className="text-2xl font-bold tracking-tight">Dashboard</h1>
</div>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
{[
{ label: "Arrivals today", value: arrivals },
{ label: "Departures today", value: departures },
{ label: "Unpaid holds", value: data.unpaidHolds ?? 0 },
{
label: "Revenue (month)",
value: formatMoney(Number.isFinite(revenueNum) ? revenueNum : 0),
},
].map((c) => (
<Card key={c.label} className="rounded-2xl">
<CardHeader className="pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">
{c.label}
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-2xl font-bold">{c.value}</p>
</CardContent>
</Card>
))}
</div>
{data.bookingsByStatus && Object.keys(data.bookingsByStatus).length > 0 ? (
<Card className="rounded-2xl">
<CardHeader>
<CardTitle className="text-base">Bookings by status</CardTitle>
</CardHeader>
<CardContent className="flex flex-wrap gap-2">
{Object.entries(data.bookingsByStatus).map(([k, v]) => (
<Badge key={k} variant="secondary">
{k}: {v}
</Badge>
))}
</CardContent>
</Card>
) : null}
</div>
);
}
if (!isDashboardPayload(data)) {
return (
<p className="text-muted-foreground">
Unexpected dashboard response. Try again or check the API.
</p>
);
}
return (
<div className="space-y-6">
@ -263,7 +358,7 @@ export function DashboardPage() {
{formatDate(b.checkIn)} {formatDate(b.checkOut)}
</TableCell>
<TableCell className="text-xs">
{roomDisplayName(b.roomId)}
{b.roomDisplayLabel ?? roomDisplayName(b.roomId)}
</TableCell>
<TableCell>
<Badge variant="secondary">{b.status}</Badge>

View File

@ -11,15 +11,17 @@ import {
TableRow,
} from "@/components/ui/table";
import { apiGet } from "@/lib/api";
import { useAuthStore } from "@/store/authStore";
import type { Payment } from "@/lib/types";
import { formatDateTime, formatMoney } from "@/lib/format";
export function PaymentsPage() {
const selectedPropertyId = useAuthStore((s) => s.selectedPropertyId);
const [rows, setRows] = useState<Payment[]>([]);
useEffect(() => {
apiGet<{ data: Payment[] }>("/payments").then((r) => setRows(r.data));
}, []);
}, [selectedPropertyId]);
return (
<div className="space-y-6">

View File

@ -11,17 +11,19 @@ import {
TableRow,
} from "@/components/ui/table";
import { apiGet } from "@/lib/api";
import { useAuthStore } from "@/store/authStore";
import type { Transaction } from "@/lib/types";
import { formatDateTime, formatMoney } from "@/lib/format";
export function TransactionsPage() {
const selectedPropertyId = useAuthStore((s) => s.selectedPropertyId);
const [rows, setRows] = useState<Transaction[]>([]);
useEffect(() => {
apiGet<{ data: Transaction[] }>("/transactions").then((r) =>
setRows(r.data)
);
}, []);
}, [selectedPropertyId]);
return (
<div className="space-y-6">