Files
worklenz/worklenz-frontend/src/components/task-templates/task-template-drawer.tsx
chamikaJ a1e8a4c464 feat(task-management): enhance bulk action bar and localization updates
- Added new features to the OptimizedBulkActionBar, including dropdowns for labels and assignees, improving task management capabilities.
- Integrated task template creation functionality for owners/admins, allowing users to create templates from selected tasks.
- Updated localization files for multiple languages, adding new strings for label searching and template name requirements to enhance user experience.
- Refactored LabelsDropdown component to support label filtering and improved UI feedback for label creation.
2025-07-02 16:01:04 +05:30

181 lines
5.4 KiB
TypeScript

import { useState } from 'react';
import { Button, Drawer, Form, Input, List, Typography } from 'antd';
import { useTranslation } from 'react-i18next';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { taskTemplatesApiService } from '@/api/task-templates/task-templates.api.service';
import logger from '@/utils/errorLogger';
import { ITaskTemplateGetResponse } from '@/types/settings/task-templates.types';
import { useAppSelector } from '@/hooks/useAppSelector';
import { setSelectedTasks } from '@/features/project/project.slice';
interface TaskTemplateDrawerProps {
showDrawer: boolean;
selectedTemplateId: string | null;
onClose: () => void;
}
const TaskTemplateDrawer = ({
showDrawer = false,
selectedTemplateId,
onClose,
}: TaskTemplateDrawerProps) => {
const dispatch = useAppDispatch();
const { t } = useTranslation('task-template-drawer');
const [form] = Form.useForm();
const [templateData, setTemplateData] = useState<ITaskTemplateGetResponse>({});
const [isLoading, setIsLoading] = useState(false);
const [creatingTemplate, setCreatingTemplate] = useState(false);
const [updatingTemplate, setUpdatingTemplate] = useState(false);
const { selectedTasks } = useAppSelector(state => state.bulkActionReducer);
const onCloseDrawer = () => {
form.resetFields();
setTemplateData({});
onClose();
};
const fetchTemplateData = async () => {
if (!selectedTemplateId) return;
try {
setIsLoading(true);
const res = await taskTemplatesApiService.getTemplate(selectedTemplateId);
if (res.done) {
setTemplateData(res.body);
form.setFieldsValue({
name: res.body.name,
});
}
} catch (error) {
logger.error('Failed to fetch template data:', error);
} finally {
setIsLoading(false);
}
};
const afterOpenChange = (open: boolean) => {
if (selectedTemplateId) {
fetchTemplateData();
return;
}
// Tasks should already have the name property set correctly
setTemplateData({ tasks: selectedTasks });
};
const handleRemoveTask = (index: number) => {
const updatedTasks = [...(templateData.tasks || [])];
updatedTasks.splice(index, 1);
setTemplateData({
...templateData,
tasks: updatedTasks,
});
};
const createTemplate = async () => {
const values = form.getFieldsValue();
if (!values.name || !templateData.tasks) return;
try {
setCreatingTemplate(true);
const res = await taskTemplatesApiService.createTemplate({
name: values.name || '',
tasks: templateData.tasks || [],
});
if (res.done) {
onCloseDrawer();
dispatch(setSelectedTasks([]));
}
} catch (error) {
logger.error('Failed to create template:', error);
} finally {
setCreatingTemplate(false);
}
};
const updateTemplate = async () => {
if (!selectedTemplateId || !templateData.name || !templateData.tasks) return;
const values = form.getFieldsValue();
try {
setUpdatingTemplate(true);
const res = await taskTemplatesApiService.updateTemplate(selectedTemplateId, {
name: values.name || '',
tasks: templateData.tasks || [],
});
if (res.done) {
onCloseDrawer();
dispatch(setSelectedTasks([]));
}
} catch (error) {
logger.error('Failed to update template:', error);
} finally {
setUpdatingTemplate(false);
}
};
const handleSaveTemplate = () => {
if (!selectedTemplateId) {
createTemplate();
} else {
updateTemplate();
}
};
return (
<Drawer
width={650}
title={selectedTemplateId ? t('editTaskTemplate') : t('createTaskTemplate')}
open={showDrawer}
onClose={onCloseDrawer}
afterOpenChange={afterOpenChange}
destroyOnHidden={true}
footer={
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', justifyContent: 'right' }}>
<Button onClick={onCloseDrawer}>{t('cancelButton')}</Button>
<Button type="primary" onClick={handleSaveTemplate} loading={creatingTemplate || updatingTemplate}>
{t('saveButton')}
</Button>
</div>
}
>
<Form form={form} initialValues={{ name: templateData?.name }}>
<Form.Item
name="name"
label={t('templateNameText')}
rules={[{ required: true, message: t('templateNameRequired') }]}
>
<Input type="text" />
</Form.Item>
<Typography.Text style={{ fontWeight: 700 }}>
{t('selectedTasks')} ({templateData?.tasks?.length || 0})
</Typography.Text>
<div style={{ marginTop: '1.5rem' }}>
<List
loading={isLoading}
bordered
dataSource={templateData?.tasks}
renderItem={(item, index) => (
<List.Item>
<div
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
}}
>
<span>{item.name}</span>
<Button type="link" onClick={() => handleRemoveTask(index)}>
{t('removeTask')}
</Button>
</div>
</List.Item>
)}
/>
</div>
</Form>
</Drawer>
);
};
export default TaskTemplateDrawer;