feat: update TodayAttendanceAlert component and enhance card styling in attendance and dashboard pages
This commit is contained in:
parent
166419134f
commit
e4d956843c
@ -1,6 +1,6 @@
|
||||
import { CalendarCheck, CheckCircle2, Clock, LogIn, LogOut } from 'lucide-react';
|
||||
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { CalendarCheck, CheckCircle2, Clock, LogIn, LogOut } from 'lucide-react';
|
||||
|
||||
type TodayAttendance = {
|
||||
id: number;
|
||||
@ -100,12 +100,7 @@ export function TodayAttendanceAlert({
|
||||
</div>
|
||||
|
||||
{showButtons && canCheckIn && !isOnLeave && (
|
||||
<div className="flex items-center gap-2">
|
||||
{locationLoading && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Mendapatkan lokasi...
|
||||
</span>
|
||||
)}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
onClick={onCheckIn}
|
||||
|
||||
@ -20,7 +20,7 @@ export function AppShell({ children, variant = 'sidebar' }: Props) {
|
||||
return (
|
||||
<SidebarProvider
|
||||
defaultOpen={isOpen}
|
||||
className="h-screen overflow-hidden"
|
||||
className="h-screen"
|
||||
>
|
||||
{children}
|
||||
</SidebarProvider>
|
||||
|
||||
@ -2,17 +2,12 @@ import * as React from "react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Card({
|
||||
className,
|
||||
size = "default",
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
|
||||
function Card({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card"
|
||||
data-size={size}
|
||||
className={cn(
|
||||
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-lg bg-card py-(--card-spacing) text-xs/relaxed text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] *:[img:first-child]:rounded-t-lg *:[img:last-child]:rounded-b-lg",
|
||||
"flex flex-col gap-6 rounded-xl border bg-card py-6 text-card-foreground shadow-sm",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@ -25,7 +20,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
||||
<div
|
||||
data-slot="card-header"
|
||||
className={cn(
|
||||
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-lg px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
|
||||
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
@ -37,7 +32,7 @@ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-title"
|
||||
className={cn("font-heading text-sm font-medium", className)}
|
||||
className={cn("leading-none font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
@ -47,7 +42,7 @@ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-description"
|
||||
className={cn("text-xs/relaxed text-muted-foreground", className)}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
@ -70,7 +65,7 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-content"
|
||||
className={cn("px-(--card-spacing)", className)}
|
||||
className={cn("px-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
@ -80,10 +75,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
||||
return (
|
||||
<div
|
||||
data-slot="card-footer"
|
||||
className={cn(
|
||||
"flex items-center rounded-b-lg px-(--card-spacing) [.border-t]:pt-(--card-spacing)",
|
||||
className
|
||||
)}
|
||||
className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
|
||||
@ -1,3 +1,19 @@
|
||||
import { TodayAttendanceAlert } from '@/components/card/today-attendance-alert';
|
||||
import { CameraCapture, LocationMap } from '@/components/inputs';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
index as attendanceIndex,
|
||||
store,
|
||||
update,
|
||||
} from '@/routes/admin/hr/attendances';
|
||||
import { Head, router } from '@inertiajs/react';
|
||||
import { addMonths, format, subMonths } from 'date-fns';
|
||||
import { id } from 'date-fns/locale';
|
||||
@ -14,23 +30,6 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { CameraCapture } from '@/components/inputs';
|
||||
import { LocationMap } from '@/components/inputs';
|
||||
import { TodayAttendanceAlert } from '@/components/card/today-attendance-alert';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog';
|
||||
import {
|
||||
index as attendanceIndex,
|
||||
store,
|
||||
update,
|
||||
} from '@/routes/admin/hr/attendances';
|
||||
|
||||
type Attendance = {
|
||||
id: number;
|
||||
@ -334,7 +333,7 @@ export default function AttendanceIndex({
|
||||
<>
|
||||
<Head title="Presensi" />
|
||||
|
||||
<div className="flex min-h-full flex-1 flex-col gap-6 overflow-auto p-4 md:p-6">
|
||||
<div className="flex h-full flex-1 flex-col gap-6 overflow-x-auto p-4 md:p-6">
|
||||
<div>
|
||||
<h2 className="text-2xl font-semibold tracking-tight">
|
||||
Presensi
|
||||
@ -415,10 +414,7 @@ export default function AttendanceIndex({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Card
|
||||
className="overflow-hidden"
|
||||
style={{ '--card-spacing': '0px' } as React.CSSProperties}
|
||||
>
|
||||
<Card className="py-0!">
|
||||
<div className="flex items-center justify-between border-b px-6 py-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="flex flex-col items-center overflow-hidden rounded-lg ring-1 ring-border">
|
||||
@ -471,19 +467,20 @@ export default function AttendanceIndex({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7 border-b">
|
||||
{WEEKDAYS.map((day) => (
|
||||
<div
|
||||
key={day}
|
||||
className="flex items-center justify-center py-3 text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="overflow-x-auto">
|
||||
<div className="grid min-w-[700px] grid-cols-7 border-b">
|
||||
{WEEKDAYS.map((day) => (
|
||||
<div
|
||||
key={day}
|
||||
className="flex items-center justify-center py-3 text-xs font-medium text-muted-foreground"
|
||||
>
|
||||
{day}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-7">
|
||||
{calendarDays.map((cell, idx) => {
|
||||
<div className="grid min-w-[700px] grid-cols-7">
|
||||
{calendarDays.map((cell, idx) => {
|
||||
const dateStr = format(cell.date, 'yyyy-MM-dd');
|
||||
const dayAttendances = attendanceByDate.get(dateStr) ?? [];
|
||||
const isSelected = isSameDay(
|
||||
@ -517,13 +514,11 @@ export default function AttendanceIndex({
|
||||
setDetailAttendance(dayAttendances[0]);
|
||||
}
|
||||
}}
|
||||
className={`group relative flex min-h-[100px] flex-col p-2 text-left transition-colors hover:bg-muted/50 ${
|
||||
!cell.isCurrentMonth
|
||||
? 'bg-muted/30 text-muted-foreground/50'
|
||||
: ''
|
||||
} ${isSelected ? 'bg-muted/50' : ''} ${
|
||||
!isAdmin && dayAttendances.length > 0 ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
className={`group relative flex min-h-[100px] flex-col p-2 text-left transition-colors hover:bg-muted/50 ${!cell.isCurrentMonth
|
||||
? 'bg-muted/30 text-muted-foreground/50'
|
||||
: ''
|
||||
} ${isSelected ? 'bg-muted/50' : ''} ${!isAdmin && dayAttendances.length > 0 ? 'cursor-pointer' : ''
|
||||
}`}
|
||||
style={{
|
||||
borderRight: '1px solid var(--border)',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
@ -531,13 +526,12 @@ export default function AttendanceIndex({
|
||||
>
|
||||
<div className="flex items-start justify-between">
|
||||
<span
|
||||
className={`flex h-7 w-7 items-center justify-center rounded-full text-xs font-semibold transition-colors ${
|
||||
isSelected
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: isTodayDate
|
||||
? 'bg-muted text-foreground'
|
||||
: 'text-foreground'
|
||||
}`}
|
||||
className={`flex h-7 w-7 items-center justify-center rounded-full text-xs font-semibold transition-colors ${isSelected
|
||||
? 'bg-primary text-primary-foreground'
|
||||
: isTodayDate
|
||||
? 'bg-muted text-foreground'
|
||||
: 'text-foreground'
|
||||
}`}
|
||||
>
|
||||
{cell.day}
|
||||
</span>
|
||||
@ -635,7 +629,8 @@ export default function AttendanceIndex({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
@ -1,19 +1,6 @@
|
||||
import { Head, router, usePage } from '@inertiajs/react';
|
||||
import {
|
||||
Clock,
|
||||
Moon,
|
||||
Sunrise,
|
||||
Sun,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
UserCheck,
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { Pie, PieChart, Cell, ResponsiveContainer, Tooltip } from 'recharts';
|
||||
import { CameraCapture } from '@/components/inputs';
|
||||
import { TodayAttendanceAlert } from '@/components/card/today-attendance-alert';
|
||||
import { StatCard } from '@/components/card/stat-card';
|
||||
import { TodayAttendanceAlert } from '@/components/card/today-attendance-alert';
|
||||
import { CameraCapture } from '@/components/inputs';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import {
|
||||
Dialog,
|
||||
@ -22,6 +9,19 @@ import {
|
||||
import { useCan } from '@/hooks/use-can';
|
||||
import { formatRupiah } from '@/lib/rupiah';
|
||||
import { store, update } from '@/routes/admin/hr/attendances';
|
||||
import { Head, router, usePage } from '@inertiajs/react';
|
||||
import {
|
||||
Clock,
|
||||
Moon,
|
||||
Sun,
|
||||
Sunrise,
|
||||
TrendingDown,
|
||||
TrendingUp,
|
||||
UserCheck,
|
||||
} from 'lucide-react';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
type TodayAttendance = {
|
||||
id: number;
|
||||
@ -297,8 +297,8 @@ export default function Dashboard({
|
||||
<>
|
||||
<Head title="Dasbor" />
|
||||
|
||||
<div className="flex h-full flex-1 flex-col gap-6 overflow-x-auto p-4 md:p-6">
|
||||
<Card className="relative overflow-hidden">
|
||||
<div className="flex h-full flex-1 flex-col gap-6 p-4 md:p-6">
|
||||
<Card className="relative overflow-visible">
|
||||
<div className="absolute inset-0 bg-linear-to-br from-primary/5 to-background" />
|
||||
<CardHeader className="relative">
|
||||
<div className="flex items-center gap-3">
|
||||
@ -306,7 +306,7 @@ export default function Dashboard({
|
||||
<GreetingIcon className="size-6 text-primary" />
|
||||
</div>
|
||||
<div>
|
||||
<CardTitle className="text-2xl font-bold">
|
||||
<CardTitle className="text-2xl font-bold">
|
||||
{greeting.text}, {auth.user?.username}!
|
||||
</CardTitle>
|
||||
<CardDescription className="mt-1">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user