Refactor UI imports and update component paths
Some checks failed
Build Frontend / build (push) Failing after 7s

Replaces imports from 'components/ui' with 'components/common' across the app and dashboard pages, and updates model and API imports to use new paths under 'lib'. Removes redundant authentication checks from several dashboard pages. Adds new dashboard components and utility files, and reorganizes hooks and services into the 'lib' directory for improved structure.
This commit is contained in:
g
2026-01-13 05:02:13 +00:00
parent a6e6cd0757
commit fe01f31538
173 changed files with 1512 additions and 867 deletions

View File

@@ -0,0 +1,413 @@
"use client"
import * as React from "react"
import { format, addDays, startOfDay, endOfDay, isSameDay, isWithinInterval, getMonth, getYear, setMonth, setYear } from "date-fns"
import { Calendar as CalendarIcon, ChevronLeft, ChevronRight, X } from "lucide-react"
import { DateRange } from "react-day-picker"
import { cn } from "@/lib/utils/styles"
import { Button } from "@/components/common/button"
import { Calendar } from "@/components/common/calendar"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/common/popover"
import { Badge } from "@/components/common/badge"
import { Input } from "@/components/common/input"
import { Label } from "@/components/common/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/common/select"
interface DatePickerProps {
date?: Date
onDateChange?: (date: Date | undefined) => void
placeholder?: string
className?: string
}
interface DateRangePickerProps {
dateRange?: DateRange
onDateRangeChange?: (range: DateRange | undefined) => void
placeholder?: string
className?: string
showPresets?: boolean
disabled?: boolean
}
interface MonthPickerProps {
selectedMonth?: Date
onMonthChange?: (date: Date | undefined) => void
placeholder?: string
className?: string
disabled?: boolean
}
// Single Date Picker
export function DatePicker({ date, onDateChange, placeholder = "Pick a date", className }: DatePickerProps) {
return (
<Popover>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-full justify-start text-left font-normal",
!date && "text-muted-foreground",
className
)}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{date ? format(date, "PPP") : placeholder}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={date}
onSelect={onDateChange}
initialFocus
/>
</PopoverContent>
</Popover>
)
}
// Month Picker Component
export function MonthPicker({ selectedMonth, onMonthChange, placeholder = "Pick a month", className, disabled = false }: MonthPickerProps) {
const [isOpen, setIsOpen] = React.useState(false)
const [selectedYear, setSelectedYear] = React.useState(selectedMonth ? getYear(selectedMonth) : new Date().getFullYear())
const [selectedMonthIndex, setSelectedMonthIndex] = React.useState(selectedMonth ? getMonth(selectedMonth) : new Date().getMonth())
const months = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
]
const years = Array.from({ length: 10 }, (_, i) => new Date().getFullYear() - 5 + i)
React.useEffect(() => {
if (selectedMonth) {
setSelectedYear(getYear(selectedMonth))
setSelectedMonthIndex(getMonth(selectedMonth))
}
}, [selectedMonth])
const handleMonthSelect = (monthIndex: number) => {
const newDate = new Date(selectedYear, monthIndex, 1)
onMonthChange?.(newDate)
setIsOpen(false)
}
const handleYearChange = (year: string) => {
const newYear = parseInt(year)
setSelectedYear(newYear)
if (selectedMonth) {
const newDate = new Date(newYear, selectedMonthIndex, 1)
onMonthChange?.(newDate)
}
}
const formatSelectedMonth = (date?: Date) => {
if (!date) return placeholder
return format(date, "MMMM yyyy")
}
return (
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-full justify-start text-left font-normal",
!selectedMonth && "text-muted-foreground",
className
)}
disabled={disabled}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{formatSelectedMonth(selectedMonth)}
{selectedMonth && (
<div
className="ml-auto h-6 w-6 p-0 flex items-center justify-center rounded-sm hover:bg-accent cursor-pointer"
onClick={(e) => {
e.stopPropagation()
onMonthChange?.(undefined)
}}
>
<X className="h-3 w-3" />
</div>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-4" align="start">
<div className="space-y-4">
<div className="flex items-center justify-between">
<h4 className="font-medium">Select Month</h4>
<Select value={selectedYear.toString()} onValueChange={handleYearChange}>
<SelectTrigger className="w-24">
<SelectValue />
</SelectTrigger>
<SelectContent>
{years.map((year) => (
<SelectItem key={year} value={year.toString()}>
{year}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid grid-cols-3 gap-2">
{months.map((month, index) => (
<Button
key={month}
variant={selectedMonthIndex === index ? "default" : "outline"}
size="sm"
onClick={() => handleMonthSelect(index)}
className="text-xs"
>
{month}
</Button>
))}
</div>
</div>
</PopoverContent>
</Popover>
)
}
// Date Range Picker with Presets
export function DateRangePicker({
dateRange,
onDateRangeChange,
placeholder = "Pick a date range",
className,
showPresets = true,
disabled = false
}: DateRangePickerProps) {
const [isOpen, setIsOpen] = React.useState(false)
const presets = [
{
label: "Today",
value: {
from: startOfDay(new Date()),
to: endOfDay(new Date())
}
},
{
label: "Yesterday",
value: {
from: startOfDay(addDays(new Date(), -1)),
to: endOfDay(addDays(new Date(), -1))
}
},
{
label: "Last 7 days",
value: {
from: startOfDay(addDays(new Date(), -6)),
to: endOfDay(new Date())
}
},
{
label: "Last 30 days",
value: {
from: startOfDay(addDays(new Date(), -29)),
to: endOfDay(new Date())
}
},
{
label: "This month",
value: {
from: startOfDay(new Date(new Date().getFullYear(), new Date().getMonth(), 1)),
to: endOfDay(new Date())
}
},
{
label: "Last month",
value: {
from: startOfDay(new Date(new Date().getFullYear(), new Date().getMonth() - 1, 1)),
to: endOfDay(new Date(new Date().getFullYear(), new Date().getMonth(), 0))
}
}
]
const handlePresetClick = (preset: typeof presets[0]) => {
onDateRangeChange?.(preset.value)
setIsOpen(false)
}
const handleClear = () => {
onDateRangeChange?.(undefined)
setIsOpen(false)
}
const formatDateRange = (range: DateRange | undefined) => {
if (!range?.from) return placeholder
if (!range.to) {
return format(range.from, "PPP")
}
if (isSameDay(range.from, range.to)) {
return format(range.from, "PPP")
}
return `${format(range.from, "MMM dd")} - ${format(range.to, "MMM dd, yyyy")}`
}
return (
<Popover open={isOpen} onOpenChange={setIsOpen}>
<PopoverTrigger asChild>
<Button
variant={"outline"}
className={cn(
"w-full justify-start text-left font-normal",
!dateRange?.from && "text-muted-foreground",
className
)}
disabled={disabled}
>
<CalendarIcon className="mr-2 h-4 w-4" />
{formatDateRange(dateRange)}
{dateRange?.from && (
<div
className="ml-auto h-6 w-6 p-0 flex items-center justify-center rounded-sm hover:bg-accent cursor-pointer"
onClick={(e) => {
e.stopPropagation()
handleClear()
}}
>
<X className="h-3 w-3" />
</div>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<div className="p-3">
{showPresets && (
<div className="space-y-2 mb-4">
<Label className="text-sm font-medium">Quick Select</Label>
<div className="grid grid-cols-2 gap-2">
{presets.map((preset) => (
<Button
key={preset.label}
variant="outline"
size="sm"
className="text-xs h-8"
onClick={() => handlePresetClick(preset)}
>
{preset.label}
</Button>
))}
</div>
</div>
)}
<Calendar
initialFocus
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange}
onSelect={onDateRangeChange}
numberOfMonths={2}
className="rounded-md border"
/>
</div>
</PopoverContent>
</Popover>
)
}
// Custom Date Range Input Component
export function CustomDateRangeInput({
dateRange,
onDateRangeChange,
className
}: DateRangePickerProps) {
const [fromDate, setFromDate] = React.useState<string>("")
const [toDate, setToDate] = React.useState<string>("")
React.useEffect(() => {
if (dateRange?.from) {
setFromDate(format(dateRange.from, "yyyy-MM-dd"))
}
if (dateRange?.to) {
setToDate(format(dateRange.to, "yyyy-MM-dd"))
}
}, [dateRange])
const handleFromDateChange = (value: string) => {
setFromDate(value)
const from = value ? new Date(value) : undefined
const to = toDate ? new Date(toDate) : dateRange?.to
if (from && to && from > to) {
// If from date is after to date, adjust to date
onDateRangeChange?.({ from, to: from })
setToDate(value)
} else {
onDateRangeChange?.({ from, to })
}
}
const handleToDateChange = (value: string) => {
setToDate(value)
const from = fromDate ? new Date(fromDate) : dateRange?.from
const to = value ? new Date(value) : undefined
if (from && to && from > to) {
// If to date is before from date, adjust from date
onDateRangeChange?.({ from: to, to })
setFromDate(value)
} else {
onDateRangeChange?.({ from, to })
}
}
return (
<div className={cn("flex items-center gap-2", className)}>
<div className="flex items-center gap-2">
<Label htmlFor="from-date" className="text-sm whitespace-nowrap">From:</Label>
<Input
id="from-date"
type="date"
value={fromDate}
onChange={(e) => handleFromDateChange(e.target.value)}
className="w-32"
/>
</div>
<div className="flex items-center gap-2">
<Label htmlFor="to-date" className="text-sm whitespace-nowrap">To:</Label>
<Input
id="to-date"
type="date"
value={toDate}
onChange={(e) => handleToDateChange(e.target.value)}
className="w-32"
/>
</div>
</div>
)
}
// Date Range Display Component
export function DateRangeDisplay({ dateRange }: { dateRange?: DateRange }) {
if (!dateRange?.from) return null
const daysDiff = dateRange.to
? Math.ceil((dateRange.to.getTime() - dateRange.from.getTime()) / (1000 * 60 * 60 * 24)) + 1
: 1
return (
<div className="flex items-center gap-2">
<Badge variant="secondary" className="text-xs">
{daysDiff} day{daysDiff !== 1 ? 's' : ''}
</Badge>
<span className="text-sm text-muted-foreground">
{format(dateRange.from, "MMM dd")}
{dateRange.to && !isSameDay(dateRange.from, dateRange.to) && (
<> - {format(dateRange.to, "MMM dd, yyyy")}</>
)}
</span>
</div>
)
}