fix(uploads): refresh destination previews and improve queue layout
ci / api / api (push) Successful in 50s
ci / db / db (push) Successful in 56s
ci / module boundaries / boundaries (push) Successful in 8s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 1m2s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m15s
ci / ts / ts (packages + web) (push) Failing after 4m28s

This commit is contained in:
mrleo1nid committed 2026-10-29 11:10:54 +03:00
1 parent eb75437708
commit 30c3c85f89
18 files changed
+603 -402

No files matched your search

@@ -1,4 +1,4 @@
.page { max-width: 1050px; margin: 0 auto; display: flex; flex-direction: column; gap: 24px; }
.page { width: 100%; min-width: 0; margin: 0 auto; display: flex; flex-direction: column; gap: 24px; }
.header, .cardHead { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 16px; }
.header h1 { margin: 6px 0; font-family: var(--font-display); font-size: 30px; }
.header p, .note { color: var(--text2); font-size: 13px; line-height: 1.6; margin: 0; }
+25 -13
View File
@@ -1,5 +1,6 @@
import { formatBytes, formatDateTime, formatDuration } from '../../shared/lib/format';
import { editIcon, refreshIcon } from '../../shared/ui/icons';
import { chevronRightIcon, editIcon, refreshIcon } from '../../shared/ui/icons';
import { useId, useState } from 'react';
import { needsFormatAttention } from './files';
import { UploadStatusBadge, useReasonText, type UploadFile } from './common';
import { useTranslation } from 'react-i18next';
@@ -24,6 +25,8 @@ export function BatchCard({
onRetry: (id: string) => void;
}) {
const { t, i18n } = useTranslation('admin');
const [collapsed, setCollapsed] = useState(true);
const contentId = useId();
const first = files[0]!;
const ids = files.map((f) => f.id);
const all = ids.every((id) => selected.has(id));
@@ -31,31 +34,36 @@ export function BatchCard({
return (
<section className={u.batch} aria-label={t('uploads.batch_of', { author: first.author.username })}>
<div className={u.batchHead}>
<Button variant="ghost" className={u.batchToggle} aria-label={t(collapsed ? 'uploads.expand_batch' : 'uploads.collapse_batch')} title={t(collapsed ? 'uploads.expand_batch' : 'uploads.collapse_batch')} aria-expanded={!collapsed} aria-controls={contentId} onClick={() => setCollapsed((value) => !value)}>
<Icon src={chevronRightIcon} size={20} className={!collapsed ? u.batchExpanded : undefined} />
</Button>
{selectable && (
<input type="checkbox" aria-label={t('uploads.select_batch')} checked={all} onChange={(e) => onToggle(ids, e.target.checked)} />
)}
<b>{first.author.username}</b>
<span>{formatDateTime(first.created_at, i18n.language)}</span>
<span>{t('uploads.batch_size', { count: files.length, size: formatBytes(size, i18n.language) })}</span>
{first.batch_comment && <span className={u.comment}>«{first.batch_comment}»</span>}
<span style={{ flex: 1 }} />
{selectable && (
<Button variant="ghost" onClick={() => onEditBatch(first.batch_id)}>
{t('uploads.edit_batch')}
</Button>
)}
{first.batch_comment && <span className={u.comment}>«{first.batch_comment}»</span>}
</div>
<div id={contentId} hidden={collapsed}>
{files.map((f) => (
<QueueRow
key={f.id}
file={f}
selectable={selectable}
checked={selected.has(f.id)}
onCheck={(on) => onToggle([f.id], on)}
onEdit={() => onEdit(f)}
onRetry={() => onRetry(f.id)}
/>
))}
</div>
{files.map((f) => (
<QueueRow
key={f.id}
file={f}
selectable={selectable}
checked={selected.has(f.id)}
onCheck={(on) => onToggle([f.id], on)}
onEdit={() => onEdit(f)}
onRetry={() => onRetry(f.id)}
/>
))}
</section>
);
}
@@ -155,6 +163,10 @@ function QueueRow({
<>
<code className={u.target}>{f.target_rel_path}</code>
<span className={u.sub}>{t('uploads.target_path_hint')}</span>
{f.target_storage_path && <>
<span className={u.placementLabel}>{t('uploads.target_full_path')}</span>
<code className={u.target}>{f.target_storage_path}</code>
</>}
</>
) : (
<>
+1 -1
View File
@@ -83,7 +83,7 @@ export function PatternsPage({ id }: { id: string }) {
});
return (
<div className={`${s.page} ${s.narrow}`} style={{ maxWidth: 1100 }}>
<div className={s.page}>
<div className={s.head}>
<div>
<h1>{t('patterns.title', { name: source.data?.name ?? '…' })}</h1>
@@ -72,6 +72,7 @@ export function WriteTemplateSection({ source }: { source: Source }) {
onSuccess: async (src) => {
queryClient.setQueryData(['source', id], src);
await queryClient.invalidateQueries({ queryKey: ['sources'] });
await queryClient.invalidateQueries({ queryKey: ['admin-uploads'] });
},
});
+2 -1
View File
@@ -1,5 +1,6 @@
.page {
max-width: 1100px;
width: 100%;
min-width: 0;
margin: 0 auto;
display: flex;
flex-direction: column;
@@ -179,6 +179,9 @@
color: var(--text);
}
.batchToggle { min-width: 44px; min-height: 44px; padding: 8px; }
.batchExpanded { transform: rotate(90deg); }
.comment {
flex-basis: 100%;
color: var(--text);