Track HiveGrid Cloud planning reference
This commit is contained in:
1 parent
b3d5ea6764
commit
e341c5938a
5784 files changed
+1104929
-3
No files matched your search
@@ -0,0 +1,87 @@
|
||||
// P4 Tasks (AK-P4b) §12 — audit + activity wiring for the Tasks module.
|
||||
//
|
||||
// Audit (DoD 13): every write action records a tamper-evident AuditEvent (M001)
|
||||
// via createAuditEvent with the shared db client.
|
||||
//
|
||||
// Activity (DoD 14): user-facing timeline entries are logged against the most
|
||||
// relevant entity. When the task is linked to a CRM entity that maps to an
|
||||
// Activity subject (contact/company/lead/deal/project) the entry hangs off that
|
||||
// entity's timeline with the task referenced; otherwise it hangs off the task's
|
||||
// own timeline (`task` is itself a valid Activity subject).
|
||||
|
||||
import type { JsonValue } from "@toolctrl/foundation";
|
||||
import { createAuditEvent } from "@toolctrl/db/audit-events.server";
|
||||
import { logActivity, type ActivitySubjectType, type ActivityType } from "@hivegrid/activity";
|
||||
import type { TasksRouteContext } from "./taskAccess.server";
|
||||
import type { TaskEntityType } from "./taskService.server";
|
||||
|
||||
export type TaskAuditAction =
|
||||
| "task.created"
|
||||
| "task.updated"
|
||||
| "task.reassigned"
|
||||
| "task.completed"
|
||||
| "task.archived"
|
||||
| "task.reactivated"
|
||||
| "task.deleted"
|
||||
| "task.watcher_added"
|
||||
| "task.watcher_removed";
|
||||
|
||||
export async function auditTaskEvent(input: {
|
||||
ctx: Pick<TasksRouteContext, "tenantId" | "userId">;
|
||||
action: TaskAuditAction;
|
||||
taskId: string;
|
||||
metadata?: Record<string, JsonValue>;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
await createAuditEvent({
|
||||
tenantId: input.ctx.tenantId,
|
||||
actorId: input.ctx.userId,
|
||||
actorType: "user",
|
||||
action: input.action,
|
||||
targetType: "task",
|
||||
targetId: input.taskId,
|
||||
metadata: input.metadata ?? null,
|
||||
});
|
||||
} catch {
|
||||
// Audit must never break the user action; the failure is logged upstream.
|
||||
}
|
||||
}
|
||||
|
||||
const ENTITY_TO_ACTIVITY_SUBJECT: Partial<Record<TaskEntityType, ActivitySubjectType>> = {
|
||||
contact: "contact",
|
||||
company: "company",
|
||||
lead: "lead",
|
||||
deal: "deal",
|
||||
project: "project",
|
||||
};
|
||||
|
||||
/**
|
||||
* Log a task activity. When the task links to a CRM entity with an Activity
|
||||
* subject, the entry hangs off that entity (with the task as a related ref);
|
||||
* otherwise it hangs off the task's own timeline. Best-effort.
|
||||
*/
|
||||
export async function logTaskActivity(input: {
|
||||
ctx: Pick<TasksRouteContext, "tenantId" | "userId">;
|
||||
taskId: string;
|
||||
activityType: Extract<ActivityType, "task_created" | "task_completed">;
|
||||
summaryText: string;
|
||||
linkedEntityType?: TaskEntityType | null;
|
||||
linkedEntityId?: string | null;
|
||||
}): Promise<void> {
|
||||
const linkedSubject = input.linkedEntityType ? ENTITY_TO_ACTIVITY_SUBJECT[input.linkedEntityType] : undefined;
|
||||
const useLinked = Boolean(linkedSubject && input.linkedEntityId);
|
||||
try {
|
||||
await logActivity({
|
||||
tenantId: input.ctx.tenantId,
|
||||
actorUserId: input.ctx.userId,
|
||||
actorKind: "user",
|
||||
activityType: input.activityType,
|
||||
subjectType: useLinked ? (linkedSubject as ActivitySubjectType) : "task",
|
||||
subjectId: useLinked ? (input.linkedEntityId as string) : input.taskId,
|
||||
summaryText: input.summaryText,
|
||||
relatedEntityRefs: useLinked ? [{ type: "task", id: input.taskId }] : undefined,
|
||||
});
|
||||
} catch {
|
||||
// Activity logging is best-effort; never break the user action.
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user