From ddf9a3ef2d89386d255b7f16bfe5ecd60c5297b6 Mon Sep 17 00:00:00 2001 From: Pablo Lara Date: Wed, 31 Jul 2024 08:55:02 +0200 Subject: [PATCH] feat: implement error boundary functionality --- app/(prowler)/error.tsx | 36 +++++++++++++++++++ app/(prowler)/layout.tsx | 2 +- app/(prowler)/providers/page.tsx | 2 +- app/error.tsx | 31 ----------------- components/icons/Icons.tsx | 22 ++++++++++++ components/index.ts | 1 + components/ui/alert/Alert.tsx | 59 ++++++++++++++++++++++++++++++++ lib/utils.ts | 15 ++++---- tailwind.config.js | 31 +++++++++++++---- 9 files changed, 154 insertions(+), 45 deletions(-) create mode 100644 app/(prowler)/error.tsx delete mode 100644 app/error.tsx create mode 100644 components/ui/alert/Alert.tsx 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()], };