init
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import { Table, TableProps, Typography } from 'antd';
|
||||
import React, { useMemo } from 'react';
|
||||
import { IOrganizationAdmin } from '@/types/admin-center/admin-center.types';
|
||||
|
||||
interface OrganizationAdminsTableProps {
|
||||
organizationAdmins: IOrganizationAdmin[] | null;
|
||||
loading: boolean;
|
||||
themeMode: string;
|
||||
}
|
||||
|
||||
const { Text } = Typography;
|
||||
|
||||
const OrganizationAdminsTable: React.FC<OrganizationAdminsTableProps> = ({
|
||||
organizationAdmins,
|
||||
loading,
|
||||
themeMode,
|
||||
}) => {
|
||||
const columns = useMemo<TableProps<IOrganizationAdmin>['columns']>(
|
||||
() => [
|
||||
{
|
||||
title: <Text strong>Name</Text>,
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
render: (text, record) => (
|
||||
<div>
|
||||
<Text>
|
||||
{text}
|
||||
{record.is_owner && <Text> (Owner)</Text>}
|
||||
</Text>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: <Text strong>Email</Text>,
|
||||
dataIndex: 'email',
|
||||
key: 'email',
|
||||
render: text => <Text>{text}</Text>,
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<Table<IOrganizationAdmin>
|
||||
className="organization-admins-table"
|
||||
columns={columns}
|
||||
dataSource={organizationAdmins || []}
|
||||
loading={loading}
|
||||
showHeader={false}
|
||||
pagination={{
|
||||
size: 'small',
|
||||
pageSize: 10,
|
||||
hideOnSinglePage: true,
|
||||
}}
|
||||
rowKey="email"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrganizationAdminsTable;
|
||||
@@ -0,0 +1,122 @@
|
||||
import { adminCenterApiService } from '@/api/admin-center/admin-center.api.service';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { EnterOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { Card, Button, Tooltip, Typography } from 'antd';
|
||||
import TextArea from 'antd/es/input/TextArea';
|
||||
import Paragraph from 'antd/es/typography/Paragraph';
|
||||
import { TFunction } from 'i18next';
|
||||
import { useState, useEffect } from 'react';
|
||||
|
||||
interface OrganizationNameProps {
|
||||
themeMode: string;
|
||||
name: string;
|
||||
t: TFunction;
|
||||
refetch: () => void;
|
||||
}
|
||||
|
||||
const OrganizationName = ({ themeMode, name, t, refetch }: OrganizationNameProps) => {
|
||||
const [isEditable, setIsEditable] = useState(false);
|
||||
const [newName, setNewName] = useState(name);
|
||||
|
||||
useEffect(() => {
|
||||
setNewName(name);
|
||||
}, [name]);
|
||||
|
||||
const handleBlur = () => {
|
||||
if (newName.trim() === '') {
|
||||
setNewName(name);
|
||||
setIsEditable(false);
|
||||
return;
|
||||
}
|
||||
if (newName !== name) {
|
||||
updateOrganizationName();
|
||||
}
|
||||
setIsEditable(false);
|
||||
};
|
||||
|
||||
const handleNameChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||
setNewName(e.target.value);
|
||||
};
|
||||
|
||||
const updateOrganizationName = async () => {
|
||||
try {
|
||||
const trimmedName = newName.trim();
|
||||
const res = await adminCenterApiService.updateOrganizationName({ name: trimmedName });
|
||||
if (res.done) {
|
||||
refetch();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error updating organization name', error);
|
||||
setNewName(name);
|
||||
}
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Escape') {
|
||||
setNewName(name);
|
||||
setIsEditable(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Typography.Title level={5} style={{ margin: 0, marginBottom: '0.5rem' }}>
|
||||
{t('name')}
|
||||
</Typography.Title>
|
||||
<div style={{ paddingTop: '8px' }}>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
{isEditable ? (
|
||||
<div style={{ position: 'relative' }}>
|
||||
<TextArea
|
||||
style={{
|
||||
height: '32px',
|
||||
paddingRight: '40px',
|
||||
resize: 'none',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
onPressEnter={handleBlur}
|
||||
value={newName}
|
||||
onChange={handleNameChange}
|
||||
onBlur={handleBlur}
|
||||
onKeyDown={handleKeyDown}
|
||||
autoFocus
|
||||
maxLength={100}
|
||||
placeholder={t('enterOrganizationName')}
|
||||
/>
|
||||
<Button
|
||||
icon={<EnterOutlined style={{ color: '#1890ff' }} />}
|
||||
type="text"
|
||||
style={{
|
||||
position: 'absolute',
|
||||
right: '4px',
|
||||
top: '50%',
|
||||
transform: 'translateY(-50%)',
|
||||
padding: '4px 8px',
|
||||
color: '#1890ff',
|
||||
}}
|
||||
onClick={handleBlur}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<Typography.Text>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
{name}
|
||||
<Tooltip title={t('edit')}>
|
||||
<Button
|
||||
onClick={() => setIsEditable(true)}
|
||||
size="small"
|
||||
type="text"
|
||||
icon={<EditOutlined />}
|
||||
style={{ padding: '4px', color: '#1890ff' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</Typography.Text>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrganizationName;
|
||||
@@ -0,0 +1,120 @@
|
||||
import { adminCenterApiService } from '@/api/admin-center/admin-center.api.service';
|
||||
import { IOrganization } from '@/types/admin-center/admin-center.types';
|
||||
import logger from '@/utils/errorLogger';
|
||||
import { MailOutlined, PhoneOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { Card, Tooltip, Input, Button, Typography, InputRef } from 'antd';
|
||||
import { TFunction } from 'i18next';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
interface OrganizationOwnerProps {
|
||||
themeMode: string;
|
||||
organization: IOrganization | null;
|
||||
t: TFunction;
|
||||
refetch: () => void;
|
||||
}
|
||||
|
||||
const OrganizationOwner = ({ themeMode, organization, t, refetch }: OrganizationOwnerProps) => {
|
||||
const [isEditableContactNumber, setIsEditableContactNumber] = useState(false);
|
||||
const [number, setNumber] = useState(organization?.contact_number || '');
|
||||
const contactNoRef = useRef<InputRef>(null);
|
||||
|
||||
const handleContactNumberBlur = () => {
|
||||
setIsEditableContactNumber(false);
|
||||
updateOrganizationContactNumber();
|
||||
};
|
||||
|
||||
const updateOrganizationContactNumber = async () => {
|
||||
try {
|
||||
const res = await adminCenterApiService.updateOwnerContactNumber({ contact_number: number });
|
||||
if (res.done) {
|
||||
refetch();
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error updating organization contact number:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const addContactNumber = () => {
|
||||
setIsEditableContactNumber(true);
|
||||
setTimeout(() => {
|
||||
contactNoRef.current?.focus();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const handleEditContactNumber = () => {
|
||||
setIsEditableContactNumber(true);
|
||||
};
|
||||
|
||||
const handleContactNumber = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const inputValue = e.target.value;
|
||||
setNumber(inputValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Typography.Title level={5} style={{ margin: 0, marginBottom: '0.5rem' }}>
|
||||
{t('owner')}
|
||||
</Typography.Title>
|
||||
<div style={{ paddingTop: '8px' }}>
|
||||
<div style={{ marginBottom: '8px' }}>
|
||||
<Typography.Text
|
||||
style={{
|
||||
color: `${themeMode === 'dark' ? '#ffffffd9' : '#000000d9'}`,
|
||||
}}
|
||||
>
|
||||
{organization?.owner_name || ''}
|
||||
</Typography.Text>
|
||||
</div>
|
||||
</div>
|
||||
<Typography.Paragraph style={{ display: 'flex', alignItems: 'center', margin: 0 }}>
|
||||
<Typography.Text
|
||||
style={{
|
||||
color: `${themeMode === 'dark' ? '#ffffffd9' : '#000000d9'}`,
|
||||
}}
|
||||
>
|
||||
<span style={{ marginRight: '8px' }}>
|
||||
<Tooltip title="Email Address">
|
||||
<MailOutlined />
|
||||
</Tooltip>
|
||||
</span>
|
||||
{organization?.email || ''}
|
||||
</Typography.Text>
|
||||
</Typography.Paragraph>
|
||||
<Typography.Paragraph style={{ marginTop: '0.5rem', marginBottom: 0 }}>
|
||||
<Tooltip title="Contact Number">
|
||||
<span style={{ marginRight: '8px' }}>
|
||||
<PhoneOutlined />
|
||||
</span>
|
||||
</Tooltip>
|
||||
{isEditableContactNumber ? (
|
||||
<Input
|
||||
onChange={handleContactNumber}
|
||||
onPressEnter={handleContactNumberBlur}
|
||||
onBlur={handleContactNumberBlur}
|
||||
style={{ width: '200px' }}
|
||||
value={number}
|
||||
type="text"
|
||||
maxLength={15}
|
||||
ref={contactNoRef}
|
||||
/>
|
||||
) : number === '' ? (
|
||||
<Typography.Link onClick={addContactNumber}>{t('contactNumber')}</Typography.Link>
|
||||
) : (
|
||||
<Typography.Text>
|
||||
{number}
|
||||
<Tooltip title="Edit">
|
||||
<Button
|
||||
onClick={handleEditContactNumber}
|
||||
size="small"
|
||||
type="link"
|
||||
icon={<EditOutlined />}
|
||||
/>
|
||||
</Tooltip>
|
||||
</Typography.Text>
|
||||
)}
|
||||
</Typography.Paragraph>
|
||||
</Card>
|
||||
);
|
||||
};
|
||||
|
||||
export default OrganizationOwner;
|
||||
Reference in New Issue
Block a user