Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/api/people/feature/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { peoplePageFlag } from "@/flags";

export const dynamic = "force-dynamic";

export async function GET() {
return Response.json({
enabled: await peoplePageFlag(),
});
}
55 changes: 29 additions & 26 deletions app/api/schedule/[planPersonId]/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from "zod";
import { after } from "next/server";
import { ApiError } from "@/lib/http/api-error";
import { handlePlanningCenterRoute } from "@/lib/http/planning-center-route";
import { logger } from "@/lib/logger";
Expand Down Expand Up @@ -32,34 +33,36 @@ export async function DELETE(
return handlePlanningCenterRoute(request, async (authContext) => {
let planPersonId: string | null = null;

const recordRemoveEventSafely = async (event: {
const recordRemoveEventSafely = (event: {
success: boolean;
statusCode: number;
errorCode: string | null;
metadata?: Record<string, unknown>;
}) => {
try {
await recordActivityEvent({
eventType: "schedule_remove",
actorUserId: authContext.session.user.id,
actorAccountId: authContext.accountId,
requestId,
path: activityRequestContext.path,
method: activityRequestContext.method,
ipAddress: activityRequestContext.ipAddress,
userAgent: activityRequestContext.userAgent,
success: event.success,
statusCode: event.statusCode,
errorCode: event.errorCode,
metadata: {
planPersonId,
...event.metadata,
},
});
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
log.warn({ err }, "Failed to record schedule remove activity event");
}
after(async () => {
try {
await recordActivityEvent({
eventType: "schedule_remove",
actorUserId: authContext.session.user.id,
actorAccountId: authContext.accountId,
requestId,
path: activityRequestContext.path,
method: activityRequestContext.method,
ipAddress: activityRequestContext.ipAddress,
userAgent: activityRequestContext.userAgent,
success: event.success,
statusCode: event.statusCode,
errorCode: event.errorCode,
metadata: {
planPersonId,
...event.metadata,
},
});
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
log.warn({ err }, "Failed to record schedule remove activity event");
}
});
};

try {
Expand All @@ -84,7 +87,7 @@ export async function DELETE(
}

log.info({ planPersonId }, "PlanPerson removed successfully");
await recordRemoveEventSafely({
recordRemoveEventSafely({
success: true,
statusCode: 200,
errorCode: null,
Expand All @@ -94,13 +97,13 @@ export async function DELETE(
return { success: true };
} catch (error) {
if (error instanceof ApiError) {
await recordRemoveEventSafely({
recordRemoveEventSafely({
success: false,
statusCode: error.status,
errorCode: error.code,
});
} else {
await recordRemoveEventSafely({
recordRemoveEventSafely({
success: false,
statusCode: 500,
errorCode: "INTERNAL_SERVER_ERROR",
Expand Down
80 changes: 51 additions & 29 deletions app/api/schedule/[planPersonId]/status/route.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { z } from "zod";
import { after } from "next/server";
import { ApiError } from "@/lib/http/api-error";
import { handlePlanningCenterRoute } from "@/lib/http/planning-center-route";
import { logger } from "@/lib/logger";
Expand All @@ -7,6 +8,7 @@ import {
getActivityRequestContext,
recordActivityEvent,
} from "@/lib/db/activity-events";
import { invalidateCandidateHistoryForPerson } from "@/lib/use-cases/planning-center/get-people-for-position";

export const dynamic = "force-dynamic";

Expand All @@ -16,6 +18,9 @@ const paramsSchema = z.object({

const bodySchema = z.object({
status: z.enum(["C", "U", "D"]),
serviceTypeId: z.string().min(1).optional(),
personId: z.string().min(1).optional(),
planId: z.string().min(1).optional(),
});

export async function PATCH(
Expand All @@ -27,41 +32,44 @@ export async function PATCH(
const log = logger.withRequest(request).child({ requestId });

return handlePlanningCenterRoute(request, async (authContext) => {
const recordStatusEventSafely = async (event: {
const recordStatusEventSafely = (event: {
success: boolean;
statusCode: number;
errorCode: string | null;
planPersonId: string | null;
status: "C" | "U" | "D" | null;
metadata?: Record<string, unknown>;
}) => {
try {
await recordActivityEvent({
eventType: "schedule_status_change",
actorUserId: authContext.session.user.id,
actorAccountId: authContext.accountId,
requestId,
path: activityRequestContext.path,
method: activityRequestContext.method,
ipAddress: activityRequestContext.ipAddress,
userAgent: activityRequestContext.userAgent,
success: event.success,
statusCode: event.statusCode,
errorCode: event.errorCode,
metadata: {
planPersonId: event.planPersonId,
status: event.status,
...event.metadata,
},
});
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
log.warn({ err }, "Failed to record schedule status change activity event");
}
after(async () => {
try {
await recordActivityEvent({
eventType: "schedule_status_change",
actorUserId: authContext.session.user.id,
actorAccountId: authContext.accountId,
requestId,
path: activityRequestContext.path,
method: activityRequestContext.method,
ipAddress: activityRequestContext.ipAddress,
userAgent: activityRequestContext.userAgent,
success: event.success,
statusCode: event.statusCode,
errorCode: event.errorCode,
metadata: {
planPersonId: event.planPersonId,
status: event.status,
...event.metadata,
},
});
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
log.warn({ err }, "Failed to record schedule status change activity event");
}
});
};

let planPersonId: string | null = null;
let nextStatus: "C" | "U" | "D" | null = null;
let requestBody: z.infer<typeof bodySchema> | null = null;

try {
const parsedParams = paramsSchema.safeParse(await params);
Expand Down Expand Up @@ -92,38 +100,52 @@ export async function PATCH(
parsedBody.error.issues
);
}
nextStatus = parsedBody.data.status;
requestBody = parsedBody.data;
nextStatus = requestBody.status;

await planningCenterPeopleService.updatePlanPersonStatus(
planPersonId,
nextStatus
nextStatus,
{
personId: requestBody.personId,
serviceTypeId: requestBody.serviceTypeId,
planId: requestBody.planId,
}
);
if (requestBody.personId) {
invalidateCandidateHistoryForPerson(requestBody.personId);
}

log.info(
{ planPersonId, status: nextStatus },
"PlanPerson status updated successfully"
);

await recordStatusEventSafely({
recordStatusEventSafely({
success: true,
statusCode: 200,
errorCode: null,
planPersonId,
status: nextStatus,
metadata: {
personId: requestBody.personId ?? null,
serviceTypeId: requestBody.serviceTypeId ?? null,
planId: requestBody.planId ?? null,
},
});

return { success: true };
} catch (error) {
if (error instanceof ApiError) {
await recordStatusEventSafely({
recordStatusEventSafely({
success: false,
statusCode: error.status,
errorCode: error.code,
planPersonId,
status: nextStatus,
});
} else {
await recordStatusEventSafely({
recordStatusEventSafely({
success: false,
statusCode: 500,
errorCode: "INTERNAL_SERVER_ERROR",
Expand Down
Loading
Loading