mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-22 12:01:54 +00:00
Merge pull request #64 from prowler-cloud/PRWLR-4917-Improving-Filtering-Impacts-the-whole-app-followUp
Codebase improvements: Tailwind formatting, accessibility fixes, and search optimization
This commit is contained in:
@@ -10,6 +10,7 @@ module.exports = {
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:security/recommended-legacy",
|
||||
"plugin:jsx-a11y/recommended",
|
||||
"eslint-config-prettier",
|
||||
"prettier",
|
||||
],
|
||||
parserOptions: {
|
||||
|
||||
+2
-1
@@ -5,5 +5,6 @@
|
||||
"tabWidth": 2,
|
||||
"useTabs": false,
|
||||
"semi": true,
|
||||
"printWidth": 80
|
||||
"printWidth": 80,
|
||||
"plugins": ["prettier-plugin-tailwindcss"]
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export default function Error({
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<Alert className="w-fit mx-auto mt-[35%]">
|
||||
<Alert className="mx-auto mt-[35%] w-fit">
|
||||
<RocketIcon className="h-5 w-5" />
|
||||
<AlertTitle className="text-lg">An unexpected error occurred</AlertTitle>
|
||||
<AlertDescription className="mb-5">
|
||||
|
||||
@@ -13,7 +13,7 @@ export default async function Findings() {
|
||||
<>
|
||||
<Header title="Findings" icon="ph:list-checks-duotone" />
|
||||
<Spacer />
|
||||
<div className="flex flex-col items-start w-full overflow-hidden">
|
||||
<div className="flex w-full flex-col items-start overflow-hidden">
|
||||
<Spacer y={6} />
|
||||
<Suspense fallback={<SkeletonTableFindings />}>
|
||||
<SSRDataTable />
|
||||
|
||||
@@ -44,9 +44,9 @@ export default function RootLayout({
|
||||
)}
|
||||
>
|
||||
<Providers themeProps={{ attribute: "class", defaultTheme: "dark" }}>
|
||||
<div className="flex items-center h-dvh justify-center overflow-hidden">
|
||||
<div className="flex h-dvh items-center justify-center overflow-hidden">
|
||||
<SidebarWrap />
|
||||
<main className="container h-full flex-1 flex-col p-4 mb-auto overflow-y-auto no-scrollbar">
|
||||
<main className="no-scrollbar container mb-auto h-full flex-1 flex-col overflow-y-auto p-4">
|
||||
{children}
|
||||
<Toaster />
|
||||
</main>
|
||||
|
||||
@@ -29,7 +29,7 @@ export default async function Users({
|
||||
<>
|
||||
<Header title="User Management" icon="ci:users" />
|
||||
<Spacer y={4} />
|
||||
<div className="flex flex-col items-end w-full">
|
||||
<div className="flex w-full flex-col items-end">
|
||||
<div className="flex space-x-6">
|
||||
<AddUserModal />
|
||||
</div>
|
||||
|
||||
@@ -57,7 +57,7 @@ export const ThemeSwitch: FC<ThemeSwitchProps> = ({
|
||||
className={slots.wrapper({
|
||||
class: clsx(
|
||||
[
|
||||
"w-auto h-auto",
|
||||
"h-auto w-auto",
|
||||
"bg-transparent",
|
||||
"rounded-lg",
|
||||
"flex items-center justify-center",
|
||||
|
||||
@@ -74,7 +74,7 @@ export const AuthForm = ({ type }: { type: string }) => {
|
||||
>
|
||||
{/* Brand Logo and ThemeSwitch */}
|
||||
<div className="absolute right-10 top-10">
|
||||
<div className="flex items-center self-center gap-4">
|
||||
<div className="flex items-center gap-4 self-center">
|
||||
<ThemeSwitch aria-label="Toggle theme" />
|
||||
<ProwlerExtended />
|
||||
</div>
|
||||
@@ -82,7 +82,7 @@ export const AuthForm = ({ type }: { type: string }) => {
|
||||
|
||||
{/* Testimonial */}
|
||||
<div className="absolute bottom-10 right-10 hidden md:block">
|
||||
<p className="max-w-xl text-right text-white/60 text-md">
|
||||
<p className="text-md max-w-xl text-right text-white/60">
|
||||
<span className="font-medium">“</span>
|
||||
Open Cloud Security
|
||||
<span className="font-medium">”</span>
|
||||
|
||||
@@ -55,7 +55,7 @@ export function StatusChart() {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-center self-center my-auto">
|
||||
<div className="my-auto flex items-center justify-center self-center">
|
||||
<ChartContainer
|
||||
config={chartConfig}
|
||||
className="mx-auto aspect-square min-w-[200px] md:min-h-[250px]"
|
||||
@@ -102,7 +102,7 @@ export function StatusChart() {
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ChartContainer>
|
||||
<div className="flex flex-col justify-center gap-2 mx-6 text-small">
|
||||
<div className="mx-6 flex flex-col justify-center gap-2 text-small">
|
||||
<div className="flex space-x-4">
|
||||
<Chip
|
||||
className="h-5"
|
||||
@@ -122,7 +122,7 @@ export function StatusChart() {
|
||||
No change from last scan
|
||||
</div>
|
||||
<Spacer y={4} />
|
||||
<div className="flex flex-col gap-2 leading-none text-muted-foreground">
|
||||
<div className="text-muted-foreground flex flex-col gap-2 leading-none">
|
||||
<div className="flex space-x-4">
|
||||
<Chip
|
||||
className="h-5"
|
||||
@@ -138,7 +138,7 @@ export function StatusChart() {
|
||||
<span>{updatedChartData[1].percent}</span>
|
||||
<Divider orientation="vertical" />
|
||||
</div>
|
||||
<div className="flex items-center font-medium leading-none gap-1">
|
||||
<div className="flex items-center gap-1 font-medium leading-none">
|
||||
+2 findings from last scan <TrendingUp className="h-4 w-4" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,15 +50,15 @@ export const ComplianceCard: React.FC<ComplianceCardProps> = ({
|
||||
|
||||
return (
|
||||
<Card fullWidth isPressable isHoverable shadow="sm">
|
||||
<CardBody className="flex flex-row items-center space-x-4 justify-between">
|
||||
<div className="flex space-x-4 items-center w-full">
|
||||
<CardBody className="flex flex-row items-center justify-between space-x-4">
|
||||
<div className="flex w-full items-center space-x-4">
|
||||
<Image
|
||||
src={getComplianceIcon(title)}
|
||||
alt={`${title} logo`}
|
||||
className="rounded-md p-1 border-gray-300 border-1 bg-white object-contain h-10 w-10 min-w-10"
|
||||
className="h-10 w-10 min-w-10 rounded-md border-1 border-gray-300 bg-white object-contain p-1"
|
||||
/>
|
||||
<div className="flex flex-col w-full">
|
||||
<h4 className="font-bold text-md 3xl:text-lg leading-5">{title}</h4>
|
||||
<div className="flex w-full flex-col">
|
||||
<h4 className="text-md font-bold leading-5 3xl:text-lg">{title}</h4>
|
||||
<Progress
|
||||
label="Your Rating:"
|
||||
size="sm"
|
||||
@@ -68,9 +68,9 @@ export const ComplianceCard: React.FC<ComplianceCardProps> = ({
|
||||
className="mt-2 font-semibold"
|
||||
color={getRatingColor(ratingPercentage)}
|
||||
/>
|
||||
<div className="flex justify-between mt-2">
|
||||
<div className="mt-2 flex justify-between">
|
||||
<small>
|
||||
<span className="font-semibold mr-1">
|
||||
<span className="mr-1 font-semibold">
|
||||
{passingRequirements} / {totalRequirements}
|
||||
</span>
|
||||
Passing Requirements
|
||||
|
||||
@@ -3,8 +3,8 @@ import React from "react";
|
||||
|
||||
export const ComplianceSkeletonGrid = () => {
|
||||
return (
|
||||
<Card className="w-full h-fit p-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 3xl:grid-cols-4 gap-4">
|
||||
<Card className="h-fit w-full p-4">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 3xl:grid-cols-4">
|
||||
{[...Array(28)].map((_, index) => (
|
||||
<div key={index} className="flex flex-col space-y-4">
|
||||
<Skeleton className="h-28 rounded-lg">
|
||||
|
||||
@@ -3,7 +3,12 @@ import React from "react";
|
||||
|
||||
export const CustomCheckboxMutedFindings = () => {
|
||||
return (
|
||||
<Checkbox className="xl:-mt-8" size="md" color="danger">
|
||||
<Checkbox
|
||||
className="xl:-mt-8"
|
||||
size="md"
|
||||
color="danger"
|
||||
aria-label="Include Muted Findings"
|
||||
>
|
||||
Include Muted Findings
|
||||
</Checkbox>
|
||||
);
|
||||
|
||||
@@ -18,6 +18,7 @@ export const CustomAccountSelection = () => {
|
||||
return (
|
||||
<Select
|
||||
label="Account"
|
||||
aria-label="Select an Account"
|
||||
placeholder="Select an account"
|
||||
selectionMode="multiple"
|
||||
className="w-full"
|
||||
|
||||
@@ -57,12 +57,13 @@ export const CustomDatePicker = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col md:gap-2 w-full">
|
||||
<div className="flex w-full flex-col md:gap-2">
|
||||
<DatePicker
|
||||
aria-label="Select a Date"
|
||||
CalendarTopContent={
|
||||
<ButtonGroup
|
||||
fullWidth
|
||||
className="px-3 pb-2 pt-3 bg-content1 [&>button]:text-default-500 [&>button]:border-default-200/60"
|
||||
className="bg-content1 px-3 pb-2 pt-3 [&>button]:border-default-200/60 [&>button]:text-default-500"
|
||||
radius="full"
|
||||
size="sm"
|
||||
variant="bordered"
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
|
||||
export const CustomProviderInputAWS = () => {
|
||||
return (
|
||||
<div className="flex gap-x-2 items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<AWSProviderBadge width={25} height={25} />
|
||||
<p className="text-sm">Amazon Web Services</p>
|
||||
</div>
|
||||
@@ -18,7 +18,7 @@ export const CustomProviderInputAWS = () => {
|
||||
|
||||
export const CustomProviderInputAzure = () => {
|
||||
return (
|
||||
<div className="flex gap-x-2 items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<AzureProviderBadge width={25} height={25} />
|
||||
<p className="text-sm">Azure</p>
|
||||
</div>
|
||||
@@ -27,7 +27,7 @@ export const CustomProviderInputAzure = () => {
|
||||
|
||||
export const CustomProviderInputGCP = () => {
|
||||
return (
|
||||
<div className="flex gap-x-2 items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<GCPProviderBadge width={25} height={25} />
|
||||
<p className="text-sm">Google Cloud Platform</p>
|
||||
</div>
|
||||
@@ -36,7 +36,7 @@ export const CustomProviderInputGCP = () => {
|
||||
|
||||
export const CustomProviderInputKubernetes = () => {
|
||||
return (
|
||||
<div className="flex gap-x-2 items-center">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<KS8ProviderBadge width={25} height={25} />
|
||||
<p className="text-sm">Kubernetes</p>
|
||||
</div>
|
||||
|
||||
@@ -37,6 +37,7 @@ export const CustomRegionSelection = () => {
|
||||
return (
|
||||
<Select
|
||||
label="Region"
|
||||
aria-label="Select a Region"
|
||||
placeholder="Select a region"
|
||||
selectionMode="multiple"
|
||||
className="w-full"
|
||||
|
||||
@@ -38,6 +38,7 @@ export const CustomSearchInput: React.FC = () => {
|
||||
return (
|
||||
<Input
|
||||
variant="flat"
|
||||
aria-label="Search"
|
||||
placeholder="Search..."
|
||||
labelPlacement="outside"
|
||||
value={searchQuery}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import { Select, SelectItem } from "@nextui-org/react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import React, { useCallback, useMemo } from "react";
|
||||
|
||||
import {
|
||||
CustomProviderInputAWS,
|
||||
@@ -34,10 +34,9 @@ const dataInputsProvider = [
|
||||
},
|
||||
];
|
||||
|
||||
export const CustomSelectProvider = () => {
|
||||
export const CustomSelectProvider: React.FC = () => {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
const [selectedProvider, setSelectedProvider] = useState("");
|
||||
|
||||
const applyProviderFilter = useCallback(
|
||||
(value: string) => {
|
||||
@@ -52,38 +51,39 @@ export const CustomSelectProvider = () => {
|
||||
[router, searchParams],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const providerFromUrl = searchParams.get("filter[provider__in]") || "";
|
||||
setSelectedProvider(providerFromUrl);
|
||||
}, [searchParams]);
|
||||
const currentProvider = searchParams.get("filter[provider__in]") || "";
|
||||
|
||||
const selectedKeys = useMemo(() => {
|
||||
return dataInputsProvider.some(
|
||||
(provider) => provider.key === currentProvider,
|
||||
)
|
||||
? [currentProvider]
|
||||
: [];
|
||||
}, [currentProvider]);
|
||||
|
||||
return (
|
||||
<Select
|
||||
items={dataInputsProvider}
|
||||
// selectionMode="multiple"
|
||||
// label="Select a Provider"
|
||||
aria-label="Select a Provider"
|
||||
placeholder="Select a provider"
|
||||
labelPlacement="outside"
|
||||
size="sm"
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
setSelectedProvider(value);
|
||||
applyProviderFilter(value);
|
||||
}}
|
||||
selectedKeys={selectedProvider ? [selectedProvider] : []}
|
||||
selectedKeys={selectedKeys}
|
||||
renderValue={(items) => {
|
||||
return items.map((item) => {
|
||||
return (
|
||||
<div key={item.key} className="flex items-center gap-2">
|
||||
{item.data?.value}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return items.map((item) => (
|
||||
<div key={item.key} className="flex items-center gap-2">
|
||||
{item.data?.value}
|
||||
</div>
|
||||
));
|
||||
}}
|
||||
>
|
||||
{(item) => (
|
||||
<SelectItem key={item.key} textValue={item.key} aria-label={item.label}>
|
||||
<div className="flex gap-2 items-center">{item.value}</div>
|
||||
<div className="flex items-center gap-2">{item.value}</div>
|
||||
</SelectItem>
|
||||
)}
|
||||
</Select>
|
||||
|
||||
@@ -6,7 +6,7 @@ export const filtersProviders = [
|
||||
},
|
||||
{
|
||||
key: "connected",
|
||||
labelCheckboxGroup: "Status provider",
|
||||
labelCheckboxGroup: "Connection",
|
||||
values: ["false", "true"],
|
||||
},
|
||||
// Add more filter categories as needed
|
||||
|
||||
@@ -47,7 +47,7 @@ export const FilterControls: React.FC<FilterControlsProps> = ({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-x-4 gap-y-4 items-center">
|
||||
<div className="grid grid-cols-1 items-center gap-x-4 gap-y-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
{search && <CustomSearchInput />}
|
||||
{providers && <CustomSelectProvider />}
|
||||
{date && <CustomDatePicker />}
|
||||
@@ -57,7 +57,8 @@ export const FilterControls: React.FC<FilterControlsProps> = ({
|
||||
|
||||
{showClearButton && (
|
||||
<CustomButton
|
||||
className="w-fit"
|
||||
ariaLabel="Reset"
|
||||
className="w-full md:w-fit"
|
||||
onPress={clearAllFilters}
|
||||
variant="dashed"
|
||||
size="sm"
|
||||
|
||||
@@ -41,12 +41,12 @@ export const FindingsCard: React.FC<FindingsCardProps> = ({ selectedRow }) => {
|
||||
className={clsx(
|
||||
"max-h-[calc(100vh-146px)] overflow-y-auto rounded-md border bg-background transition-all duration-300",
|
||||
{
|
||||
"w-1/3 translate-x-0 ml-2 p-3 opacity-100": selectedRow,
|
||||
"translate-x-full w-0 opacity-0": !selectedRow,
|
||||
"ml-2 w-1/3 translate-x-0 p-3 opacity-100": selectedRow,
|
||||
"w-0 translate-x-full opacity-0": !selectedRow,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<p className="font-bold mb-3">{CheckTitle}</p>
|
||||
<p className="mb-3 font-bold">{CheckTitle}</p>
|
||||
<Divider />
|
||||
|
||||
<FindingsCardContent
|
||||
|
||||
@@ -14,7 +14,7 @@ export const FindingsCardContent: React.FC<FindingsCardContentProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<>
|
||||
<p className="text-sm font-bold mt-3">{title}</p>
|
||||
<p className="mt-3 text-sm font-bold">{title}</p>
|
||||
{url ? (
|
||||
<Link className="text-sm" href={url}>
|
||||
{description}
|
||||
|
||||
@@ -34,9 +34,9 @@ export const FindingsCardDetail: React.FC<FindingsCardDetailProps> = ({
|
||||
<>
|
||||
{description && (
|
||||
<div
|
||||
className={`text-sm mt-3 rounded-md border-2 ${getDetailColorClass(type)} p-2`}
|
||||
className={`mt-3 rounded-md border-2 text-sm ${getDetailColorClass(type)} p-2`}
|
||||
>
|
||||
<p className="font-bold flex justify-between items-center mb-2">
|
||||
<p className="mb-2 flex items-center justify-between font-bold">
|
||||
<span>{title}</span>
|
||||
{url && (
|
||||
<Button
|
||||
|
||||
@@ -16,7 +16,7 @@ export const FindingsCardScan: React.FC<FindingsCardScanProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="text-sm font-bold mt-3">{title}</p>
|
||||
<p className="mt-3 text-sm font-bold">{title}</p>
|
||||
<p className="text-sm">
|
||||
{formattedDate} at {formattedTime}
|
||||
</p>
|
||||
|
||||
@@ -16,7 +16,7 @@ export const FindingsCardType: React.FC<FindingsCardTypeProps> = ({
|
||||
|
||||
return (
|
||||
<>
|
||||
<p className="text-sm font-bold mt-3">
|
||||
<p className="mt-3 text-sm font-bold">
|
||||
{type.length > 1 ? "Types:" : "Type:"}
|
||||
</p>
|
||||
<p className="text-sm">{typeContent()}</p>
|
||||
|
||||
@@ -47,7 +47,7 @@ export const ColumnsFindings: ColumnDef<FindingProps>[] = [
|
||||
header: "Region",
|
||||
cell: ({ row }) => {
|
||||
const { region } = getFindingsAttributes(row);
|
||||
return <p className="text-sm text-nowrap">{region}</p>;
|
||||
return <p className="text-nowrap text-sm">{region}</p>;
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -69,8 +69,8 @@ export const ColumnsFindings: ColumnDef<FindingProps>[] = [
|
||||
{
|
||||
accessorKey: "actions",
|
||||
header: () => (
|
||||
<div className="relative flex justify-end items-center gap-2">
|
||||
<Dropdown className="bg-background border-1 border-default-200">
|
||||
<div className="relative flex items-center justify-end gap-2">
|
||||
<Dropdown className="border-1 border-default-200 bg-background">
|
||||
<DropdownTrigger>
|
||||
<Button isIconOnly radius="full" size="sm" variant="light">
|
||||
<VerticalDotsIcon className="text-default-400" />
|
||||
|
||||
@@ -48,7 +48,7 @@ export function DataTableFindings<TData extends FindingProps, TValue>({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row w-full">
|
||||
<div className="flex w-full flex-row">
|
||||
<div
|
||||
className={clsx("transition-all duration-300", {
|
||||
"w-2/3": selectedRow,
|
||||
|
||||
@@ -3,9 +3,9 @@ import React from "react";
|
||||
|
||||
export const SkeletonTableFindings = () => {
|
||||
return (
|
||||
<Card className="w-full h-full space-y-5 p-4" radius="sm">
|
||||
<Card className="h-full w-full space-y-5 p-4" radius="sm">
|
||||
{/* Table headers */}
|
||||
<div className="hidden md:flex justify-between">
|
||||
<div className="hidden justify-between md:flex">
|
||||
<Skeleton className="w-1/12 rounded-lg">
|
||||
<div className="h-8 bg-default-200"></div>
|
||||
</Skeleton>
|
||||
@@ -34,27 +34,27 @@ export const SkeletonTableFindings = () => {
|
||||
{[...Array(3)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex flex-col md:flex-row justify-between items-center space-x-0 md:space-x-4"
|
||||
className="flex flex-col items-center justify-between space-x-0 md:flex-row md:space-x-4"
|
||||
>
|
||||
<Skeleton className="w-full md:w-1/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-1/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="w-full md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-1/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-1/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
</div>
|
||||
|
||||
@@ -22,8 +22,9 @@ export const AddProvider = () => {
|
||||
<AddForm setIsOpen={setIsAddOpen} />
|
||||
</CustomAlertModal>
|
||||
|
||||
<div className="w-full flex items-center justify-end">
|
||||
<div className="flex w-full items-center justify-end">
|
||||
<CustomButton
|
||||
ariaLabel="Add Account"
|
||||
variant="solid"
|
||||
color="action"
|
||||
size="md"
|
||||
|
||||
@@ -12,7 +12,7 @@ import { CustomButton, CustomInput } from "@/components/ui/custom";
|
||||
import { Form } from "@/components/ui/form";
|
||||
import { addProviderFormSchema } from "@/types";
|
||||
|
||||
import { CustomRadioProvider } from "../CustomRadioProvider";
|
||||
import { RadioGroupProvider } from "../radio-group-provider";
|
||||
|
||||
export const AddForm = ({
|
||||
setIsOpen,
|
||||
@@ -67,7 +67,7 @@ export const AddForm = ({
|
||||
onSubmit={form.handleSubmit(onSubmitClient)}
|
||||
className="flex flex-col space-y-4"
|
||||
>
|
||||
<CustomRadioProvider control={form.control} />
|
||||
<RadioGroupProvider control={form.control} />
|
||||
<CustomInput
|
||||
control={form.control}
|
||||
name="providerId"
|
||||
@@ -91,9 +91,10 @@ export const AddForm = ({
|
||||
isInvalid={!!form.formState.errors.providerAlias}
|
||||
/>
|
||||
|
||||
<div className="w-full flex justify-center sm:space-x-6">
|
||||
<div className="flex w-full justify-center sm:space-x-6">
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Cancel"
|
||||
className="w-full bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
@@ -106,6 +107,7 @@ export const AddForm = ({
|
||||
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel="Confirm"
|
||||
className="w-full"
|
||||
variant="solid"
|
||||
color="action"
|
||||
+3
-1
@@ -54,9 +54,10 @@ export const DeleteForm = ({
|
||||
<Form {...form}>
|
||||
<form action={onSubmitClient}>
|
||||
<input type="hidden" name="id" value={providerId} />
|
||||
<div className="w-full flex justify-center sm:space-x-6">
|
||||
<div className="flex w-full justify-center sm:space-x-6">
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Cancel"
|
||||
className="w-full bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
@@ -69,6 +70,7 @@ export const DeleteForm = ({
|
||||
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel="Delete"
|
||||
className="w-full"
|
||||
variant="solid"
|
||||
color="danger"
|
||||
@@ -86,9 +86,10 @@ export const EditForm = ({
|
||||
</div>
|
||||
<input type="hidden" name="providerId" value={providerId} />
|
||||
|
||||
<div className="w-full flex justify-center sm:space-x-6">
|
||||
<div className="flex w-full justify-center sm:space-x-6">
|
||||
<CustomButton
|
||||
type="button"
|
||||
ariaLabel="Cancel"
|
||||
className="w-full bg-transparent"
|
||||
variant="faded"
|
||||
size="lg"
|
||||
@@ -101,6 +102,7 @@ export const EditForm = ({
|
||||
|
||||
<CustomButton
|
||||
type="submit"
|
||||
ariaLabel="Save"
|
||||
className="w-full"
|
||||
variant="solid"
|
||||
color="action"
|
||||
@@ -1,3 +1,3 @@
|
||||
export * from "./AddForm";
|
||||
export * from "./DeleteForm";
|
||||
export * from "./EditForm";
|
||||
export * from "./add-form";
|
||||
export * from "./delete-form";
|
||||
export * from "./edit-form";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export * from "./add-provider";
|
||||
export * from "./CheckConnectionProvider";
|
||||
export * from "./CustomRadioProvider";
|
||||
export * from "./DateWithTime";
|
||||
export * from "./forms/DeleteForm";
|
||||
export * from "./ProviderInfo";
|
||||
export * from "./ScanStatus";
|
||||
export * from "./SnippetIdProvider";
|
||||
export * from "./date-with-time";
|
||||
export * from "./forms/delete-form";
|
||||
export * from "./provider-info";
|
||||
export * from "./radio-group-provider";
|
||||
export * from "./scan-status";
|
||||
export * from "./snippet-id-provider";
|
||||
|
||||
@@ -23,19 +23,19 @@ export const ProviderInfo: React.FC<ProviderInfoProps> = ({
|
||||
switch (connected) {
|
||||
case true:
|
||||
return (
|
||||
<div className="flex items-center justify-center rounded-medium border p-1 bg-system-success-lighter border-system-success">
|
||||
<div className="flex items-center justify-center rounded-medium border border-system-success bg-system-success-lighter p-1">
|
||||
<ConnectionIcon className="text-system-success" size={24} />
|
||||
</div>
|
||||
);
|
||||
case false:
|
||||
return (
|
||||
<div className="flex items-center justify-center rounded-medium border p-1 bg-system-error-lighter border-danger">
|
||||
<div className="flex items-center justify-center rounded-medium border border-danger bg-system-error-lighter p-1">
|
||||
<ConnectionIcon className="text-danger" size={24} />
|
||||
</div>
|
||||
);
|
||||
case null:
|
||||
return (
|
||||
<div className="flex items-center justify-center rounded-medium border p-1 bg-info-lighter border-info-lighter">
|
||||
<div className="bg-info-lighter border-info-lighter flex items-center justify-center rounded-medium border p-1">
|
||||
<ConnectionIcon className="text-info" size={24} />
|
||||
</div>
|
||||
);
|
||||
+4
-4
@@ -35,8 +35,8 @@ export const CustomRadio: React.FC<CustomRadioProps> = (props) => {
|
||||
<Component
|
||||
{...getBaseProps()}
|
||||
className={cn(
|
||||
"group inline-flex items-center hover:opacity-70 active:opacity-50 justify-between flex-row-reverse tap-highlight-transparent",
|
||||
"max-w-full cursor-pointer border-2 border-default rounded-lg gap-4 p-4",
|
||||
"group inline-flex flex-row-reverse items-center justify-between tap-highlight-transparent hover:opacity-70 active:opacity-50",
|
||||
"max-w-full cursor-pointer gap-4 rounded-lg border-2 border-default p-4",
|
||||
"data-[selected=true]:border-primary",
|
||||
)}
|
||||
>
|
||||
@@ -58,11 +58,11 @@ export const CustomRadio: React.FC<CustomRadioProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
interface CustomRadioProviderProps {
|
||||
interface RadioGroupProviderProps {
|
||||
control: Control<z.infer<typeof addProviderFormSchema>>;
|
||||
}
|
||||
|
||||
export const CustomRadioProvider: React.FC<CustomRadioProviderProps> = ({
|
||||
export const RadioGroupProvider: React.FC<RadioGroupProviderProps> = ({
|
||||
control,
|
||||
}) => {
|
||||
return (
|
||||
+1
-1
@@ -22,7 +22,7 @@ export const SnippetIdProvider: React.FC<SnippetIdProviderProps> = ({
|
||||
>
|
||||
<p className="flex items-center space-x-2">
|
||||
<IdIcon size={16} />
|
||||
<span className="text-sm max-w-24 overflow-x-scroll no-scrollbar">
|
||||
<span className="no-scrollbar max-w-24 overflow-x-scroll text-sm">
|
||||
{providerId}
|
||||
</span>
|
||||
</p>
|
||||
@@ -6,9 +6,9 @@ import { add } from "date-fns";
|
||||
import { StatusBadge } from "@/components/ui/table";
|
||||
import { ProviderProps } from "@/types";
|
||||
|
||||
import { DateWithTime } from "../DateWithTime";
|
||||
import { ProviderInfo } from "../ProviderInfo";
|
||||
import { SnippetIdProvider } from "../SnippetIdProvider";
|
||||
import { DateWithTime } from "../date-with-time";
|
||||
import { ProviderInfo } from "../provider-info";
|
||||
import { SnippetIdProvider } from "../snippet-id-provider";
|
||||
import { DataTableColumnHeader } from "./data-table-column-header";
|
||||
import { DataTableRowActions } from "./data-table-row-actions";
|
||||
|
||||
@@ -18,7 +18,7 @@ const getProviderData = (row: { original: ProviderProps }) => {
|
||||
|
||||
export const ColumnsProvider: ColumnDef<ProviderProps>[] = [
|
||||
{
|
||||
header: "n",
|
||||
header: " ",
|
||||
cell: ({ row }) => <p className="text-medium">{row.index + 1}</p>,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -80,7 +80,7 @@ export const DataTableColumnHeader = <TData, TValue>({
|
||||
|
||||
return (
|
||||
<Button
|
||||
className="w-full justify-between px-0 text-left align-middle dark:text-slate-400 h-10 bg-transparent whitespace-nowrap text-foreground-500 text-tiny font-semibold outline-none"
|
||||
className="h-10 w-full justify-between whitespace-nowrap bg-transparent px-0 text-left align-middle text-tiny font-semibold text-foreground-500 outline-none dark:text-slate-400"
|
||||
onClick={getToggleSortingHandler}
|
||||
>
|
||||
<span>{title}</span>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"use client";
|
||||
|
||||
import { Divider } from "@nextui-org/react";
|
||||
import { useRouter, useSearchParams } from "next/navigation";
|
||||
import React, { useState } from "react";
|
||||
import { useCallback } from "react";
|
||||
@@ -37,25 +36,28 @@ export const DataTableFilterCustom = ({
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="flex flex-row items-center gap-4">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-start">
|
||||
<CustomButton
|
||||
ariaLabel={showFilters ? "Hide Filters" : "Show Filters"}
|
||||
variant="flat"
|
||||
color={showFilters ? "action" : "primary"}
|
||||
size="sm"
|
||||
startContent={<CustomFilterIcon size={16} />}
|
||||
onPress={() => setShowFilters(!showFilters)}
|
||||
>
|
||||
{showFilters ? "Hide Filters" : "Show Filters"}
|
||||
<h3 className="text-small">
|
||||
{showFilters ? "Hide Filters" : "Show Filters"}
|
||||
</h3>
|
||||
</CustomButton>
|
||||
|
||||
<div
|
||||
className={`transition-all duration-500 ease-in-out ${
|
||||
className={`transition-all duration-700 ease-in-out ${
|
||||
showFilters
|
||||
? "opacity-100 max-h-96 overflow-visible"
|
||||
: "opacity-0 max-h-0 overflow-hidden"
|
||||
? "max-h-96 w-full translate-x-0 overflow-visible opacity-100 md:max-w-80"
|
||||
: "max-h-0 -translate-x-full overflow-hidden opacity-0"
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-row gap-4">
|
||||
<div className="flex flex-col gap-4 md:flex-row">
|
||||
{filters.map((filter) => (
|
||||
<CustomDropdownFilter
|
||||
key={filter.key}
|
||||
@@ -66,10 +68,6 @@ export const DataTableFilterCustom = ({
|
||||
onFilterChange={pushDropdownFilter}
|
||||
/>
|
||||
))}
|
||||
<div className="flex flex-row items-center gap-2">
|
||||
<Divider className="text-default-800 h-5" orientation="vertical" />
|
||||
<span className="text-sm text-default-800">Selected</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -49,7 +49,7 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) {
|
||||
<div className="flex items-center space-x-2">
|
||||
<Link
|
||||
aria-label="Go to first page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(1)}
|
||||
aria-disabled="true"
|
||||
>
|
||||
@@ -57,7 +57,7 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) {
|
||||
</Link>
|
||||
<Link
|
||||
aria-label="Go to previous page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(currentPage - 1)}
|
||||
aria-disabled="true"
|
||||
>
|
||||
@@ -65,14 +65,14 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) {
|
||||
</Link>
|
||||
<Link
|
||||
aria-label="Go to next page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(currentPage + 1)}
|
||||
>
|
||||
<ChevronRightIcon className="size-4" aria-hidden="true" />
|
||||
</Link>
|
||||
<Link
|
||||
aria-label="Go to last page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(totalPages)}
|
||||
>
|
||||
<DoubleArrowRightIcon className="size-4" aria-hidden="true" />
|
||||
|
||||
@@ -56,7 +56,7 @@ export function DataTableProvider<TData, TValue>({
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="p-4 z-0 flex flex-col relative justify-between gap-4 bg-content1 overflow-auto rounded-large shadow-small w-full ">
|
||||
<div className="relative z-0 flex w-full flex-col justify-between gap-4 overflow-auto rounded-large bg-content1 p-4 shadow-small">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
@@ -106,7 +106,7 @@ export function DataTableProvider<TData, TValue>({
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center w-full space-x-2 py-4">
|
||||
<div className="flex w-full items-center space-x-2 py-4">
|
||||
<DataTablePagination metadata={metadata} />
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -22,7 +22,7 @@ import { CustomAlertModal } from "@/components/ui/custom";
|
||||
|
||||
import { CheckConnectionProvider } from "../CheckConnectionProvider";
|
||||
import { EditForm } from "../forms";
|
||||
import { DeleteForm } from "../forms/DeleteForm";
|
||||
import { DeleteForm } from "../forms/delete-form";
|
||||
|
||||
interface DataTableRowActionsProps<ProviderProps> {
|
||||
row: Row<ProviderProps>;
|
||||
@@ -60,7 +60,7 @@ export function DataTableRowActions<ProviderProps>({
|
||||
<DeleteForm providerId={providerId} setIsOpen={setIsDeleteOpen} />
|
||||
</CustomAlertModal>
|
||||
|
||||
<div className="relative flex justify-end items-center gap-2">
|
||||
<div className="relative flex items-center justify-end gap-2">
|
||||
<Dropdown className="shadow-xl" placement="bottom">
|
||||
<DropdownTrigger>
|
||||
<Button isIconOnly radius="full" size="sm" variant="light">
|
||||
|
||||
@@ -3,9 +3,9 @@ import React from "react";
|
||||
|
||||
export const SkeletonTableProvider = () => {
|
||||
return (
|
||||
<Card className="w-full h-full space-y-5 p-4" radius="sm">
|
||||
<Card className="h-full w-full space-y-5 p-4" radius="sm">
|
||||
{/* Table headers */}
|
||||
<div className="hidden md:flex justify-between">
|
||||
<div className="hidden justify-between md:flex">
|
||||
<Skeleton className="w-1/12 rounded-lg">
|
||||
<div className="h-8 bg-default-200"></div>
|
||||
</Skeleton>
|
||||
@@ -34,27 +34,27 @@ export const SkeletonTableProvider = () => {
|
||||
{[...Array(3)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex flex-col md:flex-row justify-between items-center space-x-0 md:space-x-4"
|
||||
className="flex flex-col items-center justify-between space-x-0 md:flex-row md:space-x-4"
|
||||
>
|
||||
<Skeleton className="w-full md:w-1/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-1/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="w-full md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-1/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-1/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
</div>
|
||||
|
||||
@@ -12,11 +12,11 @@ export const ServiceCard: React.FC<CardServiceProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<Card fullWidth isPressable isHoverable shadow="sm">
|
||||
<CardBody className="flex flex-row items-center space-x-4 justify-between">
|
||||
<div className="flex space-x-4 items-center">
|
||||
<CardBody className="flex flex-row items-center justify-between space-x-4">
|
||||
<div className="flex items-center space-x-4">
|
||||
{getAWSIcon(serviceAlias)}
|
||||
<div className="flex flex-col">
|
||||
<h4 className="font-bold text-md leading-5">{serviceAlias}</h4>
|
||||
<h4 className="text-md font-bold leading-5">{serviceAlias}</h4>
|
||||
<small className="text-default-500">
|
||||
{fidingsFailed > 0
|
||||
? `${fidingsFailed} Failed Findings`
|
||||
|
||||
@@ -3,8 +3,8 @@ import React from "react";
|
||||
|
||||
export const ServiceSkeletonGrid = () => {
|
||||
return (
|
||||
<Card className="w-full h-fit p-4">
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-4">
|
||||
<Card className="h-fit w-full p-4">
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{[...Array(25)].map((_, index) => (
|
||||
<div key={index} className="flex flex-col space-y-4">
|
||||
<Skeleton className="h-16 rounded-lg">
|
||||
|
||||
@@ -60,7 +60,7 @@ export const ActionCard = React.forwardRef<HTMLDivElement, ActionCardProps>(
|
||||
shadow="sm"
|
||||
{...props}
|
||||
>
|
||||
<CardBody className="flex h-full flex-row gap-2 p-2 items-center">
|
||||
<CardBody className="flex h-full flex-row items-center gap-2 p-2">
|
||||
<div
|
||||
className={cn(
|
||||
"item-center flex rounded-medium border p-1",
|
||||
|
||||
@@ -34,7 +34,7 @@ const AlertDialogContent = React.forwardRef<
|
||||
<AlertDialogPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-neutral-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg dark:border-neutral-800 dark:bg-neutral-950",
|
||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border border-neutral-200 bg-white p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] dark:border-neutral-800 dark:bg-neutral-950 sm:rounded-lg",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -57,7 +57,7 @@ const ChartContainer = React.forwardRef<
|
||||
data-chart={chartId}
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex aspect-video justify-center text-xs [&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
||||
"[&_.recharts-cartesian-axis-tick_text]:fill-muted-foreground [&_.recharts-cartesian-grid_line[stroke='#ccc']]:stroke-border/50 [&_.recharts-curve.recharts-tooltip-cursor]:stroke-border [&_.recharts-polar-grid_[stroke='#ccc']]:stroke-border [&_.recharts-radial-bar-background-sector]:fill-muted [&_.recharts-rectangle.recharts-tooltip-cursor]:fill-muted [&_.recharts-reference-line_[stroke='#ccc']]:stroke-border flex aspect-video justify-center text-xs [&_.recharts-dot[stroke='#fff']]:stroke-transparent [&_.recharts-layer]:outline-none [&_.recharts-sector[stroke='#fff']]:stroke-transparent [&_.recharts-sector]:outline-none [&_.recharts-surface]:outline-none",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -19,17 +19,17 @@ export const CustomBox = ({
|
||||
<Card fullWidth {...props}>
|
||||
{(preTitle || subTitle || title) && (
|
||||
<>
|
||||
<CardHeader className="pt-4 pb-3 px-3 flex-col items-start">
|
||||
<CardHeader className="flex-col items-start px-3 pb-3 pt-4">
|
||||
{preTitle && (
|
||||
<p className="text-tiny uppercase font-bold">{preTitle}</p>
|
||||
<p className="text-tiny font-bold uppercase">{preTitle}</p>
|
||||
)}
|
||||
{subTitle && <small className="text-default-500">{subTitle}</small>}
|
||||
{title && <h4 className="font-bold text-large">{title}</h4>}
|
||||
{title && <h4 className="text-large font-bold">{title}</h4>}
|
||||
</CardHeader>
|
||||
<Divider />
|
||||
</>
|
||||
)}
|
||||
<CardBody className="px-3 pt-3 pb-4">{children}</CardBody>
|
||||
<CardBody className="px-3 pb-4 pt-3">{children}</CardBody>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,7 +17,7 @@ export const CustomButtonClientAction = ({
|
||||
|
||||
return (
|
||||
<Button
|
||||
className={clsx("bg-transparent border-none p-0 m-0 h-fit min-w-min", {
|
||||
className={clsx("m-0 h-fit min-w-min border-none bg-transparent p-0", {
|
||||
"text-danger": danger,
|
||||
})}
|
||||
spinner={<CircularProgress aria-label="Loading..." size="sm" />}
|
||||
|
||||
@@ -5,7 +5,7 @@ import clsx from "clsx";
|
||||
import { NextUIColors, NextUIVariants } from "@/types";
|
||||
|
||||
export const buttonClasses = {
|
||||
base: "px-2 inline-flex items-center justify-center relative z-0 text-center whitespace-nowrap",
|
||||
base: "px-4 inline-flex items-center justify-center relative z-0 text-center whitespace-nowrap",
|
||||
primary: "bg-default-100 hover:bg-default-200 text-default-800",
|
||||
secondary: "bg-prowler-grey-light dark:bg-prowler-grey-medium text-white",
|
||||
action: "text-white bg-prowler-blue-smoky dark:bg-prowler-grey-medium",
|
||||
@@ -18,6 +18,7 @@ export const buttonClasses = {
|
||||
|
||||
interface ButtonProps {
|
||||
type?: "button" | "submit" | "reset";
|
||||
ariaLabel: string;
|
||||
className?: string;
|
||||
variant?:
|
||||
| "solid"
|
||||
@@ -50,6 +51,7 @@ interface ButtonProps {
|
||||
|
||||
export const CustomButton = ({
|
||||
type = "button",
|
||||
ariaLabel,
|
||||
className,
|
||||
variant = "solid",
|
||||
color = "primary",
|
||||
@@ -66,6 +68,7 @@ export const CustomButton = ({
|
||||
}: ButtonProps) => (
|
||||
<Button
|
||||
type={type}
|
||||
aria-label={ariaLabel}
|
||||
onPress={onPress}
|
||||
variant={variant as NextUIVariants}
|
||||
color={color as NextUIColors}
|
||||
@@ -1,7 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Checkbox,
|
||||
CheckboxGroup,
|
||||
@@ -9,13 +8,18 @@ import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
ScrollShadow,
|
||||
} from "@nextui-org/react";
|
||||
import _ from "lodash";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
import React, { useCallback, useEffect, useMemo, useState } from "react";
|
||||
|
||||
import { PlusCircleIcon } from "@/components/icons";
|
||||
import { CustomDropdownFilterProps } from "@/types";
|
||||
|
||||
const filterSelectedClass =
|
||||
"inline-flex items-center border py-0.5 text-xs transition-colors border-transparent bg-default-500 text-secondary-foreground hover:bg-default-500/80 rounded-md px-2 font-normal";
|
||||
|
||||
export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
filter,
|
||||
onFilterChange,
|
||||
@@ -89,45 +93,32 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
}
|
||||
}, [groupSelected, allFilterKeys]);
|
||||
return (
|
||||
<div className="flex w-full max-w-xs flex-col gap-2">
|
||||
<Popover placement="bottom">
|
||||
<div className="flex w-full flex-col gap-2">
|
||||
<Popover backdrop="transparent" placement="bottom-start">
|
||||
<PopoverTrigger>
|
||||
<Button
|
||||
className="bg-default-100 text-default-800"
|
||||
startContent={
|
||||
<PlusCircleIcon className="text-default-400" width={16} />
|
||||
}
|
||||
className="border-input hover:bg-accent hover:text-accent-foreground inline-flex h-8 items-center justify-center whitespace-nowrap rounded-md border border-dashed bg-background px-3 text-xs font-medium shadow-sm transition-colors focus-visible:outline-none disabled:opacity-50"
|
||||
startContent={<PlusCircleIcon size={16} />}
|
||||
size="sm"
|
||||
>
|
||||
{filter?.labelCheckboxGroup}
|
||||
<h3 className="text-small">{filter?.labelCheckboxGroup}</h3>
|
||||
|
||||
{groupSelected.size > 0 && (
|
||||
<>
|
||||
<Divider orientation="vertical" className="mx-2 h-4" />
|
||||
<Badge
|
||||
variant="flat"
|
||||
className="rounded-sm px-1 font-normal lg:hidden"
|
||||
>
|
||||
{groupSelected.size}
|
||||
</Badge>
|
||||
<div className="hidden space-x-1 lg:flex">
|
||||
{groupSelected.size > 2 ? (
|
||||
<Badge
|
||||
variant="flat"
|
||||
className="rounded-sm px-1 font-normal"
|
||||
>
|
||||
{groupSelected.size} selected
|
||||
</Badge>
|
||||
|
||||
<div className="no-scrollbar hidden max-w-24 space-x-1 overflow-x-auto lg:flex">
|
||||
{groupSelected.size > 3 ? (
|
||||
<span
|
||||
className={filterSelectedClass}
|
||||
>{`+${groupSelected.size - 2} selected`}</span>
|
||||
) : (
|
||||
Array.from(groupSelected)
|
||||
.filter((value) => value !== "all")
|
||||
.map((value) => (
|
||||
<Badge
|
||||
variant="flat"
|
||||
key={value}
|
||||
className="rounded-sm px-1 font-normal"
|
||||
>
|
||||
{value}
|
||||
</Badge>
|
||||
<div key={value} className={filterSelectedClass}>
|
||||
{_.capitalize(value)}
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
@@ -136,32 +127,36 @@ export const CustomDropdownFilter: React.FC<CustomDropdownFilterProps> = ({
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-80">
|
||||
<div className="flex w-full flex-col gap-6 px-2 py-4">
|
||||
<div className="flex w-full flex-col gap-6 p-2">
|
||||
<CheckboxGroup
|
||||
color="default"
|
||||
label={filter?.labelCheckboxGroup}
|
||||
value={Array.from(groupSelected)}
|
||||
onValueChange={onSelectionChange}
|
||||
className="font-bold"
|
||||
>
|
||||
<Checkbox value="all" onValueChange={handleSelectAllClick}>
|
||||
<Checkbox
|
||||
className="font-normal"
|
||||
value="all"
|
||||
onValueChange={handleSelectAllClick}
|
||||
>
|
||||
Select All
|
||||
</Checkbox>
|
||||
{allFilterKeys.map((value) => (
|
||||
<Checkbox key={value} value={value}>
|
||||
{value}
|
||||
</Checkbox>
|
||||
))}
|
||||
<Divider orientation="horizontal" className="mt-2" />
|
||||
<ScrollShadow
|
||||
hideScrollBar
|
||||
className="flex max-h-96 max-w-56 flex-col gap-y-2 py-2"
|
||||
>
|
||||
{allFilterKeys.map((value) => (
|
||||
<Checkbox className="font-normal" key={value} value={value}>
|
||||
{_.capitalize(value)}
|
||||
</Checkbox>
|
||||
))}
|
||||
</ScrollShadow>
|
||||
</CheckboxGroup>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{groupSelected?.size > 0 && (
|
||||
<p className="text-small text-default-500">
|
||||
Selected:{" "}
|
||||
{Array.from(groupSelected)
|
||||
.filter((item) => item !== "all")
|
||||
.join(", ")}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
export * from "./custom-button";
|
||||
export * from "./custom-dropdown-filter";
|
||||
export * from "./CustomAlertModal";
|
||||
export * from "./CustomBox";
|
||||
export * from "./CustomButton";
|
||||
export * from "./CustomButtonClientAction";
|
||||
export * from "./CustomInput";
|
||||
export * from "./CustomLoader";
|
||||
|
||||
@@ -21,7 +21,7 @@ const DialogOverlay = React.forwardRef<
|
||||
<DialogPrimitive.Overlay
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -44,7 +44,7 @@ const DialogContent = React.forwardRef<
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||
<DialogPrimitive.Close className="focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-offset-2 disabled:pointer-events-none">
|
||||
<Cross2Icon className="h-4 w-4" />
|
||||
<span className="sr-only">Close</span>
|
||||
</DialogPrimitive.Close>
|
||||
@@ -102,7 +102,7 @@ const DialogDescription = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DialogPrimitive.Description
|
||||
ref={ref}
|
||||
className={cn("text-sm text-muted-foreground", className)}
|
||||
className={cn("text-muted-foreground text-sm", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -27,7 +27,7 @@ const DropdownMenuSubTrigger = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubTrigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent",
|
||||
"focus:bg-accent data-[state=open]:bg-accent flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none",
|
||||
inset && "pl-8",
|
||||
className,
|
||||
)}
|
||||
@@ -47,7 +47,7 @@ const DropdownMenuSubContent = React.forwardRef<
|
||||
<DropdownMenuPrimitive.SubContent
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
"bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -65,7 +65,7 @@ const DropdownMenuContent = React.forwardRef<
|
||||
ref={ref}
|
||||
sideOffset={sideOffset}
|
||||
className={cn(
|
||||
"z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
"bg-popover text-popover-foreground z-50 min-w-[8rem] overflow-hidden rounded-md border p-1 shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -83,7 +83,7 @@ const DropdownMenuItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
inset && "pl-8",
|
||||
className,
|
||||
)}
|
||||
@@ -99,7 +99,7 @@ const DropdownMenuCheckboxItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.CheckboxItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
checked={checked}
|
||||
@@ -123,7 +123,7 @@ const DropdownMenuRadioItem = React.forwardRef<
|
||||
<DropdownMenuPrimitive.RadioItem
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -162,7 +162,7 @@ const DropdownMenuSeparator = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<DropdownMenuPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
className={cn("bg-muted -mx-1 my-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -12,7 +12,7 @@ export const Header: React.FC<HeaderProps> = ({ title, icon }) => {
|
||||
<>
|
||||
<header className="flex items-center gap-3 py-4">
|
||||
<Icon className="text-default-500" height={40} icon={icon} width={40} />
|
||||
<h1 className="font-bold text-default-700 text-2xl">{title}</h1>
|
||||
<h1 className="text-2xl font-bold text-default-700">{title}</h1>
|
||||
</header>
|
||||
<Divider className="mb-4" />
|
||||
</>
|
||||
|
||||
@@ -24,7 +24,7 @@ const SelectTrigger = React.forwardRef<
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-slate-200 bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-white placeholder:text-slate-500 focus:outline-none focus:ring-1 focus:ring-slate-950 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1 dark:border-slate-800 dark:ring-offset-slate-950 dark:placeholder:text-slate-400 dark:focus:ring-slate-300",
|
||||
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-slate-200 bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-white placeholder:text-slate-500 focus:outline-none focus:ring-1 focus:ring-slate-950 disabled:cursor-not-allowed disabled:opacity-50 dark:border-slate-800 dark:ring-offset-slate-950 dark:placeholder:text-slate-400 dark:focus:ring-slate-300 [&>span]:line-clamp-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -113,7 +113,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
endContent={
|
||||
isCompact || isNestType || hideEndContent
|
||||
? null
|
||||
: item.endContent ?? null
|
||||
: (item.endContent ?? null)
|
||||
}
|
||||
startContent={
|
||||
isCompact || isNestType ? null : item.icon ? (
|
||||
@@ -127,7 +127,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
item.startContent ?? null
|
||||
(item.startContent ?? null)
|
||||
)
|
||||
}
|
||||
title={isCompact || isNestType ? null : item.title}
|
||||
@@ -146,7 +146,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
item.startContent ?? null
|
||||
(item.startContent ?? null)
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
@@ -180,7 +180,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
item.startContent ?? null
|
||||
(item.startContent ?? null)
|
||||
)
|
||||
}
|
||||
>
|
||||
@@ -223,7 +223,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
{...item}
|
||||
key={item.key}
|
||||
endContent={
|
||||
isCompact || hideEndContent ? null : item.endContent ?? null
|
||||
isCompact || hideEndContent ? null : (item.endContent ?? null)
|
||||
}
|
||||
startContent={
|
||||
isCompact ? null : item.icon ? (
|
||||
@@ -236,7 +236,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
item.startContent ?? null
|
||||
(item.startContent ?? null)
|
||||
)
|
||||
}
|
||||
textValue={item.title}
|
||||
@@ -255,7 +255,7 @@ const Sidebar = React.forwardRef<HTMLElement, SidebarProps>(
|
||||
width={24}
|
||||
/>
|
||||
) : (
|
||||
item.startContent ?? null
|
||||
(item.startContent ?? null)
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
@@ -42,7 +42,7 @@ export const SidebarWrap = () => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
"relative flex h-screen flex-col !border-r-small border-divider transition-width bg-prowler-blue-midnight",
|
||||
"relative flex h-screen flex-col !border-r-small border-divider transition-width",
|
||||
{
|
||||
"w-72 p-6": !isCompact,
|
||||
"w-16 items-center px-2 py-6": isCompact,
|
||||
@@ -50,7 +50,7 @@ export const SidebarWrap = () => {
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={clsx("flex items-center gap-3 px-3 justify-center", {
|
||||
className={clsx("flex items-center justify-center gap-3 px-3", {
|
||||
"gap-0": isCompact,
|
||||
})}
|
||||
>
|
||||
@@ -268,9 +268,9 @@ export const SidebarWrap = () => {
|
||||
>
|
||||
<div
|
||||
className={clsx(
|
||||
"text-default-500 data-[hover=true]:text-foreground px-0",
|
||||
"px-0 text-default-500 data-[hover=true]:text-foreground",
|
||||
{
|
||||
"justify-center mt-3": isCompact,
|
||||
"mt-3 justify-center": isCompact,
|
||||
},
|
||||
)}
|
||||
>
|
||||
@@ -284,7 +284,7 @@ export const SidebarWrap = () => {
|
||||
<Button
|
||||
aria-label={isCompact ? "Open sidebar" : "Close sidebar"}
|
||||
className={clsx(
|
||||
"text-default-500 data-[hover=true]:text-foreground px-0",
|
||||
"px-0 text-default-500 data-[hover=true]:text-foreground",
|
||||
{
|
||||
"justify-center": isCompact,
|
||||
},
|
||||
|
||||
@@ -36,7 +36,7 @@ export const SeverityBadge = ({ severity }: { severity: Severity }) => {
|
||||
|
||||
return (
|
||||
<Chip
|
||||
className={clsx("capitalize border-none gap-1 text-default-600", {
|
||||
className={clsx("gap-1 border-none capitalize text-default-600", {
|
||||
"bg-rose-700/20": severity === "critical",
|
||||
})}
|
||||
size="sm"
|
||||
|
||||
@@ -30,7 +30,7 @@ export const StatusBadge = ({ status }: { status: Status }) => {
|
||||
|
||||
return (
|
||||
<Chip
|
||||
className="capitalize border-none gap-1 text-default-600"
|
||||
className="gap-1 border-none capitalize text-default-600"
|
||||
size="sm"
|
||||
variant="flat"
|
||||
color={color}
|
||||
|
||||
@@ -45,7 +45,7 @@ const TableFooter = React.forwardRef<
|
||||
<tfoot
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"font-medium [&>tr]:last:border-b-0 dark:bg-slate-800/50",
|
||||
"font-medium dark:bg-slate-800/50 [&>tr]:last:border-b-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
@@ -75,7 +75,7 @@ const TableHead = React.forwardRef<
|
||||
<th
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"px-4 text-left align-middle bg-default-100 [&:has([role=checkbox])]:pr-0 dark:text-slate-400 h-10 rtl:text-right whitespace-nowrap text-foreground-500 text-tiny font-semibold first:rounded-l-lg rtl:first:rounded-r-lg rtl:first:rounded-l-[unset] last:rounded-r-lg rtl:last:rounded-l-lg rtl:last:rounded-r-[unset] data-[hover=true]:text-foreground-400 outline-none data-[focus-visible=true]:z-10 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-focus data-[focus-visible=true]:outline-offset-2",
|
||||
"h-10 whitespace-nowrap bg-default-100 px-4 text-left align-middle text-tiny font-semibold text-foreground-500 outline-none first:rounded-l-lg last:rounded-r-lg data-[focus-visible=true]:z-10 data-[hover=true]:text-foreground-400 data-[focus-visible=true]:outline-2 data-[focus-visible=true]:outline-offset-2 data-[focus-visible=true]:outline-focus dark:text-slate-400 rtl:text-right rtl:first:rounded-l-[unset] rtl:first:rounded-r-lg rtl:last:rounded-l-lg rtl:last:rounded-r-[unset] [&:has([role=checkbox])]:pr-0",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
||||
@@ -95,7 +95,7 @@ const ToastTitle = React.forwardRef<
|
||||
>(({ className, ...props }, ref) => (
|
||||
<ToastPrimitives.Title
|
||||
ref={ref}
|
||||
className={cn("text-lg font-semibold [&+div]:text-md", className)}
|
||||
className={cn("[&+div]:text-md text-lg font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
|
||||
@@ -30,10 +30,10 @@ export const AddUserModal = () => {
|
||||
className="flex flex-col sm:max-w-md md:max-w-lg"
|
||||
>
|
||||
<DialogHeader className="mb-6 space-y-3">
|
||||
<DialogTitle className="text-2xl text-center">Add User</DialogTitle>
|
||||
<DialogTitle className="text-center text-2xl">Add User</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form ref={ref} onSubmit={() => setOpen(false)}>
|
||||
<div className="col-span-1 flex flex-col gap-y-2 my-auto">
|
||||
<div className="col-span-1 my-auto flex flex-col gap-y-2">
|
||||
<Input
|
||||
type="text"
|
||||
name="email"
|
||||
@@ -52,7 +52,7 @@ export const AddUserModal = () => {
|
||||
/>
|
||||
<CustomSelectUser />
|
||||
</div>
|
||||
<div className="col-span-2 flex justify-center mt-4">
|
||||
<div className="col-span-2 mt-4 flex justify-center">
|
||||
<ButtonAddUser />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -22,7 +22,7 @@ export const CustomSelectUser: React.FC<CustomSelectUserProps> = ({
|
||||
Select a role<span className="text-red-500">*</span>
|
||||
</Label>
|
||||
<Select>
|
||||
<SelectTrigger className="shadow-none h-12 rounded-xl bg-zinc-100 border-transparent text-foreground-500 focus:ring-2 focus:ring-blue-600 ring-0 hover:bg-zinc-200 focus:border-2 focus:border-white">
|
||||
<SelectTrigger className="h-12 rounded-xl border-transparent bg-zinc-100 text-foreground-500 shadow-none ring-0 hover:bg-zinc-200 focus:border-2 focus:border-white focus:ring-2 focus:ring-blue-600">
|
||||
<SelectValue
|
||||
placeholder={(userData && userData?.role) || "Select a role"}
|
||||
/>
|
||||
|
||||
@@ -34,10 +34,10 @@ export const EditUserModal: React.FC<EditUserModalProps> = ({
|
||||
className="flex flex-col sm:max-w-md md:max-w-lg"
|
||||
>
|
||||
<DialogHeader className="mb-6 space-y-3">
|
||||
<DialogTitle className="text-2xl text-center">Edit User</DialogTitle>
|
||||
<DialogTitle className="text-center text-2xl">Edit User</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form ref={ref} onSubmit={() => setIsOpen(false)}>
|
||||
<div className="col-span-1 flex flex-col gap-y-2 my-auto">
|
||||
<div className="col-span-1 my-auto flex flex-col gap-y-2">
|
||||
<Input
|
||||
type="name"
|
||||
name="name"
|
||||
@@ -72,7 +72,7 @@ export const EditUserModal: React.FC<EditUserModalProps> = ({
|
||||
/>
|
||||
<CustomSelectUser userData={userData} />
|
||||
</div>
|
||||
<div className="col-span-2 flex justify-center mt-4">
|
||||
<div className="col-span-2 mt-4 flex justify-center">
|
||||
<ButtonEditUser />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -49,7 +49,7 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) {
|
||||
<div className="flex items-center space-x-2">
|
||||
<Link
|
||||
aria-label="Go to first page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(1)}
|
||||
aria-disabled="true"
|
||||
>
|
||||
@@ -57,7 +57,7 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) {
|
||||
</Link>
|
||||
<Link
|
||||
aria-label="Go to previous page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(currentPage - 1)}
|
||||
aria-disabled="true"
|
||||
>
|
||||
@@ -65,14 +65,14 @@ export function DataTablePagination({ metadata }: DataTablePaginationProps) {
|
||||
</Link>
|
||||
<Link
|
||||
aria-label="Go to next page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(currentPage + 1)}
|
||||
>
|
||||
<ChevronRightIcon className="size-4" aria-hidden="true" />
|
||||
</Link>
|
||||
<Link
|
||||
aria-label="Go to last page"
|
||||
className="page-link relative block py-1.5 px-3 border-0 bg-transparent outline-none transition-all duration-300 rounded text-gray-800 hover:text-gray-800 hover:bg-gray-200 focus:shadow-none"
|
||||
className="page-link relative block rounded border-0 bg-transparent px-3 py-1.5 text-gray-800 outline-none transition-all duration-300 hover:bg-gray-200 hover:text-gray-800 focus:shadow-none"
|
||||
href={createPageUrl(totalPages)}
|
||||
>
|
||||
<DoubleArrowRightIcon className="size-4" aria-hidden="true" />
|
||||
|
||||
@@ -49,7 +49,7 @@ export function DataTableUser<TData, TValue>({
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<div className="rounded-md border w-full">
|
||||
<div className="w-full rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
@@ -99,7 +99,7 @@ export function DataTableUser<TData, TValue>({
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="flex items-center w-full space-x-2 py-4">
|
||||
<div className="flex w-full items-center space-x-2 py-4">
|
||||
<DataTablePagination metadata={metadata} />
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -3,9 +3,9 @@ import React from "react";
|
||||
|
||||
export const SkeletonTableUser = () => {
|
||||
return (
|
||||
<Card className="w-full h-full space-y-5 p-4" radius="sm">
|
||||
<Card className="h-full w-full space-y-5 p-4" radius="sm">
|
||||
{/* Table headers */}
|
||||
<div className="hidden md:flex justify-between">
|
||||
<div className="hidden justify-between md:flex">
|
||||
<Skeleton className="w-2/12 rounded-lg">
|
||||
<div className="h-8 bg-default-200"></div>
|
||||
</Skeleton>
|
||||
@@ -31,24 +31,24 @@ export const SkeletonTableUser = () => {
|
||||
{[...Array(10)].map((_, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="flex flex-col md:flex-row justify-between items-center space-x-0 md:space-x-4"
|
||||
className="flex flex-col items-center justify-between space-x-0 md:flex-row md:space-x-4"
|
||||
>
|
||||
<Skeleton className="w-full md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="w-full md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 w-full rounded-lg md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-2/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-2/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
<Skeleton className="hidden sm:flex md:w-1/12 rounded-lg mb-2 md:mb-0">
|
||||
<Skeleton className="mb-2 hidden rounded-lg sm:flex md:mb-0 md:w-1/12">
|
||||
<div className="h-12 bg-default-300"></div>
|
||||
</Skeleton>
|
||||
</div>
|
||||
|
||||
@@ -21,22 +21,22 @@ export const UserActions: React.FC<UserActionsProps> = ({ userData }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative flex justify-end items-center gap-2">
|
||||
<div className="relative flex items-center justify-end gap-2">
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger>
|
||||
<VerticalDotsIcon
|
||||
size={28}
|
||||
className="text-default-400 p-0.5 hover:bg-gray-200 hover:rounded-full"
|
||||
className="p-0.5 text-default-400 hover:rounded-full hover:bg-gray-200"
|
||||
/>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="bg-white">
|
||||
<DropdownMenuItem
|
||||
className="hover:bg-gray-200 hover:cursor-pointer"
|
||||
className="hover:cursor-pointer hover:bg-gray-200"
|
||||
onClick={() => setIsEditOpen(true)}
|
||||
>
|
||||
Edit
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="hover:bg-gray-200 hover:cursor-pointer">
|
||||
<DropdownMenuItem className="hover:cursor-pointer hover:bg-gray-200">
|
||||
Delete
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
|
||||
Generated
+86
-4
@@ -73,6 +73,8 @@
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.7",
|
||||
"postcss": "8.4.38",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.8",
|
||||
"tailwind-variants": "0.1.20",
|
||||
"tailwindcss": "3.4.3",
|
||||
"typescript": "^5.5.4",
|
||||
@@ -7627,6 +7629,7 @@
|
||||
"resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz",
|
||||
"integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"eslint-config-prettier": "bin/cli.js"
|
||||
},
|
||||
@@ -10880,11 +10883,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.3.2",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.2.tgz",
|
||||
"integrity": "sha512-rAVeHYMcv8ATV5d508CFdn+8/pHPpXeIid1DdrPwXnaAdH7cqjVbpJaT5eq4yRAFU/lsbwYwSF/n5iNrdJHPQA==",
|
||||
"version": "3.3.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
|
||||
"integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
},
|
||||
@@ -10907,6 +10910,85 @@
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/prettier-plugin-tailwindcss": {
|
||||
"version": "0.6.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.8.tgz",
|
||||
"integrity": "sha512-dGu3kdm7SXPkiW4nzeWKCl3uoImdd5CTZEJGxyypEPL37Wj0HT2pLqjrvSei1nTeuQfO4PUfjeW5cTUNRLZ4sA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.21.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@ianvs/prettier-plugin-sort-imports": "*",
|
||||
"@prettier/plugin-pug": "*",
|
||||
"@shopify/prettier-plugin-liquid": "*",
|
||||
"@trivago/prettier-plugin-sort-imports": "*",
|
||||
"@zackad/prettier-plugin-twig-melody": "*",
|
||||
"prettier": "^3.0",
|
||||
"prettier-plugin-astro": "*",
|
||||
"prettier-plugin-css-order": "*",
|
||||
"prettier-plugin-import-sort": "*",
|
||||
"prettier-plugin-jsdoc": "*",
|
||||
"prettier-plugin-marko": "*",
|
||||
"prettier-plugin-multiline-arrays": "*",
|
||||
"prettier-plugin-organize-attributes": "*",
|
||||
"prettier-plugin-organize-imports": "*",
|
||||
"prettier-plugin-sort-imports": "*",
|
||||
"prettier-plugin-style-order": "*",
|
||||
"prettier-plugin-svelte": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@ianvs/prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"@prettier/plugin-pug": {
|
||||
"optional": true
|
||||
},
|
||||
"@shopify/prettier-plugin-liquid": {
|
||||
"optional": true
|
||||
},
|
||||
"@trivago/prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"@zackad/prettier-plugin-twig-melody": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-astro": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-css-order": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-import-sort": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-jsdoc": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-marko": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-multiline-arrays": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-organize-attributes": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-organize-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-sort-imports": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-style-order": {
|
||||
"optional": true
|
||||
},
|
||||
"prettier-plugin-svelte": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pretty-format": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-3.8.0.tgz",
|
||||
|
||||
@@ -65,6 +65,8 @@
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.7",
|
||||
"postcss": "8.4.38",
|
||||
"prettier": "^3.3.3",
|
||||
"prettier-plugin-tailwindcss": "^0.6.8",
|
||||
"tailwind-variants": "0.1.20",
|
||||
"tailwindcss": "3.4.3",
|
||||
"typescript": "^5.5.4",
|
||||
|
||||
Reference in New Issue
Block a user