From c9bb67d1dd0257840e876a86858d2626f3282159 Mon Sep 17 00:00:00 2001 From: Daniel Reinhard <119338658+danielreinhard1129@users.noreply.github.com> Date: Mon, 14 Oct 2024 19:54:18 +0700 Subject: [PATCH] Merge develop to main (#40) * feat: final (#34) * fix: fixx * feat: final * fix: update profile bug (#36) * fix: verify token for verify email and forgot password (#38) * fix: fin (#39) * fix: conflict (#41) * Update main.yml * Update package.json * Update axios.ts * feat: final (#34) (#35) * fix: fixx * feat: final Co-authored-by: SansanSaga <154500077+SansanSaga@users.noreply.github.com> * Merge develop to main (#37) * feat: final (#34) * fix: fixx * feat: final * fix: update profile bug (#36) --------- Co-authored-by: SansanSaga <154500077+SansanSaga@users.noreply.github.com> Co-authored-by: Laila Yunita <161436964+lailayunita@users.noreply.github.com> --------- Co-authored-by: Daniel Reinhard <119338658+danielreinhard1129@users.noreply.github.com> Co-authored-by: Laila Yunita <161436964+lailayunita@users.noreply.github.com> * fix: fixing (#42) --------- Co-authored-by: SansanSaga <154500077+SansanSaga@users.noreply.github.com> Co-authored-by: Laila Yunita <161436964+lailayunita@users.noreply.github.com> Co-authored-by: Samboga --- apps/api/src/config.ts | 4 +- ...tEmailFromToken.ts => VerifyTokenEmail.ts} | 4 +- apps/api/src/lib/verifyTokenForgotPassword.ts | 37 +++ apps/api/src/routers/auth.router.ts | 6 +- .../services/auth/forgot-password.service.ts | 7 +- .../api/src/services/auth/register.service.ts | 5 +- apps/web/src/components/Header.tsx | 1 + apps/web/src/components/TableSkeletonPure.tsx | 41 +++ .../delivery-orders/admins/index.tsx | 11 +- .../delivery-orders/drivers/index.tsx | 17 +- .../src/features/dashboard/earnings/index.tsx | 33 ++- .../components/NotificationCard.tsx | 34 +++ .../dashboard/notifications/index.tsx | 45 ++- .../src/features/dashboard/orders/index.tsx | 9 +- .../dashboard/pickup-orders/admins/index.tsx | 9 +- .../dashboard/pickup-orders/drivers/index.tsx | 9 +- .../dashboard/users/customers/index.tsx | 168 ++++++------ .../dashboard/users/employees/index.tsx | 33 ++- .../dashboard/work-orders/admins/index.tsx | 256 +++++++++--------- .../dashboard/work-orders/workers/index.tsx | 230 ++++++++-------- apps/web/src/features/request/index.tsx | 6 - 21 files changed, 600 insertions(+), 365 deletions(-) rename apps/api/src/lib/{getEmailFromToken.ts => VerifyTokenEmail.ts} (86%) create mode 100644 apps/api/src/lib/verifyTokenForgotPassword.ts create mode 100644 apps/web/src/components/TableSkeletonPure.tsx create mode 100644 apps/web/src/features/dashboard/notifications/components/NotificationCard.tsx diff --git a/apps/api/src/config.ts b/apps/api/src/config.ts index 636cf7f..857c181 100644 --- a/apps/api/src/config.ts +++ b/apps/api/src/config.ts @@ -12,6 +12,8 @@ config({ path: resolve(__dirname, `../${envFile}`) }); export const PORT = process.env.PORT || 8000; export const DATABASE_URL = process.env.DATABASE_URL || ''; export const JWT_SECRET = process.env.JWT_SECRET; +export const JWT_SECRET_PASSWORD = process.env.JWT_SECRET_PASSWORD; +export const JWT_SECRET_EMAIL = process.env.JWT_SECRET_EMAIL; export const BASE_URL_FE = process.env.BASE_URL_FE; export const GMAIL_EMAIL = process.env.GMAIL_EMAIL; export const GMAIL_APP_PASSWORD = process.env.GMAIL_APP_PASSWORD; @@ -20,4 +22,4 @@ export const CLOUDINARY_API_SECRET = process.env.CLOUDINARY_API_SECRET; export const CLOUDINARY_CLOUD_NAME = process.env.CLOUDINARY_CLOUD_NAME; export const MIDTRANS_MERCHANT_ID = process.env.MIDTRANS_MERCHANT_ID; export const MIDTRANS_CLIENT_KEY = process.env.MIDTRANS_CLIENT_KEY; -export const MIDTRANS_SERVER_KEY = process.env.MIDTRANS_SERVER_KEY; \ No newline at end of file +export const MIDTRANS_SERVER_KEY = process.env.MIDTRANS_SERVER_KEY; diff --git a/apps/api/src/lib/getEmailFromToken.ts b/apps/api/src/lib/VerifyTokenEmail.ts similarity index 86% rename from apps/api/src/lib/getEmailFromToken.ts rename to apps/api/src/lib/VerifyTokenEmail.ts index b3af352..c05895e 100644 --- a/apps/api/src/lib/getEmailFromToken.ts +++ b/apps/api/src/lib/VerifyTokenEmail.ts @@ -1,4 +1,4 @@ -import { JWT_SECRET } from '@/config'; +import { JWT_SECRET, JWT_SECRET_EMAIL } from '@/config'; import { NextFunction, Request, Response } from 'express'; import { TokenExpiredError, verify } from 'jsonwebtoken'; @@ -19,7 +19,7 @@ export const getEmailFromToken = ( }); } - verify(token, JWT_SECRET!, (err, payload) => { + verify(token, JWT_SECRET_EMAIL!, (err, payload) => { if (err) { if (err instanceof TokenExpiredError) { return res.status(403).send({ message: 'token expired' }); diff --git a/apps/api/src/lib/verifyTokenForgotPassword.ts b/apps/api/src/lib/verifyTokenForgotPassword.ts new file mode 100644 index 0000000..a144199 --- /dev/null +++ b/apps/api/src/lib/verifyTokenForgotPassword.ts @@ -0,0 +1,37 @@ +import { JWT_SECRET_EMAIL, JWT_SECRET_PASSWORD } from '@/config'; +import { Role } from '@prisma/client'; +import { NextFunction, Request, Response } from 'express'; +import { TokenExpiredError, verify } from 'jsonwebtoken'; + +interface PayloadToken { + id: number; + role: Role; +} + +export const verifyTokenForgotPassword = ( + req: Request, + res: Response, + next: NextFunction, +) => { + const token = req.headers.authorization?.split(' ')[1]; + + if (!token) { + return res.status(401).send({ + message: 'token is missing', + }); + } + + verify(token, JWT_SECRET_PASSWORD!, (err, payload) => { + if (err) { + if (err instanceof TokenExpiredError) { + return res.status(403).send({ message: 'token expired' }); + } else { + return res.status(401).send({ message: 'unauthorized' }); + } + } + + res.locals.user = payload as PayloadToken; + + next(); + }); +}; diff --git a/apps/api/src/routers/auth.router.ts b/apps/api/src/routers/auth.router.ts index a3a01bf..f48089d 100644 --- a/apps/api/src/routers/auth.router.ts +++ b/apps/api/src/routers/auth.router.ts @@ -1,6 +1,6 @@ import { AuthController } from '@/controllers/auth.controller'; -import { getEmailFromToken } from '@/lib/getEmailFromToken'; -import { verifyToken } from '@/lib/verifyToken'; +import { getEmailFromToken } from '@/lib/VerifyTokenEmail'; +import { verifyTokenForgotPassword } from '@/lib/verifyTokenForgotPassword'; import { validateCompleteRegistration, validateEmail, @@ -44,7 +44,7 @@ export class AuthRouter { ); this.router.patch( '/reset-password', - verifyToken, + verifyTokenForgotPassword, validateResetPassword, this.authController.resetPassword, ); diff --git a/apps/api/src/services/auth/forgot-password.service.ts b/apps/api/src/services/auth/forgot-password.service.ts index 8839588..bdacd17 100644 --- a/apps/api/src/services/auth/forgot-password.service.ts +++ b/apps/api/src/services/auth/forgot-password.service.ts @@ -1,4 +1,4 @@ -import { BASE_URL_FE, JWT_SECRET } from '@/config'; +import { BASE_URL_FE, JWT_SECRET_PASSWORD } from '@/config'; import { transporter } from '@/lib/nodemailer'; import prisma from '@/prisma'; import { sign } from 'jsonwebtoken'; @@ -16,7 +16,7 @@ export const forgotPasswordService = async (email: string) => { throw new Error('Invalid email address'); } - const token = sign({ id: user.id }, JWT_SECRET!, { + const token = sign({ id: user.id }, JWT_SECRET_PASSWORD!, { expiresIn: '30m', }); @@ -50,7 +50,8 @@ export const forgotPasswordService = async (email: string) => { }); return { - message: 'Send email success', + message: 'Update profile success', + token, }; } catch (error) { throw error; diff --git a/apps/api/src/services/auth/register.service.ts b/apps/api/src/services/auth/register.service.ts index b381fa0..8219e83 100644 --- a/apps/api/src/services/auth/register.service.ts +++ b/apps/api/src/services/auth/register.service.ts @@ -1,4 +1,4 @@ -import { BASE_URL_FE, JWT_SECRET } from '@/config'; +import { BASE_URL_FE, JWT_SECRET, JWT_SECRET_EMAIL } from '@/config'; import { transporter } from '@/lib/nodemailer'; import prisma from '@/prisma'; import { User } from '@prisma/client'; @@ -28,7 +28,7 @@ export const registerService = async (body: User) => { }, }); - const token = sign({ email }, JWT_SECRET!, { + const token = sign({ email }, JWT_SECRET_EMAIL!, { expiresIn: '60m', }); @@ -73,6 +73,7 @@ export const registerService = async (body: User) => { return { newUser, message: 'Register Success', + token, }; }); } catch (error) { diff --git a/apps/web/src/components/Header.tsx b/apps/web/src/components/Header.tsx index eda59ec..d4f7805 100644 --- a/apps/web/src/components/Header.tsx +++ b/apps/web/src/components/Header.tsx @@ -250,6 +250,7 @@ export const Header = () => { My Orders Pickup Orders Delivery Orders + Notifications
diff --git a/apps/web/src/components/TableSkeletonPure.tsx b/apps/web/src/components/TableSkeletonPure.tsx new file mode 100644 index 0000000..665d884 --- /dev/null +++ b/apps/web/src/components/TableSkeletonPure.tsx @@ -0,0 +1,41 @@ +import { Skeleton } from "@/components/ui/skeleton"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; +import { FaPlus } from "react-icons/fa"; + +const TableSkeletonPure = () => { + return ( +
+
+

+ +

+
+ +
+ +
+ {[...Array(3)].map((_, index) => ( +
+ + + + + +
+ + +
+
+ ))} +
+
+ +
+ +
+
+ ); +}; + +export default TableSkeletonPure; diff --git a/apps/web/src/features/dashboard/delivery-orders/admins/index.tsx b/apps/web/src/features/dashboard/delivery-orders/admins/index.tsx index abf0336..bf417e5 100644 --- a/apps/web/src/features/dashboard/delivery-orders/admins/index.tsx +++ b/apps/web/src/features/dashboard/delivery-orders/admins/index.tsx @@ -92,7 +92,7 @@ const DashboardDeliveryOrdersAdminsPage = () => { page: String(searchParams.page), }).toString(); - router.push(`/dashboard/orders?${query}`); + router.push(`/dashboard/delivery-orders?${query}`); refetch(); }, [searchParams]); @@ -107,10 +107,15 @@ const DashboardDeliveryOrdersAdminsPage = () => { return ( <> -
+
+
+

Delivery Orders

+
+
+
- Delivery orders + List of delivery orders List of delivery orders diff --git a/apps/web/src/features/dashboard/delivery-orders/drivers/index.tsx b/apps/web/src/features/dashboard/delivery-orders/drivers/index.tsx index 2c93717..264de59 100644 --- a/apps/web/src/features/dashboard/delivery-orders/drivers/index.tsx +++ b/apps/web/src/features/dashboard/delivery-orders/drivers/index.tsx @@ -29,7 +29,7 @@ import { useRouter, useSearchParams } from "next/navigation"; import DeliveryOrderCard from "../components/DeliveryOrderCard"; const DashboardDeliveryOrdersDriversPage = () => { - const router = useRouter() + const router = useRouter(); const [searchValue, setSearchValue] = useState(""); const [debouncedSearch] = useDebounceValue(searchValue, 500); const isDesktop = useMediaQuery("(min-width: 768px)", { @@ -43,7 +43,9 @@ const DashboardDeliveryOrdersDriversPage = () => { sortBy: queryParams.get("sortBy") || "createdAt", sortOrder: (queryParams.get("sortOrder") as "asc" | "desc") || "desc", search: queryParams.get("search") || "", - status: (queryParams.get("status") as "ONGOING" | "REQUEST" | "HISTORY") || "REQUEST", + status: + (queryParams.get("status") as "ONGOING" | "REQUEST" | "HISTORY") || + "REQUEST", }); const { data, isPending, refetch } = useGetDeliveryOrdersDrivers({ @@ -92,10 +94,17 @@ const DashboardDeliveryOrdersDriversPage = () => { return ( <> -
+
+
+

+ Delivery Orders +

+
+
+
- Delivery Orders + List of delivery Orders List of delivery orders diff --git a/apps/web/src/features/dashboard/earnings/index.tsx b/apps/web/src/features/dashboard/earnings/index.tsx index 845eb49..978057f 100644 --- a/apps/web/src/features/dashboard/earnings/index.tsx +++ b/apps/web/src/features/dashboard/earnings/index.tsx @@ -32,6 +32,7 @@ import { getDaysInMonth } from "date-fns"; import { useSession } from "next-auth/react"; import { useState } from "react"; import ChartEvents from "./components/ChartEvents"; +import { Loader2 } from "lucide-react"; ChartJS.register( LineElement, @@ -85,6 +86,21 @@ const DashboardEarningsPage = () => { } }; + const months = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ]; + if (!session.data) { return ; } @@ -97,6 +113,7 @@ const DashboardEarningsPage = () => { return ( <> + ); } @@ -104,13 +121,19 @@ const DashboardEarningsPage = () => { return ( <> -
+
+
+

Earnings

+
+
+
- Earnings + + {months[Number(filterMonth) - 1]} + -
Earning data
-
All time total: {result.format(data.totalIncome)}
+
Total: {result.format(data.totalIncome)}
@@ -120,7 +143,7 @@ const DashboardEarningsPage = () => { +
+
+

Customers

+
+
+
+ + + Users + List of users + + +
+
+ +
+ + +
- - - -
- {isPending ? ( - - ) : data?.data ? ( - <> + {isPending ? ( + + ) : data?.data ? ( + <> + +
+ +
+ + ) : ( -
- -
- - ) : ( - - )} -
- - + )} + + +
) : ( <> diff --git a/apps/web/src/features/dashboard/users/employees/index.tsx b/apps/web/src/features/dashboard/users/employees/index.tsx index 00f7d78..b98abd6 100644 --- a/apps/web/src/features/dashboard/users/employees/index.tsx +++ b/apps/web/src/features/dashboard/users/employees/index.tsx @@ -124,14 +124,25 @@ const DashboardUsersEmployeesPage = () => { session.data?.user.role === "OUTLET_ADMIN" ? ( <> - - - Users - List of users - - - -
+
+
+

Employees

+ +
+
+
+ + + Users + List of users + +
{ meta={{ page: 1, take: 8, total: 0 }} /> )} -
- - + + +
) : ( <> diff --git a/apps/web/src/features/dashboard/work-orders/admins/index.tsx b/apps/web/src/features/dashboard/work-orders/admins/index.tsx index 1888591..f115f9d 100644 --- a/apps/web/src/features/dashboard/work-orders/admins/index.tsx +++ b/apps/web/src/features/dashboard/work-orders/admins/index.tsx @@ -112,137 +112,149 @@ const DashboardWorkOrdersAdminsPage = () => { return ( <> -
- - - Work Orders - List of work orders - - -
- -
-
- - - - {session.data?.user.role === "ADMIN" ? ( - +
+
+ - ) : null} -
- {isPending ? ( - - ) : data?.data ? ( - isDesktop ? ( - <> - -
- + + + + + + Sort order + Ascending + Descending + + + + + {session.data?.user.role === "ADMIN" ? ( + + ) : null} +
+ {isPending ? ( + + ) : data?.data ? ( + isDesktop ? ( + <> + -
- +
+ +
+ + ) : ( + <> +
+ {data.data.map((order) => { + return ( + + ); + })} +
+
+ +
+ + ) ) : ( - <> -
- {data.data.map((order) => { - return ( - - ); - })} -
-
- -
- - ) - ) : ( - - )} -
-
+ + )} + + +
); diff --git a/apps/web/src/features/dashboard/work-orders/workers/index.tsx b/apps/web/src/features/dashboard/work-orders/workers/index.tsx index e8d4d67..ea8a7ed 100644 --- a/apps/web/src/features/dashboard/work-orders/workers/index.tsx +++ b/apps/web/src/features/dashboard/work-orders/workers/index.tsx @@ -42,7 +42,9 @@ const DashboardWorkOrdersWorkerPage = () => { sortBy: queryParams.get("sortBy") || "createdAt", sortOrder: (queryParams.get("sortOrder") as "asc" | "desc") || "desc", search: queryParams.get("search") || "", - status: (queryParams.get("status") as 'ONGOING' | 'HISTORY' | "REQUEST") || "REQUEST", + status: + (queryParams.get("status") as "ONGOING" | "HISTORY" | "REQUEST") || + "REQUEST", }); const { data, isPending, refetch } = useGetWorkOrdersWorker({ @@ -91,115 +93,129 @@ const DashboardWorkOrdersWorkerPage = () => { return ( <> -
- - - Work Orders - List of work orders - - -
- -
-
- - - -
- {isPending ? ( - - ) : data?.data ? isDesktop ? ( - <> +
+
+
+

+ Work Orders +

+
+
+
+ + + Work Orders + List of work orders + + +
+ +
+
+ + + +
+ {isPending ? ( + + ) : data?.data ? ( + isDesktop ? ( + <> + +
+ +
+ + ) : ( + <> +
+ {data.data.map((order) => { + return ( + + ); + })} +
+
+ +
+ + ) + ) : ( -
- -
- - ) : ( - <> -
- {data.data.map((order) => { - return ( - - ); - })} -
-
- -
- - ) : ( - - )} -
-
+ )} + + +
); diff --git a/apps/web/src/features/request/index.tsx b/apps/web/src/features/request/index.tsx index a3140b2..f35fb3f 100644 --- a/apps/web/src/features/request/index.tsx +++ b/apps/web/src/features/request/index.tsx @@ -292,12 +292,6 @@ const RequestOrderPage = () => { Delivery: {deliveryDistance} km - {/* - Total:{" "} - {Number(pickupDistance) + - Number(deliveryDistance)}{" "} - km - */} {distanceLimit ? ( Maximum distance is 10km