From d29a9ae93554128136d8a9965a0e8b28fb8ebfc7 Mon Sep 17 00:00:00 2001 From: siddharth Date: Wed, 15 Jan 2025 20:16:11 +0530 Subject: [PATCH] chore: context aware actions for command menu --- apps/admin-panel/app/command-menu.tsx | 368 ++++++++++++++---- .../components/data-table/index.tsx | 5 +- .../components/paginated-table/index.tsx | 1 + 3 files changed, 302 insertions(+), 72 deletions(-) diff --git a/apps/admin-panel/app/command-menu.tsx b/apps/admin-panel/app/command-menu.tsx index 5dfd3249d..361a83b0b 100644 --- a/apps/admin-panel/app/command-menu.tsx +++ b/apps/admin-panel/app/command-menu.tsx @@ -1,5 +1,29 @@ "use client" + import React from "react" +import { usePathname, useRouter } from "next/navigation" +import { HiPlus } from "react-icons/hi" + +import { CreateCustomerDialog } from "./customers/create" +import { CreateDepositDialog } from "./deposits/create" +import { WithdrawalInitiateDialog } from "./withdrawals/initiate" +import { CreateCreditFacilityDialog } from "./credit-facilities/create" +import { CreditFacilityPartialPaymentDialog } from "./credit-facilities/partial-payment" +import { CreateUserDialog } from "./users/create" +import { CreateTermsTemplateDialog } from "./terms-templates/create" +import { CreateCommitteeDialog } from "./committees/create" +import { CreditFacilityDisbursalInitiateDialog } from "./disbursals/create" + +import { PATH_CONFIGS, useCreateContext } from "./create" + +import { + navDashboardItems, + navLoansItems, + navCustomersItems, + navTransactionItems, + navAdminItems, + navFinanceItems, +} from "@/components/app-sidebar/nav-items" import { Command, @@ -11,22 +35,55 @@ import { CommandList, CommandSeparator, } from "@/ui/command" +import { CreditFacilityStatus } from "@/lib/graphql/generated" -import { - navDashboardItems, - navLoansItems, - navCustomersItems, - navTransactionItems, - navAdminItems, - navFinanceItems, -} from "@/components/app-sidebar/nav-items" +const isItemAllowedOnCurrentPath = ( + allowedPaths: (string | RegExp)[], + currentPath: string, +) => { + return allowedPaths.some((path) => { + if (typeof path === "string") { + return path === currentPath + } else if (path instanceof RegExp) { + return path.test(currentPath) + } + return false + }) +} -import { useRouter } from "next/navigation" +const allNavItems = [ + ...navDashboardItems, + ...navLoansItems, + ...navCustomersItems, + ...navTransactionItems, + ...navAdminItems, + ...navFinanceItems, +] const CommandMenu = () => { + const router = useRouter() + const pathName = usePathname() + const [open, setOpen] = React.useState(false) const [pages, setPages] = React.useState<"main" | "navigation">("main") - const router = useRouter() + + const [createCustomer, setCreateCustomer] = React.useState(false) + const [createDeposit, setCreateDeposit] = React.useState(false) + const [createWithdrawal, setCreateWithdrawal] = React.useState(false) + const [createFacility, setCreateFacility] = React.useState(false) + const [initiateDisbursal, setInitiateDisbursal] = React.useState(false) + const [makePayment, setMakePayment] = React.useState(false) + const [openCreateUserDialog, setOpenCreateUserDialog] = React.useState(false) + const [openCreateTermsTemplateDialog, setOpenCreateTermsTemplateDialog] = + React.useState(false) + const [openCreateCommitteeDialog, setOpenCreateCommitteeDialog] = React.useState(false) + + const { customer, facility, setCustomer } = useCreateContext() + + const userIsInCustomerDetailsPage = Boolean(pathName.match(/^\/customers\/.+$/)) + const setCustomerToNullIfNotInCustomerDetails = () => { + if (!userIsInCustomerDetailsPage) setCustomer(null) + } React.useEffect(() => { const down = (e: KeyboardEvent) => { @@ -41,7 +98,6 @@ const CommandMenu = () => { setOpen((open) => !open) } } - document.addEventListener("keydown", down) return () => document.removeEventListener("keydown", down) }, []) @@ -52,44 +108,170 @@ const CommandMenu = () => { } }, [open]) - const allNavItems = [ - ...navDashboardItems, - ...navLoansItems, - ...navCustomersItems, - ...navTransactionItems, - ...navAdminItems, - ...navFinanceItems, + const menuItems = [ + { + label: "Create Deposit", + icon: HiPlus, + action: () => { + if (!customer) return + setCreateDeposit(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.CUSTOMER_DETAILS], + }, + { + label: "Create Withdrawal", + icon: HiPlus, + action: () => { + if (!customer) return + setCreateWithdrawal(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.CUSTOMER_DETAILS], + }, + { + label: "Create Customer", + icon: HiPlus, + action: () => { + setCreateCustomer(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.CUSTOMERS, PATH_CONFIGS.CUSTOMER_DETAILS], + }, + { + label: "Create Credit Facility", + icon: HiPlus, + action: () => { + if (!customer) return + setCreateFacility(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.CUSTOMER_DETAILS], + }, + { + label: "Create Disbursal", + icon: HiPlus, + action: () => { + if (!facility) return + setInitiateDisbursal(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.CREDIT_FACILITY_DETAILS], + condition: () => facility?.status === CreditFacilityStatus.Active, + }, + { + label: "Make Payment", + icon: HiPlus, + action: () => { + if (!facility) return + setMakePayment(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.CREDIT_FACILITY_DETAILS], + condition: () => facility?.status === CreditFacilityStatus.Active, + }, + { + label: "Create User", + icon: HiPlus, + action: () => { + setOpenCreateUserDialog(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.USERS, PATH_CONFIGS.USER_DETAILS], + }, + { + label: "Create Terms Template", + icon: HiPlus, + action: () => { + setOpenCreateTermsTemplateDialog(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.TERMS_TEMPLATES, PATH_CONFIGS.TERMS_TEMPLATE_DETAILS], + }, + { + label: "Create Committee", + icon: HiPlus, + action: () => { + setOpenCreateCommitteeDialog(true) + setOpen(false) + }, + allowedPaths: [PATH_CONFIGS.COMMITTEES, PATH_CONFIGS.COMMITTEE_DETAILS], + }, ] + const availableItems = menuItems.filter((item) => + isItemAllowedOnCurrentPath(item.allowedPaths, pathName), + ) + return ( - - - - - No results found. - - {pages === "main" ? ( - <> - - Navigation - - Ctrl +N - - - } - > + <> + + + + + No results found. + + {pages === "main" ? ( + <> + {availableItems.length > 0 && ( + <> + + + {availableItems.map((item) => ( + { + item.action() + }} + > + + {item.label} + + ))} + + + )} + + + + + Navigation + + Ctrl +N + + + } + > + {allNavItems.map((item) => ( + { + router.push(item.url) + setOpen(false) + }} + className="flex items-center gap-2" + > + + {item.title} + + ))} + + + ) : ( + {allNavItems.map((item) => ( { - window.location.href = item.url setOpen(false) + router.push(item.url) }} className="flex items-center gap-2" > @@ -98,38 +280,82 @@ const CommandMenu = () => { ))} + )} + + + - + - - - Change Theme... - - - Copy Current URL - - - - ) : ( - - {allNavItems.map((item) => ( - { - setOpen(false) - router.push(item.url) - }} - className="flex items-center gap-2" - > - - {item.title} - - ))} - - )} - - - + + + + + + + {customer && ( + <> + { + setCustomerToNullIfNotInCustomerDetails() + setCreateDeposit(false) + }} + depositAccountId={customer.depositAccount.depositAccountId} + /> + + { + setCustomerToNullIfNotInCustomerDetails() + setCreateWithdrawal(false) + }} + depositAccountId={customer.depositAccount.depositAccountId} + /> + + { + setCustomerToNullIfNotInCustomerDetails() + setCreateFacility(false) + }} + customerId={customer.customerId} + /> + + )} + + {facility && ( + <> + { + setInitiateDisbursal(false) + }} + /> + + { + setMakePayment(false) + }} + /> + + )} + ) } diff --git a/apps/admin-panel/components/data-table/index.tsx b/apps/admin-panel/components/data-table/index.tsx index 96079c3ad..c227ef352 100644 --- a/apps/admin-panel/components/data-table/index.tsx +++ b/apps/admin-panel/components/data-table/index.tsx @@ -4,13 +4,14 @@ import React, { useState, useEffect, useRef } from "react" import Link from "next/link" import { ArrowRight } from "lucide-react" +import { useRouter } from "next/navigation" + import { useBreakpointDown } from "@/hooks/use-media-query" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/ui/table" import { Button } from "@/ui/button" import { cn } from "@/lib/utils" import { Skeleton } from "@/ui/skeleton" import { Card } from "@/ui/card" -import { useRouter } from "next/navigation" export type Column = { [K in keyof T]: { @@ -116,12 +117,14 @@ const DataTable = ({ window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [data, focusedRowIndex, onRowClick, navigateTo]) useEffect(() => { if (data.length && focusedRowIndex === -1) { focusRow(0) } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [data.length]) if (loading) { diff --git a/apps/admin-panel/components/paginated-table/index.tsx b/apps/admin-panel/components/paginated-table/index.tsx index 0cb496316..f9c1b5c11 100644 --- a/apps/admin-panel/components/paginated-table/index.tsx +++ b/apps/admin-panel/components/paginated-table/index.tsx @@ -143,6 +143,7 @@ const PaginatedTable = ({ window.addEventListener("keydown", handleKeyDown) return () => window.removeEventListener("keydown", handleKeyDown) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [displayData, focusedRowIndex, onClick, navigateTo, router]) useEffect(() => {