From ebd0f6676821473f613b4810c0f27d74ab43d281 Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Fri, 16 May 2025 12:37:02 +0530 Subject: [PATCH] feat(task-drawer): integrate socket handling for recurring task updates Enhance the TaskDrawerRecurringConfig component to include socket communication for handling recurring task changes. This update introduces the use of Redux for managing state updates related to recurring schedules, ensuring real-time synchronization of task configurations. Additionally, the code has been refactored for improved readability and maintainability. --- .../task-drawer-recurring-config.tsx | 480 ++++++++++-------- .../src/features/tasks/tasks.slice.ts | 11 + 2 files changed, 275 insertions(+), 216 deletions(-) diff --git a/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-recurring-config/task-drawer-recurring-config.tsx b/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-recurring-config/task-drawer-recurring-config.tsx index 56daee49..1e5af1d8 100644 --- a/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-recurring-config/task-drawer-recurring-config.tsx +++ b/worklenz-frontend/src/components/task-drawer/shared/info-tab/details/task-drawer-recurring-config/task-drawer-recurring-config.tsx @@ -1,245 +1,293 @@ import React, { useState, useMemo, useEffect } from 'react'; -import { Form, Switch, Button, Popover, Select, Checkbox, Radio, InputNumber, Skeleton, Row, Col } from 'antd'; +import { + Form, + Switch, + Button, + Popover, + Select, + Checkbox, + Radio, + InputNumber, + Skeleton, + Row, + Col, +} from 'antd'; import { SettingOutlined } from '@ant-design/icons'; import { useSocket } from '@/socket/socketContext'; import { SocketEvents } from '@/shared/socket-events'; import { ITaskRecurringScheduleData } from '@/types/tasks/task-recurring-schedule'; import { ITaskViewModel } from '@/types/tasks/task.types'; import { useTranslation } from 'react-i18next'; +import { useAppDispatch } from '@/hooks/useAppDispatch'; +import { updateRecurringChange } from '@/features/tasks/tasks.slice'; -// Dummy enums and types for demonstration; replace with actual imports/types const ITaskRecurring = { - Weekly: 'weekly', - EveryXDays: 'every_x_days', - EveryXWeeks: 'every_x_weeks', - EveryXMonths: 'every_x_months', - Monthly: 'monthly', + Weekly: 'weekly', + EveryXDays: 'every_x_days', + EveryXWeeks: 'every_x_weeks', + EveryXMonths: 'every_x_months', + Monthly: 'monthly', }; const repeatOptions = [ - { label: 'Weekly', value: ITaskRecurring.Weekly }, - { label: 'Every X Days', value: ITaskRecurring.EveryXDays }, - { label: 'Every X Weeks', value: ITaskRecurring.EveryXWeeks }, - { label: 'Every X Months', value: ITaskRecurring.EveryXMonths }, - { label: 'Monthly', value: ITaskRecurring.Monthly }, + { label: 'Weekly', value: ITaskRecurring.Weekly }, + { label: 'Every X Days', value: ITaskRecurring.EveryXDays }, + { label: 'Every X Weeks', value: ITaskRecurring.EveryXWeeks }, + { label: 'Every X Months', value: ITaskRecurring.EveryXMonths }, + { label: 'Monthly', value: ITaskRecurring.Monthly }, ]; const daysOfWeek = [ - { label: 'Mon', value: 'mon' }, - { label: 'Tue', value: 'tue' }, - { label: 'Wed', value: 'wed' }, - { label: 'Thu', value: 'thu' }, - { label: 'Fri', value: 'fri' }, - { label: 'Sat', value: 'sat' }, - { label: 'Sun', value: 'sun' }, + { label: 'Mon', value: 'mon' }, + { label: 'Tue', value: 'tue' }, + { label: 'Wed', value: 'wed' }, + { label: 'Thu', value: 'thu' }, + { label: 'Fri', value: 'fri' }, + { label: 'Sat', value: 'sat' }, + { label: 'Sun', value: 'sun' }, ]; const monthlyDateOptions = Array.from({ length: 31 }, (_, i) => i + 1); const weekOptions = [ - { label: 'First', value: 'first' }, - { label: 'Second', value: 'second' }, - { label: 'Third', value: 'third' }, - { label: 'Fourth', value: 'fourth' }, - { label: 'Last', value: 'last' }, + { label: 'First', value: 'first' }, + { label: 'Second', value: 'second' }, + { label: 'Third', value: 'third' }, + { label: 'Fourth', value: 'fourth' }, + { label: 'Last', value: 'last' }, ]; const dayOptions = daysOfWeek.map(d => ({ label: d.label, value: d.value })); -const TaskDrawerRecurringConfig = ({ task }: {task: ITaskViewModel}) => { - const { socket, connected } = useSocket(); - const { t } = useTranslation('task-drawer/task-drawer-recurring-config'); +const TaskDrawerRecurringConfig = ({ task }: { task: ITaskViewModel }) => { + const { socket, connected } = useSocket(); + const dispatch = useAppDispatch(); + const { t } = useTranslation('task-drawer/task-drawer-recurring-config'); - const [recurring, setRecurring] = useState(false); - const [showConfig, setShowConfig] = useState(false); - const [repeatOption, setRepeatOption] = useState(repeatOptions[0]); - const [selectedDays, setSelectedDays] = useState([]); - const [monthlyOption, setMonthlyOption] = useState('date'); - const [selectedMonthlyDate, setSelectedMonthlyDate] = useState(1); - const [selectedMonthlyWeek, setSelectedMonthlyWeek] = useState(weekOptions[0].value); - const [selectedMonthlyDay, setSelectedMonthlyDay] = useState(dayOptions[0].value); - const [intervalDays, setIntervalDays] = useState(1); - const [intervalWeeks, setIntervalWeeks] = useState(1); - const [intervalMonths, setIntervalMonths] = useState(1); - const [loadingData, setLoadingData] = useState(false); - const [updatingData, setUpdatingData] = useState(false); + const [recurring, setRecurring] = useState(false); + const [showConfig, setShowConfig] = useState(false); + const [repeatOption, setRepeatOption] = useState(repeatOptions[0]); + const [selectedDays, setSelectedDays] = useState([]); + const [monthlyOption, setMonthlyOption] = useState('date'); + const [selectedMonthlyDate, setSelectedMonthlyDate] = useState(1); + const [selectedMonthlyWeek, setSelectedMonthlyWeek] = useState(weekOptions[0].value); + const [selectedMonthlyDay, setSelectedMonthlyDay] = useState(dayOptions[0].value); + const [intervalDays, setIntervalDays] = useState(1); + const [intervalWeeks, setIntervalWeeks] = useState(1); + const [intervalMonths, setIntervalMonths] = useState(1); + const [loadingData, setLoadingData] = useState(false); + const [updatingData, setUpdatingData] = useState(false); + + const handleChange = (checked: boolean) => { + if (!task.id) return; + + socket?.emit(SocketEvents.TASK_RECURRING_CHANGE.toString(), { + task_id: task.id, + schedule_id: task.schedule_id, + }); + + socket?.once( + SocketEvents.TASK_RECURRING_CHANGE.toString(), + (schedule: ITaskRecurringScheduleData) => { + if (schedule.id && schedule.schedule_type) { + const selected = repeatOptions.find(e => e.value == schedule.schedule_type); + if (selected) setRepeatOption(selected); + } + dispatch(updateRecurringChange(schedule)); - const handleChange = (checked: boolean) => { setRecurring(checked); if (!checked) setShowConfig(false); - }; - - const configVisibleChange = (visible: boolean) => { - setShowConfig(visible); - }; - - const isMonthlySelected = useMemo(() => repeatOption.value === ITaskRecurring.Monthly, [repeatOption]); - - const handleDayCheckboxChange = (checkedValues: string[]) => { - setSelectedDays(checkedValues as unknown as string[]); - }; - - const handleSave = () => { - // Compose the schedule data and call the update handler - const data = { - recurring, - repeatOption, - selectedDays, - monthlyOption, - selectedMonthlyDate, - selectedMonthlyWeek, - selectedMonthlyDay, - intervalDays, - intervalWeeks, - intervalMonths, - }; - // if (onUpdateSchedule) onUpdateSchedule(data); - setShowConfig(false); - }; - - const getScheduleData = () => { - - }; - - const handleResponse = (response: ITaskRecurringScheduleData) => { - if (!task || !response.task_id) return; - } - - useEffect(() => { - if (task) setRecurring(!!task.schedule_id); - if (recurring) void getScheduleData(); - socket?.on(SocketEvents.TASK_RECURRING_CHANGE.toString(), handleResponse) - }, []) - - return ( -
- -
- -   - {recurring && ( - -
- - ({ label: date.toString(), value: date }))} - style={{ width: 120 }} - /> - - )} - {monthlyOption === 'day' && ( - <> - - - - - )} - - )} - - {repeatOption.value === ITaskRecurring.EveryXDays && ( - - value && setIntervalDays(value)} /> - - )} - {repeatOption.value === ITaskRecurring.EveryXWeeks && ( - - value && setIntervalWeeks(value)} /> - - )} - {repeatOption.value === ITaskRecurring.EveryXMonths && ( - - value && setIntervalMonths(value)} /> - - )} - - - -
- - } - overlayStyle={{ width: 510 }} - open={showConfig} - onOpenChange={configVisibleChange} - trigger="click" - > - -
- )} -
-
-
+ } ); + }; + + const configVisibleChange = (visible: boolean) => { + setShowConfig(visible); + }; + + const isMonthlySelected = useMemo( + () => repeatOption.value === ITaskRecurring.Monthly, + [repeatOption] + ); + + const handleDayCheckboxChange = (checkedValues: string[]) => { + setSelectedDays(checkedValues as unknown as string[]); + }; + + const handleSave = () => { + // Compose the schedule data and call the update handler + const data = { + recurring, + repeatOption, + selectedDays, + monthlyOption, + selectedMonthlyDate, + selectedMonthlyWeek, + selectedMonthlyDay, + intervalDays, + intervalWeeks, + intervalMonths, + }; + // if (onUpdateSchedule) onUpdateSchedule(data); + setShowConfig(false); + }; + + const getScheduleData = () => {}; + + const handleResponse = (response: ITaskRecurringScheduleData) => { + if (!task || !response.task_id) return; + }; + + useEffect(() => { + if (task) setRecurring(!!task.schedule_id); + if (recurring) void getScheduleData(); + socket?.on(SocketEvents.TASK_RECURRING_CHANGE.toString(), handleResponse); + }, []); + + return ( +
+ +
+ +   + {recurring && ( + +
+ + ({ + label: date.toString(), + value: date, + }))} + style={{ width: 120 }} + /> + + )} + {monthlyOption === 'day' && ( + <> + + + + + )} + + )} + + {repeatOption.value === ITaskRecurring.EveryXDays && ( + + value && setIntervalDays(value)} + /> + + )} + {repeatOption.value === ITaskRecurring.EveryXWeeks && ( + + value && setIntervalWeeks(value)} + /> + + )} + {repeatOption.value === ITaskRecurring.EveryXMonths && ( + + value && setIntervalMonths(value)} + /> + + )} + + + +
+ + } + overlayStyle={{ width: 510 }} + open={showConfig} + onOpenChange={configVisibleChange} + trigger="click" + > + +
+ )} +
+
+
+ ); }; -export default TaskDrawerRecurringConfig; \ No newline at end of file +export default TaskDrawerRecurringConfig; diff --git a/worklenz-frontend/src/features/tasks/tasks.slice.ts b/worklenz-frontend/src/features/tasks/tasks.slice.ts index cd443dbf..49c85e28 100644 --- a/worklenz-frontend/src/features/tasks/tasks.slice.ts +++ b/worklenz-frontend/src/features/tasks/tasks.slice.ts @@ -22,6 +22,7 @@ import { ITaskPhaseChangeResponse } from '@/types/tasks/task-phase-change-respon import { produce } from 'immer'; import { tasksCustomColumnsService } from '@/api/tasks/tasks-custom-columns.service'; import { SocketEvents } from '@/shared/socket-events'; +import { ITaskRecurringScheduleData } from '@/types/tasks/task-recurring-schedule'; export enum IGroupBy { STATUS = 'status', @@ -1006,6 +1007,15 @@ const taskSlice = createSlice({ column.pinned = isVisible; } }, + + updateRecurringChange: (state, action: PayloadAction) => { + const {id, schedule_type, task_id} = action.payload; + const taskInfo = findTaskInGroups(state.taskGroups, task_id as string); + if (!taskInfo) return; + + const { task } = taskInfo; + task.schedule_id = id; + } }, extraReducers: builder => { @@ -1165,6 +1175,7 @@ export const { updateSubTasks, updateCustomColumnValue, updateCustomColumnPinned, + updateRecurringChange } = taskSlice.actions; export default taskSlice.reducer;