Files
plant-man/plant-manager-web/src/form-state.ts
T

327 lines
9.2 KiB
TypeScript

import type {
ActionResource,
ActionResourcePayload,
CareActivity,
CareActivityPayload,
CareAction,
CareActionPayload,
BulkPlantCareSchedulePayload,
Recipe,
RecipePayload,
Plant,
PlantCareScheduleRule,
AssignPlantFlagPayload,
PlantPayload,
PlantFlagDefinition,
PlantFlagDefinitionPayload,
PlantGroup,
PlantGroupPayload,
PlantLocation,
PlantLocationPayload,
RecipeMeasurementMode,
ScheduleEndsMode,
ScheduleRecurrenceMode,
ScheduleRepeatUnit,
PlantTaxon,
} from './domain';
export const emptyPlantForm = {
nickname: '',
birthday: '',
taxonId: '',
locationId: '',
};
export const emptyLocationForm = {
name: '',
notes: '',
};
export const emptyPlantGroupForm = {
name: '',
plantIds: [] as string[],
notes: '',
};
export const emptyActionForm = {
name: '',
description: '',
};
export const emptyResourceForm = {
name: '',
notes: '',
};
export const emptyActivityForm = {
name: '',
actions: [] as CareActivityActionFormState[],
notes: '',
};
export const emptyRecipeForm = {
name: '',
measurementMode: 'quantity' as RecipeMeasurementMode,
outputResourceName: '',
components: [] as RecipeComponentFormState[],
notes: '',
};
export type CareActivityActionResourceFormState = {
actionResourceId: string;
quantity: string;
unit: string;
notes: string;
};
export type CareActivityActionFormState = {
careActionId: string;
resources: CareActivityActionResourceFormState[];
};
export type RecipeComponentFormState = {
actionResourceId: string;
quantity: string;
unit: string;
notes: string;
};
export const emptyFlagDefinitionForm = {
name: '',
color: '#F1F2F4',
};
export const emptyPlantFlagForm = {
plantFlagDefinitionId: '',
startedOn: '',
notes: '',
};
export const emptyBulkScheduleForm = {
careActivityId: '',
everyDays: '7',
scheduledFor: '',
recurrenceMode: 'weekly',
repeatEvery: '1',
repeatUnit: 'week',
repeatOnDays: [] as string[],
endsMode: 'never',
endsOn: '',
endsAfterOccurrences: '12',
plantIds: [] as string[],
};
export type PlantFormState = typeof emptyPlantForm;
export type LocationFormState = typeof emptyLocationForm;
export type PlantGroupFormState = typeof emptyPlantGroupForm;
export type ActionFormState = typeof emptyActionForm;
export type ResourceFormState = typeof emptyResourceForm;
export type ActivityFormState = typeof emptyActivityForm;
export type RecipeFormState = typeof emptyRecipeForm;
export type FlagDefinitionFormState = typeof emptyFlagDefinitionForm;
export type PlantFlagFormState = typeof emptyPlantFlagForm;
export type BulkScheduleFormState = typeof emptyBulkScheduleForm;
export type View = 'home' | 'plant-management' | 'care' | 'schedules' | 'taxa' | 'locations' | 'groups' | 'actions' | 'resources' | 'recipes' | 'activities' | 'flags' | 'import-export';
export function toPlantForm(plant: Plant): PlantFormState {
return {
nickname: plant.nickname,
birthday: plant.birthday ?? '',
taxonId: plant.taxonId === null ? '' : String(plant.taxonId),
locationId: plant.locationId === null ? '' : String(plant.locationId),
};
}
export function toPlantPayload(form: PlantFormState): PlantPayload {
return {
nickname: form.nickname.trim(),
birthday: form.birthday || null,
taxonId: form.taxonId ? Number(form.taxonId) : null,
locationId: form.locationId ? Number(form.locationId) : null,
careSchedules: null,
};
}
export function toLocationForm(location: PlantLocation): LocationFormState {
return {
name: location.name,
notes: location.notes ?? '',
};
}
export function toLocationPayload(form: LocationFormState): PlantLocationPayload {
return {
name: form.name.trim(),
notes: form.notes.trim() || null,
};
}
export function toPlantGroupForm(group: PlantGroup): PlantGroupFormState {
return {
name: group.name,
plantIds: group.plants.map((plant) => String(plant.id)),
notes: group.notes ?? '',
};
}
export function toPlantGroupPayload(form: PlantGroupFormState): PlantGroupPayload {
return {
name: form.name.trim(),
plantIds: form.plantIds.map((id) => Number(id)),
notes: form.notes.trim() || null,
};
}
export function toActionForm(action: CareAction): ActionFormState {
return {
name: action.name,
description: action.description ?? '',
};
}
export function toActionPayload(form: ActionFormState): CareActionPayload {
return {
name: form.name.trim(),
description: form.description.trim() || null,
};
}
export function toResourceForm(resource: ActionResource): ResourceFormState {
return {
name: resource.name,
notes: resource.notes ?? '',
};
}
export function toResourcePayload(form: ResourceFormState): ActionResourcePayload {
return {
name: form.name.trim(),
notes: form.notes.trim() || null,
};
}
export function toActivityForm(activity: CareActivity): ActivityFormState {
return {
name: activity.name,
actions: activity.actions.map((action) => ({
careActionId: String(action.careActionId),
resources: action.resources.map((resource) => ({
actionResourceId: String(resource.actionResourceId),
quantity: resource.quantity === null ? '' : String(resource.quantity),
unit: resource.unit ?? '',
notes: resource.notes ?? '',
})),
})),
notes: activity.notes ?? '',
};
}
export function toActivityPayload(form: ActivityFormState): CareActivityPayload {
return {
name: form.name.trim(),
actions: form.actions.map((action) => ({
careActionId: Number(action.careActionId),
resources: action.resources.map((resource) => ({
actionResourceId: Number(resource.actionResourceId),
quantity: resource.quantity.trim() ? Number(resource.quantity) : null,
unit: resource.unit.trim() || null,
notes: resource.notes.trim() || null,
})),
})),
notes: form.notes.trim() || null,
};
}
export function toRecipeForm(recipe: Recipe): RecipeFormState {
return {
name: recipe.name,
measurementMode: recipe.measurementMode,
outputResourceName: recipe.outputResource?.name ?? '',
components: recipe.components.map((component) => ({
actionResourceId: String(component.actionResourceId),
quantity: component.quantity === null ? '' : String(component.quantity),
unit: component.unit ?? '',
notes: component.notes ?? '',
})),
notes: recipe.notes ?? '',
};
}
export function toRecipePayload(form: RecipeFormState): RecipePayload {
return {
name: form.name.trim(),
measurementMode: form.measurementMode,
outputResourceName: form.outputResourceName.trim() || null,
components: form.components.map((component) => ({
actionResourceId: Number(component.actionResourceId),
quantity: component.quantity.trim() ? Number(component.quantity) : null,
unit: form.measurementMode === 'quantity' ? component.unit.trim() || null : '%',
notes: component.notes.trim() || null,
})),
notes: form.notes.trim() || null,
};
}
export function toFlagDefinitionForm(flag: PlantFlagDefinition): FlagDefinitionFormState {
return {
name: flag.name,
color: flag.color,
};
}
export function toFlagDefinitionPayload(form: FlagDefinitionFormState): PlantFlagDefinitionPayload {
return {
name: form.name.trim(),
color: form.color.trim() || null,
};
}
export function toPlantFlagPayload(form: PlantFlagFormState): AssignPlantFlagPayload {
return {
plantFlagDefinitionId: Number(form.plantFlagDefinitionId),
startedOn: form.startedOn || null,
notes: form.notes.trim() || null,
};
}
export function toBulkSchedulePayload(form: BulkScheduleFormState): BulkPlantCareSchedulePayload {
return {
plantIds: form.plantIds.map((id) => Number(id)),
careActivityId: Number(form.careActivityId),
everyDays: Number(form.everyDays),
scheduledFor: form.scheduledFor || null,
recurrenceMode: form.recurrenceMode as ScheduleRecurrenceMode,
repeatEvery: Number(form.repeatEvery),
repeatUnit: form.repeatUnit as ScheduleRepeatUnit,
repeatOnDays: form.repeatOnDays.length > 0 ? form.repeatOnDays.join(',') : null,
endsMode: (form.recurrenceMode === 'none' ? 'after' : form.endsMode) as ScheduleEndsMode,
endsOn: form.endsMode === 'on' ? form.endsOn || null : null,
endsAfterOccurrences: form.recurrenceMode === 'none'
? 1
: form.endsMode === 'after'
? Number(form.endsAfterOccurrences)
: null,
};
}
export function toBulkScheduleForm(schedule: PlantCareScheduleRule): BulkScheduleFormState {
return {
careActivityId: String(schedule.careActivityId),
everyDays: String(schedule.everyDays),
scheduledFor: schedule.scheduledFor ?? '',
recurrenceMode: schedule.recurrenceMode,
repeatEvery: String(schedule.repeatEvery),
repeatUnit: schedule.repeatUnit,
repeatOnDays: schedule.repeatOnDays ? schedule.repeatOnDays.split(',').filter(Boolean) : [],
endsMode: schedule.endsMode,
endsOn: schedule.endsOn ?? '',
endsAfterOccurrences: schedule.endsAfterOccurrences === null ? '12' : String(schedule.endsAfterOccurrences),
plantIds: schedule.plants.map((plant) => String(plant.id)),
};
}
export function formatTaxon(taxon: PlantTaxon) {
const botanical = `${taxon.genus} ${taxon.species}`.trim();
return taxon.name === botanical ? taxon.name : `${taxon.name} (${botanical})`;
}