fix(admin): таблица конференций вылезала за экран на мобильном
На мобильном в таблице остаются только колонки «Конференция» и «Владелец» — название конференции с номером стало кнопкой, открывающей то же окно редактирования, что и карандаш в скрытой на мобильном колонке действий. Туда же добавлена кнопка «Удалить» (с подтверждением) — раньше удаление было доступно только из строки таблицы, которая на мобильном теперь скрыта.
This commit is contained in:
@@ -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)} />}
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user