Files
3x-ui/frontend/src/pages/clients/BulkAddToGroupModal.tsx
T
MHSanaei 987a6dd1e5 feat(clients/inbounds): IP log popups, clearer titles, tag-based inbound labels
Add an IP Log popup (view list + refresh + clear) to the client edit form and the Client Information modal, with IPs stacked vertically.

Identify inbounds by their xray tag (not remark/protocol:port) across every picker and chip: attach/detach modals, the attached-inbounds column and field, the filter drawer, and bulk-add. Add the tag field to the InboundOption schema (the backend already returned it).

Clarify modal titles/labels: Client Information (was More Information) and Inbound Information (was Inbound's Data); Client Information / QR Code titles now include the client email.

i18n: rename keys moreInformation->clientInfo and inboundData->inboundInfo with proper translations in all languages; addTitle->addClient, editTitle->editClient, addToGroupPlaceholder->groupName.
2026-05-29 23:22:49 +02:00

82 lines
2.2 KiB
TypeScript

import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { AutoComplete, Form, Modal, message } from 'antd';
interface BulkAddToGroupModalProps {
open: boolean;
count: number;
groups: string[];
onOpenChange: (open: boolean) => void;
onSubmit: (group: string) => Promise<{ affected?: number } | null>;
}
export default function BulkAddToGroupModal({
open,
count,
groups,
onOpenChange,
onSubmit,
}: BulkAddToGroupModalProps) {
const { t } = useTranslation();
const [messageApi, messageContextHolder] = message.useMessage();
const [value, setValue] = useState('');
const [submitting, setSubmitting] = useState(false);
useEffect(() => {
if (open) setValue('');
}, [open]);
async function submit() {
const next = value.trim();
if (!next) return;
setSubmitting(true);
try {
const result = await onSubmit(next);
if (result) {
const affected = result.affected ?? 0;
messageApi.success(t('pages.clients.addToGroupSuccessToast', { count: affected, group: next }));
onOpenChange(false);
}
} finally {
setSubmitting(false);
}
}
return (
<>
{messageContextHolder}
<Modal
open={open}
title={t('pages.clients.addToGroupTitle', { count })}
okText={t('add')}
cancelText={t('cancel')}
confirmLoading={submitting}
okButtonProps={{ disabled: !value.trim() }}
onCancel={() => onOpenChange(false)}
onOk={submit}
destroyOnHidden
>
<Form layout="vertical">
<Form.Item
label={t('pages.clients.group')}
tooltip={t('pages.clients.addToGroupTooltip')}
>
<AutoComplete
value={value}
placeholder={t('pages.clients.groupName')}
options={groups.map((g) => ({ value: g }))}
onChange={(v) => setValue(v ?? '')}
filterOption={(input, option) =>
String(option?.value ?? '').toLowerCase().includes((input || '').toLowerCase())
}
allowClear
style={{ width: '100%' }}
autoFocus
/>
</Form.Item>
</Form>
</Modal>
</>
);
}