krona-front/src/features/communications/components/Modals.tsx

369 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, type FormEvent } from 'react';
import { Modal, Button, Input } from '@krona/ui';
import { useCommunicationsStore } from '../services/communicationsService';
import { useToast } from '../../../shared/ui';
import { Paperclip } from 'lucide-react';
export function ComposeEmailModal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
const { sendEmail } = useCommunicationsStore();
const { showToast } = useToast();
const [toEmail, setToEmail] = useState('');
const [subject, setSubject] = useState('');
const [body, setBody] = useState('');
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
if (!toEmail.trim() || !subject.trim()) return;
sendEmail({
mailboxId: 'mb-work',
folder: 'sent',
from: { name: 'Krona Office', email: 'office@krona.systems' },
to: { name: toEmail.split('@')[0], email: toEmail.trim() },
subject: subject.trim(),
preview: body.slice(0, 80) + '...',
body: body.trim(),
isUnread: false,
hasAttachment: false,
});
showToast({
title: 'Письмо отправлено',
message: `Отправлено на адрес ${toEmail}`,
type: 'success',
});
onClose();
};
return (
<Modal isOpen={isOpen} onClose={onClose} title="Новое сообщение">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Кому (Email)
</label>
<Input
type="email"
value={toEmail}
onChange={(e) => setToEmail(e.target.value)}
placeholder="client@company.ru"
required
/>
</div>
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Тема
</label>
<Input
type="text"
value={subject}
onChange={(e) => setSubject(e.target.value)}
placeholder="Тема письма..."
required
/>
</div>
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Текст письма
</label>
<textarea
rows={6}
value={body}
onChange={(e) => setBody(e.target.value)}
placeholder="Здравствуйте!..."
className="w-full p-3 rounded-xl bg-[var(--krona-bg-workspace)] border border-[var(--krona-border)] text-sm text-[var(--krona-text-primary)] placeholder-[var(--krona-text-muted)] focus:outline-none focus:border-[var(--krona-primary)] resize-none"
required
/>
</div>
<div className="flex items-center justify-between pt-2 border-t border-[var(--krona-border-subtle)]">
<button
type="button"
className="text-xs text-[var(--krona-text-secondary)] hover:text-[var(--krona-text-primary)] flex items-center gap-1.5"
>
<Paperclip className="w-3.5 h-3.5" />
<span>Прикрепить файл</span>
</button>
<div className="flex items-center gap-2">
<Button variant="secondary" size="md" onClick={onClose}>
Отмена
</Button>
<Button variant="primary" size="md" type="submit">
Отправить
</Button>
</div>
</div>
</form>
</Modal>
);
}
export function CreateChannelModal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
const { createChannel } = useCommunicationsStore();
const { showToast } = useToast();
const [name, setName] = useState('');
const [topic, setTopic] = useState('');
const [isPrivate, setIsPrivate] = useState(false);
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
if (!name.trim()) return;
createChannel({
name: name.trim(),
topic: topic.trim() || 'Новый канал организации',
isPrivate,
});
showToast({
title: 'Канал создан',
message: `Канал #${name} успешно добавлен`,
type: 'success',
});
onClose();
};
return (
<Modal isOpen={isOpen} onClose={onClose} title="Создание внутреннего канала">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Название канала
</label>
<Input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="маркетинг-и-реклама"
required
/>
</div>
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Тема / Описание канала
</label>
<Input
type="text"
value={topic}
onChange={(e) => setTopic(e.target.value)}
placeholder="Обсуждение совместных проектов..."
/>
</div>
<div className="flex items-center gap-3 p-3 rounded-xl bg-[var(--krona-bg-workspace)] border border-[var(--krona-border-subtle)]">
<input
type="checkbox"
id="channel-private"
checked={isPrivate}
onChange={(e) => setIsPrivate(e.target.checked)}
className="w-4 h-4 rounded text-[var(--krona-primary)] focus:ring-[var(--krona-primary)]"
/>
<label htmlFor="channel-private" className="text-xs text-[var(--krona-text-primary)] cursor-pointer">
<strong className="block font-semibold">Закрытый канал (Private)</strong>
<span className="text-[var(--krona-text-secondary)]">Доступ только по приглашению руководителей</span>
</label>
</div>
<div className="flex items-center justify-end gap-2 pt-2 border-t border-[var(--krona-border-subtle)]">
<Button variant="secondary" size="md" onClick={onClose}>
Отмена
</Button>
<Button variant="primary" size="md" type="submit">
Создать канал
</Button>
</div>
</form>
</Modal>
);
}
export function AddContactModal({ isOpen, onClose }: { isOpen: boolean; onClose: () => void }) {
const { addContact } = useCommunicationsStore();
const { showToast } = useToast();
const [name, setName] = useState('');
const [company, setCompany] = useState('');
const [role, setRole] = useState('Клиент');
const [channelValue, setChannelValue] = useState('');
const [isVip, setIsVip] = useState(false);
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
if (!name.trim()) return;
addContact({
name: name.trim(),
company: company.trim() || 'Частное лицо',
role: role.trim() || 'Клиент',
isVip,
channels: [
{
type: 'telegram',
value: channelValue.trim() || '@client_tg',
label: 'Telegram',
isConnected: true,
},
],
});
showToast({
title: 'Контакт добавлен',
message: `${name} добавлен в справочник и список диалогов`,
type: 'success',
});
onClose();
};
return (
<Modal isOpen={isOpen} onClose={onClose} title="Новый контакт">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
ФИО / Имя контакта
</label>
<Input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Константин Васильев"
required
/>
</div>
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Компания
</label>
<Input
type="text"
value={company}
onChange={(e) => setCompany(e.target.value)}
placeholder="ООО «Северный Альянс»"
/>
</div>
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Должность
</label>
<Input
type="text"
value={role}
onChange={(e) => setRole(e.target.value)}
placeholder="Коммерческий директор"
/>
</div>
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
Telegram / Телефон
</label>
<Input
type="text"
value={channelValue}
onChange={(e) => setChannelValue(e.target.value)}
placeholder="@vasiliev_kostya или +7 999 123-45-67"
/>
</div>
<div className="flex items-center gap-2">
<input
type="checkbox"
id="contact-vip"
checked={isVip}
onChange={(e) => setIsVip(e.target.checked)}
className="w-4 h-4 rounded text-[var(--krona-gold)]"
/>
<label htmlFor="contact-vip" className="text-xs font-semibold text-[var(--krona-text-primary)] cursor-pointer">
VIP статус клиента
</label>
</div>
<div className="flex items-center justify-end gap-2 pt-2 border-t border-[var(--krona-border-subtle)]">
<Button variant="secondary" size="md" onClick={onClose}>
Отмена
</Button>
<Button variant="primary" size="md" type="submit">
Сохранить контакт
</Button>
</div>
</form>
</Modal>
);
}
export function ConnectChannelModal({ isOpen, onClose, mode = 'messenger' }: { isOpen: boolean; onClose: () => void; mode?: 'messenger' | 'email' }) {
const { showToast } = useToast();
const [platform, setPlatform] = useState('telegram');
const [identifier, setIdentifier] = useState('');
const [useVpn, setUseVpn] = useState(false);
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
showToast({
title: mode === 'email' ? 'Почта подключена' : 'Мессенджер подключен',
message: `${mode === 'email' ? 'Ящик' : platform.toUpperCase()} (${identifier}) успешно подключен${useVpn && mode === 'messenger' ? ' через VPN' : ''}`,
type: 'success',
});
onClose();
};
return (
<Modal isOpen={isOpen} onClose={onClose} title={mode === 'email' ? 'Добавление почтового ящика' : 'Добавление мессенджера'}>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
{mode === 'email' ? 'Почтовый сервис' : 'Мессенджер'}
</label>
<select
value={mode === 'email' ? 'email' : platform}
onChange={(e) => setPlatform(e.target.value)}
className="w-full h-10 px-3 rounded-xl bg-[var(--krona-bg-workspace)] border border-[var(--krona-border)] text-sm text-[var(--krona-text-primary)] focus:outline-none focus:border-[var(--krona-primary)]"
>
{mode === 'email' ? <>
<option value="email">IMAP / SMTP</option>
<option value="yandex">Яндекс Почта</option>
<option value="gmail">Google Workspace / Gmail</option>
<option value="mailru">Mail.ru</option>
</> : <>
<option value="telegram">Telegram</option>
<option value="whatsapp">WhatsApp</option>
<option value="vk">VK Мессенджер</option>
<option value="max">MAX</option>
</>}
</select>
</div>
<div>
<label className="block text-xs font-semibold text-[var(--krona-text-secondary)] mb-1">
{mode === 'email' ? 'Адрес электронной почты' : 'Телефон или логин аккаунта'}
</label>
<Input
type="text"
value={identifier}
onChange={(e) => setIdentifier(e.target.value)}
placeholder={mode === 'email' ? 'name@company.ru' : '+7 900 000-00-00 или @username'}
required
/>
</div>
{mode === 'messenger' && <label className="p-3 rounded-xl bg-[var(--krona-bg-workspace)] border border-[var(--krona-border)] flex items-center gap-3 text-sm text-[var(--krona-text-primary)] cursor-pointer">
<input type="checkbox" checked={useVpn} onChange={(e) => setUseVpn(e.target.checked)} className="w-4 h-4 accent-[var(--krona-primary)]" />
<span><strong className="block">Подключать через VPN</strong><span className="text-xs text-[var(--krona-text-secondary)]">Использовать VPN-шлюз организации для этого аккаунта</span></span>
</label>}
<div className="flex items-center justify-end gap-2 pt-2 border-t border-[var(--krona-border-subtle)]">
<Button variant="secondary" size="md" onClick={onClose}>
Отмена
</Button>
<Button variant="primary" size="md" type="submit">
Подключить
</Button>
</div>
</form>
</Modal>
);
}