Initial top-reservation app: bookings, objects catalog, statistics, PWA
This commit is contained in:
commit
14a849bfb9
62 files changed
+12427
No files matched your search
@@ -0,0 +1,54 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter, usePathname } from "next/navigation";
|
||||
import { CopyButton } from "@/components/copy-button";
|
||||
|
||||
function formatMonthLabel(monthKey: string): string {
|
||||
const [y, m] = monthKey.split("-").map(Number);
|
||||
const labels = ["янв", "фев", "мар", "апр", "май", "июн", "июл", "авг", "сен", "окт", "ноя", "дек"];
|
||||
return `${labels[m - 1]} ${y}`;
|
||||
}
|
||||
|
||||
export function StatisticsView({ monthKey, stats }: { monthKey: string; stats: { name: string; count: number }[] }) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const total = stats.reduce((s, r) => s + r.count, 0);
|
||||
const max = Math.max(1, ...stats.map((r) => r.count));
|
||||
|
||||
const summaryText = [`Статистика по бронированиям на ${formatMonthLabel(monthKey)}:`, "", ...stats.map((r) => `${r.name}: ${r.count}`)].join(
|
||||
"\n",
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="card flex flex-wrap items-center justify-between gap-3 p-3">
|
||||
<input
|
||||
type="month"
|
||||
value={monthKey}
|
||||
onChange={(e) => router.push(`${pathname}?month=${e.target.value}`)}
|
||||
className="rounded-md border border-border bg-surface px-3 py-1.5 text-sm outline-none focus:border-accent"
|
||||
/>
|
||||
<span className="text-sm text-text-muted">Всего бронирований: {total}</span>
|
||||
{stats.length > 0 && <CopyButton text={summaryText} label="Скопировать статистику" size="lg" />}
|
||||
</div>
|
||||
|
||||
<div className="card p-4">
|
||||
{stats.length === 0 ? (
|
||||
<p className="text-sm text-text-muted">За этот месяц бронирований нет.</p>
|
||||
) : (
|
||||
<div className="flex flex-col gap-3">
|
||||
{stats.map((r) => (
|
||||
<div key={r.name} className="flex items-center gap-3">
|
||||
<span className="w-40 shrink-0 truncate text-sm">{r.name}</span>
|
||||
<div className="h-2.5 flex-1 overflow-hidden rounded-full bg-surface-hover">
|
||||
<div className="h-full rounded-full bg-accent" style={{ width: `${(r.count / max) * 100}%` }} />
|
||||
</div>
|
||||
<span className="w-8 shrink-0 text-right text-sm font-medium">{r.count}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user