mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-04 19:21:51 +00:00
fix(BC: NextUI): fix BC from NextUI, resolve ESLint warnings and optimize hooks dependencies (#6404)
This commit is contained in:
+9
-1
@@ -12,6 +12,7 @@ module.exports = {
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"eslint-config-prettier",
|
||||
"prettier",
|
||||
"next/core-web-vitals",
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
@@ -37,7 +38,14 @@ module.exports = {
|
||||
"eol-last": ["error", "always"],
|
||||
"simple-import-sort/imports": "error",
|
||||
"simple-import-sort/exports": "error",
|
||||
"jsx-a11y/anchor-is-valid": "error",
|
||||
"jsx-a11y/anchor-is-valid": [
|
||||
"error",
|
||||
{
|
||||
components: ["Link"],
|
||||
specialLink: ["hrefLeft", "hrefRight"],
|
||||
aspects: ["invalidHref", "preferButton"],
|
||||
},
|
||||
],
|
||||
"jsx-a11y/alt-text": "error",
|
||||
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
||||
},
|
||||
|
||||
@@ -42,6 +42,7 @@ export const getProviders = async ({
|
||||
revalidatePath("/providers");
|
||||
return parsedData;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error fetching providers:", error);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { redirect } from "next/navigation";
|
||||
import { parse } from "path";
|
||||
|
||||
import { auth } from "@/auth.config";
|
||||
import { getErrorMessage, parseStringify } from "@/lib";
|
||||
|
||||
export const getServices = async ({}) => {
|
||||
export const getServices = async () => {
|
||||
const session = await auth();
|
||||
|
||||
const keyServer = process.env.API_BASE_URL;
|
||||
@@ -90,6 +87,7 @@ export const getServices = async ({}) => {
|
||||
fail_findings: failFindings,
|
||||
});
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Error fetching data for service ${service.id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ export const getUsers = async ({
|
||||
revalidatePath("/users");
|
||||
return parsedData;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error fetching users:", error);
|
||||
return undefined;
|
||||
}
|
||||
@@ -200,6 +201,7 @@ export const getProfileInfo = async () => {
|
||||
revalidatePath("/profile");
|
||||
return parsedData;
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error fetching profile:", error);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
import { Spacer } from "@nextui-org/react";
|
||||
import { Suspense } from "react";
|
||||
|
||||
|
||||
@@ -24,11 +24,10 @@ export default function Error({
|
||||
<RocketIcon className="h-5 w-5" />
|
||||
<AlertTitle className="text-lg">An unexpected error occurred</AlertTitle>
|
||||
<AlertDescription className="mb-5">
|
||||
We're sorry for the inconvenience. Please try again or contact support
|
||||
if the problem persists.
|
||||
We're sorry for the inconvenience. Please try again or contact
|
||||
support if the problem persists.
|
||||
</AlertDescription>
|
||||
<Link href={"/"} className="font-bold">
|
||||
{" "}
|
||||
<Link href="/" className="font-bold">
|
||||
Go to the homepage
|
||||
</Link>
|
||||
</Alert>
|
||||
|
||||
@@ -72,15 +72,15 @@ const SSRDataTable = async ({
|
||||
return acc;
|
||||
}, {}) || {};
|
||||
|
||||
const enrichedProviders = providersData?.data.map(
|
||||
(provider: ProviderProps) => {
|
||||
const groupNames = provider.relationships.provider_groups.data.map(
|
||||
(group: { id: string }) =>
|
||||
providerGroupDict[group.id] || "Unknown Group",
|
||||
);
|
||||
const enrichedProviders =
|
||||
providersData?.data?.map((provider: ProviderProps) => {
|
||||
const groupNames =
|
||||
provider.relationships?.provider_groups?.data?.map(
|
||||
(group: { id: string }) =>
|
||||
providerGroupDict[group.id] || "Unknown Group",
|
||||
) || [];
|
||||
return { ...provider, groupNames };
|
||||
},
|
||||
);
|
||||
}) || [];
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
|
||||
@@ -23,19 +23,14 @@ export default async function Services({
|
||||
<FilterControls />
|
||||
<Spacer y={4} />
|
||||
<Suspense key={searchParamsKey} fallback={<ServiceSkeletonGrid />}>
|
||||
<SSRServiceGrid searchParams={searchParams} />
|
||||
<SSRServiceGrid />
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const SSRServiceGrid = async ({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: SearchParamsProps;
|
||||
}) => {
|
||||
const servicesData = await getServices(searchParams);
|
||||
const [services] = await Promise.all([servicesData]);
|
||||
const SSRServiceGrid = async () => {
|
||||
const services = await getServices();
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
|
||||
@@ -15,6 +15,9 @@ export const SelectScanComplianceData = ({
|
||||
<Select
|
||||
aria-label="Select a Scan"
|
||||
placeholder="Select a scan"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
labelPlacement="outside"
|
||||
size="md"
|
||||
selectedKeys={new Set([selectedScanId])}
|
||||
|
||||
@@ -20,6 +20,9 @@ export const CustomAccountSelection = () => {
|
||||
label="Account"
|
||||
aria-label="Select an Account"
|
||||
placeholder="Select an account"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
selectionMode="multiple"
|
||||
className="w-full"
|
||||
size="sm"
|
||||
|
||||
@@ -4,7 +4,10 @@ import React from "react";
|
||||
export const CustomCheckboxMutedFindings = () => {
|
||||
return (
|
||||
<Checkbox
|
||||
className="xl:-mt-8"
|
||||
classNames={{
|
||||
label: "text-small",
|
||||
wrapper: "checkbox-update xl:-mt-8",
|
||||
}}
|
||||
size="md"
|
||||
color="danger"
|
||||
aria-label="Include Muted Findings"
|
||||
|
||||
@@ -34,7 +34,9 @@ export const CustomRegionSelection: React.FC = () => {
|
||||
label="Region"
|
||||
aria-label="Select a Region"
|
||||
placeholder="Select a region"
|
||||
selectionMode="multiple"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
className="w-full"
|
||||
size="sm"
|
||||
selectedKeys={selectedKeys}
|
||||
|
||||
@@ -23,7 +23,12 @@ export const CustomSearchInput: React.FC = () => {
|
||||
[router, searchParams],
|
||||
);
|
||||
|
||||
const debouncedChangeHandler = useCallback(debounce(applySearch, 300), []);
|
||||
const debouncedChangeHandler = useCallback(
|
||||
(value: string) => {
|
||||
debounce((val) => applySearch(val), 300)(value);
|
||||
},
|
||||
[applySearch],
|
||||
);
|
||||
|
||||
const clearIconSearch = () => {
|
||||
setSearchQuery("");
|
||||
|
||||
@@ -66,6 +66,9 @@ export const CustomSelectProvider: React.FC = () => {
|
||||
items={dataInputsProvider}
|
||||
aria-label="Select a Provider"
|
||||
placeholder="Select a provider"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
label="Provider"
|
||||
labelPlacement="inside"
|
||||
size="sm"
|
||||
|
||||
@@ -52,30 +52,33 @@ const getProviderData = (
|
||||
// );
|
||||
// };
|
||||
|
||||
const FindingDetailsCell = ({ row }: { row: any }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const findingId = searchParams.get("id");
|
||||
const isOpen = findingId === row.original.id;
|
||||
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<TriggerSheet
|
||||
triggerComponent={<InfoIcon className="text-primary" size={16} />}
|
||||
title="Finding Details"
|
||||
description="View the finding details"
|
||||
defaultOpen={isOpen}
|
||||
>
|
||||
<DataTableRowDetails
|
||||
entityId={row.original.id}
|
||||
findingDetails={row.original}
|
||||
/>
|
||||
</TriggerSheet>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColumnFindings: ColumnDef<FindingProps>[] = [
|
||||
{
|
||||
id: "moreInfo",
|
||||
header: "Details",
|
||||
cell: ({ row }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const findingId = searchParams.get("id");
|
||||
const isOpen = findingId === row.original.id;
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<TriggerSheet
|
||||
triggerComponent={<InfoIcon className="text-primary" size={16} />}
|
||||
title="Finding Details"
|
||||
description="View the finding details"
|
||||
defaultOpen={isOpen}
|
||||
>
|
||||
<DataTableRowDetails
|
||||
entityId={row.original.id}
|
||||
findingDetails={row.original}
|
||||
/>
|
||||
</TriggerSheet>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => <FindingDetailsCell row={row} />,
|
||||
},
|
||||
{
|
||||
accessorKey: "check",
|
||||
|
||||
@@ -133,6 +133,9 @@ export const EditForm = ({
|
||||
{...field}
|
||||
label="Role"
|
||||
placeholder="Select a role"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
variant="bordered"
|
||||
selectedKeys={[field.value || ""]}
|
||||
onSelectionChange={(selected) =>
|
||||
|
||||
@@ -115,6 +115,9 @@ export const SendInvitationForm = ({
|
||||
{...field}
|
||||
label="Role"
|
||||
placeholder="Select a role"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
variant="bordered"
|
||||
isDisabled={isSelectorDisabled}
|
||||
selectedKeys={[field.value]}
|
||||
|
||||
@@ -63,18 +63,21 @@ export const FindingsByStatusChart: React.FC<FindingsByStatusChartProps> = ({
|
||||
pass_new = 0,
|
||||
fail_new = 0,
|
||||
} = findingsByStatus?.data?.attributes || {};
|
||||
const chartData = [
|
||||
{
|
||||
findings: "Success",
|
||||
number: pass,
|
||||
fill: "var(--color-success)",
|
||||
},
|
||||
{
|
||||
findings: "Fail",
|
||||
number: fail,
|
||||
fill: "var(--color-fail)",
|
||||
},
|
||||
];
|
||||
const chartData = useMemo(
|
||||
() => [
|
||||
{
|
||||
findings: "Success",
|
||||
number: pass,
|
||||
fill: "var(--color-success)",
|
||||
},
|
||||
{
|
||||
findings: "Fail",
|
||||
number: fail,
|
||||
fill: "var(--color-fail)",
|
||||
},
|
||||
],
|
||||
[pass, fail],
|
||||
);
|
||||
|
||||
const updatedChartData = calculatePercent(chartData);
|
||||
|
||||
|
||||
@@ -38,30 +38,33 @@ const getProviderData = (
|
||||
);
|
||||
};
|
||||
|
||||
const FindingDetailsCell = ({ row }: { row: any }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const findingId = searchParams.get("id");
|
||||
const isOpen = findingId === row.original.id;
|
||||
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<TriggerSheet
|
||||
triggerComponent={<InfoIcon className="text-primary" size={16} />}
|
||||
title="Finding Details"
|
||||
description="View the finding details"
|
||||
defaultOpen={isOpen}
|
||||
>
|
||||
<DataTableRowDetails
|
||||
entityId={row.original.id}
|
||||
findingDetails={row.original}
|
||||
/>
|
||||
</TriggerSheet>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColumnNewFindingsToDate: ColumnDef<FindingProps>[] = [
|
||||
{
|
||||
id: "moreInfo",
|
||||
header: "Details",
|
||||
cell: ({ row }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const findingId = searchParams.get("id");
|
||||
const isOpen = findingId === row.original.id;
|
||||
return (
|
||||
<div className="flex justify-center">
|
||||
<TriggerSheet
|
||||
triggerComponent={<InfoIcon className="text-primary" size={16} />}
|
||||
title="Finding Details"
|
||||
description="View the finding details"
|
||||
defaultOpen={isOpen}
|
||||
>
|
||||
<DataTableRowDetails
|
||||
entityId={row.original.id}
|
||||
findingDetails={row.original}
|
||||
/>
|
||||
</TriggerSheet>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => <FindingDetailsCell row={row} />,
|
||||
},
|
||||
{
|
||||
accessorKey: "check",
|
||||
|
||||
@@ -97,6 +97,7 @@ export const ConnectAccountForm = () => {
|
||||
router.push(`/providers/add-credentials?type=${providerType}&id=${id}`);
|
||||
}
|
||||
} catch (error: any) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error during submission:", error);
|
||||
toast({
|
||||
variant: "destructive",
|
||||
|
||||
@@ -173,6 +173,7 @@ export const AddRoleForm = ({
|
||||
onChange={(e) => onSelectAllChange(e.target.checked)}
|
||||
classNames={{
|
||||
label: "text-small",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
>
|
||||
Grant all admin permissions
|
||||
@@ -187,6 +188,7 @@ export const AddRoleForm = ({
|
||||
isSelected={!!form.watch(field as keyof FormValues)}
|
||||
classNames={{
|
||||
label: "text-small",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
|
||||
@@ -196,6 +196,7 @@ export const EditRoleForm = ({
|
||||
onChange={(e) => onSelectAllChange(e.target.checked)}
|
||||
classNames={{
|
||||
label: "text-small",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
>
|
||||
Grant all admin permissions
|
||||
@@ -210,6 +211,7 @@ export const EditRoleForm = ({
|
||||
isSelected={!!form.watch(field as keyof FormValues)}
|
||||
classNames={{
|
||||
label: "text-small",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
|
||||
@@ -58,6 +58,9 @@ export const SelectScanProvider = <
|
||||
aria-label="Select a scan job"
|
||||
placeholder="Choose a scan job"
|
||||
labelPlacement="outside"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
size="md"
|
||||
selectedKeys={field.value ? new Set([field.value]) : new Set()}
|
||||
onSelectionChange={(keys) => {
|
||||
|
||||
@@ -11,8 +11,8 @@ export const NoProvidersAdded = () => {
|
||||
<CardBody className="space-y-6 p-6 text-center">
|
||||
<h2 className="text-xl font-bold">No Cloud Accounts Configured</h2>
|
||||
<p className="text-md text-gray-600">
|
||||
You don't have any cloud accounts configured yet. This is the first
|
||||
step to get started.
|
||||
You don't have any cloud accounts configured yet. This is the
|
||||
first step to get started.
|
||||
</p>
|
||||
<CustomButton
|
||||
asLink="/providers/connect-account"
|
||||
|
||||
@@ -17,28 +17,30 @@ const getScanData = (row: { original: ScanProps }) => {
|
||||
return row.original;
|
||||
};
|
||||
|
||||
const ScanDetailsCell = ({ row }: { row: any }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const scanId = searchParams.get("scanId");
|
||||
const isOpen = scanId === row.original.id;
|
||||
|
||||
return (
|
||||
<div className="flex w-9 items-center justify-center">
|
||||
<TriggerSheet
|
||||
triggerComponent={<InfoIcon className="text-primary" size={16} />}
|
||||
title="Scan Details"
|
||||
description="View the scan details"
|
||||
defaultOpen={isOpen}
|
||||
>
|
||||
<DataTableRowDetails entityId={row.original.id} />
|
||||
</TriggerSheet>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ColumnGetScans: ColumnDef<ScanProps>[] = [
|
||||
{
|
||||
id: "moreInfo",
|
||||
header: "Details",
|
||||
cell: ({ row }) => {
|
||||
const searchParams = useSearchParams();
|
||||
const scanId = searchParams.get("scanId");
|
||||
const isOpen = scanId === row.original.id;
|
||||
|
||||
return (
|
||||
<div className="flex w-9 items-center justify-center">
|
||||
<TriggerSheet
|
||||
triggerComponent={<InfoIcon className="text-primary" size={16} />}
|
||||
title="Scan Details"
|
||||
description="View the scan details"
|
||||
defaultOpen={isOpen}
|
||||
>
|
||||
<DataTableRowDetails entityId={row.original.id} />
|
||||
</TriggerSheet>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
cell: ({ row }) => <ScanDetailsCell row={row} />,
|
||||
},
|
||||
{
|
||||
accessorKey: "cloudProvider",
|
||||
|
||||
@@ -50,6 +50,7 @@ export const DataTableRowDetails = ({ entityId }: { entityId: string }) => {
|
||||
setScanDetails(result.data);
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error("Error in fetchScanDetails:", error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
|
||||
@@ -7,7 +7,6 @@ interface CustomAlertModalProps {
|
||||
title?: string;
|
||||
description?: string;
|
||||
children: ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const CustomAlertModal: React.FC<CustomAlertModalProps> = ({
|
||||
@@ -16,14 +15,16 @@ export const CustomAlertModal: React.FC<CustomAlertModalProps> = ({
|
||||
title,
|
||||
description,
|
||||
children,
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<Modal
|
||||
isOpen={isOpen}
|
||||
onOpenChange={onOpenChange}
|
||||
size="xl"
|
||||
className={`dark:bg-prowler-blue-800 ${className || ""}`}
|
||||
classNames={{
|
||||
base: "dark:bg-prowler-blue-800",
|
||||
closeButton: "right-0",
|
||||
}}
|
||||
backdrop="blur"
|
||||
>
|
||||
<ModalContent className="py-4">
|
||||
|
||||
@@ -31,7 +31,7 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
null,
|
||||
);
|
||||
|
||||
const allFilterKeys = filter?.values || [];
|
||||
const allFilterKeys = useMemo(() => filter?.values || [], [filter?.values]);
|
||||
|
||||
const getActiveFilter = useMemo(() => {
|
||||
const currentFilters: Record<string, string> = {};
|
||||
@@ -63,7 +63,7 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
} else {
|
||||
setGroupSelected(new Set());
|
||||
}
|
||||
}, [getActiveFilter, filter?.key, memoizedFilterValues]);
|
||||
}, [getActiveFilter, filter?.key, memoizedFilterValues, filter]);
|
||||
|
||||
const onSelectionChange = useCallback(
|
||||
(keys: string[]) => {
|
||||
@@ -127,10 +127,7 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="light"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
onClearFilter(filter.key);
|
||||
}}
|
||||
onPress={() => onClearFilter(filter.key)}
|
||||
className={`absolute right-2 top-1/2 z-40 -translate-y-1/2 ${
|
||||
groupSelected.size === 0 ? "hidden" : ""
|
||||
}`}
|
||||
@@ -181,7 +178,10 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
className="font-bold"
|
||||
>
|
||||
<Checkbox
|
||||
className="font-normal"
|
||||
classNames={{
|
||||
label: "text-small font-normal",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
value="all"
|
||||
isSelected={groupSelected.has("all")}
|
||||
onClick={handleSelectAllClick}
|
||||
@@ -194,7 +194,14 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
className="flex max-h-96 max-w-56 flex-col gap-y-2 py-2"
|
||||
>
|
||||
{memoizedFilterValues.map((value) => (
|
||||
<Checkbox className="font-normal" key={value} value={value}>
|
||||
<Checkbox
|
||||
classNames={{
|
||||
label: "text-small font-normal",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
key={value}
|
||||
value={value}
|
||||
>
|
||||
{value}
|
||||
</Checkbox>
|
||||
))}
|
||||
|
||||
@@ -47,7 +47,7 @@ export const CustomDropdownSelection: React.FC<
|
||||
}
|
||||
setSelectedValues(newSelection);
|
||||
}
|
||||
}, [selectedKeys]);
|
||||
}, [selectedKeys, allValues.length, selectedValues]);
|
||||
|
||||
const onSelectionChange = useCallback(
|
||||
(keys: string[]) => {
|
||||
@@ -114,7 +114,10 @@ export const CustomDropdownSelection: React.FC<
|
||||
className="font-bold"
|
||||
>
|
||||
<Checkbox
|
||||
className="font-normal"
|
||||
classNames={{
|
||||
label: "text-small font-normal",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
value="all"
|
||||
onClick={handleSelectAllClick}
|
||||
>
|
||||
@@ -126,7 +129,14 @@ export const CustomDropdownSelection: React.FC<
|
||||
className="flex max-h-96 max-w-56 flex-col gap-y-2 py-2"
|
||||
>
|
||||
{memoizedValues.map(({ id, name }) => (
|
||||
<Checkbox className="font-normal" key={id} value={id}>
|
||||
<Checkbox
|
||||
classNames={{
|
||||
label: "text-small font-normal",
|
||||
wrapper: "checkbox-update",
|
||||
}}
|
||||
key={id}
|
||||
value={id}
|
||||
>
|
||||
{name}
|
||||
</Checkbox>
|
||||
))}
|
||||
|
||||
@@ -37,7 +37,7 @@ export const SidebarWrap = () => {
|
||||
const onToggle = useCallback(() => {
|
||||
if (!isCollapsed) openSideMenu();
|
||||
if (isCollapsed) closeSideMenu();
|
||||
}, [isCollapsed]);
|
||||
}, [isCollapsed, openSideMenu, closeSideMenu]);
|
||||
|
||||
const currentPath = pathname === "/" ? "overview" : pathname.split("/")?.[1];
|
||||
|
||||
|
||||
@@ -83,130 +83,132 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
}),
|
||||
};
|
||||
|
||||
const renderNestItem = React.useCallback(
|
||||
(item: SidebarItem) => {
|
||||
const isNestType =
|
||||
item.items &&
|
||||
item.items?.length > 0 &&
|
||||
item?.type === SidebarItemType.Nest;
|
||||
const renderNestItem: (item: SidebarItem) => JSX.Element =
|
||||
React.useCallback(
|
||||
(item) => {
|
||||
const isNestType =
|
||||
item.items &&
|
||||
item.items?.length > 0 &&
|
||||
item?.type === SidebarItemType.Nest;
|
||||
|
||||
if (isNestType) {
|
||||
// Is a nest type item , so we need to remove the href
|
||||
delete item.href;
|
||||
}
|
||||
if (isNestType) {
|
||||
// Is a nest type item , so we need to remove the href
|
||||
delete item.href;
|
||||
}
|
||||
|
||||
return (
|
||||
<ListboxItem
|
||||
{...item}
|
||||
key={item.key}
|
||||
aria-label={item.title}
|
||||
classNames={{
|
||||
base: clsx(
|
||||
{
|
||||
"h-auto p-0": !isCompact && isNestType,
|
||||
},
|
||||
{
|
||||
"inline-block w-11": isCompact && isNestType,
|
||||
},
|
||||
),
|
||||
}}
|
||||
endContent={
|
||||
isCompact || isNestType || hideEndContent
|
||||
? null
|
||||
: (item.endContent ?? null)
|
||||
}
|
||||
startContent={
|
||||
isCompact || isNestType ? null : item.icon ? (
|
||||
<Icon
|
||||
className={clsx(
|
||||
"text-default-500 group-data-[selected=true]:text-foreground",
|
||||
iconClassName,
|
||||
)}
|
||||
icon={item.icon}
|
||||
aria-hidden={"true"}
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
(item.startContent ?? null)
|
||||
)
|
||||
}
|
||||
title={isCompact || isNestType ? null : item.title}
|
||||
>
|
||||
{isCompact ? (
|
||||
<Tooltip content={item.title} placement="right">
|
||||
<div className="flex w-full items-center justify-center">
|
||||
{item.icon ? (
|
||||
<Icon
|
||||
className={clsx(
|
||||
"text-default-500 group-data-[selected=true]:text-foreground",
|
||||
iconClassName,
|
||||
)}
|
||||
aria-hidden={"true"}
|
||||
icon={item.icon}
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
(item.startContent ?? null)
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
{!isCompact && isNestType ? (
|
||||
<Accordion className={"p-0"}>
|
||||
<AccordionItem
|
||||
key={item.key}
|
||||
aria-label={item.title}
|
||||
classNames={{
|
||||
heading: "pr-3",
|
||||
trigger: "p-0",
|
||||
content: "py-0 pl-4",
|
||||
}}
|
||||
title={
|
||||
item.icon ? (
|
||||
<div className={"flex items-center gap-2 px-2 py-1.5"}>
|
||||
<Icon
|
||||
className={clsx(
|
||||
"text-default-500 group-data-[selected=true]:text-foreground",
|
||||
iconClassName,
|
||||
)}
|
||||
aria-hidden="true"
|
||||
icon={item.icon}
|
||||
width={24}
|
||||
/>
|
||||
<span className="text-small font-medium text-default-500 group-data-[selected=true]:text-foreground">
|
||||
{item.title}
|
||||
</span>
|
||||
</div>
|
||||
return (
|
||||
<ListboxItem
|
||||
{...item}
|
||||
key={item.key}
|
||||
aria-label={item.title}
|
||||
classNames={{
|
||||
base: clsx(
|
||||
{
|
||||
"h-auto p-0": !isCompact && isNestType,
|
||||
},
|
||||
{
|
||||
"inline-block w-11": isCompact && isNestType,
|
||||
},
|
||||
),
|
||||
}}
|
||||
endContent={
|
||||
isCompact || isNestType || hideEndContent
|
||||
? null
|
||||
: (item.endContent ?? null)
|
||||
}
|
||||
startContent={
|
||||
isCompact || isNestType ? null : item.icon ? (
|
||||
<Icon
|
||||
className={clsx(
|
||||
"text-default-500 group-data-[selected=true]:text-foreground",
|
||||
iconClassName,
|
||||
)}
|
||||
icon={item.icon}
|
||||
aria-hidden={"true"}
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
(item.startContent ?? null)
|
||||
)
|
||||
}
|
||||
title={isCompact || isNestType ? null : item.title}
|
||||
>
|
||||
{isCompact ? (
|
||||
<Tooltip content={item.title} placement="right">
|
||||
<div className="flex w-full items-center justify-center">
|
||||
{item.icon ? (
|
||||
<Icon
|
||||
className={clsx(
|
||||
"text-default-500 group-data-[selected=true]:text-foreground",
|
||||
iconClassName,
|
||||
)}
|
||||
aria-hidden={"true"}
|
||||
icon={item.icon}
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
(item.startContent ?? null)
|
||||
)
|
||||
}
|
||||
>
|
||||
{item.items && item.items?.length > 0 ? (
|
||||
<Listbox
|
||||
className={"mt-0.5"}
|
||||
classNames={{
|
||||
list: clsx("border-l border-default-200 pl-4"),
|
||||
}}
|
||||
items={item.items}
|
||||
variant="flat"
|
||||
>
|
||||
{item.items.map(renderItem)}
|
||||
</Listbox>
|
||||
) : (
|
||||
renderItem(item)
|
||||
)}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
) : null}
|
||||
</ListboxItem>
|
||||
);
|
||||
},
|
||||
[isCompact, hideEndContent, iconClassName, items],
|
||||
);
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
{!isCompact && isNestType ? (
|
||||
<Accordion className={"p-0"}>
|
||||
<AccordionItem
|
||||
key={item.key}
|
||||
aria-label={item.title}
|
||||
classNames={{
|
||||
heading: "pr-3",
|
||||
trigger: "p-0",
|
||||
content: "py-0 pl-4",
|
||||
}}
|
||||
title={
|
||||
item.icon ? (
|
||||
<div className={"flex items-center gap-2 px-2 py-1.5"}>
|
||||
<Icon
|
||||
className={clsx(
|
||||
"text-default-500 group-data-[selected=true]:text-foreground",
|
||||
iconClassName,
|
||||
)}
|
||||
aria-hidden="true"
|
||||
icon={item.icon}
|
||||
width={24}
|
||||
/>
|
||||
<span className="text-small font-medium text-default-500 group-data-[selected=true]:text-foreground">
|
||||
{item.title}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
(item.startContent ?? null)
|
||||
)
|
||||
}
|
||||
>
|
||||
{item.items && item.items?.length > 0 ? (
|
||||
<Listbox
|
||||
className={"mt-0.5"}
|
||||
classNames={{
|
||||
list: clsx("border-l border-default-200 pl-4"),
|
||||
}}
|
||||
items={item.items}
|
||||
variant="flat"
|
||||
>
|
||||
{item.items.map(renderItem)}
|
||||
</Listbox>
|
||||
) : (
|
||||
renderItem(item)
|
||||
)}
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
) : null}
|
||||
</ListboxItem>
|
||||
);
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[isCompact, hideEndContent, iconClassName],
|
||||
);
|
||||
|
||||
const renderItem = React.useCallback(
|
||||
(item: SidebarItem) => {
|
||||
const renderItem: (item: SidebarItem) => JSX.Element = React.useCallback(
|
||||
(item) => {
|
||||
const isNestType =
|
||||
item.items &&
|
||||
item.items?.length > 0 &&
|
||||
@@ -261,7 +263,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
</ListboxItem>
|
||||
);
|
||||
},
|
||||
[isCompact, hideEndContent, iconClassName, itemClasses?.base],
|
||||
[isCompact, hideEndContent, iconClassName, renderNestItem],
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -18,10 +18,11 @@ interface DataTablePaginationProps {
|
||||
}
|
||||
|
||||
export function DataTablePagination({ metadata }: DataTablePaginationProps) {
|
||||
if (!metadata) return null;
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
if (!metadata) return null;
|
||||
|
||||
const { currentPage, totalPages, totalEntries } = getPaginationInfo(metadata);
|
||||
|
||||
const createPageUrl = (pageNumber: number | string) => {
|
||||
|
||||
@@ -178,6 +178,9 @@ export const EditForm = ({
|
||||
label="Role"
|
||||
labelPlacement="outside"
|
||||
placeholder="Select a role"
|
||||
classNames={{
|
||||
selectorIcon: "right-2",
|
||||
}}
|
||||
variant="bordered"
|
||||
selectedKeys={[field.value || ""]}
|
||||
onSelectionChange={(selected) => {
|
||||
|
||||
@@ -21,6 +21,7 @@ export const useLocalStorage = (
|
||||
setState(JSON.parse(value));
|
||||
}
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
}, [key]);
|
||||
@@ -38,6 +39,7 @@ export const useLocalStorage = (
|
||||
window.localStorage.setItem(key, JSON.stringify(valueToStore));
|
||||
setState(valueToStore);
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
Generated
+447
-141
@@ -44,6 +44,7 @@
|
||||
"recharts": "^2.13.0-alpha.4",
|
||||
"server-only": "^0.0.1",
|
||||
"shadcn-ui": "^0.2.3",
|
||||
"sharp": "^0.33.5",
|
||||
"tailwind-merge": "^2.5.3",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^10.0.0",
|
||||
@@ -61,7 +62,7 @@
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"autoprefixer": "10.4.19",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "14.2.1",
|
||||
"eslint-config-next": "^14.2.23",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||
@@ -247,6 +248,16 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/runtime": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz",
|
||||
"integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==",
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint-community/eslint-utils": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
|
||||
@@ -489,6 +500,367 @@
|
||||
"integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-arm64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
|
||||
"integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-darwin-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-arm64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
|
||||
"integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-darwin-x64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
|
||||
"integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
|
||||
"integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-arm64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
|
||||
"integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-s390x": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
|
||||
"integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linux-x64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
|
||||
"integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
|
||||
"integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
|
||||
"integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
|
||||
"integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm": "1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-arm64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
|
||||
"integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-s390x": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
|
||||
"integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linux-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linux-x64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-arm64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
|
||||
"integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-linuxmusl-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-wasm32": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
|
||||
"integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/runtime": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-ia32": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
|
||||
"integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@img/sharp-win32-x64": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
|
||||
"integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "Apache-2.0 AND LGPL-3.0-or-later",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
}
|
||||
},
|
||||
"node_modules/@internationalized/date": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.6.0.tgz",
|
||||
@@ -615,6 +987,16 @@
|
||||
"resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.22.tgz",
|
||||
"integrity": "sha512-EQ6y1QeNQglNmNIXvwP/Bb+lf7n9WtgcWvtoFsHquVLCJUuxRs+6SfZ5EK0/EqkkLex4RrDySvKgKNN7PXip7Q=="
|
||||
},
|
||||
"node_modules/@next/eslint-plugin-next": {
|
||||
"version": "14.2.23",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.23.tgz",
|
||||
"integrity": "sha512-efRC7m39GoiU1fXZRgGySqYbQi6ZyLkuGlvGst7IwkTTczehQTJA/7PoMg4MMjUZvZEGpiSEu+oJBAjPawiC3Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"glob": "10.3.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-darwin-arm64": {
|
||||
"version": "14.2.22",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.22.tgz",
|
||||
@@ -7242,9 +7624,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001640",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001640.tgz",
|
||||
"integrity": "sha512-lA4VMpW0PSUrFnkmVuEKBUovSWKhj7puyCg8StBChgu298N1AtuF1sKWEvfDuimSEDbhlb/KqPKC3fs1HbuQUA==",
|
||||
"version": "1.0.30001692",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz",
|
||||
"integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -7258,7 +7640,8 @@
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
]
|
||||
],
|
||||
"license": "CC-BY-4.0"
|
||||
},
|
||||
"node_modules/chalk": {
|
||||
"version": "4.1.2",
|
||||
@@ -8252,6 +8635,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-libc": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz",
|
||||
"integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/detect-node-es": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
|
||||
@@ -8605,14 +8997,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next": {
|
||||
"version": "14.2.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.1.tgz",
|
||||
"integrity": "sha512-BgD0kPCWMlqoItRf3xe9fG0MqwObKfVch+f2ccwDpZiCJA8ghkz2wrASH+bI6nLZzGcOJOpMm1v1Q1euhfpt4Q==",
|
||||
"version": "14.2.23",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-14.2.23.tgz",
|
||||
"integrity": "sha512-qtWJzOsDZxnLtXLNtnVjbutHmnEp6QTTSZBTlTCge/Wy0AsUaq8nwR91dBcZZvFg3eY3zKFPBhUkLMHu3Qpauw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@next/eslint-plugin-next": "14.2.1",
|
||||
"@next/eslint-plugin-next": "14.2.23",
|
||||
"@rushstack/eslint-patch": "^1.3.3",
|
||||
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || 7.0.0 - 7.2.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
|
||||
"eslint-import-resolver-node": "^0.3.6",
|
||||
"eslint-import-resolver-typescript": "^3.5.2",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
@@ -8630,133 +9024,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next/node_modules/@next/eslint-plugin-next": {
|
||||
"version": "14.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-14.2.1.tgz",
|
||||
"integrity": "sha512-Fp+mthEBjkn8r9qd6o4JgxKp0IDEzW0VYHD8ZC05xS5/lFNwHKuOdr2kVhWG7BQCO9L6eeepshM1Wbs2T+LgSg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"glob": "10.3.10"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next/node_modules/@typescript-eslint/parser": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.2.0.tgz",
|
||||
"integrity": "sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "7.2.0",
|
||||
"@typescript-eslint/types": "7.2.0",
|
||||
"@typescript-eslint/typescript-estree": "7.2.0",
|
||||
"@typescript-eslint/visitor-keys": "7.2.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^8.56.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next/node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.2.0.tgz",
|
||||
"integrity": "sha512-Qh976RbQM/fYtjx9hs4XkayYujB/aPwglw2choHmf3zBjB4qOywWSdt9+KLRdHubGcoSwBnXUH2sR3hkyaERRg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.2.0",
|
||||
"@typescript-eslint/visitor-keys": "7.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next/node_modules/@typescript-eslint/types": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.2.0.tgz",
|
||||
"integrity": "sha512-XFtUHPI/abFhm4cbCDc5Ykc8npOKBSJePY3a3s+lwumt7XWJuzP5cZcfZ610MIPHjQjNsOLlYK8ASPaNG8UiyA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next/node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.2.0.tgz",
|
||||
"integrity": "sha512-cyxS5WQQCoBwSakpMrvMXuMDEbhOo9bNHHrNcEWis6XHx6KF518tkF1wBvKIn/tpq5ZpUYK7Bdklu8qY0MsFIA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.2.0",
|
||||
"@typescript-eslint/visitor-keys": "7.2.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
"minimatch": "9.0.3",
|
||||
"semver": "^7.5.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next/node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.2.0.tgz",
|
||||
"integrity": "sha512-c6EIQRHhcpl6+tO8EMR+kjkkV+ugUNXOmeASA1rlzkd8EPIriavpWoiEz1HR/VLhbVIdhqnV6E7JZm00cBDx2A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.2.0",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.0.0 || >=18.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/typescript-eslint"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-next/node_modules/minimatch": {
|
||||
"version": "9.0.3",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
|
||||
"integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-config-prettier": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
|
||||
@@ -12737,10 +13004,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver": {
|
||||
"version": "7.6.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
||||
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
|
||||
"dev": true,
|
||||
"version": "7.6.3",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
|
||||
"integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
@@ -12898,6 +13165,45 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/sharp": {
|
||||
"version": "0.33.5",
|
||||
"resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
|
||||
"integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"color": "^4.2.3",
|
||||
"detect-libc": "^2.0.3",
|
||||
"semver": "^7.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/libvips"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@img/sharp-darwin-arm64": "0.33.5",
|
||||
"@img/sharp-darwin-x64": "0.33.5",
|
||||
"@img/sharp-libvips-darwin-arm64": "1.0.4",
|
||||
"@img/sharp-libvips-darwin-x64": "1.0.4",
|
||||
"@img/sharp-libvips-linux-arm": "1.0.5",
|
||||
"@img/sharp-libvips-linux-arm64": "1.0.4",
|
||||
"@img/sharp-libvips-linux-s390x": "1.0.4",
|
||||
"@img/sharp-libvips-linux-x64": "1.0.4",
|
||||
"@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
|
||||
"@img/sharp-libvips-linuxmusl-x64": "1.0.4",
|
||||
"@img/sharp-linux-arm": "0.33.5",
|
||||
"@img/sharp-linux-arm64": "0.33.5",
|
||||
"@img/sharp-linux-s390x": "0.33.5",
|
||||
"@img/sharp-linux-x64": "0.33.5",
|
||||
"@img/sharp-linuxmusl-arm64": "0.33.5",
|
||||
"@img/sharp-linuxmusl-x64": "0.33.5",
|
||||
"@img/sharp-wasm32": "0.33.5",
|
||||
"@img/sharp-win32-ia32": "0.33.5",
|
||||
"@img/sharp-win32-x64": "0.33.5"
|
||||
}
|
||||
},
|
||||
"node_modules/shebang-command": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
|
||||
+2
-1
@@ -36,6 +36,7 @@
|
||||
"recharts": "^2.13.0-alpha.4",
|
||||
"server-only": "^0.0.1",
|
||||
"shadcn-ui": "^0.2.3",
|
||||
"sharp": "^0.33.5",
|
||||
"tailwind-merge": "^2.5.3",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"uuid": "^10.0.0",
|
||||
@@ -53,7 +54,7 @@
|
||||
"@typescript-eslint/parser": "^7.10.0",
|
||||
"autoprefixer": "10.4.19",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-next": "14.2.1",
|
||||
"eslint-config-next": "^14.2.23",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||
|
||||
@@ -36,4 +36,8 @@
|
||||
.no-scrollbar {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.checkbox-update {
|
||||
@apply mr-2 bg-background;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user