From b9a4f462d90b231aa21caab9c41b5a713cef829f Mon Sep 17 00:00:00 2001 From: Kavish Shah Date: Wed, 23 Apr 2025 10:49:46 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20filter=20raw=20deals=20by=20status;=20r?= =?UTF-8?q?ename=20Infer=E2=86=92Screened;=20full-width=20in-process=20but?= =?UTF-8?q?ton?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/(protected)/in-process/page.tsx | 63 ++++++++++++++++++++++++ app/(protected)/raw-deals/page.tsx | 2 + app/actions/get-deal.ts | 7 ++- components/DealCard.tsx | 76 +++++++++++++++++++++++------ components/Header.tsx | 3 +- 5 files changed, 133 insertions(+), 18 deletions(-) create mode 100644 app/(protected)/in-process/page.tsx diff --git a/app/(protected)/in-process/page.tsx b/app/(protected)/in-process/page.tsx new file mode 100644 index 0000000..43c4b88 --- /dev/null +++ b/app/(protected)/in-process/page.tsx @@ -0,0 +1,63 @@ +// /app/(protected)/in-process/page.tsx +import React from "react"; +import { Metadata } from "next"; +import DealCard from "@/components/DealCard"; +import Pagination from "@/components/pagination"; +import SearchDeals from "@/components/SearchDeal"; +import getCurrentUserRole from "../../../lib/data/current-user-role"; +import prismaDB from "../../../lib/prisma"; + +export const metadata: Metadata = { + title: "In Process Deals", + description: "Deals you’ve moved into processing", +}; + +// Strictly type your props so TS knows what searchParams contains +type Props = { + searchParams: { + page?: string; + [key: string]: string | undefined; + }; +}; + +const LIMIT = 20; + +export default async function InProcessPage({ searchParams }: Props) { + // parse page number + const currentPage = Number(searchParams.page ?? "1"); + const offset = (currentPage - 1) * LIMIT; + + // fetch only deals with status = IN_PROCESS + const [data, totalCount] = await Promise.all([ + prismaDB.deal.findMany({ + where: { status: "IN_PROCESS" }, + orderBy: { createdAt: "desc" }, + skip: offset, + take: LIMIT, + }), + prismaDB.deal.count({ where: { status: "IN_PROCESS" } }), + ]); + + const totalPages = Math.ceil(totalCount / LIMIT); + + // getCurrentUserRole can return undefined; assert non-null + const userRole = (await getCurrentUserRole())!; + + return ( +
+

In Process Deals

+

Total: {totalCount}

+ +
+ + +
+ +
+ {data.map((deal) => ( + + ))} +
+
+ ); +} diff --git a/app/(protected)/raw-deals/page.tsx b/app/(protected)/raw-deals/page.tsx index 76948fb..3d3711a 100644 --- a/app/(protected)/raw-deals/page.tsx +++ b/app/(protected)/raw-deals/page.tsx @@ -12,6 +12,7 @@ import DealTypeFilter from "@/components/DealTypeFilter"; import { DealType } from "@prisma/client"; import SearchDealsSkeleton from "@/components/skeletons/SearchDealsSkeleton"; import SearchEbitdaDeals from "@/components/SearchEbitdaDeals"; +import { DealStatus } from "@prisma/client"; export const metadata: Metadata = { title: "Inferred Deals", @@ -45,6 +46,7 @@ const RawDealsPage = async (props: { searchParams: SearchParams }) => { limit, dealTypes: dealTypes as DealType[], ebitda, + status: DealStatus.RAW, }); const currentUserRole = await getCurrentUserRole(); diff --git a/app/actions/get-deal.ts b/app/actions/get-deal.ts index e18958a..25a5ce5 100644 --- a/app/actions/get-deal.ts +++ b/app/actions/get-deal.ts @@ -2,7 +2,7 @@ import "server-only"; import prismaDB from "@/lib/prisma"; -import { Deal, DealType } from "@prisma/client"; +import { Deal, DealType, DealStatus } from "@prisma/client"; import { unstable_cache } from "next/cache"; interface GetDealsResult { @@ -55,21 +55,24 @@ export const GetAllDeals = async ({ limit = 20, dealTypes, ebitda, + status, }: { search?: string | undefined; offset?: number; limit?: number; dealTypes?: DealType[]; ebitda?: string; + status?: DealStatus; }): Promise => { const ebitdaValue = ebitda ? parseFloat(ebitda) : undefined; - const whereClause = { + const whereClause: any = { ...(search ? { dealCaption: { contains: search } } : {}), ...(dealTypes && dealTypes.length > 0 ? { dealType: { in: dealTypes } } : {}), ...(ebitdaValue !== undefined ? { ebitda: { gte: ebitdaValue } } : {}), + ...(status ? { status } : {}), }; console.log("whereClause", whereClause); diff --git a/components/DealCard.tsx b/components/DealCard.tsx index b014ccb..38e9a83 100644 --- a/components/DealCard.tsx +++ b/components/DealCard.tsx @@ -1,3 +1,4 @@ +// components/DealCard.tsx "use client"; import React from "react"; @@ -29,6 +30,9 @@ import { useToast } from "@/hooks/use-toast"; import DeleteDealFromDB from "@/app/actions/delete-deal"; import { cn } from "@/lib/utils"; +import moveToInProcess from "@/app/actions/move-to-in-process"; +import { exportDealToBitrix } from "@/app/actions/upload-bitrix"; + const DealCard = ({ deal, userRole, @@ -42,19 +46,27 @@ const DealCard = ({ showActions?: boolean; showScreenButton?: boolean; }) => { - const editLink = `/raw-deals/${deal.id}/edit`; + const editLink = `/raw-deals/${deal.id}/edit`; const detailLink = `/raw-deals/${deal.id}`; const screenLink = `/raw-deals/${deal.id}/screen`; - const { toast } = useToast(); - const formatCurrency = (amount: number) => { - return new Intl.NumberFormat("en-US", { + + // Wrapper that Next.js expects for
+ const handleMove = async (_formData: FormData): Promise => { + await moveToInProcess(deal.id); + }; + + const handleExport = async (_formData: FormData): Promise => { + await exportDealToBitrix(deal); + }; + + const formatCurrency = (amount: number) => + new Intl.NumberFormat("en-US", { style: "currency", currency: "USD", notation: "compact", maximumFractionDigits: 1, }).format(amount); - }; const handleDelete = async () => { try { @@ -64,7 +76,7 @@ const DealCard = ({ description: response.message, variant: response.type === "success" ? "default" : "destructive", }); - } catch (error) { + } catch { toast({ title: "Error", description: "Failed to delete deal", @@ -77,7 +89,7 @@ const DealCard = ({ @@ -87,6 +99,7 @@ const DealCard = ({ {showActions && (
+ {/* Edit */} @@ -106,6 +119,7 @@ const DealCard = ({ + {/* Delete */} {userRole === "ADMIN" && ( @@ -129,6 +143,7 @@ const DealCard = ({ )}
+ } @@ -150,7 +165,7 @@ const DealCard = ({ label="Industry" value={deal.industry} /> - {deal.askingPrice && ( + {deal.askingPrice != null && ( } label="Asking Price" @@ -165,19 +180,50 @@ const DealCard = ({ /> )} + + {/* View Details */} {showScreenButton && ( - + <> + {/* Screen Deal */} + + + {/* RAW → Move to In Process */} + {deal.status === "RAW" && ( + + + + )} + + {/* IN_PROCESS → Publish to Bitrix */} + {deal.status === "IN_PROCESS" && ( +
+ +
+ )} + )}
diff --git a/components/Header.tsx b/components/Header.tsx index 0fa763a..386da23 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -41,8 +41,9 @@ type NavLinkType = { export const NavLinks: NavLinkType = [ { navlink: "/new-deal", navlabel: "New", icon: FiPlus }, { navlink: "/raw-deals", navlabel: "Raw", icon: FiList }, + { navlink: "/in-process", navlabel: "In Process", icon: FiTrendingUp }, { navlink: "/published-deals", navlabel: "Published", icon: FiCheckSquare }, - { navlink: "/infer", navlabel: "Infer", icon: FiSearch }, + { navlink: "/infer", navlabel: "Screened", icon: FiSearch }, ]; const Header = ({ className, session }: HeaderProps) => {