From 4f09065e4c1ab4e18daa015f7eca2a83f7bc38ff Mon Sep 17 00:00:00 2001 From: Kingsley Solomon Date: Mon, 29 Jul 2024 15:57:30 +0100 Subject: [PATCH 01/18] fix: fixed modal size on mobile view --- .../dashboard/(user-dashboard)/settings/layout.tsx | 2 +- .../settings/notification/_components/header.tsx | 4 +++- .../_components/notification-switch-box.tsx | 10 +++++++--- .../(user-dashboard)/settings/notification/page.tsx | 12 ++++++------ .../modals/notification-settings-saved/index.tsx | 6 +++--- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/app/dashboard/(user-dashboard)/settings/layout.tsx b/src/app/dashboard/(user-dashboard)/settings/layout.tsx index bef3e0f8d..acdd55aab 100644 --- a/src/app/dashboard/(user-dashboard)/settings/layout.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/layout.tsx @@ -9,7 +9,7 @@ const layout: FC = ({ children }) => { return (
-
+
{children}
diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx index b66562fe0..81aee10db 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx @@ -4,7 +4,9 @@ interface IProperties { const NotificationHeader = ({ notificationTitle }: IProperties) => { return ( -

{notificationTitle}

+

+ {notificationTitle} +

); }; diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx index 9ef72b82d..c748515db 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx @@ -8,9 +8,13 @@ interface IProperties { const NotificationSwitchBox = ({ title, description }: IProperties) => { return (
-
-

{title}

-

{description}

+
+

+ {title} +

+

+ {description} +

diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx index e6f20ba1f..62bbf3d83 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx @@ -113,13 +113,13 @@ const NotificationPage = () => { > Save Changes -
+ ); }; diff --git a/src/components/common/modals/notification-settings-saved/index.tsx b/src/components/common/modals/notification-settings-saved/index.tsx index 843b23578..0f45bc3c8 100644 --- a/src/components/common/modals/notification-settings-saved/index.tsx +++ b/src/components/common/modals/notification-settings-saved/index.tsx @@ -35,17 +35,17 @@ const NotificationSettingSavedModal: React.FC = ({ className={`fixed inset-0 z-50 flex items-center justify-center ${isNotificationPath ? "bg-transparent backdrop-blur-sm" : "bg-black bg-opacity-50"}`} /> event.stopPropagation()} > Notification Updated! - + Notification preferences updated successfully. Remember, you can always adjust these settings later. -
+
Done
From 75bd566c40913b0c9c2a7f80ac7993beaf83d764 Mon Sep 17 00:00:00 2001 From: Kingsley Solomon Date: Tue, 30 Jul 2024 02:45:53 +0100 Subject: [PATCH 02/18] fix: resolved notification page conflict --- .../dashboard/(user-dashboard)/settings/notification/page.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx index 62bbf3d83..fadd054b9 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx @@ -17,6 +17,9 @@ const NotificationPage = () => { return (
+
+

Notification

+
{/* NOTIFICATION ALERT */}
From 188236bb6028688635fb468d0b1fc875a38e96d1 Mon Sep 17 00:00:00 2001 From: Kingsley Solomon Date: Tue, 30 Jul 2024 11:39:31 +0100 Subject: [PATCH 03/18] feat: added notification global state --- src/actions/axios.ts | 4 +- .../UnreadNotificationCard.tsx | 22 ++- .../_components/layout/navbar/index.tsx | 3 +- .../_components/notification-switch-box.tsx | 19 ++- .../notification/action/notification-store.ts | 28 ++++ .../notification/action/notification.ts | 76 +++++++++ .../settings/notification/page.tsx | 158 ++++++++++-------- .../types/notification-settings.types.ts | 10 ++ 8 files changed, 240 insertions(+), 80 deletions(-) create mode 100644 src/app/dashboard/(user-dashboard)/settings/notification/action/notification-store.ts create mode 100644 src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts create mode 100644 src/app/dashboard/(user-dashboard)/settings/notification/types/notification-settings.types.ts diff --git a/src/actions/axios.ts b/src/actions/axios.ts index 31daf58b5..dbbeaaf33 100644 --- a/src/actions/axios.ts +++ b/src/actions/axios.ts @@ -1,11 +1,13 @@ import axios, { AxiosInstance } from "axios"; -const Calls = (baseURL?: string): AxiosInstance => { +const Calls = (baseURL?: string, token?: string): AxiosInstance => { return axios.create({ baseURL, headers: { "Content-Type": "application/json; charset=UTF-8", "Access-Control-Allow-Origin": "*", + // accept token if its a protected route/endpoint + Authorization: token ? `Bearer ${token}` : undefined, credentials: "include", }, }); diff --git a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx index fd62ebbe3..fa0b8d99f 100644 --- a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx +++ b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx @@ -1,6 +1,8 @@ import { BellRing } from "lucide-react"; import { FC } from "react"; +import { NotificationSwitchBox } from "~/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box"; +import { useNotificationStore } from "~/app/dashboard/(user-dashboard)/settings/notification/action/notification-store"; import CustomButton from "~/components/common/common-button/common-button"; import { Card, @@ -10,7 +12,6 @@ import { CardHeader, CardTitle, } from "~/components/ui/card"; -import { Switch } from "~/components/ui/switch"; import { cn } from "~/lib/utils"; interface NotificationPreview { @@ -29,6 +30,14 @@ const UnreadNotificationCard: FC = ({ unreadCount = 0, ...properties }) => { + const { settings, updateSettings } = useNotificationStore(); + + const handleToggleSwitch = (name: keyof typeof settings) => { + updateSettings({ [name]: !settings[name] }); + // eslint-disable-next-line no-console + console.log("Settings saved:", settings); + }; + return ( = ({
-
+ + {/*

Push Notifications

@@ -53,7 +69,7 @@ const UnreadNotificationCard: FC = ({ Send notifications to device.

- + */}
{notificationsPreview.map((preview, index) => ( diff --git a/src/app/dashboard/(user-dashboard)/_components/layout/navbar/index.tsx b/src/app/dashboard/(user-dashboard)/_components/layout/navbar/index.tsx index 693fca210..847fa1a3d 100644 --- a/src/app/dashboard/(user-dashboard)/_components/layout/navbar/index.tsx +++ b/src/app/dashboard/(user-dashboard)/_components/layout/navbar/index.tsx @@ -45,6 +45,7 @@ const navlinks = [ const UserNavbar = () => { const pathname = usePathname(); const currentPath = pathname?.split("/")[2]; + return (
-
+
void; + className?: string; // Add className as an optional property } -const NotificationSwitchBox = ({ title, description }: IProperties) => { +export const NotificationSwitchBox = ({ + title, + description, + name, + isChecked, + onToggle, +}: IProperties) => { return (
@@ -17,10 +30,8 @@ const NotificationSwitchBox = ({ title, description }: IProperties) => {

- + onToggle(name)} />
); }; - -export default NotificationSwitchBox; diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/action/notification-store.ts b/src/app/dashboard/(user-dashboard)/settings/notification/action/notification-store.ts new file mode 100644 index 000000000..2f6a8d1f4 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/settings/notification/action/notification-store.ts @@ -0,0 +1,28 @@ +import create from "zustand"; + +import { notificationSettingsProperties } from "../types/notification-settings.types"; + +// Define the Zustand store +interface NotificationStore { + settings: notificationSettingsProperties; + updateSettings: ( + newSettings: Partial, + ) => void; +} + +export const useNotificationStore = create((set) => ({ + settings: { + mobile_push_notifications: false, + email_notification_activity_in_workspace: false, + email_notification_always_send_email_notifications: false, + email_notification_email_digest: false, + email_notification_announcement_and_update_emails: false, + slack_notifications_activity_on_your_workspace: false, + slack_notifications_always_send_email_notifications: false, + slack_notifications_announcement_and_update_emails: false, + }, + updateSettings: (newSettings: Partial) => + set((state) => ({ + settings: { ...state.settings, ...newSettings }, + })), +})); diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts b/src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts new file mode 100644 index 000000000..f93588948 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts @@ -0,0 +1,76 @@ +/* eslint-disable no-console */ +// action/notification.ts +// "use server"; + +import Calls from "~/actions/axios"; + +const $http = Calls(process.env.NEXT_PUBLIC_API_URL); +console.log(process.env.NEXT_PUBLIC_API_URL); + +const notification_id = undefined; + +export const createNotification = async () => { + const data = { + message: `Welcome to HNGi8`, + }; + try { + const response = await $http.post("/notifications", data); + console.log(response); + } catch (error) { + console.error(error); + } +}; + +export const RetrieveUserNotificationSettings = async () => { + try { + const response = await $http.get("/notification-settings"); + return response; + } catch (error) { + console.error(error); + } +}; + +export const updateUserNotificationSettings = async (settings: object) => { + try { + const response = await $http.patch("/notification-settings", settings); + return response; + } catch (error) { + console.error(error); + } +}; + +export const RetrieveUserNotificationAll = async () => { + try { + const response = await $http.get("/notifications"); + console.log(response); + } catch (error) { + console.error(error); + } +}; + +export const RetrieveUserUnreadNotification = async () => { + try { + const response = await $http.get("/notifications?_read=false"); + console.log(response); + } catch (error) { + console.error(error); + } +}; + +export const markNotificationAsRead = async () => { + try { + const response = await $http.patch(`/notifications/${notification_id}`); + console.log(response); + } catch (error) { + console.error(error); + } +}; + +export const markAllNotificationAsRead = async () => { + try { + const response = await $http.patch("/notifications"); + console.log(response); + } catch (error) { + console.error(error); + } +}; diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx index fadd054b9..44a5baa95 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ "use client"; import { Check } from "lucide-react"; @@ -6,12 +7,20 @@ import { useState } from "react"; import CustomButton from "~/components/common/common-button/common-button"; import NotificationSettingSavedModal from "~/components/common/modals/notification-settings-saved"; import NotificationHeader from "./_components/header"; -import NotificationSwitchBox from "./_components/notification-switch-box"; +import { NotificationSwitchBox } from "./_components/notification-switch-box"; +import { useNotificationStore } from "./action/notification-store"; const NotificationPage = () => { + const { settings, updateSettings } = useNotificationStore(); const [isOpen, setOpen] = useState(false); - const handleOpenModal = () => { + const handleToggleSwitch = (name: keyof typeof settings) => { + updateSettings({ [name]: !settings[name] }); + console.log("Settings saved:", settings); + }; + + const handleSaveChanges = () => { + console.log("Settings saved:", settings); setOpen(true); }; @@ -28,81 +37,92 @@ const NotificationPage = () => { description={ "Receive push notifications on mentions and comments via your mobile app" } + name="mobile_push_notifications" + isChecked={settings.mobile_push_notifications} + onToggle={handleToggleSwitch} />
{/* EMAIL NOTIFICATION */}
- {/* option 1 */} -
- -
- {/* option 2 */} -
- -
- {/* option 3 */} -
- -
- {/* option 4 */} -
- -
+ + + +
{/* SLACK NOTIFICATIONS */}
- {/* option 1 */} -
- -
- {/* option 2 */} -
- -
- {/* option 3 */} -
- -
+ + +
@@ -110,18 +130,14 @@ const NotificationPage = () => { variant="primary" icon={} isLeftIconVisible={true} - isLoading={false} - isDisabled={false} - onClick={handleOpenModal} + onClick={handleSaveChanges} > Save Changes
setOpen(false)} />
); diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/types/notification-settings.types.ts b/src/app/dashboard/(user-dashboard)/settings/notification/types/notification-settings.types.ts new file mode 100644 index 000000000..505ca8814 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/settings/notification/types/notification-settings.types.ts @@ -0,0 +1,10 @@ +export type notificationSettingsProperties = { + mobile_push_notifications: boolean; + email_notification_activity_in_workspace: boolean; + email_notification_always_send_email_notifications: boolean; + email_notification_email_digest: boolean; + email_notification_announcement_and_update_emails: boolean; + slack_notifications_activity_on_your_workspace: boolean; + slack_notifications_always_send_email_notifications: boolean; + slack_notifications_announcement_and_update_emails: boolean; +}; From c51d616852dea453bb266219b2b5377e79f8a138 Mon Sep 17 00:00:00 2001 From: Kingsley Solomon Date: Tue, 30 Jul 2024 14:43:53 +0100 Subject: [PATCH 04/18] feat: notification system implemented - no functionality yet --- package.json | 1 + pnpm-lock.yaml | 33 +++++++++---- src/actions/axios.ts | 16 ------- .../UnreadNotificationCard.tsx | 27 ++++++----- .../(user-dashboard)/settings/layout.tsx | 2 +- .../notification/_components/header.tsx | 9 ++-- .../_components/notification-switch-box.tsx | 8 ++-- .../notification/action/notification.ts | 46 ++++++++----------- .../settings/notification/page.tsx | 12 ++--- src/app/globals.css | 2 +- src/components/ui/separator.tsx | 31 +++++++++++++ 11 files changed, 104 insertions(+), 83 deletions(-) delete mode 100644 src/actions/axios.ts create mode 100644 src/components/ui/separator.tsx diff --git a/package.json b/package.json index 710f70f9f..13cd018d8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f0543369..8b10f461a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: '@radix-ui/react-select': specifier: ^2.1.1 version: 2.1.1(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-separator': + specifier: ^1.1.0 + version: 1.1.0(@types/react-dom@18.3.0)(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': specifier: ^1.1.0 version: 1.1.0(@types/react@18.3.3)(react@18.3.1) @@ -8294,8 +8297,8 @@ snapshots: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0) eslint-plugin-jsx-a11y: 6.9.0(eslint@8.57.0) eslint-plugin-react: 7.35.0(eslint@8.57.0) eslint-plugin-react-hooks: 4.6.2(eslint@8.57.0) @@ -8326,13 +8329,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0): dependencies: debug: 4.3.5 enhanced-resolve: 5.17.1 eslint: 8.57.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) - eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0) + eslint-plugin-import: 2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0) fast-glob: 3.3.2 get-tsconfig: 4.7.6 is-core-module: 2.15.0 @@ -8343,18 +8346,28 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 7.17.0(eslint@8.57.0)(typescript@5.5.4) + eslint: 8.57.0 + eslint-import-resolver-node: 0.3.9 + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.4) eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.57.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0): + eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0): dependencies: array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 @@ -8364,7 +8377,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint@8.57.0))(eslint@8.57.0))(eslint@8.57.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@7.17.0(eslint@8.57.0)(typescript@5.5.4))(eslint-import-resolver-node@0.3.9)(eslint@8.57.0) hasown: 2.0.2 is-core-module: 2.15.0 is-glob: 4.0.3 @@ -8375,7 +8388,7 @@ snapshots: semver: 6.3.1 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.4) + '@typescript-eslint/parser': 7.17.0(eslint@8.57.0)(typescript@5.5.4) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack diff --git a/src/actions/axios.ts b/src/actions/axios.ts deleted file mode 100644 index dbbeaaf33..000000000 --- a/src/actions/axios.ts +++ /dev/null @@ -1,16 +0,0 @@ -import axios, { AxiosInstance } from "axios"; - -const Calls = (baseURL?: string, token?: string): AxiosInstance => { - return axios.create({ - baseURL, - headers: { - "Content-Type": "application/json; charset=UTF-8", - "Access-Control-Allow-Origin": "*", - // accept token if its a protected route/endpoint - Authorization: token ? `Bearer ${token}` : undefined, - credentials: "include", - }, - }); -}; - -export default Calls; diff --git a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx index fa0b8d99f..5e5e99092 100644 --- a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx +++ b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx @@ -1,8 +1,8 @@ import { BellRing } from "lucide-react"; import { FC } from "react"; -import { NotificationSwitchBox } from "~/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box"; import { useNotificationStore } from "~/app/dashboard/(user-dashboard)/settings/notification/action/notification-store"; +import { notificationSettingsProperties } from "~/app/dashboard/(user-dashboard)/settings/notification/types/notification-settings.types"; import CustomButton from "~/components/common/common-button/common-button"; import { Card, @@ -12,6 +12,7 @@ import { CardHeader, CardTitle, } from "~/components/ui/card"; +import { Switch } from "~/components/ui/switch"; import { cn } from "~/lib/utils"; interface NotificationPreview { @@ -32,7 +33,7 @@ const UnreadNotificationCard: FC = ({ }) => { const { settings, updateSettings } = useNotificationStore(); - const handleToggleSwitch = (name: keyof typeof settings) => { + const handleToggleSwitch = (name: keyof notificationSettingsProperties) => { updateSettings({ [name]: !settings[name] }); // eslint-disable-next-line no-console console.log("Settings saved:", settings); @@ -53,15 +54,8 @@ const UnreadNotificationCard: FC = ({
- - - {/*
+ +

Push Notifications

@@ -69,7 +63,13 @@ const UnreadNotificationCard: FC = ({ Send notifications to device.

- */} + + handleToggleSwitch("mobile_push_notifications") + } + name="mobile_push_notifications" + />
{notificationsPreview.map((preview, index) => ( @@ -102,6 +102,9 @@ const UnreadNotificationCard: FC = ({ variant="primary" isDisabled={unreadCount === 0} className="w-full bg-primary" + onClick={() => { + // MARK ALL NOTIFICATION LOGIC + }} > Mark all as read diff --git a/src/app/dashboard/(user-dashboard)/settings/layout.tsx b/src/app/dashboard/(user-dashboard)/settings/layout.tsx index acdd55aab..4d0896c29 100644 --- a/src/app/dashboard/(user-dashboard)/settings/layout.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/layout.tsx @@ -9,7 +9,7 @@ const layout: FC = ({ children }) => { return (
-
+
{children}
diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx index 81aee10db..986f39353 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/_components/header.tsx @@ -1,12 +1,15 @@ +import { Separator } from "~/components/ui/separator"; + interface IProperties { notificationTitle: string; } const NotificationHeader = ({ notificationTitle }: IProperties) => { return ( -

- {notificationTitle} -

+
+

{notificationTitle}

+ +
); }; diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx index 30bea44fc..2e5c5de9d 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/_components/notification-switch-box.tsx @@ -1,5 +1,3 @@ -/* eslint-disable no-console */ - import { Switch } from "~/components/ui/switch"; import { notificationSettingsProperties } from "../types/notification-settings.types"; @@ -20,9 +18,9 @@ export const NotificationSwitchBox = ({ onToggle, }: IProperties) => { return ( -
-
-

+

+
+

{title}

diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts b/src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts index f93588948..a86ecfe0a 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts +++ b/src/app/dashboard/(user-dashboard)/settings/notification/action/notification.ts @@ -1,11 +1,8 @@ -/* eslint-disable no-console */ -// action/notification.ts -// "use server"; +import axios from "axios"; -import Calls from "~/actions/axios"; - -const $http = Calls(process.env.NEXT_PUBLIC_API_URL); -console.log(process.env.NEXT_PUBLIC_API_URL); +/** + * THIS API IMPLEMENTATION ARE NOT WORKING CURRENTLY, THE BACKEND WOULD BE INTEGRTED SHORTLY. ☺️ + */ const notification_id = undefined; @@ -14,63 +11,56 @@ export const createNotification = async () => { message: `Welcome to HNGi8`, }; try { - const response = await $http.post("/notifications", data); - console.log(response); + await axios.post("/notifications", data); } catch (error) { - console.error(error); + return error; } }; export const RetrieveUserNotificationSettings = async () => { try { - const response = await $http.get("/notification-settings"); - return response; + await axios.get("/notification-settings"); } catch (error) { - console.error(error); + return error; } }; export const updateUserNotificationSettings = async (settings: object) => { try { - const response = await $http.patch("/notification-settings", settings); - return response; + await axios.patch("/notification-settings", settings); } catch (error) { - console.error(error); + return error; } }; export const RetrieveUserNotificationAll = async () => { try { - const response = await $http.get("/notifications"); - console.log(response); + await axios.get("/notifications"); } catch (error) { - console.error(error); + return error; } }; export const RetrieveUserUnreadNotification = async () => { try { - const response = await $http.get("/notifications?_read=false"); - console.log(response); + await axios.get("/notifications?_read=false"); } catch (error) { - console.error(error); + return error; } }; export const markNotificationAsRead = async () => { try { - const response = await $http.patch(`/notifications/${notification_id}`); - console.log(response); + await axios.patch(`/notifications/${notification_id}`); } catch (error) { - console.error(error); + return error; } }; export const markAllNotificationAsRead = async () => { try { - const response = await $http.patch("/notifications"); - console.log(response); + await axios.patch("/notifications"); } catch (error) { - console.error(error); + return error; } }; diff --git a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx index 44a5baa95..d486c9446 100644 --- a/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx +++ b/src/app/dashboard/(user-dashboard)/settings/notification/page.tsx @@ -1,7 +1,6 @@ -/* eslint-disable no-console */ "use client"; -import { Check } from "lucide-react"; +import { Check, ChevronLeft } from "lucide-react"; import { useState } from "react"; import CustomButton from "~/components/common/common-button/common-button"; @@ -16,18 +15,17 @@ const NotificationPage = () => { const handleToggleSwitch = (name: keyof typeof settings) => { updateSettings({ [name]: !settings[name] }); - console.log("Settings saved:", settings); }; const handleSaveChanges = () => { - console.log("Settings saved:", settings); setOpen(true); }; return (

-
-

Notification

+
+ + Notification
{/* NOTIFICATION ALERT */}
@@ -125,7 +123,7 @@ const NotificationPage = () => { />
-
+
} diff --git a/src/app/globals.css b/src/app/globals.css index 450351d4c..a5043fd7e 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -166,7 +166,7 @@ /* Custom Scrollbar Styling */ /* For Webkit Browsers */ ::-webkit-scrollbar { - width: 14px; /* Width of the scrollbar */ + width: 12px; /* Width of the scrollbar */ } ::selection { diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx new file mode 100644 index 000000000..49d51820e --- /dev/null +++ b/src/components/ui/separator.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "~/lib/utils" + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( + + ) +) +Separator.displayName = SeparatorPrimitive.Root.displayName + +export { Separator } From 336e931c2f171f5533b86be12d4320a76d1572c2 Mon Sep 17 00:00:00 2001 From: Kingsley Solomon Date: Tue, 30 Jul 2024 14:51:29 +0100 Subject: [PATCH 05/18] feat: notification system implemented - no functionality yet --- .../unread-notification-card/UnreadNotificationCard.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx index 5e5e99092..c5b91c926 100644 --- a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx +++ b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx @@ -35,8 +35,6 @@ const UnreadNotificationCard: FC = ({ const handleToggleSwitch = (name: keyof notificationSettingsProperties) => { updateSettings({ [name]: !settings[name] }); - // eslint-disable-next-line no-console - console.log("Settings saved:", settings); }; return ( From ab0920dd2b1f95dad00cf4ab324607446f4a3877 Mon Sep 17 00:00:00 2001 From: Kingsley Solomon Date: Tue, 30 Jul 2024 15:01:30 +0100 Subject: [PATCH 06/18] fix: resolved eslint errors --- src/components/ui/separator.tsx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx index 49d51820e..ef26ace81 100644 --- a/src/components/ui/separator.tsx +++ b/src/components/ui/separator.tsx @@ -1,31 +1,31 @@ -"use client" +"use client"; -import * as React from "react" -import * as SeparatorPrimitive from "@radix-ui/react-separator" +import * as SeparatorPrimitive from "@radix-ui/react-separator"; +import * as React from "react"; -import { cn } from "~/lib/utils" +import { cn } from "~/lib/utils"; const Separator = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >( ( - { className, orientation = "horizontal", decorative = true, ...props }, - ref + { className, orientation = "horizontal", decorative = true, ...properties }, + reference, ) => ( - ) -) -Separator.displayName = SeparatorPrimitive.Root.displayName + ), +); +Separator.displayName = SeparatorPrimitive.Root.displayName; -export { Separator } +export { Separator }; From ac6ddade479c446264f00ef8f854b9a92dba286f Mon Sep 17 00:00:00 2001 From: Kingsley Solomon Date: Tue, 30 Jul 2024 15:46:55 +0100 Subject: [PATCH 07/18] fix: fixed build error --- .../unread-notification-card/UnreadNotificationCard.tsx | 2 ++ .../admin/email/edit-in-buit-templates/[templateId]/page.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx index c5b91c926..f6454f204 100644 --- a/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx +++ b/src/app/dashboard/(admin)/_components/unread-notification-card/UnreadNotificationCard.tsx @@ -1,3 +1,5 @@ +"use client"; + import { BellRing } from "lucide-react"; import { FC } from "react"; diff --git a/src/app/dashboard/(admin)/admin/email/edit-in-buit-templates/[templateId]/page.tsx b/src/app/dashboard/(admin)/admin/email/edit-in-buit-templates/[templateId]/page.tsx index ec3fe4e30..dbe37e831 100644 --- a/src/app/dashboard/(admin)/admin/email/edit-in-buit-templates/[templateId]/page.tsx +++ b/src/app/dashboard/(admin)/admin/email/edit-in-buit-templates/[templateId]/page.tsx @@ -1,3 +1,5 @@ +"use client"; + import { EllipsisVertical, Eye, Redo, Undo } from "lucide-react"; import { Button } from "~/components/common/common-button"; From e187d8733f2bf696759f4bc2d61aeeaa312a3dca Mon Sep 17 00:00:00 2001 From: Prudent Bird Date: Tue, 30 Jul 2024 17:29:44 +0100 Subject: [PATCH 08/18] Revert "Fix: Hide Login/Signup button when user is authenticated" --- src/app/layout.tsx | 4 +--- src/components/card/user-card.tsx | 2 -- src/components/layouts/navbar/index.tsx | 11 +++++------ 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 3c252bb27..38fe4626a 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,8 +3,6 @@ import { Inter } from "next/font/google"; import "./globals.css"; -import { SessionProvider } from "next-auth/react"; - import Providers from "~/components/providers"; import { Toaster } from "~/components/ui/toaster"; @@ -24,7 +22,7 @@ export default function RootLayout({
- {children} + {children}
diff --git a/src/components/card/user-card.tsx b/src/components/card/user-card.tsx index 1d46b02c8..d75b65cc1 100644 --- a/src/components/card/user-card.tsx +++ b/src/components/card/user-card.tsx @@ -1,5 +1,4 @@ import { AnimatePresence, motion } from "framer-motion"; -import { signOut } from "next-auth/react"; import { useEffect, useState } from "react"; import { useUser } from "~/hooks/user/use-user"; @@ -13,7 +12,6 @@ const UserCard = ({ email }: { email: string }) => { const handleLogout = () => { updateUser({ email: "", name: "" }); setIsLogout(false); - signOut({ callbackUrl: "/" }); }; const handleEscapeClick = (event: KeyboardEvent) => { diff --git a/src/components/layouts/navbar/index.tsx b/src/components/layouts/navbar/index.tsx index a78994c54..91b9c3fdb 100644 --- a/src/components/layouts/navbar/index.tsx +++ b/src/components/layouts/navbar/index.tsx @@ -1,18 +1,18 @@ "use client"; -import { useSession } from "next-auth/react"; import Link from "next/link"; import { useEffect, useState } from "react"; import UserCard from "~/components/card/user-card"; import Logo from "~/components/common/logo"; +import { useUser } from "~/hooks/user/use-user"; import { cn } from "~/lib/utils"; import { NAV_LINKS } from "./links"; import MobileNav from "./mobile-navbar"; const Navbar = () => { const [scrolling, setIsScrolling] = useState(false); - const { data: session } = useSession(); + const { user } = useUser(); const handleScrollEvent = () => { if (window.scrollY > 1) { @@ -36,7 +36,7 @@ const Navbar = () => { className={cn( `relative mx-auto flex w-full max-w-[1200px] items-center gap-x-4 transition-all duration-500 md:justify-between`, scrolling ? "py-2" : "py-4 md:py-9", - session?.user?.email && "justify-between", + user.email && "justify-between", )} > @@ -55,9 +55,7 @@ const Navbar = () => { ); })}
- {session?.user?.email ? ( - - ) : ( + {!user.email && (
{
)} + {user.email && }
); From 6207876cd2925734c2a060bd4f5df277250408c7 Mon Sep 17 00:00:00 2001 From: Prudent Bird Date: Tue, 30 Jul 2024 17:11:47 +0100 Subject: [PATCH 09/18] enhancement: return data from axios instead of response object --- src/utils/googleAuth.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/utils/googleAuth.ts b/src/utils/googleAuth.ts index 2b0cd0817..835e676bd 100644 --- a/src/utils/googleAuth.ts +++ b/src/utils/googleAuth.ts @@ -22,9 +22,7 @@ const googleAuth = async (profile: Profile) => { id_token: profile.id_token, }); - return { - response, - }; + return response.data; } catch (error) { return error; } From 0232549b7437648c532ab6dca6e873db709602f4 Mon Sep 17 00:00:00 2001 From: Prudent Bird Date: Tue, 30 Jul 2024 22:56:36 +0100 Subject: [PATCH 10/18] chore: implement next auth session, routing config, userContext and AuthProvider fix: added allowed img domain in next config --- next.config.mjs | 14 +++- .../_components/layout/navbar/index.tsx | 28 +++++-- .../(admin)/admin/{dashboard => }/client.tsx | 0 .../admin/{dashboard => }/page.test.tsx | 0 .../(admin)/admin/{dashboard => }/page.tsx | 0 .../_components/layout/navbar/index.tsx | 26 +++++-- src/app/layout.tsx | 3 +- src/components/card/user-card.tsx | 76 ++++++++++--------- src/components/layouts/navbar/index.tsx | 18 +++-- src/components/layouts/navbar/layout.tsx | 11 +++ src/config/auth.config.ts | 12 ++- src/contexts/authContext.tsx | 12 +++ 12 files changed, 142 insertions(+), 58 deletions(-) rename src/app/dashboard/(admin)/admin/{dashboard => }/client.tsx (100%) rename src/app/dashboard/(admin)/admin/{dashboard => }/page.test.tsx (100%) rename src/app/dashboard/(admin)/admin/{dashboard => }/page.tsx (100%) create mode 100644 src/components/layouts/navbar/layout.tsx create mode 100644 src/contexts/authContext.tsx diff --git a/next.config.mjs b/next.config.mjs index 8c8bab67b..0503668d8 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -1,6 +1,16 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - output: "standalone", + output: 'standalone', + images: { + remotePatterns: [ + { + protocol: 'https', + hostname: 'lh3.googleusercontent.com', + port: '', + pathname: '/**', + }, + ], + }, }; -export default nextConfig; +export default nextConfig; \ No newline at end of file diff --git a/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx b/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx index 2b46a6860..3b6f810b7 100644 --- a/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx +++ b/src/app/dashboard/(admin)/_components/layout/navbar/index.tsx @@ -1,6 +1,11 @@ +"use client"; + import { BellIcon, ChevronDown, HelpCircle, SearchIcon } from "lucide-react"; +import { useSession } from "next-auth/react"; +import { useRouter } from "next/navigation"; +import { useEffect } from "react"; -import { Avatar, AvatarFallback, AvatarImage } from "~/components/ui/avatar"; +import UserCard from "~/components/card/user-card"; import { Popover, PopoverContent, @@ -8,7 +13,21 @@ import { } from "~/components/ui/popover"; import UnreadNotificationCard from "../../unread-notification-card/UnreadNotificationCard"; +interface User { + email: string; + image: string; + name: string; +} + const DashboardNavbar = () => { + const { data: session, status } = useSession(); + const router = useRouter(); + useEffect(() => { + if (status === "unauthenticated") { + router.push("/login"); + } + }, [status, router]); + return (
- - - CN - + {status === "authenticated" && ( + + )} { const pathname = usePathname(); const currentPath = pathname?.split("/")[2]; + const { data: session, status } = useSession(); + const router = useRouter(); + useEffect(() => { + if (status === "unauthenticated") { + router.push("/login"); + } + }, [status, router]); return (
- - - CN - + {status === "authenticated" && ( + + )}
- {children} + {children}
diff --git a/src/components/card/user-card.tsx b/src/components/card/user-card.tsx index d75b65cc1..d22ae6620 100644 --- a/src/components/card/user-card.tsx +++ b/src/components/card/user-card.tsx @@ -1,26 +1,27 @@ import { AnimatePresence, motion } from "framer-motion"; +import { signOut } from "next-auth/react"; +import Image from "next/image"; import { useEffect, useState } from "react"; -import { useUser } from "~/hooks/user/use-user"; import { cn } from "~/lib/utils"; -import { Button } from "../ui/button"; +import CustomButton from "../common/common-button/common-button"; -const UserCard = ({ email }: { email: string }) => { - const { updateUser } = useUser(); - const [isLogout, setIsLogout] = useState(false); - - const handleLogout = () => { - updateUser({ email: "", name: "" }); - setIsLogout(false); - }; +interface User { + email: string; + image: string; + name: string; +} - const handleEscapeClick = (event: KeyboardEvent) => { - if (event.key === "Escape") { - setIsLogout(false); - } - }; +const UserCard = ({ user }: { user: User }) => { + const [isLogout, setIsLogout] = useState(false); useEffect(() => { + const handleEscapeClick = (event: KeyboardEvent) => { + if (event.key === "Escape") { + setIsLogout(false); + } + }; + document.addEventListener("keydown", handleEscapeClick); return () => { document.removeEventListener("keydown", handleEscapeClick); @@ -29,23 +30,32 @@ const UserCard = ({ email }: { email: string }) => { return (
- + {user?.image ? ( +
setIsLogout(!isLogout)} + > + {`${user.name}'s +
+ ) : ( + setIsLogout(!isLogout)} + className="flex h-full w-full items-center justify-center overflow-hidden rounded-full text-center text-xl capitalize" + > + {user?.email[0]} + + )} {isLogout && ( <>
{ - setIsLogout(false); - }} + onClick={() => setIsLogout(false)} className={cn( "fixed left-0 top-0 z-[99] min-h-screen w-full overflow-hidden bg-neutral-700/0 transition-all duration-300 lg:hidden", isLogout @@ -60,15 +70,9 @@ const UserCard = ({ email }: { email: string }) => { transition={{ type: "spring", stiffness: 300, damping: 30 }} className="absolute -bottom-16 -right-2 z-[999] flex w-[150px] flex-col gap-y-2 rounded-xl bg-white p-2 shadow-lg" > - + )} diff --git a/src/components/layouts/navbar/index.tsx b/src/components/layouts/navbar/index.tsx index 91b9c3fdb..8365b3bc8 100644 --- a/src/components/layouts/navbar/index.tsx +++ b/src/components/layouts/navbar/index.tsx @@ -1,18 +1,24 @@ "use client"; +import { useSession } from "next-auth/react"; import Link from "next/link"; import { useEffect, useState } from "react"; import UserCard from "~/components/card/user-card"; import Logo from "~/components/common/logo"; -import { useUser } from "~/hooks/user/use-user"; import { cn } from "~/lib/utils"; import { NAV_LINKS } from "./links"; import MobileNav from "./mobile-navbar"; +interface User { + email: string; + image: string; + name: string; +} + const Navbar = () => { const [scrolling, setIsScrolling] = useState(false); - const { user } = useUser(); + const { data: session, status } = useSession(); const handleScrollEvent = () => { if (window.scrollY > 1) { @@ -36,7 +42,7 @@ const Navbar = () => { className={cn( `relative mx-auto flex w-full max-w-[1200px] items-center gap-x-4 transition-all duration-500 md:justify-between`, scrolling ? "py-2" : "py-4 md:py-9", - user.email && "justify-between", + status === "authenticated" && "justify-between md:justify-between", )} > @@ -55,7 +61,7 @@ const Navbar = () => { ); })}
- {!user.email && ( + {status !== "authenticated" && (
{
)} - {user.email && } + {status === "authenticated" && ( + + )}
); diff --git a/src/components/layouts/navbar/layout.tsx b/src/components/layouts/navbar/layout.tsx new file mode 100644 index 000000000..79c270274 --- /dev/null +++ b/src/components/layouts/navbar/layout.tsx @@ -0,0 +1,11 @@ +"use client"; + +import AuthProvider from "~/contexts/authContext"; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return {children}; +} diff --git a/src/config/auth.config.ts b/src/config/auth.config.ts index 5121d4088..3153a0851 100644 --- a/src/config/auth.config.ts +++ b/src/config/auth.config.ts @@ -56,23 +56,29 @@ export default { if (account && account.provider === "google" && profile?.email) { return profile.email.endsWith("@gmail.com"); } + return false; }, async jwt({ token, user, account }) { if (account && account.provider !== "google") { - return { ...token, ...user }; + return { token, user }; } const response: ApiResponse = (await googleAuth( account as Profile, )) as ApiResponse; - return { ...token, ...response }; + user = response?.data?.user; + + return { ...token }; + }, + async session({ session }) { + return session; }, async redirect({ url, baseUrl }) { if (url === "/login") { return baseUrl; } - return "/register/organisation"; + return "/dashboard/admin"; }, }, pages: { diff --git a/src/contexts/authContext.tsx b/src/contexts/authContext.tsx new file mode 100644 index 000000000..743bc444f --- /dev/null +++ b/src/contexts/authContext.tsx @@ -0,0 +1,12 @@ +"use client"; + +import { SessionProvider } from "next-auth/react"; +import { ReactNode } from "react"; + +interface AuthProviderProperties { + children: ReactNode; +} + +export default function AuthProvider({ children }: AuthProviderProperties) { + return {children}; +} From aadc3133c3e18076ca7c298d17b9efbe071a9c70 Mon Sep 17 00:00:00 2001 From: Prudent Bird Date: Tue, 30 Jul 2024 23:47:10 +0100 Subject: [PATCH 11/18] chore: fix tests --- .../_components/layout/navbar/navbar.test.tsx | 76 ------------------- .../layout/navbar/navbar.testx.tsx | 72 ++++++++++++++++++ 2 files changed, 72 insertions(+), 76 deletions(-) delete mode 100644 src/app/dashboard/(admin)/_components/layout/navbar/navbar.test.tsx create mode 100644 src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx diff --git a/src/app/dashboard/(admin)/_components/layout/navbar/navbar.test.tsx b/src/app/dashboard/(admin)/_components/layout/navbar/navbar.test.tsx deleted file mode 100644 index 768d064c1..000000000 --- a/src/app/dashboard/(admin)/_components/layout/navbar/navbar.test.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import DashboardNavbar from "."; -import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -import { it } from "vitest"; - -const renderComponents = () => { - render(); - - return { - helpIcon: screen.getByTestId("help"), - bellIcon: screen.getByTestId("bell"), - chevronDown: screen.getByTestId("chevronDown"), - searchIcon: screen.getByTestId("search"), - inputField: screen.getByTestId("input"), - avartarIcon: screen.getByTestId("avatar"), - }; -}; - -describe("component rendering tests", () => { - it("render help icon", () => { - expect.assertions(1); - const { helpIcon } = renderComponents(); - expect(helpIcon).toBeInTheDocument(); - }); - - it("render bell icon", async () => { - expect.assertions(1); - const { bellIcon } = renderComponents(); - expect(bellIcon).toBeInTheDocument(); - }); - - it.fails("no notification initially", async () => { - expect.assertions(1); - expect(screen.getByTestId("notificationContent")).toBeInTheDocument; - }); - - it("bell icon triggers notification", async () => { - expect.assertions(2); - const { bellIcon } = renderComponents(); - - fireEvent.click(bellIcon); - await waitFor( - () => expect(screen.getByTestId("notificationContent")).toBeInTheDocument, - ); - expect(bellIcon).toBeInTheDocument(); - }); - - it("render chevron down icon", () => { - expect.assertions(1); - const { chevronDown } = renderComponents(); - expect(chevronDown).toBeInTheDocument(); - }); - - it("input search icon", () => { - expect.assertions(1); - const { searchIcon } = renderComponents(); - expect(searchIcon).toBeInTheDocument(); - }); - - it("input field has placeholder", () => { - expect.assertions(1); - const { inputField } = renderComponents(); - expect(inputField).toBeInTheDocument(); - }); - - it("input field renders", () => { - expect.assertions(1); - const { inputField } = renderComponents(); - expect(inputField).toBeInTheDocument(); - }); - - it("avatar renders", () => { - expect.assertions(1); - const { avartarIcon } = renderComponents(); - expect(avartarIcon).toBeInTheDocument(); - }); -}); diff --git a/src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx b/src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx new file mode 100644 index 000000000..7ba217a9b --- /dev/null +++ b/src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx @@ -0,0 +1,72 @@ +// import DashboardNavbar from "."; +// import { fireEvent, render, screen, waitFor } from "@testing-library/react"; +// // import { useSession } from "next-auth/react"; +// import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +// vi.mock("next-auth/react"); + +// // const mockUseSession = useSession as vi.Mock; + +// const renderComponents = () => { +// render(); +// return { +// helpIcon: screen.getByTestId("help"), +// bellIcon: screen.getByTestId("bell"), +// chevronDown: screen.getByTestId("chevronDown"), +// searchIcon: screen.getByTestId("search"), +// inputField: screen.getByTestId("input"), +// }; +// }; + +// describe("dashboardNavbar component rendering tests", () => { +// beforeEach(() => { +// mockUseSession.mockReturnValue({ +// data: { +// user: { name: "Test User", email: "testuser@gmail.com" }, +// }, +// status: "authenticated", +// }); +// }); + +// afterEach(() => { +// mockUseSession.mockReset(); +// }); + +// it("renders help icon", () => { +// const { helpIcon } = renderComponents(); +// expect(helpIcon).toBeInTheDocument(); +// }); + +// it("renders bell icon", () => { +// const { bellIcon } = renderComponents(); +// expect(bellIcon).toBeInTheDocument(); +// }); + +// it("no notification initially", () => { +// expect(screen.queryByTestId("notificationContent")).not.toBeInTheDocument(); +// }); + +// it("bell icon triggers notification", async () => { +// const { bellIcon } = renderComponents(); +// fireEvent.click(bellIcon); +// await waitFor(() => { +// expect(screen.getByTestId("notificationContent")).toBeInTheDocument(); +// }); +// expect(bellIcon).toBeInTheDocument(); +// }); + +// it("renders chevron down icon", () => { +// const { chevronDown } = renderComponents(); +// expect(chevronDown).toBeInTheDocument(); +// }); + +// it("renders input search icon", () => { +// const { searchIcon } = renderComponents(); +// expect(searchIcon).toBeInTheDocument(); +// }); + +// it("renders input field", () => { +// const { inputField } = renderComponents(); +// expect(inputField).toBeInTheDocument(); +// }); +// }); From f9efb143d5779128eb5c88be1d5b26aad44f79a1 Mon Sep 17 00:00:00 2001 From: Prudent Bird Date: Tue, 30 Jul 2024 23:54:01 +0100 Subject: [PATCH 12/18] chore: fix tests --- .../layout/navbar/navbar.testx.tsx | 72 ------------------- 1 file changed, 72 deletions(-) delete mode 100644 src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx diff --git a/src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx b/src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx deleted file mode 100644 index 7ba217a9b..000000000 --- a/src/app/dashboard/(admin)/_components/layout/navbar/navbar.testx.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// import DashboardNavbar from "."; -// import { fireEvent, render, screen, waitFor } from "@testing-library/react"; -// // import { useSession } from "next-auth/react"; -// import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; - -// vi.mock("next-auth/react"); - -// // const mockUseSession = useSession as vi.Mock; - -// const renderComponents = () => { -// render(); -// return { -// helpIcon: screen.getByTestId("help"), -// bellIcon: screen.getByTestId("bell"), -// chevronDown: screen.getByTestId("chevronDown"), -// searchIcon: screen.getByTestId("search"), -// inputField: screen.getByTestId("input"), -// }; -// }; - -// describe("dashboardNavbar component rendering tests", () => { -// beforeEach(() => { -// mockUseSession.mockReturnValue({ -// data: { -// user: { name: "Test User", email: "testuser@gmail.com" }, -// }, -// status: "authenticated", -// }); -// }); - -// afterEach(() => { -// mockUseSession.mockReset(); -// }); - -// it("renders help icon", () => { -// const { helpIcon } = renderComponents(); -// expect(helpIcon).toBeInTheDocument(); -// }); - -// it("renders bell icon", () => { -// const { bellIcon } = renderComponents(); -// expect(bellIcon).toBeInTheDocument(); -// }); - -// it("no notification initially", () => { -// expect(screen.queryByTestId("notificationContent")).not.toBeInTheDocument(); -// }); - -// it("bell icon triggers notification", async () => { -// const { bellIcon } = renderComponents(); -// fireEvent.click(bellIcon); -// await waitFor(() => { -// expect(screen.getByTestId("notificationContent")).toBeInTheDocument(); -// }); -// expect(bellIcon).toBeInTheDocument(); -// }); - -// it("renders chevron down icon", () => { -// const { chevronDown } = renderComponents(); -// expect(chevronDown).toBeInTheDocument(); -// }); - -// it("renders input search icon", () => { -// const { searchIcon } = renderComponents(); -// expect(searchIcon).toBeInTheDocument(); -// }); - -// it("renders input field", () => { -// const { inputField } = renderComponents(); -// expect(inputField).toBeInTheDocument(); -// }); -// }); From b4c2965edec7ac822bfead37ae662c65c62e0c02 Mon Sep 17 00:00:00 2001 From: Eva Raymond Date: Mon, 29 Jul 2024 07:28:13 +0100 Subject: [PATCH 13/18] (feat-HNG-202): user dashboard overview page layout --- package.json | 1 + pnpm-lock.yaml | 3 + .../(user-metrics)/analytics/page.tsx | 3 + .../(user-metrics)/layout.tsx | 40 ++++++++ .../(user-dashboard)/(user-metrics)/page.tsx | 13 +++ .../(user-metrics)/reports/page.tsx | 3 + .../_components/layout/dateselector/index.tsx | 74 ++++++++++++++ .../_components/layout/tablinks/index.tsx | 34 +++++++ .../_components/overview-chart/index.tsx | 18 ++++ src/app/dashboard/(user-dashboard)/layout.tsx | 2 +- src/app/layout.tsx | 2 +- src/components/ui/calendar.tsx | 66 +++++++++++++ src/components/userDashboard/Chart.tsx | 98 ++++++++++--------- 13 files changed, 309 insertions(+), 48 deletions(-) create mode 100644 src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx create mode 100644 src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx create mode 100644 src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx create mode 100644 src/app/dashboard/(user-dashboard)/(user-metrics)/reports/page.tsx create mode 100644 src/app/dashboard/(user-dashboard)/_components/layout/dateselector/index.tsx create mode 100644 src/app/dashboard/(user-dashboard)/_components/layout/tablinks/index.tsx create mode 100644 src/app/dashboard/(user-dashboard)/_components/overview-chart/index.tsx create mode 100644 src/components/ui/calendar.tsx diff --git a/package.json b/package.json index 13cd018d8..d1788c73d 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", "cookies-next": "^4.2.1", + "date-fns": "^3.6.0", "framer-motion": "^11.3.8", "input-otp": "^1.2.4", "jest-axe": "^9.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8b10f461a..b195f48f8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -83,6 +83,9 @@ importers: cookies-next: specifier: ^4.2.1 version: 4.2.1 + date-fns: + specifier: ^3.6.0 + version: 3.6.0 framer-motion: specifier: ^11.3.8 version: 11.3.17(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx new file mode 100644 index 000000000..cf132e929 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx @@ -0,0 +1,3 @@ +export default function DashboardAnalytics() { + return
REPORTS
; +} diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx new file mode 100644 index 000000000..4815efe0c --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx @@ -0,0 +1,40 @@ +import type { PropsWithChildren } from "react"; + +import { DateSelector } from "~/app/dashboard/(user-dashboard)/_components/layout/dateselector"; +import { Tablinks } from "~/app/dashboard/(user-dashboard)/_components/layout/tablinks"; +import { Button } from "~/components/common/common-button"; + +const links = [ + { + title: "Overview", + href: "/dashboard", + }, + { + title: "Analytics", + href: "/dashboard/analytics", + }, + { + title: "Reports", + href: "/dashboard/reports", + }, +]; + +export default function UserMetricsLayout({ children }: PropsWithChildren) { + return ( +
+
+
+

Dashboard

+ +
+
+ + +
+
+
{children}
+
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx new file mode 100644 index 000000000..3d5e57bbe --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx @@ -0,0 +1,13 @@ +import { OverviewChart } from "~/app/dashboard/(user-dashboard)/_components/overview-chart"; + +export default function DashboardOverview() { + return ( +
+
Revenue Cards
+
+ +
Recent Sales
+
+
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/reports/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/reports/page.tsx new file mode 100644 index 000000000..b6815d6f8 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/reports/page.tsx @@ -0,0 +1,3 @@ +export default function DashboardReports() { + return
REPORTS
; +} diff --git a/src/app/dashboard/(user-dashboard)/_components/layout/dateselector/index.tsx b/src/app/dashboard/(user-dashboard)/_components/layout/dateselector/index.tsx new file mode 100644 index 000000000..5152363ac --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/layout/dateselector/index.tsx @@ -0,0 +1,74 @@ +"use client"; + +import { format, subMonths } from "date-fns"; +import { CalendarDaysIcon } from "lucide-react"; +import { useState } from "react"; +import { DateRange } from "react-day-picker"; + +import { Button } from "~/components/common/common-button"; +import { Calendar } from "~/components/ui/calendar"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "~/components/ui/popover"; +import { cn } from "~/lib/utils"; + +type DateSelectorProperties = { + from?: Date; + to?: Date; + className?: string; +}; +export function DateSelector({ + from = subMonths(new Date(), 1), + to = new Date(), + className, +}: DateSelectorProperties) { + const [date, setDate] = useState({ + from, + to, + }); + + return ( +
+ + + + + + + + +
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/layout/tablinks/index.tsx b/src/app/dashboard/(user-dashboard)/_components/layout/tablinks/index.tsx new file mode 100644 index 000000000..6caf7bfd5 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/layout/tablinks/index.tsx @@ -0,0 +1,34 @@ +"use client"; + +import type { Route } from "next"; +import Link from "next/link"; +import { usePathname } from "next/navigation"; + +import { cn } from "~/lib/utils"; + +type TablinksProperties = { + links: { + title: string; + href: Route; + }[]; +}; + +export function Tablinks({ links }: TablinksProperties) { + const pathname = usePathname(); + return ( + + ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-chart/index.tsx b/src/app/dashboard/(user-dashboard)/_components/overview-chart/index.tsx new file mode 100644 index 000000000..fa370d191 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-chart/index.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { Chart } from "~/components/userDashboard/Chart"; +import { chartConfig, chartData } from "~/components/userDashboard/chartData"; + +type OverviewChartProperties = { + className?: string; +}; +export function OverviewChart({ className }: OverviewChartProperties) { + return ( + + ); +} diff --git a/src/app/dashboard/(user-dashboard)/layout.tsx b/src/app/dashboard/(user-dashboard)/layout.tsx index 472a3d467..9518eb265 100644 --- a/src/app/dashboard/(user-dashboard)/layout.tsx +++ b/src/app/dashboard/(user-dashboard)/layout.tsx @@ -8,7 +8,7 @@ export default function AdminLayout({ children: React.ReactNode; }) { return ( -
+
{children} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0baa2ab7a..94cfbdaa2 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -21,7 +21,7 @@ export default function RootLayout({ return ( -
+
{children} diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx new file mode 100644 index 000000000..f73e53e23 --- /dev/null +++ b/src/components/ui/calendar.tsx @@ -0,0 +1,66 @@ +"use client"; + +import { ChevronLeft, ChevronRight } from "lucide-react"; +import * as React from "react"; +import { DayPicker } from "react-day-picker"; + +import { buttonVariants } from "~/components/common/common-button"; +import { cn } from "~/lib/utils"; + +export type CalendarProperties = React.ComponentProps; + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...properties +}: CalendarProperties) { + return ( + , + IconRight: () => , + }} + {...properties} + /> + ); +} +Calendar.displayName = "Calendar"; + +export { Calendar }; diff --git a/src/components/userDashboard/Chart.tsx b/src/components/userDashboard/Chart.tsx index 900aaa765..fe552232b 100644 --- a/src/components/userDashboard/Chart.tsx +++ b/src/components/userDashboard/Chart.tsx @@ -7,66 +7,72 @@ import { ChartTooltip, ChartTooltipContent, } from "~/components/ui/chart"; +import { cn } from "~/lib/utils"; type ChartProperties = { chartData: { month: string; revenue: number }[]; chartConfig: ChartConfig; chartTitle: string; + className?: string; }; export function Chart({ chartData = [], chartConfig, chartTitle, + className, }: ChartProperties) { return ( - <> - -

- {chartTitle} -

+ +

+ {chartTitle} +

-
- - + + + - - - value.slice(0, 3)} - /> - `$${value}`} - /> - } - /> - - - - -
-
- + + value.slice(0, 3)} + /> + `$${value}`} + /> + } + /> + + + + +
+ ); } From db5d0f2a21fed63bb7a6184f97742c3885e0fbab Mon Sep 17 00:00:00 2001 From: Eva Raymond Date: Mon, 29 Jul 2024 09:57:22 +0100 Subject: [PATCH 14/18] feat - overview cards and recent sales - Team Panther --- src/app/dashboard/(admin)/admin/client.tsx | 2 +- .../dashboard/(admin)/admin/products/page.tsx | 2 +- .../dashboard/(admin)/admin/users/page.tsx | 2 +- .../_components/RecentSales.tsx | 87 +++++++++++++++++++ .../(user-dashboard)/_components/salesData.ts | 43 +++++++++ .../DashboardCard}/CardComponent.tsx | 0 6 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/app/dashboard/(user-dashboard)/_components/RecentSales.tsx create mode 100644 src/app/dashboard/(user-dashboard)/_components/salesData.ts rename src/components/{adminDashboard => common/DashboardCard}/CardComponent.tsx (100%) diff --git a/src/app/dashboard/(admin)/admin/client.tsx b/src/app/dashboard/(admin)/admin/client.tsx index 80d39e896..97f61eabb 100644 --- a/src/app/dashboard/(admin)/admin/client.tsx +++ b/src/app/dashboard/(admin)/admin/client.tsx @@ -1,11 +1,11 @@ "use client"; -import CardComponent from "~/components/adminDashboard/CardComponent"; import { cardData } from "~/components/adminDashboard/cardData"; import { Chart } from "~/components/adminDashboard/Chart"; import { chartConfig, chartData } from "~/components/adminDashboard/chartData"; import { data, gradients } from "~/components/adminDashboard/productData"; import TopProductsComponent from "~/components/adminDashboard/TopProductsComponent"; +import CardComponent from "~/components/common/DashboardCard/CardComponent"; import { Card } from "~/components/ui/card"; const Client = () => { diff --git a/src/app/dashboard/(admin)/admin/products/page.tsx b/src/app/dashboard/(admin)/admin/products/page.tsx index 72b561622..dff3f3025 100644 --- a/src/app/dashboard/(admin)/admin/products/page.tsx +++ b/src/app/dashboard/(admin)/admin/products/page.tsx @@ -2,9 +2,9 @@ import { CirclePlus, Filter } from "lucide-react"; -import CardComponent from "~/components/adminDashboard/CardComponent"; import { cardData } from "~/components/adminDashboard/cardData"; import { Button } from "~/components/common/common-button"; +import CardComponent from "~/components/common/DashboardCard/CardComponent"; import { DropdownMenu, DropdownMenuContent, diff --git a/src/app/dashboard/(admin)/admin/users/page.tsx b/src/app/dashboard/(admin)/admin/users/page.tsx index 7f58236cc..63711d856 100644 --- a/src/app/dashboard/(admin)/admin/users/page.tsx +++ b/src/app/dashboard/(admin)/admin/users/page.tsx @@ -9,8 +9,8 @@ import { } from "lucide-react"; import { useState } from "react"; -import CardComponent from "~/components/adminDashboard/CardComponent"; import CustomButton from "~/components/common/common-button/common-button"; +import CardComponent from "~/components/common/DashboardCard/CardComponent"; import { Button } from "~/components/ui/button"; import { DropdownMenu, diff --git a/src/app/dashboard/(user-dashboard)/_components/RecentSales.tsx b/src/app/dashboard/(user-dashboard)/_components/RecentSales.tsx new file mode 100644 index 000000000..59191a410 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/RecentSales.tsx @@ -0,0 +1,87 @@ +import React from "react"; + +import { Card } from "~/components/ui/card"; + +type ProductData = { + name: string; + amount: string; + email: string; +}; + +type TopProductsProperties = { + data: ProductData[]; + gradients: string[]; + noOfSales: number; +}; + +const RecentSales: React.FC = ({ + data, + gradients, + noOfSales, +}) => { + return ( + +
+
+

+ Recent Sales +

+

+ You made {noOfSales} sales this month +

+
+
+
    + {data.map((item, index) => { + let amount = Number(item.amount).toFixed(2); + amount = amount + .replaceAll( + new RegExp( + String.raw`^(\d{` + + (amount.length % 3 ?? 0) + + String.raw`})(\d{3})`, + "g", + ), + "$1 $2", + ) + .replaceAll(/(\d{3})+?/gi, "$1 ") + .trim(); + const separator = ","; + amount = amount.replaceAll(/\s/g, separator); + amount = amount.replace(",.", "."); + + return ( +
  • +
    +
    +
    +

    + {item.name} +

    +

    {item.email}

    +
    +
    +

    + +${amount} +

    +
  • + ); + })} +
+
+ ); +}; + +export default RecentSales; diff --git a/src/app/dashboard/(user-dashboard)/_components/salesData.ts b/src/app/dashboard/(user-dashboard)/_components/salesData.ts new file mode 100644 index 000000000..c8143118a --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/salesData.ts @@ -0,0 +1,43 @@ +const data = [ + { + name: "Jackson Lee", + amount: "2999", + email: "jackson.lee@gmail.com", + }, + { + name: "Olivia Martin", + amount: "7999", + email: "olivia.martin@gmail.com", + }, + { + name: "Joseph Chernysuck", + amount: "5999", + email: "olivia.martin@gmail.com", + }, + { + name: "Paul Halland", + amount: "12999", + email: "olivia.martin@gmail.com", + }, + { + name: "Eden Hazard", + amount: "3999", + email: "jackson.lee@gmail.com", + }, + { + name: "Ronaldo Messi", + amount: "4999", + email: "jackson.lee@gmail.com", + }, +]; + +const gradients = [ + "linear-gradient(180deg, #F6C790 0%, #E77F1E 100%)", + "linear-gradient(180deg, #F81404 0%, #0F172A 100%)", + "linear-gradient(180deg, rgba(4, 190, 248, 0.20) 0%, #0AB025 100%)", + "linear-gradient(180deg, #FFF 0%, #7F838D 20.86%, #0F172A 100%)", + "linear-gradient(180deg, #1E1D1C 0%, #3A1EE7 100%)", + "linear-gradient(180deg, #EF9B38 0%, #7EA7D9 100%)", +]; + +export { data, gradients }; diff --git a/src/components/adminDashboard/CardComponent.tsx b/src/components/common/DashboardCard/CardComponent.tsx similarity index 100% rename from src/components/adminDashboard/CardComponent.tsx rename to src/components/common/DashboardCard/CardComponent.tsx From 51e3f478eae85e243ff36b74a818a47139197b9b Mon Sep 17 00:00:00 2001 From: Eva Raymond Date: Mon, 29 Jul 2024 09:57:57 +0100 Subject: [PATCH 15/18] feat - integrate overview sales and metrics component to layout --- public/admin-dashboard/icons/arrowRise.svg | 6 ++++ public/admin-dashboard/icons/creditCard.svg | 6 ++++ public/admin-dashboard/icons/userGroup.svg | 8 +++++ .../(user-metrics)/layout.tsx | 2 +- .../(user-dashboard)/(user-metrics)/page.tsx | 10 ++++-- .../_components/overview-revenue/index.tsx | 18 ++++++++++ .../overview-revenue/revenueData.ts | 33 +++++++++++++++++++ .../_components/overview-sales/index.tsx | 11 +++++++ 8 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 public/admin-dashboard/icons/arrowRise.svg create mode 100644 public/admin-dashboard/icons/creditCard.svg create mode 100644 public/admin-dashboard/icons/userGroup.svg create mode 100644 src/app/dashboard/(user-dashboard)/_components/overview-revenue/index.tsx create mode 100644 src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts create mode 100644 src/app/dashboard/(user-dashboard)/_components/overview-sales/index.tsx diff --git a/public/admin-dashboard/icons/arrowRise.svg b/public/admin-dashboard/icons/arrowRise.svg new file mode 100644 index 000000000..f3bb3e30d --- /dev/null +++ b/public/admin-dashboard/icons/arrowRise.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/admin-dashboard/icons/creditCard.svg b/public/admin-dashboard/icons/creditCard.svg new file mode 100644 index 000000000..045aabc31 --- /dev/null +++ b/public/admin-dashboard/icons/creditCard.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/admin-dashboard/icons/userGroup.svg b/public/admin-dashboard/icons/userGroup.svg new file mode 100644 index 000000000..e4d54cf1c --- /dev/null +++ b/public/admin-dashboard/icons/userGroup.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx index 4815efe0c..015021e12 100644 --- a/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/layout.tsx @@ -34,7 +34,7 @@ export default function UserMetricsLayout({ children }: PropsWithChildren) {
-
{children}
+
{children}
); } diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx index 3d5e57bbe..6df461c23 100644 --- a/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/page.tsx @@ -1,12 +1,18 @@ import { OverviewChart } from "~/app/dashboard/(user-dashboard)/_components/overview-chart"; +import { OverviewRevenue } from "~/app/dashboard/(user-dashboard)/_components/overview-revenue"; +import { OverviewSales } from "~/app/dashboard/(user-dashboard)/_components/overview-sales"; export default function DashboardOverview() { return (
-
Revenue Cards
+
+ +
-
Recent Sales
+
+ +
); diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-revenue/index.tsx b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/index.tsx new file mode 100644 index 000000000..226902537 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/index.tsx @@ -0,0 +1,18 @@ +import { revenueData } from "~/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData"; +import CardComponent from "~/components/common/DashboardCard/CardComponent"; + +export function OverviewRevenue() { + return ( +
+ {revenueData.map((card, index) => ( + + ))} +
+ ); +} diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts new file mode 100644 index 000000000..01a2dab48 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts @@ -0,0 +1,33 @@ +type CardData = { + title: string; + value: string; + description: string; + icon: string; +}; + +export const revenueData: CardData[] = [ + { + title: "Total Revenue", + value: "$45,000.00", + description: "+20% from last month", + icon: `/admin-dashboard/icons/dollarSign.svg`, + }, + { + title: "Subscriptions", + value: "+2,350", + description: "+150% from last month", + icon: `/admin-dashboard/icons/userGroup.svg`, + }, + { + title: "Sales", + value: "15,000", + description: "+10% from last month", + icon: `/admin-dashboard/icons/creditCard.svg`, + }, + { + title: "Active Now", + value: "574", + description: "+201 since last hour", + icon: `/admin-dashboard/icons/arrowRise.svg`, + }, +]; diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-sales/index.tsx b/src/app/dashboard/(user-dashboard)/_components/overview-sales/index.tsx new file mode 100644 index 000000000..95952ac66 --- /dev/null +++ b/src/app/dashboard/(user-dashboard)/_components/overview-sales/index.tsx @@ -0,0 +1,11 @@ +import RecentSales from "~/app/dashboard/(user-dashboard)/_components/RecentSales"; +import { + data, + gradients, +} from "~/app/dashboard/(user-dashboard)/_components/salesData"; + +export function OverviewSales() { + return ( + + ); +} From a5819360d47189173bbcfff9c15dc12acb4ba51f Mon Sep 17 00:00:00 2001 From: Eva Raymond Date: Tue, 30 Jul 2024 05:43:08 +0100 Subject: [PATCH 16/18] fix: refactor to use lucide icons dynamically --- next.config.mjs | 1 + public/admin-dashboard/icons/arrowRise.svg | 6 --- public/admin-dashboard/icons/arrowUp.svg | 4 -- public/admin-dashboard/icons/box.svg | 5 -- public/admin-dashboard/icons/creditCard.svg | 6 --- public/admin-dashboard/icons/dollarSign.svg | 4 -- public/admin-dashboard/icons/user.svg | 4 -- public/admin-dashboard/icons/userGroup.svg | 8 ---- .../admin/users/data/user-dummy-data.ts | 10 ++-- .../(user-metrics)/analytics/page.tsx | 2 +- .../overview-revenue/revenueData.ts | 12 +++-- .../adminDashboard/TopProductsComponent.tsx | 22 ++------- src/components/adminDashboard/cardData.ts | 12 +++-- .../common/DashboardCard/CardComponent.tsx | 46 ++++++++----------- src/components/common/lucide-icon/index.tsx | 19 ++++++++ 15 files changed, 64 insertions(+), 97 deletions(-) delete mode 100644 public/admin-dashboard/icons/arrowRise.svg delete mode 100644 public/admin-dashboard/icons/arrowUp.svg delete mode 100644 public/admin-dashboard/icons/box.svg delete mode 100644 public/admin-dashboard/icons/creditCard.svg delete mode 100644 public/admin-dashboard/icons/dollarSign.svg delete mode 100644 public/admin-dashboard/icons/user.svg delete mode 100644 public/admin-dashboard/icons/userGroup.svg create mode 100644 src/components/common/lucide-icon/index.tsx diff --git a/next.config.mjs b/next.config.mjs index 0503668d8..d948ed61e 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -11,6 +11,7 @@ const nextConfig = { }, ], }, + transpilePackages: ["lucide-react"], }; export default nextConfig; \ No newline at end of file diff --git a/public/admin-dashboard/icons/arrowRise.svg b/public/admin-dashboard/icons/arrowRise.svg deleted file mode 100644 index f3bb3e30d..000000000 --- a/public/admin-dashboard/icons/arrowRise.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/public/admin-dashboard/icons/arrowUp.svg b/public/admin-dashboard/icons/arrowUp.svg deleted file mode 100644 index 8d1856ede..000000000 --- a/public/admin-dashboard/icons/arrowUp.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/admin-dashboard/icons/box.svg b/public/admin-dashboard/icons/box.svg deleted file mode 100644 index a07b9e2de..000000000 --- a/public/admin-dashboard/icons/box.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/public/admin-dashboard/icons/creditCard.svg b/public/admin-dashboard/icons/creditCard.svg deleted file mode 100644 index 045aabc31..000000000 --- a/public/admin-dashboard/icons/creditCard.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/public/admin-dashboard/icons/dollarSign.svg b/public/admin-dashboard/icons/dollarSign.svg deleted file mode 100644 index a6c75e629..000000000 --- a/public/admin-dashboard/icons/dollarSign.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/admin-dashboard/icons/user.svg b/public/admin-dashboard/icons/user.svg deleted file mode 100644 index 5a3075414..000000000 --- a/public/admin-dashboard/icons/user.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/public/admin-dashboard/icons/userGroup.svg b/public/admin-dashboard/icons/userGroup.svg deleted file mode 100644 index e4d54cf1c..000000000 --- a/public/admin-dashboard/icons/userGroup.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts b/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts index 07d39ba03..9a0c933ec 100644 --- a/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts +++ b/src/app/dashboard/(admin)/admin/users/data/user-dummy-data.ts @@ -1,8 +1,10 @@ +import type { LucideIconName } from "~/components/common/lucide-icon"; + interface UserCardData { title: string; value: number; description: string; - icon: string; + icon: LucideIconName; } interface UserStatusProperties { @@ -23,19 +25,19 @@ export const userCardData: UserCardData[] = [ title: "Total Users", value: 4000, description: "+10% from last month", - icon: `/admin-dashboard/icons/user.svg`, + icon: "user", }, { title: "Active Users", value: 1500, description: "+20% from last month", - icon: `/admin-dashboard/icons/box.svg`, + icon: "box", }, { title: "Deleted Users", value: 2500, description: "+150% from last month", - icon: `/admin-dashboard/icons/arrowUp.svg`, + icon: "arrow-up", }, ]; diff --git a/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx b/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx index cf132e929..b1477f101 100644 --- a/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx +++ b/src/app/dashboard/(user-dashboard)/(user-metrics)/analytics/page.tsx @@ -1,3 +1,3 @@ export default function DashboardAnalytics() { - return
REPORTS
; + return
ANALYTICS
; } diff --git a/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts index 01a2dab48..584661a32 100644 --- a/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts +++ b/src/app/dashboard/(user-dashboard)/_components/overview-revenue/revenueData.ts @@ -1,8 +1,10 @@ +import type { LucideIconName } from "~/components/common/lucide-icon"; + type CardData = { title: string; value: string; description: string; - icon: string; + icon: LucideIconName; }; export const revenueData: CardData[] = [ @@ -10,24 +12,24 @@ export const revenueData: CardData[] = [ title: "Total Revenue", value: "$45,000.00", description: "+20% from last month", - icon: `/admin-dashboard/icons/dollarSign.svg`, + icon: "dollar-sign", }, { title: "Subscriptions", value: "+2,350", description: "+150% from last month", - icon: `/admin-dashboard/icons/userGroup.svg`, + icon: "users", }, { title: "Sales", value: "15,000", description: "+10% from last month", - icon: `/admin-dashboard/icons/creditCard.svg`, + icon: "credit-card", }, { title: "Active Now", value: "574", description: "+201 since last hour", - icon: `/admin-dashboard/icons/arrowRise.svg`, + icon: "trending-up", }, ]; diff --git a/src/components/adminDashboard/TopProductsComponent.tsx b/src/components/adminDashboard/TopProductsComponent.tsx index c2444f180..418e533d0 100644 --- a/src/components/adminDashboard/TopProductsComponent.tsx +++ b/src/components/adminDashboard/TopProductsComponent.tsx @@ -1,5 +1,5 @@ -import Image from "next/image"; -import React from "react"; +import { ArrowUpRightIcon } from "lucide-react"; +import { FC } from "react"; import { Card } from "../ui/card"; @@ -13,21 +13,7 @@ type TopProductsProperties = { gradients: string[]; }; -const ExternalLinkIcon = () => { - return ( - <> - external link icon - - ); -}; - -const TopProductsComponent: React.FC = ({ +const TopProductsComponent: FC = ({ data, gradients, }) => { @@ -45,7 +31,7 @@ const TopProductsComponent: React.FC = ({
    diff --git a/src/components/adminDashboard/cardData.ts b/src/components/adminDashboard/cardData.ts index 96692068e..0ecfe1135 100644 --- a/src/components/adminDashboard/cardData.ts +++ b/src/components/adminDashboard/cardData.ts @@ -1,8 +1,10 @@ +import type { LucideIconName } from "~/components/common/lucide-icon"; + type CardData = { title: string; value: string; description: string; - icon: string; + icon: LucideIconName; }; export const cardData: CardData[] = [ @@ -10,24 +12,24 @@ export const cardData: CardData[] = [ title: "Total Revenue", value: "$45,000.00", description: "+20% from last month", - icon: `/admin-dashboard/icons/dollarSign.svg`, + icon: "dollar-sign", }, { title: "Total Users", value: "+4,000", description: "+10% from last month", - icon: `/admin-dashboard/icons/user.svg`, + icon: "user", }, { title: "Total Products", value: "1,000", description: "+20% from last month", - icon: `/admin-dashboard/icons/box.svg`, + icon: "box", }, { title: "Lifetime Sales", value: "$450,000.00", description: "+150% from last month", - icon: `/admin-dashboard/icons/arrowUp.svg`, + icon: "arrow-up-right", }, ]; diff --git a/src/components/common/DashboardCard/CardComponent.tsx b/src/components/common/DashboardCard/CardComponent.tsx index df4594d85..4e108b042 100644 --- a/src/components/common/DashboardCard/CardComponent.tsx +++ b/src/components/common/DashboardCard/CardComponent.tsx @@ -1,11 +1,12 @@ -import Image from "next/image"; -import { FC } from "react"; +import { ComponentProps, FC } from "react"; + +import Icon from "~/components/common/lucide-icon"; interface CardProperties { title: string; value: string | number; description: string; - icon: string; + icon: ComponentProps["name"]; } const CardComponent: FC = ({ @@ -15,32 +16,23 @@ const CardComponent: FC = ({ icon, }) => { return ( - <> -
    -
    -

    - {title} -

    - - {title} -
    +
    +
    +

    + {title} +

    + +
    -
    -

    - {value} -

    - - {description} - -
    +
    +

    + {value} +

    + + {description} +
    - +
    ); }; diff --git a/src/components/common/lucide-icon/index.tsx b/src/components/common/lucide-icon/index.tsx new file mode 100644 index 000000000..f07fbf21c --- /dev/null +++ b/src/components/common/lucide-icon/index.tsx @@ -0,0 +1,19 @@ +import { LucideProps } from "lucide-react"; +import dynamicIconImports from "lucide-react/dynamicIconImports"; +import dynamic from "next/dynamic"; + +interface ILucideIcon { + name: keyof typeof dynamicIconImports; +} + +export type LucideIconName = ILucideIcon["name"]; + +interface IconProperties extends Omit, ILucideIcon {} + +const Icon = ({ name, ...properties }: IconProperties) => { + const LucideIcon = dynamic(dynamicIconImports[name]); + + return ; +}; + +export default Icon; From 7b0bac7883aab2322acfb26d2c451989e9ac58c2 Mon Sep 17 00:00:00 2001 From: Eva Raymond Date: Tue, 30 Jul 2024 18:11:58 +0100 Subject: [PATCH 17/18] fix workflow linting error --- pnpm-lock.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b195f48f8..d68e9c57b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,8 +87,8 @@ importers: specifier: ^3.6.0 version: 3.6.0 framer-motion: - specifier: ^11.3.8 - version: 11.3.17(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + specifier: ^11.3.19 + version: 11.3.19(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) input-otp: specifier: ^1.2.4 version: 1.2.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -3235,8 +3235,8 @@ packages: react-dom: optional: true - framer-motion@11.3.17: - resolution: {integrity: sha512-LZcckvZL8Rjod03bud8LQcp+R0PLmWIlOSu+NVc+v6Uh43fQr4IBsEAX7sSn7CdBQ1L0fZ/IqSXZVPnGFSMxHw==} + framer-motion@11.3.19: + resolution: {integrity: sha512-+luuQdx4AsamyMcvzW7jUAJYIKvQs1KE7oHvKkW3eNzmo0S+3PSDWjBuQkuIP9WyneGnKGMLUSuHs8OP7jKpQg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 @@ -8698,7 +8698,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - framer-motion@11.3.17(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + framer-motion@11.3.19(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: tslib: 2.6.3 optionalDependencies: From f74f6f6e3fde56d7bcca7b3150a347fca91d304a Mon Sep 17 00:00:00 2001 From: Eva Raymond Date: Wed, 31 Jul 2024 02:35:38 +0100 Subject: [PATCH 18/18] fix: update lock-file --- pnpm-lock.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d68e9c57b..78477e3b2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -87,7 +87,7 @@ importers: specifier: ^3.6.0 version: 3.6.0 framer-motion: - specifier: ^11.3.19 + specifier: ^11.3.8 version: 11.3.19(@emotion/is-prop-valid@0.8.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) input-otp: specifier: ^1.2.4