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