Skip to content
This repository has been archived by the owner on Dec 6, 2024. It is now read-only.

alert system component #52

Merged
merged 5 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions frontend/src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import { bowlby_one, poppins, roboto } from './fonts';
import { StarknetProvider } from '@/components/StarknetProvider';
import NavBar from '@/components/Navbar';
import { bowlby_one, poppins, roboto } from './fonts';
import Footer from '@/components/Footer';
import NavBar from '@/components/Navbar';
import Alert from '@/components/Alert';
import { AlertProvider } from '@/hooks/useAlert';

const inter = Inter({ subsets: ['latin'] });

Expand All @@ -25,10 +27,13 @@ export default function RootLayout({
${bowlby_one.variable} ${roboto.variable} bg-secondary`}
>
<StarknetProvider>
<NavBar />
{children}
<AlertProvider>
<NavBar />
{children}

<Footer />
<Alert />
<Footer />
</AlertProvider>
</StarknetProvider>
</body>
</html>
Expand Down
32 changes: 32 additions & 0 deletions frontend/src/components/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use client';

import React from 'react';
import { AlertType } from '../hooks/useAlert';
import { useAlert } from '../hooks/useAlert';

const Alert: React.FC = () => {
const { alert } = useAlert();

if (!alert.isVisible) return null;

const alertStyles: Record<AlertType, string> = {
success: 'bg-green-100 border-green-500 text-green-700',
error: 'bg-red-100 border-red-500 text-red-700',
warning: 'bg-yellow-100 border-yellow-500 text-yellow-700',
info: 'bg-blue-100 border-blue-500 text-blue-700',
};

return (
<div
className={`fixed top-4 right-4 p-4 rounded border-l-4 shadow-md transition-opacity z-[9999999] ${
alertStyles[alert.type]
}`}
role="alert"
>
{alert.title && <p className="font-semibold">{alert.title}</p>}
<p className="font-medium">{alert.message}</p>
</div>
);
};

export default Alert;
18 changes: 13 additions & 5 deletions frontend/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
'use client';

import { BrandImage } from '@/assets/landing-page';
import Link from 'next/link';
import ConnectWallet from './ConnectWallet';
import { roboto } from '@/app/fonts';
import { useAlert } from '@/hooks/useAlert';

const NavBar = (props: any) => (
<nav
className={`flex items-center justify-between px-4 lg:px-20
const NavBar = (props: any) => {
const { showAlert } = useAlert();
return (
<nav
className={`flex items-center justify-between px-4 lg:px-20
xl:px-0 2xl:px-0 py-4 mb-12 container mx-auto font-roboto
bg-[#1e1e1e]/80 backdrop-blur-sm sticky top-0 z-10
lg:static lg:bg-none`}
Expand All @@ -17,7 +22,9 @@ const NavBar = (props: any) => (
<BrandImage />
</div>
<div className="md:hidden">
<p className="text-textPrimary text-lg font-normal font-bowlby">
<p
onClick={() => showAlert('success', 'You clicked the scanGuard logo', 'ScanGaurd Alert')}
className="text-textPrimary text-lg font-normal font-bowlby">
ScanGuard
</p>
</div>
Expand All @@ -28,7 +35,8 @@ const NavBar = (props: any) => (
<ConnectWallet />
</div>
</nav>
);
);
};

const NavLinks = () => (
<ul
Expand Down
94 changes: 94 additions & 0 deletions frontend/src/hooks/useAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
'use client';

import { useState, useCallback, createContext, useContext, } from 'react';

/**
* Alert types supported by the useAlert hook
* @typedef {'success' | 'error' | 'warning' | 'info'} AlertType
*/
export type AlertType = 'success' | 'error' | 'warning' | 'info';

/**
* State interface for the alert
* @interface AlertState
*/
interface AlertState {
message: string;
title?: string;
type: AlertType;
isVisible: boolean;
}

interface AlertContextType {
alert: AlertState;
showAlert: (type: AlertType, message: string, title?: string, duration?: number) => void;
}

const AlertContext = createContext<AlertContextType | undefined>(undefined);

export function AlertProvider({ children }: { children: React.ReactNode }) {
const [alert, setAlert] = useState<AlertState>({
message: '',
type: 'info',
isVisible: false,
});

const showAlert = useCallback(
(type: AlertType, message: string, title?: string, duration: number = 5000) => {
setAlert({
type,
title,
message,
isVisible: true,
});

setTimeout(() => {
setAlert((prev) => ({ ...prev, isVisible: false }));
}, duration);
},
[]
);

return (
<AlertContext.Provider value={{ alert, showAlert }}>
{children}
</AlertContext.Provider>
);
}


/**
* Custom hook for managing alert notifications in React applications.
*
* @returns {AlertContextType}
*
* @example
* // Basic usage
* function MyComponent() {
* const { alert, showAlert } = useAlert();
*
* return (
* <>
* <Alert {...alert} />
* <button onClick={() => showAlert('success', 'It worked!', 10000)}>
* Show Success
* </button>
* </>
* );
* }
*
* @example
* // All alert types
* showAlert('success', 'Operation completed successfully', 10000);
* showAlert('error', 'An error occurred', 10000);
* showAlert('warning', 'Please be careful', 10000);
* showAlert('info', 'Just so you know...');
*/
export function useAlert() {
const context = useContext(AlertContext);
if (context === undefined) {
throw new Error('useAlert must be used within an AlertProvider');
}
return context;
}

Loading