refactor: centralize Ant Design imports and remove unused components
- Replaced direct imports from 'antd' with centralized imports from '@/shared/antd-imports' across multiple components for improved maintainability. - Removed the TawkTo component as it is no longer needed. - Updated the CustomAvatar and various other components to utilize the new import structure, enhancing code clarity and consistency. - Introduced the InviteTeamMembersModal component, streamlining the invitation process for team members.
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import React from 'react';
|
||||
import Tooltip from 'antd/es/tooltip';
|
||||
import Avatar from 'antd/es/avatar';
|
||||
|
||||
import { AvatarNamesMap } from '../shared/constants';
|
||||
import { Avatar, Tooltip } from '@/shared/antd-imports';
|
||||
|
||||
interface CustomAvatarProps {
|
||||
avatarName: string;
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import { useEffect } from 'react';
|
||||
|
||||
// Add TypeScript declarations for Tawk_API
|
||||
declare global {
|
||||
interface Window {
|
||||
Tawk_API?: any;
|
||||
Tawk_LoadStart?: Date;
|
||||
}
|
||||
}
|
||||
|
||||
interface TawkToProps {
|
||||
propertyId: string;
|
||||
widgetId: string;
|
||||
}
|
||||
|
||||
const TawkTo: React.FC<TawkToProps> = ({ propertyId, widgetId }) => {
|
||||
useEffect(() => {
|
||||
// Initialize tawk.to chat
|
||||
const s1 = document.createElement('script');
|
||||
s1.async = true;
|
||||
s1.src = `https://embed.tawk.to/${propertyId}/${widgetId}`;
|
||||
s1.setAttribute('crossorigin', '*');
|
||||
|
||||
const s0 = document.getElementsByTagName('script')[0];
|
||||
s0.parentNode?.insertBefore(s1, s0);
|
||||
|
||||
return () => {
|
||||
// Clean up when the component unmounts
|
||||
// Remove the script tag
|
||||
const tawkScript = document.querySelector(`script[src*="tawk.to/${propertyId}"]`);
|
||||
if (tawkScript && tawkScript.parentNode) {
|
||||
tawkScript.parentNode.removeChild(tawkScript);
|
||||
}
|
||||
|
||||
// Remove the tawk.to iframe
|
||||
const tawkIframe = document.getElementById('tawk-iframe');
|
||||
if (tawkIframe) {
|
||||
tawkIframe.remove();
|
||||
}
|
||||
|
||||
// Reset Tawk globals
|
||||
delete window.Tawk_API;
|
||||
delete window.Tawk_LoadStart;
|
||||
};
|
||||
}, [propertyId, widgetId]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default TawkTo;
|
||||
@@ -1,11 +1,8 @@
|
||||
import { adminCenterApiService } from '@/api/admin-center/admin-center.api.service';
|
||||
import { fetchStorageInfo } from '@/features/admin-center/admin-center.slice';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { SUBSCRIPTION_STATUS } from '@/shared/constants';
|
||||
import { IBillingAccountStorage } from '@/types/admin-center/admin-center.types';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { Card, Progress, Typography } from 'antd/es';
|
||||
import { Card, Progress, Typography } from '@/shared/antd-imports';
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { adminCenterApiService } from '@/api/admin-center/admin-center.api.service';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { EnterOutlined, EditOutlined } from '@/shared/antd-imports';
|
||||
import { Card, Button, Tooltip, Typography } from '@/shared/antd-imports';
|
||||
import TextArea from 'antd/es/input/TextArea';
|
||||
import { Card, Button, Tooltip, Typography, TextArea } from '@/shared/antd-imports';
|
||||
import { TFunction } from 'i18next';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
|
||||
@@ -1,31 +1,32 @@
|
||||
import { InputRef } from 'antd/es/input';
|
||||
import Card from 'antd/es/card';
|
||||
import Checkbox from 'antd/es/checkbox';
|
||||
import Divider from 'antd/es/divider';
|
||||
import Dropdown from 'antd/es/dropdown';
|
||||
import Empty from 'antd/es/empty';
|
||||
import Flex from 'antd/es/flex';
|
||||
import Input from 'antd/es/input';
|
||||
import List from 'antd/es/list';
|
||||
import Typography from 'antd/es/typography';
|
||||
import Button from 'antd/es/button';
|
||||
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
InputRef,
|
||||
PlusOutlined,
|
||||
UsergroupAddOutlined,
|
||||
Card,
|
||||
Flex,
|
||||
Input,
|
||||
List,
|
||||
Typography,
|
||||
Checkbox,
|
||||
Divider,
|
||||
Button,
|
||||
Empty,
|
||||
Dropdown,
|
||||
CheckboxChangeEvent,
|
||||
} from '@/shared/antd-imports';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { toggleProjectMemberDrawer } from '../../../features/projects/singleProject/members/projectMembersSlice';
|
||||
import { colors } from '../../../styles/colors';
|
||||
import { PlusOutlined, UsergroupAddOutlined } from '@/shared/antd-imports';
|
||||
import { toggleProjectMemberDrawer } from '@features/projects/singleProject/members/projectMembersSlice';
|
||||
import { colors } from '@/styles/colors';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import SingleAvatar from '@/components/common/single-avatar/single-avatar';
|
||||
import { CheckboxChangeEvent } from 'antd/es/checkbox';
|
||||
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
||||
import { ITeamMembersViewModel } from '@/types/teamMembers/teamMembersViewModel.types';
|
||||
import { sortByBooleanField, sortBySelection, sortTeamMembers } from '@/utils/sort-team-members';
|
||||
import { sortTeamMembers } from '@/utils/sort-team-members';
|
||||
import { useAuthService } from '@/hooks/useAuth';
|
||||
import { useSocket } from '@/socket/socketContext';
|
||||
import { SocketEvents } from '@/shared/socket-events';
|
||||
import { getTeamMembers } from '@/features/team-members/team-members.slice';
|
||||
|
||||
interface BoardAssigneeSelectorProps {
|
||||
task: IProjectTask;
|
||||
|
||||
@@ -0,0 +1,280 @@
|
||||
import { Button, Flex, Input, message, Modal, Select, Space, Typography, List, Avatar } from '@/shared/antd-imports';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import {
|
||||
toggleInviteMemberDrawer,
|
||||
triggerTeamMembersRefresh,
|
||||
} from '../../../features/settings/member/memberSlice';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useState, useEffect, useCallback, useMemo, memo } from 'react';
|
||||
import { ITeamMemberCreateRequest } from '@/types/teamMembers/team-member-create-request';
|
||||
import { DeleteOutlined, UserOutlined } from '@ant-design/icons';
|
||||
|
||||
interface MemberEntry {
|
||||
email: string;
|
||||
access: 'member' | 'admin' | 'guest';
|
||||
}
|
||||
|
||||
|
||||
const InviteTeamMembersModal = () => {
|
||||
const [newMembers, setNewMembers] = useState<MemberEntry[]>([]);
|
||||
const [emailInput, setEmailInput] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
const { t } = useTranslation('settings/team-members');
|
||||
const isModalOpen = useAppSelector(state => state.memberReducer.isInviteMemberDrawerOpen);
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (isModalOpen) {
|
||||
// Reset state when modal opens
|
||||
setNewMembers([]);
|
||||
setEmailInput('');
|
||||
// Focus on email input when modal opens
|
||||
setTimeout(() => {
|
||||
const emailInput = document.querySelector('input[type="text"]');
|
||||
if (emailInput) {
|
||||
(emailInput as HTMLElement).focus();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
}, [isModalOpen]);
|
||||
|
||||
const handleFormSubmit = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
if (newMembers.length === 0) {
|
||||
message.error('Please add at least one member');
|
||||
return;
|
||||
}
|
||||
|
||||
// Send invitations for each member
|
||||
const promises = newMembers.map(member => {
|
||||
const body: ITeamMemberCreateRequest = {
|
||||
emails: [member.email],
|
||||
is_admin: member.access === 'admin',
|
||||
is_guest: member.access === 'guest',
|
||||
};
|
||||
return teamMembersApiService.createTeamMember(body);
|
||||
});
|
||||
|
||||
const results = await Promise.allSettled(promises);
|
||||
const successful = results.filter(r => r.status === 'fulfilled').length;
|
||||
|
||||
if (successful > 0) {
|
||||
message.success(`${successful} invitation(s) sent successfully`);
|
||||
setNewMembers([]);
|
||||
setEmailInput('');
|
||||
dispatch(triggerTeamMembersRefresh());
|
||||
dispatch(toggleInviteMemberDrawer());
|
||||
}
|
||||
|
||||
const failed = results.length - successful;
|
||||
if (failed > 0) {
|
||||
message.error(`${failed} invitation(s) failed`);
|
||||
}
|
||||
} catch (error) {
|
||||
message.error(t('createMemberErrorMessage'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setNewMembers([]);
|
||||
setEmailInput('');
|
||||
dispatch(toggleInviteMemberDrawer());
|
||||
};
|
||||
|
||||
const handleEmailKeyPress = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault();
|
||||
|
||||
const trimmedEmail = emailInput.trim();
|
||||
|
||||
// Don't show error for empty input, just ignore
|
||||
if (!trimmedEmail) {
|
||||
return;
|
||||
}
|
||||
|
||||
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
if (!emailPattern.test(trimmedEmail)) {
|
||||
message.error('Please enter a valid email address');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if email already exists
|
||||
if (newMembers.find(m => m.email === trimmedEmail)) {
|
||||
message.warning('Email already added');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add new member
|
||||
setNewMembers([...newMembers, { email: trimmedEmail, access: 'member' }]);
|
||||
setEmailInput('');
|
||||
}
|
||||
};
|
||||
|
||||
const updateMemberAccess = (index: number, access: 'member' | 'admin' | 'guest') => {
|
||||
const updated = [...newMembers];
|
||||
updated[index].access = access;
|
||||
setNewMembers(updated);
|
||||
};
|
||||
|
||||
const removeMember = (index: number) => {
|
||||
setNewMembers(newMembers.filter((_, i) => i !== index));
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
const accessOptions = useMemo(() => [
|
||||
{ value: 'member', label: t('memberText') },
|
||||
{ value: 'admin', label: t('adminText') },
|
||||
{ value: 'guest', label: t('guestText') },
|
||||
], [t]);
|
||||
|
||||
const renderContent = () => (
|
||||
<div>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Input
|
||||
placeholder="Enter email address and press Enter to add"
|
||||
value={emailInput}
|
||||
onChange={(e) => setEmailInput(e.target.value)}
|
||||
onKeyPress={handleEmailKeyPress}
|
||||
size="middle"
|
||||
autoFocus
|
||||
style={{
|
||||
borderRadius: 8,
|
||||
fontSize: 14
|
||||
}}
|
||||
/>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 12, marginTop: 6, display: 'block', fontStyle: 'italic' }}>
|
||||
Press Enter to add • Multiple emails can be added
|
||||
</Typography.Text>
|
||||
</div>
|
||||
|
||||
{newMembers.length > 0 && (
|
||||
<div style={{ marginBottom: 20 }}>
|
||||
<Typography.Text type="secondary" style={{ fontSize: 13, marginBottom: 12, display: 'block', fontWeight: 500 }}>
|
||||
Members to invite ({newMembers.length})
|
||||
</Typography.Text>
|
||||
<div style={{
|
||||
background: 'rgba(0, 0, 0, 0.02)',
|
||||
borderRadius: 8,
|
||||
padding: '8px 12px',
|
||||
border: '1px solid rgba(0, 0, 0, 0.06)'
|
||||
}}>
|
||||
<List
|
||||
size="small"
|
||||
dataSource={newMembers}
|
||||
split={false}
|
||||
renderItem={(member, index) => (
|
||||
<List.Item
|
||||
style={{
|
||||
padding: '8px 0',
|
||||
borderRadius: 6,
|
||||
marginBottom: index < newMembers.length - 1 ? 4 : 0
|
||||
}}
|
||||
actions={[
|
||||
<Select
|
||||
size="small"
|
||||
value={member.access}
|
||||
onChange={(value) => updateMemberAccess(index, value)}
|
||||
options={accessOptions}
|
||||
style={{ width: 90 }}
|
||||
variant="outlined"
|
||||
/>,
|
||||
<Button
|
||||
type="text"
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => removeMember(index)}
|
||||
size="small"
|
||||
danger
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}
|
||||
/>
|
||||
]}
|
||||
>
|
||||
<List.Item.Meta
|
||||
avatar={
|
||||
<Avatar size={32} style={{
|
||||
backgroundColor: '#1677ff',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
}}>
|
||||
<UserOutlined style={{ fontSize: 14 }} />
|
||||
</Avatar>
|
||||
}
|
||||
title={
|
||||
<span style={{
|
||||
fontSize: 14,
|
||||
fontWeight: 500,
|
||||
color: 'rgba(0, 0, 0, 0.88)'
|
||||
}}>
|
||||
{member.email}
|
||||
</span>
|
||||
}
|
||||
description={
|
||||
<span style={{
|
||||
fontSize: 12,
|
||||
color: '#52c41a',
|
||||
fontWeight: 500
|
||||
}}>
|
||||
Ready to invite
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
<Typography.Text strong style={{ fontSize: 18 }}>
|
||||
{t('inviteTeamMembersModalTitle')}
|
||||
</Typography.Text>
|
||||
}
|
||||
open={isModalOpen}
|
||||
onCancel={handleClose}
|
||||
width={520}
|
||||
destroyOnClose
|
||||
bodyStyle={{ padding: '16px 20px' }}
|
||||
footer={
|
||||
<Flex justify="end">
|
||||
<Space>
|
||||
<Button onClick={handleClose}>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleFormSubmit}
|
||||
loading={loading}
|
||||
disabled={newMembers.length === 0}
|
||||
>
|
||||
Send
|
||||
</Button>
|
||||
</Space>
|
||||
</Flex>
|
||||
}
|
||||
>
|
||||
{renderContent()}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(InviteTeamMembersModal);
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from './InviteTeamMembersModal';
|
||||
@@ -1,26 +1,32 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Card, Empty } from '@/shared/antd-imports';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { RootState } from '@/app/store';
|
||||
import '../EnhancedKanbanBoard.css';
|
||||
import '../EnhancedKanbanGroup.css';
|
||||
import '../EnhancedKanbanTaskCard.css';
|
||||
import ImprovedTaskFilters from '../../task-management/improved-task-filters';
|
||||
import Card from 'antd/es/card';
|
||||
import Spin from 'antd/es/spin';
|
||||
import Empty from 'antd/es/empty';
|
||||
import { reorderGroups, reorderEnhancedKanbanGroups, reorderTasks, reorderEnhancedKanbanTasks, fetchEnhancedKanbanLabels, fetchEnhancedKanbanGroups, fetchEnhancedKanbanTaskAssignees } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||
import { fetchStatusesCategories } from '@/features/taskAttributes/taskStatusSlice';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import KanbanGroup from './KanbanGroup';
|
||||
import EnhancedKanbanCreateSection from '../EnhancedKanbanCreateSection';
|
||||
import { useSocket } from '@/socket/socketContext';
|
||||
import { SocketEvents } from '@/shared/socket-events';
|
||||
import { useAuthService } from '@/hooks/useAuth';
|
||||
import { useSocket } from '@/socket/socketContext';
|
||||
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
|
||||
import { statusApiService } from '@/api/taskAttributes/status/status.api.service';
|
||||
import alertService from '@/services/alerts/alertService';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import {
|
||||
reorderGroups,
|
||||
reorderEnhancedKanbanGroups,
|
||||
reorderTasks,
|
||||
reorderEnhancedKanbanTasks,
|
||||
fetchEnhancedKanbanLabels,
|
||||
fetchEnhancedKanbanGroups,
|
||||
fetchEnhancedKanbanTaskAssignees,
|
||||
} from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||
import { fetchStatusesCategories } from '@/features/taskAttributes/taskStatusSlice';
|
||||
import { checkTaskDependencyStatus } from '@/utils/check-task-dependency-status';
|
||||
import { useTaskSocketHandlers } from '@/hooks/useTaskSocketHandlers';
|
||||
import KanbanGroup from './KanbanGroup';
|
||||
import EnhancedKanbanCreateSection from '../EnhancedKanbanCreateSection';
|
||||
import ImprovedTaskFilters from '../../task-management/improved-task-filters';
|
||||
import { SocketEvents } from '@/shared/socket-events';
|
||||
import '../EnhancedKanbanBoard.css';
|
||||
import '../EnhancedKanbanGroup.css';
|
||||
import '../EnhancedKanbanTaskCard.css';
|
||||
|
||||
const EnhancedKanbanBoardNativeDnD: React.FC<{ projectId: string }> = ({ projectId }) => {
|
||||
const dispatch = useDispatch();
|
||||
|
||||
@@ -1,37 +1,38 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
ForkOutlined,
|
||||
CaretDownFilled,
|
||||
CaretRightFilled,
|
||||
Tag,
|
||||
Flex,
|
||||
Tooltip,
|
||||
Progress,
|
||||
Typography,
|
||||
Button,
|
||||
Divider,
|
||||
List,
|
||||
Skeleton,
|
||||
PlusOutlined,
|
||||
Dayjs,
|
||||
dayjs,
|
||||
} from '@/shared/antd-imports';
|
||||
import { useSortable, defaultAnimateLayoutChanges } from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import './EnhancedKanbanTaskCard.css';
|
||||
import Flex from 'antd/es/flex';
|
||||
import Tag from 'antd/es/tag';
|
||||
import Tooltip from 'antd/es/tooltip';
|
||||
import Progress from 'antd/es/progress';
|
||||
import Button from 'antd/es/button';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { setShowTaskDrawer, setSelectedTaskId } from '@/features/task-drawer/task-drawer.slice';
|
||||
import PrioritySection from '../board/taskCard/priority-section/priority-section';
|
||||
import Typography from 'antd/es/typography';
|
||||
import CustomDueDatePicker from '../board/custom-due-date-picker';
|
||||
import { themeWiseColor } from '@/utils/themeWiseColor';
|
||||
import { ForkOutlined } from '@/shared/antd-imports';
|
||||
import { Dayjs } from 'dayjs';
|
||||
import dayjs from 'dayjs';
|
||||
import { CaretDownFilled, CaretRightFilled } from '@/shared/antd-imports';
|
||||
import {
|
||||
fetchBoardSubTasks,
|
||||
toggleTaskExpansion,
|
||||
} from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||
import { Divider } from '@/shared/antd-imports';
|
||||
import { List } from '@/shared/antd-imports';
|
||||
import { Skeleton } from '@/shared/antd-imports';
|
||||
import { PlusOutlined } from '@/shared/antd-imports';
|
||||
import { IProjectTask } from '@/types/project/projectTasksViewModel.types';
|
||||
import { themeWiseColor } from '@/utils/themeWiseColor';
|
||||
import './EnhancedKanbanTaskCard.css';
|
||||
import LazyAssigneeSelectorWrapper from '../task-management/lazy-assignee-selector';
|
||||
import CustomDueDatePicker from '../board/custom-due-date-picker';
|
||||
import BoardSubTaskCard from '@/pages/projects/projectView/board/board-section/board-sub-task-card/board-sub-task-card';
|
||||
import BoardCreateSubtaskCard from '@/pages/projects/projectView/board/board-section/board-sub-task-card/board-create-sub-task-card';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import EnhancedKanbanCreateSubtaskCard from './EnhancedKanbanCreateSubtaskCard';
|
||||
import LazyAssigneeSelectorWrapper from '@/components/task-management/lazy-assignee-selector';
|
||||
import AvatarGroup from '@/components/AvatarGroup';
|
||||
|
||||
interface EnhancedKanbanTaskCardProps {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { SettingOutlined } from '@/shared/antd-imports';
|
||||
import Tooltip from 'antd/es/tooltip';
|
||||
import Button from 'antd/es/button';
|
||||
import { SettingOutlined, Tooltip, Button } from '@/shared/antd-imports';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { toggleDrawer } from '../../../features/projects/status/StatusSlice';
|
||||
import { colors } from '@/styles/colors';
|
||||
|
||||
@@ -1,20 +1,16 @@
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
import { Form, Input, Select, Button, Drawer, Flex, Badge } from '@/shared/antd-imports';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Flex from 'antd/es/flex';
|
||||
import Badge from 'antd/es/badge';
|
||||
import Drawer from 'antd/es/drawer';
|
||||
import Form from 'antd/es/form';
|
||||
import Input from 'antd/es/input';
|
||||
import Select from 'antd/es/select';
|
||||
import Button from 'antd/es/button/button';
|
||||
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { toggleDrawer } from '@/features/projects/status/StatusSlice';
|
||||
|
||||
import './create-status-drawer.css';
|
||||
|
||||
import { createStatus, fetchStatusesCategories, fetchStatuses } from '@/features/taskAttributes/taskStatusSlice';
|
||||
import {
|
||||
createStatus,
|
||||
fetchStatusesCategories,
|
||||
fetchStatuses,
|
||||
} from '@/features/taskAttributes/taskStatusSlice';
|
||||
import { ITaskStatusCategory } from '@/types/status.types';
|
||||
import { useMixpanelTracking } from '@/hooks/useMixpanelTracking';
|
||||
import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||
@@ -22,6 +18,8 @@ import { evt_project_board_create_status } from '@/shared/worklenz-analytics-eve
|
||||
import { fetchTaskGroups } from '@/features/tasks/tasks.slice';
|
||||
import { fetchBoardTaskGroups } from '@/features/board/board-slice';
|
||||
|
||||
import './create-status-drawer.css';
|
||||
|
||||
const StatusDrawer: React.FC = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const { trackMixpanelEvent } = useMixpanelTracking();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { Drawer, Alert, Card, Select, Button, Typography, Badge, Form } from '@/shared/antd-imports';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Form from 'antd/es/form';
|
||||
import { useAppSelector } from '@/hooks/useAppSelector';
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch';
|
||||
import { fetchStatuses, fetchStatusesCategories } from '@/features/taskAttributes/taskStatusSlice';
|
||||
@@ -9,9 +9,7 @@ import useTabSearchParam from '@/hooks/useTabSearchParam';
|
||||
import { fetchTaskGroups } from '@/features/tasks/tasks.slice';
|
||||
|
||||
import { deleteStatusToggleDrawer } from '@/features/projects/status/DeleteStatusSlice';
|
||||
import { Drawer, Alert, Card, Select, Button, Typography, Badge } from '@/shared/antd-imports';
|
||||
import { DownOutlined } from '@/shared/antd-imports';
|
||||
import { useSelector } from 'react-redux';
|
||||
import {
|
||||
deleteSection,
|
||||
IGroupBy,
|
||||
@@ -21,7 +19,6 @@ import { phasesApiService } from '@/api/taskAttributes/phases/phases.api.service
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { fetchEnhancedKanbanGroups } from '@/features/enhanced-kanban/enhanced-kanban.slice';
|
||||
const { Title, Text } = Typography;
|
||||
const { Option } = Select;
|
||||
|
||||
const DeleteStatusDrawer: React.FC = () => {
|
||||
const [currentStatus, setCurrentStatus] = useState<string>('');
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { useEffect, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { CaretDownFilled } from '@/shared/antd-imports';
|
||||
import { ConfigProvider, Flex, Dropdown, Button } from 'antd/es';
|
||||
import { CaretDownFilled, Dropdown, Button, Flex, ConfigProvider } from '@/shared/antd-imports';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import ConfigPhaseButton from '@features/projects/singleProject/phase/ConfigPhaseButton';
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import { CaretDownFilled } from '@/shared/antd-imports';
|
||||
import Badge from 'antd/es/badge';
|
||||
import Button from 'antd/es/button';
|
||||
import Card from 'antd/es/card';
|
||||
import Checkbox from 'antd/es/checkbox';
|
||||
import Dropdown from 'antd/es/dropdown';
|
||||
import Empty from 'antd/es/empty';
|
||||
import Flex from 'antd/es/flex';
|
||||
import Input, { InputRef } from 'antd/es/input';
|
||||
import List from 'antd/es/list';
|
||||
import Space from 'antd/es/space';
|
||||
import {
|
||||
CaretDownFilled,
|
||||
Card,
|
||||
Flex,
|
||||
Input,
|
||||
List,
|
||||
Checkbox,
|
||||
Dropdown,
|
||||
Button,
|
||||
Empty,
|
||||
Space,
|
||||
InputRef,
|
||||
} from '@/shared/antd-imports';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
|
||||
import { useMemo, useRef, useState, useEffect } from 'react';
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { Col, ConfigProvider, Flex, Menu, MenuProps, Alert } from '@/shared/antd-imports';
|
||||
import { createPortal } from 'react-dom';
|
||||
|
||||
import InviteTeamMembers from '../../components/common/invite-team-members/invite-team-members';
|
||||
import InviteTeamMembersModal from '../../components/common/invite-team-members-modal';
|
||||
import InviteButton from './invite/InviteButton';
|
||||
import MobileMenuButton from './mobileMenu/MobileMenuButton';
|
||||
import NavbarLogo from './navbar-logo';
|
||||
@@ -179,7 +179,7 @@ const Navbar = () => {
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
{isOwnerOrAdmin && createPortal(<InviteTeamMembers />, document.body, 'invite-team-members')}
|
||||
{isOwnerOrAdmin && createPortal(<InviteTeamMembersModal />, document.body, 'invite-team-members-modal')}
|
||||
{createPortal(<NotificationDrawer />, document.body, 'notification-drawer')}
|
||||
</Col>
|
||||
);
|
||||
|
||||
@@ -67,6 +67,8 @@ import {
|
||||
Radio,
|
||||
} from 'antd/es';
|
||||
|
||||
import TextArea from 'antd/es/input/TextArea';
|
||||
|
||||
// Icons - Import commonly used ones
|
||||
export {
|
||||
EditOutlined,
|
||||
@@ -240,6 +242,7 @@ export {
|
||||
Timeline,
|
||||
Mentions,
|
||||
Radio,
|
||||
TextArea
|
||||
};
|
||||
|
||||
// TypeScript Types - Import commonly used ones
|
||||
@@ -263,6 +266,7 @@ export type {
|
||||
PaginationProps,
|
||||
CollapseProps,
|
||||
TablePaginationConfig,
|
||||
CheckboxChangeEvent
|
||||
} from 'antd/es';
|
||||
|
||||
// Dayjs
|
||||
|
||||
@@ -4,4 +4,5 @@ export interface ITeamMemberCreateRequest extends ITeamMember {
|
||||
job_title?: string | null;
|
||||
emails?: string | string[];
|
||||
is_admin?: boolean;
|
||||
is_guest?: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user