style: refine shared UI primitives and management flows

This commit is contained in:
2026-07-09 23:06:25 +05:30
parent 0e8108b875
commit 8727963c54
70 changed files with 631 additions and 254 deletions

View File

@@ -33,7 +33,7 @@ function ResetPasswordContent() {
</header> </header>
{!hasToken ? ( {!hasToken ? (
<div className="rounded-md border border-destructive/30 bg-destructive/5 p-4 text-destructive"> <div className="rounded-lg border border-destructive/30 bg-destructive/5 p-4 text-destructive">
Password reset token is missing. Password reset token is missing.
</div> </div>
) : ( ) : (

View File

@@ -9,7 +9,7 @@ export function ModuleShellHeader() {
const { isMobile } = useSidebar(); const { isMobile } = useSidebar();
return ( return (
<div className="sticky top-0 z-50 flex items-center justify-between gap-4 border-b border-border bg-background/95 px-6 py-3 backdrop-blur supports-[backdrop-filter]:bg-background/80"> <div className="sticky top-0 z-50 flex h-14 items-center justify-between gap-4 border-b border-border bg-background/95 px-6 backdrop-blur supports-backdrop-filter:bg-background/80">
<div className="flex min-w-0 items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
{isMobile ? ( {isMobile ? (
<> <>

View File

@@ -13,6 +13,7 @@ import { FormField, PhoneInput } from '@/components/form';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
@@ -71,12 +72,12 @@ export function ClientSheet({
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="flex max-h-[calc(100vh-2rem)] flex-col gap-0 p-0 sm:max-w-3xl"> <DialogContent className="sm:max-w-3xl">
<DialogHeader className="shrink-0 border-b px-6 py-4 pr-12"> <DialogHeader>
<DialogTitle className="text-lg leading-none font-semibold tracking-tight"> <DialogTitle>
{clientId ? 'Edit Client' : 'Create Client'} {clientId ? 'Edit Client' : 'Create Client'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-sm"> <DialogDescription>
Manage company and primary contact details. Manage company and primary contact details.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -86,7 +87,7 @@ export function ClientSheet({
onSubmit={onSubmit} onSubmit={onSubmit}
className="flex min-h-0 flex-1 flex-col" className="flex min-h-0 flex-1 flex-col"
> >
<div className="max-h-[58vh] space-y-5 overflow-y-auto px-6 py-4"> <DialogBody className="space-y-5">
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
<FormField <FormField
id="client-first-name" id="client-first-name"
@@ -240,9 +241,9 @@ export function ClientSheet({
/> />
</FormField> </FormField>
</div> </div>
</div> </DialogBody>
<DialogFooter className="shrink-0 border-t px-6 py-4"> <DialogFooter>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"

View File

@@ -10,6 +10,7 @@ import { ProjectSelect } from '@/components/lookups/ProjectSelect';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
@@ -62,14 +63,14 @@ export function PackageDialog({
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent <DialogContent
className="flex max-h-[calc(100vh-2rem)] flex-col gap-0 p-0 sm:max-w-2xl" className="sm:max-w-2xl"
onOpenAutoFocus={(event) => event.preventDefault()} onOpenAutoFocus={(event) => event.preventDefault()}
> >
<DialogHeader className="shrink-0 border-b px-6 py-4 pr-12"> <DialogHeader>
<DialogTitle className="text-lg leading-none font-semibold tracking-tight"> <DialogTitle>
{packageId ? 'Edit Package' : 'Create Package'} {packageId ? 'Edit Package' : 'Create Package'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-sm"> <DialogDescription>
Manage package details and optional chainage range. Manage package details and optional chainage range.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -79,7 +80,7 @@ export function PackageDialog({
onSubmit={onSubmit} onSubmit={onSubmit}
className="flex min-h-0 flex-1 flex-col" className="flex min-h-0 flex-1 flex-col"
> >
<div className="max-h-[58vh] space-y-6 overflow-y-auto px-6 py-4"> <DialogBody className="space-y-6">
<section className="space-y-4"> <section className="space-y-4">
<div className="space-y-1"> <div className="space-y-1">
<h3 className="text-sm font-semibold">Basic details</h3> <h3 className="text-sm font-semibold">Basic details</h3>
@@ -173,9 +174,9 @@ export function PackageDialog({
</FormField> </FormField>
</div> </div>
</section> </section>
</div> </DialogBody>
<DialogFooter className="shrink-0 border-t px-6 py-4"> <DialogFooter>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"

View File

@@ -4,6 +4,16 @@ import { useCallback, useState } from 'react';
import { Package as PackageIcon, Plus } from 'lucide-react'; import { Package as PackageIcon, Plus } from 'lucide-react';
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import type { Package } from '@/types'; import type { Package } from '@/types';
@@ -20,6 +30,7 @@ import {
export default function PackagePage() { export default function PackagePage() {
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const [packageToDelete, setPackageToDelete] = useState<Package | null>(null);
const { skip, setSkip, limit, setLimit } = usePackageFilters(); const { skip, setSkip, limit, setLimit } = usePackageFilters();
const packagesQuery = usePackagesQuery({ skip, limit }); const packagesQuery = usePackagesQuery({ skip, limit });
const projectsQuery = useProjectOptionsQuery(); const projectsQuery = useProjectOptionsQuery();
@@ -67,14 +78,25 @@ export default function PackagePage() {
const deletePackage = useCallback( const deletePackage = useCallback(
(pkg: Package) => { (pkg: Package) => {
if (!confirm(`Are you sure you want to delete package "${pkg.name}"?`)) { setPackageToDelete(pkg);
return;
}
deletePackageMutation.mutate(pkg);
}, },
[deletePackageMutation], [],
); );
const handleDeleteDialogOpenChange = useCallback((open: boolean) => {
if (!open) {
setPackageToDelete(null);
}
}, []);
const confirmDeletePackage = useCallback(() => {
if (!packageToDelete) return;
deletePackageMutation.mutate(packageToDelete, {
onSettled: () => setPackageToDelete(null),
});
}, [deletePackageMutation, packageToDelete]);
const projects = projectsQuery.data?.items ?? []; const projects = projectsQuery.data?.items ?? [];
const packages = packagesQuery.data?.items ?? []; const packages = packagesQuery.data?.items ?? [];
const total = packagesQuery.data?.totalItems ?? 0; const total = packagesQuery.data?.totalItems ?? 0;
@@ -122,6 +144,36 @@ export default function PackagePage() {
canSubmit={canSubmit} canSubmit={canSubmit}
isSaving={isSaving} isSaving={isSaving}
/> />
<AlertDialog
open={Boolean(packageToDelete)}
onOpenChange={handleDeleteDialogOpenChange}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete package?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete{' '}
<span className="font-medium text-foreground">
{packageToDelete?.name}
</span>
. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={deletePackageMutation.isPending}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
disabled={deletePackageMutation.isPending}
onClick={confirmDeletePackage}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</> </>
); );
} }

View File

@@ -242,7 +242,7 @@ export function PlanSheet({
<label className="flex items-center gap-2"> <label className="flex items-center gap-2">
<input <input
type="checkbox" type="checkbox"
className="size-4 rounded border-border accent-primary" className="size-4 rounded-lg border-border accent-primary"
{...register('is_active')} {...register('is_active')}
/> />
Active Active
@@ -250,7 +250,7 @@ export function PlanSheet({
<label className="flex items-center gap-2"> <label className="flex items-center gap-2">
<input <input
type="checkbox" type="checkbox"
className="size-4 rounded border-border accent-primary" className="size-4 rounded-lg border-border accent-primary"
{...register('is_custom')} {...register('is_custom')}
/> />
Custom plan Custom plan
@@ -269,7 +269,7 @@ export function PlanSheet({
} }
> >
{isPermissionsLoading ? ( {isPermissionsLoading ? (
<div className="rounded-md border p-6 text-muted-foreground"> <div className="rounded-lg border p-6 text-muted-foreground">
Loading permissions... Loading permissions...
</div> </div>
) : ( ) : (

View File

@@ -8,6 +8,7 @@ import { FormField } from '@/components/form';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
@@ -54,14 +55,14 @@ export function ProjectDialog({
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent <DialogContent
className="flex max-h-[calc(100vh-2rem)] flex-col gap-0 p-0 sm:max-w-2xl" className="sm:max-w-2xl"
onOpenAutoFocus={(event) => event.preventDefault()} onOpenAutoFocus={(event) => event.preventDefault()}
> >
<DialogHeader className="shrink-0 border-b px-6 py-4 pr-12"> <DialogHeader>
<DialogTitle className="text-lg leading-none font-semibold tracking-tight"> <DialogTitle>
{projectId ? 'Edit Project' : 'Create Project'} {projectId ? 'Edit Project' : 'Create Project'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-sm"> <DialogDescription>
Manage road project identity and optional coordinate boundaries. Manage road project identity and optional coordinate boundaries.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -71,7 +72,7 @@ export function ProjectDialog({
onSubmit={onSubmit} onSubmit={onSubmit}
className="flex min-h-0 flex-1 flex-col" className="flex min-h-0 flex-1 flex-col"
> >
<div className="max-h-[58vh] space-y-6 overflow-y-auto px-6 py-4"> <DialogBody className="space-y-6">
<section className="space-y-4"> <section className="space-y-4">
<div className="space-y-1"> <div className="space-y-1">
<h3 className="text-sm font-semibold">Basic details</h3> <h3 className="text-sm font-semibold">Basic details</h3>
@@ -192,9 +193,9 @@ export function ProjectDialog({
</div> </div>
</div> </div>
</section> </section>
</div> </DialogBody>
<DialogFooter className="shrink-0 border-t px-6 py-4"> <DialogFooter>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"

View File

@@ -4,6 +4,16 @@ import { useCallback, useState } from 'react';
import { Layers, Plus } from 'lucide-react'; import { Layers, Plus } from 'lucide-react';
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import type { Project } from '@/types'; import type { Project } from '@/types';
@@ -19,6 +29,7 @@ import {
export default function ProjectPage() { export default function ProjectPage() {
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const [projectToDelete, setProjectToDelete] = useState<Project | null>(null);
const { skip, setSkip, limit, setLimit } = useProjectFilters(); const { skip, setSkip, limit, setLimit } = useProjectFilters();
const projectsQuery = useProjectsQuery({ skip, limit }); const projectsQuery = useProjectsQuery({ skip, limit });
const deleteProjectMutation = useDeleteProjectMutation(); const deleteProjectMutation = useDeleteProjectMutation();
@@ -63,16 +74,25 @@ export default function ProjectPage() {
const deleteProject = useCallback( const deleteProject = useCallback(
(project: Project) => { (project: Project) => {
if ( setProjectToDelete(project);
!confirm(`Are you sure you want to delete project "${project.name}"?`)
) {
return;
}
deleteProjectMutation.mutate(project);
}, },
[deleteProjectMutation], [],
); );
const handleDeleteDialogOpenChange = useCallback((open: boolean) => {
if (!open) {
setProjectToDelete(null);
}
}, []);
const confirmDeleteProject = useCallback(() => {
if (!projectToDelete) return;
deleteProjectMutation.mutate(projectToDelete, {
onSettled: () => setProjectToDelete(null),
});
}, [deleteProjectMutation, projectToDelete]);
const columns = useProjectColumns(); const columns = useProjectColumns();
const projects = projectsQuery.data?.items ?? []; const projects = projectsQuery.data?.items ?? [];
const total = projectsQuery.data?.totalItems ?? 0; const total = projectsQuery.data?.totalItems ?? 0;
@@ -117,6 +137,36 @@ export default function ProjectPage() {
canSubmit={canSubmit} canSubmit={canSubmit}
isSaving={isSaving} isSaving={isSaving}
/> />
<AlertDialog
open={Boolean(projectToDelete)}
onOpenChange={handleDeleteDialogOpenChange}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete project?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete{' '}
<span className="font-medium text-foreground">
{projectToDelete?.name}
</span>
. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={deleteProjectMutation.isPending}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
disabled={deleteProjectMutation.isPending}
onClick={confirmDeleteProject}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</> </>
); );
} }

View File

@@ -5,7 +5,7 @@ import { Skeleton } from '@/components/ui/skeleton';
function StatSkeleton() { function StatSkeleton() {
return ( return (
<div className="flex flex-col items-center rounded-lg border border-muted bg-muted/30 p-4 text-center"> <div className="flex flex-col items-center rounded-lg border border-muted bg-muted/30 p-4 text-center">
<Skeleton className="mb-2 size-9 rounded-md" /> <Skeleton className="mb-2 size-9 rounded-lg" />
<Skeleton className="h-6 w-12" /> <Skeleton className="h-6 w-12" />
<Skeleton className="mt-2 h-3 w-20" /> <Skeleton className="mt-2 h-3 w-20" />
</div> </div>
@@ -14,9 +14,9 @@ function StatSkeleton() {
function DetectionLogSkeleton() { function DetectionLogSkeleton() {
return ( return (
<div className="rounded-md border bg-card p-3"> <div className="rounded-lg border bg-card p-3">
<div className="flex gap-3"> <div className="flex gap-3">
<Skeleton className="size-20 shrink-0 rounded-md" /> <Skeleton className="size-20 shrink-0 rounded-lg" />
<div className="min-w-0 flex-1 space-y-2"> <div className="min-w-0 flex-1 space-y-2">
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="space-y-2"> <div className="space-y-2">
@@ -41,7 +41,7 @@ export function VideoResultsSkeleton() {
<div className="space-y-8"> <div className="space-y-8">
<div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between"> <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div className="flex min-w-0 items-start gap-3"> <div className="flex min-w-0 items-start gap-3">
<Skeleton className="size-10 rounded-md" /> <Skeleton className="size-10 rounded-lg" />
<div className="min-w-0 space-y-2"> <div className="min-w-0 space-y-2">
<Skeleton className="h-7 w-60 max-w-full" /> <Skeleton className="h-7 w-60 max-w-full" />
<Skeleton className="h-3 w-80 max-w-full" /> <Skeleton className="h-3 w-80 max-w-full" />
@@ -50,10 +50,10 @@ export function VideoResultsSkeleton() {
<Skeleton className="h-8 w-20" /> <Skeleton className="h-8 w-20" />
</div> </div>
<section className="overflow-hidden rounded-xl border bg-card"> <section className="overflow-hidden rounded-lg border bg-card">
<div className="border-b p-6 pb-4"> <div className="border-b p-6 pb-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<Skeleton className="size-10 rounded" /> <Skeleton className="size-10 rounded-lg" />
<div className="space-y-2"> <div className="space-y-2">
<Skeleton className="h-5 w-44" /> <Skeleton className="h-5 w-44" />
<Skeleton className="h-3 w-64" /> <Skeleton className="h-3 w-64" />
@@ -76,7 +76,7 @@ export function VideoResultsSkeleton() {
</div> </div>
<div className="grid grid-cols-1 gap-2 p-3 sm:grid-cols-3"> <div className="grid grid-cols-1 gap-2 p-3 sm:grid-cols-3">
{Array.from({ length: 3 }).map((_, index) => ( {Array.from({ length: 3 }).map((_, index) => (
<Skeleton key={index} className="h-16 w-full rounded-md" /> <Skeleton key={index} className="h-16 w-full rounded-lg" />
))} ))}
</div> </div>
</section> </section>

View File

@@ -125,7 +125,7 @@ export function RoleSheet({
} }
> >
{isPermissionsLoading ? ( {isPermissionsLoading ? (
<div className="rounded-md border p-6 text-muted-foreground"> <div className="rounded-lg border p-6 text-muted-foreground">
Loading permissions... Loading permissions...
</div> </div>
) : ( ) : (

View File

@@ -11,6 +11,7 @@ import { ProjectSelect } from '@/components/lookups/ProjectSelect';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
@@ -71,14 +72,14 @@ export function SegmentDialog({
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent <DialogContent
className="flex max-h-[calc(100vh-2rem)] flex-col gap-0 p-0 sm:max-w-2xl" className="sm:max-w-2xl"
onOpenAutoFocus={(event) => event.preventDefault()} onOpenAutoFocus={(event) => event.preventDefault()}
> >
<DialogHeader className="shrink-0 border-b px-6 py-4 pr-12"> <DialogHeader>
<DialogTitle className="text-lg leading-none font-semibold tracking-tight"> <DialogTitle>
{segmentId ? 'Edit Segment' : 'Create Segment'} {segmentId ? 'Edit Segment' : 'Create Segment'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-sm"> <DialogDescription>
Manage segment binding, chainage values, direction, and coordinates. Manage segment binding, chainage values, direction, and coordinates.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -88,7 +89,7 @@ export function SegmentDialog({
onSubmit={onSubmit} onSubmit={onSubmit}
className="flex min-h-0 flex-1 flex-col" className="flex min-h-0 flex-1 flex-col"
> >
<div className="max-h-[58vh] space-y-6 overflow-y-auto px-6 py-4"> <DialogBody className="space-y-6">
<section className="space-y-4"> <section className="space-y-4">
<div className="space-y-1"> <div className="space-y-1">
<h3 className="text-sm font-semibold">Basic details</h3> <h3 className="text-sm font-semibold">Basic details</h3>
@@ -300,9 +301,9 @@ export function SegmentDialog({
</div> </div>
</div> </div>
</section> </section>
</div> </DialogBody>
<DialogFooter className="shrink-0 border-t px-6 py-4"> <DialogFooter>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"

View File

@@ -4,6 +4,16 @@ import { useCallback, useState } from 'react';
import { Milestone, Plus } from 'lucide-react'; import { Milestone, Plus } from 'lucide-react';
import { PageHeader } from '@/components/page-header'; import { PageHeader } from '@/components/page-header';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import type { Chainage } from '@/types'; import type { Chainage } from '@/types';
@@ -21,6 +31,7 @@ import {
export default function SegmentPage() { export default function SegmentPage() {
const [isDialogOpen, setIsDialogOpen] = useState(false); const [isDialogOpen, setIsDialogOpen] = useState(false);
const [segmentToDelete, setSegmentToDelete] = useState<Chainage | null>(null);
const { skip, setSkip, limit, setLimit } = useSegmentFilters(); const { skip, setSkip, limit, setLimit } = useSegmentFilters();
const segmentsQuery = useSegmentsQuery({ skip, limit }); const segmentsQuery = useSegmentsQuery({ skip, limit });
const projectsQuery = useProjectOptionsQuery(); const projectsQuery = useProjectOptionsQuery();
@@ -75,18 +86,25 @@ export default function SegmentPage() {
const deleteSegment = useCallback( const deleteSegment = useCallback(
(segment: Chainage) => { (segment: Chainage) => {
if ( setSegmentToDelete(segment);
!confirm(
`Are you sure you want to delete segment "${segment.segment_name}"?`,
)
) {
return;
}
deleteSegmentMutation.mutate(segment);
}, },
[deleteSegmentMutation], [],
); );
const handleDeleteDialogOpenChange = useCallback((open: boolean) => {
if (!open) {
setSegmentToDelete(null);
}
}, []);
const confirmDeleteSegment = useCallback(() => {
if (!segmentToDelete) return;
deleteSegmentMutation.mutate(segmentToDelete, {
onSettled: () => setSegmentToDelete(null),
});
}, [deleteSegmentMutation, segmentToDelete]);
const projects = projectsQuery.data?.items ?? []; const projects = projectsQuery.data?.items ?? [];
const allPackages = allPackagesQuery.data?.items ?? []; const allPackages = allPackagesQuery.data?.items ?? [];
const segments = segmentsQuery.data?.items ?? []; const segments = segmentsQuery.data?.items ?? [];
@@ -139,6 +157,36 @@ export default function SegmentPage() {
canSubmit={canSubmit} canSubmit={canSubmit}
isSaving={isSaving} isSaving={isSaving}
/> />
<AlertDialog
open={Boolean(segmentToDelete)}
onOpenChange={handleDeleteDialogOpenChange}
>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete segment?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete{' '}
<span className="font-medium text-foreground">
{segmentToDelete?.segment_name}
</span>
. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={deleteSegmentMutation.isPending}>
Cancel
</AlertDialogCancel>
<AlertDialogAction
variant="destructive"
disabled={deleteSegmentMutation.isPending}
onClick={confirmDeleteSegment}
>
Delete
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</> </>
); );
} }

View File

@@ -13,6 +13,7 @@ import { FormField, PhoneInput, SelectPopover } from '@/components/form';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
@@ -98,12 +99,12 @@ export function TenantSheet({
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="flex max-h-[calc(100vh-2rem)] flex-col gap-0 p-0 sm:max-w-3xl"> <DialogContent className="sm:max-w-3xl">
<DialogHeader className="shrink-0 border-b px-6 py-4 pr-12"> <DialogHeader>
<DialogTitle className="text-lg leading-none font-semibold tracking-tight"> <DialogTitle>
{tenantId ? 'Edit Tenant' : 'Create Tenant'} {tenantId ? 'Edit Tenant' : 'Create Tenant'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-sm"> <DialogDescription>
Bind a client and subscription plan, then invite the tenant Bind a client and subscription plan, then invite the tenant
administrator. administrator.
</DialogDescription> </DialogDescription>
@@ -114,7 +115,7 @@ export function TenantSheet({
onSubmit={onSubmit} onSubmit={onSubmit}
className="flex min-h-0 flex-1 flex-col" className="flex min-h-0 flex-1 flex-col"
> >
<div className="max-h-[50vh] space-y-5 overflow-y-auto px-6 py-4"> <DialogBody className="space-y-5">
<div className="space-y-1"> <div className="space-y-1">
<p>Basic Information</p> <p>Basic Information</p>
<p className="text-muted-foreground"> <p className="text-muted-foreground">
@@ -227,7 +228,7 @@ export function TenantSheet({
id="tenant-description" id="tenant-description"
placeholder="North zone operations" placeholder="North zone operations"
{...register('description')} {...register('description')}
className="min-h-20 w-full rounded-md border border-input bg-background px-3 py-2 outline-none focus-visible:border-ring" className="min-h-20 w-full rounded-lg border border-input bg-background px-3 py-2 outline-none focus-visible:border-ring"
/> />
</FormField> </FormField>
</div> </div>
@@ -314,7 +315,7 @@ export function TenantSheet({
</div> </div>
</> </>
) : adminEmail ? ( ) : adminEmail ? (
<div className="rounded-md border bg-muted/40 p-4 text-muted-foreground"> <div className="rounded-lg border bg-muted/40 p-4 text-muted-foreground">
Admin invitation details cannot be changed after tenant Admin invitation details cannot be changed after tenant
creation. creation.
<p className="mt-1 text-foreground"> <p className="mt-1 text-foreground">
@@ -322,9 +323,9 @@ export function TenantSheet({
</p> </p>
</div> </div>
) : null} ) : null}
</div> </DialogBody>
<DialogFooter className="shrink-0 border-t px-6 py-4"> <DialogFooter>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"

View File

@@ -33,8 +33,8 @@ export function TicketClassContentSkeleton() {
<Skeleton className="h-5 w-40" /> <Skeleton className="h-5 w-40" />
</CardHeader> </CardHeader>
<CardContent className="grid grid-cols-1 gap-4 lg:grid-cols-2"> <CardContent className="grid grid-cols-1 gap-4 lg:grid-cols-2">
<Skeleton className="aspect-video w-full rounded-md" /> <Skeleton className="aspect-video w-full rounded-lg" />
<Skeleton className="aspect-video w-full rounded-md" /> <Skeleton className="aspect-video w-full rounded-lg" />
</CardContent> </CardContent>
</Card> </Card>
@@ -57,7 +57,7 @@ export function TicketClassContentSkeleton() {
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Skeleton className="size-5 rounded" /> <Skeleton className="size-5 rounded-lg" />
<Skeleton className="h-5 w-56" /> <Skeleton className="h-5 w-56" />
</div> </div>
</CardHeader> </CardHeader>
@@ -89,7 +89,7 @@ export function TicketClassActionsSkeleton() {
<Card aria-label="Loading defect class actions"> <Card aria-label="Loading defect class actions">
<CardHeader> <CardHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Skeleton className="size-4 rounded" /> <Skeleton className="size-4 rounded-lg" />
<Skeleton className="h-5 w-32" /> <Skeleton className="h-5 w-32" />
</div> </div>
</CardHeader> </CardHeader>

View File

@@ -149,7 +149,7 @@ export function TicketClassDetectionPreview({
<div className="flex min-w-0 items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
<span <span
className={cn( className={cn(
'flex size-10 shrink-0 items-center justify-center rounded-full', 'flex size-10 shrink-0 self-start items-center justify-center rounded-lg',
visual.cardClassName, visual.cardClassName,
)} )}
> >
@@ -157,13 +157,13 @@ export function TicketClassDetectionPreview({
</span> </span>
<div className="min-w-0 space-y-1"> <div className="min-w-0 space-y-1">
<div className="flex min-w-0 flex-wrap items-center gap-2"> <div className="flex min-w-0 flex-wrap items-center gap-2">
<h3 className="truncate text-lg font-semibold text-foreground"> <h3 >
{displayName} {displayName}
</h3> </h3>
<Badge <Badge
variant="secondary" variant="secondary"
className={cn( className={cn(
'rounded-full border-0 px-2.5 py-1 text-xs font-semibold', 'rounded-lg',
visual.cardClassName, visual.cardClassName,
visual.colorClassName, visual.colorClassName,
)} )}
@@ -171,7 +171,7 @@ export function TicketClassDetectionPreview({
{detectionCount} Detections {detectionCount} Detections
</Badge> </Badge>
</div> </div>
<p className="text-sm text-muted-foreground"> <p className="text-muted-foreground">
Review detections in ascending timestamp order. Review detections in ascending timestamp order.
</p> </p>
</div> </div>
@@ -180,8 +180,7 @@ export function TicketClassDetectionPreview({
<div className="flex items-center gap-2 self-start sm:self-auto"> <div className="flex items-center gap-2 self-start sm:self-auto">
<Button <Button
type="button" type="button"
variant="outline" variant="secondary"
size="sm"
onClick={() => setCurrentIndex((index) => Math.max(index - 1, 0))} onClick={() => setCurrentIndex((index) => Math.max(index - 1, 0))}
disabled={isPreviousDisabled} disabled={isPreviousDisabled}
aria-label="Previous detection" aria-label="Previous detection"
@@ -195,8 +194,7 @@ export function TicketClassDetectionPreview({
</div> </div>
<Button <Button
type="button" type="button"
variant="outline" variant="secondary"
size="sm"
onClick={() => setCurrentIndex((index) => index + 1)} onClick={() => setCurrentIndex((index) => index + 1)}
disabled={isNextDisabled} disabled={isNextDisabled}
aria-label="Next detection" aria-label="Next detection"
@@ -233,7 +231,7 @@ export function TicketClassDetectionPreview({
<> <>
<div className="grid grid-cols-1 items-start gap-4 xl:grid-cols-2"> <div className="grid grid-cols-1 items-start gap-4 xl:grid-cols-2">
<div className="min-w-0 space-y-2"> <div className="min-w-0 space-y-2">
<p className="text-xs font-medium text-muted-foreground"> <p className="text-muted-foreground">
Detection Image Detection Image
</p> </p>
<div className="overflow-hidden rounded-lg"> <div className="overflow-hidden rounded-lg">

View File

@@ -75,9 +75,9 @@ export function TicketDefectClassTabs({
if (defectClassesQuery.isLoading) { if (defectClassesQuery.isLoading) {
return ( return (
<Card className="w-full"> <Card className="w-full rounded-lg">
<CardContent className="space-y-4 p-5"> <CardContent className="space-y-4 p-5">
<div className="h-5 w-24 animate-pulse rounded bg-muted" /> <div className="h-5 w-24 animate-pulse rounded-lg bg-muted" />
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{Array.from({ length: 3 }).map((_, index) => ( {Array.from({ length: 3 }).map((_, index) => (
<div <div
@@ -104,7 +104,7 @@ export function TicketDefectClassTabs({
> >
<Card className="w-full"> <Card className="w-full">
<CardContent className="grid gap-0 p-0 lg:grid-cols-[minmax(220px,280px)_1fr] lg:divide-x lg:divide-border"> <CardContent className="grid gap-0 p-0 lg:grid-cols-[minmax(220px,280px)_1fr] lg:divide-x lg:divide-border">
<div className="space-y-4 p-5"> <div className="space-y-4 px-5">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<ListChecks className="size-5 shrink-0 text-primary" /> <ListChecks className="size-5 shrink-0 text-primary" />
<h2 className="text-base font-semibold">Detected Issues</h2> <h2 className="text-base font-semibold">Detected Issues</h2>
@@ -126,7 +126,7 @@ export function TicketDefectClassTabs({
key={item.class_name} key={item.class_name}
value={item.class_name} value={item.class_name}
className={cn( className={cn(
'group h-auto w-full flex-none items-center justify-between! gap-2 rounded-lg border border-border bg-muted/30 px-3 py-2.5 text-sm text-muted-foreground shadow-none after:hidden', 'group h-auto w-full flex-none items-center justify-between! gap-2 rounded-lg border bg-muted/30 px-3 py-2.5 text-sm text-muted-foreground shadow-none after:hidden',
'data-[state=active]:bg-muted data-[state=active]:text-foreground', 'data-[state=active]:bg-muted data-[state=active]:text-foreground',
)} )}
> >
@@ -135,7 +135,7 @@ export function TicketDefectClassTabs({
</span> </span>
<Badge <Badge
className={cn( className={cn(
'shrink-0 gap-1.5 rounded-full border-0 px-2.5 py-1 text-xs font-medium', 'shrink-0 rounded-full',
statusConfig.badgeClassName, statusConfig.badgeClassName,
)} )}
> >
@@ -148,7 +148,7 @@ export function TicketDefectClassTabs({
</TabsList> </TabsList>
</div> </div>
<div className="min-w-0 p-5"> <div className="min-w-0 px-5">
<TicketClassDetectionPreview <TicketClassDetectionPreview
videoId={previewVideoId} videoId={previewVideoId}
defectClassName={selectedClass} defectClassName={selectedClass}

View File

@@ -23,8 +23,7 @@ export function TicketDetailHeader({
<div className="flex shrink-0 items-center gap-2"> <div className="flex shrink-0 items-center gap-2">
<Button <Button
variant="outline" variant="secondary"
size="sm"
onClick={() => router.push('/ticket')} onClick={() => router.push('/ticket')}
> >
<ArrowLeft className="size-4" /> <ArrowLeft className="size-4" />

View File

@@ -48,10 +48,10 @@ function SystemHistoryEntry({ item }: { item: TicketHistoryItem }) {
const noteText = item.note?.text?.trim(); const noteText = item.note?.text?.trim();
return ( return (
<div className="rounded-md border border-dashed border-primary/25 bg-primary/5 px-4 py-3.5"> <div className="rounded-lg border border-dashed border-primary/25 bg-primary/5 px-4 py-3.5">
<Badge <Badge
className="rounded-xs px-2.5 py-0.5 text-[10px] font-semibold tracking-wider uppercase" className="rounded-lg px-2.5 py-0.5 text-[10px] font-semibold tracking-wider uppercase"
> >
{getSourceLabel(item.source)} {getSourceLabel(item.source)}
</Badge> </Badge>
@@ -70,7 +70,7 @@ function UserHistoryEntry({ item }: { item: TicketHistoryItem }) {
const isAssigned = item.event_type === 'assigned'; const isAssigned = item.event_type === 'assigned';
return ( return (
<div className="rounded-md border border-border bg-card p-4"> <div className="rounded-lg border bg-card p-4">
<h3 className="mb-3 text-sm font-semibold text-foreground">{item.title}</h3> <h3 className="mb-3 text-sm font-semibold text-foreground">{item.title}</h3>
<PersonInfo person={item.actor} /> <PersonInfo person={item.actor} />
{isAssigned && item.target_user ? ( {isAssigned && item.target_user ? (
@@ -124,7 +124,7 @@ export function TicketHistoryCard({ ticket }: { ticket: TicketDetail }) {
))} ))}
</Timeline> </Timeline>
) : ( ) : (
<div className="rounded-md border border-dashed border-border/80 bg-muted/10 px-4 py-8 text-center"> <div className="rounded-lg border border-dashed border-border/80 bg-muted/10 px-4 py-8 text-center">
<p className="text-sm font-medium text-muted-foreground"> <p className="text-sm font-medium text-muted-foreground">
No timeline entries yet No timeline entries yet
</p> </p>

View File

@@ -8,6 +8,7 @@ import {
MapPin, MapPin,
Monitor, Monitor,
UserRound, UserRound,
Play
} from 'lucide-react'; } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
@@ -21,6 +22,7 @@ import {
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogHeader, DialogHeader,
@@ -77,7 +79,7 @@ function SummaryCard({
return ( return (
<div <div
className={cn( className={cn(
'rounded-xl bg-muted/40 p-4 transition-colors hover:bg-muted/60', 'rounded-lg bg-muted/40 p-4 hover:scale-105 transition-transform ease-in-out',
cardClassName, cardClassName,
)} )}
> >
@@ -180,11 +182,10 @@ export function TicketOverviewCard({
<CardAction> <CardAction>
<PermissionGuard permissions={PERMISSIONS.TICKET.ASSIGN}> <PermissionGuard permissions={PERMISSIONS.TICKET.ASSIGN}>
<Button <Button
variant="ghost" variant="secondary"
size="sm"
className="text-primary hover:bg-primary/10 hover:text-primary"
onClick={() => setIsAnalysisOpen(true)} onClick={() => setIsAnalysisOpen(true)}
> >
<Play className="mr-2 size-4" />
View Video View Video
</Button> </Button>
</PermissionGuard> </PermissionGuard>
@@ -232,16 +233,16 @@ export function TicketOverviewCard({
</Card> </Card>
<Dialog open={isAnalysisOpen} onOpenChange={setIsAnalysisOpen}> <Dialog open={isAnalysisOpen} onOpenChange={setIsAnalysisOpen}>
<DialogContent className="gap-3 p-3 sm:max-w-5xl sm:p-4"> <DialogContent className="sm:max-w-5xl">
<DialogHeader className="min-w-0 pr-8"> <DialogHeader>
<DialogTitle className="truncate text-left text-base"> <DialogTitle>{analysisTitle}</DialogTitle>
{analysisTitle} <DialogDescription>
</DialogTitle>
<DialogDescription className="truncate text-xs">
Video analysis with annotation overlay Video analysis with annotation overlay
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogBody>
<TicketInlineAnalysisVideo ticket={ticket} /> <TicketInlineAnalysisVideo ticket={ticket} />
</DialogBody>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</div> </div>

View File

@@ -89,7 +89,7 @@ export function ClosedTicketAction({ ticket }: { ticket: TicketDetail }) {
</CardHeader> </CardHeader>
<CardContent className="space-y-5"> <CardContent className="space-y-5">
<div className="space-y-4 rounded-xl border bg-muted/20 p-4"> <div className="space-y-4 rounded-lg border bg-muted/20 p-4">
<MetaField <MetaField
label="Reviewed By" label="Reviewed By"
value={<PersonInfo person={reviewer} size="lg" />} value={<PersonInfo person={reviewer} size="lg" />}

View File

@@ -56,11 +56,11 @@ function MediaSectionHeader({
</div> </div>
<Button <Button
type="button" type="button"
variant="outline" variant="secondary"
size="sm" size="sm"
disabled={disabled} disabled={disabled}
onClick={onAdd} onClick={onAdd}
className="h-8 shrink-0 border-primary/30 text-primary hover:bg-primary/5 hover:text-primary" className="shrink-0"
> >
<Plus className="size-3.5" /> <Plus className="size-3.5" />
{addLabel} {addLabel}
@@ -71,7 +71,7 @@ function MediaSectionHeader({
function MediaHint({ children }: { children: ReactNode }) { function MediaHint({ children }: { children: ReactNode }) {
return ( return (
<p className="rounded-md bg-muted/50 px-3 py-2 text-xs text-muted-foreground"> <p className="rounded-lg bg-muted/50 px-3 py-2 text-xs text-muted-foreground">
{children} {children}
</p> </p>
); );
@@ -182,7 +182,7 @@ function VideoThumbnail({
</span> </span>
</div> </div>
{duration ? ( {duration ? (
<span className="pointer-events-none absolute bottom-1.5 right-1.5 rounded bg-black/75 px-1.5 py-0.5 text-[10px] font-medium text-white"> <span className="pointer-events-none absolute bottom-1.5 right-1.5 rounded-lg bg-black/75 px-1.5 py-0.5 text-[10px] font-medium text-white">
{duration} {duration}
</span> </span>
) : null} ) : null}
@@ -382,7 +382,7 @@ export function RepairEvidenceForm({
description="Tap to upload repair photos" description="Tap to upload repair photos"
disabled={isSubmitting} disabled={isSubmitting}
onClick={openImagePicker} onClick={openImagePicker}
className="min-h-[104px]" className="min-h-26"
/> />
) : ( ) : (
<div className="flex min-w-min gap-3"> <div className="flex min-w-min gap-3">
@@ -457,7 +457,7 @@ export function RepairEvidenceForm({
</div> </div>
{error ? ( {error ? (
<div className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive"> <div className="rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
{error} {error}
</div> </div>
) : null} ) : null}

View File

@@ -32,6 +32,7 @@ export function ReviewRepairAction({ ticket }: { ticket: TicketDetail }) {
</div> </div>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
<Button <Button
variant={'secondary'}
disabled={!reviewComment || reviewRepairMutation.isPending} disabled={!reviewComment || reviewRepairMutation.isPending}
onClick={() => onClick={() =>
reviewRepairMutation.mutate({ reviewRepairMutation.mutate({

View File

@@ -22,7 +22,7 @@ export function TicketActionCard({
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2 text-base"> <CardTitle className="flex items-center gap-2 text-base">
<Icon className={cn('size-4 text-primary', iconClassName)} /> <Icon className={cn('text-primary', iconClassName)} />
{title} {title}
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>

View File

@@ -43,11 +43,11 @@ export function RepairProofGallery({ imageUrls }: RepairProofGalleryProps) {
if (queries.some((query) => query.isLoading)) { if (queries.some((query) => query.isLoading)) {
return ( return (
<div className="grid grid-cols-4 gap-1 overflow-hidden rounded-xl"> <div className="grid grid-cols-4 gap-1 overflow-hidden rounded-lg">
{imageUrls.slice(0, MAX_VISIBLE_IMAGES).map((url, index) => ( {imageUrls.slice(0, MAX_VISIBLE_IMAGES).map((url, index) => (
<Skeleton <Skeleton
key={`${url}-${index}`} key={`${url}-${index}`}
className="aspect-square rounded-none" className="aspect-square rounded-lg"
/> />
))} ))}
</div> </div>
@@ -70,7 +70,7 @@ export function RepairProofGallery({ imageUrls }: RepairProofGalleryProps) {
return ( return (
<LightGallery <LightGallery
elementClassNames="grid grid-cols-4 gap-1 overflow-hidden rounded-xl" elementClassNames="grid grid-cols-4 gap-1 overflow-hidden rounded-lg"
mode="lg-fade" mode="lg-fade"
speed={400} speed={400}
plugins={[lgThumbnail, lgZoom]} plugins={[lgThumbnail, lgZoom]}

View File

@@ -1,4 +1,4 @@
import { CheckCircle2, HardHat, Wrench } from 'lucide-react'; import { CheckCircle2, HardHat, Wrench, ToolCase } from 'lucide-react';
import { ProtectedVideoPlayer } from '@/components/media/ProtectedVideoPlayer'; import { ProtectedVideoPlayer } from '@/components/media/ProtectedVideoPlayer';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@@ -72,7 +72,7 @@ export function RepairProofSummary({ ticket }: { ticket: TicketDetail }) {
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle className="flex items-center gap-2 text-base"> <CardTitle className="flex items-center gap-2 text-base">
<Wrench className="size-4 text-primary" /> <ToolCase className="text-primary" />
Repair Proof Repair Proof
</CardTitle> </CardTitle>
</CardHeader> </CardHeader>

View File

@@ -6,6 +6,7 @@ import { Loader2, Play } from 'lucide-react';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
@@ -89,7 +90,7 @@ export function CreateUploadDialog({
return ( return (
<Dialog open={open} onOpenChange={handleOpenChange}> <Dialog open={open} onOpenChange={handleOpenChange}>
<DialogContent <DialogContent
className="max-h-[calc(100vh-2rem)] overflow-y-auto sm:max-w-5xl" className="sm:max-w-5xl"
onOpenAutoFocus={(event) => event.preventDefault()} onOpenAutoFocus={(event) => event.preventDefault()}
> >
<DialogHeader> <DialogHeader>
@@ -99,7 +100,7 @@ export function CreateUploadDialog({
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<div className="space-y-5"> <DialogBody className="space-y-5">
<LocationSection <LocationSection
value={session} value={session}
onChange={setSession} onChange={setSession}
@@ -128,11 +129,11 @@ export function CreateUploadDialog({
portalContainer={dropdownPortalRef} portalContainer={dropdownPortalRef}
/> />
{error ? ( {error ? (
<div className="rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive"> <div className="rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive">
{error} {error}
</div> </div>
) : null} ) : null}
</div> </DialogBody>
<DialogFooter> <DialogFooter>
<Button <Button

View File

@@ -56,7 +56,7 @@ export function UploadFileDropzone({
handleFiles(event.dataTransfer.files); handleFiles(event.dataTransfer.files);
}} }}
className={cn( className={cn(
'group flex min-h-32 cursor-pointer flex-col justify-between rounded-md border border-dashed bg-card/60 p-4 transition-colors', 'group flex min-h-32 cursor-pointer flex-col justify-between rounded-lg border border-dashed bg-card/60 p-4 transition-colors',
'hover:border-primary/60 hover:bg-secondary/40', 'hover:border-primary/60 hover:bg-secondary/40',
isDragging && 'border-primary bg-secondary/60', isDragging && 'border-primary bg-secondary/60',
disabled && 'pointer-events-none cursor-not-allowed opacity-60', disabled && 'pointer-events-none cursor-not-allowed opacity-60',
@@ -72,7 +72,7 @@ export function UploadFileDropzone({
/> />
<div className="space-y-3"> <div className="space-y-3">
<div className="flex size-9 items-center justify-center rounded-md border bg-background"> <div className="flex size-9 items-center justify-center rounded-lg border bg-background">
<UploadCloud className="size-4 text-muted-foreground" /> <UploadCloud className="size-4 text-muted-foreground" />
</div> </div>
<div> <div>

View File

@@ -39,7 +39,7 @@ export function useUploadListColumns(
return ( return (
<div <div
className="flex aspect-video w-20 items-center justify-center overflow-hidden rounded-md border bg-muted bg-cover bg-center" className="flex aspect-video w-20 items-center justify-center overflow-hidden rounded-lg border bg-muted bg-cover bg-center"
style={ style={
thumbnail thumbnail
? { backgroundImage: `url("${thumbnail}")` } ? { backgroundImage: `url("${thumbnail}")` }

View File

@@ -1,7 +1,13 @@
'use client'; 'use client';
import { ProtectedVideoPlayer } from '@/components/media/ProtectedVideoPlayer'; import { ProtectedVideoPlayer } from '@/components/media/ProtectedVideoPlayer';
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; import {
Dialog,
DialogBody,
DialogContent,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import type { UploadListItem } from '@/types'; import type { UploadListItem } from '@/types';
interface VideoPreviewDialogProps { interface VideoPreviewDialogProps {
@@ -22,11 +28,13 @@ export function VideoPreviewDialog({
<DialogTitle>Video Preview</DialogTitle> <DialogTitle>Video Preview</DialogTitle>
</DialogHeader> </DialogHeader>
<DialogBody>
<ProtectedVideoPlayer <ProtectedVideoPlayer
url={open ? upload?.raw_video_url : undefined} url={open ? upload?.raw_video_url : undefined}
className="rounded-lg" className="rounded-lg"
unavailableTitle="Video preview unavailable" unavailableTitle="Video preview unavailable"
/> />
</DialogBody>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );

View File

@@ -14,6 +14,7 @@ import { RoleSelect } from '@/components/lookups/RoleSelect';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { import {
Dialog, Dialog,
DialogBody,
DialogContent, DialogContent,
DialogDescription, DialogDescription,
DialogFooter, DialogFooter,
@@ -67,13 +68,13 @@ export function UserSheet({
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="flex max-h-[calc(100vh-2rem)] flex-col gap-0 p-0 sm:max-w-xl"> <DialogContent className="sm:max-w-xl">
<DialogHeader className="shrink-0 border-b px-6 py-4 pr-12"> <DialogHeader>
<DialogTitle className="text-lg leading-none font-semibold tracking-tight"> <DialogTitle>
{userId ? 'Edit User' : 'Create User'} {userId ? 'Edit User' : 'Create User'}
</DialogTitle> </DialogTitle>
<DialogDescription className="text-sm"> <DialogDescription>
Assign user details and a role. Assign user details and a role.
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
@@ -83,7 +84,7 @@ export function UserSheet({
onSubmit={onSubmit} onSubmit={onSubmit}
className="flex min-h-0 flex-1 flex-col" className="flex min-h-0 flex-1 flex-col"
> >
<div className="max-h-[50vh] space-y-5 overflow-y-auto px-6 py-4"> <DialogBody className="space-y-5">
<div className="grid gap-4 md:grid-cols-2"> <div className="grid gap-4 md:grid-cols-2">
<FormField <FormField
id="first-name" id="first-name"
@@ -172,9 +173,9 @@ export function UserSheet({
readOnly readOnly
/> />
</FormField> </FormField>
</div> </DialogBody>
<DialogFooter className="shrink-0 border-t px-6 py-4"> <DialogFooter>
<Button <Button
type="button" type="button"
variant="outline" variant="outline"

View File

@@ -7,7 +7,7 @@
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));
:root { :root {
--radius: 0.625rem; --radius: 0.225rem;
--card: oklch(1 0 0); --card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0); --card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0); --popover: oklch(1 0 0);

View File

@@ -3,8 +3,8 @@ import Image from 'next/image';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
const sizeClasses = { const sizeClasses = {
sm: 'size-8 rounded-md border-2 p-0.5', sm: 'size-8 rounded-lg border-2 p-0.5',
md: 'size-12 rounded-md border-2 p-1.5', md: 'size-12 rounded-lg border-2 p-1.5',
lg: 'size-28 rounded-lg border-[5px] p-3', lg: 'size-28 rounded-lg border-[5px] p-3',
} as const; } as const;

View File

@@ -1,6 +1,7 @@
'use client'; 'use client';
import React from 'react'; import React from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation'; import { usePathname } from 'next/navigation';
import { import {
Breadcrumb, Breadcrumb,
@@ -27,7 +28,9 @@ export function BreadcrumbBasic() {
<Breadcrumb> <Breadcrumb>
<BreadcrumbList> <BreadcrumbList>
<BreadcrumbItem> <BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink> <BreadcrumbLink asChild>
<Link href="/">Home</Link>
</BreadcrumbLink>
</BreadcrumbItem> </BreadcrumbItem>
{segments.length > 0 && <BreadcrumbSeparator />} {segments.length > 0 && <BreadcrumbSeparator />}
@@ -44,8 +47,8 @@ export function BreadcrumbBasic() {
{isLast ? ( {isLast ? (
<BreadcrumbPage>{formatSegment(segment)}</BreadcrumbPage> <BreadcrumbPage>{formatSegment(segment)}</BreadcrumbPage>
) : ( ) : (
<BreadcrumbLink href={href}> <BreadcrumbLink asChild>
{formatSegment(segment)} <Link href={href}>{formatSegment(segment)}</Link>
</BreadcrumbLink> </BreadcrumbLink>
)} )}
</BreadcrumbItem> </BreadcrumbItem>

View File

@@ -86,7 +86,7 @@ export function AuthBackground({ className }: AuthBackgroundProps) {
<div className="mb-9 inline-flex items-center gap-5 text-left"> <div className="mb-9 inline-flex items-center gap-5 text-left">
<LogoWrapper <LogoWrapper
alt="" alt=""
className="size-28 rounded-2xl border-[5px] p-4" className="size-28 rounded-lg border-[5px] p-4"
priority priority
variant="glass" variant="glass"
/> />

View File

@@ -70,7 +70,7 @@ const TableHeader = <TData, _>({ table }: { table: Table<TData> }) => {
onMouseDown={header.getResizeHandler()} onMouseDown={header.getResizeHandler()}
onTouchStart={header.getResizeHandler()} onTouchStart={header.getResizeHandler()}
className={cn( className={cn(
'absolute -right-2 top-0 h-full w-1 cursor-col-resize touch-none select-none rounded bg-border opacity-0 transition-opacity hover:opacity-100', 'absolute -right-2 top-0 h-full w-1 cursor-col-resize touch-none select-none rounded-lg bg-border opacity-0 transition-opacity hover:opacity-100',
header.column.getIsResizing() && 'opacity-100', header.column.getIsResizing() && 'opacity-100',
)} )}
/> />

View File

@@ -259,7 +259,7 @@ function DataTableContent<TData, TValue>({
} }
/> />
<div className="overflow-hidden rounded-lg border border-border bg-background"> <div className="overflow-hidden rounded-lg border bg-background">
<Table <Table
containerClassName="max-h-[calc(100vh-350px)] overflow-auto" containerClassName="max-h-[calc(100vh-350px)] overflow-auto"
className="table-fixed border-separate border-spacing-0" className="table-fixed border-separate border-spacing-0"

View File

@@ -32,7 +32,7 @@ interface MultiSelectPopoverProps {
function checkboxClass(checked: boolean) { function checkboxClass(checked: boolean) {
return cn( return cn(
'flex size-4 shrink-0 items-center justify-center rounded border', 'flex size-4 shrink-0 items-center justify-center rounded-lg border',
checked && 'border-primary bg-primary text-primary-foreground', checked && 'border-primary bg-primary text-primary-foreground',
); );
} }
@@ -106,7 +106,7 @@ export function MultiSelectPopover({
type="button" type="button"
onClick={toggleAll} onClick={toggleAll}
aria-label={`Toggle all ${label}`} aria-label={`Toggle all ${label}`}
className="shrink-0 cursor-pointer rounded-md p-0.5 hover:bg-muted" className="shrink-0 cursor-pointer rounded-lg p-0.5 hover:bg-muted"
> >
<span className={checkboxClass(areAllSelected)}> <span className={checkboxClass(areAllSelected)}>
{areAllSelected ? <Check className="size-3" /> : null} {areAllSelected ? <Check className="size-3" /> : null}
@@ -128,7 +128,7 @@ export function MultiSelectPopover({
return ( return (
<div <div
key={option.value} key={option.value}
className="flex items-center gap-2 rounded-md px-2 py-1.5 hover:bg-muted" className="flex items-center gap-2 rounded-lg px-2 py-1.5 hover:bg-muted"
> >
<button <button
type="button" type="button"

View File

@@ -101,7 +101,7 @@ export function SelectPopover({
setOpen(false); setOpen(false);
}} }}
className={cn( className={cn(
'flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-left text-sm hover:bg-muted', 'flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left text-sm hover:bg-muted',
option.disabled && 'cursor-not-allowed opacity-60', option.disabled && 'cursor-not-allowed opacity-60',
)} )}
> >

View File

@@ -109,7 +109,7 @@ export function CountryCombobox({
type="button" type="button"
variant="ghost" variant="ghost"
disabled={disabled} disabled={disabled}
className="h-9 shrink-0 gap-1 rounded-none border-r px-2.5 shadow-none hover:bg-transparent" className="h-9 shrink-0 gap-1 rounded-lg border-r px-2.5 shadow-none hover:bg-transparent"
> >
<CountryFlag country={value} /> <CountryFlag country={value} />
</Button> </Button>

View File

@@ -7,7 +7,7 @@ export function CountryFlag({ country }: { country: CountryCode }) {
<div <div
aria-hidden aria-hidden
className={cn( className={cn(
'flag inline-block h-3.75 w-5.5 shrink-0 rounded-[2px] bg-size-[100%_auto]', 'flag inline-block h-3.75 w-5.5 shrink-0 rounded-lg bg-size-[100%_auto]',
`flag-${country.toLowerCase()}`, `flag-${country.toLowerCase()}`,
)} )}
/> />

View File

@@ -59,7 +59,7 @@ export function PhoneInput({
return ( return (
<div <div
className={cn( className={cn(
'flex h-9 w-full min-w-0 rounded-md border border-input bg-transparent shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30', 'flex h-9 w-full min-w-0 rounded-lg border border-input bg-transparent shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/50 dark:bg-input/30',
ariaInvalid && ariaInvalid &&
'border-destructive focus-within:border-destructive focus-within:ring-destructive/20', 'border-destructive focus-within:border-destructive focus-within:ring-destructive/20',
disabled && 'cursor-not-allowed opacity-50', disabled && 'cursor-not-allowed opacity-50',
@@ -80,7 +80,7 @@ export function PhoneInput({
disabled={disabled} disabled={disabled}
aria-invalid={ariaInvalid} aria-invalid={ariaInvalid}
placeholder={placeholder} placeholder={placeholder}
className="h-9 flex-1 rounded-none border-0 bg-transparent px-3 py-1 shadow-none focus-visible:ring-0" className="h-9 flex-1 rounded-lg border-0 bg-transparent px-3 py-1 shadow-none focus-visible:ring-0"
/> />
</div> </div>
); );

View File

@@ -116,7 +116,7 @@ export default function DetectionLocationMap({
return ( return (
<div className={cn('space-y-2', className)}> <div className={cn('space-y-2', className)}>
{title ? ( {title ? (
<p className="text-xs font-medium text-muted-foreground">{title}</p> <p className="text-muted-foreground">{title}</p>
) : null} ) : null}
{mapContent} {mapContent}
</div> </div>
@@ -127,7 +127,7 @@ export default function DetectionLocationMap({
<Card className={cn('overflow-hidden', className)}> <Card className={cn('overflow-hidden', className)}>
<CardHeader className="border-b pb-4"> <CardHeader className="border-b pb-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="rounded bg-secondary p-2"> <div className="rounded-lg bg-secondary p-2">
<MapPin className="h-5 w-5 text-primary" /> <MapPin className="h-5 w-5 text-primary" />
</div> </div>
<div> <div>

View File

@@ -41,7 +41,7 @@ export function ProtectedVideoPlayer({
return ( return (
<div <div
className={cn( className={cn(
'flex aspect-video w-full flex-col items-center justify-center gap-3 rounded-md border bg-muted/30 text-foreground', 'flex aspect-video w-full flex-col items-center justify-center gap-3 rounded-lg border bg-muted/30 text-foreground',
className, className,
)} )}
> >
@@ -69,7 +69,7 @@ export function ProtectedVideoPlayer({
return ( return (
<div <div
className={cn( className={cn(
'group relative overflow-hidden rounded-md border bg-black/90', 'group relative overflow-hidden rounded-lg border bg-black/90',
className, className,
)} )}
> >
@@ -84,7 +84,7 @@ export function ProtectedVideoPlayer({
</video> </video>
{label ? ( {label ? (
<span className="absolute top-3 left-3 rounded bg-black/65 px-2.5 py-1 text-[11px] font-semibold text-white backdrop-blur-sm"> <span className="absolute top-3 left-3 rounded-lg bg-black/65 px-2.5 py-1 text-[11px] font-semibold text-white backdrop-blur-sm">
{label} {label}
</span> </span>
) : null} ) : null}
@@ -110,7 +110,7 @@ function VideoState({
return ( return (
<div <div
className={cn( className={cn(
'flex aspect-video flex-col items-center justify-center rounded-md border border-dashed border-border/80 bg-muted/10 px-4 py-8 text-center', 'flex aspect-video flex-col items-center justify-center rounded-lg border border-dashed border-border/80 bg-muted/10 px-4 py-8 text-center',
className, className,
)} )}
> >

View File

@@ -0,0 +1,199 @@
"use client"
import * as React from "react"
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
import { Button } from "@/components/ui/button"
function AlertDialog({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Root>) {
return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />
}
function AlertDialogTrigger({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
return (
<AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />
)
}
function AlertDialogPortal({
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Portal>) {
return (
<AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />
)
}
function AlertDialogOverlay({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
return (
<AlertDialogPrimitive.Overlay
data-slot="alert-dialog-overlay"
className={cn(
"fixed inset-0 z-50 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
className
)}
{...props}
/>
)
}
function AlertDialogContent({
className,
size = "default",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Content> & {
size?: "default" | "sm"
}) {
return (
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogPrimitive.Content
data-slot="alert-dialog-content"
data-size={size}
className={cn(
"group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-6 rounded-lg bg-popover p-6 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-lg data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className
)}
{...props}
/>
</AlertDialogPortal>
)
}
function AlertDialogHeader({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-header"
className={cn(
"grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-6 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]",
className
)}
{...props}
/>
)
}
function AlertDialogFooter({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-footer"
className={cn(
"flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
className
)}
{...props}
/>
)
}
function AlertDialogMedia({
className,
...props
}: React.ComponentProps<"div">) {
return (
<div
data-slot="alert-dialog-media"
className={cn(
"mb-2 inline-flex size-16 items-center justify-center rounded-lg bg-muted sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-8",
className
)}
{...props}
/>
)
}
function AlertDialogTitle({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
return (
<AlertDialogPrimitive.Title
data-slot="alert-dialog-title"
className={cn(
"font-heading text-lg font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2",
className
)}
{...props}
/>
)
}
function AlertDialogDescription({
className,
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
return (
<AlertDialogPrimitive.Description
data-slot="alert-dialog-description"
className={cn(
"text-sm text-balance text-muted-foreground md:text-pretty *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
className
)}
{...props}
/>
)
}
function AlertDialogAction({
className,
variant = "default",
size = "default",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Action> &
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
return (
<Button variant={variant} size={size} asChild>
<AlertDialogPrimitive.Action
data-slot="alert-dialog-action"
className={cn(className)}
{...props}
/>
</Button>
)
}
function AlertDialogCancel({
className,
variant = "outline",
size = "default",
...props
}: React.ComponentProps<typeof AlertDialogPrimitive.Cancel> &
Pick<React.ComponentProps<typeof Button>, "variant" | "size">) {
return (
<Button variant={variant} size={size} asChild>
<AlertDialogPrimitive.Cancel
data-slot="alert-dialog-cancel"
className={cn(className)}
{...props}
/>
</Button>
)
}
export {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogTitle,
AlertDialogTrigger,
}

View File

@@ -5,7 +5,7 @@ import { Slot } from "radix-ui"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const badgeVariants = cva( const badgeVariants = cva(
"group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-4xl border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!", "group/badge inline-flex h-5 w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-lg border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-all focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3!",
{ {
variants: { variants: {
variant: { variant: {

View File

@@ -5,7 +5,7 @@ import { Slot } from "radix-ui"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
const buttonVariants = cva( const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-md border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{ {
variants: { variants: {
variant: { variant: {
@@ -22,15 +22,15 @@ const buttonVariants = cva(
}, },
size: { size: {
default: default:
"h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", "h-9 gap-1.5 px-2.5 in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
xs: "h-6 gap-1 rounded-[min(var(--radius-md),8px)] px-2 text-xs in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3", xs: "h-6 gap-1 rounded-lg px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1 rounded-[min(var(--radius-md),10px)] px-2.5 in-data-[slot=button-group]:rounded-md has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5", sm: "h-8 gap-1 rounded-lg px-2.5 in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5",
lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2", lg: "h-10 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
icon: "size-9", icon: "size-9",
"icon-xs": "icon-xs":
"size-6 rounded-[min(var(--radius-md),8px)] in-data-[slot=button-group]:rounded-md [&_svg:not([class*='size-'])]:size-3", "size-6 rounded-lg in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "icon-sm":
"size-8 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-md", "size-8 rounded-lg in-data-[slot=button-group]:rounded-lg",
"icon-lg": "size-10", "icon-lg": "size-10",
}, },
}, },

View File

@@ -32,7 +32,7 @@ const PICKER_HEADER_CLASS =
'grid min-h-8 grid-cols-[2rem_1fr_2rem] items-center gap-1 border-b border-border pb-2'; 'grid min-h-8 grid-cols-[2rem_1fr_2rem] items-center gap-1 border-b border-border pb-2';
const PICKER_NAV_BUTTON_CLASS = 'size-8 shrink-0 p-0'; const PICKER_NAV_BUTTON_CLASS = 'size-8 shrink-0 p-0';
const GRID_BUTTON_CLASS = const GRID_BUTTON_CLASS =
'h-9 w-full rounded-md font-medium text-foreground hover:bg-accent/60 hover:text-accent-foreground'; 'h-9 w-full rounded-lg font-medium text-foreground hover:bg-accent/60 hover:text-accent-foreground';
const GRID_BUTTON_ACTIVE_CLASS = const GRID_BUTTON_ACTIVE_CLASS =
'bg-muted text-foreground hover:bg-muted hover:text-foreground'; 'bg-muted text-foreground hover:bg-muted hover:text-foreground';
const GRID_BUTTON_DISABLED_CLASS = const GRID_BUTTON_DISABLED_CLASS =
@@ -134,7 +134,7 @@ function PickerNav({
{onLabelClick ? ( {onLabelClick ? (
<button <button
type="button" type="button"
className="rounded-sm px-2 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" className="rounded-lg px-2 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
onClick={onLabelClick} onClick={onLabelClick}
> >
{label} {label}
@@ -277,7 +277,7 @@ function CalendarMonthCaption({
<div className="flex items-center justify-center gap-1 text-sm font-semibold"> <div className="flex items-center justify-center gap-1 text-sm font-semibold">
<button <button
type="button" type="button"
className="rounded-sm px-1 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" className="rounded-lg px-1 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
onClick={() => { onClick={() => {
setPickerYear(year); setPickerYear(year);
setView('months'); setView('months');
@@ -287,7 +287,7 @@ function CalendarMonthCaption({
</button> </button>
<button <button
type="button" type="button"
className="rounded-sm px-1 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring" className="rounded-lg px-1 text-center text-sm font-semibold text-foreground hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
onClick={() => { onClick={() => {
setDecadeStart(Math.floor(year / 10) * 10); setDecadeStart(Math.floor(year / 10) * 10);
setPickerYear(year); setPickerYear(year);

View File

@@ -73,7 +73,7 @@ function Calendar({
defaultClassNames.dropdowns defaultClassNames.dropdowns
), ),
dropdown_root: cn( dropdown_root: cn(
"relative rounded-(--cell-radius)", "relative rounded-lg",
defaultClassNames.dropdown_root defaultClassNames.dropdown_root
), ),
dropdown: cn( dropdown: cn(
@@ -84,13 +84,13 @@ function Calendar({
"font-medium select-none", "font-medium select-none",
captionLayout === "label" captionLayout === "label"
? "text-sm" ? "text-sm"
: "flex items-center gap-1 rounded-(--cell-radius) text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground", : "flex items-center gap-1 rounded-lg text-sm [&>svg]:size-3.5 [&>svg]:text-muted-foreground",
defaultClassNames.caption_label defaultClassNames.caption_label
), ),
month_grid: cn("w-full border-collapse", defaultClassNames.month_grid), month_grid: cn("w-full border-collapse", defaultClassNames.month_grid),
weekdays: cn("flex", defaultClassNames.weekdays), weekdays: cn("flex", defaultClassNames.weekdays),
weekday: cn( weekday: cn(
"flex-1 rounded-(--cell-radius) text-[0.8rem] font-normal text-muted-foreground select-none", "flex-1 rounded-lg text-[0.8rem] font-normal text-muted-foreground select-none",
defaultClassNames.weekday defaultClassNames.weekday
), ),
week: cn("mt-2 flex w-full", defaultClassNames.week), week: cn("mt-2 flex w-full", defaultClassNames.week),
@@ -103,23 +103,23 @@ function Calendar({
defaultClassNames.week_number defaultClassNames.week_number
), ),
day: cn( day: cn(
"group/day relative aspect-square h-full w-full rounded-(--cell-radius) p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-r-(--cell-radius)", "group/day relative aspect-square h-full w-full rounded-lg p-0 text-center select-none [&:last-child[data-selected=true]_button]:rounded-lg",
props.showWeekNumber props.showWeekNumber
? "[&:nth-child(2)[data-selected=true]_button]:rounded-l-(--cell-radius)" ? "[&:nth-child(2)[data-selected=true]_button]:rounded-lg"
: "[&:first-child[data-selected=true]_button]:rounded-l-(--cell-radius)", : "[&:first-child[data-selected=true]_button]:rounded-lg",
defaultClassNames.day defaultClassNames.day
), ),
range_start: cn( range_start: cn(
"relative isolate z-0 rounded-l-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted", "relative isolate z-0 rounded-lg bg-muted after:absolute after:inset-y-0 after:right-0 after:w-4 after:bg-muted",
defaultClassNames.range_start defaultClassNames.range_start
), ),
range_middle: cn("rounded-none", defaultClassNames.range_middle), range_middle: cn("rounded-lg", defaultClassNames.range_middle),
range_end: cn( range_end: cn(
"relative isolate z-0 rounded-r-(--cell-radius) bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted", "relative isolate z-0 rounded-lg bg-muted after:absolute after:inset-y-0 after:left-0 after:w-4 after:bg-muted",
defaultClassNames.range_end defaultClassNames.range_end
), ),
today: cn( today: cn(
"rounded-(--cell-radius) bg-muted text-foreground data-[selected=true]:rounded-none", "rounded-lg bg-muted text-foreground data-[selected=true]:rounded-lg",
defaultClassNames.today defaultClassNames.today
), ),
outside: cn( outside: cn(
@@ -210,7 +210,7 @@ function CalendarDayButton({
data-range-end={modifiers.range_end} data-range-end={modifiers.range_end}
data-range-middle={modifiers.range_middle} data-range-middle={modifiers.range_middle}
className={cn( className={cn(
"relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-(--cell-radius) data-[range-end=true]:rounded-r-(--cell-radius) data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-none data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-(--cell-radius) data-[range-start=true]:rounded-l-(--cell-radius) data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70", "relative isolate z-10 flex aspect-square size-auto w-full min-w-(--cell-size) flex-col gap-1 border-0 leading-none font-normal group-data-[focused=true]/day:relative group-data-[focused=true]/day:z-10 group-data-[focused=true]/day:border-ring group-data-[focused=true]/day:ring-[3px] group-data-[focused=true]/day:ring-ring/50 data-[range-end=true]:rounded-lg data-[range-end=true]:rounded-lg data-[range-end=true]:bg-primary data-[range-end=true]:text-primary-foreground data-[range-middle=true]:rounded-lg data-[range-middle=true]:bg-muted data-[range-middle=true]:text-foreground data-[range-start=true]:rounded-lg data-[range-start=true]:rounded-lg data-[range-start=true]:bg-primary data-[range-start=true]:text-primary-foreground data-[selected-single=true]:bg-primary data-[selected-single=true]:text-primary-foreground dark:hover:text-foreground [&>span]:text-xs [&>span]:opacity-70",
defaultClassNames.day, defaultClassNames.day,
className className
)} )}

View File

@@ -12,7 +12,7 @@ function Card({
data-slot="card" data-slot="card"
data-size={size} data-size={size}
className={cn( className={cn(
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 [--card-spacing:--spacing(6)] has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl", "group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-lg bg-card py-(--card-spacing) text-sm text-card-foreground shadow-xs ring-1 ring-foreground/10 [--card-spacing:--spacing(6)] has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(4)] *:[img:first-child]:rounded-lg *:[img:last-child]:rounded-lg",
className className
)} )}
{...props} {...props}
@@ -25,7 +25,7 @@ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-header" data-slot="card-header"
className={cn( className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl 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)", "group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-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)",
className className
)} )}
{...props} {...props}
@@ -84,7 +84,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
<div <div
data-slot="card-footer" data-slot="card-footer"
className={cn( className={cn(
"flex items-center rounded-b-xl px-(--card-spacing) [.border-t]:pt-(--card-spacing)", "flex items-center rounded-lg px-(--card-spacing) [.border-t]:pt-(--card-spacing)",
className className
)} )}
{...props} {...props}

View File

@@ -222,7 +222,7 @@ function ChartTooltipContent({
!hideIndicator && ( !hideIndicator && (
<div <div
className={cn( className={cn(
"shrink-0 rounded-[2px] border-(--color-border) bg-(--color-bg)", "shrink-0 rounded-lg border-(--color-border) bg-(--color-bg)",
{ {
"h-2.5 w-2.5": indicator === "dot", "h-2.5 w-2.5": indicator === "dot",
"w-1": indicator === "line", "w-1": indicator === "line",
@@ -313,7 +313,7 @@ function ChartLegendContent({
<itemConfig.icon /> <itemConfig.icon />
) : ( ) : (
<div <div
className="h-2 w-2 shrink-0 rounded-[2px]" className="h-2 w-2 shrink-0 rounded-lg"
style={{ style={{
backgroundColor: item.color, backgroundColor: item.color,
}} }}

View File

@@ -115,7 +115,7 @@ function ComboboxContent({
<ComboboxPrimitive.Popup <ComboboxPrimitive.Popup
data-slot="combobox-content" data-slot="combobox-content"
data-chips={!!anchor} data-chips={!!anchor}
className={cn("group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )} className={cn("group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 *:data-[slot=input-group]:border-input/30 *:data-[slot=input-group]:bg-input/30 *:data-[slot=input-group]:shadow-none data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props} {...props}
/> />
</ComboboxPrimitive.Positioner> </ComboboxPrimitive.Positioner>
@@ -145,7 +145,7 @@ function ComboboxItem({
<ComboboxPrimitive.Item <ComboboxPrimitive.Item
data-slot="combobox-item" data-slot="combobox-item"
className={cn( className={cn(
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex w-full cursor-default items-center gap-2 rounded-lg py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@@ -226,7 +226,7 @@ function ComboboxChips({
<ComboboxPrimitive.Chips <ComboboxPrimitive.Chips
data-slot="combobox-chips" data-slot="combobox-chips"
className={cn( className={cn(
"flex min-h-9 flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1.5 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40", "flex min-h-9 flex-wrap items-center gap-1.5 rounded-lg border border-input bg-transparent bg-clip-padding px-2.5 py-1.5 text-sm shadow-xs transition-[color,box-shadow] focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50 has-aria-invalid:border-destructive has-aria-invalid:ring-3 has-aria-invalid:ring-destructive/20 has-data-[slot=combobox-chip]:px-1.5 dark:bg-input/30 dark:has-aria-invalid:border-destructive/50 dark:has-aria-invalid:ring-destructive/40",
className className
)} )}
{...props} {...props}
@@ -246,7 +246,7 @@ function ComboboxChip({
<ComboboxPrimitive.Chip <ComboboxPrimitive.Chip
data-slot="combobox-chip" data-slot="combobox-chip"
className={cn( className={cn(
"flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-sm bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0", "flex h-[calc(--spacing(5.5))] w-fit items-center justify-center gap-1 rounded-lg bg-muted px-1.5 text-xs font-medium whitespace-nowrap text-foreground has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0",
className className
)} )}
{...props} {...props}

View File

@@ -61,7 +61,7 @@ function DialogContent({
<DialogPrimitive.Content <DialogPrimitive.Content
data-slot="dialog-content" data-slot="dialog-content"
className={cn( className={cn(
"fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-xl bg-popover p-6 text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "fixed top-1/2 left-1/2 z-50 flex max-h-[calc(100vh-2rem)] w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-lg bg-popover text-sm text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}
@@ -89,7 +89,17 @@ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="dialog-header" data-slot="dialog-header"
className={cn("flex flex-col gap-2", className)} className={cn("flex shrink-0 flex-col gap-2 border-b px-6 py-4 pr-12", className)}
{...props}
/>
)
}
function DialogBody({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="dialog-body"
className={cn("min-h-0 flex-1 overflow-y-auto px-6 py-4", className)}
{...props} {...props}
/> />
) )
@@ -107,7 +117,7 @@ function DialogFooter({
<div <div
data-slot="dialog-footer" data-slot="dialog-footer"
className={cn( className={cn(
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", "flex shrink-0 flex-col-reverse gap-2 border-t px-6 py-4 sm:flex-row sm:justify-end",
className className
)} )}
{...props} {...props}
@@ -153,6 +163,7 @@ function DialogDescription({
export { export {
Dialog, Dialog,
DialogBody,
DialogClose, DialogClose,
DialogContent, DialogContent,
DialogDescription, DialogDescription,

View File

@@ -56,7 +56,7 @@ function DrawerContent({
<DrawerPrimitive.Content <DrawerPrimitive.Content
data-slot="drawer-content" data-slot="drawer-content"
className={cn( className={cn(
"group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-t-xl data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-r-xl data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-l-xl data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-b-xl data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm", "group/drawer-content fixed z-50 flex h-auto flex-col bg-popover text-sm text-popover-foreground data-[vaul-drawer-direction=bottom]:inset-x-0 data-[vaul-drawer-direction=bottom]:bottom-0 data-[vaul-drawer-direction=bottom]:mt-24 data-[vaul-drawer-direction=bottom]:max-h-[80vh] data-[vaul-drawer-direction=bottom]:rounded-lg data-[vaul-drawer-direction=bottom]:border-t data-[vaul-drawer-direction=left]:inset-y-0 data-[vaul-drawer-direction=left]:left-0 data-[vaul-drawer-direction=left]:w-3/4 data-[vaul-drawer-direction=left]:rounded-lg data-[vaul-drawer-direction=left]:border-r data-[vaul-drawer-direction=right]:inset-y-0 data-[vaul-drawer-direction=right]:right-0 data-[vaul-drawer-direction=right]:w-3/4 data-[vaul-drawer-direction=right]:rounded-lg data-[vaul-drawer-direction=right]:border-l data-[vaul-drawer-direction=top]:inset-x-0 data-[vaul-drawer-direction=top]:top-0 data-[vaul-drawer-direction=top]:mb-24 data-[vaul-drawer-direction=top]:max-h-[80vh] data-[vaul-drawer-direction=top]:rounded-lg data-[vaul-drawer-direction=top]:border-b data-[vaul-drawer-direction=left]:sm:max-w-sm data-[vaul-drawer-direction=right]:sm:max-w-sm",
className className
)} )}
{...props} {...props}

View File

@@ -43,7 +43,7 @@ function DropdownMenuContent({
data-slot="dropdown-menu-content" data-slot="dropdown-menu-content"
sideOffset={sideOffset} sideOffset={sideOffset}
align={align} align={align}
className={cn("z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:overflow-hidden data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )} className={cn("z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:overflow-hidden data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props} {...props}
/> />
</DropdownMenuPrimitive.Portal> </DropdownMenuPrimitive.Portal>
@@ -73,7 +73,7 @@ function DropdownMenuItem({
data-inset={inset} data-inset={inset}
data-variant={variant} data-variant={variant}
className={cn( className={cn(
"group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive", "group/dropdown-menu-item relative flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
className className
)} )}
{...props} {...props}
@@ -95,7 +95,7 @@ function DropdownMenuCheckboxItem({
data-slot="dropdown-menu-checkbox-item" data-slot="dropdown-menu-checkbox-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex cursor-default items-center gap-2 rounded-lg py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
checked={checked} checked={checked}
@@ -139,7 +139,7 @@ function DropdownMenuRadioItem({
data-slot="dropdown-menu-radio-item" data-slot="dropdown-menu-radio-item"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative flex cursor-default items-center gap-2 rounded-lg py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-8 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@@ -226,7 +226,7 @@ function DropdownMenuSubTrigger({
data-slot="dropdown-menu-sub-trigger" data-slot="dropdown-menu-sub-trigger"
data-inset={inset} data-inset={inset}
className={cn( className={cn(
"flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-8 data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@@ -244,7 +244,7 @@ function DropdownMenuSubContent({
return ( return (
<DropdownMenuPrimitive.SubContent <DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content" data-slot="dropdown-menu-sub-content"
className={cn("z-50 min-w-[96px] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )} className={cn("z-50 min-w-[96px] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className )}
{...props} {...props}
/> />
) )

View File

@@ -14,7 +14,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
data-slot="input-group" data-slot="input-group"
role="group" role="group"
className={cn( className={cn(
"group/input-group relative flex h-9 w-full min-w-0 items-center rounded-md border border-input shadow-xs transition-[color,box-shadow] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5", "group/input-group relative flex h-9 w-full min-w-0 items-center rounded-lg border border-input shadow-xs transition-[color,box-shadow] outline-none in-data-[slot=combobox-content]:focus-within:border-inherit in-data-[slot=combobox-content]:focus-within:ring-0 has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-3 has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-3 has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>textarea]:h-auto dark:bg-input/30 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40 has-[>[data-align=block-end]]:[&>input]:pt-3 has-[>[data-align=block-start]]:[&>input]:pb-3 has-[>[data-align=inline-end]]:[&>input]:pr-1.5 has-[>[data-align=inline-start]]:[&>input]:pl-1.5",
className className
)} )}
{...props} {...props}
@@ -23,7 +23,7 @@ function InputGroup({ className, ...props }: React.ComponentProps<"div">) {
} }
const inputGroupAddonVariants = cva( const inputGroupAddonVariants = cva(
"flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4", "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-lg [&>svg:not([class*='size-'])]:size-4",
{ {
variants: { variants: {
align: { align: {
@@ -70,10 +70,10 @@ const inputGroupButtonVariants = cva(
{ {
variants: { variants: {
size: { size: {
xs: "h-6 gap-1 rounded-[calc(var(--radius)-5px)] px-1.5 [&>svg:not([class*='size-'])]:size-3.5", xs: "h-6 gap-1 rounded-lg px-1.5 [&>svg:not([class*='size-'])]:size-3.5",
sm: "", sm: "",
"icon-xs": "icon-xs":
"size-6 rounded-[calc(var(--radius)-5px)] p-0 has-[>svg]:p-0", "size-6 rounded-lg p-0 has-[>svg]:p-0",
"icon-sm": "size-8 p-0 has-[>svg]:p-0", "icon-sm": "size-8 p-0 has-[>svg]:p-0",
}, },
}, },
@@ -122,7 +122,7 @@ function InputGroupInput({
<Input <Input
data-slot="input-group-control" data-slot="input-group-control"
className={cn( className={cn(
"flex-1 rounded-none border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent", "flex-1 rounded-lg border-0 bg-transparent shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
className className
)} )}
{...props} {...props}
@@ -138,7 +138,7 @@ function InputGroupTextarea({
<Textarea <Textarea
data-slot="input-group-control" data-slot="input-group-control"
className={cn( className={cn(
"flex-1 resize-none rounded-none border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent", "flex-1 resize-none rounded-lg border-0 bg-transparent py-2 shadow-none ring-0 focus-visible:ring-0 aria-invalid:ring-0 dark:bg-transparent",
className className
)} )}
{...props} {...props}

View File

@@ -8,7 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
type={type} type={type}
data-slot="input" data-slot="input"
className={cn( className={cn(
"h-9 w-full min-w-0 rounded-md border border-input bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", "h-9 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className className
)} )}
{...props} {...props}

View File

@@ -38,7 +38,7 @@ function PopoverContent({
align={align} align={align}
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-4 rounded-md bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "z-50 flex w-72 origin-(--radix-popover-content-transform-origin) flex-col gap-4 rounded-lg bg-popover p-4 text-sm text-popover-foreground shadow-md ring-1 ring-foreground/10 outline-hidden duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}

View File

@@ -18,7 +18,7 @@ function ScrollArea({
> >
<ScrollAreaPrimitive.Viewport <ScrollAreaPrimitive.Viewport
data-slot="scroll-area-viewport" data-slot="scroll-area-viewport"
className="size-full rounded-[inherit] transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1" className="size-full rounded-lg transition-[color,box-shadow] outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1"
> >
{children} {children}
</ScrollAreaPrimitive.Viewport> </ScrollAreaPrimitive.Viewport>

View File

@@ -44,7 +44,7 @@ function SelectTrigger({
data-slot="select-trigger" data-slot="select-trigger"
data-size={size} data-size={size}
className={cn( className={cn(
"flex w-fit items-center justify-between gap-1.5 rounded-md border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "flex w-fit items-center justify-between gap-1.5 rounded-lg border border-input bg-transparent py-2 pr-2 pl-2.5 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 data-placeholder:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-1.5 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className className
)} )}
{...props} {...props}
@@ -69,7 +69,7 @@ function SelectContent({
<SelectPrimitive.Content <SelectPrimitive.Content
data-slot="select-content" data-slot="select-content"
data-align-trigger={position === "item-aligned"} data-align-trigger={position === "item-aligned"}
className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )} className={cn("relative z-50 max-h-(--radix-select-content-available-height) min-w-36 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[align-trigger=true]:animate-none data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", position ==="popper"&&"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1", className )}
position={position} position={position}
align={align} align={align}
{...props} {...props}
@@ -112,7 +112,7 @@ function SelectItem({
<SelectPrimitive.Item <SelectPrimitive.Item
data-slot="select-item" data-slot="select-item"
className={cn( className={cn(
"relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2", "relative flex w-full cursor-default items-center gap-2 rounded-lg py-1.5 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
className className
)} )}
{...props} {...props}

View File

@@ -306,7 +306,7 @@ function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
<main <main
data-slot="sidebar-inset" data-slot="sidebar-inset"
className={cn( className={cn(
"relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2", "relative flex w-full flex-1 flex-col bg-background md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-lg md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
className className
)} )}
{...props} {...props}
@@ -401,7 +401,7 @@ function SidebarGroupLabel({
data-slot="sidebar-group-label" data-slot="sidebar-group-label"
data-sidebar="group-label" data-sidebar="group-label"
className={cn( className={cn(
"flex h-8 shrink-0 items-center rounded-md px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0", "flex h-8 shrink-0 items-center rounded-lg px-2 text-xs font-medium text-sidebar-foreground/70 ring-sidebar-ring outline-hidden transition-[margin,opacity] duration-200 ease-linear group-data-[collapsible=icon]:-mt-8 group-data-[collapsible=icon]:opacity-0 focus-visible:ring-2 [&>svg]:size-4 [&>svg]:shrink-0",
className className
)} )}
{...props} {...props}
@@ -421,7 +421,7 @@ function SidebarGroupAction({
data-slot="sidebar-group-action" data-slot="sidebar-group-action"
data-sidebar="group-action" data-sidebar="group-action"
className={cn( className={cn(
"absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0", "absolute top-3.5 right-3 flex aspect-square w-5 items-center justify-center rounded-lg p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
className className
)} )}
{...props} {...props}
@@ -466,7 +466,7 @@ function SidebarMenuItem({ className, ...props }: React.ComponentProps<"li">) {
} }
const sidebarMenuButtonVariants = cva( const sidebarMenuButtonVariants = cva(
"peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate", "peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-lg p-2 text-left text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pr-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
{ {
variants: { variants: {
variant: { variant: {
@@ -553,7 +553,7 @@ function SidebarMenuAction({
data-slot="sidebar-menu-action" data-slot="sidebar-menu-action"
data-sidebar="menu-action" data-sidebar="menu-action"
className={cn( className={cn(
"absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0", "absolute top-1.5 right-1 flex aspect-square w-5 items-center justify-center rounded-lg p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
showOnHover && showOnHover &&
"group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0", "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0",
className className
@@ -572,7 +572,7 @@ function SidebarMenuBadge({
data-slot="sidebar-menu-badge" data-slot="sidebar-menu-badge"
data-sidebar="menu-badge" data-sidebar="menu-badge"
className={cn( className={cn(
"pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground", "pointer-events-none absolute right-1 flex h-5 min-w-5 items-center justify-center rounded-lg px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground",
className className
)} )}
{...props} {...props}
@@ -596,12 +596,12 @@ function SidebarMenuSkeleton({
<div <div
data-slot="sidebar-menu-skeleton" data-slot="sidebar-menu-skeleton"
data-sidebar="menu-skeleton" data-sidebar="menu-skeleton"
className={cn("flex h-8 items-center gap-2 rounded-md px-2", className)} className={cn("flex h-8 items-center gap-2 rounded-lg px-2", className)}
{...props} {...props}
> >
{showIcon && ( {showIcon && (
<Skeleton <Skeleton
className="size-4 rounded-md" className="size-4 rounded-lg"
data-sidebar="menu-skeleton-icon" data-sidebar="menu-skeleton-icon"
/> />
)} )}
@@ -666,7 +666,7 @@ function SidebarMenuSubButton({
data-size={size} data-size={size}
data-active={isActive} data-active={isActive}
className={cn( className={cn(
"flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground", "flex h-7 min-w-0 -translate-x-px items-center gap-2 overflow-hidden rounded-lg px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
className className
)} )}
{...props} {...props}

View File

@@ -4,7 +4,7 @@ function Skeleton({ className, ...props }: React.ComponentProps<"div">) {
return ( return (
<div <div
data-slot="skeleton" data-slot="skeleton"
className={cn("animate-pulse rounded-md bg-muted", className)} className={cn("animate-pulse rounded-lg bg-muted", className)}
{...props} {...props}
/> />
) )

View File

@@ -25,7 +25,7 @@ function Tabs({
} }
const tabsListVariants = cva( const tabsListVariants = cva(
"group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-9 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-none", "group/tabs-list inline-flex w-fit items-center justify-center rounded-lg p-[3px] text-muted-foreground group-data-horizontal/tabs:h-9 group-data-vertical/tabs:h-fit group-data-vertical/tabs:flex-col data-[variant=line]:rounded-lg",
{ {
variants: { variants: {
variant: { variant: {
@@ -63,7 +63,7 @@ function TabsTrigger({
<TabsPrimitive.Trigger <TabsPrimitive.Trigger
data-slot="tabs-trigger" data-slot="tabs-trigger"
className={cn( className={cn(
"relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", "relative inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-lg border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap text-foreground/60 transition-all group-data-vertical/tabs:w-full group-data-vertical/tabs:justify-start hover:text-foreground focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 focus-visible:outline-1 focus-visible:outline-ring disabled:pointer-events-none disabled:opacity-50 has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 dark:text-muted-foreground dark:hover:text-foreground group-data-[variant=default]/tabs-list:data-active:shadow-sm group-data-[variant=line]/tabs-list:data-active:shadow-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
"group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent", "group-data-[variant=line]/tabs-list:bg-transparent group-data-[variant=line]/tabs-list:data-active:bg-transparent dark:group-data-[variant=line]/tabs-list:data-active:border-transparent dark:group-data-[variant=line]/tabs-list:data-active:bg-transparent",
"data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground", "data-active:bg-background data-active:text-foreground dark:data-active:border-input dark:data-active:bg-input/30 dark:data-active:text-foreground",
"after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100", "after:absolute after:bg-foreground after:opacity-0 after:transition-opacity group-data-horizontal/tabs:after:inset-x-0 group-data-horizontal/tabs:after:bottom-[-5px] group-data-horizontal/tabs:after:h-0.5 group-data-vertical/tabs:after:inset-y-0 group-data-vertical/tabs:after:-right-1 group-data-vertical/tabs:after:w-0.5 group-data-[variant=line]/tabs-list:data-active:after:opacity-100",

View File

@@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
<textarea <textarea
data-slot="textarea" data-slot="textarea"
className={cn( className={cn(
"flex field-sizing-content min-h-16 w-full rounded-md border border-input bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", "flex field-sizing-content min-h-16 w-full rounded-lg border border-input bg-transparent px-2.5 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className className
)} )}
{...props} {...props}

View File

@@ -42,13 +42,13 @@ function TooltipContent({
data-slot="tooltip-content" data-slot="tooltip-content"
sideOffset={sideOffset} sideOffset={sideOffset}
className={cn( className={cn(
"z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-md bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-sm data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", "z-50 inline-flex w-fit max-w-xs origin-(--radix-tooltip-content-transform-origin) items-center gap-1.5 rounded-lg bg-foreground px-3 py-1.5 text-xs text-background has-data-[slot=kbd]:pr-1.5 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 **:data-[slot=kbd]:relative **:data-[slot=kbd]:isolate **:data-[slot=kbd]:z-50 **:data-[slot=kbd]:rounded-lg data-[state=delayed-open]:animate-in data-[state=delayed-open]:fade-in-0 data-[state=delayed-open]:zoom-in-95 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
className className
)} )}
{...props} {...props}
> >
{children} {children}
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" /> <TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-lg bg-foreground fill-foreground" />
</TooltipPrimitive.Content> </TooltipPrimitive.Content>
</TooltipPrimitive.Portal> </TooltipPrimitive.Portal>
) )

View File

@@ -43,7 +43,7 @@ function TreeCheckbox({
type="checkbox" type="checkbox"
checked={checked} checked={checked}
onChange={(event) => onChange(event.target.checked)} onChange={(event) => onChange(event.target.checked)}
className="size-4 shrink-0 rounded border-border accent-primary" className="size-4 shrink-0 rounded-lg border-border accent-primary"
/> />
); );
} }
@@ -70,7 +70,7 @@ function CheckboxTreeNode<TItem extends TreeItem>({
return ( return (
<li className="space-y-2"> <li className="space-y-2">
<div className="rounded-md border border-border/60 bg-background px-3 py-2"> <div className="rounded-lg border border-border/60 bg-background px-3 py-2">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
{hasChildren ? ( {hasChildren ? (
<button <button
@@ -79,7 +79,7 @@ function CheckboxTreeNode<TItem extends TreeItem>({
aria-label={ aria-label={
isExpanded ? `Collapse ${item.label}` : `Expand ${item.label}` isExpanded ? `Collapse ${item.label}` : `Expand ${item.label}`
} }
className="inline-flex size-5 shrink-0 items-center justify-center rounded text-muted-foreground transition hover:bg-muted hover:text-foreground" className="inline-flex size-5 shrink-0 items-center justify-center rounded-lg text-muted-foreground transition hover:bg-muted hover:text-foreground"
> >
<ChevronDown <ChevronDown
className={`size-4 transition-transform ${isExpanded ? '' : '-rotate-90'}`} className={`size-4 transition-transform ${isExpanded ? '' : '-rotate-90'}`}
@@ -155,7 +155,7 @@ export function CheckboxTree<TItem extends TreeItem>({
if (items.length === 0) { if (items.length === 0) {
return ( return (
<div className="rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground"> <div className="rounded-lg border border-dashed p-6 text-center text-sm text-muted-foreground">
{emptyMessage} {emptyMessage}
</div> </div>
); );

View File

@@ -47,17 +47,17 @@ export default function AnnotatedDetectionImage({
{!detection ? ( {!detection ? (
<div className="flex flex-col items-center gap-2 text-muted-foreground"> <div className="flex flex-col items-center gap-2 text-muted-foreground">
<ImageIcon className="size-7" /> <ImageIcon className="size-7" />
<p className="text-sm">Select a detection to view its image.</p> <p>Select a detection to view its image.</p>
</div> </div>
) : isLoading ? ( ) : isLoading ? (
<div className="flex flex-col items-center gap-2 text-muted-foreground"> <div className="flex flex-col items-center gap-2 text-muted-foreground">
<Loader2 className="size-7 animate-spin text-primary" /> <Loader2 className="size-7 animate-spin text-primary" />
<p className="text-sm">Loading detection image...</p> <p>Loading detection image...</p>
</div> </div>
) : isError || !objectUrl ? ( ) : isError || !objectUrl ? (
<div className="flex flex-col items-center gap-2 text-destructive"> <div className="flex flex-col items-center gap-2 text-destructive">
<AlertTriangle className="size-7" /> <AlertTriangle className="size-7" />
<p className="text-sm">Failed to load the detection image.</p> <p>Failed to load the detection image.</p>
</div> </div>
) : ( ) : (
<> <>

View File

@@ -45,7 +45,7 @@ export default function CurrentDetectionBar({
</div> </div>
{coordinates ? ( {coordinates ? (
<div className="flex max-w-full items-center gap-2 rounded-md bg-muted/40 px-3 py-1.5 text-xs text-muted-foreground"> <div className="flex max-w-full items-center gap-2 rounded-lg bg-muted/40 px-3 py-1.5 text-xs text-muted-foreground">
<MapPin className="size-3.5 shrink-0" /> <MapPin className="size-3.5 shrink-0" />
<span className="shrink-0 font-medium">Location</span> <span className="shrink-0 font-medium">Location</span>
<span className="truncate font-mono font-semibold text-foreground"> <span className="truncate font-mono font-semibold text-foreground">
@@ -63,11 +63,11 @@ export default function CurrentDetectionBar({
return ( return (
<div <div
key={item.class_name} key={item.class_name}
className="flex items-center justify-between gap-3 rounded-md bg-muted/25 px-3 py-2.5 ring-1 ring-border/60" className="flex items-center justify-between gap-3 rounded-lg bg-muted/25 px-3 py-2.5 ring-1 ring-border/60"
> >
<div className="flex min-w-0 items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
<span <span
className={`flex size-8 shrink-0 items-center justify-center rounded-md ${visual.iconClassName}`} className={`flex size-8 shrink-0 items-center justify-center rounded-lg ${visual.iconClassName}`}
> >
<Icon className="size-4" /> <Icon className="size-4" />
</span> </span>
@@ -84,9 +84,9 @@ export default function CurrentDetectionBar({
); );
})} })}
<div className="flex items-center justify-between gap-3 rounded-md bg-muted/25 px-3 py-2.5 ring-1 ring-border/60"> <div className="flex items-center justify-between gap-3 rounded-lg bg-muted/25 px-3 py-2.5 ring-1 ring-border/60">
<div className="flex min-w-0 items-center gap-3"> <div className="flex min-w-0 items-center gap-3">
<span className="flex size-8 shrink-0 items-center justify-center rounded-md bg-green-500/10 text-green-500"> <span className="flex size-8 shrink-0 items-center justify-center rounded-lg bg-green-500/10 text-green-500">
<Activity className="size-4" /> <Activity className="size-4" />
</span> </span>
<p className="truncate text-xs font-semibold uppercase tracking-wide text-muted-foreground"> <p className="truncate text-xs font-semibold uppercase tracking-wide text-muted-foreground">

View File

@@ -33,26 +33,26 @@ function DetectionLogThumbnail({
if (!url) { if (!url) {
return ( return (
<div className="flex size-20 shrink-0 items-center justify-center rounded-md border border-dashed border-border/70 bg-muted/20 text-muted-foreground/60"> <div className="flex size-20 shrink-0 items-center justify-center rounded-lg border border-dashed border-border/70 bg-muted/20 text-muted-foreground/60">
<ImageIcon className="size-5" /> <ImageIcon className="size-5" />
</div> </div>
); );
} }
if (isLoading) { if (isLoading) {
return <Skeleton className="size-20 shrink-0 rounded-md" />; return <Skeleton className="size-20 shrink-0 rounded-lg" />;
} }
if (isError || !objectUrl) { if (isError || !objectUrl) {
return ( return (
<div className="flex size-20 shrink-0 items-center justify-center rounded-md border border-dashed bg-muted/20 text-[10px] text-muted-foreground"> <div className="flex size-20 shrink-0 items-center justify-center rounded-lg border border-dashed bg-muted/20 text-[10px] text-muted-foreground">
N/A N/A
</div> </div>
); );
} }
return ( return (
<div className="size-20 shrink-0 overflow-hidden rounded-md border bg-muted/20"> <div className="size-20 shrink-0 overflow-hidden rounded-lg border bg-muted/20">
<img <img
src={objectUrl} src={objectUrl}
alt={label} alt={label}
@@ -102,7 +102,7 @@ const DetectionLogs = ({ logs, activeLogId, onSelect }: DetectionLogsProps) => {
type="button" type="button"
onClick={() => onSelect(log)} onClick={() => onSelect(log)}
className={cn( className={cn(
'w-full rounded-md border bg-card p-3 text-left transition-colors hover:border-primary', 'w-full rounded-lg border bg-card p-3 text-left transition-colors hover:border-primary',
isActive && 'border-primary bg-primary/5', isActive && 'border-primary bg-primary/5',
)} )}
> >

View File

@@ -67,7 +67,7 @@ export default function ResultStatsGrid({ data }: ResultStatsGridProps) {
return ( return (
<div <div
key={stat.label} key={stat.label}
className="rounded-xl bg-muted/40 p-4 transition-colors hover:bg-muted/60" className="rounded-lg bg-muted/40 p-4 transition-colors hover:bg-muted/60"
> >
<div className="flex items-center justify-between gap-3"> <div className="flex items-center justify-between gap-3">
<p <p

View File

@@ -61,7 +61,7 @@ export default function VideoPlayerSection({ data }: VideoPlayerSectionProps) {
<Card className="overflow-hidden"> <Card className="overflow-hidden">
<CardHeader className="border-b pb-4"> <CardHeader className="border-b pb-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="rounded bg-secondary p-2"> <div className="rounded-lg bg-secondary p-2">
<Film className="h-5 w-5 text-primary" /> <Film className="h-5 w-5 text-primary" />
</div> </div>
<div> <div>