forked from nixcn/nixcn-cms
chore(client): eslint format
Signed-off-by: Noa Virellia <noa@requiem.garden>
This commit is contained in:
@@ -1,9 +1,9 @@
|
|||||||
"use client"
|
'use client';
|
||||||
|
|
||||||
import * as React from "react"
|
import type { ChartConfig } from '@/components/ui/chart';
|
||||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
|
import * as React from 'react';
|
||||||
|
|
||||||
import { useIsMobile } from "@/hooks/use-mobile"
|
import { Area, AreaChart, CartesianGrid, XAxis } from 'recharts';
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
CardAction,
|
CardAction,
|
||||||
@@ -11,158 +11,160 @@ import {
|
|||||||
CardDescription,
|
CardDescription,
|
||||||
CardHeader,
|
CardHeader,
|
||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card"
|
} from '@/components/ui/card';
|
||||||
import {
|
import {
|
||||||
|
|
||||||
ChartContainer,
|
ChartContainer,
|
||||||
ChartTooltip,
|
ChartTooltip,
|
||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
type ChartConfig,
|
} from '@/components/ui/chart';
|
||||||
} from "@/components/ui/chart"
|
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select"
|
} from '@/components/ui/select';
|
||||||
import {
|
import {
|
||||||
ToggleGroup,
|
ToggleGroup,
|
||||||
ToggleGroupItem,
|
ToggleGroupItem,
|
||||||
} from "@/components/ui/toggle-group"
|
} from '@/components/ui/toggle-group';
|
||||||
|
import { useIsMobile } from '@/hooks/use-mobile';
|
||||||
|
|
||||||
export const description = "An interactive area chart"
|
export const description = 'An interactive area chart';
|
||||||
|
|
||||||
const chartData = [
|
const chartData = [
|
||||||
{ date: "2024-04-01", desktop: 222, mobile: 150 },
|
{ date: '2024-04-01', desktop: 222, mobile: 150 },
|
||||||
{ date: "2024-04-02", desktop: 97, mobile: 180 },
|
{ date: '2024-04-02', desktop: 97, mobile: 180 },
|
||||||
{ date: "2024-04-03", desktop: 167, mobile: 120 },
|
{ date: '2024-04-03', desktop: 167, mobile: 120 },
|
||||||
{ date: "2024-04-04", desktop: 242, mobile: 260 },
|
{ date: '2024-04-04', desktop: 242, mobile: 260 },
|
||||||
{ date: "2024-04-05", desktop: 373, mobile: 290 },
|
{ date: '2024-04-05', desktop: 373, mobile: 290 },
|
||||||
{ date: "2024-04-06", desktop: 301, mobile: 340 },
|
{ date: '2024-04-06', desktop: 301, mobile: 340 },
|
||||||
{ date: "2024-04-07", desktop: 245, mobile: 180 },
|
{ date: '2024-04-07', desktop: 245, mobile: 180 },
|
||||||
{ date: "2024-04-08", desktop: 409, mobile: 320 },
|
{ date: '2024-04-08', desktop: 409, mobile: 320 },
|
||||||
{ date: "2024-04-09", desktop: 59, mobile: 110 },
|
{ date: '2024-04-09', desktop: 59, mobile: 110 },
|
||||||
{ date: "2024-04-10", desktop: 261, mobile: 190 },
|
{ date: '2024-04-10', desktop: 261, mobile: 190 },
|
||||||
{ date: "2024-04-11", desktop: 327, mobile: 350 },
|
{ date: '2024-04-11', desktop: 327, mobile: 350 },
|
||||||
{ date: "2024-04-12", desktop: 292, mobile: 210 },
|
{ date: '2024-04-12', desktop: 292, mobile: 210 },
|
||||||
{ date: "2024-04-13", desktop: 342, mobile: 380 },
|
{ date: '2024-04-13', desktop: 342, mobile: 380 },
|
||||||
{ date: "2024-04-14", desktop: 137, mobile: 220 },
|
{ date: '2024-04-14', desktop: 137, mobile: 220 },
|
||||||
{ date: "2024-04-15", desktop: 120, mobile: 170 },
|
{ date: '2024-04-15', desktop: 120, mobile: 170 },
|
||||||
{ date: "2024-04-16", desktop: 138, mobile: 190 },
|
{ date: '2024-04-16', desktop: 138, mobile: 190 },
|
||||||
{ date: "2024-04-17", desktop: 446, mobile: 360 },
|
{ date: '2024-04-17', desktop: 446, mobile: 360 },
|
||||||
{ date: "2024-04-18", desktop: 364, mobile: 410 },
|
{ date: '2024-04-18', desktop: 364, mobile: 410 },
|
||||||
{ date: "2024-04-19", desktop: 243, mobile: 180 },
|
{ date: '2024-04-19', desktop: 243, mobile: 180 },
|
||||||
{ date: "2024-04-20", desktop: 89, mobile: 150 },
|
{ date: '2024-04-20', desktop: 89, mobile: 150 },
|
||||||
{ date: "2024-04-21", desktop: 137, mobile: 200 },
|
{ date: '2024-04-21', desktop: 137, mobile: 200 },
|
||||||
{ date: "2024-04-22", desktop: 224, mobile: 170 },
|
{ date: '2024-04-22', desktop: 224, mobile: 170 },
|
||||||
{ date: "2024-04-23", desktop: 138, mobile: 230 },
|
{ date: '2024-04-23', desktop: 138, mobile: 230 },
|
||||||
{ date: "2024-04-24", desktop: 387, mobile: 290 },
|
{ date: '2024-04-24', desktop: 387, mobile: 290 },
|
||||||
{ date: "2024-04-25", desktop: 215, mobile: 250 },
|
{ date: '2024-04-25', desktop: 215, mobile: 250 },
|
||||||
{ date: "2024-04-26", desktop: 75, mobile: 130 },
|
{ date: '2024-04-26', desktop: 75, mobile: 130 },
|
||||||
{ date: "2024-04-27", desktop: 383, mobile: 420 },
|
{ date: '2024-04-27', desktop: 383, mobile: 420 },
|
||||||
{ date: "2024-04-28", desktop: 122, mobile: 180 },
|
{ date: '2024-04-28', desktop: 122, mobile: 180 },
|
||||||
{ date: "2024-04-29", desktop: 315, mobile: 240 },
|
{ date: '2024-04-29', desktop: 315, mobile: 240 },
|
||||||
{ date: "2024-04-30", desktop: 454, mobile: 380 },
|
{ date: '2024-04-30', desktop: 454, mobile: 380 },
|
||||||
{ date: "2024-05-01", desktop: 165, mobile: 220 },
|
{ date: '2024-05-01', desktop: 165, mobile: 220 },
|
||||||
{ date: "2024-05-02", desktop: 293, mobile: 310 },
|
{ date: '2024-05-02', desktop: 293, mobile: 310 },
|
||||||
{ date: "2024-05-03", desktop: 247, mobile: 190 },
|
{ date: '2024-05-03', desktop: 247, mobile: 190 },
|
||||||
{ date: "2024-05-04", desktop: 385, mobile: 420 },
|
{ date: '2024-05-04', desktop: 385, mobile: 420 },
|
||||||
{ date: "2024-05-05", desktop: 481, mobile: 390 },
|
{ date: '2024-05-05', desktop: 481, mobile: 390 },
|
||||||
{ date: "2024-05-06", desktop: 498, mobile: 520 },
|
{ date: '2024-05-06', desktop: 498, mobile: 520 },
|
||||||
{ date: "2024-05-07", desktop: 388, mobile: 300 },
|
{ date: '2024-05-07', desktop: 388, mobile: 300 },
|
||||||
{ date: "2024-05-08", desktop: 149, mobile: 210 },
|
{ date: '2024-05-08', desktop: 149, mobile: 210 },
|
||||||
{ date: "2024-05-09", desktop: 227, mobile: 180 },
|
{ date: '2024-05-09', desktop: 227, mobile: 180 },
|
||||||
{ date: "2024-05-10", desktop: 293, mobile: 330 },
|
{ date: '2024-05-10', desktop: 293, mobile: 330 },
|
||||||
{ date: "2024-05-11", desktop: 335, mobile: 270 },
|
{ date: '2024-05-11', desktop: 335, mobile: 270 },
|
||||||
{ date: "2024-05-12", desktop: 197, mobile: 240 },
|
{ date: '2024-05-12', desktop: 197, mobile: 240 },
|
||||||
{ date: "2024-05-13", desktop: 197, mobile: 160 },
|
{ date: '2024-05-13', desktop: 197, mobile: 160 },
|
||||||
{ date: "2024-05-14", desktop: 448, mobile: 490 },
|
{ date: '2024-05-14', desktop: 448, mobile: 490 },
|
||||||
{ date: "2024-05-15", desktop: 473, mobile: 380 },
|
{ date: '2024-05-15', desktop: 473, mobile: 380 },
|
||||||
{ date: "2024-05-16", desktop: 338, mobile: 400 },
|
{ date: '2024-05-16', desktop: 338, mobile: 400 },
|
||||||
{ date: "2024-05-17", desktop: 499, mobile: 420 },
|
{ date: '2024-05-17', desktop: 499, mobile: 420 },
|
||||||
{ date: "2024-05-18", desktop: 315, mobile: 350 },
|
{ date: '2024-05-18', desktop: 315, mobile: 350 },
|
||||||
{ date: "2024-05-19", desktop: 235, mobile: 180 },
|
{ date: '2024-05-19', desktop: 235, mobile: 180 },
|
||||||
{ date: "2024-05-20", desktop: 177, mobile: 230 },
|
{ date: '2024-05-20', desktop: 177, mobile: 230 },
|
||||||
{ date: "2024-05-21", desktop: 82, mobile: 140 },
|
{ date: '2024-05-21', desktop: 82, mobile: 140 },
|
||||||
{ date: "2024-05-22", desktop: 81, mobile: 120 },
|
{ date: '2024-05-22', desktop: 81, mobile: 120 },
|
||||||
{ date: "2024-05-23", desktop: 252, mobile: 290 },
|
{ date: '2024-05-23', desktop: 252, mobile: 290 },
|
||||||
{ date: "2024-05-24", desktop: 294, mobile: 220 },
|
{ date: '2024-05-24', desktop: 294, mobile: 220 },
|
||||||
{ date: "2024-05-25", desktop: 201, mobile: 250 },
|
{ date: '2024-05-25', desktop: 201, mobile: 250 },
|
||||||
{ date: "2024-05-26", desktop: 213, mobile: 170 },
|
{ date: '2024-05-26', desktop: 213, mobile: 170 },
|
||||||
{ date: "2024-05-27", desktop: 420, mobile: 460 },
|
{ date: '2024-05-27', desktop: 420, mobile: 460 },
|
||||||
{ date: "2024-05-28", desktop: 233, mobile: 190 },
|
{ date: '2024-05-28', desktop: 233, mobile: 190 },
|
||||||
{ date: "2024-05-29", desktop: 78, mobile: 130 },
|
{ date: '2024-05-29', desktop: 78, mobile: 130 },
|
||||||
{ date: "2024-05-30", desktop: 340, mobile: 280 },
|
{ date: '2024-05-30', desktop: 340, mobile: 280 },
|
||||||
{ date: "2024-05-31", desktop: 178, mobile: 230 },
|
{ date: '2024-05-31', desktop: 178, mobile: 230 },
|
||||||
{ date: "2024-06-01", desktop: 178, mobile: 200 },
|
{ date: '2024-06-01', desktop: 178, mobile: 200 },
|
||||||
{ date: "2024-06-02", desktop: 470, mobile: 410 },
|
{ date: '2024-06-02', desktop: 470, mobile: 410 },
|
||||||
{ date: "2024-06-03", desktop: 103, mobile: 160 },
|
{ date: '2024-06-03', desktop: 103, mobile: 160 },
|
||||||
{ date: "2024-06-04", desktop: 439, mobile: 380 },
|
{ date: '2024-06-04', desktop: 439, mobile: 380 },
|
||||||
{ date: "2024-06-05", desktop: 88, mobile: 140 },
|
{ date: '2024-06-05', desktop: 88, mobile: 140 },
|
||||||
{ date: "2024-06-06", desktop: 294, mobile: 250 },
|
{ date: '2024-06-06', desktop: 294, mobile: 250 },
|
||||||
{ date: "2024-06-07", desktop: 323, mobile: 370 },
|
{ date: '2024-06-07', desktop: 323, mobile: 370 },
|
||||||
{ date: "2024-06-08", desktop: 385, mobile: 320 },
|
{ date: '2024-06-08', desktop: 385, mobile: 320 },
|
||||||
{ date: "2024-06-09", desktop: 438, mobile: 480 },
|
{ date: '2024-06-09', desktop: 438, mobile: 480 },
|
||||||
{ date: "2024-06-10", desktop: 155, mobile: 200 },
|
{ date: '2024-06-10', desktop: 155, mobile: 200 },
|
||||||
{ date: "2024-06-11", desktop: 92, mobile: 150 },
|
{ date: '2024-06-11', desktop: 92, mobile: 150 },
|
||||||
{ date: "2024-06-12", desktop: 492, mobile: 420 },
|
{ date: '2024-06-12', desktop: 492, mobile: 420 },
|
||||||
{ date: "2024-06-13", desktop: 81, mobile: 130 },
|
{ date: '2024-06-13', desktop: 81, mobile: 130 },
|
||||||
{ date: "2024-06-14", desktop: 426, mobile: 380 },
|
{ date: '2024-06-14', desktop: 426, mobile: 380 },
|
||||||
{ date: "2024-06-15", desktop: 307, mobile: 350 },
|
{ date: '2024-06-15', desktop: 307, mobile: 350 },
|
||||||
{ date: "2024-06-16", desktop: 371, mobile: 310 },
|
{ date: '2024-06-16', desktop: 371, mobile: 310 },
|
||||||
{ date: "2024-06-17", desktop: 475, mobile: 520 },
|
{ date: '2024-06-17', desktop: 475, mobile: 520 },
|
||||||
{ date: "2024-06-18", desktop: 107, mobile: 170 },
|
{ date: '2024-06-18', desktop: 107, mobile: 170 },
|
||||||
{ date: "2024-06-19", desktop: 341, mobile: 290 },
|
{ date: '2024-06-19', desktop: 341, mobile: 290 },
|
||||||
{ date: "2024-06-20", desktop: 408, mobile: 450 },
|
{ date: '2024-06-20', desktop: 408, mobile: 450 },
|
||||||
{ date: "2024-06-21", desktop: 169, mobile: 210 },
|
{ date: '2024-06-21', desktop: 169, mobile: 210 },
|
||||||
{ date: "2024-06-22", desktop: 317, mobile: 270 },
|
{ date: '2024-06-22', desktop: 317, mobile: 270 },
|
||||||
{ date: "2024-06-23", desktop: 480, mobile: 530 },
|
{ date: '2024-06-23', desktop: 480, mobile: 530 },
|
||||||
{ date: "2024-06-24", desktop: 132, mobile: 180 },
|
{ date: '2024-06-24', desktop: 132, mobile: 180 },
|
||||||
{ date: "2024-06-25", desktop: 141, mobile: 190 },
|
{ date: '2024-06-25', desktop: 141, mobile: 190 },
|
||||||
{ date: "2024-06-26", desktop: 434, mobile: 380 },
|
{ date: '2024-06-26', desktop: 434, mobile: 380 },
|
||||||
{ date: "2024-06-27", desktop: 448, mobile: 490 },
|
{ date: '2024-06-27', desktop: 448, mobile: 490 },
|
||||||
{ date: "2024-06-28", desktop: 149, mobile: 200 },
|
{ date: '2024-06-28', desktop: 149, mobile: 200 },
|
||||||
{ date: "2024-06-29", desktop: 103, mobile: 160 },
|
{ date: '2024-06-29', desktop: 103, mobile: 160 },
|
||||||
{ date: "2024-06-30", desktop: 446, mobile: 400 },
|
{ date: '2024-06-30', desktop: 446, mobile: 400 },
|
||||||
]
|
];
|
||||||
|
|
||||||
const chartConfig = {
|
const chartConfig = {
|
||||||
visitors: {
|
visitors: {
|
||||||
label: "Visitors",
|
label: 'Visitors',
|
||||||
},
|
},
|
||||||
desktop: {
|
desktop: {
|
||||||
label: "Desktop",
|
label: 'Desktop',
|
||||||
color: "var(--primary)",
|
color: 'var(--primary)',
|
||||||
},
|
},
|
||||||
mobile: {
|
mobile: {
|
||||||
label: "Mobile",
|
label: 'Mobile',
|
||||||
color: "var(--primary)",
|
color: 'var(--primary)',
|
||||||
},
|
},
|
||||||
} satisfies ChartConfig
|
} satisfies ChartConfig;
|
||||||
|
|
||||||
export function ChartAreaInteractive() {
|
export function ChartAreaInteractive() {
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile();
|
||||||
const [timeRange, setTimeRange] = React.useState("90d")
|
const [timeRange, setTimeRange] = React.useState('90d');
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
setTimeRange("7d")
|
setTimeRange('7d');
|
||||||
}
|
}
|
||||||
}, [isMobile])
|
}, [isMobile]);
|
||||||
|
|
||||||
const filteredData = chartData.filter((item) => {
|
const filteredData = chartData.filter((item) => {
|
||||||
const date = new Date(item.date)
|
const date = new Date(item.date);
|
||||||
const referenceDate = new Date("2024-06-30")
|
const referenceDate = new Date('2024-06-30');
|
||||||
let daysToSubtract = 90
|
let daysToSubtract = 90;
|
||||||
if (timeRange === "30d") {
|
if (timeRange === '30d') {
|
||||||
daysToSubtract = 30
|
daysToSubtract = 30;
|
||||||
} else if (timeRange === "7d") {
|
|
||||||
daysToSubtract = 7
|
|
||||||
}
|
}
|
||||||
const startDate = new Date(referenceDate)
|
else if (timeRange === '7d') {
|
||||||
startDate.setDate(startDate.getDate() - daysToSubtract)
|
daysToSubtract = 7;
|
||||||
return date >= startDate
|
}
|
||||||
})
|
const startDate = new Date(referenceDate);
|
||||||
|
startDate.setDate(startDate.getDate() - daysToSubtract);
|
||||||
|
return date >= startDate;
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className="@container/card">
|
<Card className="@container/card">
|
||||||
@@ -248,26 +250,26 @@ export function ChartAreaInteractive() {
|
|||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={32}
|
minTickGap={32}
|
||||||
tickFormatter={(value) => {
|
tickFormatter={(value) => {
|
||||||
const date = new Date(value)
|
const date = new Date(value);
|
||||||
return date.toLocaleDateString("en-US", {
|
return date.toLocaleDateString('en-US', {
|
||||||
month: "short",
|
month: 'short',
|
||||||
day: "numeric",
|
day: 'numeric',
|
||||||
})
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
cursor={false}
|
cursor={false}
|
||||||
content={
|
content={(
|
||||||
<ChartTooltipContent
|
<ChartTooltipContent
|
||||||
labelFormatter={(value) => {
|
labelFormatter={(value) => {
|
||||||
return new Date(value).toLocaleDateString("en-US", {
|
return new Date(value).toLocaleDateString('en-US', {
|
||||||
month: "short",
|
month: 'short',
|
||||||
day: "numeric",
|
day: 'numeric',
|
||||||
})
|
});
|
||||||
}}
|
}}
|
||||||
indicator="dot"
|
indicator="dot"
|
||||||
/>
|
/>
|
||||||
}
|
)}
|
||||||
/>
|
/>
|
||||||
<Area
|
<Area
|
||||||
dataKey="mobile"
|
dataKey="mobile"
|
||||||
@@ -287,5 +289,5 @@ export function ChartAreaInteractive() {
|
|||||||
</ChartContainer>
|
</ChartContainer>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,25 @@
|
|||||||
import * as React from "react"
|
import type { DragEndEvent, UniqueIdentifier } from '@dnd-kit/core';
|
||||||
|
import type { ColumnDef, ColumnFiltersState, Row, SortingState, VisibilityState } from '@tanstack/react-table';
|
||||||
|
import type { ChartConfig } from '@/components/ui/chart';
|
||||||
import {
|
import {
|
||||||
closestCenter,
|
closestCenter,
|
||||||
DndContext,
|
DndContext,
|
||||||
|
|
||||||
KeyboardSensor,
|
KeyboardSensor,
|
||||||
MouseSensor,
|
MouseSensor,
|
||||||
TouchSensor,
|
TouchSensor,
|
||||||
|
|
||||||
useSensor,
|
useSensor,
|
||||||
useSensors,
|
useSensors,
|
||||||
type DragEndEvent,
|
} from '@dnd-kit/core';
|
||||||
type UniqueIdentifier,
|
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||||
} from "@dnd-kit/core"
|
|
||||||
import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
|
|
||||||
import {
|
import {
|
||||||
arrayMove,
|
arrayMove,
|
||||||
SortableContext,
|
SortableContext,
|
||||||
useSortable,
|
useSortable,
|
||||||
verticalListSortingStrategy,
|
verticalListSortingStrategy,
|
||||||
} from "@dnd-kit/sortable"
|
} from '@dnd-kit/sortable';
|
||||||
import { CSS } from "@dnd-kit/utilities"
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import {
|
import {
|
||||||
IconChevronDown,
|
IconChevronDown,
|
||||||
IconChevronLeft,
|
IconChevronLeft,
|
||||||
@@ -31,8 +33,9 @@ import {
|
|||||||
IconLoader,
|
IconLoader,
|
||||||
IconPlus,
|
IconPlus,
|
||||||
IconTrendingUp,
|
IconTrendingUp,
|
||||||
} from "@tabler/icons-react"
|
} from '@tabler/icons-react';
|
||||||
import {
|
import {
|
||||||
|
|
||||||
flexRender,
|
flexRender,
|
||||||
getCoreRowModel,
|
getCoreRowModel,
|
||||||
getFacetedRowModel,
|
getFacetedRowModel,
|
||||||
@@ -40,27 +43,24 @@ import {
|
|||||||
getFilteredRowModel,
|
getFilteredRowModel,
|
||||||
getPaginationRowModel,
|
getPaginationRowModel,
|
||||||
getSortedRowModel,
|
getSortedRowModel,
|
||||||
useReactTable,
|
|
||||||
type ColumnDef,
|
|
||||||
type ColumnFiltersState,
|
|
||||||
type Row,
|
|
||||||
type SortingState,
|
|
||||||
type VisibilityState,
|
|
||||||
} from "@tanstack/react-table"
|
|
||||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
import { useIsMobile } from "@/hooks/use-mobile"
|
useReactTable,
|
||||||
import { Badge } from "@/components/ui/badge"
|
|
||||||
import { Button } from "@/components/ui/button"
|
} from '@tanstack/react-table';
|
||||||
|
import * as React from 'react';
|
||||||
|
import { Area, AreaChart, CartesianGrid, XAxis } from 'recharts';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { Badge } from '@/components/ui/badge';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
|
|
||||||
ChartContainer,
|
ChartContainer,
|
||||||
ChartTooltip,
|
ChartTooltip,
|
||||||
ChartTooltipContent,
|
ChartTooltipContent,
|
||||||
type ChartConfig,
|
} from '@/components/ui/chart';
|
||||||
} from "@/components/ui/chart"
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
import { Checkbox } from "@/components/ui/checkbox"
|
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerClose,
|
DrawerClose,
|
||||||
@@ -70,7 +70,7 @@ import {
|
|||||||
DrawerHeader,
|
DrawerHeader,
|
||||||
DrawerTitle,
|
DrawerTitle,
|
||||||
DrawerTrigger,
|
DrawerTrigger,
|
||||||
} from "@/components/ui/drawer"
|
} from '@/components/ui/drawer';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuCheckboxItem,
|
DropdownMenuCheckboxItem,
|
||||||
@@ -78,17 +78,17 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from '@/components/ui/dropdown-menu';
|
||||||
import { Input } from "@/components/ui/input"
|
import { Input } from '@/components/ui/input';
|
||||||
import { Label } from "@/components/ui/label"
|
import { Label } from '@/components/ui/label';
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
SelectItem,
|
SelectItem,
|
||||||
SelectTrigger,
|
SelectTrigger,
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select"
|
} from '@/components/ui/select';
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from '@/components/ui/separator';
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -96,13 +96,14 @@ import {
|
|||||||
TableHead,
|
TableHead,
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table"
|
} from '@/components/ui/table';
|
||||||
import {
|
import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsContent,
|
TabsContent,
|
||||||
TabsList,
|
TabsList,
|
||||||
TabsTrigger,
|
TabsTrigger,
|
||||||
} from "@/components/ui/tabs"
|
} from '@/components/ui/tabs';
|
||||||
|
import { useIsMobile } from '@/hooks/use-mobile';
|
||||||
|
|
||||||
export const schema = z.object({
|
export const schema = z.object({
|
||||||
id: z.number(),
|
id: z.number(),
|
||||||
@@ -112,13 +113,13 @@ export const schema = z.object({
|
|||||||
target: z.string(),
|
target: z.string(),
|
||||||
limit: z.string(),
|
limit: z.string(),
|
||||||
reviewer: z.string(),
|
reviewer: z.string(),
|
||||||
})
|
});
|
||||||
|
|
||||||
// Create a separate component for the drag handle
|
// Create a separate component for the drag handle
|
||||||
function DragHandle({ id }: { id: number }) {
|
function DragHandle({ id }: { id: number }) {
|
||||||
const { attributes, listeners } = useSortable({
|
const { attributes, listeners } = useSortable({
|
||||||
id,
|
id,
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
@@ -131,25 +132,25 @@ function DragHandle({ id }: { id: number }) {
|
|||||||
<IconGripVertical className="text-muted-foreground size-3" />
|
<IconGripVertical className="text-muted-foreground size-3" />
|
||||||
<span className="sr-only">Drag to reorder</span>
|
<span className="sr-only">Drag to reorder</span>
|
||||||
</Button>
|
</Button>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
||||||
{
|
{
|
||||||
id: "drag",
|
id: 'drag',
|
||||||
header: () => null,
|
header: () => null,
|
||||||
cell: ({ row }) => <DragHandle id={row.original.id} />,
|
cell: ({ row }) => <DragHandle id={row.original.id} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "select",
|
id: 'select',
|
||||||
header: ({ table }) => (
|
header: ({ table }) => (
|
||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
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"
|
aria-label="Select all"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -158,7 +159,7 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
|||||||
<div className="flex items-center justify-center">
|
<div className="flex items-center justify-center">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={row.getIsSelected()}
|
checked={row.getIsSelected()}
|
||||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
onCheckedChange={value => row.toggleSelected(!!value)}
|
||||||
aria-label="Select row"
|
aria-label="Select row"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -167,16 +168,16 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
|||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "header",
|
accessorKey: 'header',
|
||||||
header: "Header",
|
header: 'Header',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
return <TableCellViewer item={row.original} />
|
return <TableCellViewer item={row.original} />;
|
||||||
},
|
},
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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="text-muted-foreground px-1.5">
|
<Badge variant="outline" className="text-muted-foreground px-1.5">
|
||||||
@@ -186,31 +187,33 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "status",
|
accessorKey: 'status',
|
||||||
header: "Status",
|
header: 'Status',
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<Badge variant="outline" className="text-muted-foreground px-1.5">
|
<Badge variant="outline" className="text-muted-foreground px-1.5">
|
||||||
{row.original.status === "Done" ? (
|
{row.original.status === 'Done'
|
||||||
<IconCircleCheckFilled className="fill-green-500 dark:fill-green-400" />
|
? (
|
||||||
) : (
|
<IconCircleCheckFilled className="fill-green-500 dark:fill-green-400" />
|
||||||
<IconLoader />
|
)
|
||||||
)}
|
: (
|
||||||
|
<IconLoader />
|
||||||
|
)}
|
||||||
{row.original.status}
|
{row.original.status}
|
||||||
</Badge>
|
</Badge>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
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) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
|
toast.promise(new Promise(resolve => setTimeout(resolve, 1000)), {
|
||||||
loading: `Saving ${row.original.header}`,
|
loading: `Saving ${row.original.header}`,
|
||||||
success: "Done",
|
success: 'Done',
|
||||||
error: "Error",
|
error: 'Error',
|
||||||
})
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label htmlFor={`${row.original.id}-target`} className="sr-only">
|
<Label htmlFor={`${row.original.id}-target`} className="sr-only">
|
||||||
@@ -225,17 +228,17 @@ 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) => {
|
||||||
e.preventDefault()
|
e.preventDefault();
|
||||||
toast.promise(new Promise((resolve) => setTimeout(resolve, 1000)), {
|
toast.promise(new Promise(resolve => setTimeout(resolve, 1000)), {
|
||||||
loading: `Saving ${row.original.header}`,
|
loading: `Saving ${row.original.header}`,
|
||||||
success: "Done",
|
success: 'Done',
|
||||||
error: "Error",
|
error: 'Error',
|
||||||
})
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Label htmlFor={`${row.original.id}-limit`} className="sr-only">
|
<Label htmlFor={`${row.original.id}-limit`} className="sr-only">
|
||||||
@@ -250,13 +253,13 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "reviewer",
|
accessorKey: 'reviewer',
|
||||||
header: "Reviewer",
|
header: 'Reviewer',
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const isAssigned = row.original.reviewer !== "Assign reviewer"
|
const isAssigned = row.original.reviewer !== 'Assign reviewer';
|
||||||
|
|
||||||
if (isAssigned) {
|
if (isAssigned) {
|
||||||
return row.original.reviewer
|
return row.original.reviewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -280,11 +283,11 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
|||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</>
|
</>
|
||||||
)
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "actions",
|
id: 'actions',
|
||||||
cell: () => (
|
cell: () => (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
@@ -307,61 +310,61 @@ const columns: ColumnDef<z.infer<typeof schema>>[] = [
|
|||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
]
|
];
|
||||||
|
|
||||||
function DraggableRow({ row }: { row: Row<z.infer<typeof schema>> }) {
|
function DraggableRow({ row }: { row: Row<z.infer<typeof schema>> }) {
|
||||||
const { transform, transition, setNodeRef, isDragging } = useSortable({
|
const { transform, transition, setNodeRef, isDragging } = useSortable({
|
||||||
id: row.original.id,
|
id: row.original.id,
|
||||||
})
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableRow
|
<TableRow
|
||||||
data-state={row.getIsSelected() && "selected"}
|
data-state={row.getIsSelected() && 'selected'}
|
||||||
data-dragging={isDragging}
|
data-dragging={isDragging}
|
||||||
ref={setNodeRef}
|
ref={setNodeRef}
|
||||||
className="relative z-0 data-[dragging=true]:z-10 data-[dragging=true]:opacity-80"
|
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,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{row.getVisibleCells().map((cell) => (
|
{row.getVisibleCells().map(cell => (
|
||||||
<TableCell key={cell.id}>
|
<TableCell key={cell.id}>
|
||||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||||
</TableCell>
|
</TableCell>
|
||||||
))}
|
))}
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DataTable({
|
export function DataTable({
|
||||||
data: initialData,
|
data: initialData,
|
||||||
}: {
|
}: {
|
||||||
data: z.infer<typeof schema>[]
|
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] =
|
const [columnVisibility, setColumnVisibility]
|
||||||
React.useState<VisibilityState>({})
|
= React.useState<VisibilityState>({});
|
||||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||||
[]
|
[],
|
||||||
)
|
);
|
||||||
const [sorting, setSorting] = React.useState<SortingState>([])
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
const [pagination, setPagination] = React.useState({
|
const [pagination, setPagination] = React.useState({
|
||||||
pageIndex: 0,
|
pageIndex: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
})
|
});
|
||||||
const sortableId = React.useId()
|
const sortableId = React.useId();
|
||||||
const sensors = useSensors(
|
const sensors = useSensors(
|
||||||
useSensor(MouseSensor, {}),
|
useSensor(MouseSensor, {}),
|
||||||
useSensor(TouchSensor, {}),
|
useSensor(TouchSensor, {}),
|
||||||
useSensor(KeyboardSensor, {})
|
useSensor(KeyboardSensor, {}),
|
||||||
)
|
);
|
||||||
|
|
||||||
const dataIds = React.useMemo<UniqueIdentifier[]>(
|
const dataIds = React.useMemo<UniqueIdentifier[]>(
|
||||||
() => data?.map(({ id }) => id) || [],
|
() => data?.map(({ id }) => id) || [],
|
||||||
[data]
|
[data],
|
||||||
)
|
);
|
||||||
|
|
||||||
const table = useReactTable({
|
const table = useReactTable({
|
||||||
data,
|
data,
|
||||||
@@ -373,7 +376,7 @@ export function DataTable({
|
|||||||
columnFilters,
|
columnFilters,
|
||||||
pagination,
|
pagination,
|
||||||
},
|
},
|
||||||
getRowId: (row) => row.id.toString(),
|
getRowId: row => row.id.toString(),
|
||||||
enableRowSelection: true,
|
enableRowSelection: true,
|
||||||
onRowSelectionChange: setRowSelection,
|
onRowSelectionChange: setRowSelection,
|
||||||
onSortingChange: setSorting,
|
onSortingChange: setSorting,
|
||||||
@@ -386,16 +389,16 @@ export function DataTable({
|
|||||||
getSortedRowModel: getSortedRowModel(),
|
getSortedRowModel: getSortedRowModel(),
|
||||||
getFacetedRowModel: getFacetedRowModel(),
|
getFacetedRowModel: getFacetedRowModel(),
|
||||||
getFacetedUniqueValues: getFacetedUniqueValues(),
|
getFacetedUniqueValues: getFacetedUniqueValues(),
|
||||||
})
|
});
|
||||||
|
|
||||||
function handleDragEnd(event: DragEndEvent) {
|
function handleDragEnd(event: DragEndEvent) {
|
||||||
const { active, over } = event
|
const { active, over } = event;
|
||||||
if (active && over && active.id !== over.id) {
|
if (active && over && active.id !== over.id) {
|
||||||
setData((data) => {
|
setData((data) => {
|
||||||
const oldIndex = dataIds.indexOf(active.id)
|
const oldIndex = dataIds.indexOf(active.id);
|
||||||
const newIndex = dataIds.indexOf(over.id)
|
const newIndex = dataIds.indexOf(over.id);
|
||||||
return arrayMove(data, oldIndex, newIndex)
|
return arrayMove(data, oldIndex, newIndex);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -426,10 +429,14 @@ export function DataTable({
|
|||||||
<TabsList className="**:data-[slot=badge]:bg-muted-foreground/30 hidden **:data-[slot=badge]:size-5 **:data-[slot=badge]:rounded-full **:data-[slot=badge]:px-1 @4xl/main:flex">
|
<TabsList className="**:data-[slot=badge]:bg-muted-foreground/30 hidden **:data-[slot=badge]:size-5 **:data-[slot=badge]:rounded-full **:data-[slot=badge]:px-1 @4xl/main:flex">
|
||||||
<TabsTrigger value="outline">Outline</TabsTrigger>
|
<TabsTrigger value="outline">Outline</TabsTrigger>
|
||||||
<TabsTrigger value="past-performance">
|
<TabsTrigger value="past-performance">
|
||||||
Past Performance <Badge variant="secondary">3</Badge>
|
Past Performance
|
||||||
|
{' '}
|
||||||
|
<Badge variant="secondary">3</Badge>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="key-personnel">
|
<TabsTrigger value="key-personnel">
|
||||||
Key Personnel <Badge variant="secondary">2</Badge>
|
Key Personnel
|
||||||
|
{' '}
|
||||||
|
<Badge variant="secondary">2</Badge>
|
||||||
</TabsTrigger>
|
</TabsTrigger>
|
||||||
<TabsTrigger value="focus-documents">Focus Documents</TabsTrigger>
|
<TabsTrigger value="focus-documents">Focus Documents</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
@@ -447,9 +454,9 @@ export function DataTable({
|
|||||||
{table
|
{table
|
||||||
.getAllColumns()
|
.getAllColumns()
|
||||||
.filter(
|
.filter(
|
||||||
(column) =>
|
column =>
|
||||||
typeof column.accessorFn !== "undefined" &&
|
typeof column.accessorFn !== 'undefined'
|
||||||
column.getCanHide()
|
&& column.getCanHide(),
|
||||||
)
|
)
|
||||||
.map((column) => {
|
.map((column) => {
|
||||||
return (
|
return (
|
||||||
@@ -457,13 +464,12 @@ export function DataTable({
|
|||||||
key={column.id}
|
key={column.id}
|
||||||
className="capitalize"
|
className="capitalize"
|
||||||
checked={column.getIsVisible()}
|
checked={column.getIsVisible()}
|
||||||
onCheckedChange={(value) =>
|
onCheckedChange={value =>
|
||||||
column.toggleVisibility(!!value)
|
column.toggleVisibility(!!value)}
|
||||||
}
|
|
||||||
>
|
>
|
||||||
{column.id}
|
{column.id}
|
||||||
</DropdownMenuCheckboxItem>
|
</DropdownMenuCheckboxItem>
|
||||||
)
|
);
|
||||||
})}
|
})}
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
@@ -487,7 +493,7 @@ export function DataTable({
|
|||||||
>
|
>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader className="bg-muted sticky top-0 z-10">
|
<TableHeader className="bg-muted sticky top-0 z-10">
|
||||||
{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 (
|
||||||
@@ -496,42 +502,49 @@ export function DataTable({
|
|||||||
? null
|
? null
|
||||||
: flexRender(
|
: flexRender(
|
||||||
header.column.columnDef.header,
|
header.column.columnDef.header,
|
||||||
header.getContext()
|
header.getContext(),
|
||||||
)}
|
)}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
)
|
);
|
||||||
})}
|
})}
|
||||||
</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}
|
<SortableContext
|
||||||
strategy={verticalListSortingStrategy}
|
items={dataIds}
|
||||||
>
|
strategy={verticalListSortingStrategy}
|
||||||
{table.getRowModel().rows.map((row) => (
|
>
|
||||||
<DraggableRow key={row.id} row={row} />
|
{table.getRowModel().rows.map(row => (
|
||||||
))}
|
<DraggableRow key={row.id} row={row} />
|
||||||
</SortableContext>
|
))}
|
||||||
) : (
|
</SortableContext>
|
||||||
<TableRow>
|
)
|
||||||
<TableCell
|
: (
|
||||||
colSpan={columns.length}
|
<TableRow>
|
||||||
className="h-24 text-center"
|
<TableCell
|
||||||
>
|
colSpan={columns.length}
|
||||||
No results.
|
className="h-24 text-center"
|
||||||
</TableCell>
|
>
|
||||||
</TableRow>
|
No results.
|
||||||
)}
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
</TableBody>
|
</TableBody>
|
||||||
</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="text-muted-foreground hidden flex-1 text-sm lg:flex">
|
<div className="text-muted-foreground hidden flex-1 text-sm lg:flex">
|
||||||
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
{table.getFilteredSelectedRowModel().rows.length}
|
||||||
{table.getFilteredRowModel().rows.length} row(s) selected.
|
{' '}
|
||||||
|
of
|
||||||
|
{' '}
|
||||||
|
{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">
|
||||||
@@ -541,7 +554,7 @@ export function DataTable({
|
|||||||
<Select
|
<Select
|
||||||
value={`${table.getState().pagination.pageSize}`}
|
value={`${table.getState().pagination.pageSize}`}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
table.setPageSize(Number(value))
|
table.setPageSize(Number(value));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger size="sm" className="w-20" id="rows-per-page">
|
<SelectTrigger size="sm" className="w-20" id="rows-per-page">
|
||||||
@@ -550,7 +563,7 @@ export function DataTable({
|
|||||||
/>
|
/>
|
||||||
</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}
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
@@ -559,7 +572,12 @@ export function DataTable({
|
|||||||
</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{" "}
|
Page
|
||||||
|
{' '}
|
||||||
|
{table.getState().pagination.pageIndex + 1}
|
||||||
|
{' '}
|
||||||
|
of
|
||||||
|
{' '}
|
||||||
{table.getPageCount()}
|
{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">
|
||||||
@@ -622,34 +640,34 @@ export function DataTable({
|
|||||||
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
|
<div className="aspect-video w-full flex-1 rounded-lg border border-dashed"></div>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const chartData = [
|
const chartData = [
|
||||||
{ month: "January", desktop: 186, mobile: 80 },
|
{ month: 'January', desktop: 186, mobile: 80 },
|
||||||
{ month: "February", desktop: 305, mobile: 200 },
|
{ month: 'February', desktop: 305, mobile: 200 },
|
||||||
{ month: "March", desktop: 237, mobile: 120 },
|
{ month: 'March', desktop: 237, mobile: 120 },
|
||||||
{ month: "April", desktop: 73, mobile: 190 },
|
{ month: 'April', desktop: 73, mobile: 190 },
|
||||||
{ month: "May", desktop: 209, mobile: 130 },
|
{ month: 'May', desktop: 209, mobile: 130 },
|
||||||
{ month: "June", desktop: 214, mobile: 140 },
|
{ month: 'June', desktop: 214, mobile: 140 },
|
||||||
]
|
];
|
||||||
|
|
||||||
const chartConfig = {
|
const chartConfig = {
|
||||||
desktop: {
|
desktop: {
|
||||||
label: "Desktop",
|
label: 'Desktop',
|
||||||
color: "var(--primary)",
|
color: 'var(--primary)',
|
||||||
},
|
},
|
||||||
mobile: {
|
mobile: {
|
||||||
label: "Mobile",
|
label: 'Mobile',
|
||||||
color: "var(--primary)",
|
color: 'var(--primary)',
|
||||||
},
|
},
|
||||||
} satisfies ChartConfig
|
} satisfies ChartConfig;
|
||||||
|
|
||||||
function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
||||||
const isMobile = useIsMobile()
|
const isMobile = useIsMobile();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Drawer direction={isMobile ? "bottom" : "right"}>
|
<Drawer direction={isMobile ? 'bottom' : 'right'}>
|
||||||
<DrawerTrigger asChild>
|
<DrawerTrigger asChild>
|
||||||
<Button variant="link" className="text-foreground w-fit px-0 text-left">
|
<Button variant="link" className="text-foreground w-fit px-0 text-left">
|
||||||
{item.header}
|
{item.header}
|
||||||
@@ -680,7 +698,7 @@ function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
|||||||
tickLine={false}
|
tickLine={false}
|
||||||
axisLine={false}
|
axisLine={false}
|
||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
tickFormatter={(value) => value.slice(0, 3)}
|
tickFormatter={value => value.slice(0, 3)}
|
||||||
hide
|
hide
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
@@ -708,7 +726,8 @@ function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
|||||||
<Separator />
|
<Separator />
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<div className="flex gap-2 leading-none font-medium">
|
<div className="flex gap-2 leading-none font-medium">
|
||||||
Trending up by 5.2% this month{" "}
|
Trending up by 5.2% this month
|
||||||
|
{' '}
|
||||||
<IconTrendingUp className="size-4" />
|
<IconTrendingUp className="size-4" />
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted-foreground">
|
<div className="text-muted-foreground">
|
||||||
@@ -801,5 +820,5 @@ function TableCellViewer({ item }: { item: z.infer<typeof schema> }) {
|
|||||||
</DrawerFooter>
|
</DrawerFooter>
|
||||||
</DrawerContent>
|
</DrawerContent>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
"use client"
|
'use client';
|
||||||
|
|
||||||
|
import type { Icon } from '@tabler/icons-react';
|
||||||
import {
|
import {
|
||||||
|
|
||||||
IconDots,
|
IconDots,
|
||||||
IconFolder,
|
IconFolder,
|
||||||
IconShare3,
|
IconShare3,
|
||||||
IconTrash,
|
IconTrash,
|
||||||
type Icon,
|
} from '@tabler/icons-react';
|
||||||
} from "@tabler/icons-react"
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -14,7 +15,7 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from '@/components/ui/dropdown-menu';
|
||||||
import {
|
import {
|
||||||
SidebarGroup,
|
SidebarGroup,
|
||||||
SidebarGroupLabel,
|
SidebarGroupLabel,
|
||||||
@@ -23,24 +24,24 @@ import {
|
|||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
useSidebar,
|
useSidebar,
|
||||||
} from "@/components/ui/sidebar"
|
} from '@/components/ui/sidebar';
|
||||||
|
|
||||||
export function NavDocuments({
|
export function NavDocuments({
|
||||||
items,
|
items,
|
||||||
}: {
|
}: {
|
||||||
items: {
|
items: {
|
||||||
name: string
|
name: string;
|
||||||
url: string
|
url: string;
|
||||||
icon: Icon
|
icon: Icon;
|
||||||
}[]
|
}[];
|
||||||
}) {
|
}) {
|
||||||
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 asChild>
|
||||||
<a href={item.url}>
|
<a href={item.url}>
|
||||||
@@ -60,8 +61,8 @@ export function NavDocuments({
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent
|
<DropdownMenuContent
|
||||||
className="w-24 rounded-lg"
|
className="w-24 rounded-lg"
|
||||||
side={isMobile ? "bottom" : "right"}
|
side={isMobile ? 'bottom' : 'right'}
|
||||||
align={isMobile ? "end" : "start"}
|
align={isMobile ? 'end' : 'start'}
|
||||||
>
|
>
|
||||||
<DropdownMenuItem>
|
<DropdownMenuItem>
|
||||||
<IconFolder />
|
<IconFolder />
|
||||||
@@ -88,5 +89,5 @@ export function NavDocuments({
|
|||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client"
|
'use client';
|
||||||
|
|
||||||
import * as React from "react"
|
import type { Icon } from '@tabler/icons-react';
|
||||||
import { type Icon } from "@tabler/icons-react"
|
import * as React from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SidebarGroup,
|
SidebarGroup,
|
||||||
@@ -9,23 +9,23 @@ import {
|
|||||||
SidebarMenu,
|
SidebarMenu,
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
} from "@/components/ui/sidebar"
|
} from '@/components/ui/sidebar';
|
||||||
|
|
||||||
export function NavSecondary({
|
export function NavSecondary({
|
||||||
items,
|
items,
|
||||||
...props
|
...props
|
||||||
}: {
|
}: {
|
||||||
items: {
|
items: {
|
||||||
title: string
|
title: string;
|
||||||
url: string
|
url: string;
|
||||||
icon: Icon
|
icon: Icon;
|
||||||
}[]
|
}[];
|
||||||
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
|
} & React.ComponentPropsWithoutRef<typeof SidebarGroup>) {
|
||||||
return (
|
return (
|
||||||
<SidebarGroup {...props}>
|
<SidebarGroup {...props}>
|
||||||
<SidebarGroupContent>
|
<SidebarGroupContent>
|
||||||
<SidebarMenu>
|
<SidebarMenu>
|
||||||
{items.map((item) => (
|
{items.map(item => (
|
||||||
<SidebarMenuItem key={item.title}>
|
<SidebarMenuItem key={item.title}>
|
||||||
<SidebarMenuButton asChild>
|
<SidebarMenuButton asChild>
|
||||||
<a href={item.url}>
|
<a href={item.url}>
|
||||||
@@ -38,5 +38,5 @@ export function NavSecondary({
|
|||||||
</SidebarMenu>
|
</SidebarMenu>
|
||||||
</SidebarGroupContent>
|
</SidebarGroupContent>
|
||||||
</SidebarGroup>
|
</SidebarGroup>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import * as React from "react"
|
import * as React from 'react';
|
||||||
|
|
||||||
const MOBILE_BREAKPOINT = 768
|
const MOBILE_BREAKPOINT = 768;
|
||||||
|
|
||||||
export function useIsMobile() {
|
export function useIsMobile() {
|
||||||
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)
|
const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
|
const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`);
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||||
}
|
};
|
||||||
mql.addEventListener("change", onChange)
|
mql.addEventListener('change', onChange);
|
||||||
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
|
setIsMobile(window.innerWidth < MOBILE_BREAKPOINT);
|
||||||
return () => mql.removeEventListener("change", onChange)
|
return () => mql.removeEventListener('change', onChange);
|
||||||
}, [])
|
}, []);
|
||||||
|
|
||||||
return !!isMobile
|
return !!isMobile;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,18 +16,18 @@ function RouteComponent() {
|
|||||||
const { email } = Route.useSearch();
|
const { email } = Route.useSearch();
|
||||||
return email !== undefined
|
return email !== undefined
|
||||||
? (
|
? (
|
||||||
<div className="
|
<div className="
|
||||||
bg-background flex min-h-svh flex-row items-center justify-center gap-6 p-6 md:p-10"
|
bg-background flex min-h-svh flex-row items-center justify-center gap-6 p-6 md:p-10"
|
||||||
>
|
>
|
||||||
<NixOSLogo className="size-12" />
|
<NixOSLogo className="size-12" />
|
||||||
<span
|
<span
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
className="mx-2 inline-block h-6 w-px bg-current opacity-40"
|
className="mx-2 inline-block h-6 w-px bg-current opacity-40"
|
||||||
/>
|
/>
|
||||||
登录链接已发送至
|
登录链接已发送至
|
||||||
{' '}
|
{' '}
|
||||||
{email}
|
{email}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: <Navigate to="/login" />;
|
: <Navigate to="/login" />;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./src/*"],
|
"@/*": ["./src/*"]
|
||||||
},
|
},
|
||||||
"types": ["vite/client", "vite-plugin-svgr/client"],
|
"types": ["vite/client", "vite-plugin-svgr/client"],
|
||||||
"allowImportingTsExtensions": true,
|
"allowImportingTsExtensions": true,
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"verbatimModuleSyntax": true,
|
"verbatimModuleSyntax": true,
|
||||||
"erasableSyntaxOnly": true,
|
"erasableSyntaxOnly": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"noUncheckedSideEffectImports": true,
|
"noUncheckedSideEffectImports": true
|
||||||
},
|
},
|
||||||
"include": ["src"],
|
"include": ["src"]
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user