This commit is contained in:
chamikaJ
2025-04-17 18:28:54 +05:30
parent f583291d8a
commit 8825b0410a
2837 changed files with 241385 additions and 127578 deletions

View File

@@ -0,0 +1,85 @@
import React, { useRef, useState } from 'react';
import { Button, Drawer, Form, Input, InputRef, Typography } from 'antd';
import { fetchTeams } from '@features/teams/teamSlice';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { useTranslation } from 'react-i18next';
import logger from '@/utils/errorLogger';
import { teamsApiService } from '@/api/teams/teams.api.service';
interface AddTeamDrawerProps {
isDrawerOpen: boolean;
onClose: () => void;
reloadTeams: () => void;
}
const AddTeamDrawer: React.FC<AddTeamDrawerProps> = ({ isDrawerOpen, onClose, reloadTeams }) => {
const { t } = useTranslation('admin-center/teams');
const dispatch = useAppDispatch();
const [form] = Form.useForm();
const [creating, setCreating] = useState(false);
const addTeamNameInputRef = useRef<InputRef>(null);
const handleFormSubmit = async (values: any) => {
if (!values.name || values.name.trim() === '') return;
try {
setCreating(true);
const newTeam = {
name: values.name,
};
const res = await teamsApiService.createTeam(newTeam);
if (res.done) {
onClose();
reloadTeams();
dispatch(fetchTeams());
}
} catch (error) {
logger.error('Error adding team', error);
} finally {
setCreating(false);
}
form.resetFields();
};
return (
<Drawer
title={
<Typography.Text style={{ fontWeight: 500, fontSize: 16 }}>
{t('drawerTitle')}
</Typography.Text>
}
open={isDrawerOpen}
destroyOnClose
afterOpenChange={() => {
setTimeout(() => {
addTeamNameInputRef.current?.focus();
}, 100);
}}
onClose={onClose}
>
<Form form={form} layout="vertical" onFinish={handleFormSubmit}>
<Form.Item
name="name"
label={t('label')}
rules={[
{
required: true,
message: t('message'),
},
]}
>
<Input placeholder={t('drawerPlaceholder')} ref={addTeamNameInputRef} />
</Form.Item>
<Form.Item>
<Button type="primary" style={{ width: '100%' }} htmlType="submit" loading={creating}>
{t('create')}
</Button>
</Form.Item>
</Form>
</Drawer>
);
};
export default AddTeamDrawer;

View File

@@ -0,0 +1,4 @@
.setting-team-table .ant-table-thead > tr > th,
.setting-team-table .ant-table-tbody > tr > td {
padding: 12px 10px !important;
}

View File

@@ -0,0 +1,198 @@
import {
Avatar,
Button,
Drawer,
Flex,
Form,
Input,
message,
Select,
Table,
TableProps,
Typography,
} from 'antd';
import React, { useState } from 'react';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { toggleSettingDrawer, updateTeam } from '@/features/teams/teamSlice';
import { TeamsType } from '@/types/admin-center/team.types';
import './settings-drawer.css';
import CustomAvatar from '@/components/CustomAvatar';
import { teamsApiService } from '@/api/teams/teams.api.service';
import logger from '@/utils/errorLogger';
import { adminCenterApiService } from '@/api/admin-center/admin-center.api.service';
import {
IOrganizationTeam,
IOrganizationTeamMember,
} from '@/types/admin-center/admin-center.types';
import Avatars from '@/components/avatars/avatars';
import { AvatarNamesMap } from '@/shared/constants';
import SingleAvatar from '@/components/common/single-avatar/single-avatar';
import { useTranslation } from 'react-i18next';
interface SettingTeamDrawerProps {
teamId: string;
isSettingDrawerOpen: boolean;
setIsSettingDrawerOpen: (value: boolean) => void;
}
const SettingTeamDrawer: React.FC<SettingTeamDrawerProps> = ({
teamId,
isSettingDrawerOpen,
setIsSettingDrawerOpen,
}) => {
const dispatch = useAppDispatch();
const { t } = useTranslation('admin-center/teams');
const [form] = Form.useForm();
const [teamData, setTeamData] = useState<IOrganizationTeam | null>(null);
const [loadingTeamMembers, setLoadingTeamMembers] = useState(false);
const [updatingTeam, setUpdatingTeam] = useState(false);
const [total, setTotal] = useState(0);
const fetchTeamMembers = async () => {
if (!teamId) return;
try {
setLoadingTeamMembers(true);
const res = await adminCenterApiService.getOrganizationTeam(teamId);
if (res.done) {
setTeamData(res.body);
setTotal(res.body.team_members?.length || 0);
form.setFieldsValue({ name: res.body.name || '' });
}
} catch (error) {
logger.error('Error fetching team members', error);
} finally {
setLoadingTeamMembers(false);
}
};
const handleFormSubmit = async (values: any) => {
console.log(values);
// const newTeam: TeamsType = {
// teamId: teamId,
// teamName: values.name,
// membersCount: team?.membersCount || 1,
// members: team?.members || ['Raveesha Dilanka'],
// owner: values.name,
// created: team?.created || new Date(),
// isActive: false,
// };
// dispatch(updateTeam(newTeam));
// dispatch(toggleSettingDrawer());
// form.resetFields();
// message.success('Team updated!');
};
const roleOptions = [
{ value: 'Admin', label: t('admin') },
{ value: 'Member', label: t('member') },
{ value: 'Owner', label: t('owner') },
];
const columns: TableProps['columns'] = [
{
title: t('user'),
key: 'user',
render: (_, record: IOrganizationTeamMember) => (
<Flex align="center" gap="8px" key={record.id}>
<SingleAvatar avatarUrl={record.avatar_url} name={record.name} />
<Typography.Text>{record.name || ''}</Typography.Text>
</Flex>
),
},
{
title: t('role'),
key: 'role',
render: (_, record: IOrganizationTeamMember) => (
<div>
<Select
style={{ width: '150px', height: '32px' }}
options={roleOptions.map(option => ({ ...option, key: option.value }))}
defaultValue={record.role_name || ''}
disabled={record.role_name === 'Owner'}
/>
</div>
),
},
];
return (
<Drawer
title={
<Typography.Text style={{ fontWeight: 500, fontSize: 16 }}>
{t('teamSettings')}
</Typography.Text>
}
width={550}
open={isSettingDrawerOpen}
onClose={() => {
form.resetFields();
setTeamData(null);
setTimeout(() => {
setIsSettingDrawerOpen(false);
}, 100);
}}
destroyOnClose
afterOpenChange={open => {
if (open) {
form.resetFields();
setTeamData(null);
fetchTeamMembers();
}
}}
footer={
<Flex justify="end">
<Button type="primary" onClick={form.submit} loading={updatingTeam}>
{t('update')}
</Button>
</Flex>
}
>
<Form
form={form}
layout="vertical"
onFinish={handleFormSubmit}
initialValues={{
name: teamData?.name,
}}
>
<Form.Item
name="name"
key="name"
label={t('teamName')}
rules={[
{
required: true,
message: t('message'),
},
]}
>
<Input placeholder={t('teamNamePlaceholder')} />
</Form.Item>
<Form.Item
name="users"
label={
<span>
{t('members')} ({teamData?.team_members?.length})
</span>
}
>
<Table
className="setting-team-table"
style={{ marginBottom: '24px' }}
columns={columns}
dataSource={teamData?.team_members?.map(member => ({ ...member, key: member.id }))}
pagination={false}
loading={loadingTeamMembers}
rowKey={record => record.team_member_id}
size="small"
/>
</Form.Item>
</Form>
</Drawer>
);
};
export default SettingTeamDrawer;

View File

@@ -0,0 +1,146 @@
import { adminCenterApiService } from '@/api/admin-center/admin-center.api.service';
import Avatars from '@/components/avatars/avatars';
import SettingTeamDrawer from '@/components/admin-center/teams/settings-drawer/settings-drawer';
import { toggleSettingDrawer, deleteTeam, fetchTeams } from '@/features/teams/teamSlice';
import { useAppDispatch } from '@/hooks/useAppDispatch';
import { IOrganizationTeam } from '@/types/admin-center/admin-center.types';
import logger from '@/utils/errorLogger';
import { SettingOutlined, DeleteOutlined } from '@ant-design/icons';
import { Badge, Button, Card, Popconfirm, Table, TableProps, Tooltip, Typography } from 'antd';
import { TFunction } from 'i18next';
import { useState } from 'react';
import { useMediaQuery } from 'react-responsive';
interface TeamsTableProps {
teams: IOrganizationTeam[];
currentTeam: IOrganizationTeam | null;
t: TFunction;
loading: boolean;
reloadTeams: () => void;
}
const TeamsTable: React.FC<TeamsTableProps> = ({
teams,
currentTeam = null,
t,
loading,
reloadTeams,
}) => {
const dispatch = useAppDispatch();
const isTablet = useMediaQuery({ query: '(min-width: 1000px)' });
const [deleting, setDeleting] = useState(false);
const [isSettingDrawerOpen, setIsSettingDrawerOpen] = useState(false);
const [selectedTeam, setSelectedTeam] = useState<string>('');
const handleTeamDelete = async (teamId: string) => {
if (!teamId) return;
try {
setDeleting(true);
const res = await adminCenterApiService.deleteTeam(teamId);
if (res.done) {
reloadTeams();
dispatch(fetchTeams());
}
} catch (error) {
logger.error('Error deleting team', error);
} finally {
setDeleting(false);
}
};
const columns: TableProps['columns'] = [
{
title: t('team'),
key: 'teamName',
render: (record: IOrganizationTeam) => (
<Typography.Text style={{ fontSize: `${isTablet ? '14px' : '10px'}` }}>
<Badge
status={currentTeam?.id === record.id ? 'success' : 'default'}
style={{ marginRight: '8px' }}
/>
{record.name}
</Typography.Text>
),
},
{
title: <span style={{ display: 'flex', justifyContent: 'center' }}>{t('membersCount')}</span>,
key: 'membersCount',
render: (record: IOrganizationTeam) => (
<Typography.Text
style={{
display: 'flex',
justifyContent: 'center',
fontSize: `${isTablet ? '14px' : '10px'}`,
}}
>
{record.members_count}
</Typography.Text>
),
},
{
title: t('members'),
key: 'members',
render: (record: IOrganizationTeam) => (
<span>
<Avatars members={record.names} />
</span>
),
},
{
title: '',
key: 'button',
render: (record: IOrganizationTeam) => (
<div className="row-buttons">
<Tooltip title={t('settings')}>
<Button
style={{ marginRight: '8px' }}
size="small"
onClick={() => {
setSelectedTeam(record.id || '');
setIsSettingDrawerOpen(true);
}}
>
<SettingOutlined />
</Button>
</Tooltip>
<Tooltip title={t('delete')}>
<Popconfirm title={t('popTitle')} onConfirm={() => handleTeamDelete(record.id || '')}>
<Button size="small">
<DeleteOutlined />
</Button>
</Popconfirm>
</Tooltip>
</div>
),
},
];
return (
<>
<Card>
<Table
rowClassName="team-table-row"
className="team-table"
size="small"
columns={columns}
dataSource={teams}
rowKey={record => record.id}
loading={loading}
pagination={{
showSizeChanger: true,
defaultPageSize: 20,
pageSizeOptions: ['5', '10', '15', '20', '50', '100'],
}}
/>
</Card>
<SettingTeamDrawer
teamId={selectedTeam}
isSettingDrawerOpen={isSettingDrawerOpen}
setIsSettingDrawerOpen={setIsSettingDrawerOpen}
/>
</>
);
};
export default TeamsTable;