feat: add administration page and account role management

- Implemented `AdministrationContent` component for managing account roles.
- Created `AdministrationPage` to serve as the main entry point for administration tasks.
- Added PDF preview functionality with `PdfPreviewFrame` component for invoice generation.
- Introduced `InputColor` component for advanced color selection with various formats.
- Established color conversion utilities in `color-converter.ts` for handling color formats.
- Defined appearance-related schemas and types in `appearance.ts` for consistent theme management.
This commit is contained in:
2026-04-30 10:50:50 -04:00
parent ddc2b42672
commit 0e46fdafb2
87 changed files with 4566 additions and 2425 deletions
@@ -53,14 +53,13 @@ const columns: ColumnDef<InvoiceItem>[] = [
return (
<>
{/* Desktop: plain description */}
<div className="hidden font-medium sm:block">
{item.description}
</div>
<div className="hidden font-medium sm:block">{item.description}</div>
{/* Mobile: description + date + hours @ rate stacked */}
<div className="sm:hidden">
<p className="font-medium">{item.description}</p>
<p className="text-muted-foreground mt-0.5 text-xs">
{formatDate(item.date)} &middot; {item.hours}h @ {formatCurrency(item.rate)}/hr
{formatDate(item.date)} &middot; {item.hours}h @{" "}
{formatCurrency(item.rate)}/hr
</p>
</div>
</>
+5 -7
View File
@@ -75,7 +75,7 @@ function InvoiceViewContent({ invoiceId }: { invoiceId: string }) {
const handleMarkAsPaid = () => {
updateStatus.mutate({
id: invoiceId,
status: "paid" as StoredInvoiceStatus,
status: "paid",
});
};
@@ -109,17 +109,15 @@ function InvoiceViewContent({ invoiceId }: { invoiceId: string }) {
const subtotal = invoice.items.reduce((sum, item) => sum + item.amount, 0);
const taxAmount = (subtotal * invoice.taxRate) / 100;
const total = subtotal + taxAmount;
const storedStatus = invoice.status as StoredInvoiceStatus;
const effectiveStatus = getEffectiveInvoiceStatus(
invoice.status as StoredInvoiceStatus,
invoice.dueDate,
);
const isOverdue = isInvoiceOverdue(
invoice.status as StoredInvoiceStatus,
storedStatus,
invoice.dueDate,
);
const isOverdue = isInvoiceOverdue(storedStatus, invoice.dueDate);
const getStatusType = (): StatusType => {
return effectiveStatus as StatusType;
return effectiveStatus;
};
return (
@@ -86,7 +86,7 @@ const getStatusType = (invoice: Invoice): StatusType =>
getEffectiveInvoiceStatus(
invoice.status as StoredInvoiceStatus,
invoice.dueDate,
) as StatusType;
);
const formatDate = (date: Date) =>
new Intl.DateTimeFormat("en-US", {
+7 -7
View File
@@ -29,7 +29,7 @@ function FormatInstructions() {
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="bg-muted/50 p-4">
<div className="bg-muted/50 p-4">
<p className="text-muted-foreground font-mono text-sm">
DATE,DESCRIPTION,HOURS,RATE,AMOUNT
</p>
@@ -85,7 +85,7 @@ function FormatInstructions() {
for importing time entries.
</p>
<div className="bg-primary/10 p-4">
<div className="bg-primary/10 p-4">
<div className="flex items-start gap-3">
<Info className="text-primary mt-0.5 h-5 w-5" />
<div>
@@ -100,7 +100,7 @@ function FormatInstructions() {
<div className="space-y-2">
<h4 className="text-sm font-semibold">Sample Row:</h4>
<div className="bg-muted/50 p-3">
<div className="bg-muted/50 p-3">
<p className="text-muted font-mono text-xs break-all">
1/15/24,&quot;Web development work&quot;,8,75.00,600.00
</p>
@@ -109,7 +109,7 @@ function FormatInstructions() {
<div className="space-y-2">
<h4 className="text-sm font-semibold">Sample Filename:</h4>
<div className="bg-muted/50 p-3">
<div className="bg-muted/50 p-3">
<p className="text-muted font-mono text-xs">2024-01-15.csv</p>
</div>
</div>
@@ -168,7 +168,7 @@ function FileFormatHelp() {
<CardContent className="space-y-4">
<div className="grid gap-6 md:grid-cols-3">
<div className="space-y-2 text-center">
<div className="bg-accent mx-auto w-fit p-3">
<div className="bg-accent mx-auto w-fit p-3">
<FileSpreadsheet className="text-foreground-foreground h-6 w-6" />
</div>
<h4 className="font-semibold">CSV Files</h4>
@@ -178,7 +178,7 @@ function FileFormatHelp() {
</p>
</div>
<div className="space-y-2 text-center">
<div className="bg-primary/10 mx-auto w-fit p-3">
<div className="bg-primary/10 mx-auto w-fit p-3">
<Upload className="text-primary h-6 w-6" />
</div>
<h4 className="font-semibold">Max Size</h4>
@@ -187,7 +187,7 @@ function FileFormatHelp() {
</p>
</div>
<div className="space-y-2 text-center">
<div className="bg-secondary mx-auto w-fit p-3">
<div className="bg-secondary mx-auto w-fit p-3">
<CheckCircle className="text-muted-foreground-foreground h-6 w-6" />
</div>
<h4 className="font-semibold">Validation</h4>