- Implemented ProcessFeedback component for error handling in process forms. - Created ProcessHelp component to provide contextual assistance for users. - Added internationalization support for process-related strings in Spanish and English. - Developed ProcessStartForm for initiating new processes with person and template selection. - Introduced ProcessTemplateForm for creating and editing process templates. - Built ProcessTemplatesPage to display and manage process templates. - Enhanced ProcessesPage to list current processes and allow for creation and detail viewing. - Added unit tests for various components to ensure functionality and correctness.
91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
|
|
import { UniqueID } from "@repo/rdx-ddd";
|
|
import { Maybe } from "@repo/rdx-utils";
|
|
import { z } from "zod/v4";
|
|
|
|
import { ProcessTemplate } from "../../domain/process-template.aggregate";
|
|
import { ProcessTemplateRevision } from "../../domain/process-template-revision.entity";
|
|
import { ProcessTemplateInputSchema } from "../../domain/process-values";
|
|
import { ProcessTemplateValues } from "../../domain/value-objects/process-template-values";
|
|
|
|
import { persistedJson } from "./work-profile-persistence.mapper";
|
|
export type ProcessTemplateRow = { id: string; nameKey: string; values: unknown };
|
|
export type ProcessTemplateRevisionRow = {
|
|
id: string;
|
|
itemId: string;
|
|
position: number;
|
|
actorId: string | null;
|
|
recordedAt: string;
|
|
values: unknown;
|
|
};
|
|
function identity(value: string) {
|
|
const parsed = UniqueID.create(value);
|
|
if (!parsed.isSuccess) {
|
|
throw new Error("Identidad de proceso inconsistente.");
|
|
}
|
|
return parsed.data;
|
|
}
|
|
function mapped(raw: unknown) {
|
|
const values = ProcessTemplateValues.create(ProcessTemplateInputSchema.parse(persistedJson(raw)));
|
|
if (!values.isSuccess) {
|
|
throw new Error("Valores de proceso inconsistentes.");
|
|
}
|
|
return values.data;
|
|
}
|
|
export class ProcessTemplatePersistenceMapper {
|
|
toDomain(row: ProcessTemplateRow, revisions: readonly ProcessTemplateRevisionRow[]) {
|
|
const values = mapped(row.values);
|
|
const ordered = [...revisions].sort((a, b) => a.position - b.position);
|
|
const history = ordered.map((r, index) => {
|
|
if (
|
|
r.itemId !== row.id ||
|
|
r.position !== index ||
|
|
!z.iso.datetime().safeParse(r.recordedAt).success ||
|
|
r.recordedAt < (ordered[index - 1]?.recordedAt ?? "") ||
|
|
(!r.actorId && index !== 0)
|
|
) {
|
|
throw new Error("Historial de proceso inconsistente.");
|
|
}
|
|
return ProcessTemplateRevision.rehydrate(
|
|
{
|
|
values: mapped(r.values),
|
|
actorId: r.actorId ? Maybe.some(identity(r.actorId)) : Maybe.none(),
|
|
recordedAt: r.recordedAt,
|
|
},
|
|
identity(r.id)
|
|
);
|
|
});
|
|
if (
|
|
!(history.length && history.at(-1)?.values.equals(values)) ||
|
|
row.nameKey !== processTemplateNameKey(values.name.toPrimitive())
|
|
) {
|
|
throw new Error("Raíz de proceso inconsistente.");
|
|
}
|
|
|
|
return ProcessTemplate.rehydrate({ values, history }, identity(row.id));
|
|
}
|
|
toPersistence(item: ProcessTemplate) {
|
|
const values = item.values.toPrimitive();
|
|
return {
|
|
root: {
|
|
id: item.id.toPrimitive(),
|
|
nameKey: processTemplateNameKey(values.name),
|
|
values: JSON.stringify(values),
|
|
},
|
|
revisions: item.history.map((r, position) => ({
|
|
id: r.id.toPrimitive(),
|
|
itemId: item.id.toPrimitive(),
|
|
position,
|
|
actorId: r.actorId.getOrUndefined()?.toPrimitive() ?? null,
|
|
recordedAt: r.recordedAt,
|
|
values: JSON.stringify(r.values.toPrimitive()),
|
|
})),
|
|
};
|
|
}
|
|
}
|
|
|
|
export function processTemplateNameKey(name: string) {
|
|
return createHash("sha256").update(name.toLowerCase()).digest("hex");
|
|
}
|