feat: improve loaders and styles

This commit is contained in:
Vasily Zubarev
2025-03-24 19:52:09 +01:00
parent df2d646a47
commit a80684c3fb
8 changed files with 143 additions and 12 deletions

View File

@@ -33,8 +33,8 @@
--sidebar-foreground: 200 10% 30%; --sidebar-foreground: 200 10% 30%;
--sidebar-primary: 200 13% 80%; --sidebar-primary: 200 13% 80%;
--sidebar-primary-foreground: 0 0% 98%; --sidebar-primary-foreground: 0 0% 98%;
--sidebar-accent: 200 13% 90%; --sidebar-accent: 240 5.9% 10%;
--sidebar-accent-foreground: 200 10% 30%; --sidebar-accent-foreground: 0 0% 98%;
--sidebar-border: 200 13% 85%; --sidebar-border: 200 13% 85%;
--sidebar-ring: 200 13% 80%; --sidebar-ring: 200 13% 80%;
} }
@@ -68,8 +68,8 @@
--sidebar-foreground: 200 10% 90%; --sidebar-foreground: 200 10% 90%;
--sidebar-primary: 200 13% 60%; --sidebar-primary: 200 13% 60%;
--sidebar-primary-foreground: 0 0% 100%; --sidebar-primary-foreground: 0 0% 100%;
--sidebar-accent: 200 13% 25%; --sidebar-accent: 240 5.9% 10%;
--sidebar-accent-foreground: 200 10% 90%; --sidebar-accent-foreground: 0 0% 98%;
--sidebar-border: 200 13% 25%; --sidebar-border: 200 13% 25%;
--sidebar-ring: 200 13% 60%; --sidebar-ring: 200 13% 60%;
} }

10
app/settings/loading.tsx Normal file
View File

@@ -0,0 +1,10 @@
import { Skeleton } from "@/components/ui/skeleton"
export default function Loading() {
return (
<div className="flex flex-col gap-4 w-full">
<Skeleton className="h-10 w-56" />
<Skeleton className="w-full h-[350px]" />
</div>
)
}

View File

@@ -2,6 +2,9 @@ import { Skeleton } from "@/components/ui/skeleton"
export default function Loading() { export default function Loading() {
return ( return (
<Skeleton className="flex flex-row flex-wrap md:flex-nowrap justify-center items-start gap-10 p-5 bg-accent max-w-[1200px] min-h-[800px]" /> <div className="flex flex-wrap flex-row items-start justify-center gap-4 max-w-6xl">
<Skeleton className="w-full h-[800px]" />
<Skeleton className="w-1/3 max-w-[380px]" />
</div>
) )
} }

View File

@@ -6,7 +6,10 @@ export default function Loading() {
return ( return (
<> <>
<header className="flex items-center justify-between mb-12"> <header className="flex items-center justify-between mb-12">
<h2 className="text-3xl font-bold tracking-tight">Transactions</h2> <h2 className="flex flex-row gap-3 md:gap-5">
<span className="text-3xl font-bold tracking-tight">Transactions</span>
<Skeleton className="h-10 w-16" />
</h2>
<div className="flex gap-2"> <div className="flex gap-2">
<Button variant="outline"> <Button variant="outline">
<Download /> <Download />
@@ -18,9 +21,17 @@ export default function Loading() {
</div> </div>
</header> </header>
<div className="flex flex-row gap-2 w-full">
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
<Skeleton className="h-8 w-full" />
</div>
<main> <main>
<div className="flex flex-col gap-3 w-full"> <div className="flex flex-col gap-3 w-full">
{[...Array(20)].map((_, i) => ( {[...Array(15)].map((_, i) => (
<Skeleton key={i} className="h-8" /> <Skeleton key={i} className="h-8" />
))} ))}
</div> </div>

31
app/unsorted/loading.tsx Normal file
View File

@@ -0,0 +1,31 @@
import { Skeleton } from "@/components/ui/skeleton"
import { Loader2 } from "lucide-react"
export default function Loading() {
return (
<div className="flex flex-col gap-4">
<header className="flex items-center justify-between">
<h2 className="text-3xl font-bold tracking-tight flex flex-row gap-2">
<Loader2 className="h-10 w-10 animate-spin" /> <span>Loading unsorted files...</span>
</h2>
</header>
<Skeleton className="w-full h-[800px] flex flex-row flex-wrap md:flex-nowrap justify-center items-start gap-5 p-6">
<Skeleton className="w-full h-full" />
<div className="w-full flex flex-col gap-5">
<Skeleton className="w-full h-12 mb-7" />
{[...Array(4)].map((_, i) => (
<div key={i} className="flex flex-col gap-2">
<Skeleton className="w-[120px] h-4" />
<Skeleton className="w-full h-9" />
</div>
))}
<div className="flex flex-row justify-end gap-2 mt-2">
<Skeleton className="w-[80px] h-9" />
<Skeleton className="w-[130px] h-9" />
</div>
</div>
</Skeleton>
</div>
)
}

View File

@@ -1,9 +1,16 @@
"use client"
import { Button } from "@/components/ui/button"
import { Calendar } from "@/components/ui/calendar"
import { Input } from "@/components/ui/input" import { Input } from "@/components/ui/input"
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Textarea } from "@/components/ui/textarea" import { Textarea } from "@/components/ui/textarea"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import { SelectProps } from "@radix-ui/react-select" import { SelectProps } from "@radix-ui/react-select"
import { InputHTMLAttributes, TextareaHTMLAttributes } from "react" import { format } from "date-fns"
import { CalendarIcon } from "lucide-react"
import { InputHTMLAttributes, TextareaHTMLAttributes, useState } from "react"
type FormInputProps = InputHTMLAttributes<HTMLInputElement> & { type FormInputProps = InputHTMLAttributes<HTMLInputElement> & {
title: string title: string
@@ -75,3 +82,68 @@ export const FormSelect = ({
</span> </span>
) )
} }
export const FormDate = ({
title,
name,
placeholder = "Select date",
defaultValue,
...props
}: {
title: string
name: string
placeholder?: string
defaultValue?: Date
}) => {
const [date, setDate] = useState<Date | undefined>(defaultValue)
const [manualInput, setManualInput] = useState<string>(date ? format(date, "yyyy-MM-dd") : "")
const handleDateSelect = (newDate: Date | undefined) => {
setDate(newDate)
setManualInput(newDate ? format(newDate, "yyyy-MM-dd") : "")
}
const handleManualInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setManualInput(e.target.value)
setDate(undefined)
try {
const newDate = new Date(e.currentTarget.value)
if (!isNaN(newDate.getTime())) {
setDate(newDate)
}
} catch (error) {}
}
return (
<label className="flex flex-col gap-1">
<span className="text-sm font-medium">{title}</span>
<div className="relative">
<Popover>
<PopoverTrigger asChild>
<Button
type="button"
variant={"outline"}
className={cn(
"w-full justify-start text-left font-normal bg-background",
!date && "text-muted-foreground"
)}
>
{date ? format(date, "PPP") : placeholder}
<CalendarIcon className="ml-1 h-4 w-4 text-muted-foreground" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-1 flex flex-col gap-2" align="start">
<Input
type="text"
name={name}
value={manualInput}
onChange={handleManualInputChange}
className="text-center"
/>
<Calendar mode="single" selected={date} onSelect={handleDateSelect} initialFocus {...props} />
</PopoverContent>
</Popover>
</div>
</label>
)
}

View File

@@ -21,7 +21,11 @@ export function SidebarMenuItemWithHighlight({
return ( return (
<SidebarMenuItem <SidebarMenuItem
className={cn(isActive && "bg-sidebar-accent font-medium text-sidebar-accent-foreground", className)} className={cn(
isActive && "bg-sidebar-accent text-sidebar-accent-foreground",
"font-medium rounded-md",
className
)}
{...props} {...props}
> >
{children} {children}

View File

@@ -25,13 +25,13 @@ export const standardFieldRenderers: Record<string, FieldRenderer> = {
name: { name: {
name: "Name", name: "Name",
code: "name", code: "name",
classes: "font-medium max-w-[300px] min-w-[120px] overflow-hidden", classes: "font-medium min-w-[120px] max-w-[300px] overflow-hidden",
sortable: true, sortable: true,
}, },
merchant: { merchant: {
name: "Merchant", name: "Merchant",
code: "merchant", code: "merchant",
classes: "max-w-[200px] max-h-[20px] min-w-[120px] overflow-hidden", classes: "min-w-[120px] max-w-[250px] overflow-hidden",
sortable: true, sortable: true,
}, },
issuedAt: { issuedAt: {
@@ -221,7 +221,7 @@ export function TransactionList({ transactions, fields = [] }: { transactions: T
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className="w-[50px] select-none"> <TableHead className="min-w-[30px] select-none">
<Checkbox checked={selectedIds.length === transactions.length} onCheckedChange={toggleAllRows} /> <Checkbox checked={selectedIds.length === transactions.length} onCheckedChange={toggleAllRows} />
</TableHead> </TableHead>
{visibleFields.map((field) => ( {visibleFields.map((field) => (