task-fic-display
This commit is contained in:
156
src/app/api/foundation/master-options/[id]/route.ts
Normal file
156
src/app/api/foundation/master-options/[id]/route.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { z } from 'zod';
|
||||
import { auditDelete, auditUpdate } from '@/features/foundation/audit-log/service';
|
||||
import {
|
||||
getMasterOptionById,
|
||||
softDeleteMasterOption,
|
||||
updateMasterOption
|
||||
} from '@/features/foundation/master-options/service';
|
||||
import { PERMISSIONS } from '@/lib/auth/rbac';
|
||||
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
|
||||
|
||||
const masterOptionUpdateSchema = z.object({
|
||||
category: z.string().min(1).optional(),
|
||||
code: z.string().min(1).optional(),
|
||||
label: z.string().min(1).optional(),
|
||||
value: z.string().nullable().optional(),
|
||||
parent_id: z.string().nullable().optional(),
|
||||
sort_order: z.number().int().optional(),
|
||||
is_active: z.boolean().optional(),
|
||||
metadata: z.record(z.string(), z.unknown()).nullable().optional()
|
||||
});
|
||||
|
||||
function mapMasterOptionResponse(item: Awaited<ReturnType<typeof getMasterOptionById>>) {
|
||||
return {
|
||||
id: item.id,
|
||||
organization_id: item.organizationId,
|
||||
category: item.category,
|
||||
code: item.code,
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
parent_id: item.parentId,
|
||||
parent_label: item.parentLabel,
|
||||
sort_order: item.sortOrder,
|
||||
is_active: item.isActive,
|
||||
metadata: item.metadata,
|
||||
deleted_at: item.deletedAt,
|
||||
created_at: item.createdAt,
|
||||
updated_at: item.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
type RouteContext = {
|
||||
params: Promise<{ id: string }>;
|
||||
};
|
||||
|
||||
export async function GET(_request: NextRequest, context: RouteContext) {
|
||||
try {
|
||||
const { organization } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmMasterOptionRead
|
||||
});
|
||||
const { id } = await context.params;
|
||||
const item = await getMasterOptionById(id, organization.id, { includeDeleted: true });
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
time: new Date().toISOString(),
|
||||
message: 'Master option loaded successfully',
|
||||
item: mapMasterOptionResponse(item)
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to load master option' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest, context: RouteContext) {
|
||||
try {
|
||||
const { organization, session } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmMasterOptionUpdate
|
||||
});
|
||||
const { id } = await context.params;
|
||||
const before = await getMasterOptionById(id, organization.id, { includeDeleted: true });
|
||||
const payload = masterOptionUpdateSchema.parse(await request.json());
|
||||
|
||||
const updated = await updateMasterOption(id, organization.id, {
|
||||
category: payload.category,
|
||||
code: payload.code,
|
||||
label: payload.label,
|
||||
value: payload.value,
|
||||
parentId: payload.parent_id,
|
||||
sortOrder: payload.sort_order,
|
||||
isActive: payload.is_active,
|
||||
metadata: payload.metadata
|
||||
});
|
||||
|
||||
await auditUpdate({
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
entityType: 'ms_option',
|
||||
entityId: updated.id,
|
||||
beforeData: before,
|
||||
afterData: updated
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Master option updated successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json({ message: error.flatten().formErrors.join(', ') }, { status: 400 });
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to update master option' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function DELETE(_request: NextRequest, context: RouteContext) {
|
||||
try {
|
||||
const { organization, session } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmMasterOptionDelete
|
||||
});
|
||||
const { id } = await context.params;
|
||||
const before = await getMasterOptionById(id, organization.id, { includeDeleted: true });
|
||||
const deleted = await softDeleteMasterOption(id, organization.id);
|
||||
|
||||
await auditDelete({
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
entityType: 'ms_option',
|
||||
entityId: deleted.id,
|
||||
beforeData: before,
|
||||
afterData: deleted
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Master option deleted successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to delete master option' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
34
src/app/api/foundation/master-options/categories/route.ts
Normal file
34
src/app/api/foundation/master-options/categories/route.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
import { listMasterOptionCategories } from '@/features/foundation/master-options/service';
|
||||
import { PERMISSIONS } from '@/lib/auth/rbac';
|
||||
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
|
||||
|
||||
export async function GET() {
|
||||
try {
|
||||
const { organization } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmMasterOptionRead
|
||||
});
|
||||
const items = await listMasterOptionCategories(organization.id);
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
time: new Date().toISOString(),
|
||||
message: 'Master option categories loaded successfully',
|
||||
items: items.map((item) => ({
|
||||
category: item.category,
|
||||
total_items: item.totalItems,
|
||||
active_items: item.activeItems
|
||||
}))
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to load master option categories' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,47 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { listMasterOptions } from '@/features/foundation/master-options/service';
|
||||
import { z } from 'zod';
|
||||
import {
|
||||
createMasterOption,
|
||||
listMasterOptions
|
||||
} from '@/features/foundation/master-options/service';
|
||||
import { auditCreate } from '@/features/foundation/audit-log/service';
|
||||
import { PERMISSIONS } from '@/lib/auth/rbac';
|
||||
import { AuthError, requireOrganizationAccess } from '@/lib/auth/session';
|
||||
|
||||
const masterOptionSchema = z.object({
|
||||
category: z.string().min(1),
|
||||
code: z.string().min(1),
|
||||
label: z.string().min(1),
|
||||
value: z.string().nullable().optional(),
|
||||
parent_id: z.string().nullable().optional(),
|
||||
sort_order: z.number().int().optional(),
|
||||
is_active: z.boolean().optional(),
|
||||
metadata: z.record(z.string(), z.unknown()).nullable().optional()
|
||||
});
|
||||
|
||||
function mapMasterOptionResponse(item: Awaited<ReturnType<typeof createMasterOption>>) {
|
||||
return {
|
||||
id: item.id,
|
||||
organization_id: item.organizationId,
|
||||
category: item.category,
|
||||
code: item.code,
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
parent_id: item.parentId,
|
||||
parent_label: item.parentLabel,
|
||||
sort_order: item.sortOrder,
|
||||
is_active: item.isActive,
|
||||
metadata: item.metadata,
|
||||
deleted_at: item.deletedAt,
|
||||
created_at: item.createdAt,
|
||||
updated_at: item.updatedAt
|
||||
};
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
try {
|
||||
const { organization } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.organizationManage
|
||||
permission: PERMISSIONS.crmMasterOptionRead
|
||||
});
|
||||
const { searchParams } = request.nextUrl;
|
||||
const page = Number(searchParams.get('page') ?? 1);
|
||||
@@ -14,6 +49,7 @@ export async function GET(request: NextRequest) {
|
||||
const search = searchParams.get('search') ?? undefined;
|
||||
const category = searchParams.get('category') ?? undefined;
|
||||
const sort = searchParams.get('sort') ?? undefined;
|
||||
|
||||
const result = await listMasterOptions({
|
||||
organizationId: organization.id,
|
||||
page,
|
||||
@@ -22,29 +58,15 @@ export async function GET(request: NextRequest) {
|
||||
category,
|
||||
sort
|
||||
});
|
||||
const offset = (page - 1) * limit;
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
time: new Date().toISOString(),
|
||||
message: 'Master options loaded successfully',
|
||||
total_items: result.totalItems,
|
||||
offset,
|
||||
offset: (page - 1) * limit,
|
||||
limit,
|
||||
items: result.items.map((item) => ({
|
||||
id: item.id,
|
||||
organization_id: item.organizationId,
|
||||
category: item.category,
|
||||
code: item.code,
|
||||
label: item.label,
|
||||
value: item.value,
|
||||
parent_id: item.parentId,
|
||||
parent_label: item.parentLabel,
|
||||
sort_order: item.sortOrder,
|
||||
is_active: item.isActive,
|
||||
deleted_at: item.deletedAt,
|
||||
updated_at: item.updatedAt
|
||||
}))
|
||||
items: result.items.map(mapMasterOptionResponse)
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
@@ -58,3 +80,49 @@ export async function GET(request: NextRequest) {
|
||||
return NextResponse.json({ message: 'Unable to load master options' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
try {
|
||||
const { organization, session } = await requireOrganizationAccess({
|
||||
permission: PERMISSIONS.crmMasterOptionCreate
|
||||
});
|
||||
const payload = masterOptionSchema.parse(await request.json());
|
||||
const created = await createMasterOption(organization.id, {
|
||||
category: payload.category,
|
||||
code: payload.code,
|
||||
label: payload.label,
|
||||
value: payload.value,
|
||||
parentId: payload.parent_id,
|
||||
sortOrder: payload.sort_order,
|
||||
isActive: payload.is_active,
|
||||
metadata: payload.metadata
|
||||
});
|
||||
|
||||
await auditCreate({
|
||||
organizationId: organization.id,
|
||||
userId: session.user.id,
|
||||
entityType: 'ms_option',
|
||||
entityId: created.id,
|
||||
afterData: created
|
||||
});
|
||||
|
||||
return NextResponse.json({
|
||||
success: true,
|
||||
message: 'Master option created successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof AuthError) {
|
||||
return NextResponse.json({ message: error.message }, { status: error.status });
|
||||
}
|
||||
|
||||
if (error instanceof z.ZodError) {
|
||||
return NextResponse.json({ message: error.flatten().formErrors.join(', ') }, { status: 400 });
|
||||
}
|
||||
|
||||
if (error instanceof Error) {
|
||||
return NextResponse.json({ message: error.message }, { status: 400 });
|
||||
}
|
||||
|
||||
return NextResponse.json({ message: 'Unable to create master option' }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
1
src/app/api/foundation/ms-options/[id]/route.ts
Normal file
1
src/app/api/foundation/ms-options/[id]/route.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { DELETE, GET, PATCH } from '@/app/api/foundation/master-options/[id]/route';
|
||||
1
src/app/api/foundation/ms-options/categories/route.ts
Normal file
1
src/app/api/foundation/ms-options/categories/route.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { GET } from '@/app/api/foundation/master-options/categories/route';
|
||||
1
src/app/api/foundation/ms-options/route.ts
Normal file
1
src/app/api/foundation/ms-options/route.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { GET, POST } from '@/app/api/foundation/master-options/route';
|
||||
Reference in New Issue
Block a user