From f6467113ce114fc9ce6d561aaafb2a1e75b5c628 Mon Sep 17 00:00:00 2001 From: iacopolea Date: Thu, 19 Feb 2026 15:12:40 +0100 Subject: [PATCH 01/16] feat: enhance task list creation by incorporating module information into user prompts --- .../Component/parts/CreateTaskListsWithAI.tsx | 56 +++++++++++++++++-- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx index ad112900d..2b19ceb42 100644 --- a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx +++ b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx @@ -1,21 +1,33 @@ import { + AccordionNew, Button, - Textarea, FormField, - Label, - Message, Input, - AccordionNew, + Label, LG, + Message, + Textarea, } from '@appquality/unguess-design-system'; import { useEffect, useMemo, useState } from 'react'; import { useParams } from 'react-router-dom'; +import { useAppSelector } from 'src/app/hooks'; import { appTheme } from 'src/app/theme'; import { useGetServicesApiKJobsByJobIdQuery, usePostServicesApiKUsecasesMutation, } from 'src/features/api'; +// constants +const MODULES_TO_PROMPT = [ + 'title', + 'goal', + 'out_of_scope', + 'tasks', + 'language', + 'touchpoints', +]; +const MAX_PROMPT_LENGTH = 102300; + export const CreateTaskListsWithAI = () => { const { planId } = useParams(); const MIN_LENGTH = 1; @@ -23,9 +35,12 @@ export const CreateTaskListsWithAI = () => { const [taskCount, setTaskCount] = useState(3); const [isCreating, setIsCreating] = useState(false); const [pollingInterval, setPollingInterval] = useState(0); + + const { records } = useAppSelector((state) => state.planModules); + + // API hooks const [postServicesApiKUsecases, { data: jobData, error: postError }] = usePostServicesApiKUsecasesMutation(); - const { data: useCasesData, error: useCasesError } = useGetServicesApiKJobsByJobIdQuery( { jobId: jobData?.jobId || '' }, @@ -35,11 +50,13 @@ export const CreateTaskListsWithAI = () => { } ); + // Form inputs and button are disabled while creating the task lists or while polling for results const isFormDisabled = useMemo( () => isCreating || pollingInterval > 0, [isCreating, pollingInterval] ); + // Button is disabled if form is disabled or if user prompt is too short const isButtonDisabled = useMemo( () => userPrompt.length < MIN_LENGTH || isFormDisabled, [userPrompt, isFormDisabled] @@ -47,11 +64,21 @@ export const CreateTaskListsWithAI = () => { const handleClick = async () => { setIsCreating(true); + // gather modules info to prepend to the user prompt + const modulesInfo = Object.entries(records) + .filter(([key]) => MODULES_TO_PROMPT.includes(key)) + .map( + ([key, item]) => + `Module: ${key}, Config: ${JSON.stringify(processItemOutput(item))}` + ) + .join('\n'); + const fullPrompt = `User prompt:\n${userPrompt}\nModules info:\n${modulesInfo}`; + await postServicesApiKUsecases({ body: { planId: planId || '', count: taskCount, - requirements: userPrompt, + requirements: fullPrompt.slice(0, MAX_PROMPT_LENGTH), }, }); setIsCreating(false); @@ -153,3 +180,20 @@ export const CreateTaskListsWithAI = () => { ); }; + +// Helper function to process module item output for better readability in the prompt +const processItemOutput = (item: any): any => { + if (Array.isArray(item)) { + // If it's an array, process each item in the array + return item.map((subItem) => processItemOutput(subItem)); + } else if (typeof item === 'object' && item !== null) { + // Process object: strip out id and recursively process nested values + const { id, ...rest } = item; + const processedRest: any = {}; + for (const [key, value] of Object.entries(rest)) { + processedRest[key] = processItemOutput(value); + } + return processedRest; + } + return item; +}; From fba0df999cc6631fb29ee1195d799f4dfe6be7fb Mon Sep 17 00:00:00 2001 From: iacopolea Date: Thu, 19 Feb 2026 16:04:45 +0100 Subject: [PATCH 02/16] feat: change out of scope module position. UN-2337 --- src/locales/en/translation.json | 1 - src/locales/it/translation.json | 1 - src/pages/Plan/common/constants.ts | 8 +------- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index cb69a3748..17e21a3d2 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -803,7 +803,6 @@ "__PLAN_PAGE_EXPERT_REVIEW_TOOLTIP_CONTENT": "An expert will contact you within 3-5 business days with a custom quote", "__PLAN_PAGE_EXPERT_REVIEW_WARNING": "Requires expert review", "__PLAN_PAGE_GROUP_TITLE_ACTIVITY_SCOPE": "Activity Scope", - "__PLAN_PAGE_GROUP_TITLE_ADDITIONAL_DETAILS": "Additional Details", "__PLAN_PAGE_GROUP_TITLE_ADVANCED_CRITERIA": "Other", "__PLAN_PAGE_GROUP_TITLE_BEFORE_STARTING": " ", "__PLAN_PAGE_GROUP_TITLE_BEHAVIOURAL_DATA": "Behavioural Data", diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json index 1ab3b65b9..a32fa6842 100644 --- a/src/locales/it/translation.json +++ b/src/locales/it/translation.json @@ -833,7 +833,6 @@ "__PLAN_PAGE_EXPERT_REVIEW_TOOLTIP_CONTENT": "", "__PLAN_PAGE_EXPERT_REVIEW_WARNING": "", "__PLAN_PAGE_GROUP_TITLE_ACTIVITY_SCOPE": "", - "__PLAN_PAGE_GROUP_TITLE_ADDITIONAL_DETAILS": "", "__PLAN_PAGE_GROUP_TITLE_ADVANCED_CRITERIA": "", "__PLAN_PAGE_GROUP_TITLE_BEFORE_STARTING": "", "__PLAN_PAGE_GROUP_TITLE_BEHAVIOURAL_DATA": "", diff --git a/src/pages/Plan/common/constants.ts b/src/pages/Plan/common/constants.ts index ec1f8ebb4..131200246 100644 --- a/src/pages/Plan/common/constants.ts +++ b/src/pages/Plan/common/constants.ts @@ -15,7 +15,7 @@ export const MODULE_GROUPS: Record = { { id: 'essentials', title: '__PLAN_PAGE_GROUP_TITLE_ESSENTIALS', - modules: ['goal'], + modules: ['goal', 'out_of_scope'], }, { id: 'technicalRequirements', @@ -75,11 +75,6 @@ export const MODULE_GROUPS: Record = { title: '__PLAN_PAGE_GROUP_TITLE_ACTIVITY_SCOPE', modules: ['tasks'], }, - { - id: 'additionalDetails', - title: '__PLAN_PAGE_GROUP_TITLE_ADDITIONAL_DETAILS', - modules: ['out_of_scope'], - }, ], summary: [], }; @@ -99,7 +94,6 @@ const i18nPlanTitles = () => { t('__PLAN_PAGE_GROUP_TITLE_BEHAVIOURAL_DATA'); t('__PLAN_PAGE_GROUP_TITLE_ADVANCED_CRITERIA'); t('__PLAN_PAGE_GROUP_TITLE_ACTIVITY_SCOPE'); - t('__PLAN_PAGE_GROUP_TITLE_ADDITIONAL_DETAILS'); // Tab titles t('__PLAN_PAGE_TAB_SETUP_TAB_TITLE'); t('__PLAN_PAGE_TAB_TARGET_TAB_TITLE'); From 306e590dcb0c43b18bc1f44d155f2a6fddf71dd4 Mon Sep 17 00:00:00 2001 From: iacopolea Date: Thu, 19 Feb 2026 16:17:40 +0100 Subject: [PATCH 03/16] validation --- .../Component/parts/CreateTaskListsWithAI.tsx | 18 +----------------- .../Tasks/Component/parts/processItemOutput.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 17 deletions(-) create mode 100644 src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/processItemOutput.ts diff --git a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx index 2b19ceb42..53ed73e09 100644 --- a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx +++ b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx @@ -16,6 +16,7 @@ import { useGetServicesApiKJobsByJobIdQuery, usePostServicesApiKUsecasesMutation, } from 'src/features/api'; +import { processItemOutput } from './processItemOutput'; // constants const MODULES_TO_PROMPT = [ @@ -180,20 +181,3 @@ export const CreateTaskListsWithAI = () => { ); }; - -// Helper function to process module item output for better readability in the prompt -const processItemOutput = (item: any): any => { - if (Array.isArray(item)) { - // If it's an array, process each item in the array - return item.map((subItem) => processItemOutput(subItem)); - } else if (typeof item === 'object' && item !== null) { - // Process object: strip out id and recursively process nested values - const { id, ...rest } = item; - const processedRest: any = {}; - for (const [key, value] of Object.entries(rest)) { - processedRest[key] = processItemOutput(value); - } - return processedRest; - } - return item; -}; diff --git a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/processItemOutput.ts b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/processItemOutput.ts new file mode 100644 index 000000000..1a3596fd0 --- /dev/null +++ b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/processItemOutput.ts @@ -0,0 +1,17 @@ +// Helper function to process module item output for better readability in the AI prompt +export const processItemOutput = (item: any): any => { + if (Array.isArray(item)) { + // If it's an array, process each item in the array + return item.map((subItem) => processItemOutput(subItem)); + } + if (typeof item === 'object' && item !== null) { + // recursively process nested values + return Object.fromEntries( + Object.entries(item) + // here we can filter out any keys we don't want to include in the prompt, for example 'id' + .filter(([key]) => key !== 'id') + .map(([key, value]) => [key, processItemOutput(value)]) + ); + } + return item; +}; From 38d026e07e166a84bf859ebd2e80a4a61c94859f Mon Sep 17 00:00:00 2001 From: iacopolea Date: Mon, 23 Feb 2026 15:54:12 +0100 Subject: [PATCH 04/16] feat: add CreateTaskListsWithAIModal component and integrate AI task list creation in TasksList --- .../Tasks/Component/parts/TasksList.tsx | 24 +-- .../CreateTaskListsWithAIModal.tsx} | 157 ++++++++++-------- 2 files changed, 101 insertions(+), 80 deletions(-) rename src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/{CreateTaskListsWithAI.tsx => modal/CreateTaskListsWithAIModal.tsx} (54%) diff --git a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/TasksList.tsx b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/TasksList.tsx index e579b2fb1..b3cc35aa8 100644 --- a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/TasksList.tsx +++ b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/TasksList.tsx @@ -5,23 +5,21 @@ import { Message, Span, } from '@appquality/unguess-design-system'; -import { useMemo, useState } from 'react'; +import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { appTheme } from 'src/app/theme'; import { FEATURE_FLAG_CHANGE_MODULES_VARIANTS } from 'src/constants'; -import { useGetServicesApiKHealthQuery } from 'src/features/api'; import { useModule } from 'src/features/modules/useModule'; import { useModuleConfiguration } from 'src/features/modules/useModuleConfiguration'; import { useFeatureFlag } from 'src/hooks/useFeatureFlag'; import useWindowSize from 'src/hooks/useWindowSize'; -import { useCanShowAiChat } from 'src/pages/Dashboard/hooks/useCanShowAiChat'; import { DeleteModuleConfirmationModal } from 'src/pages/Plan/modules/modal/DeleteModuleConfirmationModal'; import styled from 'styled-components'; import { useIconWithValidation } from '../../useIcon'; import { useModuleTasks } from '../hooks'; import { AddTaskButton } from './AddTaskButton'; -import { CreateTaskListsWithAI } from './CreateTaskListsWithAI'; import { TasksModal } from './modal'; +import { CreateTaskListsWithAIModal } from './modal/CreateTaskListsWithAIModal'; import { TasksContainerAnimation } from './TasksContainerAnimation'; const StyledCard = styled(ContainerCard)` @@ -54,22 +52,14 @@ const TasksList = () => { const { getPlanStatus } = useModuleConfiguration(); const { t } = useTranslation(); const { hasFeatureFlag } = useFeatureFlag(); - const { data: apiK_HealthResponse } = useGetServicesApiKHealthQuery(); const [isOpenDeleteModal, setIsOpenDeleteModal] = useState(false); + const [isOpenCreateTasksWithAIModal, setIsOpenCreateTasksWithAIModal] = + useState(false); const Icon = useIconWithValidation(); const { width } = useWindowSize(); const breakpointSm = parseInt(appTheme.breakpoints.sm, 10); const isMobile = width < breakpointSm; - const canShowChat = useCanShowAiChat(); - const canShowAiFeatures = useMemo( - () => - canShowChat && - apiK_HealthResponse?.success && - apiK_HealthResponse?.status === 'healthy', - [apiK_HealthResponse, canShowChat] - ); - const handleDelete = () => { setIsOpenDeleteModal(true); }; @@ -113,7 +103,6 @@ const TasksList = () => { {t('__PLAN_PAGE_MODULE_TASKS_SUBTITLE')} * - {!!canShowAiFeatures && } {error && (errorEmpty ? ( { onConfirm={remove} /> )} + {isOpenCreateTasksWithAIModal && ( + setIsOpenCreateTasksWithAIModal(false)} + /> + )} ); }; diff --git a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/modal/CreateTaskListsWithAIModal.tsx similarity index 54% rename from src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx rename to src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/modal/CreateTaskListsWithAIModal.tsx index 53ed73e09..c2c8386a1 100644 --- a/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/CreateTaskListsWithAI.tsx +++ b/src/pages/Plan/modules/Factory/modules/Tasks/Component/parts/modal/CreateTaskListsWithAIModal.tsx @@ -2,10 +2,13 @@ import { AccordionNew, Button, FormField, + FooterItem, Input, Label, LG, Message, + Modal, + ModalClose, Textarea, } from '@appquality/unguess-design-system'; import { useEffect, useMemo, useState } from 'react'; @@ -16,7 +19,7 @@ import { useGetServicesApiKJobsByJobIdQuery, usePostServicesApiKUsecasesMutation, } from 'src/features/api'; -import { processItemOutput } from './processItemOutput'; +import { processItemOutput } from '../processItemOutput'; // constants const MODULES_TO_PROMPT = [ @@ -29,7 +32,7 @@ const MODULES_TO_PROMPT = [ ]; const MAX_PROMPT_LENGTH = 102300; -export const CreateTaskListsWithAI = () => { +const CreateTaskListsWithAIModal = ({ onQuit }: { onQuit: () => void }) => { const { planId } = useParams(); const MIN_LENGTH = 1; const [userPrompt, setUserPrompt] = useState(''); @@ -107,61 +110,62 @@ export const CreateTaskListsWithAI = () => { }, [useCasesError]); return ( -
-
- - -