lead menu

nquiry menu
This commit is contained in:
phaichayon
2026-06-19 10:39:11 +07:00
parent 0650cf6297
commit c0cc880be2
46 changed files with 5985 additions and 420 deletions

View File

@@ -54,6 +54,11 @@ export default async function EnquiryDetailRoute({ params }: PageProps) {
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmEnquiryReassign)));
const canReadQuotation =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmQuotationRead)));
const queryClient = getQueryClient();
if (canRead) {
@@ -67,14 +72,14 @@ export default async function EnquiryDetailRoute({ params }: PageProps) {
? await getEnquiryReferenceData(session.user.activeOrganizationId)
: null;
const relatedQuotations =
canRead && session?.user?.activeOrganizationId
canRead && canReadQuotation && session?.user?.activeOrganizationId
? await listEnquiryQuotationRelations(id, session.user.activeOrganizationId)
: [];
return (
<PageContainer
pageTitle='Enquiry Detail'
pageDescription='Opportunity context, follow-up timeline, and future quotation handoff.'
pageDescription='Lead or enquiry context, follow-up timeline, and ownership lifecycle.'
access={canRead}
accessFallback={
<div className='text-muted-foreground rounded-lg border border-dashed p-8 text-center'>
@@ -88,9 +93,11 @@ export default async function EnquiryDetailRoute({ params }: PageProps) {
enquiryId={id}
referenceData={referenceData}
relatedQuotations={relatedQuotations}
workspace='enquiry'
canUpdate={canUpdate}
canAssign={canAssign}
canReassign={canReassign}
canViewRelatedQuotations={canReadQuotation}
canManageFollowups={{
create: canFollowupCreate,
update: canFollowupUpdate,

View File

@@ -50,6 +50,7 @@ export default async function EnquiriesRoute(props: PageProps) {
session.user.activePermissions.includes(
PERMISSIONS.crmEnquiryReassign,
)));
const businessRole = session?.user?.activeBusinessRole ?? null;
searchParamsCache.parse(searchParams);
@@ -60,8 +61,8 @@ export default async function EnquiriesRoute(props: PageProps) {
return (
<PageContainer
pageTitle="Enquiries (Lead)"
pageDescription="Production opportunity registry with customer linkage, follow-up workflow, and audit-backed activity."
pageTitle="Enquiries"
pageDescription="Sales workspace for active enquiries, follow-up workflow, and quotation preparation."
access={canRead}
accessFallback={
<div className="text-muted-foreground rounded-lg border border-dashed p-8 text-center">
@@ -70,13 +71,14 @@ export default async function EnquiriesRoute(props: PageProps) {
}
pageHeaderAction={
canCreate && referenceData ? (
<EnquiryFormSheetTrigger referenceData={referenceData} />
<EnquiryFormSheetTrigger referenceData={referenceData} workspace='enquiry' />
) : null
}
>
{referenceData ? (
<EnquiryListing
referenceData={referenceData}
workspace='enquiry'
canUpdate={canUpdate}
canDelete={canDelete}
canAssign={canAssign}

View File

@@ -0,0 +1,99 @@
import { auth } from '@/auth';
import { HydrationBoundary, dehydrate } from '@tanstack/react-query';
import { notFound, redirect } from 'next/navigation';
import PageContainer from '@/components/layout/page-container';
import {
enquiryByIdOptions,
enquiryFollowupsOptions,
enquiryProjectPartiesOptions
} from '@/features/crm/enquiries/api/queries';
import { EnquiryDetail } from '@/features/crm/enquiries/components/enquiry-detail';
import { getEnquiryDetail, getEnquiryReferenceData } from '@/features/crm/enquiries/server/service';
import { PERMISSIONS } from '@/lib/auth/rbac';
import { getQueryClient } from '@/lib/query-client';
type PageProps = {
params: Promise<{ id: string }>;
};
export default async function LeadDetailRoute({ params }: PageProps) {
const { id } = await params;
const session = await auth();
const canRead =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadRead)));
const canUpdate =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadUpdate)));
const canAssign =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadAssign)));
const queryClient = getQueryClient();
if (!session?.user?.activeOrganizationId) {
notFound();
}
const enquiry = canRead
? await getEnquiryDetail(id, session.user.activeOrganizationId, {
organizationId: session.user.activeOrganizationId,
userId: session.user.id,
membershipRole: session.user.activeMembershipRole ?? 'user',
businessRole: session.user.activeBusinessRole ?? 'sales_support'
})
: null;
if (enquiry && enquiry.pipelineStage !== 'lead') {
redirect(`/dashboard/crm/enquiries/${id}`);
}
if (canRead) {
void queryClient.prefetchQuery(enquiryByIdOptions(id));
void queryClient.prefetchQuery(enquiryProjectPartiesOptions(id));
void queryClient.prefetchQuery(enquiryFollowupsOptions(id));
}
const referenceData =
canRead && session.user.activeOrganizationId
? await getEnquiryReferenceData(session.user.activeOrganizationId)
: null;
return (
<PageContainer
pageTitle='Lead Detail'
pageDescription='Marketing lead information and assignment handoff context.'
access={canRead}
accessFallback={
<div className='text-muted-foreground rounded-lg border border-dashed p-8 text-center'>
You do not have access to this lead.
</div>
}
>
{referenceData ? (
<HydrationBoundary state={dehydrate(queryClient)}>
<EnquiryDetail
enquiryId={id}
referenceData={referenceData}
relatedQuotations={[]}
workspace='lead'
canUpdate={canUpdate}
canAssign={canAssign}
canReassign={false}
canViewRelatedQuotations={false}
canManageFollowups={{
create: false,
update: false,
delete: false
}}
/>
</HydrationBoundary>
) : null}
</PageContainer>
);
}

View File

@@ -0,0 +1,82 @@
import { auth } from '@/auth';
import PageContainer from '@/components/layout/page-container';
import EnquiryListing from '@/features/crm/enquiries/components/enquiry-listing';
import { EnquiryFormSheetTrigger } from '@/features/crm/enquiries/components/enquiry-form-sheet';
import { getEnquiryReferenceData } from '@/features/crm/enquiries/server/service';
import { PERMISSIONS } from '@/lib/auth/rbac';
import { searchParamsCache } from '@/lib/searchparams';
import type { SearchParams } from 'nuqs/server';
export const metadata = {
title: 'Dashboard: CRM Leads'
};
type PageProps = {
searchParams: Promise<SearchParams>;
};
export default async function LeadsRoute(props: PageProps) {
const searchParams = await props.searchParams;
const session = await auth();
const canRead =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadRead)));
const canCreate =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadCreate)));
const canUpdate =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadUpdate)));
const canDelete =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadDelete)));
const canAssign =
session?.user?.systemRole === 'super_admin' ||
(!!session?.user?.activeOrganizationId &&
(session.user.activeMembershipRole === 'admin' ||
session.user.activePermissions.includes(PERMISSIONS.crmLeadAssign)));
searchParamsCache.parse(searchParams);
const referenceData =
canRead && session?.user?.activeOrganizationId
? await getEnquiryReferenceData(session.user.activeOrganizationId)
: null;
return (
<PageContainer
pageTitle='Leads'
pageDescription='Marketing workspace for lead collection, qualification, nurturing, and sales assignment.'
access={canRead}
accessFallback={
<div className='text-muted-foreground rounded-lg border border-dashed p-8 text-center'>
You do not have access to CRM leads.
</div>
}
pageHeaderAction={
canCreate && referenceData ? (
<EnquiryFormSheetTrigger referenceData={referenceData} workspace='lead' />
) : null
}
>
{referenceData ? (
<EnquiryListing
referenceData={referenceData}
workspace='lead'
canUpdate={canUpdate}
canDelete={canDelete}
canAssign={canAssign}
canReassign={false}
/>
) : null}
</PageContainer>
);
}