feat(localization): update and enhance localization files for multiple languages
- Updated localization files for various languages, including English, German, Spanish, Portuguese, and Chinese, to ensure consistency and accuracy across the application. - Added new keys and updated existing ones to support recent UI changes and features, particularly in project views, task lists, and admin center settings. - Enhanced the structure of localization files to improve maintainability and facilitate future updates. - Implemented performance optimizations in the frontend components to better handle localization data.
This commit is contained in:
@@ -117,16 +117,16 @@ const ProjectList: React.FC = () => {
|
||||
statuses: requestParams.statuses,
|
||||
categories: requestParams.categories,
|
||||
};
|
||||
|
||||
|
||||
// Create a stable key for comparison
|
||||
const paramsKey = JSON.stringify(params);
|
||||
|
||||
|
||||
// Only return new params if they've actually changed
|
||||
if (paramsKey !== lastQueryParamsRef.current) {
|
||||
lastQueryParamsRef.current = paramsKey;
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
// Return the previous params to maintain reference stability
|
||||
return JSON.parse(lastQueryParamsRef.current || '{}');
|
||||
}, [requestParams]);
|
||||
@@ -147,8 +147,6 @@ const ProjectList: React.FC = () => {
|
||||
skip: viewMode === ProjectViewType.GROUP,
|
||||
});
|
||||
|
||||
|
||||
|
||||
// Add performance monitoring
|
||||
const performanceRef = useRef<{ startTime: number | null }>({ startTime: null });
|
||||
|
||||
@@ -163,15 +161,17 @@ const ProjectList: React.FC = () => {
|
||||
|
||||
// Optimized debounced search with better cleanup and performance
|
||||
const debouncedSearch = useCallback(
|
||||
debounce((searchTerm: string) => {
|
||||
debounce((searchTerm: string) => {
|
||||
// Clear any error messages when starting a new search
|
||||
setErrorMessage(null);
|
||||
|
||||
|
||||
if (viewMode === ProjectViewType.LIST) {
|
||||
dispatch(setRequestParams({
|
||||
search: searchTerm,
|
||||
index: 1 // Reset to first page on search
|
||||
}));
|
||||
dispatch(
|
||||
setRequestParams({
|
||||
search: searchTerm,
|
||||
index: 1, // Reset to first page on search
|
||||
})
|
||||
);
|
||||
} else if (viewMode === ProjectViewType.GROUP) {
|
||||
const newGroupedParams = {
|
||||
...groupedRequestParams,
|
||||
@@ -179,12 +179,12 @@ const ProjectList: React.FC = () => {
|
||||
index: 1,
|
||||
};
|
||||
dispatch(setGroupedRequestParams(newGroupedParams));
|
||||
|
||||
|
||||
// Add timeout for grouped search to prevent rapid API calls
|
||||
if (searchTimeoutRef.current) {
|
||||
clearTimeout(searchTimeoutRef.current);
|
||||
}
|
||||
|
||||
|
||||
searchTimeoutRef.current = setTimeout(() => {
|
||||
dispatch(fetchGroupedProjects(newGroupedParams));
|
||||
}, 100);
|
||||
@@ -208,21 +208,21 @@ const ProjectList: React.FC = () => {
|
||||
const handleSearchChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newSearchValue = e.target.value;
|
||||
|
||||
|
||||
// Validate input length to prevent excessive API calls
|
||||
if (newSearchValue.length > 100) {
|
||||
return; // Prevent extremely long search terms
|
||||
}
|
||||
|
||||
|
||||
setSearchValue(newSearchValue);
|
||||
trackMixpanelEvent(evt_projects_search);
|
||||
|
||||
|
||||
// Clear any existing timeout
|
||||
if (searchTimeoutRef.current) {
|
||||
clearTimeout(searchTimeoutRef.current);
|
||||
searchTimeoutRef.current = null;
|
||||
}
|
||||
|
||||
|
||||
// Debounce the actual search execution
|
||||
debouncedSearch(newSearchValue);
|
||||
},
|
||||
@@ -381,7 +381,7 @@ const ProjectList: React.FC = () => {
|
||||
trackMixpanelEvent(evt_projects_refresh_click);
|
||||
setIsLoading(true);
|
||||
setErrorMessage(null);
|
||||
|
||||
|
||||
if (viewMode === ProjectViewType.LIST) {
|
||||
await refetchProjects();
|
||||
} else if (viewMode === ProjectViewType.GROUP && groupBy) {
|
||||
@@ -398,7 +398,7 @@ const ProjectList: React.FC = () => {
|
||||
const emptyContent = useMemo(() => {
|
||||
if (errorMessage) {
|
||||
return (
|
||||
<Empty
|
||||
<Empty
|
||||
description={
|
||||
<div>
|
||||
<p>{errorMessage}</p>
|
||||
@@ -406,7 +406,7 @@ const ProjectList: React.FC = () => {
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -455,7 +455,11 @@ const ProjectList: React.FC = () => {
|
||||
const newOrder = Array.isArray(sorter) ? sorter[0].order : sorter.order;
|
||||
const newField = (Array.isArray(sorter) ? sorter[0].columnKey : sorter.columnKey) as string;
|
||||
|
||||
if (newOrder && newField && (newOrder !== requestParams.order || newField !== requestParams.field)) {
|
||||
if (
|
||||
newOrder &&
|
||||
newField &&
|
||||
(newOrder !== requestParams.order || newField !== requestParams.field)
|
||||
) {
|
||||
updates.order = newOrder ?? 'ascend';
|
||||
updates.field = newField ?? 'name';
|
||||
setSortingValues(updates.field, updates.order);
|
||||
@@ -463,7 +467,10 @@ const ProjectList: React.FC = () => {
|
||||
}
|
||||
|
||||
// Handle pagination
|
||||
if (newPagination.current !== requestParams.index || newPagination.pageSize !== requestParams.size) {
|
||||
if (
|
||||
newPagination.current !== requestParams.index ||
|
||||
newPagination.pageSize !== requestParams.size
|
||||
) {
|
||||
updates.index = newPagination.current || 1;
|
||||
updates.size = newPagination.pageSize || DEFAULT_PAGE_SIZE;
|
||||
hasChanges = true;
|
||||
@@ -494,9 +501,12 @@ const ProjectList: React.FC = () => {
|
||||
index: newPagination.current || 1,
|
||||
size: newPagination.pageSize || DEFAULT_PAGE_SIZE,
|
||||
};
|
||||
|
||||
|
||||
// Only update if values actually changed
|
||||
if (newParams.index !== groupedRequestParams.index || newParams.size !== groupedRequestParams.size) {
|
||||
if (
|
||||
newParams.index !== groupedRequestParams.index ||
|
||||
newParams.size !== groupedRequestParams.size
|
||||
) {
|
||||
dispatch(setGroupedRequestParams(newParams));
|
||||
}
|
||||
},
|
||||
@@ -511,19 +521,23 @@ const ProjectList: React.FC = () => {
|
||||
|
||||
// Batch updates to reduce re-renders
|
||||
const baseUpdates = { filter: newFilterIndex, index: 1 };
|
||||
|
||||
|
||||
dispatch(setRequestParams(baseUpdates));
|
||||
dispatch(setGroupedRequestParams({
|
||||
...groupedRequestParams,
|
||||
...baseUpdates,
|
||||
}));
|
||||
dispatch(
|
||||
setGroupedRequestParams({
|
||||
...groupedRequestParams,
|
||||
...baseUpdates,
|
||||
})
|
||||
);
|
||||
|
||||
// Only trigger data fetch for group view (list view will auto-refetch via query)
|
||||
if (viewMode === ProjectViewType.GROUP && groupBy) {
|
||||
dispatch(fetchGroupedProjects({
|
||||
...groupedRequestParams,
|
||||
...baseUpdates,
|
||||
}));
|
||||
dispatch(
|
||||
fetchGroupedProjects({
|
||||
...groupedRequestParams,
|
||||
...baseUpdates,
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
[filters, setFilterIndex, dispatch, groupedRequestParams, viewMode, groupBy]
|
||||
@@ -697,26 +711,28 @@ const ProjectList: React.FC = () => {
|
||||
useEffect(() => {
|
||||
const filterIndex = getFilterIndex();
|
||||
const initialParams = { filter: filterIndex };
|
||||
|
||||
|
||||
// Only update if values are different
|
||||
if (requestParams.filter !== filterIndex) {
|
||||
dispatch(setRequestParams(initialParams));
|
||||
}
|
||||
|
||||
|
||||
// Initialize grouped request params with proper groupBy value
|
||||
if (!groupedRequestParams.groupBy) {
|
||||
const initialGroupBy = groupBy || ProjectGroupBy.CATEGORY;
|
||||
dispatch(setGroupedRequestParams({
|
||||
filter: filterIndex,
|
||||
index: 1,
|
||||
size: DEFAULT_PAGE_SIZE,
|
||||
field: 'name',
|
||||
order: 'ascend',
|
||||
search: '',
|
||||
groupBy: initialGroupBy,
|
||||
statuses: null,
|
||||
categories: null,
|
||||
}));
|
||||
dispatch(
|
||||
setGroupedRequestParams({
|
||||
filter: filterIndex,
|
||||
index: 1,
|
||||
size: DEFAULT_PAGE_SIZE,
|
||||
field: 'name',
|
||||
order: 'ascend',
|
||||
search: '',
|
||||
groupBy: initialGroupBy,
|
||||
statuses: null,
|
||||
categories: null,
|
||||
})
|
||||
);
|
||||
}
|
||||
}, [dispatch, getFilterIndex, groupBy]); // Add groupBy to deps to handle initial state
|
||||
|
||||
@@ -732,8 +748,9 @@ const ProjectList: React.FC = () => {
|
||||
// 2. We have a groupBy value (either from Redux or default)
|
||||
if (viewMode === ProjectViewType.GROUP && groupBy) {
|
||||
// Always ensure grouped request params are properly set with current groupBy
|
||||
const shouldUpdateParams = !groupedRequestParams.groupBy || groupedRequestParams.groupBy !== groupBy;
|
||||
|
||||
const shouldUpdateParams =
|
||||
!groupedRequestParams.groupBy || groupedRequestParams.groupBy !== groupBy;
|
||||
|
||||
if (shouldUpdateParams) {
|
||||
const updatedParams = {
|
||||
...groupedRequestParams,
|
||||
@@ -757,7 +774,7 @@ const ProjectList: React.FC = () => {
|
||||
useEffect(() => {
|
||||
const loadLookups = async () => {
|
||||
const promises = [];
|
||||
|
||||
|
||||
if (projectStatuses.length === 0) {
|
||||
promises.push(dispatch(fetchProjectStatuses()));
|
||||
}
|
||||
@@ -767,19 +784,20 @@ const ProjectList: React.FC = () => {
|
||||
if (projectHealths.length === 0) {
|
||||
promises.push(dispatch(fetchProjectHealth()));
|
||||
}
|
||||
|
||||
|
||||
// Load all lookups in parallel
|
||||
if (promises.length > 0) {
|
||||
await Promise.allSettled(promises);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
loadLookups();
|
||||
}, [dispatch]); // Remove length dependencies to avoid re-runs
|
||||
|
||||
// Sync search input value with Redux state
|
||||
useEffect(() => {
|
||||
const currentSearch = viewMode === ProjectViewType.LIST ? requestParams.search : groupedRequestParams.search;
|
||||
const currentSearch =
|
||||
viewMode === ProjectViewType.LIST ? requestParams.search : groupedRequestParams.search;
|
||||
if (searchValue !== (currentSearch || '')) {
|
||||
setSearchValue(currentSearch || '');
|
||||
}
|
||||
@@ -788,13 +806,13 @@ const ProjectList: React.FC = () => {
|
||||
// Optimize loading state management
|
||||
useEffect(() => {
|
||||
let newLoadingState = false;
|
||||
|
||||
|
||||
if (viewMode === ProjectViewType.LIST) {
|
||||
newLoadingState = loadingProjects || isFetchingProjects;
|
||||
} else {
|
||||
newLoadingState = groupedProjects.loading;
|
||||
}
|
||||
|
||||
|
||||
// Only update if loading state actually changed
|
||||
if (isLoading !== newLoadingState) {
|
||||
setIsLoading(newLoadingState);
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Avatar, Checkbox, DatePicker, Flex, Tag, Tooltip, Typography } from '@/shared/antd-imports';
|
||||
import {
|
||||
Avatar,
|
||||
Checkbox,
|
||||
DatePicker,
|
||||
Flex,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@/shared/antd-imports';
|
||||
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { columnList } from '@/pages/projects/project-view-1/taskList/taskListTable/columns/columnList';
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
import { Badge, Button, Collapse, ConfigProvider, Dropdown, Flex, Input, Typography } from '@/shared/antd-imports';
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Collapse,
|
||||
ConfigProvider,
|
||||
Dropdown,
|
||||
Flex,
|
||||
Input,
|
||||
Typography,
|
||||
} from '@/shared/antd-imports';
|
||||
import { useState } from 'react';
|
||||
import { TaskType } from '@/types/task.types';
|
||||
import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@/shared/antd-imports';
|
||||
import {
|
||||
EditOutlined,
|
||||
EllipsisOutlined,
|
||||
RetweetOutlined,
|
||||
RightOutlined,
|
||||
} from '@/shared/antd-imports';
|
||||
import { colors } from '@/styles/colors';
|
||||
import './task-list-table-wrapper.css';
|
||||
import TaskListTable from '../table-v2';
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { CaretDownFilled, SortAscendingOutlined, SortDescendingOutlined } from '@/shared/antd-imports';
|
||||
import {
|
||||
CaretDownFilled,
|
||||
SortAscendingOutlined,
|
||||
SortDescendingOutlined,
|
||||
} from '@/shared/antd-imports';
|
||||
import { Badge, Button, Card, Checkbox, Dropdown, List, Space } from '@/shared/antd-imports';
|
||||
import React, { useState } from 'react';
|
||||
import { colors } from '../../../../../styles/colors';
|
||||
|
||||
@@ -1,6 +1,20 @@
|
||||
import { Badge, Button, Collapse, ConfigProvider, Dropdown, Flex, Input, Typography } from '@/shared/antd-imports';
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
Collapse,
|
||||
ConfigProvider,
|
||||
Dropdown,
|
||||
Flex,
|
||||
Input,
|
||||
Typography,
|
||||
} from '@/shared/antd-imports';
|
||||
import { useState } from 'react';
|
||||
import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@/shared/antd-imports';
|
||||
import {
|
||||
EditOutlined,
|
||||
EllipsisOutlined,
|
||||
RetweetOutlined,
|
||||
RightOutlined,
|
||||
} from '@/shared/antd-imports';
|
||||
import { colors } from '../../../../../styles/colors';
|
||||
import './taskListTableWrapper.css';
|
||||
import TaskListTable from './TaskListTable';
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import { Col, Flex, Typography, List, Dropdown, MenuProps, Popconfirm } from '@/shared/antd-imports';
|
||||
import {
|
||||
Col,
|
||||
Flex,
|
||||
Typography,
|
||||
List,
|
||||
Dropdown,
|
||||
MenuProps,
|
||||
Popconfirm,
|
||||
} from '@/shared/antd-imports';
|
||||
import {
|
||||
UserAddOutlined,
|
||||
DeleteOutlined,
|
||||
|
||||
@@ -184,19 +184,19 @@ const ProjectViewHeader = memo(() => {
|
||||
const handleSettingsClick = useCallback(() => {
|
||||
if (selectedProject?.id) {
|
||||
console.log('Opening project drawer from project view for project:', selectedProject.id);
|
||||
|
||||
|
||||
// Set project ID first
|
||||
dispatch(setProjectId(selectedProject.id));
|
||||
|
||||
|
||||
// Then fetch project data
|
||||
dispatch(fetchProjectData(selectedProject.id))
|
||||
.unwrap()
|
||||
.then((projectData) => {
|
||||
.then(projectData => {
|
||||
console.log('Project data fetched successfully from project view:', projectData);
|
||||
// Open drawer after data is fetched
|
||||
dispatch(toggleProjectDrawer());
|
||||
})
|
||||
.catch((error) => {
|
||||
.catch(error => {
|
||||
console.error('Failed to fetch project data from project view:', error);
|
||||
// Still open drawer even if fetch fails, so user can see error state
|
||||
dispatch(toggleProjectDrawer());
|
||||
@@ -270,7 +270,11 @@ const ProjectViewHeader = memo(() => {
|
||||
{
|
||||
key: 'import',
|
||||
label: (
|
||||
<div style={{ width: '100%', margin: 0, padding: 0 }} onClick={handleImportTaskTemplate} title={t('importTaskTooltip')}>
|
||||
<div
|
||||
style={{ width: '100%', margin: 0, padding: 0 }}
|
||||
onClick={handleImportTaskTemplate}
|
||||
title={t('importTaskTooltip')}
|
||||
>
|
||||
<ImportOutlined /> {t('importTask')}
|
||||
</div>
|
||||
),
|
||||
@@ -287,7 +291,10 @@ const ProjectViewHeader = memo(() => {
|
||||
|
||||
if (selectedProject.category_id) {
|
||||
elements.push(
|
||||
<Tooltip key="category-tooltip" title={`${t('projectCategoryTooltip')}: ${selectedProject.category_name}`}>
|
||||
<Tooltip
|
||||
key="category-tooltip"
|
||||
title={`${t('projectCategoryTooltip')}: ${selectedProject.category_name}`}
|
||||
>
|
||||
<Tag
|
||||
key="category"
|
||||
color={colors.vibrantOrange}
|
||||
@@ -381,7 +388,10 @@ const ProjectViewHeader = memo(() => {
|
||||
|
||||
// Subscribe button
|
||||
actions.push(
|
||||
<Tooltip key="subscribe" title={selectedProject?.subscribed ? t('unsubscribeTooltip') : t('subscribeTooltip')}>
|
||||
<Tooltip
|
||||
key="subscribe"
|
||||
title={selectedProject?.subscribed ? t('unsubscribeTooltip') : t('subscribeTooltip')}
|
||||
>
|
||||
<Button
|
||||
shape="round"
|
||||
loading={subscriptionLoading}
|
||||
@@ -464,7 +474,10 @@ const ProjectViewHeader = memo(() => {
|
||||
() => (
|
||||
<Flex gap={4} align="center">
|
||||
<Tooltip title={t('navigateBackTooltip')}>
|
||||
<ArrowLeftOutlined style={{ fontSize: 16, cursor: 'pointer' }} onClick={handleNavigateToProjects} />
|
||||
<ArrowLeftOutlined
|
||||
style={{ fontSize: 16, cursor: 'pointer' }}
|
||||
onClick={handleNavigateToProjects}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Typography.Title level={4} style={{ marginBlockEnd: 0, marginInlineStart: 8 }}>
|
||||
{selectedProject?.name}
|
||||
|
||||
@@ -26,5 +26,3 @@
|
||||
[data-theme="dark"] .project-view-tabs .ant-tabs-ink-bar {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -8,7 +8,12 @@ import Dropdown from 'antd/es/dropdown';
|
||||
import Input from 'antd/es/input';
|
||||
import Typography from 'antd/es/typography';
|
||||
import { MenuProps } from 'antd/es/menu';
|
||||
import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@/shared/antd-imports';
|
||||
import {
|
||||
EditOutlined,
|
||||
EllipsisOutlined,
|
||||
RetweetOutlined,
|
||||
RightOutlined,
|
||||
} from '@/shared/antd-imports';
|
||||
|
||||
import { colors } from '@/styles/colors';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
|
||||
@@ -17,7 +17,12 @@ import {
|
||||
DragEndEvent,
|
||||
DragStartEvent,
|
||||
} from '@dnd-kit/core';
|
||||
import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@/shared/antd-imports';
|
||||
import {
|
||||
EditOutlined,
|
||||
EllipsisOutlined,
|
||||
RetweetOutlined,
|
||||
RightOutlined,
|
||||
} from '@/shared/antd-imports';
|
||||
|
||||
import { colors } from '@/styles/colors';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Badge, Card, Dropdown, Empty, Flex, Menu, MenuProps, Typography } from '@/shared/antd-imports';
|
||||
import {
|
||||
Badge,
|
||||
Card,
|
||||
Dropdown,
|
||||
Empty,
|
||||
Flex,
|
||||
Menu,
|
||||
MenuProps,
|
||||
Typography,
|
||||
} from '@/shared/antd-imports';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { DownOutlined } from '@/shared/antd-imports';
|
||||
// custom css file
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Badge, Card, Dropdown, Empty, Flex, Menu, MenuProps, Typography } from '@/shared/antd-imports';
|
||||
import {
|
||||
Badge,
|
||||
Card,
|
||||
Dropdown,
|
||||
Empty,
|
||||
Flex,
|
||||
Menu,
|
||||
MenuProps,
|
||||
Typography,
|
||||
} from '@/shared/antd-imports';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { DownOutlined } from '@/shared/antd-imports';
|
||||
// custom css file
|
||||
|
||||
@@ -43,8 +43,8 @@ import {
|
||||
import { themeWiseColor } from '@/utils/themeWiseColor';
|
||||
import KeyTypeColumn from './key-type-column/key-type-column';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import {
|
||||
fetchTasksV3,
|
||||
import {
|
||||
fetchTasksV3,
|
||||
fetchTaskListColumns,
|
||||
addCustomColumn,
|
||||
deleteCustomColumn as deleteCustomColumnFromTaskManagement,
|
||||
@@ -91,8 +91,6 @@ const CustomColumnModal = () => {
|
||||
// Use the column data passed from TaskListV2
|
||||
const openedColumn = currentColumnData;
|
||||
|
||||
|
||||
|
||||
// Function to reset all form and Redux state
|
||||
const resetModalData = () => {
|
||||
mainForm.resetFields();
|
||||
@@ -104,11 +102,12 @@ const CustomColumnModal = () => {
|
||||
const handleDeleteColumn = async () => {
|
||||
// The customColumnId should now be the UUID passed from TaskListV2
|
||||
// But also check the column data as a fallback, prioritizing uuid over id
|
||||
const columnUUID = customColumnId ||
|
||||
openedColumn?.uuid ||
|
||||
openedColumn?.id ||
|
||||
openedColumn?.custom_column_obj?.uuid ||
|
||||
openedColumn?.custom_column_obj?.id;
|
||||
const columnUUID =
|
||||
customColumnId ||
|
||||
openedColumn?.uuid ||
|
||||
openedColumn?.id ||
|
||||
openedColumn?.custom_column_obj?.uuid ||
|
||||
openedColumn?.custom_column_obj?.id;
|
||||
|
||||
if (!customColumnId || !columnUUID) {
|
||||
message.error('Cannot delete column: Missing UUID');
|
||||
@@ -260,10 +259,10 @@ const CustomColumnModal = () => {
|
||||
dispatch(addCustomColumn(newColumn));
|
||||
dispatch(toggleCustomColumnModalOpen(false));
|
||||
resetModalData();
|
||||
|
||||
|
||||
// Show success message
|
||||
message.success(t('customColumns.modal.createSuccessMessage'));
|
||||
|
||||
|
||||
// Refresh tasks and columns to include the new custom column values
|
||||
if (projectId) {
|
||||
dispatch(fetchTaskListColumns(projectId));
|
||||
@@ -301,11 +300,12 @@ const CustomColumnModal = () => {
|
||||
: null;
|
||||
|
||||
// Get the correct UUID for the update operation, prioritizing uuid over id
|
||||
const updateColumnUUID = customColumnId ||
|
||||
openedColumn?.uuid ||
|
||||
openedColumn?.id ||
|
||||
openedColumn?.custom_column_obj?.uuid ||
|
||||
openedColumn?.custom_column_obj?.id;
|
||||
const updateColumnUUID =
|
||||
customColumnId ||
|
||||
openedColumn?.uuid ||
|
||||
openedColumn?.id ||
|
||||
openedColumn?.custom_column_obj?.uuid ||
|
||||
openedColumn?.custom_column_obj?.id;
|
||||
|
||||
if (updatedColumn && updateColumnUUID) {
|
||||
try {
|
||||
@@ -377,7 +377,11 @@ const CustomColumnModal = () => {
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={customColumnModalType === 'create' ? t('customColumns.modal.addFieldTitle') : t('customColumns.modal.editFieldTitle')}
|
||||
title={
|
||||
customColumnModalType === 'create'
|
||||
? t('customColumns.modal.addFieldTitle')
|
||||
: t('customColumns.modal.editFieldTitle')
|
||||
}
|
||||
centered
|
||||
open={isCustomColumnModalOpen}
|
||||
onCancel={() => {
|
||||
@@ -490,7 +494,10 @@ const CustomColumnModal = () => {
|
||||
]}
|
||||
required={false}
|
||||
>
|
||||
<Input placeholder={t('customColumns.modal.columnTitlePlaceholder')} style={{ minWidth: '100%', width: 300 }} />
|
||||
<Input
|
||||
placeholder={t('customColumns.modal.columnTitlePlaceholder')}
|
||||
style={{ minWidth: '100%', width: 300 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
@@ -541,10 +548,14 @@ const CustomColumnModal = () => {
|
||||
)}
|
||||
|
||||
<Flex gap={8}>
|
||||
<Button onClick={() => {
|
||||
dispatch(toggleCustomColumnModalOpen(false));
|
||||
resetModalData();
|
||||
}}>{t('customColumns.modal.cancelButton')}</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
dispatch(toggleCustomColumnModalOpen(false));
|
||||
resetModalData();
|
||||
}}
|
||||
>
|
||||
{t('customColumns.modal.cancelButton')}
|
||||
</Button>
|
||||
{customColumnModalType === 'create' ? (
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t('customColumns.modal.createButton')}
|
||||
|
||||
@@ -35,8 +35,6 @@ const SelectionTypeColumn = () => {
|
||||
// Use the current column data passed from TaskListV2
|
||||
const openedColumn = currentColumnData;
|
||||
|
||||
|
||||
|
||||
// Load existing selections when in edit mode
|
||||
useEffect(() => {
|
||||
if (customColumnModalType === 'edit' && openedColumn?.custom_column_obj?.selectionsList) {
|
||||
|
||||
@@ -539,7 +539,7 @@ const TaskGroupWrapper = ({ taskGroups, groupBy }: TaskGroupWrapperProps) => {
|
||||
const updatedTasks = [...sourceGroup.tasks];
|
||||
updatedTasks.splice(fromIndex, 1);
|
||||
updatedTasks.splice(toIndex, 0, task);
|
||||
|
||||
|
||||
updatedTasks.forEach((task, index) => {
|
||||
taskUpdates.push({
|
||||
task_id: task.id,
|
||||
@@ -550,7 +550,7 @@ const TaskGroupWrapper = ({ taskGroups, groupBy }: TaskGroupWrapperProps) => {
|
||||
// Different groups - update both source and target
|
||||
const updatedSourceTasks = sourceGroup.tasks.filter((_, i) => i !== fromIndex);
|
||||
const updatedTargetTasks = [...targetGroup.tasks];
|
||||
|
||||
|
||||
if (isTargetGroupEmpty) {
|
||||
updatedTargetTasks.push(task);
|
||||
} else if (toIndex >= 0 && toIndex <= updatedTargetTasks.length) {
|
||||
@@ -573,7 +573,7 @@ const TaskGroupWrapper = ({ taskGroups, groupBy }: TaskGroupWrapperProps) => {
|
||||
task_id: task.id,
|
||||
sort_order: index + 1,
|
||||
};
|
||||
|
||||
|
||||
// Add group-specific updates
|
||||
if (groupBy === IGroupBy.STATUS) {
|
||||
update.status_id = targetGroup.id;
|
||||
@@ -582,7 +582,7 @@ const TaskGroupWrapper = ({ taskGroups, groupBy }: TaskGroupWrapperProps) => {
|
||||
} else if (groupBy === IGroupBy.PHASE) {
|
||||
update.phase_id = targetGroup.id;
|
||||
}
|
||||
|
||||
|
||||
taskUpdates.push(update);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -9,7 +9,12 @@ import Dropdown from 'antd/es/dropdown';
|
||||
import Input from 'antd/es/input';
|
||||
import Typography from 'antd/es/typography';
|
||||
import { MenuProps } from 'antd/es/menu';
|
||||
import { EditOutlined, EllipsisOutlined, RetweetOutlined, RightOutlined } from '@/shared/antd-imports';
|
||||
import {
|
||||
EditOutlined,
|
||||
EllipsisOutlined,
|
||||
RetweetOutlined,
|
||||
RightOutlined,
|
||||
} from '@/shared/antd-imports';
|
||||
import { colors } from '@/styles/colors';
|
||||
import './task-list-table-wrapper.css';
|
||||
import TaskListTable from '../task-list-table';
|
||||
|
||||
@@ -944,8 +944,6 @@ const SelectionFieldCell: React.FC<{
|
||||
columnKey: string;
|
||||
updateValue: (taskId: string, columnKey: string, value: string) => void;
|
||||
}> = ({ selectionsList, value, task, columnKey, updateValue }) => {
|
||||
|
||||
|
||||
return (
|
||||
<CustomColumnSelectionCell
|
||||
selectionsList={selectionsList}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Button, ConfigProvider, Flex, Form, Mentions, Space, Tooltip, Typography } from '@/shared/antd-imports';
|
||||
import {
|
||||
Button,
|
||||
ConfigProvider,
|
||||
Flex,
|
||||
Form,
|
||||
Mentions,
|
||||
Space,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@/shared/antd-imports';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppSelector } from '../../../../hooks/useAppSelector';
|
||||
|
||||
Reference in New Issue
Block a user