Extends the listForClientAggregated pattern to three new symmetric
helpers in notes.service so the Notes tab on yacht / company /
residential-client detail pages surfaces the full timeline (own notes
+ related-entity notes) instead of just rows on the entity itself.
- listForYachtAggregated: yacht own + owner client (when ownership
is polymorphic 'client') + linked interest notes.
- listForCompanyAggregated: company own + company-owned yacht notes
+ interests linked to those yachts.
- listForResidentialClientAggregated: own + residential interests.
Generalises NotesList so aggregate=true works for all four entity
types via SELF_SOURCE / AGGREGATABLE / SOURCE_BADGE_CLASS / SOURCE_LABEL
maps; cross-source notes render with a coloured chip and are read-only
(rep edits on the source entity's page so the right timeline records
the change).
Wires ?aggregate=true into the yacht / company / residential-client
notes routes; the yacht / company / residential-client tabs now pass
aggregate. Drops the legacy single-textarea spots on the companies
overview tab and the residential-interest "Initial brief" row in
favour of the threaded feed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
51 lines
1.7 KiB
TypeScript
51 lines
1.7 KiB
TypeScript
import { NextResponse } from 'next/server';
|
|
|
|
import { withAuth, withPermission } from '@/lib/api/helpers';
|
|
import { parseBody } from '@/lib/api/route-helpers';
|
|
import { createAuditLog } from '@/lib/audit';
|
|
import { errorResponse, NotFoundError } from '@/lib/errors';
|
|
import { createNoteSchema } from '@/lib/validators/notes';
|
|
import * as notesService from '@/lib/services/notes.service';
|
|
|
|
export const GET = withAuth(
|
|
withPermission('companies', 'view', async (req, ctx, params) => {
|
|
try {
|
|
const companyId = params.id;
|
|
if (!companyId) throw new NotFoundError('Company');
|
|
const aggregate = new URL(req.url).searchParams.get('aggregate') === 'true';
|
|
const notes = aggregate
|
|
? await notesService.listForCompanyAggregated(ctx.portId, companyId)
|
|
: await notesService.listForEntity(ctx.portId, 'companies', companyId);
|
|
return NextResponse.json({ data: notes });
|
|
} catch (error) {
|
|
return errorResponse(error);
|
|
}
|
|
}),
|
|
);
|
|
|
|
export const POST = withAuth(
|
|
withPermission('companies', 'edit', async (req, ctx, params) => {
|
|
try {
|
|
const companyId = params.id;
|
|
if (!companyId) throw new NotFoundError('Company');
|
|
const body = await parseBody(req, createNoteSchema);
|
|
const note = await notesService.create(ctx.portId, 'companies', companyId, ctx.userId, body);
|
|
|
|
void createAuditLog({
|
|
userId: ctx.userId,
|
|
portId: ctx.portId,
|
|
action: 'create',
|
|
entityType: 'company_note',
|
|
entityId: note.id,
|
|
metadata: { companyId },
|
|
ipAddress: ctx.ipAddress,
|
|
userAgent: ctx.userAgent,
|
|
});
|
|
|
|
return NextResponse.json({ data: note }, { status: 201 });
|
|
} catch (error) {
|
|
return errorResponse(error);
|
|
}
|
|
}),
|
|
);
|