diff --git a/frontend/src/components/admin/AdminConferencesTab.tsx b/frontend/src/components/admin/AdminConferencesTab.tsx index 3b223aa..ab2473e 100644 --- a/frontend/src/components/admin/AdminConferencesTab.tsx +++ b/frontend/src/components/admin/AdminConferencesTab.tsx @@ -50,7 +50,18 @@ function StatusBadge({ conference }: { conference: AdminConferenceOut }) { } /** Форма быстрого редактирования конференции из админки (title/время/пароль/рассылка). */ -function EditConferenceModal({ conference, onClose }: { conference: AdminConferenceOut; onClose: () => void }) { +function EditConferenceModal({ + conference, + onClose, + onDelete, + deleting, +}: { + conference: AdminConferenceOut + onClose: () => void + /** Удаление конференции — та же мутация, что и у корзины в строке таблицы (см. AdminConferencesTab). */ + onDelete: () => void + deleting: boolean +}) { const queryClient = useQueryClient() const toast = useToast() const initialSlot = conference.scheduled_at ? toLocalDateTimeParts(conference.scheduled_at) : { date: '', time: '' } @@ -64,6 +75,7 @@ function EditConferenceModal({ conference, onClose }: { conference: AdminConfere const [summaryRecipients, setSummaryRecipients] = useState<'' | SummaryRecipientsMode>( conference.summary_recipients ?? '', ) + const [confirmDelete, setConfirmDelete] = useState(false) const mutation = useMutation({ mutationFn: (payload: AdminConferenceUpdatePayload) => updateAdminConference(conference.id, payload), @@ -179,13 +191,31 @@ function EditConferenceModal({ conference, onClose }: { conference: AdminConfere -
- - +
+ {confirmDelete ? ( +
+ Удалить без возможности восстановления? + + +
+ ) : ( + + )} +
+ + +
@@ -293,6 +323,9 @@ export function AdminConferencesTab() { queryClient.invalidateQueries({ queryKey: ['admin', 'conferences'] }) toast.show('Конференция удалена', 'success') setConfirmingDeleteId(null) + // Удаление могло прийти и из модалки редактирования (кнопка «Удалить» + // там дёргает эту же мутацию) — закрываем её тоже, если открыта. + setEditingConference(null) }, onError: (err: unknown) => { if (err instanceof ApiError && err.status === 409) { @@ -340,7 +373,7 @@ export function AdminConferencesTab() { -
+
@@ -363,11 +396,16 @@ export function AdminConferencesTab() { {items.map((conf) => (
- {conf.is_closed && {conf.owner_name ?? '—'} @@ -443,7 +481,14 @@ export function AdminConferencesTab() { )} - {editingConference && setEditingConference(null)} />} + {editingConference && ( + setEditingConference(null)} + onDelete={() => deleteMutation.mutate(editingConference.id)} + deleting={deleteMutation.isPending} + /> + )} {invitingConference && setInvitingConference(null)} />} ) diff --git a/frontend/src/styles/admin.css b/frontend/src/styles/admin.css index b00a90a..14dcc04 100644 --- a/frontend/src/styles/admin.css +++ b/frontend/src/styles/admin.css @@ -30,6 +30,16 @@ .admin-page .users-table td:not(:first-child) { display: none; } + + /* Таблица «Конференции» — оставляем «Конференция» и «Владелец», статус/ + тип/начало/действия скрыты; клик по названию (.conf-cell-name) открывает + то же редактирование, что раньше было доступно только через карандаш в + скрытой колонке действий — там же теперь есть и удаление (см. кнопку + «Удалить» в EditConferenceModal). */ + .admin-page .conferences-table th:not(:nth-child(-n+2)), + .admin-page .conferences-table td:not(:nth-child(-n+2)) { + display: none; + } } .admin-page .section-tab { padding: 10px 4px; margin-right: var(--space-6); background: none; border: none; border-bottom: 2px solid transparent; font: var(--text-body-lg); font-weight: 600; color: var(--color-ink-500); cursor: pointer; } .admin-page .section-tab:hover { color: var(--color-ink-900); } @@ -81,6 +91,14 @@ .admin-page .user-cell-name { background: none; border: none; padding: 0; font: inherit; color: inherit; cursor: pointer; text-align: left; } .admin-page .user-cell-name:hover { text-decoration: underline; color: var(--color-ink-700); } +/* Название конференции с №, оформленное кнопкой — открывает то же + редактирование, что и карандаш в row-actions правее (см. + AdminConferencesTab). На мобильном (.conferences-table ниже) это + единственный вход в редактирование строки, на десктопе — просто более + крупная область клика поверх уже существующей иконки. */ +.admin-page .conf-cell-name { background: none; border: none; padding: 0; margin: 0; font: inherit; color: inherit; cursor: pointer; text-align: left; display: block; width: 100%; } +.admin-page .conf-cell-name:hover { color: var(--color-ink-700); } + .admin-page select { font: var(--text-body); color: var(--color-ink-900); padding: 6px 10px; border-radius: var(--radius-md); border: 1px solid var(--color-border); background: var(--color-surface); font-family: var(--font-body); } .admin-page select:disabled { opacity: .5; cursor: not-allowed; } .admin-page select:focus { outline: none; border-color: var(--color-focus-ring); } diff --git a/frontend/src/styles/shell.css b/frontend/src/styles/shell.css index 4797c71..fdd31e3 100644 --- a/frontend/src/styles/shell.css +++ b/frontend/src/styles/shell.css @@ -265,6 +265,23 @@ cursor: not-allowed; } +.btn-danger { + background: transparent; + color: var(--color-danger); + border: 1.5px solid var(--color-danger); + font: var(--text-body); + font-weight: 600; + padding: 9px 18px; + border-radius: var(--radius-full); +} +.btn-danger:hover:not(:disabled) { + background: var(--color-danger-bg); +} +.btn-danger:disabled { + opacity: 0.5; + cursor: not-allowed; +} + /* ---------- Бейджи статуса ---------- */ .badge { display: inline-flex; @@ -393,6 +410,23 @@ .modal-actions { display: flex; gap: var(--space-3); margin-top: var(--space-5); } .modal-actions .btn { flex: 1; justify-content: center; } +/* Опасное действие (удаление) — отдельно слева, Отмена/Сохранить — группой + справа, без растягивания на всю ширину (см. EditConferenceModal). */ +.modal-actions.modal-actions--split { justify-content: space-between; } +.modal-actions--split > .btn-danger { flex: none; } +.modal-actions--split .modal-actions-group { display: flex; gap: var(--space-3); } +.modal-actions--split .row-actions { display: flex; align-items: center; gap: var(--space-3); } + +/* Узкий экран: «Удалить» + «Отмена» + «Сохранить» в один ряд не помещаются + (в сумме шире содержимого модалки на телефоне) — переносим «Удалить» на + свою строку сверху. */ +@media (max-width: 480px) { + .modal-actions--split { flex-wrap: wrap; } + .modal-actions--split > .btn-danger { flex: 1 1 100%; } + .modal-actions--split .modal-actions-group { flex: 1 1 100%; } + .modal-actions--split .row-actions { flex-wrap: wrap; } +} + /* ---------- Поля форм в модалках (общие для всех диалогов оболочки) ---------- */ .field { margin-bottom: var(--space-5); } .field:last-child { margin-bottom: 0; }