whisper-money/resources/js/pages/budgets/show.tsx

185 lines
7.0 KiB
TypeScript

import { index, show } from '@/actions/App/Http/Controllers/BudgetController';
import { BudgetSpendingChart } from '@/components/budgets/budget-spending-chart';
import { DeleteBudgetDialog } from '@/components/budgets/delete-budget-dialog';
import { EditBudgetDialog } from '@/components/budgets/edit-budget-dialog';
import HeadingSmall from '@/components/heading-small';
import { TransactionList } from '@/components/transactions/transaction-list';
import { Button } from '@/components/ui/button';
import { ButtonGroup } from '@/components/ui/button-group';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import AppSidebarLayout from '@/layouts/app/app-sidebar-layout';
import { BreadcrumbItem } from '@/types';
import { Account, Bank } from '@/types/account';
import { Budget, BudgetPeriod, getBudgetPeriodTypeLabel } from '@/types/budget';
import { Category } from '@/types/category';
import { Head } from '@inertiajs/react';
import { ChevronDown } from 'lucide-react';
import { useMemo, useState } from 'react';
interface Props {
budget: Budget;
currentPeriod: BudgetPeriod;
categories: Category[];
accounts: Account[];
banks: Bank[];
currencyCode: string;
}
export default function BudgetShow({
budget,
currentPeriod,
categories,
accounts,
banks,
currencyCode,
}: Props) {
const [editOpen, setEditOpen] = useState(false);
const [deleteOpen, setDeleteOpen] = useState(false);
const breadcrumbs: BreadcrumbItem[] = [
{
title: 'Budgets',
href: index().url,
},
{
title: budget.name,
href: show({ budget: budget.id }).url,
},
];
const periodLabel = useMemo(() => {
const start = new Date(currentPeriod.start_date).toLocaleDateString(
'en-US',
{ month: 'short', day: 'numeric', year: '2-digit' },
);
const end = new Date(currentPeriod.end_date).toLocaleDateString(
'en-US',
{ month: 'short', day: 'numeric', year: '2-digit' },
);
return `${start} - ${end}`;
}, [currentPeriod]);
const trackingLabel = useMemo((): string | null => {
if (budget.category) return budget.category.name;
if (budget.label) return budget.label.name;
return null;
}, [budget]);
const periodTransactions = useMemo(() => {
return (
currentPeriod.budget_transactions
?.map((bt) => bt.transaction)
.filter((t) => t !== undefined && t !== null) || []
);
}, [currentPeriod]);
return (
<AppSidebarLayout breadcrumbs={breadcrumbs}>
<Head title={budget.name} />
<div className="space-y-6 p-6">
<div className="flex flex-col items-start justify-between gap-4 sm:flex-row sm:items-center">
<div className="flex items-center gap-4">
<HeadingSmall
title={budget.name}
description={
<div className="flex flex-row items-center gap-1 text-sm">
<div className="inline">
{trackingLabel !== null ? (
<>
<span className="opacity-50">
Tracking{' '}
</span>
<span>{trackingLabel}</span>
</>
) : (
<span className="opacity-50">
No tracking
</span>
)}
</div>
<span className="opacity-25">/</span>
<div className="inline">
<span>{periodLabel} </span>
<span className="opacity-50">
(
{getBudgetPeriodTypeLabel(
budget.period_type,
)}
)
</span>
</div>
</div>
}
/>
</div>
<ButtonGroup>
<Button
variant="outline"
onClick={() => setEditOpen(true)}
>
Edit budget
</Button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="icon"
aria-label="More options"
>
<ChevronDown className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem
onClick={() => setDeleteOpen(true)}
variant="destructive"
>
Delete
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</ButtonGroup>
</div>
<BudgetSpendingChart
currentPeriod={currentPeriod}
budgetName={budget.name}
currencyCode={currencyCode}
/>
<TransactionList
categories={categories}
accounts={accounts}
banks={banks}
transactions={periodTransactions}
pageSize={10}
showActionsMenu={false}
maxHeight={600}
/>
</div>
<EditBudgetDialog
budget={budget}
currentPeriod={currentPeriod}
currencyCode={currencyCode}
open={editOpen}
onOpenChange={setEditOpen}
/>
<DeleteBudgetDialog
budget={budget}
open={deleteOpen}
onOpenChange={setDeleteOpen}
redirectTo={index().url}
/>
</AppSidebarLayout>
);
}