- Added `useAssetPickerController` for managing asset selection logic. - Created `AssetSelectProps` type for defining asset selection properties. - Implemented `AssetPickerDialog` for displaying the asset selection interface. - Developed `AssetPickerList` for rendering the list of selectable assets. - Introduced `AssetSelect` component for integrating the asset picker into forms. - Created `AssetList` component for shared asset listing functionality. - Added tests for workstation creation logic to ensure proper validation. - Implemented workstation asset fields and association fields for managing workstation assets. - Developed workstation lifecycle dialog for managing work profile states. - Added form progress component for visualizing multi-step forms.
107 lines
2.8 KiB
TypeScript
107 lines
2.8 KiB
TypeScript
import type { UniqueID } from "@repo/rdx-ddd";
|
|
import { AggregateRoot } from "@repo/rdx-ddd";
|
|
import { Result } from "@repo/rdx-utils";
|
|
import { z } from "zod/v4";
|
|
|
|
import { WorkProfileValues } from "./value-objects/work-profile-values.vo";
|
|
import { WorkProfileRevision } from "./work-profile-revision.entity";
|
|
import { WorkProfileError } from "./work-profile-values";
|
|
|
|
type WorkProfileProps = Readonly<{
|
|
companyId: UniqueID;
|
|
values: WorkProfileValues;
|
|
history: readonly WorkProfileRevision[];
|
|
}>;
|
|
|
|
export class WorkProfile extends AggregateRoot<WorkProfileProps> {
|
|
private constructor(props: WorkProfileProps, id?: UniqueID) {
|
|
super(Object.freeze({ ...props, history: Object.freeze([...props.history]) }), id);
|
|
}
|
|
|
|
static create(
|
|
companyId: UniqueID,
|
|
values: WorkProfileValues,
|
|
actorId: UniqueID,
|
|
recordedAt: string
|
|
): Result<WorkProfile, WorkProfileError> {
|
|
if (!z.iso.datetime().safeParse(recordedAt).success) {
|
|
return Result.fail(new WorkProfileError("INVALID_REQUEST"));
|
|
}
|
|
|
|
return Result.ok(
|
|
new WorkProfile({
|
|
companyId,
|
|
values,
|
|
history: [WorkProfileRevision.create({ values, actorId, recordedAt })],
|
|
})
|
|
);
|
|
}
|
|
|
|
static rehydrate(props: WorkProfileProps, id: UniqueID) {
|
|
return new WorkProfile(props, id);
|
|
}
|
|
|
|
get companyId() {
|
|
return this.props.companyId;
|
|
}
|
|
|
|
get values() {
|
|
return this.props.values;
|
|
}
|
|
|
|
get history() {
|
|
return this.props.history;
|
|
}
|
|
|
|
deactivate(actorId: UniqueID, recordedAt: string): Result<WorkProfile, WorkProfileError> {
|
|
const values = WorkProfileValues.create({ ...this.values.toPrimitive(), active: false });
|
|
|
|
return values.isSuccess ? this.update(values.data, actorId, recordedAt) : values;
|
|
}
|
|
|
|
checkRemoval({
|
|
hasConfigurations,
|
|
}: {
|
|
hasConfigurations: boolean;
|
|
}): Result<void, WorkProfileError> {
|
|
if (this.values.active) {
|
|
return Result.fail(new WorkProfileError("PROFILE_ACTIVE"));
|
|
}
|
|
if (hasConfigurations) {
|
|
return Result.fail(new WorkProfileError("PROFILE_IN_USE"));
|
|
}
|
|
|
|
return Result.ok(undefined);
|
|
}
|
|
|
|
update(
|
|
values: WorkProfileValues,
|
|
actorId: UniqueID,
|
|
recordedAt: string
|
|
): Result<WorkProfile, WorkProfileError> {
|
|
if (this.values.equals(values)) {
|
|
return Result.ok(this);
|
|
}
|
|
if (!this.values.sameTarget(values)) {
|
|
return Result.fail(new WorkProfileError("PROFILE_TARGET_IMMUTABLE"));
|
|
}
|
|
if (
|
|
!z.iso.datetime().safeParse(recordedAt).success ||
|
|
recordedAt < (this.history.at(-1)?.recordedAt ?? "")
|
|
) {
|
|
return Result.fail(new WorkProfileError("INVALID_REQUEST"));
|
|
}
|
|
|
|
return Result.ok(
|
|
new WorkProfile(
|
|
{
|
|
...this.props,
|
|
values,
|
|
history: [...this.history, WorkProfileRevision.create({ values, actorId, recordedAt })],
|
|
},
|
|
this.id
|
|
)
|
|
);
|
|
}
|
|
}
|