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

@@ -39,7 +39,8 @@ export async function GET(request: NextRequest) {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
businessRole: membership.businessRole,
permissions: membership.permissions
},
{
dateFrom: searchParams.get('dateFrom'),
@@ -84,11 +85,11 @@ export async function GET(request: NextRequest) {
];
} else {
rows = [
['Sales Person', 'Lead Count', 'Opportunity Count', 'Quotation Count', 'Approved Quotations', 'Won Revenue', 'Conversion Rate'],
['Sales Person', 'Lead Count', 'Enquiry Count', 'Quotation Count', 'Approved Quotations', 'Won Revenue', 'Conversion Rate'],
...data.salesRanking.map((row) => [
row.salesPersonName,
String(row.leadCount),
String(row.opportunityCount),
String(row.enquiryCount),
String(row.quotationCount),
String(row.approvedQuotations),
String(row.wonRevenue),

View File

@@ -14,7 +14,8 @@ export async function GET(request: NextRequest) {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
businessRole: membership.businessRole,
permissions: membership.permissions
},
{
dateFrom: searchParams.get('dateFrom'),

View File

@@ -13,11 +13,16 @@ type Params = {
export async function POST(request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryAssign
});
const payload = enquiryAssignmentRequestSchema.parse(await request.json());
const before = await getEnquiryDetail(id, organization.id);
const before = await getEnquiryDetail(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
const updated = await assignEnquiryToUser(id, organization.id, session.user.id, payload);
await auditAction({
@@ -26,19 +31,21 @@ export async function POST(request: NextRequest, { params }: Params) {
userId: session.user.id,
entityType: 'crm_enquiry',
entityId: id,
action: 'assign',
action: 'lead_to_enquiry',
beforeData: {
oldAssignedToUserId: before.assignedToUserId
oldAssignedToUserId: before.assignedToUserId,
oldPipelineStage: before.pipelineStage
},
afterData: {
newAssignedToUserId: updated.assignedToUserId,
newPipelineStage: updated.pipelineStage,
remark: updated.assignmentRemark
}
});
return NextResponse.json({
success: true,
message: 'Enquiry assigned successfully',
message: 'Lead converted to enquiry successfully',
enquiry: updated
});
} catch (error) {

View File

@@ -10,10 +10,15 @@ type Params = {
export async function GET(_request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryRead
});
const items = await listEnquiryProjectParties(id, organization.id);
const items = await listEnquiryProjectParties(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
return NextResponse.json({
success: true,

View File

@@ -22,11 +22,18 @@ const followupRequestSchema = enquiryFollowupSchema.extend({
export async function PATCH(request: NextRequest, { params }: Params) {
try {
const { id, followupId } = await params;
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryFollowupUpdate
});
const payload = followupRequestSchema.parse(await request.json());
const before = (await listEnquiryFollowups(id, organization.id)).find(
const before = (
await listEnquiryFollowups(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
})
).find(
(item) => item.id === followupId
);
@@ -39,7 +46,13 @@ export async function PATCH(request: NextRequest, { params }: Params) {
followupId,
organization.id,
session.user.id,
payload
payload,
{
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
}
);
await auditUpdate({
@@ -79,10 +92,17 @@ export async function PATCH(request: NextRequest, { params }: Params) {
export async function DELETE(_request: NextRequest, { params }: Params) {
try {
const { id, followupId } = await params;
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryFollowupDelete
});
const before = (await listEnquiryFollowups(id, organization.id)).find(
const before = (
await listEnquiryFollowups(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
})
).find(
(item) => item.id === followupId
);
@@ -94,7 +114,13 @@ export async function DELETE(_request: NextRequest, { params }: Params) {
id,
followupId,
organization.id,
session.user.id
session.user.id,
{
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
}
);
await auditDelete({

View File

@@ -21,10 +21,15 @@ const followupRequestSchema = enquiryFollowupSchema.extend({
export async function GET(_request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryFollowupRead
});
const items = await listEnquiryFollowups(id, organization.id);
const items = await listEnquiryFollowups(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
return NextResponse.json({
success: true,
@@ -48,11 +53,22 @@ export async function GET(_request: NextRequest, { params }: Params) {
export async function POST(request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryFollowupCreate
});
const payload = followupRequestSchema.parse(await request.json());
const created = await createEnquiryFollowup(id, organization.id, session.user.id, payload);
const created = await createEnquiryFollowup(
id,
organization.id,
session.user.id,
payload,
{
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
}
);
await auditCreate({
organizationId: organization.id,

View File

@@ -13,11 +13,16 @@ type Params = {
export async function POST(request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryReassign
});
const payload = enquiryAssignmentRequestSchema.parse(await request.json());
const before = await getEnquiryDetail(id, organization.id);
const before = await getEnquiryDetail(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
const updated = await reassignEnquiryToUser(id, organization.id, session.user.id, payload);
await auditAction({
@@ -28,10 +33,12 @@ export async function POST(request: NextRequest, { params }: Params) {
entityId: id,
action: 'reassign',
beforeData: {
oldAssignedToUserId: before.assignedToUserId
oldAssignedToUserId: before.assignedToUserId,
oldPipelineStage: before.pipelineStage
},
afterData: {
newAssignedToUserId: updated.assignedToUserId,
newPipelineStage: updated.pipelineStage,
remark: updated.assignmentRemark
}
});

View File

@@ -25,12 +25,18 @@ const enquiryRequestSchema = enquirySchema.extend({
export async function GET(_request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryRead
});
const accessContext = {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
};
const [enquiry, activity] = await Promise.all([
getEnquiryDetail(id, organization.id),
getEnquiryActivity(id, organization.id)
getEnquiryDetail(id, organization.id, accessContext),
getEnquiryActivity(id, organization.id, accessContext)
]);
return NextResponse.json({
@@ -56,12 +62,22 @@ export async function GET(_request: NextRequest, { params }: Params) {
export async function PATCH(request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryUpdate
});
const payload = enquiryRequestSchema.parse(await request.json());
const before = await getEnquiryDetail(id, organization.id);
const updated = await updateEnquiry(id, organization.id, session.user.id, payload);
const before = await getEnquiryDetail(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
const updated = await updateEnquiry(id, organization.id, session.user.id, payload, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
await auditUpdate({
organizationId: organization.id,
@@ -101,11 +117,21 @@ export async function PATCH(request: NextRequest, { params }: Params) {
export async function DELETE(_request: NextRequest, { params }: Params) {
try {
const { id } = await params;
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryDelete
});
const before = await getEnquiryDetail(id, organization.id);
const updated = await softDeleteEnquiry(id, organization.id, session.user.id);
const before = await getEnquiryDetail(id, organization.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
const updated = await softDeleteEnquiry(id, organization.id, session.user.id, {
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
});
await auditDelete({
organizationId: organization.id,

View File

@@ -15,30 +15,40 @@ const enquiryRequestSchema = enquirySchema.extend({
export async function GET(request: NextRequest) {
try {
const { organization } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryRead
});
const { searchParams } = request.nextUrl;
const page = Number(searchParams.get('page') ?? 1);
const limit = Number(searchParams.get('limit') ?? 10);
const search = searchParams.get('search') ?? undefined;
const pipelineStage = searchParams.get('pipelineStage') ?? undefined;
const status = searchParams.get('status') ?? undefined;
const productType = searchParams.get('productType') ?? undefined;
const priority = searchParams.get('priority') ?? undefined;
const branch = searchParams.get('branch') ?? undefined;
const customer = searchParams.get('customer') ?? undefined;
const sort = searchParams.get('sort') ?? undefined;
const result = await listEnquiries(organization.id, {
page,
limit,
search,
status,
productType,
priority,
branch,
customer,
sort
});
const result = await listEnquiries(
{
organizationId: organization.id,
userId: session.user.id,
membershipRole: membership.role,
businessRole: membership.businessRole
},
{
page,
limit,
search,
pipelineStage: pipelineStage as 'lead' | 'enquiry' | 'closed_won' | 'closed_lost' | undefined,
status,
productType,
priority,
branch,
customer,
sort
}
);
const offset = (page - 1) * limit;
return NextResponse.json({
@@ -65,11 +75,16 @@ export async function GET(request: NextRequest) {
export async function POST(request: NextRequest) {
try {
const { organization, session } = await requireOrganizationAccess({
const { organization, session, membership } = await requireOrganizationAccess({
permission: PERMISSIONS.crmEnquiryCreate
});
const payload = enquiryRequestSchema.parse(await request.json());
const created = await createEnquiry(organization.id, session.user.id, payload);
const created = await createEnquiry(
organization.id,
session.user.id,
membership.businessRole,
payload
);
await auditCreate({
organizationId: organization.id,