diff --git a/app/(prowler)/error.tsx b/app/(prowler)/error.tsx
new file mode 100644
index 0000000000..fd8468f123
--- /dev/null
+++ b/app/(prowler)/error.tsx
@@ -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 (
+
+
+ An unexpected error occurred
+
+ We're sorry for the inconvenience. Please try again or contact support
+ if the problem persists.
+
+
+ {" "}
+ Go to the homepage
+
+
+ );
+}
diff --git a/app/(prowler)/layout.tsx b/app/(prowler)/layout.tsx
index 13319c3540..6ef0e1aaa4 100644
--- a/app/(prowler)/layout.tsx
+++ b/app/(prowler)/layout.tsx
@@ -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";
diff --git a/app/(prowler)/providers/page.tsx b/app/(prowler)/providers/page.tsx
index d51901ef7b..4301b83e37 100644
--- a/app/(prowler)/providers/page.tsx
+++ b/app/(prowler)/providers/page.tsx
@@ -30,7 +30,7 @@ export default async function Providers() {
>
diff --git a/app/error.tsx b/app/error.tsx
deleted file mode 100644
index 9ed5104e8a..0000000000
--- a/app/error.tsx
+++ /dev/null
@@ -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 (
-
-
Something went wrong!
-
-
- );
-}
diff --git a/components/icons/Icons.tsx b/components/icons/Icons.tsx
index 9b79a7c35d..aca8d6fc4d 100644
--- a/components/icons/Icons.tsx
+++ b/components/icons/Icons.tsx
@@ -270,3 +270,25 @@ export const WifiOffIcon: React.FC = ({
/>
);
+
+export const RocketIcon: React.FC = ({
+ size = 24,
+ width,
+ height,
+ ...props
+}) => {
+ return (
+
+ );
+};
diff --git a/components/index.ts b/components/index.ts
index faa5b19c57..cc6a14b022 100644
--- a/components/index.ts
+++ b/components/index.ts
@@ -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";
diff --git a/components/ui/alert/Alert.tsx b/components/ui/alert/Alert.tsx
new file mode 100644
index 0000000000..7f6fb50705
--- /dev/null
+++ b/components/ui/alert/Alert.tsx
@@ -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 & VariantProps
+>(({ className, variant, ...props }, ref) => (
+
+));
+Alert.displayName = "Alert";
+
+const AlertTitle = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+AlertTitle.displayName = "AlertTitle";
+
+const AlertDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => (
+
+));
+AlertDescription.displayName = "AlertDescription";
+
+export { Alert, AlertDescription, AlertTitle };
diff --git a/lib/utils.ts b/lib/utils.ts
index 261f2865a8..c3e96dd8df 100644
--- a/lib/utils.ts
+++ b/lib/utils.ts
@@ -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(
diff --git a/tailwind.config.js b/tailwind.config.js
index 5a6f937328..ac42eb57ec 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -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()],
};