diff --git a/packages/dashboard/src/components/tasks/task-schedule-utils.ts b/packages/dashboard/src/components/tasks/task-schedule-utils.ts index bab632762..ff8f7023f 100644 --- a/packages/dashboard/src/components/tasks/task-schedule-utils.ts +++ b/packages/dashboard/src/components/tasks/task-schedule-utils.ts @@ -173,8 +173,8 @@ export const getScheduledTaskTitle = ( let remappedTaskName: string | undefined = undefined; if (supportedTasks) { for (const s of supportedTasks) { - if (s.task_definition_id === task.task_request.category) { - remappedTaskName = s.task_display_name; + if (s.taskDefinitionId === task.task_request.category) { + remappedTaskName = s.taskDisplayName; } } } diff --git a/packages/dashboard/src/managers/resource-manager-tasks.ts b/packages/dashboard/src/managers/resource-manager-tasks.ts index 42fcf40a1..b7277e03f 100644 --- a/packages/dashboard/src/managers/resource-manager-tasks.ts +++ b/packages/dashboard/src/managers/resource-manager-tasks.ts @@ -7,7 +7,7 @@ export class TaskResourceManager { this.supportedTasks = {}; if (supportedTasks) { for (const t of supportedTasks) { - this.supportedTasks[t.task_definition_id] = t; + this.supportedTasks[t.taskDefinitionId] = t; } } } diff --git a/packages/dashboard/src/managers/resource-manager.ts b/packages/dashboard/src/managers/resource-manager.ts index d5965a8fb..53f841d70 100644 --- a/packages/dashboard/src/managers/resource-manager.ts +++ b/packages/dashboard/src/managers/resource-manager.ts @@ -4,8 +4,8 @@ import { LogoResource, LogoResourceManager } from './resource-manager-logos'; import { RobotResource, RobotResourceManager } from './resource-manager-robots'; import { DefaultPatrolTaskDefinition, - DefaultSimpleDeliveryTaskDefinition, - DefaultCleanTaskDefinition, + DefaultDeliveryTaskDefinition, + DefaultComposeCleanTaskDefinition, TaskDefinition, DefaultCustomComposeTaskDefinition, } from 'react-components'; @@ -82,8 +82,8 @@ export default class ResourceManager { this.loggedInDisplayLevel = resources.loggedInDisplayLevel; this.supportedTasks = resources.supportedTasks || [ DefaultPatrolTaskDefinition, - DefaultSimpleDeliveryTaskDefinition, - DefaultCleanTaskDefinition, + DefaultDeliveryTaskDefinition, + DefaultComposeCleanTaskDefinition, DefaultCustomComposeTaskDefinition, ]; } diff --git a/packages/react-components/lib/tasks/create-task.tsx b/packages/react-components/lib/tasks/create-task.tsx index 3d2e0e46c..9a903fffb 100644 --- a/packages/react-components/lib/tasks/create-task.tsx +++ b/packages/react-components/lib/tasks/create-task.tsx @@ -40,12 +40,12 @@ import React from 'react'; import { Loading } from '..'; import { ConfirmationDialog, ConfirmationDialogProps } from '../confirmation-dialog'; import { - CleanTaskDescription, - CleanTaskForm, - DefaultCleanTaskDefinition, - makeCleanTaskBookingLabel, - makeDefaultCleanTaskDescription, -} from './types/clean'; + ComposeCleanTaskDescription, + ComposeCleanTaskForm, + DefaultComposeCleanTaskDefinition, + makeComposeCleanTaskBookingLabel, + makeDefaultComposeCleanTaskDescription, +} from './types/compose-clean'; import { DefaultCustomComposeTaskDefinition, CustomComposeTaskDescription, @@ -53,24 +53,24 @@ import { makeCustomComposeTaskBookingLabel, } from './types/custom-compose'; import { - DefaultSimpleDeliveryTaskDefinition, - SimpleDeliveryTaskDescription, - SimpleDeliveryTaskForm, - makeDefaultSimpleDeliveryTaskDescription, - makeSimpleDeliveryTaskBookingLabel, -} from './types/delivery-simple'; + DefaultDeliveryTaskDefinition, + DeliveryTaskDescription, + DeliveryTaskForm, + makeDefaultDeliveryTaskDescription, + makeDeliveryTaskBookingLabel, +} from './types/delivery'; import { DefaultDeliveryPickupTaskDefinition, DefaultDeliveryAreaPickupTaskDefinition, DefaultDeliverySequentialLotPickupTaskDefinition, DeliveryCustomTaskForm, DeliveryCustomTaskDescription, - DeliveryTaskDescription, - DeliveryTaskForm, + DeliveryPickupTaskDescription, + DeliveryPickupTaskForm, makeDefaultDeliveryCustomTaskDescription, - makeDefaultDeliveryTaskDescription, + makeDefaultDeliveryPickupTaskDescription, makeDeliveryCustomTaskBookingLabel, - makeDeliveryTaskBookingLabel, + makeDeliveryPickupTaskBookingLabel, } from './types/delivery-custom'; import { makeDefaultPatrolTask, @@ -79,22 +79,26 @@ import { PatrolTaskDescription, PatrolTaskForm, } from './types/patrol'; -import { serializeTaskBookingLabel } from './task-booking-label-utils'; +import { + getTaskBookingLabelFromTaskRequest, + serializeTaskBookingLabel, +} from './task-booking-label-utils'; export interface TaskDefinition { - task_definition_id: string; - task_display_name: string; + taskDefinitionId: string; + taskDisplayName: string; + requestCategory: string; } // If no task definition id is found in a past task (scheduled or favorite) -const DefaultTaskDefinitionId = DefaultCustomComposeTaskDefinition.task_definition_id; +const DefaultTaskDefinitionId = DefaultCustomComposeTaskDefinition.taskDefinitionId; type TaskDescription = - | DeliveryTaskDescription + | DeliveryPickupTaskDescription | DeliveryCustomTaskDescription | PatrolTaskDescription - | SimpleDeliveryTaskDescription - | CleanTaskDescription; + | DeliveryTaskDescription + | ComposeCleanTaskDescription; const classes = { title: 'dialogue-info-value', @@ -193,43 +197,45 @@ function FavoriteTask({ ); } -function defaultTaskDescription(taskName: string): TaskDescription | undefined { - switch (taskName) { - case 'delivery_pickup': - return makeDefaultDeliveryTaskDescription(); - case 'delivery_sequential_lot_pickup': - case 'delivery_area_pickup': - return makeDefaultDeliveryCustomTaskDescription(taskName); - case 'patrol': +function defaultTaskDescription(taskDefinitionId: string): TaskDescription | undefined { + switch (taskDefinitionId) { + case DefaultDeliveryPickupTaskDefinition.taskDefinitionId: + return makeDefaultDeliveryPickupTaskDescription(); + case DefaultDeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + case DefaultDeliveryAreaPickupTaskDefinition.taskDefinitionId: + return makeDefaultDeliveryCustomTaskDescription(taskDefinitionId); + case DefaultPatrolTaskDefinition.taskDefinitionId: return makeDefaultPatrolTask(); - case 'delivery': - return makeDefaultSimpleDeliveryTaskDescription(); - case 'clean': - return makeDefaultCleanTaskDescription(); + case DefaultDeliveryTaskDefinition.taskDefinitionId: + return makeDefaultDeliveryTaskDescription(); + case DefaultComposeCleanTaskDefinition.taskDefinitionId: + return makeDefaultComposeCleanTaskDescription(); default: return undefined; } } -function taskRequestCategory(task_definition_id: string): string | undefined { - switch (task_definition_id) { - case DefaultPatrolTaskDefinition.task_definition_id: - case DefaultSimpleDeliveryTaskDefinition.task_definition_id: - return task_definition_id; - case DefaultDeliveryPickupTaskDefinition.task_definition_id: - case DefaultDeliverySequentialLotPickupTaskDefinition.task_definition_id: - case DefaultDeliveryAreaPickupTaskDefinition.task_definition_id: - case DefaultCleanTaskDefinition.task_definition_id: - case DefaultCustomComposeTaskDefinition.task_definition_id: +// TODO: Move this into task defintion as well. To consider moving +// description.category into task definition too. +function taskRequestCategory(taskDefinitionId: string): string | undefined { + switch (taskDefinitionId) { + case DefaultPatrolTaskDefinition.taskDefinitionId: + case DefaultDeliveryTaskDefinition.taskDefinitionId: + return taskDefinitionId; + case DefaultDeliveryPickupTaskDefinition.taskDefinitionId: + case DefaultDeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + case DefaultDeliveryAreaPickupTaskDefinition.taskDefinitionId: + case DefaultComposeCleanTaskDefinition.taskDefinitionId: + case DefaultCustomComposeTaskDefinition.taskDefinitionId: return 'compose'; default: return undefined; } } -function defaultTaskRequest(task_definition_id: string): TaskRequest { - const category = taskRequestCategory(task_definition_id); - const description = defaultTaskDescription(task_definition_id); +function defaultTaskRequest(taskDefinitionId: string): TaskRequest { + const category = taskRequestCategory(taskDefinitionId); + const description = defaultTaskDescription(taskDefinitionId); return { category: category ?? 'compose', @@ -351,8 +357,8 @@ export function CreateTaskForm({ user, supportedTasks = [ DefaultPatrolTaskDefinition, - DefaultSimpleDeliveryTaskDefinition, - DefaultCleanTaskDefinition, + DefaultDeliveryTaskDefinition, + DefaultComposeCleanTaskDefinition, DefaultCustomComposeTaskDefinition, ], /* eslint-disable @typescript-eslint/no-unused-vars */ @@ -394,16 +400,21 @@ export function CreateTaskForm({ const [favoriteTaskTitleError, setFavoriteTaskTitleError] = React.useState(false); const [savingFavoriteTask, setSavingFavoriteTask] = React.useState(false); - const [taskDefinitionId, setTaskDefinitionId] = React.useState( - supportedTasks.length > 0 ? supportedTasks[0].task_definition_id : DefaultTaskDefinitionId, - ); const [taskRequest, setTaskRequest] = React.useState( () => requestTask ?? defaultTaskRequest( - supportedTasks.length > 0 ? supportedTasks[0].task_definition_id : DefaultTaskDefinitionId, + supportedTasks.length > 0 ? supportedTasks[0].taskDefinitionId : DefaultTaskDefinitionId, ), ); + const initialBookingLabel = requestTask ? getTaskBookingLabelFromTaskRequest(requestTask) : null; + const [taskDefinitionId, setTaskDefinitionId] = React.useState( + initialBookingLabel + ? initialBookingLabel.description.task_definition_id + : supportedTasks.length > 0 + ? supportedTasks[0].taskDefinitionId + : DefaultTaskDefinitionId, + ); const [submitting, setSubmitting] = React.useState(false); const [formFullyFilled, setFormFullyFilled] = React.useState(requestTask !== undefined || false); @@ -482,8 +493,8 @@ export function CreateTaskForm({ ); case 'delivery': return ( - handleTaskDescriptionChange('delivery', desc)} @@ -505,10 +516,10 @@ export function CreateTaskForm({ switch (taskRequest.description.category) { case 'clean': return ( - { + onChange={(desc: ComposeCleanTaskDescription) => { desc.category = taskRequest.description.category; handleTaskDescriptionChange('compose', desc); }} @@ -517,12 +528,12 @@ export function CreateTaskForm({ ); case 'delivery_pickup': return ( - { + onChange={(desc: DeliveryPickupTaskDescription) => { desc.category = taskRequest.description.category; desc.phases[0].activity.description.activities[1].description.category = taskRequest.description.category; @@ -558,6 +569,7 @@ export function CreateTaskForm({ }; const handleTaskTypeChange = (ev: React.ChangeEvent) => { const newType = ev.target.value; + console.log(`handleTaskTypeChange ${newType}`); setTaskDefinitionId(newType); if (newType === 'custom_compose') { @@ -612,7 +624,7 @@ export function CreateTaskForm({ let requestBookingLabel: TaskBookingLabel | null = null; switch (taskDefinitionId) { case 'delivery_pickup': - requestBookingLabel = makeDeliveryTaskBookingLabel(request.description); + requestBookingLabel = makeDeliveryPickupTaskBookingLabel(request.description); break; case 'delivery_sequential_lot_pickup': case 'delivery_area_pickup': @@ -622,9 +634,11 @@ export function CreateTaskForm({ requestBookingLabel = makePatrolTaskBookingLabel(request.description); break; case 'delivery': - requestBookingLabel = makeSimpleDeliveryTaskBookingLabel(request.description); + requestBookingLabel = makeDeliveryTaskBookingLabel(request.description); + break; case 'clean': - requestBookingLabel = makeCleanTaskBookingLabel(request.description); + requestBookingLabel = makeComposeCleanTaskBookingLabel(request.description); + break; case 'custom_compose': requestBookingLabel = makeCustomComposeTaskBookingLabel(); break; @@ -726,7 +740,7 @@ export function CreateTaskForm({ setTaskRequest( supportedTasks && supportedTasks.length > 0 - ? defaultTaskRequest(supportedTasks[0].task_definition_id) + ? defaultTaskRequest(supportedTasks[0].taskDefinitionId) : defaultTaskRequest('patrol'), ); setOpenFavoriteDialog(false); @@ -819,8 +833,11 @@ export function CreateTaskForm({ > {supportedTasks.map((taskDefinition) => { return ( - - {taskDefinition.task_display_name} + + {taskDefinition.taskDisplayName} ); })} diff --git a/packages/react-components/lib/tasks/task-booking-label-utils.tsx b/packages/react-components/lib/tasks/task-booking-label-utils.tsx index bfd4b0150..82b8983e4 100644 --- a/packages/react-components/lib/tasks/task-booking-label-utils.tsx +++ b/packages/react-components/lib/tasks/task-booking-label-utils.tsx @@ -1,6 +1,6 @@ import { ajv } from '../utils/schema-utils'; import schema from 'api-client/dist/schema'; -import type { TaskBookingLabel, TaskState } from 'api-client'; +import type { TaskBookingLabel, TaskRequest, TaskState } from 'api-client'; const validateTaskBookingLabel = ajv.compile(schema.components.schemas.TaskBookingLabel); @@ -44,3 +44,25 @@ export function getTaskBookingLabelFromTaskState(taskState: TaskState): TaskBook } return requestLabel; } + +export function getTaskBookingLabelFromTaskRequest( + taskRequest: TaskRequest, +): TaskBookingLabel | null { + if (!taskRequest.labels) { + return null; + } + + let requestLabel: TaskBookingLabel | null = null; + for (const label of taskRequest.labels) { + try { + const parsedLabel = getTaskBookingLabelFromJsonString(label); + if (parsedLabel) { + requestLabel = parsedLabel; + break; + } + } catch (e) { + continue; + } + } + return requestLabel; +} diff --git a/packages/react-components/lib/tasks/types/clean.tsx b/packages/react-components/lib/tasks/types/compose-clean.tsx similarity index 71% rename from packages/react-components/lib/tasks/types/clean.tsx rename to packages/react-components/lib/tasks/types/compose-clean.tsx index 22e4eae10..50f1fb33f 100644 --- a/packages/react-components/lib/tasks/types/clean.tsx +++ b/packages/react-components/lib/tasks/types/compose-clean.tsx @@ -3,9 +3,10 @@ import React from 'react'; import type { TaskBookingLabel } from 'api-client'; import { TaskDefinition } from '../create-task'; -export const DefaultCleanTaskDefinition: TaskDefinition = { - task_definition_id: 'clean', - task_display_name: 'Clean', +export const DefaultComposeCleanTaskDefinition: TaskDefinition = { + taskDefinitionId: 'compose-clean', + taskDisplayName: 'Clean', + requestCategory: 'compose', }; interface GoToPlaceActivity { @@ -26,7 +27,7 @@ interface CleanActivity { }; } -export interface CleanTaskDescription { +export interface ComposeCleanTaskDescription { category: string; phases: [ cleanPhase: { @@ -40,19 +41,21 @@ export interface CleanTaskDescription { ]; } -export function makeCleanTaskBookingLabel( - task_description: CleanTaskDescription, +export function makeComposeCleanTaskBookingLabel( + task_description: ComposeCleanTaskDescription, ): TaskBookingLabel { return { description: { - task_definition_id: 'clean', + task_definition_id: DefaultComposeCleanTaskDefinition.taskDefinitionId, destination: task_description.phases[0].activity.description.activities[1].description.description.zone, }, }; } -export function isCleanTaskDescriptionValid(taskDescription: CleanTaskDescription): boolean { +export function isComposeCleanTaskDescriptionValid( + taskDescription: ComposeCleanTaskDescription, +): boolean { const goToPlaceActivity = taskDescription.phases[0].activity.description.activities[0]; const cleanActivity = taskDescription.phases[0].activity.description.activities[1]; return ( @@ -62,7 +65,7 @@ export function isCleanTaskDescriptionValid(taskDescription: CleanTaskDescriptio ); } -export function makeDefaultCleanTaskDescription(): CleanTaskDescription { +export function makeDefaultComposeCleanTaskDescription(): ComposeCleanTaskDescription { return { category: 'clean', phases: [ @@ -95,21 +98,31 @@ export function makeDefaultCleanTaskDescription(): CleanTaskDescription { }; } -interface CleanTaskFormProps { - taskDesc: CleanTaskDescription; +export function makeComposeCleanTaskShortDescription( + desc: ComposeCleanTaskDescription, + displayName: string | undefined, +): string { + const cleanActivity = desc.phases[0].activity.description.activities[1]; + return `[${displayName ?? DefaultComposeCleanTaskDefinition.taskDisplayName}] zone [${ + cleanActivity.description.description.zone + }]`; +} + +interface ComposeCleanTaskFormProps { + taskDesc: ComposeCleanTaskDescription; cleaningZones: string[]; - onChange(cleanTaskDescription: CleanTaskDescription): void; + onChange(cleanTaskDescription: ComposeCleanTaskDescription): void; allowSubmit(allow: boolean): void; } -export function CleanTaskForm({ +export function ComposeCleanTaskForm({ taskDesc, cleaningZones, onChange, allowSubmit, -}: CleanTaskFormProps): React.JSX.Element { - const onInputChange = (desc: CleanTaskDescription) => { - allowSubmit(isCleanTaskDescriptionValid(desc)); +}: ComposeCleanTaskFormProps): React.JSX.Element { + const onInputChange = (desc: ComposeCleanTaskDescription) => { + allowSubmit(isComposeCleanTaskDescriptionValid(desc)); onChange(desc); }; diff --git a/packages/react-components/lib/tasks/types/custom-compose.tsx b/packages/react-components/lib/tasks/types/custom-compose.tsx index cb7454262..9e43a75ca 100644 --- a/packages/react-components/lib/tasks/types/custom-compose.tsx +++ b/packages/react-components/lib/tasks/types/custom-compose.tsx @@ -4,8 +4,9 @@ import type { TaskBookingLabel } from 'api-client'; import { TaskDefinition } from '../create-task'; export const DefaultCustomComposeTaskDefinition: TaskDefinition = { - task_definition_id: 'custom_compose', - task_display_name: 'Custom Compose Task', + taskDefinitionId: 'custom_compose', + taskDisplayName: 'Custom Compose Task', + requestCategory: 'compose', }; export type CustomComposeTaskDescription = string; @@ -13,11 +14,15 @@ export type CustomComposeTaskDescription = string; export function makeCustomComposeTaskBookingLabel(): TaskBookingLabel { return { description: { - task_definition_id: 'custom_compose', + task_definition_id: DefaultCustomComposeTaskDefinition.taskDefinitionId, }, }; } +export function makeCustomComposeTaskShortDescription(desc: CustomComposeTaskDescription): string { + return desc; +} + const isCustomTaskDescriptionValid = (taskDescription: string): boolean => { if (taskDescription.length === 0) { return false; diff --git a/packages/react-components/lib/tasks/types/delivery-custom.spec.tsx b/packages/react-components/lib/tasks/types/delivery-custom.spec.tsx index b2ac5ae46..06cb112cc 100644 --- a/packages/react-components/lib/tasks/types/delivery-custom.spec.tsx +++ b/packages/react-components/lib/tasks/types/delivery-custom.spec.tsx @@ -1,23 +1,23 @@ import { - makeDefaultDeliveryTaskDescription, - makeDefaultDeliveryCustomTaskDescription, + deliveryCustomInsertCartId, + deliveryCustomInsertDropoff, + deliveryCustomInsertOnCancel, + deliveryCustomInsertPickup, DeliveryCustomTaskDescription, deliveryInsertCartId, deliveryInsertDropoff, deliveryInsertOnCancel, deliveryInsertPickup, - DeliveryTaskDescription, - deliveryCustomInsertPickup, - deliveryCustomInsertCartId, - deliveryCustomInsertDropoff, - deliveryCustomInsertOnCancel, + DeliveryPickupTaskDescription, + makeDefaultDeliveryCustomTaskDescription, + makeDefaultDeliveryPickupTaskDescription, } from '.'; describe('Custom deliveries', () => { - it('delivery 1:1', () => { - let deliveryTaskDescription: DeliveryTaskDescription | null = null; + it('delivery pickup', () => { + let deliveryPickupTaskDescription: DeliveryPickupTaskDescription | null = null; try { - deliveryTaskDescription = JSON.parse(`{ + deliveryPickupTaskDescription = JSON.parse(`{ "category": "delivery_pickup", "phases": [ { @@ -113,13 +113,13 @@ describe('Custom deliveries', () => { } ] } - `) as DeliveryTaskDescription; + `) as DeliveryPickupTaskDescription; } catch (e) { - deliveryTaskDescription = null; + deliveryPickupTaskDescription = null; } - expect(deliveryTaskDescription).not.toEqual(null); + expect(deliveryPickupTaskDescription).not.toEqual(null); - let description = makeDefaultDeliveryTaskDescription(); + let description = makeDefaultDeliveryPickupTaskDescription(); description = deliveryInsertPickup(description, 'test_pickup_place', 'test_pickup_lot'); description = deliveryInsertCartId(description, 'test_cart_id'); description = deliveryInsertDropoff(description, 'test_dropoff_place'); @@ -128,13 +128,13 @@ describe('Custom deliveries', () => { 'test_waypoint_2', 'test_waypoint_3', ]); - expect(deliveryTaskDescription).toEqual(description); + expect(deliveryPickupTaskDescription).toEqual(description); }); it('delivery_sequential_lot_pickup', () => { - let deliveryTaskDescription: DeliveryCustomTaskDescription | null = null; + let deliveryCustomTaskDescription: DeliveryCustomTaskDescription | null = null; try { - deliveryTaskDescription = JSON.parse(`{ + deliveryCustomTaskDescription = JSON.parse(`{ "category": "delivery_sequential_lot_pickup", "phases": [ { @@ -232,9 +232,9 @@ describe('Custom deliveries', () => { } `) as DeliveryCustomTaskDescription; } catch (e) { - deliveryTaskDescription = null; + deliveryCustomTaskDescription = null; } - expect(deliveryTaskDescription).not.toEqual(null); + expect(deliveryCustomTaskDescription).not.toEqual(null); let description: DeliveryCustomTaskDescription = makeDefaultDeliveryCustomTaskDescription( 'delivery_sequential_lot_pickup', @@ -247,13 +247,13 @@ describe('Custom deliveries', () => { 'test_waypoint_2', 'test_waypoint_3', ]); - expect(deliveryTaskDescription).toEqual(description); + expect(deliveryCustomTaskDescription).toEqual(description); }); it('delivery_area_pickup', () => { - let deliveryTaskDescription: DeliveryCustomTaskDescription | null = null; + let deliveryCustomTaskDescription: DeliveryCustomTaskDescription | null = null; try { - deliveryTaskDescription = JSON.parse(`{ + deliveryCustomTaskDescription = JSON.parse(`{ "category": "delivery_area_pickup", "phases": [ { @@ -351,9 +351,9 @@ describe('Custom deliveries', () => { } `) as DeliveryCustomTaskDescription; } catch (e) { - deliveryTaskDescription = null; + deliveryCustomTaskDescription = null; } - expect(deliveryTaskDescription).not.toEqual(null); + expect(deliveryCustomTaskDescription).not.toEqual(null); let description: DeliveryCustomTaskDescription = makeDefaultDeliveryCustomTaskDescription('delivery_area_pickup'); @@ -365,6 +365,6 @@ describe('Custom deliveries', () => { 'test_waypoint_2', 'test_waypoint_3', ]); - expect(deliveryTaskDescription).toEqual(description); + expect(deliveryCustomTaskDescription).toEqual(description); }); }); diff --git a/packages/react-components/lib/tasks/types/delivery-custom.tsx b/packages/react-components/lib/tasks/types/delivery-custom.tsx index 5ad247dce..cb473ef9c 100644 --- a/packages/react-components/lib/tasks/types/delivery-custom.tsx +++ b/packages/react-components/lib/tasks/types/delivery-custom.tsx @@ -5,18 +5,21 @@ import type { TaskBookingLabel } from 'api-client'; import { TaskDefinition } from '../create-task'; export const DefaultDeliveryPickupTaskDefinition: TaskDefinition = { - task_definition_id: 'delivery_pickup', - task_display_name: 'Delivery - 1:1', + taskDefinitionId: 'delivery_pickup', + taskDisplayName: 'Delivery - 1:1', + requestCategory: 'compose', }; export const DefaultDeliverySequentialLotPickupTaskDefinition: TaskDefinition = { - task_definition_id: 'delivery_sequential_lot_pickup', - task_display_name: 'Delivery - Sequential lot pick up', + taskDefinitionId: 'delivery_sequential_lot_pickup', + taskDisplayName: 'Delivery - Sequential lot pick up', + requestCategory: 'compose', }; export const DefaultDeliveryAreaPickupTaskDefinition: TaskDefinition = { - task_definition_id: 'delivery_area_pickup', - task_display_name: 'Delivery - Area pick up', + taskDefinitionId: 'delivery_area_pickup', + taskDisplayName: 'Delivery - Area pick up', + requestCategory: 'compose', }; export interface LotPickupActivity { @@ -128,7 +131,7 @@ export interface DeliveryCustomTaskDescription { ]; } -export interface DeliveryTaskDescription { +export interface DeliveryPickupTaskDescription { category: string; phases: [ pickup_phase: CartPickupPhase, @@ -137,8 +140,8 @@ export interface DeliveryTaskDescription { ]; } -export function makeDeliveryTaskBookingLabel( - task_description: DeliveryTaskDescription, +export function makeDeliveryPickupTaskBookingLabel( + task_description: DeliveryPickupTaskDescription, ): TaskBookingLabel { const pickupDescription = task_description.phases[0].activity.description.activities[1].description.description; @@ -167,8 +170,75 @@ export function makeDeliveryCustomTaskBookingLabel( }; } -const isDeliveryTaskDescriptionValid = ( - taskDescription: DeliveryTaskDescription, +export function makeDeliveryPickupTaskShortDescription( + desc: DeliveryPickupTaskDescription, + taskDisplayName: string | undefined, +): string { + try { + const goToPickup: GoToPlaceActivity = desc.phases[0].activity.description.activities[0]; + const pickup: LotPickupActivity = desc.phases[0].activity.description.activities[1]; + const cartId = pickup.description.description.cart_id; + const goToDropoff: GoToPlaceActivity = desc.phases[1].activity.description.activities[0]; + + return `[${ + taskDisplayName ?? DefaultDeliveryPickupTaskDefinition.taskDisplayName + }] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; + } catch (e) { + try { + const descriptionString = JSON.stringify(desc); + console.error(descriptionString); + return descriptionString; + } catch (e) { + console.error( + `Failed to parse task description of delivery pickup task: ${(e as Error).message}`, + ); + } + } + + return '[Unknown] delivery pickup task'; +} + +export function makeDeliveryCustomTaskShortDescription( + desc: DeliveryCustomTaskDescription, + taskDisplayName: string | undefined, +): string { + try { + const goToPickup: GoToPlaceActivity = desc.phases[0].activity.description.activities[0]; + const pickup: ZonePickupActivity = desc.phases[0].activity.description.activities[1]; + const cartId = pickup.description.description.cart_id; + const goToDropoff: GoToPlaceActivity = desc.phases[1].activity.description.activities[0]; + + switch (desc.category) { + case DefaultDeliverySequentialLotPickupTaskDefinition.taskDefinitionId: { + return `[${ + taskDisplayName ?? DefaultDeliverySequentialLotPickupTaskDefinition.taskDisplayName + }] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; + } + case DefaultDeliveryAreaPickupTaskDefinition.taskDefinitionId: { + return `[${ + taskDisplayName ?? DefaultDeliveryAreaPickupTaskDefinition.taskDisplayName + }] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; + } + default: + return `[Unknown] type "${desc.category}"`; + } + } catch (e) { + try { + const descriptionString = JSON.stringify(desc); + console.error(descriptionString); + return descriptionString; + } catch (e) { + console.error( + `Failed to parse task description of delivery pickup task: ${(e as Error).message}`, + ); + } + } + + return '[Unknown] delivery pickup task'; +} + +const isDeliveryPickupTaskDescriptionValid = ( + taskDescription: DeliveryPickupTaskDescription, pickupPoints: Record, dropoffPoints: Record, ): boolean => { @@ -204,10 +274,10 @@ const isDeliveryCustomTaskDescriptionValid = ( }; export function deliveryInsertPickup( - taskDescription: DeliveryTaskDescription, + taskDescription: DeliveryPickupTaskDescription, pickupPlace: string, pickupLot: string, -): DeliveryTaskDescription { +): DeliveryPickupTaskDescription { taskDescription.phases[0].activity.description.activities[0].description = pickupPlace; taskDescription.phases[0].activity.description.activities[1].description.description.pickup_lot = pickupLot; @@ -215,26 +285,26 @@ export function deliveryInsertPickup( } export function deliveryInsertCartId( - taskDescription: DeliveryTaskDescription, + taskDescription: DeliveryPickupTaskDescription, cartId: string, -): DeliveryTaskDescription { +): DeliveryPickupTaskDescription { taskDescription.phases[0].activity.description.activities[1].description.description.cart_id = cartId; return taskDescription; } export function deliveryInsertDropoff( - taskDescription: DeliveryTaskDescription, + taskDescription: DeliveryPickupTaskDescription, dropoffPlace: string, -): DeliveryTaskDescription { +): DeliveryPickupTaskDescription { taskDescription.phases[1].activity.description.activities[0].description = dropoffPlace; return taskDescription; } export function deliveryInsertOnCancel( - taskDescription: DeliveryTaskDescription, + taskDescription: DeliveryPickupTaskDescription, onCancelPlaces: string[], -): DeliveryTaskDescription { +): DeliveryPickupTaskDescription { const goToOneOfThePlaces: GoToOneOfThePlacesActivity = { category: 'go_to_place', description: { @@ -267,27 +337,27 @@ export function deliveryInsertOnCancel( return taskDescription; } -interface DeliveryTaskFormProps { - taskDesc: DeliveryTaskDescription; +interface DeliveryPickupTaskFormProps { + taskDesc: DeliveryPickupTaskDescription; pickupPoints: Record; cartIds: string[]; dropoffPoints: Record; - onChange(taskDesc: DeliveryTaskDescription): void; + onChange(taskDesc: DeliveryPickupTaskDescription): void; allowSubmit(allow: boolean): void; } -export function DeliveryTaskForm({ +export function DeliveryPickupTaskForm({ taskDesc, pickupPoints = {}, cartIds = [], dropoffPoints = {}, onChange, allowSubmit, -}: DeliveryTaskFormProps): React.JSX.Element { +}: DeliveryPickupTaskFormProps): React.JSX.Element { const theme = useTheme(); const isScreenHeightLessThan800 = useMediaQuery('(max-height:800px)'); - const onInputChange = (desc: DeliveryTaskDescription) => { - allowSubmit(isDeliveryTaskDescriptionValid(desc, pickupPoints, dropoffPoints)); + const onInputChange = (desc: DeliveryPickupTaskDescription) => { + allowSubmit(isDeliveryPickupTaskDescriptionValid(desc, pickupPoints, dropoffPoints)); onChange(desc); }; @@ -633,7 +703,7 @@ export function DeliveryCustomTaskForm({ ); } -export function makeDefaultDeliveryTaskDescription(): DeliveryTaskDescription { +export function makeDefaultDeliveryPickupTaskDescription(): DeliveryPickupTaskDescription { return { category: 'delivery_pickup', phases: [ diff --git a/packages/react-components/lib/tasks/types/delivery-simple.tsx b/packages/react-components/lib/tasks/types/delivery.tsx similarity index 83% rename from packages/react-components/lib/tasks/types/delivery-simple.tsx rename to packages/react-components/lib/tasks/types/delivery.tsx index c279b8f29..53a52b157 100644 --- a/packages/react-components/lib/tasks/types/delivery-simple.tsx +++ b/packages/react-components/lib/tasks/types/delivery.tsx @@ -5,9 +5,10 @@ import React from 'react'; import type { TaskBookingLabel } from 'api-client'; import { TaskDefinition } from '../create-task'; -export const DefaultSimpleDeliveryTaskDefinition: TaskDefinition = { - task_definition_id: 'delivery', - task_display_name: 'Simple delivery', +export const DefaultDeliveryTaskDefinition: TaskDefinition = { + taskDefinitionId: 'delivery', + taskDisplayName: 'Delivery', + requestCategory: 'delivery', }; interface TaskPlace { @@ -19,17 +20,17 @@ interface TaskPlace { }; } -export interface SimpleDeliveryTaskDescription { +export interface DeliveryTaskDescription { pickup: TaskPlace; dropoff: TaskPlace; } -export function makeSimpleDeliveryTaskBookingLabel( - task_description: SimpleDeliveryTaskDescription, +export function makeDeliveryTaskBookingLabel( + task_description: DeliveryTaskDescription, ): TaskBookingLabel { return { description: { - task_definition_id: 'delivery', + task_definition_id: DefaultDeliveryTaskDefinition.taskDefinitionId, pickup: task_description.pickup.place, destination: task_description.dropoff.place, cart_id: task_description.pickup.payload.sku, @@ -46,13 +47,11 @@ function isTaskPlaceValid(place: TaskPlace): boolean { ); } -function isSimpleDeliveryTaskDescriptionValid( - taskDescription: SimpleDeliveryTaskDescription, -): boolean { +function isDeliveryTaskDescriptionValid(taskDescription: DeliveryTaskDescription): boolean { return isTaskPlaceValid(taskDescription.pickup) && isTaskPlaceValid(taskDescription.dropoff); } -export function makeDefaultSimpleDeliveryTaskDescription(): SimpleDeliveryTaskDescription { +export function makeDefaultDeliveryTaskDescription(): DeliveryTaskDescription { return { pickup: { place: '', @@ -73,24 +72,33 @@ export function makeDefaultSimpleDeliveryTaskDescription(): SimpleDeliveryTaskDe }; } -export interface SimpleDeliveryTaskFormProps { - taskDesc: SimpleDeliveryTaskDescription; +export function makeDeliveryTaskShortDescription( + desc: DeliveryTaskDescription, + displayName?: string, +): string { + return `[${displayName ?? DefaultDeliveryTaskDefinition.taskDisplayName}] Pickup [${ + desc.pickup.payload.sku + }] from [${desc.pickup.place}], dropoff [${desc.dropoff.payload.sku}] at [${desc.dropoff.place}]`; +} + +export interface DeliveryTaskFormProps { + taskDesc: DeliveryTaskDescription; pickupPoints: Record; dropoffPoints: Record; - onChange(taskDesc: SimpleDeliveryTaskDescription): void; + onChange(taskDesc: DeliveryTaskDescription): void; allowSubmit(allow: boolean): void; } -export function SimpleDeliveryTaskForm({ +export function DeliveryTaskForm({ taskDesc, pickupPoints = {}, dropoffPoints = {}, onChange, allowSubmit, -}: SimpleDeliveryTaskFormProps): React.JSX.Element { +}: DeliveryTaskFormProps): React.JSX.Element { const theme = useTheme(); - const onInputChange = (desc: SimpleDeliveryTaskDescription) => { - allowSubmit(isSimpleDeliveryTaskDescriptionValid(desc)); + const onInputChange = (desc: DeliveryTaskDescription) => { + allowSubmit(isDeliveryTaskDescriptionValid(desc)); onChange(desc); }; diff --git a/packages/react-components/lib/tasks/types/index.ts b/packages/react-components/lib/tasks/types/index.ts index a0f628aac..e00fcdb75 100644 --- a/packages/react-components/lib/tasks/types/index.ts +++ b/packages/react-components/lib/tasks/types/index.ts @@ -1,6 +1,6 @@ -export * from './clean'; +export * from './compose-clean'; export * from './custom-compose'; export * from './delivery-custom'; -export * from './delivery-simple'; +export * from './delivery'; export * from './patrol'; export * from './utils'; diff --git a/packages/react-components/lib/tasks/types/patrol.tsx b/packages/react-components/lib/tasks/types/patrol.tsx index 31e9f9490..407228376 100644 --- a/packages/react-components/lib/tasks/types/patrol.tsx +++ b/packages/react-components/lib/tasks/types/patrol.tsx @@ -18,8 +18,9 @@ import { TaskDefinition } from '../create-task'; import type { TaskBookingLabel } from 'api-client'; export const DefaultPatrolTaskDefinition: TaskDefinition = { - task_definition_id: 'patrol', - task_display_name: 'Patrol', + taskDefinitionId: 'patrol', + taskDisplayName: 'Patrol', + requestCategory: 'patrol', }; export interface PatrolTaskDescription { @@ -32,7 +33,7 @@ export function makePatrolTaskBookingLabel( ): TaskBookingLabel { return { description: { - task_definition_id: 'patrol', + task_definition_id: DefaultPatrolTaskDefinition.taskDefinitionId, destination: task_description.places[task_description.places.length - 1], }, }; @@ -57,6 +58,18 @@ export function makeDefaultPatrolTask(): PatrolTaskDescription { }; } +export function makePatrolTaskShortDescription( + desc: PatrolTaskDescription, + displayName?: string, +): string { + console.log(desc); + + const formattedPlaces = desc.places.map((place: string) => `[${place}]`); + return `[${displayName ?? DefaultPatrolTaskDefinition.taskDisplayName}] [${ + desc.rounds + }] round/s, along ${formattedPlaces.join(', ')}`; +} + interface PlaceListProps { places: string[]; onClick(places_index: number): void; diff --git a/packages/react-components/lib/tasks/types/utils.ts b/packages/react-components/lib/tasks/types/utils.ts index 5b06a1e5e..ed9c49b7b 100644 --- a/packages/react-components/lib/tasks/types/utils.ts +++ b/packages/react-components/lib/tasks/types/utils.ts @@ -1,5 +1,24 @@ import { TaskRequest } from 'api-client'; -import { GoToPlaceActivity, LotPickupActivity } from './delivery-custom'; +import { DefaultPatrolTaskDefinition, makePatrolTaskShortDescription } from './patrol'; +import { + DefaultDeliveryAreaPickupTaskDefinition, + DefaultDeliveryPickupTaskDefinition, + DefaultDeliverySequentialLotPickupTaskDefinition, +} from './delivery-custom'; +import { getTaskBookingLabelFromTaskRequest } from '../task-booking-label-utils'; +import { + DefaultComposeCleanTaskDefinition, + makeComposeCleanTaskShortDescription, +} from './compose-clean'; +import { DefaultDeliveryTaskDefinition, makeDeliveryTaskShortDescription } from './delivery'; +import { + makeDeliveryPickupTaskShortDescription, + makeDeliveryCustomTaskShortDescription, +} from './delivery-custom'; +import { + DefaultCustomComposeTaskDefinition, + makeCustomComposeTaskShortDescription, +} from './custom-compose'; export function isNonEmptyString(value: string): boolean { return value.length > 0; @@ -9,90 +28,48 @@ export function isPositiveNumber(value: number): boolean { return value > 0; } +function rawStringFromJsonRequest(taskRequest: TaskRequest): string | undefined { + try { + const requestString = JSON.stringify(taskRequest); + console.error( + `Task does not have a identifying label, failed to generate short description of task: ${requestString}`, + ); + return requestString; + } catch (e) { + console.error( + `Failed to parse description of task of category: ${taskRequest.category}: ${ + (e as Error).message + }`, + ); + return undefined; + } +} + export function getShortDescription( taskRequest: TaskRequest, - remappedTaskName?: string, + taskDisplayName?: string, ): string | undefined { - switch (taskRequest.category) { - case 'patrol': { - const formattedPlaces = taskRequest.description.places.map((place: string) => `[${place}]`); - return `[${remappedTaskName ?? 'Patrol'}] [${ - taskRequest.description.rounds - }] round/s, along ${formattedPlaces.join(', ')}`; - } - case 'delivery': { - return `[${remappedTaskName ?? 'Delivery'}] Pickup [${ - taskRequest.description.pickup.payload.sku - }] from [${taskRequest.description.pickup.place}], dropoff [${ - taskRequest.description.dropoff.payload.sku - }] at [${taskRequest.description.dropoff.place}]`; - } + const bookingLabel = getTaskBookingLabelFromTaskRequest(taskRequest); + if (!bookingLabel) { + return rawStringFromJsonRequest(taskRequest); } - // FIXME: This block is only for non-primary task cateogries, that utilize - // compose. - if (taskRequest.description.category === 'clean') { - const cleanActivity = taskRequest.description.phases[0].activity.description.activities[1]; - return `[${remappedTaskName ?? 'Clean'}] zone [${cleanActivity.description.description.zone}]`; - } - - // This section is only valid for custom delivery types - // FIXME: This block looks like it makes assumptions about the structure of - // the task description in order to parse it, but it is following the - // statically defined description (object) at the top of this file. The - // descriptions should be replaced by a schema in general, however the better - // approach now should be to make each task description testable and in charge - // of their own short descriptions. - try { - const goToPickup: GoToPlaceActivity = - taskRequest.description.phases[0].activity.description.activities[0]; - const pickup: LotPickupActivity = - taskRequest.description.phases[0].activity.description.activities[1]; - const cartId = pickup.description.description.cart_id; - const goToDropoff: GoToPlaceActivity = - taskRequest.description.phases[1].activity.description.activities[0]; - - switch (taskRequest.description.category) { - case 'delivery_pickup': { - return `[${remappedTaskName ?? 'Delivery - 1:1'}] payload [${cartId}] from [${ - goToPickup.description - }] to [${goToDropoff.description}]`; - } - case 'delivery_sequential_lot_pickup': { - return `[${ - remappedTaskName ?? 'Delivery - Sequential lot pick up' - }] payload [${cartId}] from [${goToPickup.description}] to [${goToDropoff.description}]`; - } - case 'delivery_area_pickup': { - return `[${remappedTaskName ?? 'Delivery - Area pick up'}] payload [${cartId}] from [${ - goToPickup.description - }] to [${goToDropoff.description}]`; - } - default: - return `[Unknown] type "${taskRequest.description.category}"`; - } - } catch (e) { - if (e instanceof TypeError) { - console.error(`Failed to parse custom delivery: ${e.message}`); - } else { - console.error( - `Failed to generate short description from task of category: ${taskRequest.category}: ${ - (e as Error).message - }`, - ); - } - - try { - const descriptionString = JSON.stringify(taskRequest.description); - console.error(descriptionString); - return descriptionString; - } catch (e) { - console.error( - `Failed to parse description of task of category: ${taskRequest.category}: ${ - (e as Error).message - }`, - ); - return undefined; - } + const taskDefinitionId = bookingLabel.description.task_definition_id; + switch (taskDefinitionId) { + case DefaultPatrolTaskDefinition.taskDefinitionId: + return makePatrolTaskShortDescription(taskRequest.description, taskDisplayName); + case DefaultDeliveryTaskDefinition.taskDefinitionId: + return makeDeliveryTaskShortDescription(taskRequest.description, taskDisplayName); + case DefaultComposeCleanTaskDefinition.taskDefinitionId: + return makeComposeCleanTaskShortDescription(taskRequest.description, taskDisplayName); + case DefaultDeliveryPickupTaskDefinition.taskDefinitionId: + return makeDeliveryPickupTaskShortDescription(taskRequest.description, taskDisplayName); + case DefaultDeliverySequentialLotPickupTaskDefinition.taskDefinitionId: + case DefaultDeliveryAreaPickupTaskDefinition.taskDefinitionId: + return makeDeliveryCustomTaskShortDescription(taskRequest.description, taskDisplayName); + case DefaultCustomComposeTaskDefinition.taskDefinitionId: + return makeCustomComposeTaskShortDescription(taskRequest.description); + default: + return `[Unknown] type "${taskRequest.description.category}"`; } }