| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- import {
- InboxOutlined,
- ReloadOutlined,
- SettingOutlined
- } from '@ant-design/icons';
- import {
- Alert,
- Button,
- Card,
- Descriptions,
- List,
- Skeleton,
- Space,
- Table,
- Tag,
- Typography
- } from 'antd';
- import type { ColumnsType } from 'antd/es/table';
- import { useCallback, useEffect, useMemo, useState } from 'react';
- import { useNavigate } from 'react-router-dom';
- import { EmptyState } from '../components/common/EmptyState';
- import { PageHeader } from '../components/common/PageHeader';
- import { SectionCard } from '../components/common/SectionCard';
- import { MailboxPermissionTags } from '../components/mailbox/MailboxPermissionTags';
- import { useAppContext } from '../frontend/app-context';
- import { useI18n } from '../frontend/i18n/react';
- import { api } from '../frontend/services/api';
- import type { InboundMailbox, MailboxAccess } from '../frontend/types';
- import { useMediaQuery } from '../frontend/use-media-query';
- export default function Account() {
- const { user } = useAppContext();
- const { t } = useI18n();
- const navigate = useNavigate();
- const isTableView = useMediaQuery('(min-width: 768px)');
- const [mailboxes, setMailboxes] = useState<InboundMailbox[]>([]);
- const [loading, setLoading] = useState(true);
- const [loadError, setLoadError] = useState('');
- const loadMailboxes = useCallback(async () => {
- setLoading(true);
- setLoadError('');
- try {
- const result = await api.inboundMailboxes('effective');
- setMailboxes(result.mailboxes || []);
- } catch (error) {
- setLoadError(error instanceof Error ? error.message : t('common.error'));
- } finally {
- setLoading(false);
- }
- }, [t]);
- useEffect(() => {
- void loadMailboxes();
- }, [loadMailboxes]);
- const counts = useMemo(() => ({
- owned: mailboxes.filter((mailbox) => mailboxAccess(mailbox).type === 'owner').length,
- assigned: mailboxes.filter((mailbox) => mailboxAccess(mailbox).type === 'assigned').length
- }), [mailboxes]);
- function openInbox(mailbox: InboundMailbox) {
- navigate(`/inbox?mailboxId=${mailbox.id}&folder=INBOX`);
- }
- function manageMailbox(mailbox: InboundMailbox) {
- navigate(`/inbox?workspace=routing&mailboxId=${mailbox.id}`);
- }
- const columns: ColumnsType<InboundMailbox> = [
- {
- title: t('inbox.mailboxAddress'),
- render: (_, mailbox) => (
- <Space direction="vertical" size={0}>
- <Typography.Text strong>{mailbox.address}</Typography.Text>
- {mailbox.displayName ? <Typography.Text type="secondary">{mailbox.displayName}</Typography.Text> : null}
- </Space>
- )
- },
- {
- title: t('account.accessSource'),
- width: 160,
- render: (_, mailbox) => <AccessSource mailbox={mailbox} />
- },
- {
- title: t('account.permissions'),
- width: 280,
- render: (_, mailbox) => <MailboxPermissionTags permissions={mailboxAccess(mailbox).permissions} />
- },
- {
- title: t('common.actions'),
- width: 250,
- render: (_, mailbox) => (
- <Space wrap>
- {mailboxAccess(mailbox).permissions.receive ? (
- <Button aria-label={`${t('account.openInbox')} · ${mailbox.address}`} icon={<InboxOutlined />} style={{ minHeight: 44 }} onClick={() => openInbox(mailbox)}>
- {t('account.openInbox')}
- </Button>
- ) : null}
- {mailboxAccess(mailbox).type === 'owner' ? (
- <Button aria-label={`${t('account.manageMailbox')} · ${mailbox.address}`} icon={<SettingOutlined />} style={{ minHeight: 44 }} onClick={() => manageMailbox(mailbox)}>
- {t('account.manageMailbox')}
- </Button>
- ) : null}
- </Space>
- )
- }
- ];
- return (
- <Space direction="vertical" size={20} className="full-width account-page">
- <PageHeader title={t('account.title')} subtitle={t('account.subtitle')} />
- <SectionCard title={t('account.summary')}>
- <Descriptions column={isTableView ? 2 : 1} bordered size="small">
- <Descriptions.Item label={t('auth.username')}>{user?.username || '—'}</Descriptions.Item>
- <Descriptions.Item label={t('auth.email')}>{user?.email || '—'}</Descriptions.Item>
- <Descriptions.Item label={t('account.role')}>{user?.role || '—'}</Descriptions.Item>
- <Descriptions.Item label={t('common.status')}>{user?.status || '—'}</Descriptions.Item>
- </Descriptions>
- </SectionCard>
- {loadError ? (
- <Alert
- type="error"
- showIcon
- message={loadError}
- action={<Button icon={<ReloadOutlined />} onClick={() => void loadMailboxes()}>{t('common.refresh')}</Button>}
- />
- ) : null}
- <SectionCard
- title={t('account.mailboxAccess')}
- extra={<Space wrap><Tag>{t('account.owned')} {counts.owned}</Tag><Tag>{t('account.assigned')} {counts.assigned}</Tag></Space>}
- >
- {loading && !mailboxes.length ? <Skeleton active paragraph={{ rows: 7 }} /> : mailboxes.length ? (
- isTableView ? (
- <Table rowKey="id" columns={columns} dataSource={mailboxes} scroll={{ x: 900 }} />
- ) : (
- <List
- dataSource={mailboxes}
- renderItem={(mailbox) => (
- <List.Item>
- <Card size="small" title={mailbox.address} className="full-width account-mailbox-card">
- <Space direction="vertical" size={12} className="full-width">
- <AccessSource mailbox={mailbox} />
- <MailboxPermissionTags permissions={mailboxAccess(mailbox).permissions} />
- {mailboxAccess(mailbox).permissions.receive ? (
- <Button block aria-label={`${t('account.openInbox')} · ${mailbox.address}`} icon={<InboxOutlined />} style={{ minHeight: 44 }} onClick={() => openInbox(mailbox)}>
- {t('account.openInbox')}
- </Button>
- ) : null}
- {mailboxAccess(mailbox).type === 'owner' ? (
- <Button block aria-label={`${t('account.manageMailbox')} · ${mailbox.address}`} icon={<SettingOutlined />} style={{ minHeight: 44 }} onClick={() => manageMailbox(mailbox)}>
- {t('account.manageMailbox')}
- </Button>
- ) : null}
- </Space>
- </Card>
- </List.Item>
- )}
- />
- )
- ) : <EmptyState description={t('account.noMailboxes')} action={<Button onClick={() => navigate('/inbox?workspace=routing')}>{t('inbox.createMailbox')}</Button>} />}
- </SectionCard>
- </Space>
- );
- }
- function AccessSource({ mailbox }: { mailbox: InboundMailbox }) {
- const { t } = useI18n();
- const access = mailboxAccess(mailbox);
- if (access.type === 'owner') return <Tag color="blue">{t('account.ownedMailbox')}</Tag>;
- if (access.type === 'admin') return <Tag color="gold">{t('account.adminAccess')}</Tag>;
- return <Tag color="purple">{t('account.adminAssigned')}</Tag>;
- }
- function mailboxAccess(mailbox: InboundMailbox): MailboxAccess {
- return mailbox.access || {
- type: 'owner',
- permissions: { view: true, receive: true, send: true }
- };
- }
|