Migración a Base UI: Quitar atributo asChild
This commit is contained in:
parent
85556056fd
commit
909a576f08
@ -233,7 +233,7 @@ export default function ShadcnShowcasePage() {
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<AlertDialog>
|
||||
<AlertDialogTrigger asChild>
|
||||
<AlertDialogTrigger>
|
||||
<Button variant="outline">
|
||||
<span className="hidden md:block">Alert Dialog</span>
|
||||
<span className="block md:hidden">Dialog</span>
|
||||
@ -256,7 +256,7 @@ export default function ShadcnShowcasePage() {
|
||||
<ButtonGroup>
|
||||
<Button variant="outline">Button Group</Button>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<Button size="icon" variant="outline">
|
||||
<ChevronUpIcon />
|
||||
</Button>
|
||||
|
||||
@ -100,7 +100,7 @@ export const UpdateCommitButtonGroup = ({
|
||||
{/* Menú de acciones adicionales */}
|
||||
{hasSecondaryActions && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<Button className="px-2" disabled={computedDisabled} size="sm" variant="ghost">
|
||||
<MoreHorizontalIcon className="h-4 w-4" />
|
||||
<span className="sr-only">Más acciones</span>
|
||||
|
||||
@ -343,7 +343,7 @@ export function useIssuedInvoicesGridColumns(
|
||||
{/* Descargar en PDF */}
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
className={"size-8"}
|
||||
disabled={isPDFLoading || !isCompleted}
|
||||
@ -369,7 +369,7 @@ export function useIssuedInvoicesGridColumns(
|
||||
{/** biome-ignore lint/suspicious/noSelfCompare: <Desactivado por ahora> */}
|
||||
{false !== false && (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<Button
|
||||
aria-label={t("common.more_actions")}
|
||||
className="cursor-pointer text-muted-foreground hover:text-primary"
|
||||
|
||||
@ -21,7 +21,7 @@ export const VerifactuStatusBadge = ({ status, className }: VerifactuStatusBadge
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Badge
|
||||
className={cn(
|
||||
getVerifactuRecordStatusColor(normalizedStatus),
|
||||
|
||||
@ -725,7 +725,7 @@ export const CreateCustomerInvoiceEditForm = ({
|
||||
<div className="space-y-2">
|
||||
<Label>Fecha de Emisión</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<PopoverTrigger>
|
||||
<Button className="w-full justify-start text-left font-normal" variant="outline">
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{format(invoiceDate, "PPP", { locale: es })}
|
||||
@ -745,7 +745,7 @@ export const CreateCustomerInvoiceEditForm = ({
|
||||
<div className="space-y-2">
|
||||
<Label>Fecha de Operación</Label>
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<PopoverTrigger>
|
||||
<Button className="w-full justify-start text-left font-normal" variant="outline">
|
||||
<CalendarIcon className="mr-2 h-4 w-4" />
|
||||
{format(operationDate, "PPP", { locale: es })}
|
||||
|
||||
@ -101,7 +101,7 @@ export function useProformasGridColumns(
|
||||
{isIssued && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
asChild
|
||||
className="size-6 text-foreground hover:text-primary"
|
||||
@ -240,7 +240,7 @@ export function useProformasGridColumns(
|
||||
{!isIssued && actionHandlers.onEditClick && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
className="size-8 cursor-pointer"
|
||||
onClick={() => actionHandlers.onEditClick?.(proforma)}
|
||||
@ -260,7 +260,7 @@ export function useProformasGridColumns(
|
||||
{!isIssued && availableTransitions.length && actionHandlers.onChangeStatusClick && (
|
||||
<TooltipProvider key={availableTransitions[0]}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
className="size-8 cursor-pointer"
|
||||
onClick={() =>
|
||||
@ -284,7 +284,7 @@ export function useProformasGridColumns(
|
||||
{!isIssued && isApproved && actionHandlers.onIssueClick && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
className="size-8 cursor-pointer"
|
||||
onClick={() => actionHandlers.onIssueClick?.(proforma)}
|
||||
@ -303,7 +303,7 @@ export function useProformasGridColumns(
|
||||
{!isIssued && actionHandlers.onDeleteClick && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
className="size-8 text-destructive hover:text-destructive cursor-pointer"
|
||||
onClick={(e) => {
|
||||
|
||||
@ -21,7 +21,7 @@ export const ProformaStatusBadge = ({ status, className }: ProformaStatusBadgePr
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Badge
|
||||
className={cn(getProformaStatusColor(normalizedStatus), "font-semibold", className)}
|
||||
variant={getProformaStatusButtonVariant(normalizedStatus)}
|
||||
|
||||
@ -73,7 +73,7 @@ export const CustomerSummaryPanel = ({
|
||||
|
||||
return (
|
||||
<RightPanel
|
||||
className={cn("bg-transparent", className)}
|
||||
className={cn("bg-transparent shadow", className)}
|
||||
headerActions={
|
||||
<>
|
||||
{customer ? (
|
||||
|
||||
@ -164,7 +164,7 @@ export function useCustomersGridColumns(
|
||||
</Button>
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<Button aria-label={t("pages.list.actions.more")} size="icon" variant="ghost">
|
||||
<MoreHorizontalIcon className="size-4" />
|
||||
</Button>
|
||||
|
||||
@ -30,7 +30,7 @@ export const CustomerStatusBadge = ({ status }: { status: string }) => {
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<div className={cn("flex-none rounded-full p-1", statusClass)}>
|
||||
<div className="size-2 rounded-full bg-current" />
|
||||
</div>
|
||||
|
||||
@ -31,7 +31,7 @@ export const HelpButton = ({
|
||||
return (
|
||||
<div className={`flex items-baseline justify-center mr-4 font-medium ${className}`}>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<DialogTrigger>
|
||||
<Button className="inline-flex items-center font-medium group" variant="link">
|
||||
<span className="underline-offset-4 group-hover:underline">{buttonText}</span>
|
||||
<HelpCircleIcon className="w-4 h-4 ml-1 text-muted-foreground" />
|
||||
@ -44,7 +44,7 @@ export const HelpButton = ({
|
||||
<ScrollArea className="grid gap-4 py-2">
|
||||
{content}
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<DialogClose>
|
||||
<Button type="button">{t("common.close")}</Button>
|
||||
</DialogClose>
|
||||
</DialogFooter>
|
||||
|
||||
@ -31,7 +31,7 @@ export function DataTableColumnHeader<TData, TValue>({
|
||||
return (
|
||||
<div className={cn("flex items-center gap-2 ", className)}>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<Button
|
||||
//className="data-[state=open]:bg-accent -ml-4 h-8 text-xs text-muted-foreground font-semibold text-nowrap cursor-pointer"
|
||||
className="-ml-3 h-8 data-[state=open]:bg-accent cursor-pointer text-foreground"
|
||||
|
||||
@ -1,28 +1,31 @@
|
||||
import { Column } from "@tanstack/react-table"
|
||||
import { Check, PlusCircleIcon } from "lucide-react"
|
||||
import * as React from "react"
|
||||
|
||||
import {
|
||||
Badge, Button, Command,
|
||||
Badge,
|
||||
Button,
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandInput,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
CommandSeparator, Popover,
|
||||
CommandSeparator,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger, Separator
|
||||
} from '@repo/shadcn-ui/components'
|
||||
import { cn } from '@repo/shadcn-ui/lib/utils'
|
||||
PopoverTrigger,
|
||||
Separator,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import type { Column } from "@tanstack/react-table";
|
||||
import { Check, PlusCircleIcon } from "lucide-react";
|
||||
import type * as React from "react";
|
||||
|
||||
interface DataTableFacetedFilterProps<TData, TValue> {
|
||||
column?: Column<TData, TValue>
|
||||
title?: string
|
||||
column?: Column<TData, TValue>;
|
||||
title?: string;
|
||||
options: {
|
||||
label: string
|
||||
value: string
|
||||
icon?: React.ComponentType<{ className?: string }>
|
||||
}[]
|
||||
label: string;
|
||||
value: string;
|
||||
icon?: React.ComponentType<{ className?: string }>;
|
||||
}[];
|
||||
}
|
||||
|
||||
export function DataTableFacetedFilter<TData, TValue>({
|
||||
@ -30,30 +33,24 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
title,
|
||||
options,
|
||||
}: DataTableFacetedFilterProps<TData, TValue>) {
|
||||
const facets = column?.getFacetedUniqueValues()
|
||||
const selectedValues = new Set(column?.getFilterValue() as string[])
|
||||
const facets = column?.getFacetedUniqueValues();
|
||||
const selectedValues = new Set(column?.getFilterValue() as string[]);
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button type="button" variant="outline" size="sm" className="h-8 border-dashed">
|
||||
<PopoverTrigger>
|
||||
<Button className="h-8 border-dashed" size="sm" type="button" variant="outline">
|
||||
<PlusCircleIcon />
|
||||
{title}
|
||||
{selectedValues?.size > 0 && (
|
||||
<>
|
||||
<Separator orientation="vertical" className="mx-2 h-4" />
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="rounded-sm px-1 font-normal lg:hidden"
|
||||
>
|
||||
<Separator className="mx-2 h-4" orientation="vertical" />
|
||||
<Badge className="rounded-sm px-1 font-normal lg:hidden" variant="secondary">
|
||||
{selectedValues.size}
|
||||
</Badge>
|
||||
<div className="hidden gap-1 lg:flex">
|
||||
{selectedValues.size > 2 ? (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="rounded-sm px-1 font-normal"
|
||||
>
|
||||
<Badge className="rounded-sm px-1 font-normal" variant="secondary">
|
||||
{selectedValues.size} selected
|
||||
</Badge>
|
||||
) : (
|
||||
@ -61,9 +58,9 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
.filter((option) => selectedValues.has(option.value))
|
||||
.map((option) => (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
key={option.value}
|
||||
className="rounded-sm px-1 font-normal"
|
||||
key={option.value}
|
||||
variant="secondary"
|
||||
>
|
||||
{option.label}
|
||||
</Badge>
|
||||
@ -74,27 +71,25 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[200px] p-0" align="start">
|
||||
<PopoverContent align="start" className="w-[200px] p-0">
|
||||
<Command>
|
||||
<CommandInput placeholder={title} />
|
||||
<CommandList>
|
||||
<CommandEmpty>No results found.</CommandEmpty>
|
||||
<CommandGroup>
|
||||
{options.map((option) => {
|
||||
const isSelected = selectedValues.has(option.value)
|
||||
const isSelected = selectedValues.has(option.value);
|
||||
return (
|
||||
<CommandItem
|
||||
key={option.value}
|
||||
onSelect={() => {
|
||||
if (isSelected) {
|
||||
selectedValues.delete(option.value)
|
||||
selectedValues.delete(option.value);
|
||||
} else {
|
||||
selectedValues.add(option.value)
|
||||
selectedValues.add(option.value);
|
||||
}
|
||||
const filterValues = Array.from(selectedValues)
|
||||
column?.setFilterValue(
|
||||
filterValues.length ? filterValues : undefined
|
||||
)
|
||||
const filterValues = Array.from(selectedValues);
|
||||
column?.setFilterValue(filterValues.length ? filterValues : undefined);
|
||||
}}
|
||||
>
|
||||
<div
|
||||
@ -107,9 +102,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
>
|
||||
<Check className="text-primary-foreground size-3.5" />
|
||||
</div>
|
||||
{option.icon && (
|
||||
<option.icon className="text-muted-foreground size-4" />
|
||||
)}
|
||||
{option.icon && <option.icon className="text-muted-foreground size-4" />}
|
||||
<span>{option.label}</span>
|
||||
{facets?.get(option.value) && (
|
||||
<span className="text-muted-foreground ml-auto flex size-4 items-center justify-center font-mono text-xs">
|
||||
@ -117,7 +110,7 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
</span>
|
||||
)}
|
||||
</CommandItem>
|
||||
)
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
{selectedValues.size > 0 && (
|
||||
@ -125,8 +118,8 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
<CommandSeparator />
|
||||
<CommandGroup>
|
||||
<CommandItem
|
||||
onSelect={() => column?.setFilterValue(undefined)}
|
||||
className="justify-center text-center"
|
||||
onSelect={() => column?.setFilterValue(undefined)}
|
||||
>
|
||||
Clear filters
|
||||
</CommandItem>
|
||||
@ -137,5 +130,5 @@ export function DataTableFacetedFilter<TData, TValue>({
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -113,7 +113,7 @@ export function DataTableToolbar<TData>({
|
||||
|
||||
{!readOnly && meta?.bulkOps?.moveSelectedUp && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
aria-label={t("components.datatable.actions.move_up")}
|
||||
onClick={handleMoveSelectedUp}
|
||||
@ -130,7 +130,7 @@ export function DataTableToolbar<TData>({
|
||||
|
||||
{!readOnly && meta?.bulkOps?.moveSelectedDown && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button
|
||||
aria-label={t("components.datatable.actions.move_down")}
|
||||
onClick={handleMoveSelectedDown}
|
||||
@ -164,7 +164,7 @@ export function DataTableToolbar<TData>({
|
||||
<Separator className="h-6 mx-1 bg-muted/50" orientation="vertical" />
|
||||
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<TooltipTrigger>
|
||||
<Button onClick={handleClearSelection} size="sm" type="button" variant="outline">
|
||||
<ScanIcon aria-hidden="true" className="size-4 mr-1" />
|
||||
<span>{t("components.datatable.actions.clear_selection")}</span>
|
||||
|
||||
@ -18,7 +18,7 @@ export function DataTableViewOptions<TData>({ table }: { table: Table<TData> })
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<Button
|
||||
className="ml-auto hidden h-8 lg:flex gap-2 items"
|
||||
size="sm"
|
||||
|
||||
@ -1,9 +1,5 @@
|
||||
import {
|
||||
Avatar,
|
||||
AvatarFallback,
|
||||
AvatarImage,
|
||||
} from "@/registry/new-york-v4/ui/avatar"
|
||||
import { Button } from "@/registry/new-york-v4/ui/button"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/registry/new-york-v4/ui/avatar";
|
||||
import { Button } from "@/registry/new-york-v4/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -13,26 +9,24 @@ import {
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/registry/new-york-v4/ui/dropdown-menu"
|
||||
} from "@/registry/new-york-v4/ui/dropdown-menu";
|
||||
|
||||
export function UserNav() {
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="button" variant="ghost" className="relative h-8 w-8 rounded-full">
|
||||
<DropdownMenuTrigger>
|
||||
<Button className="relative h-8 w-8 rounded-full" type="button" variant="ghost">
|
||||
<Avatar className="h-9 w-9">
|
||||
<AvatarImage src="/avatars/03.png" alt="@shadcn" />
|
||||
<AvatarImage alt="@shadcn" src="/avatars/03.png" />
|
||||
<AvatarFallback>SC</AvatarFallback>
|
||||
</Avatar>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56" align="end" forceMount>
|
||||
<DropdownMenuContent align="end" className="w-56" forceMount>
|
||||
<DropdownMenuLabel className="font-normal">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<p className="text-sm leading-none font-medium">shadcn</p>
|
||||
<p className="text-muted-foreground text-xs leading-none">
|
||||
m@example.com
|
||||
</p>
|
||||
<p className="text-muted-foreground text-xs leading-none">m@example.com</p>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
@ -58,5 +52,5 @@ export function UserNav() {
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -177,7 +177,7 @@ export function DatePickerInputComp<TFormValues extends FieldValues = FieldValue
|
||||
triggerButton={
|
||||
disabled || readOnly ? null : (
|
||||
<Popover onOpenChange={setOpen} open={open}>
|
||||
<PopoverTrigger asChild>
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
aria-controls={popoverId}
|
||||
aria-expanded={open}
|
||||
|
||||
@ -151,7 +151,7 @@ export const MultiSelectField = <T extends FieldValues>({
|
||||
|
||||
<FormControl>
|
||||
<Popover modal={modalPopover} onOpenChange={setIsPopoverOpen} open={isPopoverOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
aria-expanded={isPopoverOpen}
|
||||
aria-invalid={fieldState.invalid}
|
||||
|
||||
@ -1,23 +1,12 @@
|
||||
import {
|
||||
Sidebar,
|
||||
SidebarContent,
|
||||
SidebarFooter,
|
||||
SidebarHeader,
|
||||
SidebarMenu,
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader } from "@repo/shadcn-ui/components";
|
||||
import {
|
||||
CameraIcon,
|
||||
CircleIcon,
|
||||
FileBoxIcon,
|
||||
HomeIcon,
|
||||
LogInIcon,
|
||||
Package2Icon,
|
||||
PackageIcon,
|
||||
SettingsIcon,
|
||||
FileCheckIcon,
|
||||
FileTextIcon,
|
||||
GalleryVerticalEndIcon,
|
||||
TextCursorIcon,
|
||||
UserPlusIcon,
|
||||
UsersIcon,
|
||||
} from "lucide-react";
|
||||
import type * as React from "react";
|
||||
@ -25,48 +14,66 @@ import type * as React from "react";
|
||||
import { NavMain } from "./nav-main.tsx";
|
||||
import { NavSecondary } from "./nav-secondary.tsx";
|
||||
import { NavUser } from "./nav-user.tsx";
|
||||
import { TeamSwitcher } from "./team-switcher.tsx";
|
||||
|
||||
const data = {
|
||||
teams: [
|
||||
{
|
||||
name: "Acme Inc",
|
||||
logo: GalleryVerticalEndIcon,
|
||||
plan: "Enterprise",
|
||||
},
|
||||
],
|
||||
user: {
|
||||
name: "Toby Belhome",
|
||||
email: "m@example.com",
|
||||
avatar: "https://www.tobybelhome.com/toby-belhome.png",
|
||||
},
|
||||
navMain: [
|
||||
{
|
||||
title: "Dashboard",
|
||||
url: "/dashboard",
|
||||
/*{
|
||||
title: "Inicio",
|
||||
url: "/",
|
||||
icon: HomeIcon,
|
||||
},
|
||||
isActive: true,
|
||||
},*/
|
||||
{
|
||||
title: "Users",
|
||||
url: "/dashboard/users",
|
||||
title: "Clientes",
|
||||
icon: UsersIcon,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: "Listado de clientes",
|
||||
url: "/customers",
|
||||
},
|
||||
/*{
|
||||
title: "Añadir un cliente",
|
||||
url: "/customers/create",
|
||||
},*/
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Settings",
|
||||
url: "/dashboard/settings",
|
||||
icon: SettingsIcon,
|
||||
title: "Facturas proforma",
|
||||
icon: FileTextIcon,
|
||||
items: [
|
||||
{
|
||||
title: "Listado de proformas",
|
||||
url: "/proformas",
|
||||
},
|
||||
/*{
|
||||
title: "Enviar a Veri*Factu",
|
||||
url: "#",
|
||||
},*/
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Login",
|
||||
url: "/login",
|
||||
icon: LogInIcon,
|
||||
},
|
||||
{
|
||||
title: "Register",
|
||||
url: "/register",
|
||||
icon: UserPlusIcon,
|
||||
},
|
||||
{
|
||||
title: "404 Page",
|
||||
url: "/404-page",
|
||||
icon: PackageIcon,
|
||||
},
|
||||
{
|
||||
title: "500 Page",
|
||||
url: "/500-page",
|
||||
icon: Package2Icon,
|
||||
title: "Facturas de cliente",
|
||||
icon: FileCheckIcon,
|
||||
items: [
|
||||
{
|
||||
title: "Listado de facturas",
|
||||
url: "/customer-invoices",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
navClouds: [
|
||||
@ -118,19 +125,14 @@ const data = {
|
||||
},
|
||||
],
|
||||
navSecondary: [
|
||||
{
|
||||
title: "Get Pro",
|
||||
url: "https://shadcnuikit.com/pricing",
|
||||
icon: CircleIcon,
|
||||
},
|
||||
{
|
||||
title: "Shadcn UI Kit",
|
||||
url: "https://shadcnuikit.com/",
|
||||
url: "http://192.168.0.104:5173/shadcnui",
|
||||
icon: CircleIcon,
|
||||
},
|
||||
{
|
||||
title: "Bundui Component",
|
||||
url: "https://bundui.io",
|
||||
title: "TailwindCSS v4",
|
||||
url: "http://192.168.0.104:5173/tailwindcss4",
|
||||
icon: CircleIcon,
|
||||
},
|
||||
],
|
||||
@ -139,21 +141,8 @@ const data = {
|
||||
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
|
||||
return (
|
||||
<Sidebar collapsible="icon" {...props}>
|
||||
<SidebarHeader>
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton asChild className="data-[slot=sidebar-menu-button]:!p-1.5">
|
||||
<a href="#">
|
||||
<img
|
||||
alt="shadcn ui kit svg logo"
|
||||
className="size-6 rounded-sm group-data-[collapsible=icon]:size-5"
|
||||
src="https://shadcnuikit.com/logo.png"
|
||||
/>
|
||||
<span className="text-base font-medium">Shadcn UI Kit</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
<SidebarHeader className="mb-3">
|
||||
<TeamSwitcher teams={data.teams} />
|
||||
</SidebarHeader>
|
||||
<SidebarContent>
|
||||
<NavMain items={data.navMain} />
|
||||
|
||||
@ -19,44 +19,10 @@ import {
|
||||
verticalListSortingStrategy,
|
||||
} from "@dnd-kit/sortable";
|
||||
import { CSS } from "@dnd-kit/utilities";
|
||||
import {
|
||||
ColumnDef,
|
||||
ColumnFiltersState,
|
||||
Row,
|
||||
SortingState,
|
||||
VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFacetedRowModel,
|
||||
getFacetedUniqueValues,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
CheckCircle2Icon,
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ChevronsLeftIcon,
|
||||
ChevronsRightIcon,
|
||||
ColumnsIcon,
|
||||
GripVerticalIcon,
|
||||
LoaderIcon,
|
||||
MoreVerticalIcon,
|
||||
PlusIcon,
|
||||
TrendingUpIcon,
|
||||
} from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { Badge } from "@repo/shadcn-ui/components/badge";
|
||||
import { Button } from "@repo/shadcn-ui/components/button";
|
||||
import {
|
||||
ChartConfig,
|
||||
type ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
@ -100,6 +66,39 @@ import {
|
||||
} from "@repo/shadcn-ui/components/table";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@repo/shadcn-ui/components/tabs";
|
||||
import { useIsMobile } from "@repo/shadcn-ui/hooks";
|
||||
import {
|
||||
type ColumnDef,
|
||||
type ColumnFiltersState,
|
||||
type Row,
|
||||
type SortingState,
|
||||
type VisibilityState,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFacetedRowModel,
|
||||
getFacetedUniqueValues,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
} from "@tanstack/react-table";
|
||||
import {
|
||||
CheckCircle2Icon,
|
||||
ChevronDownIcon,
|
||||
ChevronLeftIcon,
|
||||
ChevronRightIcon,
|
||||
ChevronsLeftIcon,
|
||||
ChevronsRightIcon,
|
||||
ColumnsIcon,
|
||||
GripVerticalIcon,
|
||||
LoaderIcon,
|
||||
MoreVerticalIcon,
|
||||
PlusIcon,
|
||||
TrendingUpIcon,
|
||||
} from "lucide-react";
|
||||
import * as React from "react";
|
||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
|
||||
import { toast } from "sonner";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
export const schema = z.object({
|
||||
id: z.number(),
|
||||
@ -121,12 +120,12 @@ function DragHandle({ id }: { id: number }) {
|
||||
<Button
|
||||
{...attributes}
|
||||
{...listeners}
|
||||
variant='ghost'
|
||||
size='icon'
|
||||
className='size-7 text-muted-foreground hover:bg-transparent'
|
||||
className="size-7 text-muted-foreground hover:bg-transparent"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
>
|
||||
<GripVerticalIcon className='size-3 text-muted-foreground' />
|
||||
<span className='sr-only'>Drag to reorder</span>
|
||||
<GripVerticalIcon className="size-3 text-muted-foreground" />
|
||||
<span className="sr-only">Drag to reorder</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
@ -140,23 +139,23 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
{
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<div className='flex items-center justify-center'>
|
||||
<div className="flex items-center justify-center">
|
||||
<Checkbox
|
||||
aria-label="Select all"
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label='Select all'
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className='flex items-center justify-center'>
|
||||
<div className="flex items-center justify-center">
|
||||
<Checkbox
|
||||
aria-label="Select row"
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label='Select row'
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
@ -175,8 +174,8 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
accessorKey: "type",
|
||||
header: "Section Type",
|
||||
cell: ({ row }) => (
|
||||
<div className='w-32'>
|
||||
<Badge variant='outline' className='px-1.5 text-muted-foreground'>
|
||||
<div className="w-32">
|
||||
<Badge className="px-1.5 text-muted-foreground" variant="outline">
|
||||
{row.original.type}
|
||||
</Badge>
|
||||
</div>
|
||||
@ -186,9 +185,9 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
accessorKey: "status",
|
||||
header: "Status",
|
||||
cell: ({ row }) => (
|
||||
<Badge variant='outline' className='flex gap-1 px-1.5 text-muted-foreground [&_svg]:size-3'>
|
||||
<Badge className="flex gap-1 px-1.5 text-muted-foreground [&_svg]:size-3" variant="outline">
|
||||
{row.original.status === "Done" ? (
|
||||
<CheckCircle2Icon className='text-green-500 dark:text-green-400' />
|
||||
<CheckCircle2Icon className="text-green-500 dark:text-green-400" />
|
||||
) : (
|
||||
<LoaderIcon />
|
||||
)}
|
||||
@ -198,7 +197,7 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
},
|
||||
{
|
||||
accessorKey: "target",
|
||||
header: () => <div className='w-full text-right'>Target</div>,
|
||||
header: () => <div className="w-full text-right">Target</div>,
|
||||
cell: ({ row }) => (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
@ -210,11 +209,11 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Label htmlFor={`${row.original.id}-target`} className='sr-only'>
|
||||
<Label className="sr-only" htmlFor={`${row.original.id}-target`}>
|
||||
Target
|
||||
</Label>
|
||||
<Input
|
||||
className='h-8 w-16 border-transparent bg-transparent text-right shadow-none hover:bg-input/30 focus-visible:border focus-visible:bg-background'
|
||||
className="h-8 w-16 border-transparent bg-transparent text-right shadow-none hover:bg-input/30 focus-visible:border focus-visible:bg-background"
|
||||
defaultValue={row.original.target}
|
||||
id={`${row.original.id}-target`}
|
||||
/>
|
||||
@ -223,7 +222,7 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
},
|
||||
{
|
||||
accessorKey: "limit",
|
||||
header: () => <div className='w-full text-right'>Limit</div>,
|
||||
header: () => <div className="w-full text-right">Limit</div>,
|
||||
cell: ({ row }) => (
|
||||
<form
|
||||
onSubmit={(e) => {
|
||||
@ -235,11 +234,11 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Label htmlFor={`${row.original.id}-limit`} className='sr-only'>
|
||||
<Label className="sr-only" htmlFor={`${row.original.id}-limit`}>
|
||||
Limit
|
||||
</Label>
|
||||
<Input
|
||||
className='h-8 w-16 border-transparent bg-transparent text-right shadow-none hover:bg-input/30 focus-visible:border focus-visible:bg-background'
|
||||
className="h-8 w-16 border-transparent bg-transparent text-right shadow-none hover:bg-input/30 focus-visible:border focus-visible:bg-background"
|
||||
defaultValue={row.original.limit}
|
||||
id={`${row.original.id}-limit`}
|
||||
/>
|
||||
@ -258,16 +257,16 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
|
||||
return (
|
||||
<>
|
||||
<Label htmlFor={`${row.original.id}-reviewer`} className='sr-only'>
|
||||
<Label className="sr-only" htmlFor={`${row.original.id}-reviewer`}>
|
||||
Reviewer
|
||||
</Label>
|
||||
<Select>
|
||||
<SelectTrigger className='h-8 w-40' id={`${row.original.id}-reviewer`}>
|
||||
<SelectValue placeholder='Assign reviewer' />
|
||||
<SelectTrigger className="h-8 w-40" id={`${row.original.id}-reviewer`}>
|
||||
<SelectValue placeholder="Assign reviewer" />
|
||||
</SelectTrigger>
|
||||
<SelectContent align='end'>
|
||||
<SelectItem value='Eddie Lake'>Eddie Lake</SelectItem>
|
||||
<SelectItem value='Jamik Tashpulatov'>Jamik Tashpulatov</SelectItem>
|
||||
<SelectContent align="end">
|
||||
<SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
|
||||
<SelectItem value="Jamik Tashpulatov">Jamik Tashpulatov</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</>
|
||||
@ -278,17 +277,17 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||
id: "actions",
|
||||
cell: () => (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<Button
|
||||
variant='ghost'
|
||||
className='flex size-8 text-muted-foreground data-[state=open]:bg-muted'
|
||||
size='icon'
|
||||
className="flex size-8 text-muted-foreground data-[state=open]:bg-muted"
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
>
|
||||
<MoreVerticalIcon />
|
||||
<span className='sr-only'>Open menu</span>
|
||||
<span className="sr-only">Open menu</span>
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end' className='w-32'>
|
||||
<DropdownMenuContent align="end" className="w-32">
|
||||
<DropdownMenuItem>Edit</DropdownMenuItem>
|
||||
<DropdownMenuItem>Make a copy</DropdownMenuItem>
|
||||
<DropdownMenuItem>Favorite</DropdownMenuItem>
|
||||
@ -307,10 +306,10 @@ function DraggableRow({ row }: { row: Row<z.infer<typeof schema>> }) {
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
className="relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80"
|
||||
data-dragging={isDragging}
|
||||
data-state={row.getIsSelected() && "selected"}
|
||||
ref={setNodeRef}
|
||||
className='relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80'
|
||||
style={{
|
||||
transform: CSS.Transform.toString(transform),
|
||||
transition: transition,
|
||||
@ -325,11 +324,7 @@ function DraggableRow({ row }: { row: Row<z.infer<typeof schema>> }) {
|
||||
);
|
||||
}
|
||||
|
||||
export function DataTable({
|
||||
data: initialData,
|
||||
}: {
|
||||
data: z.infer<typeof schema>[];
|
||||
}) {
|
||||
export function DataTable({ data: initialData }: { data: z.infer<typeof schema>[] }) {
|
||||
const [data, setData] = React.useState(() => initialData);
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
||||
@ -385,64 +380,64 @@ export function DataTable({
|
||||
}
|
||||
|
||||
return (
|
||||
<Tabs defaultValue='outline' className='flex w-full flex-col justify-start gap-6'>
|
||||
<div className='flex items-center justify-between px-4 lg:px-6'>
|
||||
<Label htmlFor='view-selector' className='sr-only'>
|
||||
<Tabs className="flex w-full flex-col justify-start gap-6" defaultValue="outline">
|
||||
<div className="flex items-center justify-between px-4 lg:px-6">
|
||||
<Label className="sr-only" htmlFor="view-selector">
|
||||
View
|
||||
</Label>
|
||||
<Select defaultValue='outline'>
|
||||
<SelectTrigger className='@4xl/main:hidden flex w-fit' id='view-selector'>
|
||||
<SelectValue placeholder='Select a view' />
|
||||
<Select defaultValue="outline">
|
||||
<SelectTrigger className="@4xl/main:hidden flex w-fit" id="view-selector">
|
||||
<SelectValue placeholder="Select a view" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value='outline'>Outline</SelectItem>
|
||||
<SelectItem value='past-performance'>Past Performance</SelectItem>
|
||||
<SelectItem value='key-personnel'>Key Personnel</SelectItem>
|
||||
<SelectItem value='focus-documents'>Focus Documents</SelectItem>
|
||||
<SelectItem value="outline">Outline</SelectItem>
|
||||
<SelectItem value="past-performance">Past Performance</SelectItem>
|
||||
<SelectItem value="key-personnel">Key Personnel</SelectItem>
|
||||
<SelectItem value="focus-documents">Focus Documents</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<TabsList className='@4xl/main:flex hidden'>
|
||||
<TabsTrigger value='outline'>Outline</TabsTrigger>
|
||||
<TabsTrigger value='past-performance' className='gap-1'>
|
||||
<TabsList className="@4xl/main:flex hidden">
|
||||
<TabsTrigger value="outline">Outline</TabsTrigger>
|
||||
<TabsTrigger className="gap-1" value="past-performance">
|
||||
Past Performance{" "}
|
||||
<Badge
|
||||
variant='secondary'
|
||||
className='flex size-5 items-center justify-center rounded-full bg-muted-foreground/30'
|
||||
className="flex size-5 items-center justify-center rounded-full bg-muted-foreground/30"
|
||||
variant="secondary"
|
||||
>
|
||||
3
|
||||
</Badge>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value='key-personnel' className='gap-1'>
|
||||
<TabsTrigger className="gap-1" value="key-personnel">
|
||||
Key Personnel{" "}
|
||||
<Badge
|
||||
variant='secondary'
|
||||
className='flex size-5 items-center justify-center rounded-full bg-muted-foreground/30'
|
||||
className="flex size-5 items-center justify-center rounded-full bg-muted-foreground/30"
|
||||
variant="secondary"
|
||||
>
|
||||
2
|
||||
</Badge>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value='focus-documents'>Focus Documents</TabsTrigger>
|
||||
<TabsTrigger value="focus-documents">Focus Documents</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className='flex items-center gap-2'>
|
||||
<div className="flex items-center gap-2">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant='outline' size='sm'>
|
||||
<DropdownMenuTrigger>
|
||||
<Button size="sm" variant="outline">
|
||||
<ColumnsIcon />
|
||||
<span className='hidden lg:inline'>Customize Columns</span>
|
||||
<span className='lg:hidden'>Columns</span>
|
||||
<span className="hidden lg:inline">Customize Columns</span>
|
||||
<span className="lg:hidden">Columns</span>
|
||||
<ChevronDownIcon />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align='end' className='w-56'>
|
||||
<DropdownMenuContent align="end" className="w-56">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter((column) => typeof column.accessorFn !== "undefined" && column.getCanHide())
|
||||
.map((column) => {
|
||||
return (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={column.id}
|
||||
className='capitalize'
|
||||
checked={column.getIsVisible()}
|
||||
className="capitalize"
|
||||
key={column.id}
|
||||
onCheckedChange={(value) => column.toggleVisibility(!!value)}
|
||||
>
|
||||
{column.id}
|
||||
@ -451,31 +446,31 @@ export function DataTable({
|
||||
})}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
<Button variant='outline' size='sm'>
|
||||
<Button size="sm" variant="outline">
|
||||
<PlusIcon />
|
||||
<span className='hidden lg:inline'>Add Section</span>
|
||||
<span className="hidden lg:inline">Add Section</span>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<TabsContent
|
||||
value='outline'
|
||||
className='relative flex flex-col gap-4 overflow-auto px-4 lg:px-6'
|
||||
className="relative flex flex-col gap-4 overflow-auto px-4 lg:px-6"
|
||||
value="outline"
|
||||
>
|
||||
<div className='overflow-hidden rounded-lg border'>
|
||||
<div className="overflow-hidden rounded-lg border">
|
||||
<DndContext
|
||||
collisionDetection={closestCenter}
|
||||
id={sortableId}
|
||||
modifiers={[restrictToVerticalAxis]}
|
||||
onDragEnd={handleDragEnd}
|
||||
sensors={sensors}
|
||||
id={sortableId}
|
||||
>
|
||||
<Table>
|
||||
<TableHeader className='sticky top-0 z-10 bg-muted'>
|
||||
<TableHeader className="sticky top-0 z-10 bg-muted">
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id} colSpan={header.colSpan}>
|
||||
<TableHead colSpan={header.colSpan} key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||
@ -485,7 +480,7 @@ export function DataTable({
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody className='**:data-[slot=table-cell]:first:w-8'>
|
||||
<TableBody className="**:data-[slot=table-cell]:first:w-8">
|
||||
{table.getRowModel().rows?.length ? (
|
||||
<SortableContext items={dataIds} strategy={verticalListSortingStrategy}>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
@ -494,7 +489,7 @@ export function DataTable({
|
||||
</SortableContext>
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className='h-24 text-center'>
|
||||
<TableCell className="h-24 text-center" colSpan={columns.length}>
|
||||
No results.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@ -503,26 +498,26 @@ export function DataTable({
|
||||
</Table>
|
||||
</DndContext>
|
||||
</div>
|
||||
<div className='flex items-center justify-between px-4'>
|
||||
<div className='hidden flex-1 text-sm text-muted-foreground lg:flex'>
|
||||
<div className="flex items-center justify-between px-4">
|
||||
<div className="hidden flex-1 text-sm text-muted-foreground lg:flex">
|
||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||
</div>
|
||||
<div className='flex w-full items-center gap-8 lg:w-fit'>
|
||||
<div className='hidden items-center gap-2 lg:flex'>
|
||||
<Label htmlFor='rows-per-page' className='text-sm font-medium'>
|
||||
<div className="flex w-full items-center gap-8 lg:w-fit">
|
||||
<div className="hidden items-center gap-2 lg:flex">
|
||||
<Label className="text-sm font-medium" htmlFor="rows-per-page">
|
||||
Rows per page
|
||||
</Label>
|
||||
<Select
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
onValueChange={(value) => {
|
||||
table.setPageSize(Number(value));
|
||||
}}
|
||||
value={`${table.getState().pagination.pageSize}`}
|
||||
>
|
||||
<SelectTrigger className='w-20' id='rows-per-page'>
|
||||
<SelectTrigger className="w-20" id="rows-per-page">
|
||||
<SelectValue placeholder={table.getState().pagination.pageSize} />
|
||||
</SelectTrigger>
|
||||
<SelectContent side='top'>
|
||||
<SelectContent side="top">
|
||||
{[10, 20, 30, 40, 50].map((pageSize) => (
|
||||
<SelectItem key={pageSize} value={`${pageSize}`}>
|
||||
{pageSize}
|
||||
@ -531,61 +526,61 @@ export function DataTable({
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='flex w-fit items-center justify-center text-sm font-medium'>
|
||||
<div className="flex w-fit items-center justify-center text-sm font-medium">
|
||||
Page {table.getState().pagination.pageIndex + 1} of {table.getPageCount()}
|
||||
</div>
|
||||
<div className='ml-auto flex items-center gap-2 lg:ml-0'>
|
||||
<div className="ml-auto flex items-center gap-2 lg:ml-0">
|
||||
<Button
|
||||
variant='outline'
|
||||
className='hidden size-8 p-0 lg:flex'
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
className="hidden size-8 p-0 lg:flex"
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
variant="outline"
|
||||
>
|
||||
<span className='sr-only'>Go to first page</span>
|
||||
<span className="sr-only">Go to first page</span>
|
||||
<ChevronsLeftIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='size-8'
|
||||
size='icon'
|
||||
onClick={() => table.previousPage()}
|
||||
className="size-8"
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
onClick={() => table.previousPage()}
|
||||
size="icon"
|
||||
variant="outline"
|
||||
>
|
||||
<span className='sr-only'>Go to previous page</span>
|
||||
<span className="sr-only">Go to previous page</span>
|
||||
<ChevronLeftIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='size-8'
|
||||
size='icon'
|
||||
onClick={() => table.nextPage()}
|
||||
className="size-8"
|
||||
disabled={!table.getCanNextPage()}
|
||||
onClick={() => table.nextPage()}
|
||||
size="icon"
|
||||
variant="outline"
|
||||
>
|
||||
<span className='sr-only'>Go to next page</span>
|
||||
<span className="sr-only">Go to next page</span>
|
||||
<ChevronRightIcon />
|
||||
</Button>
|
||||
<Button
|
||||
variant='outline'
|
||||
className='hidden size-8 lg:flex'
|
||||
size='icon'
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
className="hidden size-8 lg:flex"
|
||||
disabled={!table.getCanNextPage()}
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
size="icon"
|
||||
variant="outline"
|
||||
>
|
||||
<span className='sr-only'>Go to last page</span>
|
||||
<span className="sr-only">Go to last page</span>
|
||||
<ChevronsRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value='past-performance' className='flex flex-col px-4 lg:px-6'>
|
||||
<div className='aspect-video w-full flex-1 rounded-lg border border-dashed' />
|
||||
<TabsContent className="flex flex-col px-4 lg:px-6" value="past-performance">
|
||||
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed" />
|
||||
</TabsContent>
|
||||
<TabsContent value='key-personnel' className='flex flex-col px-4 lg:px-6'>
|
||||
<div className='aspect-video w-full flex-1 rounded-lg border border-dashed' />
|
||||
<TabsContent className="flex flex-col px-4 lg:px-6" value="key-personnel">
|
||||
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed" />
|
||||
</TabsContent>
|
||||
<TabsContent value='focus-documents' className='flex flex-col px-4 lg:px-6'>
|
||||
<div className='aspect-video w-full flex-1 rounded-lg border border-dashed' />
|
||||
<TabsContent className="flex flex-col px-4 lg:px-6" value="focus-documents">
|
||||
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed" />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
);
|
||||
@ -616,17 +611,17 @@ function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
||||
|
||||
return (
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant='link' className='w-fit px-0 text-left text-foreground'>
|
||||
<SheetTrigger>
|
||||
<Button className="w-fit px-0 text-left text-foreground" variant="link">
|
||||
{item.header}
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side='right' className='flex flex-col'>
|
||||
<SheetHeader className='gap-1'>
|
||||
<SheetContent className="flex flex-col" side="right">
|
||||
<SheetHeader className="gap-1">
|
||||
<SheetTitle>{item.header}</SheetTitle>
|
||||
<SheetDescription>Showing total visitors for the last 6 months</SheetDescription>
|
||||
</SheetHeader>
|
||||
<div className='flex flex-1 flex-col gap-4 overflow-y-auto py-4 text-sm'>
|
||||
<div className="flex flex-1 flex-col gap-4 overflow-y-auto py-4 text-sm">
|
||||
{!isMobile && (
|
||||
<>
|
||||
<ChartContainer config={chartConfig}>
|
||||
@ -640,38 +635,38 @@ function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
||||
>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis
|
||||
dataKey='month'
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
tickFormatter={(value) => value.slice(0, 3)}
|
||||
dataKey="month"
|
||||
hide
|
||||
tickFormatter={(value) => value.slice(0, 3)}
|
||||
tickLine={false}
|
||||
tickMargin={8}
|
||||
/>
|
||||
<ChartTooltip cursor={false} content={<ChartTooltipContent indicator='dot' />} />
|
||||
<ChartTooltip content={<ChartTooltipContent indicator="dot" />} cursor={false} />
|
||||
<Area
|
||||
dataKey='mobile'
|
||||
type='natural'
|
||||
fill='var(--color-mobile)'
|
||||
dataKey="mobile"
|
||||
fill="var(--color-mobile)"
|
||||
fillOpacity={0.6}
|
||||
stroke='var(--color-mobile)'
|
||||
stackId='a'
|
||||
stackId="a"
|
||||
stroke="var(--color-mobile)"
|
||||
type="natural"
|
||||
/>
|
||||
<Area
|
||||
dataKey='desktop'
|
||||
type='natural'
|
||||
fill='var(--color-desktop)'
|
||||
dataKey="desktop"
|
||||
fill="var(--color-desktop)"
|
||||
fillOpacity={0.4}
|
||||
stroke='var(--color-desktop)'
|
||||
stackId='a'
|
||||
stackId="a"
|
||||
stroke="var(--color-desktop)"
|
||||
type="natural"
|
||||
/>
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
<Separator />
|
||||
<div className='grid gap-2'>
|
||||
<div className='flex gap-2 font-medium leading-none'>
|
||||
Trending up by 5.2% this month <TrendingUpIcon className='size-4' />
|
||||
<div className="grid gap-2">
|
||||
<div className="flex gap-2 font-medium leading-none">
|
||||
Trending up by 5.2% this month <TrendingUpIcon className="size-4" />
|
||||
</div>
|
||||
<div className='text-muted-foreground'>
|
||||
<div className="text-muted-foreground">
|
||||
Showing total visitors for the last 6 months. This is just some random text to
|
||||
test the layout. It spans multiple lines and should wrap around.
|
||||
</div>
|
||||
@ -679,73 +674,73 @@ function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
||||
<Separator />
|
||||
</>
|
||||
)}
|
||||
<form className='flex flex-col gap-4'>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<Label htmlFor='header'>Header</Label>
|
||||
<Input id='header' defaultValue={item.header} />
|
||||
<form className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="header">Header</Label>
|
||||
<Input defaultValue={item.header} id="header" />
|
||||
</div>
|
||||
<div className='grid grid-cols-2 gap-4'>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<Label htmlFor='type'>Type</Label>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="type">Type</Label>
|
||||
<Select defaultValue={item.type}>
|
||||
<SelectTrigger id='type' className='w-full'>
|
||||
<SelectValue placeholder='Select a type' />
|
||||
<SelectTrigger className="w-full" id="type">
|
||||
<SelectValue placeholder="Select a type" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value='Table of Contents'>Table of Contents</SelectItem>
|
||||
<SelectItem value='Executive Summary'>Executive Summary</SelectItem>
|
||||
<SelectItem value='Technical Approach'>Technical Approach</SelectItem>
|
||||
<SelectItem value='Design'>Design</SelectItem>
|
||||
<SelectItem value='Capabilities'>Capabilities</SelectItem>
|
||||
<SelectItem value='Focus Documents'>Focus Documents</SelectItem>
|
||||
<SelectItem value='Narrative'>Narrative</SelectItem>
|
||||
<SelectItem value='Cover Page'>Cover Page</SelectItem>
|
||||
<SelectItem value="Table of Contents">Table of Contents</SelectItem>
|
||||
<SelectItem value="Executive Summary">Executive Summary</SelectItem>
|
||||
<SelectItem value="Technical Approach">Technical Approach</SelectItem>
|
||||
<SelectItem value="Design">Design</SelectItem>
|
||||
<SelectItem value="Capabilities">Capabilities</SelectItem>
|
||||
<SelectItem value="Focus Documents">Focus Documents</SelectItem>
|
||||
<SelectItem value="Narrative">Narrative</SelectItem>
|
||||
<SelectItem value="Cover Page">Cover Page</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<Label htmlFor='status'>Status</Label>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="status">Status</Label>
|
||||
<Select defaultValue={item.status}>
|
||||
<SelectTrigger id='status' className='w-full'>
|
||||
<SelectValue placeholder='Select a status' />
|
||||
<SelectTrigger className="w-full" id="status">
|
||||
<SelectValue placeholder="Select a status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value='Done'>Done</SelectItem>
|
||||
<SelectItem value='In Progress'>In Progress</SelectItem>
|
||||
<SelectItem value='Not Started'>Not Started</SelectItem>
|
||||
<SelectItem value="Done">Done</SelectItem>
|
||||
<SelectItem value="In Progress">In Progress</SelectItem>
|
||||
<SelectItem value="Not Started">Not Started</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div className='grid grid-cols-2 gap-4'>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<Label htmlFor='target'>Target</Label>
|
||||
<Input id='target' defaultValue={item.target} />
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="target">Target</Label>
|
||||
<Input defaultValue={item.target} id="target" />
|
||||
</div>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<Label htmlFor='limit'>Limit</Label>
|
||||
<Input id='limit' defaultValue={item.limit} />
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="limit">Limit</Label>
|
||||
<Input defaultValue={item.limit} id="limit" />
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex flex-col gap-3'>
|
||||
<Label htmlFor='reviewer'>Reviewer</Label>
|
||||
<div className="flex flex-col gap-3">
|
||||
<Label htmlFor="reviewer">Reviewer</Label>
|
||||
<Select defaultValue={item.reviewer}>
|
||||
<SelectTrigger id='reviewer' className='w-full'>
|
||||
<SelectValue placeholder='Select a reviewer' />
|
||||
<SelectTrigger className="w-full" id="reviewer">
|
||||
<SelectValue placeholder="Select a reviewer" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value='Eddie Lake'>Eddie Lake</SelectItem>
|
||||
<SelectItem value='Jamik Tashpulatov'>Jamik Tashpulatov</SelectItem>
|
||||
<SelectItem value='Emily Whalen'>Emily Whalen</SelectItem>
|
||||
<SelectItem value="Eddie Lake">Eddie Lake</SelectItem>
|
||||
<SelectItem value="Jamik Tashpulatov">Jamik Tashpulatov</SelectItem>
|
||||
<SelectItem value="Emily Whalen">Emily Whalen</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<SheetFooter className='mt-auto flex gap-2 sm:flex-col sm:space-x-0'>
|
||||
<Button className='w-full'>Submit</Button>
|
||||
<SheetClose asChild>
|
||||
<Button variant='outline' className='w-full'>
|
||||
<SheetFooter className="mt-auto flex gap-2 sm:flex-col sm:space-x-0">
|
||||
<Button className="w-full">Submit</Button>
|
||||
<SheetClose>
|
||||
<Button className="w-full" variant="outline">
|
||||
Done
|
||||
</Button>
|
||||
</SheetClose>
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { FolderIcon, type LucideIcon, MoreHorizontalIcon, ShareIcon } from "lucide-react";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -17,6 +15,7 @@ import {
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@repo/shadcn-ui/components/sidebar";
|
||||
import { FolderIcon, type LucideIcon, MoreHorizontalIcon, ShareIcon } from "lucide-react";
|
||||
|
||||
export function NavDocuments({
|
||||
items,
|
||||
@ -30,28 +29,28 @@ export function NavDocuments({
|
||||
const { isMobile } = useSidebar();
|
||||
|
||||
return (
|
||||
<SidebarGroup className='group-data-[collapsible=icon]:hidden'>
|
||||
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
|
||||
<SidebarGroupLabel>Documents</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
<SidebarMenuItem key={item.name}>
|
||||
<SidebarMenuButton asChild>
|
||||
<SidebarMenuButton>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<SidebarMenuAction showOnHover className='rounded-sm data-[state=open]:bg-accent'>
|
||||
<DropdownMenuTrigger>
|
||||
<SidebarMenuAction className="rounded-sm data-[state=open]:bg-accent" showOnHover>
|
||||
<MoreHorizontalIcon />
|
||||
<span className='sr-only'>More</span>
|
||||
<span className="sr-only">More</span>
|
||||
</SidebarMenuAction>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className='w-24 rounded-lg'
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align={isMobile ? "end" : "start"}
|
||||
className="w-24 rounded-lg"
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
>
|
||||
<DropdownMenuItem>
|
||||
<FolderIcon />
|
||||
@ -66,8 +65,8 @@ export function NavDocuments({
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton className='text-sidebar-foreground/70'>
|
||||
<MoreHorizontalIcon className='text-sidebar-foreground/70' />
|
||||
<SidebarMenuButton className="text-sidebar-foreground/70">
|
||||
<MoreHorizontalIcon className="text-sidebar-foreground/70" />
|
||||
<span>More</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
@ -46,7 +46,7 @@ export function NavMain({ items }: { items: NavMainItem[] }) {
|
||||
{items.map((item) => (
|
||||
<Collapsible asChild className="group/collapsible" defaultOpen={true} key={item.title}>
|
||||
<SidebarMenuItem className="mb-6">
|
||||
<CollapsibleTrigger asChild>
|
||||
<CollapsibleTrigger>
|
||||
<SidebarMenuButton tooltip={item.title}>
|
||||
{item.icon && <item.icon />}
|
||||
<span className="font-semibold">{item.title}</span>
|
||||
@ -57,7 +57,7 @@ export function NavMain({ items }: { items: NavMainItem[] }) {
|
||||
<SidebarMenuSub>
|
||||
{item.items?.map((subItem) => (
|
||||
<SidebarMenuSubItem key={subItem.title}>
|
||||
<SidebarMenuSubButton asChild>
|
||||
<SidebarMenuSubButton>
|
||||
<a href={subItem.url}>
|
||||
<span>{subItem.title}</span>
|
||||
</a>
|
||||
|
||||
@ -1,7 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { Folder, Forward, type LucideIcon, MoreHorizontal, Trash2 } from "lucide-react";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -18,6 +16,7 @@ import {
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@repo/shadcn-ui/components/sidebar";
|
||||
import { Folder, Forward, type LucideIcon, MoreHorizontal, Trash2 } from "lucide-react";
|
||||
|
||||
export function NavProjects({
|
||||
projects,
|
||||
@ -31,40 +30,40 @@ export function NavProjects({
|
||||
const { isMobile } = useSidebar();
|
||||
|
||||
return (
|
||||
<SidebarGroup className='group-data-[collapsible=icon]:hidden'>
|
||||
<SidebarGroup className="group-data-[collapsible=icon]:hidden">
|
||||
<SidebarGroupLabel>Projects</SidebarGroupLabel>
|
||||
<SidebarMenu>
|
||||
{projects.map((item) => (
|
||||
<SidebarMenuItem key={item.name}>
|
||||
<SidebarMenuButton asChild>
|
||||
<SidebarMenuButton>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.name}</span>
|
||||
</a>
|
||||
</SidebarMenuButton>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<SidebarMenuAction showOnHover>
|
||||
<MoreHorizontal />
|
||||
<span className='sr-only'>More</span>
|
||||
<span className="sr-only">More</span>
|
||||
</SidebarMenuAction>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className='w-48 rounded-lg'
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align={isMobile ? "end" : "start"}
|
||||
className="w-48 rounded-lg"
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
>
|
||||
<DropdownMenuItem>
|
||||
<Folder className='text-muted-foreground' />
|
||||
<Folder className="text-muted-foreground" />
|
||||
<span>View Project</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Forward className='text-muted-foreground' />
|
||||
<Forward className="text-muted-foreground" />
|
||||
<span>Share Project</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem>
|
||||
<Trash2 className='text-muted-foreground' />
|
||||
<Trash2 className="text-muted-foreground" />
|
||||
<span>Delete Project</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
@ -72,8 +71,8 @@ export function NavProjects({
|
||||
</SidebarMenuItem>
|
||||
))}
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton className='text-sidebar-foreground/70'>
|
||||
<MoreHorizontal className='text-sidebar-foreground/70' />
|
||||
<SidebarMenuButton className="text-sidebar-foreground/70">
|
||||
<MoreHorizontal className="text-sidebar-foreground/70" />
|
||||
<span>More</span>
|
||||
</SidebarMenuButton>
|
||||
</SidebarMenuItem>
|
||||
|
||||
@ -1,6 +1,3 @@
|
||||
import { LucideIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import {
|
||||
SidebarGroup,
|
||||
SidebarGroupContent,
|
||||
@ -8,6 +5,8 @@ import {
|
||||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "@repo/shadcn-ui/components/sidebar";
|
||||
import type { LucideIcon } from "lucide-react";
|
||||
import type * as React from "react";
|
||||
|
||||
export function NavSecondary({
|
||||
items,
|
||||
@ -25,7 +24,7 @@ export function NavSecondary({
|
||||
<SidebarMenu>
|
||||
{items.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton asChild>
|
||||
<SidebarMenuButton>
|
||||
<a href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
|
||||
@ -1,13 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
BellIcon,
|
||||
CreditCardIcon,
|
||||
LogOutIcon,
|
||||
MoreVerticalIcon,
|
||||
UserCircleIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@repo/shadcn-ui/components/avatar";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@ -24,6 +16,13 @@ import {
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@repo/shadcn-ui/components/sidebar";
|
||||
import {
|
||||
BellIcon,
|
||||
CreditCardIcon,
|
||||
LogOutIcon,
|
||||
MoreVerticalIcon,
|
||||
UserCircleIcon,
|
||||
} from "lucide-react";
|
||||
|
||||
export function NavUser({
|
||||
user,
|
||||
@ -40,37 +39,37 @@ export function NavUser({
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<SidebarMenuButton
|
||||
size='lg'
|
||||
className='data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground'
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
size="lg"
|
||||
>
|
||||
<Avatar className='size-8 rounded-lg grayscale'>
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className='rounded-lg'>CN</AvatarFallback>
|
||||
<Avatar className="size-8 rounded-lg grayscale">
|
||||
<AvatarImage alt={user.name} src={user.avatar} />
|
||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className='grid flex-1 text-left text-sm leading-tight'>
|
||||
<span className='truncate font-medium'>{user.name}</span>
|
||||
<span className='truncate text-xs text-muted-foreground'>{user.email}</span>
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-medium">{user.name}</span>
|
||||
<span className="truncate text-xs text-muted-foreground">{user.email}</span>
|
||||
</div>
|
||||
<MoreVerticalIcon className='ml-auto size-4' />
|
||||
<MoreVerticalIcon className="ml-auto size-4" />
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className='w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg'
|
||||
align="end"
|
||||
className="w-[--radix-dropdown-menu-trigger-width] min-w-56 rounded-lg"
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
align='end'
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuLabel className='p-0 font-normal'>
|
||||
<div className='flex items-center gap-2 px-1 py-1.5 text-left text-sm'>
|
||||
<Avatar className='size-8 rounded-lg'>
|
||||
<AvatarImage src={user.avatar} alt={user.name} />
|
||||
<AvatarFallback className='rounded-lg'>CN</AvatarFallback>
|
||||
<DropdownMenuLabel className="p-0 font-normal">
|
||||
<div className="flex items-center gap-2 px-1 py-1.5 text-left text-sm">
|
||||
<Avatar className="size-8 rounded-lg">
|
||||
<AvatarImage alt={user.name} src={user.avatar} />
|
||||
<AvatarFallback className="rounded-lg">CN</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className='grid flex-1 text-left text-sm leading-tight'>
|
||||
<span className='truncate font-medium'>{user.name}</span>
|
||||
<span className='truncate text-xs text-muted-foreground'>{user.email}</span>
|
||||
<div className="grid flex-1 text-left text-sm leading-tight">
|
||||
<span className="truncate font-medium">{user.name}</span>
|
||||
<span className="truncate text-xs text-muted-foreground">{user.email}</span>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownMenuLabel>
|
||||
|
||||
@ -1,8 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { ChevronsUpDown, Plus } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
@ -16,31 +13,33 @@ import {
|
||||
SidebarMenuItem,
|
||||
useSidebar,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import { ChevronsUpDown, Plus } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
export function TeamSwitcher({
|
||||
teams,
|
||||
}: {
|
||||
teams: {
|
||||
name: string
|
||||
logo: React.ElementType
|
||||
plan: string
|
||||
}[]
|
||||
name: string;
|
||||
logo: React.ElementType;
|
||||
plan: string;
|
||||
}[];
|
||||
}) {
|
||||
const { isMobile } = useSidebar()
|
||||
const [activeTeam, setActiveTeam] = React.useState(teams[0])
|
||||
const { isMobile } = useSidebar();
|
||||
const [activeTeam, setActiveTeam] = React.useState(teams[0]);
|
||||
|
||||
if (!activeTeam) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SidebarMenu>
|
||||
<SidebarMenuItem>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<DropdownMenuTrigger>
|
||||
<SidebarMenuButton
|
||||
size="lg"
|
||||
className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
size="lg"
|
||||
>
|
||||
<div className="bg-sidebar-primary text-sidebar-primary-foreground flex aspect-square size-8 items-center justify-center rounded-lg">
|
||||
<activeTeam.logo className="size-4" />
|
||||
@ -53,19 +52,17 @@ export function TeamSwitcher({
|
||||
</SidebarMenuButton>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent
|
||||
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
||||
align="start"
|
||||
className="w-(--radix-dropdown-menu-trigger-width) min-w-56 rounded-lg"
|
||||
side={isMobile ? "bottom" : "right"}
|
||||
sideOffset={4}
|
||||
>
|
||||
<DropdownMenuLabel className="text-muted-foreground text-xs">
|
||||
Teams
|
||||
</DropdownMenuLabel>
|
||||
<DropdownMenuLabel className="text-muted-foreground text-xs">Teams</DropdownMenuLabel>
|
||||
{teams.map((team, index) => (
|
||||
<DropdownMenuItem
|
||||
className="gap-2 p-2"
|
||||
key={team.name}
|
||||
onClick={() => setActiveTeam(team)}
|
||||
className="gap-2 p-2"
|
||||
>
|
||||
<div className="flex size-6 items-center justify-center rounded-md border">
|
||||
<team.logo className="size-3.5 shrink-0" />
|
||||
@ -85,5 +82,5 @@ export function TeamSwitcher({
|
||||
</DropdownMenu>
|
||||
</SidebarMenuItem>
|
||||
</SidebarMenu>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,7 +1,3 @@
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import { CheckIcon, ChevronDown, WandSparkles, XCircleIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
@ -18,6 +14,10 @@ import {
|
||||
Separator,
|
||||
} from "@repo/shadcn-ui/components";
|
||||
import { cn } from "@repo/shadcn-ui/lib/utils";
|
||||
import { type VariantProps, cva } from "class-variance-authority";
|
||||
import { CheckIcon, ChevronDown, WandSparkles, XCircleIcon } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { useTranslation } from "../locales/i18n.ts";
|
||||
|
||||
/**
|
||||
@ -65,7 +65,7 @@ export type MultiSelectOptionType = {
|
||||
*/
|
||||
export interface MultiSelectProps
|
||||
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
VariantProps<typeof multiSelectVariants> {
|
||||
VariantProps<typeof multiSelectVariants> {
|
||||
/**
|
||||
* An array of option objects to be displayed in the multi-select component.
|
||||
* Each option object has a label, value, and an optional icon.
|
||||
@ -130,7 +130,6 @@ export interface MultiSelectProps
|
||||
*/
|
||||
selectAllVisible?: boolean;
|
||||
|
||||
|
||||
/**
|
||||
* Filtra los items seleccionados
|
||||
*/
|
||||
@ -181,7 +180,6 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
||||
if (autoFilter) applySelectedFilter();
|
||||
}, [autoFilter, selectedValues, applySelectedFilter]);
|
||||
|
||||
|
||||
const grouped = options.reduce<Record<string, MultiSelectOptionType[]>>((acc, item) => {
|
||||
if (!acc[item.group || ""]) acc[item.group || ""] = [];
|
||||
acc[item.group || ""].push(item);
|
||||
@ -238,33 +236,33 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover open={isPopoverOpen} onOpenChange={setIsPopoverOpen} modal={modalPopover}>
|
||||
<PopoverTrigger asChild>
|
||||
<Popover modal={modalPopover} onOpenChange={setIsPopoverOpen} open={isPopoverOpen}>
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
ref={ref}
|
||||
{...props}
|
||||
onClick={handleTogglePopover}
|
||||
className={cn(
|
||||
"flex w-full -mt-0.5 px-1 py-0.5 rounded-md border min-h-8 h-auto items-center justify-between bg-background hover:bg-inherit [&_svg]:pointer-events-auto",
|
||||
className
|
||||
)}
|
||||
onClick={handleTogglePopover}
|
||||
>
|
||||
{selectedValues.length > 0 ? (
|
||||
<div className='flex justify-between items-center w-full'>
|
||||
<div className='flex flex-wrap items-center'>
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<div className="flex flex-wrap items-center">
|
||||
{selectedValues.slice(0, maxCount).map((value) => {
|
||||
const option = options.find((o) => o.value === value);
|
||||
const IconComponent = option?.icon;
|
||||
return (
|
||||
<Badge
|
||||
key={value}
|
||||
className={cn(
|
||||
isAnimating ? "animate-bounce" : "",
|
||||
multiSelectVariants({ variant })
|
||||
)}
|
||||
key={value}
|
||||
style={{ animationDuration: `${animation}s` }}
|
||||
>
|
||||
{IconComponent && <IconComponent className='h-4 w-4 mr-2' />}
|
||||
{IconComponent && <IconComponent className="h-4 w-4 mr-2" />}
|
||||
{option?.label}
|
||||
{/*<XCircle
|
||||
className='ml-2 h-4 w-4 cursor-pointer'
|
||||
@ -287,7 +285,7 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
||||
>
|
||||
{`+ ${selectedValues.length - maxCount} more`}
|
||||
<XCircleIcon
|
||||
className='ml-2 h-4 w-4 cursor-pointer'
|
||||
className="ml-2 h-4 w-4 cursor-pointer"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
clearExtraOptions();
|
||||
@ -296,33 +294,33 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
<div className='flex items-center justify-between'>
|
||||
<Separator orientation='vertical' className='flex min-h-6 h-full' />
|
||||
<ChevronDown className='h-4 mx-2 cursor-pointer text-muted-foreground' />
|
||||
<div className="flex items-center justify-between">
|
||||
<Separator className="flex min-h-6 h-full" orientation="vertical" />
|
||||
<ChevronDown className="h-4 mx-2 cursor-pointer text-muted-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className='flex items-center justify-between w-full mx-auto'>
|
||||
<span className='text-sm text-muted-foreground mx-3'>
|
||||
<div className="flex items-center justify-between w-full mx-auto">
|
||||
<span className="text-sm text-muted-foreground mx-3">
|
||||
{placeholder || t("components.multi_select.select_options")}
|
||||
</span>
|
||||
<ChevronDown className='h-4 cursor-pointer text-muted-foreground mx-2' />
|
||||
<ChevronDown className="h-4 cursor-pointer text-muted-foreground mx-2" />
|
||||
</div>
|
||||
)}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent
|
||||
className='w-auto p-0'
|
||||
align='start'
|
||||
align="start"
|
||||
className="w-auto p-0"
|
||||
onEscapeKeyDown={() => setIsPopoverOpen(false)}
|
||||
>
|
||||
<Command>
|
||||
<CommandInput placeholder={t("common.search")} onKeyDown={handleInputKeyDown} />
|
||||
<CommandInput onKeyDown={handleInputKeyDown} placeholder={t("common.search")} />
|
||||
<CommandList>
|
||||
<CommandEmpty>{t("components.multi_select.no_results")}</CommandEmpty>
|
||||
|
||||
{selectAllVisible && (
|
||||
<CommandItem key='all' onSelect={toggleAll} className='cursor-pointer'>
|
||||
<CommandItem className="cursor-pointer" key="all" onSelect={toggleAll}>
|
||||
<div
|
||||
className={cn(
|
||||
"mr-2 flex h-4 w-4 items-center justify-center rounded-sm border border-primary",
|
||||
@ -331,21 +329,21 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
||||
: "opacity-50 [&_svg]:invisible"
|
||||
)}
|
||||
>
|
||||
<CheckIcon className='h-4 w-4' />
|
||||
<CheckIcon className="h-4 w-4" />
|
||||
</div>
|
||||
<span>(Select All)</span>
|
||||
</CommandItem>
|
||||
)}
|
||||
{Object.keys(grouped).map((group) => {
|
||||
return (
|
||||
<CommandGroup key={`group-${group || "ungrouped"}`} heading={group}>
|
||||
<CommandGroup heading={group} key={`group-${group || "ungrouped"}`}>
|
||||
{grouped[group].map((option) => {
|
||||
const isSelected = selectedValues.includes(option.value);
|
||||
return (
|
||||
<CommandItem
|
||||
className="cursor-pointer"
|
||||
key={option.value}
|
||||
onSelect={() => toggleOption(option.value)}
|
||||
className='cursor-pointer'
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
@ -360,7 +358,7 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
||||
/>
|
||||
</div>
|
||||
{option.icon && (
|
||||
<option.icon className='mr-2 h-4 w-4 text-muted-foreground' />
|
||||
<option.icon className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||
)}
|
||||
<span>{option.label}</span>
|
||||
</CommandItem>
|
||||
@ -372,21 +370,21 @@ export const MultiSelect = React.forwardRef<HTMLButtonElement, MultiSelectProps>
|
||||
|
||||
<CommandSeparator />
|
||||
<CommandGroup>
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className="flex items-center justify-between">
|
||||
{selectedValues.length > 0 && (
|
||||
<>
|
||||
<CommandItem
|
||||
className="flex-1 justify-center cursor-pointer"
|
||||
onSelect={handleClear}
|
||||
className='flex-1 justify-center cursor-pointer'
|
||||
>
|
||||
{t("components.multi_select.clear_selection")}
|
||||
</CommandItem>
|
||||
<Separator orientation='vertical' className='flex min-h-6 h-full' />
|
||||
<Separator className="flex min-h-6 h-full" orientation="vertical" />
|
||||
</>
|
||||
)}
|
||||
<CommandItem
|
||||
className="flex-1 justify-center cursor-pointer max-w-full"
|
||||
onSelect={() => setIsPopoverOpen(false)}
|
||||
className='flex-1 justify-center cursor-pointer max-w-full'
|
||||
>
|
||||
{t("components.multi_select.close")}
|
||||
</CommandItem>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user