From ec545c3d7b8d207b5fcc24188ff28977c1dddb0b Mon Sep 17 00:00:00 2001 From: percyfikri Date: Thu, 3 Oct 2024 13:43:20 +0700 Subject: [PATCH] Update : Integrasi on Create Assessment Request --- .../backend/src/drizzle/schema/assessments.ts | 2 +- .../src/routes/assessmentRequest/route.ts | 7 +- .../modals/FormModal.tsx | 191 ++++++++++++++++++ .../queries/assessmentRequestQueries.ts | 56 +---- apps/frontend/src/routeTree.gen.ts | 66 ++++++ .../assessmentRequest/index.lazy.tsx | 7 +- apps/frontend/vite.config.ts | 2 +- 7 files changed, 277 insertions(+), 54 deletions(-) create mode 100644 apps/frontend/src/modules/assessmentRequestManagement/modals/FormModal.tsx diff --git a/apps/backend/src/drizzle/schema/assessments.ts b/apps/backend/src/drizzle/schema/assessments.ts index 75b9ccd..fd228cc 100644 --- a/apps/backend/src/drizzle/schema/assessments.ts +++ b/apps/backend/src/drizzle/schema/assessments.ts @@ -4,7 +4,7 @@ import { relations } from "drizzle-orm"; import { respondents } from "./respondents"; import { users } from "./users"; -const statusEnum = pgEnum("status", ["menunggu konfirmasi", "disetujui", "ditolak", "selesai"]); +const statusEnum = pgEnum("status", ["menunggu konfirmasi", "diterima", "ditolak", "selesai"]); export const assessments = pgTable("assessments", { id: varchar("id", { length: 50 }) diff --git a/apps/backend/src/routes/assessmentRequest/route.ts b/apps/backend/src/routes/assessmentRequest/route.ts index 07e823e..efc8cdf 100644 --- a/apps/backend/src/routes/assessmentRequest/route.ts +++ b/apps/backend/src/routes/assessmentRequest/route.ts @@ -1,4 +1,4 @@ -import { eq, sql, ilike, isNull, and } from "drizzle-orm"; +import { eq, sql, ilike, isNull, and, desc } from "drizzle-orm"; import { Hono } from "hono"; import { z } from "zod"; import db from "../../drizzle"; @@ -69,6 +69,9 @@ const assessmentRequestRoute = new Hono() id: assessments.id, tanggal: assessments.createdAt, status: assessments.status, + respondentId: respondents.id, + email: users.email, + username: users.username, }) .from(users) .leftJoin(rolesToUsers, eq(users.id, rolesToUsers.userId)) @@ -81,8 +84,10 @@ const assessmentRequestRoute = new Hono() q ? ilike(assessments.status, `%${q}%`) : undefined ) ) + .orderBy(desc(assessments.createdAt)) .offset(page * limit) .limit(limit); + if (!queryResult[0]) throw notFound(); diff --git a/apps/frontend/src/modules/assessmentRequestManagement/modals/FormModal.tsx b/apps/frontend/src/modules/assessmentRequestManagement/modals/FormModal.tsx new file mode 100644 index 0000000..9a896d0 --- /dev/null +++ b/apps/frontend/src/modules/assessmentRequestManagement/modals/FormModal.tsx @@ -0,0 +1,191 @@ +import stringToColorHex from "@/utils/stringToColorHex"; +import { + Avatar, + Button, + Center, + Flex, + Modal, + ScrollArea, + Stack, + Text +} from "@mantine/core"; +// import { Button } from "@/shadcn/components/ui/button"; +import { useForm } from "@mantine/form"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { getRouteApi } from "@tanstack/react-router"; +import { useEffect } from "react"; +import { notifications } from "@mantine/notifications"; +import FormResponseError from "@/errors/FormResponseError"; +import createInputComponents from "@/utils/createInputComponents"; +import { assessmentRequestQueryOptions, createAssessmentRequest } from "../queries/assessmentRequestQueries"; + +/** + * Change this + */ +const routeApi = getRouteApi("/_dashboardLayout/assessmentRequest/"); + +export default function UserFormModal() { + /** + * DON'T CHANGE FOLLOWING: + */ + const queryClient = useQueryClient(); + + const navigate = routeApi.useNavigate(); + + const searchParams = routeApi.useSearch(); + + const dataId = searchParams.detail || searchParams.edit; + + const isModalOpen = Boolean(dataId || searchParams.create); + + const detailId = searchParams.detail; + const editId = searchParams.edit; + + const formType = detailId ? "detail" : editId ? "edit" : "create"; + + /** + * CHANGE FOLLOWING: + */ + + const userQuery = useQuery(assessmentRequestQueryOptions(0, 10)); + + const modalTitle = + + Konfirmasi + + + const form = useForm({ + initialValues: { + id: "", + respondentsId: "", + name: "", + }, + }); + + useEffect(() => { + const data = userQuery.data; + + if (!data) { + form.reset(); + return; + } + + form.setValues({ + id: data.data[0].id ?? "", + respondentsId: data.data[0].respondentId ?? "", + name: data.data[0].name ?? "", + }); + + form.setErrors({}); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [userQuery.data]); + + const mutation = useMutation({ + mutationKey: ["usersMutation"], + mutationFn: async (options: { action: "create"; data: { respondentsId: string } }) => { + console.log("called"); + if (options.action === "create") { + return await createAssessmentRequest(options.data); + } + }, + onError: (error: unknown) => { + console.log(error); + + if (error instanceof FormResponseError) { + form.setErrors(error.formErrors); + return; + } + + if (error instanceof Error) { + notifications.show({ + message: error.message, + color: "red", + }); + } + }, + }); + + const handleSubmit = async (values: typeof form.values) => { + if (formType === "detail") return; + + if (formType === "create") { + try { + await mutation.mutateAsync({ + action: "create", + data: { + respondentsId: values.respondentsId, + }, + }); + notifications.show({ + message: "Permohonan Asesmen berhasil dibuat!", + color: "green", + }); + } catch (error) { + console.error(error); + } + } + + queryClient.invalidateQueries({ queryKey: ["users"] }); + navigate({ search: {} }); + }; + + + return ( + navigate({ search: {} })} + title= {modalTitle} + size="md" + > +
handleSubmit(values))}> + + Apakah anda yakin ingin membuat Permohonan Asesmen Baru? + + {createInputComponents({ + disableAll: mutation.isPending, + readonlyAll: formType === "detail", + inputs: [ + { + type: "text", + label: "User ID", + readOnly: true, + variant: "filled", + ...form.getInputProps("id"), + hidden: true + }, + { + type: "text", + label: "Respondent ID", + ...form.getInputProps("respondentsId"), + hidden: true, + }, + { + type: "text", + label: "Name", + ...form.getInputProps("name"), + hidden: true, + }, + ], + })} + + {/* Buttons */} + + + + +
+
+ ); +} diff --git a/apps/frontend/src/modules/assessmentRequestManagement/queries/assessmentRequestQueries.ts b/apps/frontend/src/modules/assessmentRequestManagement/queries/assessmentRequestQueries.ts index 17344f6..95f6923 100644 --- a/apps/frontend/src/modules/assessmentRequestManagement/queries/assessmentRequestQueries.ts +++ b/apps/frontend/src/modules/assessmentRequestManagement/queries/assessmentRequestQueries.ts @@ -1,7 +1,6 @@ import client from "@/honoClient"; import fetchRPC from "@/utils/fetchRPC"; import { queryOptions } from "@tanstack/react-query"; -import { InferRequestType } from "hono"; export const assessmentRequestQueryOptions = (page: number, limit: number, q?: string) => queryOptions({ @@ -18,51 +17,14 @@ export const assessmentRequestQueryOptions = (page: number, limit: number, q?: s ), }); -export const getUserByIdQueryOptions = (userId: string | undefined) => - queryOptions({ - queryKey: ["user", userId], - queryFn: () => - fetchRPC( - client.users[":id"].$get({ - param: { - id: userId!, - }, - query: {}, - }) - ), - enabled: Boolean(userId), +export const createAssessmentRequest = async ({ respondentsId }: { respondentsId: string }) => { + const response = await client.assessmentRequest.$post({ + json: { respondentId: respondentsId }, // pastikan key-nya sesuai dengan backend API Anda }); - -export const createUser = async ( - form: InferRequestType["form"] -) => { - return await fetchRPC( - client.users.$post({ - form, - }) - ); -}; - -export const updateUser = async ( - form: InferRequestType<(typeof client.users)[":id"]["$patch"]>["form"] & { - id: string; + + if (!response.ok) { + throw new Error("Failed to create assessment request"); } -) => { - return await fetchRPC( - client.users[":id"].$patch({ - param: { - id: form.id, - }, - form, - }) - ); -}; - -export const deleteUser = async (id: string) => { - return await fetchRPC( - client.users[":id"].$delete({ - param: { id }, - form: {}, - }) - ); -}; + + return await response.json(); + }; \ No newline at end of file diff --git a/apps/frontend/src/routeTree.gen.ts b/apps/frontend/src/routeTree.gen.ts index 8619974..c0c2314 100644 --- a/apps/frontend/src/routeTree.gen.ts +++ b/apps/frontend/src/routeTree.gen.ts @@ -17,12 +17,17 @@ import { Route as DashboardLayoutImport } from './routes/_dashboardLayout' import { Route as DashboardLayoutUsersIndexImport } from './routes/_dashboardLayout/users/index' import { Route as DashboardLayoutTimetableIndexImport } from './routes/_dashboardLayout/timetable/index' import { Route as DashboardLayoutDashboardIndexImport } from './routes/_dashboardLayout/dashboard/index' +import { Route as DashboardLayoutAssessmentRequestIndexImport } from './routes/_dashboardLayout/assessmentRequest/index' // Create Virtual Routes const IndexLazyImport = createFileRoute('/')() const LogoutIndexLazyImport = createFileRoute('/logout/')() const LoginIndexLazyImport = createFileRoute('/login/')() +const ForgotPasswordIndexLazyImport = createFileRoute('/forgot-password/')() +const ForgotPasswordVerifyLazyImport = createFileRoute( + '/forgot-password/verify', +)() // Create/Update Routes @@ -46,6 +51,20 @@ const LoginIndexLazyRoute = LoginIndexLazyImport.update({ getParentRoute: () => rootRoute, } as any).lazy(() => import('./routes/login/index.lazy').then((d) => d.Route)) +const ForgotPasswordIndexLazyRoute = ForgotPasswordIndexLazyImport.update({ + path: '/forgot-password/', + getParentRoute: () => rootRoute, +} as any).lazy(() => + import('./routes/forgot-password/index.lazy').then((d) => d.Route), +) + +const ForgotPasswordVerifyLazyRoute = ForgotPasswordVerifyLazyImport.update({ + path: '/forgot-password/verify', + getParentRoute: () => rootRoute, +} as any).lazy(() => + import('./routes/forgot-password/verify.lazy').then((d) => d.Route), +) + const DashboardLayoutUsersIndexRoute = DashboardLayoutUsersIndexImport.update({ path: '/users/', getParentRoute: () => DashboardLayoutRoute, @@ -65,6 +84,16 @@ const DashboardLayoutDashboardIndexRoute = getParentRoute: () => DashboardLayoutRoute, } as any) +const DashboardLayoutAssessmentRequestIndexRoute = + DashboardLayoutAssessmentRequestIndexImport.update({ + path: '/assessmentRequest/', + getParentRoute: () => DashboardLayoutRoute, + } as any).lazy(() => + import('./routes/_dashboardLayout/assessmentRequest/index.lazy').then( + (d) => d.Route, + ), + ) + // Populate the FileRoutesByPath interface declare module '@tanstack/react-router' { @@ -83,6 +112,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DashboardLayoutImport parentRoute: typeof rootRoute } + '/forgot-password/verify': { + id: '/forgot-password/verify' + path: '/forgot-password/verify' + fullPath: '/forgot-password/verify' + preLoaderRoute: typeof ForgotPasswordVerifyLazyImport + parentRoute: typeof rootRoute + } + '/forgot-password/': { + id: '/forgot-password/' + path: '/forgot-password' + fullPath: '/forgot-password' + preLoaderRoute: typeof ForgotPasswordIndexLazyImport + parentRoute: typeof rootRoute + } '/login/': { id: '/login/' path: '/login' @@ -97,6 +140,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof LogoutIndexLazyImport parentRoute: typeof rootRoute } + '/_dashboardLayout/assessmentRequest/': { + id: '/_dashboardLayout/assessmentRequest/' + path: '/assessmentRequest' + fullPath: '/assessmentRequest' + preLoaderRoute: typeof DashboardLayoutAssessmentRequestIndexImport + parentRoute: typeof DashboardLayoutImport + } '/_dashboardLayout/dashboard/': { id: '/_dashboardLayout/dashboard/' path: '/dashboard' @@ -126,10 +176,13 @@ declare module '@tanstack/react-router' { export const routeTree = rootRoute.addChildren({ IndexLazyRoute, DashboardLayoutRoute: DashboardLayoutRoute.addChildren({ + DashboardLayoutAssessmentRequestIndexRoute, DashboardLayoutDashboardIndexRoute, DashboardLayoutTimetableIndexRoute, DashboardLayoutUsersIndexRoute, }), + ForgotPasswordVerifyLazyRoute, + ForgotPasswordIndexLazyRoute, LoginIndexLazyRoute, LogoutIndexLazyRoute, }) @@ -144,6 +197,8 @@ export const routeTree = rootRoute.addChildren({ "children": [ "/", "/_dashboardLayout", + "/forgot-password/verify", + "/forgot-password/", "/login/", "/logout/" ] @@ -154,17 +209,28 @@ export const routeTree = rootRoute.addChildren({ "/_dashboardLayout": { "filePath": "_dashboardLayout.tsx", "children": [ + "/_dashboardLayout/assessmentRequest/", "/_dashboardLayout/dashboard/", "/_dashboardLayout/timetable/", "/_dashboardLayout/users/" ] }, + "/forgot-password/verify": { + "filePath": "forgot-password/verify.lazy.tsx" + }, + "/forgot-password/": { + "filePath": "forgot-password/index.lazy.tsx" + }, "/login/": { "filePath": "login/index.lazy.tsx" }, "/logout/": { "filePath": "logout/index.lazy.tsx" }, + "/_dashboardLayout/assessmentRequest/": { + "filePath": "_dashboardLayout/assessmentRequest/index.tsx", + "parent": "/_dashboardLayout" + }, "/_dashboardLayout/dashboard/": { "filePath": "_dashboardLayout/dashboard/index.tsx", "parent": "/_dashboardLayout" diff --git a/apps/frontend/src/routes/_dashboardLayout/assessmentRequest/index.lazy.tsx b/apps/frontend/src/routes/_dashboardLayout/assessmentRequest/index.lazy.tsx index 08db5ed..51b9dc7 100644 --- a/apps/frontend/src/routes/_dashboardLayout/assessmentRequest/index.lazy.tsx +++ b/apps/frontend/src/routes/_dashboardLayout/assessmentRequest/index.lazy.tsx @@ -1,11 +1,10 @@ import { assessmentRequestQueryOptions } from "@/modules/assessmentRequestManagement/queries/assessmentRequestQueries"; import PageTemplate from "@/components/PageTemplate"; import { createLazyFileRoute } from "@tanstack/react-router"; -import UserFormModal from "@/modules/assessmentRequestManagement/modals/UserFormModal"; +import FormModal from "@/modules/assessmentRequestManagement/modals/FormModal"; import ExtractQueryDataType from "@/types/ExtractQueryDataType"; import { createColumnHelper } from "@tanstack/react-table"; import { Badge, Flex } from "@mantine/core"; -import UserDeleteModal from "@/modules/assessmentRequestManagement/modals/UserDeleteModal"; import { Button } from "@/shadcn/components/ui/button"; export const Route = createLazyFileRoute("/_dashboardLayout/assessmentRequest/")({ @@ -19,9 +18,9 @@ const columnHelper = createColumnHelper(); export default function UsersPage() { return ( , ]} + modals={[]} columnDefs={[ columnHelper.display({ header: "No", diff --git a/apps/frontend/vite.config.ts b/apps/frontend/vite.config.ts index 2d6bad5..6c9655b 100644 --- a/apps/frontend/vite.config.ts +++ b/apps/frontend/vite.config.ts @@ -8,7 +8,7 @@ export default defineConfig({ plugins: [react(), TanStackRouterVite()], resolve: { alias: { - "@": path.resolve(__dirname,"/src"), + "@": path.resolve(__dirname,"src"), }, }, });