mirror of
https://github.com/prowler-cloud/prowler.git
synced 2026-07-24 04:51:51 +00:00
feat: implement error boundary functionality
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components";
|
||||
import { RocketIcon } from "@/components/icons";
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
// reset,
|
||||
}: {
|
||||
error: Error;
|
||||
reset: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
// Log the error to an error reporting service
|
||||
/* eslint-disable no-console */
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<Alert className="w-fit mx-auto mt-[35%]">
|
||||
<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.
|
||||
</AlertDescription>
|
||||
<Link href={"/"} className="font-bold">
|
||||
{" "}
|
||||
Go to the homepage
|
||||
</Link>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,7 @@ import "@/styles/globals.css";
|
||||
import { Metadata, Viewport } from "next";
|
||||
import React from "react";
|
||||
|
||||
import { SidebarWrap } from "@/components";
|
||||
import { SidebarWrap } from "@/components/ui/sidebar";
|
||||
import { fontSans } from "@/config/fonts";
|
||||
import { siteConfig } from "@/config/site";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -30,7 +30,7 @@ export default async function Providers() {
|
||||
<Spacer y={6} />
|
||||
<DataTable
|
||||
columns={ColumnsProviders}
|
||||
data={providers?.providers.data ?? []}
|
||||
data={providers?.providers?.data ?? []}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
|
||||
export default function Error({
|
||||
error,
|
||||
reset,
|
||||
}: {
|
||||
error: Error;
|
||||
reset: () => void;
|
||||
}) {
|
||||
useEffect(() => {
|
||||
// Log the error to an error reporting service
|
||||
/* eslint-disable no-console */
|
||||
console.error(error);
|
||||
}, [error]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2>Something went wrong!</h2>
|
||||
<button
|
||||
onClick={
|
||||
// Attempt to recover by trying to re-render the segment
|
||||
() => reset()
|
||||
}
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -270,3 +270,25 @@ export const WifiOffIcon: React.FC<IconSvgProps> = ({
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
export const RocketIcon: React.FC<IconSvgProps> = ({
|
||||
size = 24,
|
||||
width,
|
||||
height,
|
||||
...props
|
||||
}) => {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={size || width}
|
||||
height={size || height}
|
||||
viewBox="0 0 24 24"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="m5.65 10.025l1.95.825q.35-.7.725-1.35t.825-1.3l-1.4-.275zM9.2 12.1l2.85 2.825q1.05-.4 2.25-1.225t2.25-1.875q1.75-1.75 2.738-3.887T20.15 4q-1.8-.125-3.95.863T12.3 7.6q-1.05 1.05-1.875 2.25T9.2 12.1m4.45-1.625q-.575-.575-.575-1.412t.575-1.413t1.425-.575t1.425.575t.575 1.413t-.575 1.412t-1.425.575t-1.425-.575m.475 8.025l2.1-2.1l-.275-1.4q-.65.45-1.3.812t-1.35.713zM21.95 2.175q.475 3.025-.587 5.888T17.7 13.525L18.2 16q.1.5-.05.975t-.5.825l-4.2 4.2l-2.1-4.925L7.075 12.8L2.15 10.7l4.175-4.2q.35-.35.838-.5t.987-.05l2.475.5q2.6-2.6 5.45-3.675t5.875-.6m-18.025 13.8q.875-.875 2.138-.887t2.137.862t.863 2.138t-.888 2.137q-.625.625-2.087 1.075t-4.038.8q.35-2.575.8-4.038t1.075-2.087m1.425 1.4q-.25.25-.5.913t-.35 1.337q.675-.1 1.338-.337t.912-.488q.3-.3.325-.725T6.8 17.35t-.725-.288t-.725.313"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ export * from "./providers/ProviderInfo";
|
||||
export * from "./providers/ScanStatus";
|
||||
export * from "./providers/table/ColumnsProviders";
|
||||
export * from "./providers/table/DataTable";
|
||||
export * from "./ui/alert/Alert";
|
||||
export * from "./ui/header/Header";
|
||||
export * from "./ui/modal";
|
||||
export * from "./ui/sidebar";
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { cva, type VariantProps } from "class-variance-authority";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const alertVariants = cva(
|
||||
"relative w-full rounded-lg border border-slate-200 px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-slate-950 [&>svg~*]:pl-7 dark:border-slate-800 dark:[&>svg]:text-slate-50",
|
||||
{
|
||||
variants: {
|
||||
variant: {
|
||||
default: "bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50",
|
||||
destructive:
|
||||
"border-red-500/50 text-red-500 dark:border-red-500 [&>svg]:text-red-500 dark:border-red-900/50 dark:text-red-900 dark:dark:border-red-900 dark:[&>svg]:text-red-900",
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
variant: "default",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const Alert = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||
>(({ className, variant, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
role="alert"
|
||||
className={cn(alertVariants({ variant }), className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
Alert.displayName = "Alert";
|
||||
|
||||
const AlertTitle = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLHeadingElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<h5
|
||||
ref={ref}
|
||||
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AlertTitle.displayName = "AlertTitle";
|
||||
|
||||
const AlertDescription = React.forwardRef<
|
||||
HTMLParagraphElement,
|
||||
React.HTMLAttributes<HTMLParagraphElement>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AlertDescription.displayName = "AlertDescription";
|
||||
|
||||
export { Alert, AlertDescription, AlertTitle };
|
||||
+9
-6
@@ -5,16 +5,15 @@ export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
export function capitalize(str: string): string {
|
||||
return str.charAt(0).toUpperCase() + str.slice(1);
|
||||
}
|
||||
|
||||
export const parseStringify = (value: any) => JSON.parse(JSON.stringify(value));
|
||||
|
||||
export const convertFileToUrl = (file: File) => URL.createObjectURL(file);
|
||||
|
||||
// FORMAT DATE TIME
|
||||
export const formatDateTime = (dateString: Date | string) => {
|
||||
export const formatDateTime = (
|
||||
dateString: Date | string,
|
||||
timeZone: string = Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||
) => {
|
||||
const dateTimeOptions: Intl.DateTimeFormatOptions = {
|
||||
// weekday: "short", // abbreviated weekday name (e.g., 'Mon')
|
||||
month: "short", // abbreviated month name (e.g., 'Oct')
|
||||
@@ -22,7 +21,8 @@ export const formatDateTime = (dateString: Date | string) => {
|
||||
year: "numeric", // numeric year (e.g., '2023')
|
||||
hour: "numeric", // numeric hour (e.g., '8')
|
||||
minute: "numeric", // numeric minute (e.g., '30')
|
||||
hour12: true, // use 12-hour clock (true) or 24-hour clock (false)
|
||||
hour12: true, // use 12-hour clock (true) or 24-hour clock (false),
|
||||
timeZone: timeZone, // use the provided timezone
|
||||
};
|
||||
|
||||
const dateDayOptions: Intl.DateTimeFormatOptions = {
|
||||
@@ -30,18 +30,21 @@ export const formatDateTime = (dateString: Date | string) => {
|
||||
year: "numeric", // numeric year (e.g., '2023')
|
||||
month: "2-digit", // abbreviated month name (e.g., 'Oct')
|
||||
day: "2-digit", // numeric day of the month (e.g., '25')
|
||||
timeZone: timeZone, // use the provided timezone
|
||||
};
|
||||
|
||||
const dateOptions: Intl.DateTimeFormatOptions = {
|
||||
month: "short", // abbreviated month name (e.g., 'Oct')
|
||||
year: "numeric", // numeric year (e.g., '2023')
|
||||
day: "numeric", // numeric day of the month (e.g., '25')
|
||||
timeZone: timeZone, // use the provided timezone
|
||||
};
|
||||
|
||||
const timeOptions: Intl.DateTimeFormatOptions = {
|
||||
hour: "numeric", // numeric hour (e.g., '8')
|
||||
minute: "numeric", // numeric minute (e.g., '30')
|
||||
hour12: true, // use 12-hour clock (true) or 24-hour clock (false)
|
||||
timeZone: timeZone, // use the provided timezone
|
||||
};
|
||||
|
||||
const formattedDateTime: string = new Date(dateString).toLocaleString(
|
||||
|
||||
+25
-6
@@ -2,22 +2,41 @@ import { nextui } from "@nextui-org/theme";
|
||||
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
darkMode: ["class"],
|
||||
content: [
|
||||
"./components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./components/**/*.{ts,jsx,tsx,mdx}",
|
||||
"./app/**/*.{ts,jsx,tsx,mdx}",
|
||||
"./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
prefix: "",
|
||||
theme: {
|
||||
container: {
|
||||
center: true,
|
||||
padding: "2rem",
|
||||
screens: {
|
||||
"2xl": "1400px",
|
||||
},
|
||||
},
|
||||
extend: {
|
||||
fontFamily: {
|
||||
sans: ["var(--font-sans)"],
|
||||
mono: ["var(--font-geist-mono)"],
|
||||
},
|
||||
height: {
|
||||
"dvh-minus-16": "calc(100dvh - 116px)",
|
||||
keyframes: {
|
||||
"accordion-down": {
|
||||
from: { height: "0" },
|
||||
to: { height: "var(--radix-accordion-content-height)" },
|
||||
},
|
||||
"accordion-up": {
|
||||
from: { height: "var(--radix-accordion-content-height)" },
|
||||
to: { height: "0" },
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
"accordion-down": "accordion-down 0.2s ease-out",
|
||||
"accordion-up": "accordion-up 0.2s ease-out",
|
||||
},
|
||||
},
|
||||
},
|
||||
darkMode: "class",
|
||||
plugins: [nextui()],
|
||||
plugins: [require("tailwindcss-animate"), nextui()],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user