init
This commit is contained in:
@@ -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;
|
||||
@@ -0,0 +1,4 @@
|
||||
.setting-team-table .ant-table-thead > tr > th,
|
||||
.setting-team-table .ant-table-tbody > tr > td {
|
||||
padding: 12px 10px !important;
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user