mirror of
https://github.com/i701/sarlink-portal.git
synced 2025-07-01 03:05:55 +00:00
refactor: streamline package.json and tailwind.config.ts
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 1m56s
Some checks failed
Build and Push Docker Images / Build and Push Docker Images (push) Failing after 1m56s
- Simplified dependencies in package.json by removing unused packages and organizing existing ones. - Updated tailwind.config.ts for better readability and maintainability, ensuring consistent formatting and structure.
This commit is contained in:
@ -1,3 +1,9 @@
|
|||||||
{
|
{
|
||||||
"extends": ["next/core-web-vitals", "next/typescript"]
|
"extends": [
|
||||||
}
|
"next/core-web-vitals",
|
||||||
|
"next/typescript"
|
||||||
|
],
|
||||||
|
"rules": {
|
||||||
|
"@typescript-eslint/no-explicit-any": "warn"
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
import { authOptions } from "@/app/auth";
|
import { authOptions } from "@/app/auth";
|
||||||
import { DevicesTable } from "@/components/devices-table";
|
import { DevicesTable } from "@/components/devices-table";
|
||||||
import DeviceFilter from "@/components/devices/device-filter";
|
import DynamicFilter from "@/components/generic-filter";
|
||||||
import AddDeviceDialogForm from "@/components/user/add-device-dialog";
|
import AddDeviceDialogForm from "@/components/user/add-device-dialog";
|
||||||
import { getServerSession } from "next-auth";
|
import { getServerSession } from "next-auth";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
@ -29,7 +29,31 @@ export default async function Devices({
|
|||||||
id="user-filters"
|
id="user-filters"
|
||||||
className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-endO"
|
className=" pb-4 gap-4 flex sm:flex-row flex-col items-start justify-endO"
|
||||||
>
|
>
|
||||||
<DeviceFilter />
|
{/* <DeviceFilter /> */}
|
||||||
|
<DynamicFilter
|
||||||
|
description="Filter devices by name, MAC address, or vendor."
|
||||||
|
title="Device Filter"
|
||||||
|
inputs={[
|
||||||
|
{
|
||||||
|
name: "name",
|
||||||
|
label: "Device Name",
|
||||||
|
type: "string",
|
||||||
|
placeholder: "Enter device name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "mac",
|
||||||
|
label: "MAC Address",
|
||||||
|
type: "string",
|
||||||
|
placeholder: "Enter MAC address",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "vendor",
|
||||||
|
label: "Vendor",
|
||||||
|
type: "string",
|
||||||
|
placeholder: "Enter vendor name",
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Suspense key={query || page} fallback={<DevicesTableSkeleton />}>
|
<Suspense key={query || page} fallback={<DevicesTableSkeleton />}>
|
||||||
<DevicesTable parentalControl={false} searchParams={searchParams} />
|
<DevicesTable parentalControl={false} searchParams={searchParams} />
|
||||||
|
452
components/generic-filter.tsx
Normal file
452
components/generic-filter.tsx
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
"use client";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import {
|
||||||
|
Drawer,
|
||||||
|
DrawerClose,
|
||||||
|
DrawerContent,
|
||||||
|
DrawerDescription,
|
||||||
|
DrawerFooter,
|
||||||
|
DrawerHeader,
|
||||||
|
DrawerTitle,
|
||||||
|
DrawerTrigger,
|
||||||
|
} from "@/components/ui/drawer";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Label } from "@/components/ui/label"; // Assuming shadcn Label
|
||||||
|
import { cn } from "@/lib/utils";
|
||||||
|
import { ListFilter, Loader2, X } from "lucide-react";
|
||||||
|
import { usePathname, useRouter, useSearchParams } from "next/navigation";
|
||||||
|
import { useEffect, useMemo, useState, useTransition } from "react";
|
||||||
|
import { RadioGroup, RadioGroupItem } from "./ui/radio-group";
|
||||||
|
|
||||||
|
interface CheckboxGroupProps {
|
||||||
|
label: string;
|
||||||
|
options: Array<{ value: string; label: string }>;
|
||||||
|
selectedValues: string[];
|
||||||
|
onChange: (values: string[]) => void;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CheckboxGroup: React.FC<CheckboxGroupProps> = ({
|
||||||
|
label,
|
||||||
|
options,
|
||||||
|
selectedValues,
|
||||||
|
onChange,
|
||||||
|
name,
|
||||||
|
}) => {
|
||||||
|
const handleCheckedChange = (value: string, checked: boolean) => {
|
||||||
|
if (checked) {
|
||||||
|
onChange([...selectedValues, value]);
|
||||||
|
} else {
|
||||||
|
onChange(selectedValues.filter((v) => v !== value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2 p-2 border rounded-md">
|
||||||
|
<Label className="font-semibold text-sm">{label}</Label>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{options.map((option) => (
|
||||||
|
<div key={`${name}-${option.value}`} className="flex items-center space-x-2">
|
||||||
|
<Checkbox
|
||||||
|
id={`${name}-${option.value}`}
|
||||||
|
checked={selectedValues.includes(option.value)}
|
||||||
|
onCheckedChange={(checked) =>
|
||||||
|
handleCheckedChange(option.value, !!checked)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Label htmlFor={`${name}-${option.value}`}>{option.label}</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
type FilterOption = { value: string; label: string };
|
||||||
|
|
||||||
|
type FilterInputConfig<TKey extends string> =
|
||||||
|
| {
|
||||||
|
name: TKey;
|
||||||
|
label: string;
|
||||||
|
placeholder: string;
|
||||||
|
type: "string" | "number";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: TKey;
|
||||||
|
label: string;
|
||||||
|
type: "checkbox-group";
|
||||||
|
options: FilterOption[];
|
||||||
|
serialize?: (values: string[]) => string;
|
||||||
|
deserialize?: (urlValue: string) => string[];
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
name: TKey;
|
||||||
|
label: string;
|
||||||
|
type: "radio-group";
|
||||||
|
options: FilterOption[];
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility type to extract the config for a specific key from the array
|
||||||
|
type GetConfigForKey<TKey, TConfigArray extends FilterInputConfig<string>[]> =
|
||||||
|
TConfigArray extends ReadonlyArray<infer U>
|
||||||
|
? U extends FilterInputConfig<string> & { name: TKey }
|
||||||
|
? U
|
||||||
|
: never
|
||||||
|
: never;
|
||||||
|
|
||||||
|
// This type maps filter keys to their appropriate value types (string or string[])
|
||||||
|
type FilterValues<
|
||||||
|
TFilterKeys extends string,
|
||||||
|
TInputs extends FilterInputConfig<string>[],
|
||||||
|
> = {
|
||||||
|
[K in TFilterKeys]: GetConfigForKey<K, TInputs> extends { type: "checkbox-group" }
|
||||||
|
? string[]
|
||||||
|
: GetConfigForKey<K, TInputs> extends { type: "radio-group" }
|
||||||
|
? string
|
||||||
|
: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Default serialization/deserialization for checkbox-group if not provided
|
||||||
|
const defaultSerialize = (values: string[]) => values.join(",");
|
||||||
|
const defaultDeserialize = (urlValue: string) =>
|
||||||
|
urlValue ? urlValue.split(",") : [];
|
||||||
|
|
||||||
|
// --- Component Props ---
|
||||||
|
interface DynamicFilterProps<
|
||||||
|
TFilterKeys extends string,
|
||||||
|
TInputs extends FilterInputConfig<TFilterKeys>[],
|
||||||
|
> {
|
||||||
|
inputs: TInputs;
|
||||||
|
title?: string;
|
||||||
|
description?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Main DynamicFilter Component ---
|
||||||
|
export default function DynamicFilter<
|
||||||
|
TFilterKeys extends string,
|
||||||
|
TInputs extends FilterInputConfig<TFilterKeys>[],
|
||||||
|
>({
|
||||||
|
inputs,
|
||||||
|
title = "Filters",
|
||||||
|
description = "Select your desired filters here",
|
||||||
|
}: DynamicFilterProps<TFilterKeys, TInputs>) {
|
||||||
|
const { replace } = useRouter();
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const [disabled, startTransition] = useTransition();
|
||||||
|
const searchParams = useSearchParams();
|
||||||
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Use a ref or memoized value for URLSearchParams to avoid re-creations
|
||||||
|
// and ensure it's always based on the latest searchParams.
|
||||||
|
const currentParams = useMemo(
|
||||||
|
() => new URLSearchParams(searchParams.toString()),
|
||||||
|
[searchParams],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize local state for input values based on URL
|
||||||
|
const initialInputState = useMemo(() => {
|
||||||
|
const initialState: Partial<FilterValues<TFilterKeys, TInputs>> = {};
|
||||||
|
for (const input of inputs) {
|
||||||
|
const urlValue = searchParams.get(input.name);
|
||||||
|
if (input.type === "checkbox-group") {
|
||||||
|
const deserialize = input.deserialize || defaultDeserialize;
|
||||||
|
(initialState as FilterValues<TFilterKeys, TInputs>)[input.name] = deserialize(urlValue || "") as FilterValues<TFilterKeys, TInputs>[typeof input.name];
|
||||||
|
} else {
|
||||||
|
(initialState as FilterValues<TFilterKeys, TInputs>)[input.name] = (urlValue || "") as FilterValues<TFilterKeys, TInputs>[typeof input.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return initialState as FilterValues<TFilterKeys, TInputs>;
|
||||||
|
}, [inputs, searchParams]); // Re-initialize if inputs config or URL searchParams change
|
||||||
|
|
||||||
|
const [inputValues, setInputValues] = useState<
|
||||||
|
FilterValues<TFilterKeys, TInputs>
|
||||||
|
>(initialInputState);
|
||||||
|
|
||||||
|
// Update local state if URL searchParams change while drawer is closed
|
||||||
|
// This is important if filters are applied externally or on page load
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) {
|
||||||
|
setInputValues(initialInputState);
|
||||||
|
}
|
||||||
|
}, [isOpen, initialInputState]);
|
||||||
|
|
||||||
|
|
||||||
|
// Handler for text/number input changes
|
||||||
|
const handleTextOrNumberInputChange =
|
||||||
|
(name: TFilterKeys) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setInputValues((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: e.target.value,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handler for checkbox group changes
|
||||||
|
const handleCheckboxGroupChange =
|
||||||
|
(name: TFilterKeys) => (values: string[]) => {
|
||||||
|
setInputValues((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[name]: values,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handles applying all filters
|
||||||
|
const handleApplyFilters = () => {
|
||||||
|
const newParams = new URLSearchParams(currentParams.toString()); // Start fresh with current params
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
const value = inputValues[input.name];
|
||||||
|
|
||||||
|
if (input.type === "checkbox-group") {
|
||||||
|
const serialize = input.serialize || defaultSerialize;
|
||||||
|
const serializedValue = serialize(value as string[]);
|
||||||
|
if (serializedValue) {
|
||||||
|
newParams.set(input.name, serializedValue);
|
||||||
|
} else {
|
||||||
|
newParams.delete(input.name);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// String/Number inputs
|
||||||
|
if (value) {
|
||||||
|
newParams.set(input.name, value as string);
|
||||||
|
} else {
|
||||||
|
newParams.delete(input.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newParams.set("page", "1"); // Always reset page on filter apply
|
||||||
|
|
||||||
|
startTransition(() => {
|
||||||
|
replace(`${pathname}?${newParams.toString()}`);
|
||||||
|
setIsOpen(false); // Close the drawer after applying filters
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handles clearing all filters
|
||||||
|
const handleClearFilters = () => {
|
||||||
|
|
||||||
|
// Reset local input values
|
||||||
|
const clearedInputState: Partial<FilterValues<TFilterKeys, TInputs>> = {};
|
||||||
|
for (const input of inputs) {
|
||||||
|
if (input.type === "checkbox-group") {
|
||||||
|
(clearedInputState as FilterValues<TFilterKeys, TInputs>)[input.name as TFilterKeys] = [] as any;
|
||||||
|
} else {
|
||||||
|
(clearedInputState as FilterValues<TFilterKeys, TInputs>)[input.name as TFilterKeys] = "" as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInputValues(clearedInputState as FilterValues<TFilterKeys, TInputs>);
|
||||||
|
|
||||||
|
startTransition(() => {
|
||||||
|
replace(pathname); // Navigate to base path
|
||||||
|
setIsOpen(false); // Close the drawer
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Dynamically constructs applied filters for badges
|
||||||
|
const appliedFilters = useMemo(() => {
|
||||||
|
const filters: Array<{
|
||||||
|
key: TFilterKeys;
|
||||||
|
value: string | string[];
|
||||||
|
label: string;
|
||||||
|
config: FilterInputConfig<TFilterKeys>;
|
||||||
|
}> = [];
|
||||||
|
|
||||||
|
for (const input of inputs) {
|
||||||
|
const urlValue = searchParams.get(input.name);
|
||||||
|
if (urlValue) {
|
||||||
|
if (input.type === "checkbox-group") {
|
||||||
|
const deserialize = input.deserialize || defaultDeserialize;
|
||||||
|
const deserialized = deserialize(urlValue);
|
||||||
|
if (deserialized.length > 0) {
|
||||||
|
filters.push({
|
||||||
|
key: input.name,
|
||||||
|
value: deserialized,
|
||||||
|
label: input.label,
|
||||||
|
config: input,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
filters.push({
|
||||||
|
key: input.name,
|
||||||
|
value: urlValue,
|
||||||
|
label: input.label,
|
||||||
|
config: input,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filters;
|
||||||
|
}, [searchParams, inputs]);
|
||||||
|
|
||||||
|
// Dynamic `prettyPrintFilter` for badges
|
||||||
|
const prettyPrintFilter = (
|
||||||
|
key: TFilterKeys,
|
||||||
|
value: string | string[],
|
||||||
|
config: FilterInputConfig<TFilterKeys>,
|
||||||
|
) => {
|
||||||
|
if (config.type === "checkbox-group") {
|
||||||
|
const labels = (value as string[])
|
||||||
|
.map(
|
||||||
|
(v) => config.options.find((opt) => opt.value === v)?.label || v,
|
||||||
|
)
|
||||||
|
.join(", ");
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{config.label.replace(/%20/g, " ")}: <span className="text-muted-foreground">{labels}</span>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{config.label}: <span className="text-muted-foreground">{value}</span>
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handles removing an individual filter
|
||||||
|
const handleRemoveFilter = (keyToRemove: TFilterKeys) => {
|
||||||
|
const newParams = new URLSearchParams(currentParams.toString());
|
||||||
|
newParams.delete(keyToRemove);
|
||||||
|
newParams.set("page", "1"); // Reset page after removing a filter
|
||||||
|
|
||||||
|
// Clear the specific input's local state
|
||||||
|
const inputConfig = inputs.find((input) => input.name === keyToRemove);
|
||||||
|
setInputValues((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[keyToRemove]: inputConfig?.type === "checkbox-group" ? [] : "",
|
||||||
|
}));
|
||||||
|
|
||||||
|
startTransition(() => {
|
||||||
|
replace(`${pathname}?${newParams.toString()}`);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-start justify-start gap-2 w-full">
|
||||||
|
<Drawer open={isOpen} onOpenChange={setIsOpen}>
|
||||||
|
<DrawerTrigger asChild>
|
||||||
|
<Button
|
||||||
|
className="w-full sm:w-48 flex items-end justify-between"
|
||||||
|
onClick={() => setIsOpen(!isOpen)}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Filter
|
||||||
|
<ListFilter />
|
||||||
|
</Button>
|
||||||
|
</DrawerTrigger>
|
||||||
|
<DrawerContent>
|
||||||
|
<div className="mx-auto w-full max-w-3xl">
|
||||||
|
<DrawerHeader>
|
||||||
|
<DrawerTitle>{title}</DrawerTitle>
|
||||||
|
<DrawerDescription asChild>
|
||||||
|
<div>{description}</div>
|
||||||
|
</DrawerDescription>
|
||||||
|
</DrawerHeader>
|
||||||
|
|
||||||
|
<div className="grid sm:grid-cols-3 gap-4 p-4">
|
||||||
|
{inputs.map((input) => {
|
||||||
|
switch (input.type) {
|
||||||
|
case "string":
|
||||||
|
case "number":
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
key={input.name}
|
||||||
|
placeholder={input.placeholder}
|
||||||
|
value={inputValues[input.name] as string}
|
||||||
|
onChange={handleTextOrNumberInputChange(input.name)}
|
||||||
|
type={input.type}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case "checkbox-group":
|
||||||
|
return (
|
||||||
|
<CheckboxGroup
|
||||||
|
key={input.name}
|
||||||
|
name={input.name}
|
||||||
|
label={input.label}
|
||||||
|
options={input.options}
|
||||||
|
selectedValues={inputValues[input.name] as string[]}
|
||||||
|
onChange={handleCheckboxGroupChange(input.name)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case "radio-group":
|
||||||
|
return (
|
||||||
|
<div key={input.name} className="flex flex-col gap-2 p-2 border rounded-md">
|
||||||
|
<Label className="font-semibold text-sm">{input.label}</Label>
|
||||||
|
<RadioGroup
|
||||||
|
value={inputValues[input.name] as string}
|
||||||
|
onValueChange={(value) =>
|
||||||
|
setInputValues((prev) => ({
|
||||||
|
...prev,
|
||||||
|
[input.name]: value,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{input.options.map((option) => (
|
||||||
|
<div key={`${input.name}-${option.value}`} className="flex items-center space-x-2">
|
||||||
|
<RadioGroupItem value={option.value} id={`${input.name}-${option.value}`} />
|
||||||
|
<Label htmlFor={`${input.name}-${option.value}`}>{option.label}</Label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</RadioGroup>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<DrawerFooter className="max-w-sm mx-auto">
|
||||||
|
<Button onClick={handleApplyFilters} disabled={disabled}>
|
||||||
|
{disabled ? (
|
||||||
|
<>
|
||||||
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
||||||
|
Applying...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>Apply Filters</>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" onClick={handleClearFilters}>
|
||||||
|
Clear Filters
|
||||||
|
</Button>
|
||||||
|
<DrawerClose asChild>
|
||||||
|
<Button variant="outline">Cancel</Button>
|
||||||
|
</DrawerClose>
|
||||||
|
</DrawerFooter>
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
<div className="flex gap-2 w-fit flex-wrap">
|
||||||
|
{appliedFilters.map((filter) => (
|
||||||
|
<Badge
|
||||||
|
aria-disabled={disabled}
|
||||||
|
variant={"outline"}
|
||||||
|
className={cn(
|
||||||
|
"flex p-2 gap-2 items-center justify-between",
|
||||||
|
{ "opacity-50 pointer-events-none": disabled },
|
||||||
|
)}
|
||||||
|
key={filter.key}
|
||||||
|
>
|
||||||
|
<span className="text-md">
|
||||||
|
{prettyPrintFilter(filter.key, filter.value, filter.config)}
|
||||||
|
</span>
|
||||||
|
{disabled ? (
|
||||||
|
<Loader2 className="animate-spin" size={16} />
|
||||||
|
) : (
|
||||||
|
<X
|
||||||
|
className="bg-sarLinkOrange/50 rounded-full p-1 hover:cursor-pointer hover:ring ring-sarLinkOrange"
|
||||||
|
size={16}
|
||||||
|
onClick={() => handleRemoveFilter(filter.key)}
|
||||||
|
>
|
||||||
|
Remove
|
||||||
|
</X>
|
||||||
|
)}
|
||||||
|
</Badge>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as AccordionPrimitive from "@radix-ui/react-accordion"
|
import { Accordion as AccordionPrimitive } from "radix-ui"
|
||||||
import { ChevronDown } from "lucide-react"
|
import { ChevronDown } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog"
|
import { AlertDialog as AlertDialogPrimitive } from "radix-ui"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
import { buttonVariants } from "@/components/ui/button"
|
import { buttonVariants } from "@/components/ui/button"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio"
|
import { AspectRatio as AspectRatioPrimitive } from "radix-ui"
|
||||||
|
|
||||||
const AspectRatio = AspectRatioPrimitive.Root
|
const AspectRatio = AspectRatioPrimitive.Root
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as AvatarPrimitive from "@radix-ui/react-avatar"
|
import { Avatar as AvatarPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
import { Slot as SlotPrimitive } from "radix-ui"
|
||||||
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
@ -45,7 +45,7 @@ const BreadcrumbLink = React.forwardRef<
|
|||||||
asChild?: boolean
|
asChild?: boolean
|
||||||
}
|
}
|
||||||
>(({ asChild, className, ...props }, ref) => {
|
>(({ asChild, className, ...props }, ref) => {
|
||||||
const Comp = asChild ? Slot : "a"
|
const Comp = asChild ? SlotPrimitive.Slot : "a"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Slot } from "@radix-ui/react-slot"
|
import { Slot as SlotPrimitive } from "radix-ui"
|
||||||
import { type VariantProps, cva } from "class-variance-authority"
|
import { type VariantProps, cva } from "class-variance-authority"
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ function Button({
|
|||||||
VariantProps<typeof buttonVariants> & {
|
VariantProps<typeof buttonVariants> & {
|
||||||
asChild?: boolean
|
asChild?: boolean
|
||||||
}) {
|
}) {
|
||||||
const Comp = asChild ? Slot : "button"
|
const Comp = asChild ? SlotPrimitive.Slot : "button"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
import { Checkbox as CheckboxPrimitive } from "radix-ui"
|
||||||
import { Check } from "lucide-react"
|
import { Check } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
|
import { Collapsible as CollapsiblePrimitive } from "radix-ui"
|
||||||
|
|
||||||
const Collapsible = CollapsiblePrimitive.Root
|
const Collapsible = CollapsiblePrimitive.Root
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { type DialogProps } from "@radix-ui/react-dialog"
|
import { type DialogProps } from "radix-ui"
|
||||||
import { Command as CommandPrimitive } from "cmdk"
|
import { Command as CommandPrimitive } from "cmdk"
|
||||||
import { Search } from "lucide-react"
|
import { Search } from "lucide-react"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ContextMenuPrimitive from "@radix-ui/react-context-menu"
|
import { ContextMenu as ContextMenuPrimitive } from "radix-ui"
|
||||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog"
|
import { Dialog as DialogPrimitive } from "radix-ui"
|
||||||
import { X } from "lucide-react"
|
import { X } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"
|
import { DropdownMenu as DropdownMenuPrimitive } from "radix-ui"
|
||||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
import { Label as LabelPrimitive, Slot as SlotPrimitive } from "radix-ui"
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
FormProvider,
|
FormProvider,
|
||||||
@ -104,13 +104,13 @@ const FormLabel = React.forwardRef<
|
|||||||
FormLabel.displayName = "FormLabel"
|
FormLabel.displayName = "FormLabel"
|
||||||
|
|
||||||
const FormControl = React.forwardRef<
|
const FormControl = React.forwardRef<
|
||||||
React.ElementRef<typeof Slot>,
|
React.ElementRef<typeof SlotPrimitive.Slot>,
|
||||||
React.ComponentPropsWithoutRef<typeof Slot>
|
React.ComponentPropsWithoutRef<typeof SlotPrimitive.Slot>
|
||||||
>(({ ...props }, ref) => {
|
>(({ ...props }, ref) => {
|
||||||
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Slot
|
<SlotPrimitive.Slot
|
||||||
ref={ref}
|
ref={ref}
|
||||||
id={formItemId}
|
id={formItemId}
|
||||||
aria-describedby={
|
aria-describedby={
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as HoverCardPrimitive from "@radix-ui/react-hover-card"
|
import { HoverCard as HoverCardPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as LabelPrimitive from "@radix-ui/react-label"
|
import { Label as LabelPrimitive } from "radix-ui"
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as MenubarPrimitive from "@radix-ui/react-menubar"
|
import { Menubar as MenubarPrimitive } from "radix-ui"
|
||||||
import { Check, ChevronRight, Circle } from "lucide-react"
|
import { Check, ChevronRight, Circle } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu"
|
import { NavigationMenu as NavigationMenuPrimitive } from "radix-ui"
|
||||||
import { cva } from "class-variance-authority"
|
import { cva } from "class-variance-authority"
|
||||||
import { ChevronDown } from "lucide-react"
|
import { ChevronDown } from "lucide-react"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as PopoverPrimitive from "@radix-ui/react-popover"
|
import { Popover as PopoverPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ProgressPrimitive from "@radix-ui/react-progress"
|
import { Progress as ProgressPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
|
import { RadioGroup as RadioGroupPrimitive } from "radix-ui"
|
||||||
import { Circle } from "lucide-react"
|
import { Circle } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
|
import { ScrollArea as ScrollAreaPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as SelectPrimitive from "@radix-ui/react-select"
|
import { Select as SelectPrimitive } from "radix-ui"
|
||||||
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
import { Check, ChevronDown, ChevronUp } from "lucide-react"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as SeparatorPrimitive from "@radix-ui/react-separator"
|
import { Separator as SeparatorPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as SheetPrimitive from "@radix-ui/react-dialog"
|
import { Dialog as SheetPrimitive } from "radix-ui"
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
import { X } from "lucide-react"
|
import { X } from "lucide-react"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import { Slot } from "@radix-ui/react-slot"
|
import { Slot as SlotPrimitive } from "radix-ui"
|
||||||
import { VariantProps, cva } from "class-variance-authority"
|
import { VariantProps, cva } from "class-variance-authority"
|
||||||
import { PanelLeft } from "lucide-react"
|
import { PanelLeft } from "lucide-react"
|
||||||
|
|
||||||
@ -442,7 +442,7 @@ const SidebarGroupLabel = React.forwardRef<
|
|||||||
HTMLDivElement,
|
HTMLDivElement,
|
||||||
React.ComponentProps<"div"> & { asChild?: boolean }
|
React.ComponentProps<"div"> & { asChild?: boolean }
|
||||||
>(({ className, asChild = false, ...props }, ref) => {
|
>(({ className, asChild = false, ...props }, ref) => {
|
||||||
const Comp = asChild ? Slot : "div"
|
const Comp = asChild ? SlotPrimitive.Slot : "div"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
@ -463,7 +463,7 @@ const SidebarGroupAction = React.forwardRef<
|
|||||||
HTMLButtonElement,
|
HTMLButtonElement,
|
||||||
React.ComponentProps<"button"> & { asChild?: boolean }
|
React.ComponentProps<"button"> & { asChild?: boolean }
|
||||||
>(({ className, asChild = false, ...props }, ref) => {
|
>(({ className, asChild = false, ...props }, ref) => {
|
||||||
const Comp = asChild ? Slot : "button"
|
const Comp = asChild ? SlotPrimitive.Slot : "button"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
@ -563,7 +563,7 @@ const SidebarMenuButton = React.forwardRef<
|
|||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
) => {
|
) => {
|
||||||
const Comp = asChild ? Slot : "button"
|
const Comp = asChild ? SlotPrimitive.Slot : "button"
|
||||||
const { isMobile, state } = useSidebar()
|
const { isMobile, state } = useSidebar()
|
||||||
|
|
||||||
const button = (
|
const button = (
|
||||||
@ -609,7 +609,7 @@ const SidebarMenuAction = React.forwardRef<
|
|||||||
showOnHover?: boolean
|
showOnHover?: boolean
|
||||||
}
|
}
|
||||||
>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
|
>(({ className, asChild = false, showOnHover = false, ...props }, ref) => {
|
||||||
const Comp = asChild ? Slot : "button"
|
const Comp = asChild ? SlotPrimitive.Slot : "button"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
@ -723,7 +723,7 @@ const SidebarMenuSubButton = React.forwardRef<
|
|||||||
isActive?: boolean
|
isActive?: boolean
|
||||||
}
|
}
|
||||||
>(({ asChild = false, size = "md", isActive, className, ...props }, ref) => {
|
>(({ asChild = false, size = "md", isActive, className, ...props }, ref) => {
|
||||||
const Comp = asChild ? Slot : "a"
|
const Comp = asChild ? SlotPrimitive.Slot : "a"
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Comp
|
<Comp
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as SliderPrimitive from "@radix-ui/react-slider"
|
import { Slider as SliderPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as SwitchPrimitives from "@radix-ui/react-switch"
|
import { Switch as SwitchPrimitives } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
import { Tabs as TabsPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group"
|
import { ToggleGroup as ToggleGroupPrimitive } from "radix-ui"
|
||||||
import { type VariantProps } from "class-variance-authority"
|
import { type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as TogglePrimitive from "@radix-ui/react-toggle"
|
import { Toggle as TogglePrimitive } from "radix-ui"
|
||||||
import { cva, type VariantProps } from "class-variance-authority"
|
import { cva, type VariantProps } from "class-variance-authority"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
import * as React from "react"
|
||||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
import { Tooltip as TooltipPrimitive } from "radix-ui"
|
||||||
|
|
||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
18966
package-lock.json
generated
18966
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
142
package.json
142
package.json
@ -1,86 +1,60 @@
|
|||||||
{
|
{
|
||||||
"name": "sarlink-portal",
|
"name": "sarlink-portal",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@faker-js/faker": "^9.3.0",
|
"@faker-js/faker": "^9.3.0",
|
||||||
"@hookform/resolvers": "^5.1.1",
|
"@hookform/resolvers": "^5.1.1",
|
||||||
"@pyncz/tailwind-mask-image": "^2.0.0",
|
"@pyncz/tailwind-mask-image": "^2.0.0",
|
||||||
"@radix-ui/react-accordion": "^1.2.11",
|
"@tanstack/react-query": "^5.61.4",
|
||||||
"@radix-ui/react-alert-dialog": "^1.1.14",
|
"axios": "^1.8.4",
|
||||||
"@radix-ui/react-aspect-ratio": "^1.1.7",
|
"class-variance-authority": "^0.7.1",
|
||||||
"@radix-ui/react-avatar": "^1.1.10",
|
"clsx": "^2.1.1",
|
||||||
"@radix-ui/react-checkbox": "^1.3.2",
|
"cmdk": "^1.1.1",
|
||||||
"@radix-ui/react-collapsible": "^1.1.11",
|
"date-fns": "^4.1.0",
|
||||||
"@radix-ui/react-context-menu": "^2.2.15",
|
"embla-carousel-react": "^8.6.0",
|
||||||
"@radix-ui/react-dialog": "^1.1.14",
|
"input-otp": "^1.4.2",
|
||||||
"@radix-ui/react-dropdown-menu": "^2.1.15",
|
"jotai": "2.8.0",
|
||||||
"@radix-ui/react-hover-card": "^1.1.14",
|
"lucide-react": "^0.523.0",
|
||||||
"@radix-ui/react-label": "^2.1.7",
|
"millify": "^6.1.0",
|
||||||
"@radix-ui/react-menubar": "^1.1.15",
|
"moment": "^2.30.1",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.13",
|
"motion": "^12.15.0",
|
||||||
"@radix-ui/react-popover": "^1.1.14",
|
"next": "15.3.3",
|
||||||
"@radix-ui/react-progress": "^1.1.7",
|
"next-auth": "^4.24.11",
|
||||||
"@radix-ui/react-radio-group": "^1.3.7",
|
"next-themes": "^0.4.6",
|
||||||
"@radix-ui/react-scroll-area": "^1.2.9",
|
"nextjs-toploader": "^3.7.15",
|
||||||
"@radix-ui/react-select": "^2.2.5",
|
"nuqs": "^2.4.3",
|
||||||
"@radix-ui/react-separator": "^1.1.7",
|
"radix-ui": "^1.4.2",
|
||||||
"@radix-ui/react-slider": "^1.3.5",
|
"react": "19.1.0",
|
||||||
"@radix-ui/react-slot": "^1.2.3",
|
"react-aria-components": "^1.5.0",
|
||||||
"@radix-ui/react-switch": "^1.2.5",
|
"react-day-picker": "^9.7.0",
|
||||||
"@radix-ui/react-tabs": "^1.1.12",
|
"react-dom": "19.1.0",
|
||||||
"@radix-ui/react-toggle": "^1.1.9",
|
"react-hook-form": "^7.58.1",
|
||||||
"@radix-ui/react-toggle-group": "^1.1.10",
|
"react-phone-number-input": "^3.4.9",
|
||||||
"@radix-ui/react-tooltip": "^1.2.7",
|
"react-resizable-panels": "^3.0.3",
|
||||||
"@tanstack/react-query": "^5.61.4",
|
"recharts": "^3.0.0",
|
||||||
"axios": "^1.8.4",
|
"sonner": "^2.0.5",
|
||||||
"class-variance-authority": "^0.7.1",
|
"tailwind-merge": "^3.3.1",
|
||||||
"clsx": "^2.1.1",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"cmdk": "^1.1.1",
|
"vaul": "^1.1.2",
|
||||||
"date-fns": "^4.1.0",
|
"zod": "^3.25.67"
|
||||||
"embla-carousel-react": "^8.6.0",
|
},
|
||||||
"input-otp": "^1.4.2",
|
"devDependencies": {
|
||||||
"jotai": "2.8.0",
|
"@types/node": "^22.10.2",
|
||||||
"lucide-react": "^0.523.0",
|
"@types/react": "^19.1.0",
|
||||||
"millify": "^6.1.0",
|
"@types/react-dom": "^19.1.2",
|
||||||
"moment": "^2.30.1",
|
"eslint": "^9.17.0",
|
||||||
"motion": "^12.15.0",
|
"eslint-config-next": "15.1.2",
|
||||||
"next": "15.3.3",
|
"postcss": "^8.4.49",
|
||||||
"next-auth": "^4.24.11",
|
"tailwindcss": "^3.4.17",
|
||||||
"next-themes": "^0.4.6",
|
"tailwindcss-motion": "^1.1.0",
|
||||||
"nextjs-toploader": "^3.7.15",
|
"ts-node": "^10.9.2",
|
||||||
"nuqs": "^2.4.3",
|
"typescript": "^5.8.3"
|
||||||
"react": "19.1.0",
|
}
|
||||||
"react-aria-components": "^1.5.0",
|
|
||||||
"react-day-picker": "^9.7.0",
|
|
||||||
"react-dom": "19.1.0",
|
|
||||||
"react-hook-form": "^7.58.1",
|
|
||||||
"react-phone-number-input": "^3.4.9",
|
|
||||||
"react-resizable-panels": "^3.0.3",
|
|
||||||
"recharts": "^3.0.0",
|
|
||||||
"sonner": "^2.0.5",
|
|
||||||
"tailwind-merge": "^3.3.1",
|
|
||||||
"tailwindcss-animate": "^1.0.7",
|
|
||||||
"vaul": "^1.1.2",
|
|
||||||
"zod": "^3.25.67"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@biomejs/biome": "1.9.4",
|
|
||||||
"@types/node": "^22.10.2",
|
|
||||||
"@types/react": "^19.1.0",
|
|
||||||
"@types/react-dom": "^19.1.2",
|
|
||||||
"eslint": "^9.17.0",
|
|
||||||
"eslint-config-next": "15.1.2",
|
|
||||||
"postcss": "^8.4.49",
|
|
||||||
"tailwindcss": "^3.4.17",
|
|
||||||
"tailwindcss-motion": "^1.1.0",
|
|
||||||
"ts-node": "^10.9.2",
|
|
||||||
"typescript": "^5.8.3"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -9,101 +9,101 @@ export default {
|
|||||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||||
],
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
sarLinkOrange: '#f49b5b',
|
sarLinkOrange: '#f49b5b',
|
||||||
background: 'hsl(var(--background))',
|
background: 'hsl(var(--background))',
|
||||||
foreground: 'hsl(var(--foreground))',
|
foreground: 'hsl(var(--foreground))',
|
||||||
card: {
|
card: {
|
||||||
DEFAULT: 'hsl(var(--card))',
|
DEFAULT: 'hsl(var(--card))',
|
||||||
foreground: 'hsl(var(--card-foreground))'
|
foreground: 'hsl(var(--card-foreground))'
|
||||||
},
|
},
|
||||||
popover: {
|
popover: {
|
||||||
DEFAULT: 'hsl(var(--popover))',
|
DEFAULT: 'hsl(var(--popover))',
|
||||||
foreground: 'hsl(var(--popover-foreground))'
|
foreground: 'hsl(var(--popover-foreground))'
|
||||||
},
|
},
|
||||||
primary: {
|
primary: {
|
||||||
DEFAULT: 'hsl(var(--primary))',
|
DEFAULT: 'hsl(var(--primary))',
|
||||||
foreground: 'hsl(var(--primary-foreground))'
|
foreground: 'hsl(var(--primary-foreground))'
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
DEFAULT: 'hsl(var(--secondary))',
|
DEFAULT: 'hsl(var(--secondary))',
|
||||||
foreground: 'hsl(var(--secondary-foreground))'
|
foreground: 'hsl(var(--secondary-foreground))'
|
||||||
},
|
},
|
||||||
muted: {
|
muted: {
|
||||||
DEFAULT: 'hsl(var(--muted))',
|
DEFAULT: 'hsl(var(--muted))',
|
||||||
foreground: 'hsl(var(--muted-foreground))'
|
foreground: 'hsl(var(--muted-foreground))'
|
||||||
},
|
},
|
||||||
accent: {
|
accent: {
|
||||||
DEFAULT: 'hsl(var(--accent))',
|
DEFAULT: 'hsl(var(--accent))',
|
||||||
foreground: 'hsl(var(--accent-foreground))'
|
foreground: 'hsl(var(--accent-foreground))'
|
||||||
},
|
},
|
||||||
destructive: {
|
destructive: {
|
||||||
DEFAULT: 'hsl(var(--destructive))',
|
DEFAULT: 'hsl(var(--destructive))',
|
||||||
foreground: 'hsl(var(--destructive-foreground))'
|
foreground: 'hsl(var(--destructive-foreground))'
|
||||||
},
|
},
|
||||||
border: 'hsl(var(--border))',
|
border: 'hsl(var(--border))',
|
||||||
input: 'hsl(var(--input))',
|
input: 'hsl(var(--input))',
|
||||||
ring: 'hsl(var(--ring))',
|
ring: 'hsl(var(--ring))',
|
||||||
chart: {
|
chart: {
|
||||||
'1': 'hsl(var(--chart-1))',
|
'1': 'hsl(var(--chart-1))',
|
||||||
'2': 'hsl(var(--chart-2))',
|
'2': 'hsl(var(--chart-2))',
|
||||||
'3': 'hsl(var(--chart-3))',
|
'3': 'hsl(var(--chart-3))',
|
||||||
'4': 'hsl(var(--chart-4))',
|
'4': 'hsl(var(--chart-4))',
|
||||||
'5': 'hsl(var(--chart-5))'
|
'5': 'hsl(var(--chart-5))'
|
||||||
},
|
},
|
||||||
sidebar: {
|
sidebar: {
|
||||||
DEFAULT: 'hsl(var(--sidebar-background))',
|
DEFAULT: 'hsl(var(--sidebar-background))',
|
||||||
foreground: 'hsl(var(--sidebar-foreground))',
|
foreground: 'hsl(var(--sidebar-foreground))',
|
||||||
primary: 'hsl(var(--sidebar-primary))',
|
primary: 'hsl(var(--sidebar-primary))',
|
||||||
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
|
'primary-foreground': 'hsl(var(--sidebar-primary-foreground))',
|
||||||
accent: 'hsl(var(--sidebar-accent))',
|
accent: 'hsl(var(--sidebar-accent))',
|
||||||
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
|
'accent-foreground': 'hsl(var(--sidebar-accent-foreground))',
|
||||||
border: 'hsl(var(--sidebar-border))',
|
border: 'hsl(var(--sidebar-border))',
|
||||||
ring: 'hsl(var(--sidebar-ring))'
|
ring: 'hsl(var(--sidebar-ring))'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
borderRadius: {
|
borderRadius: {
|
||||||
lg: 'var(--radius)',
|
lg: 'var(--radius)',
|
||||||
md: 'calc(var(--radius) - 2px)',
|
md: 'calc(var(--radius) - 2px)',
|
||||||
sm: 'calc(var(--radius) - 4px)'
|
sm: 'calc(var(--radius) - 4px)'
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: [
|
sans: [
|
||||||
'var(--font-barlow)'
|
'var(--font-barlow)'
|
||||||
],
|
],
|
||||||
mono: [
|
mono: [
|
||||||
'var(--font-mono)'
|
'var(--font-mono)'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
keyframes: {
|
keyframes: {
|
||||||
'accordion-down': {
|
'accordion-down': {
|
||||||
from: {
|
from: {
|
||||||
height: '0'
|
height: '0'
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
height: 'var(--radix-accordion-content-height)'
|
height: 'var(--radix-accordion-content-height)'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
'accordion-up': {
|
'accordion-up': {
|
||||||
from: {
|
from: {
|
||||||
height: 'var(--radix-accordion-content-height)'
|
height: 'var(--radix-accordion-content-height)'
|
||||||
},
|
},
|
||||||
to: {
|
to: {
|
||||||
height: '0'
|
height: '0'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
animation: {
|
animation: {
|
||||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||||
'accordion-up': 'accordion-up 0.2s ease-out'
|
'accordion-up': 'accordion-up 0.2s ease-out'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
plugins: [
|
plugins: [
|
||||||
tailwindcssAnimate,
|
tailwindcssAnimate,
|
||||||
tailwindcssMotion,
|
tailwindcssMotion,
|
||||||
require("@pyncz/tailwind-mask-image"),
|
require("@pyncz/tailwind-mask-image"),
|
||||||
require("tailwindcss-animate")
|
require("tailwindcss-animate")
|
||||||
],
|
],
|
||||||
} satisfies Config;
|
} satisfies Config;
|
||||||
|
Reference in New Issue
Block a user