fix(admin): таблица конференций вылезала за экран на мобильном

На мобильном в таблице остаются только колонки «Конференция» и
«Владелец» — название конференции с номером стало кнопкой, открывающей
то же окно редактирования, что и карандаш в скрытой на мобильном колонке
действий. Туда же добавлена кнопка «Удалить» (с подтверждением) — раньше
удаление было доступно только из строки таблицы, которая на мобильном
теперь скрыта.
This commit is contained in:
2026-07-26 04:32:27 +03:00
parent 6813464e92
commit a04ae3152f
3 changed files with 112 additions and 15 deletions

View File

@@ -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
</select>
</div>
<div className="modal-actions">
<button type="button" className="btn btn-secondary" onClick={onClose}>
Отмена
</button>
<button type="submit" className="btn btn-primary" disabled={mutation.isPending}>
{mutation.isPending ? 'Сохраняем…' : 'Сохранить'}
</button>
<div className="modal-actions modal-actions--split">
{confirmDelete ? (
<div className="row-actions">
<span className="field-hint">Удалить без возможности восстановления?</span>
<button type="button" className="btn btn-secondary" onClick={() => setConfirmDelete(false)}>
Нет
</button>
<button type="button" className="btn btn-danger" disabled={deleting} onClick={onDelete}>
{deleting ? 'Удаляем…' : 'Да, удалить'}
</button>
</div>
) : (
<button type="button" className="btn btn-danger" onClick={() => setConfirmDelete(true)}>
<Trash2 style={{ width: 16, height: 16 }} aria-hidden="true" />
Удалить
</button>
)}
<div className="modal-actions-group">
<button type="button" className="btn btn-secondary" onClick={onClose}>
Отмена
</button>
<button type="submit" className="btn btn-primary" disabled={mutation.isPending}>
{mutation.isPending ? 'Сохраняем…' : 'Сохранить'}
</button>
</div>
</div>
</form>
</div>
@@ -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() {
</span>
</div>
<div className="table-card">
<div className="table-card conferences-table">
<table>
<thead>
<tr>
@@ -363,11 +396,16 @@ export function AdminConferencesTab() {
{items.map((conf) => (
<tr key={conf.id}>
<td>
{conf.is_closed && <Lock style={{ width: 14, height: 14, marginRight: 6, verticalAlign: '-2px' }} aria-hidden="true" />}
{conf.title ?? 'Без названия'}
<span style={{ display: 'block', font: 'var(--text-caption)', color: 'var(--color-ink-500)' }}>
{conf.number}
</span>
{/* На мобильном это единственный вход в редактирование строки (см. .conferences-table
в admin.css — остальные колонки там скрыты), на десктопе — то же самое, что кнопка
«Редактировать» правее, просто более крупная область клика. */}
<button type="button" className="conf-cell-name" onClick={() => setEditingConference(conf)}>
{conf.is_closed && <Lock style={{ width: 14, height: 14, marginRight: 6, verticalAlign: '-2px' }} aria-hidden="true" />}
{conf.title ?? 'Без названия'}
<span style={{ display: 'block', font: 'var(--text-caption)', color: 'var(--color-ink-500)' }}>
{conf.number}
</span>
</button>
</td>
<td>
{conf.owner_name ?? '—'}
@@ -443,7 +481,14 @@ export function AdminConferencesTab() {
</div>
)}
{editingConference && <EditConferenceModal conference={editingConference} onClose={() => setEditingConference(null)} />}
{editingConference && (
<EditConferenceModal
conference={editingConference}
onClose={() => setEditingConference(null)}
onDelete={() => deleteMutation.mutate(editingConference.id)}
deleting={deleteMutation.isPending}
/>
)}
{invitingConference && <InviteModal conference={invitingConference} onClose={() => setInvitingConference(null)} />}
</>
)