From bfb173425f92680eb69b93c2f81fecbb317c88fb Mon Sep 17 00:00:00 2001 From: Qaisar Irfan Date: Tue, 11 Feb 2025 17:43:13 +0500 Subject: [PATCH] refactor: remove notistack from project --- package-lock.json | 161 +++++++++++++----- package.json | 1 - src/app/[language]/layout.tsx | 6 +- .../AlertModal/alertModal.stories.tsx | 82 --------- src/components/AlertModal/alertModal.tsx | 29 ---- src/components/AlertModal/index.ts | 3 - src/components/AlertModal/styled.tsx | 10 -- src/components/AlertModal/types.ts | 7 - src/components/Notification/index.ts | 1 + .../Notification/notification.stories.tsx | 70 ++++++++ src/components/Notification/notification.tsx | 71 ++++++++ src/components/snackbar-provider.tsx | 5 - src/features/LoginPage/loginPage.tsx | 32 +++- 13 files changed, 290 insertions(+), 188 deletions(-) delete mode 100644 src/components/AlertModal/alertModal.stories.tsx delete mode 100644 src/components/AlertModal/alertModal.tsx delete mode 100644 src/components/AlertModal/index.ts delete mode 100644 src/components/AlertModal/styled.tsx delete mode 100644 src/components/AlertModal/types.ts create mode 100644 src/components/Notification/index.ts create mode 100644 src/components/Notification/notification.stories.tsx create mode 100644 src/components/Notification/notification.tsx delete mode 100644 src/components/snackbar-provider.tsx diff --git a/package-lock.json b/package-lock.json index 818db1a..906d95e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,6 @@ "mailparser": "^3.7.0", "nanoid": "^5.0.1", "next": "14.2.15", - "notistack": "^3.0.1", "react": "18.3.1", "react-dom": "18.3.1", "react-dropzone": "^14.2.3", @@ -3954,6 +3953,126 @@ "node": ">= 10" } }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.15.tgz", + "integrity": "sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.15.tgz", + "integrity": "sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.15.tgz", + "integrity": "sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.15.tgz", + "integrity": "sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.15.tgz", + "integrity": "sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.15.tgz", + "integrity": "sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.15.tgz", + "integrity": "sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.15", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.15.tgz", + "integrity": "sha512-SzqGbsLsP9OwKNUG9nekShTwhj6JSB9ZLMWQ8g1gG6hdE5gQLncbnbymrwy2yVmH9nikSLYRYxYMFu78Ggp7/g==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -12658,15 +12777,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/goober": { - "version": "2.1.16", - "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.16.tgz", - "integrity": "sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==", - "license": "MIT", - "peerDependencies": { - "csstype": "^3.0.10" - } - }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -17173,37 +17283,6 @@ "node": ">=0.10.0" } }, - "node_modules/notistack": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/notistack/-/notistack-3.0.2.tgz", - "integrity": "sha512-0R+/arLYbK5Hh7mEfR2adt0tyXJcCC9KkA2hc56FeWik2QN6Bm/S4uW+BjzDARsJth5u06nTjelSw/VSnB1YEA==", - "license": "MIT", - "dependencies": { - "clsx": "^1.1.0", - "goober": "^2.0.33" - }, - "engines": { - "node": ">=12.0.0", - "npm": ">=6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/notistack" - }, - "peerDependencies": { - "react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/notistack/node_modules/clsx": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", - "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", diff --git a/package.json b/package.json index 04b6a0e..123ab8d 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,6 @@ "mailparser": "^3.7.0", "nanoid": "^5.0.1", "next": "14.2.15", - "notistack": "^3.0.1", "react": "18.3.1", "react-dom": "18.3.1", "react-dropzone": "^14.2.3", diff --git a/src/app/[language]/layout.tsx b/src/app/[language]/layout.tsx index a81fb82..91b2ae8 100644 --- a/src/app/[language]/layout.tsx +++ b/src/app/[language]/layout.tsx @@ -8,7 +8,7 @@ import { GoogleOAuthProvider } from "@react-oauth/google"; import { dir } from "i18next"; import type { Metadata } from "next"; -import SnackbarProvider from "@/components/snackbar-provider"; +import { NotificationProvider } from "@/components/Notification"; import InitColorSchemeScript from "@/components/theme/init-color-scheme-script"; import ThemeProvider from "@/components/theme/theme-provider"; import { Providers } from "@/redux/store/provider"; @@ -54,9 +54,9 @@ export default function RootLayout({ - + {children} - + diff --git a/src/components/AlertModal/alertModal.stories.tsx b/src/components/AlertModal/alertModal.stories.tsx deleted file mode 100644 index bbd62e1..0000000 --- a/src/components/AlertModal/alertModal.stories.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/react"; - -import AlertModal from "./alertModal"; -import { AlertModalProps } from "./types"; - -// Define the metadata for the component -const meta: Meta = { - title: "Components/AlertModal", - component: AlertModal, // The component itself - argTypes: { - // Define controls for the props - severity: { - control: { - type: "select", - options: ["success", "info", "warning", "error"], - }, - description: "The severity of the alert modal, indicating its purpose or importance.", - }, - horizontal: { - control: { - type: "select", - options: ["center", "left", "right"], - }, - description: "The horizontal placement of the alert modal.", - }, - vertical: { - control: { - type: "select", - options: ["bottom", "top"], - }, - description: "The vertical placement of the alert modal.", - }, - errorMessage: { - control: { - type: "text", - }, - description: "The message displayed inside the alert modal.", - }, - handleCloseAlertModal: { - control: false, // Disable the control as this is a function - description: "Callback function triggered when the alert modal is closed.", - }, - }, -}; - -export default meta; - -// Define the template for the stories -type Story = StoryObj; - -// Default story -export const Default: Story = { - args: { - handleCloseAlertModal: () => {}, - }, -}; - -// Story with custom message -export const CustomMessage: Story = { - args: { - ...Default.args, - errorMessage: "This is custom message", - }, -}; - -// Story with custom severity -export const CustomSeverity: Story = { - args: { - ...Default.args, - errorMessage: "Login succesfully", - severity: "success", - }, -}; - -// Story with custom placement -export const CustomPlacement: Story = { - args: { - ...Default.args, - horizontal: "center", - vertical: "top", - }, -}; diff --git a/src/components/AlertModal/alertModal.tsx b/src/components/AlertModal/alertModal.tsx deleted file mode 100644 index ac5a488..0000000 --- a/src/components/AlertModal/alertModal.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import { FC } from "react"; - -import Snackbar from "@mui/material/Snackbar"; - -import { AlertContainer } from "./styled"; -import { AlertModalProps } from "./types"; - -const AlertModal: FC = ({ - handleCloseAlertModal, - errorMessage = "Something wrong happened", - vertical = "top", - horizontal = "right", - severity = "error", -}) => { - return ( - - - {errorMessage} - - - ); -}; - -export default AlertModal; diff --git a/src/components/AlertModal/index.ts b/src/components/AlertModal/index.ts deleted file mode 100644 index 72ea9e3..0000000 --- a/src/components/AlertModal/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -import AlertModal from "./alertModal"; - -export default AlertModal; diff --git a/src/components/AlertModal/styled.tsx b/src/components/AlertModal/styled.tsx deleted file mode 100644 index 30a376a..0000000 --- a/src/components/AlertModal/styled.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import Alert from "@mui/material/Alert"; -import { styled, css } from "@mui/material/styles"; - -export const AlertContainer = styled(Alert, { - name: "AlertContainer", -})( - () => css` - width: 100%; - ` -); diff --git a/src/components/AlertModal/types.ts b/src/components/AlertModal/types.ts deleted file mode 100644 index daeb448..0000000 --- a/src/components/AlertModal/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type AlertModalProps = { - handleCloseAlertModal: () => void; - errorMessage?: string; - vertical?: "bottom" | "top"; - horizontal?: "center" | "left" | "right"; - severity?: "success" | "info" | "warning" | "error"; -}; diff --git a/src/components/Notification/index.ts b/src/components/Notification/index.ts new file mode 100644 index 0000000..dd99c4d --- /dev/null +++ b/src/components/Notification/index.ts @@ -0,0 +1 @@ +export * from "./notification"; diff --git a/src/components/Notification/notification.stories.tsx b/src/components/Notification/notification.stories.tsx new file mode 100644 index 0000000..346d5ea --- /dev/null +++ b/src/components/Notification/notification.stories.tsx @@ -0,0 +1,70 @@ +/* eslint-disable no-restricted-syntax */ +import { FC } from "react"; + +import Button from "@mui/material/Button"; +import { SnackbarOrigin } from "@mui/material/Snackbar"; +import type { Meta, StoryObj } from "@storybook/react"; + +import { NotificationProvider, useNotification } from "./notification"; + +const meta: Meta = { + title: "Components/Notification", + component: NotificationProvider, + tags: ["autodocs"], +}; + +export default meta; + +type Story = StoryObj; + +const NotificationDemo: FC<{ vertical?: SnackbarOrigin["vertical"]; horizontal?: SnackbarOrigin["horizontal"] }> = ({ + vertical = "top", + horizontal = "right", +}) => { + const { dispatch } = useNotification(); + + const showNotification = (severity: "success" | "error" | "warning" | "info") => { + dispatch({ + type: "SHOW_NOTIFICATION", + payload: { + message: `This is a ${severity} message!`, + severity, + vertical, + horizontal, + }, + }); + }; + + return ( +
+ + + + +
+ ); +}; + +export const Default: Story = { + render: () => ( + + + + ), +}; + +export const CustomPosition: Story = { + render: () => ( + + + + ), +}; diff --git a/src/components/Notification/notification.tsx b/src/components/Notification/notification.tsx new file mode 100644 index 0000000..f9360e7 --- /dev/null +++ b/src/components/Notification/notification.tsx @@ -0,0 +1,71 @@ +"use client"; +import React, { createContext, useReducer, useContext, ReactNode } from "react"; + +import MuiAlert, { AlertColor } from "@mui/material/Alert"; +import Snackbar, { SnackbarOrigin } from "@mui/material/Snackbar"; + +type Notification = { + horizontal?: SnackbarOrigin["horizontal"]; + message: string; + open: boolean; + severity: AlertColor; + vertical?: SnackbarOrigin["vertical"]; +}; + +type NotificationAction = { type: "SHOW_NOTIFICATION"; payload: Omit } | { type: "HIDE_NOTIFICATION" }; + +type NotificationState = Notification; + +const initialState: NotificationState = { + horizontal: "right", + message: "", + open: false, + severity: "info", + vertical: "top", +}; + +const notificationReducer = (state: NotificationState, action: NotificationAction): NotificationState => { + switch (action.type) { + case "SHOW_NOTIFICATION": + return { ...action.payload, open: true }; + case "HIDE_NOTIFICATION": + return { ...state, open: false }; + default: + return state; + } +}; + +const NotificationContext = createContext<{ + state: NotificationState; + dispatch: React.Dispatch; +} | null>(null); + +const { Provider } = NotificationContext; + +export const NotificationProvider: React.FC<{ children: ReactNode }> = ({ children }) => { + const [state, dispatch] = useReducer(notificationReducer, initialState); + + return ( + + {children} + dispatch({ type: "HIDE_NOTIFICATION" })} + anchorOrigin={{ vertical: state.vertical ?? "top", horizontal: state.horizontal || "right" }} + > + dispatch({ type: "HIDE_NOTIFICATION" })} severity={state.severity}> + {state.message} + + + + ); +}; + +export const useNotification = () => { + const context = useContext(NotificationContext); + if (!context) { + throw new Error("useNotification must be used within a NotificationProvider"); + } + return context; +}; diff --git a/src/components/snackbar-provider.tsx b/src/components/snackbar-provider.tsx deleted file mode 100644 index 5004094..0000000 --- a/src/components/snackbar-provider.tsx +++ /dev/null @@ -1,5 +0,0 @@ -"use client"; - -import { SnackbarProvider } from "notistack"; - -export default SnackbarProvider; diff --git a/src/features/LoginPage/loginPage.tsx b/src/features/LoginPage/loginPage.tsx index 8ed92f3..35a0d71 100644 --- a/src/features/LoginPage/loginPage.tsx +++ b/src/features/LoginPage/loginPage.tsx @@ -10,7 +10,7 @@ import { FetchBaseQueryError } from "@reduxjs/toolkit/query"; import Image from "next/image"; import { useRouter } from "next/navigation"; -import AlertModal from "@/components/AlertModal"; +import { useNotification } from "@/components/Notification"; import useAuth from "@/hooks/useAuth"; import { useLoginMutation } from "@/redux/login/apiSlice"; import useLanguage from "@/services/i18n/use-language"; @@ -23,8 +23,8 @@ export default function LoginPage() { const router = useRouter(); const language = useLanguage(); const theme = useTheme(); + const { dispatch } = useNotification(); - const [error, setError] = useState(null); const [isLogin, setIsLogin] = useState(false); const [login] = useLoginMutation(); @@ -41,17 +41,36 @@ export default function LoginPage() { router.replace(`/${language}/videos`); } else if (errorState) { const errorMessage = errorState.data as string[]; - setError(errorMessage[0]); + if (errorMessage) { + dispatch({ + type: "SHOW_NOTIFICATION", + payload: { + message: errorMessage[0], + severity: "error", + }, + }); + } } } else { - setError("Google login failed: No credential received."); + dispatch({ + type: "SHOW_NOTIFICATION", + payload: { + message: "Google login failed: No credential received.", + severity: "error", + }, + }); return; } }; const onError = () => { - setIsLogin(false); - setError("Authentication Error: Google login failed. Please try again."); + dispatch({ + type: "SHOW_NOTIFICATION", + payload: { + message: "Authentication Error: Google login failed. Please try again.", + severity: "error", + }, + }); }; const googleLoginHandler = useGoogleLogin({ @@ -79,7 +98,6 @@ export default function LoginPage() { - {error && setError(null)} errorMessage={error} />} ); }