fix: 优化任务列表布局
This commit is contained in:
@@ -2,15 +2,15 @@
|
|||||||
// the top-nav active state in sync, and re-renders chrome strings when
|
// the top-nav active state in sync, and re-renders chrome strings when
|
||||||
// the locale changes.
|
// the locale changes.
|
||||||
|
|
||||||
import { escapeHtml, onRoute } from "./state.js?v=security-fixes-1";
|
import { escapeHtml, onRoute } from "./state.js?v=job-layout-1";
|
||||||
import { t, currentLocale, setLocale, onLocale } from "./i18n.js?v=security-fixes-1";
|
import { t, currentLocale, setLocale, onLocale } from "./i18n.js?v=job-layout-1";
|
||||||
import { renderRemotes } from "./views/remotes.js?v=security-fixes-1";
|
import { renderRemotes } from "./views/remotes.js?v=job-layout-1";
|
||||||
import { renderBrowse } from "./views/browser.js?v=security-fixes-1";
|
import { renderBrowse } from "./views/browser.js?v=job-layout-1";
|
||||||
import { renderJobs, renderNewJob, stopJobPolling } from "./views/jobs.js?v=security-fixes-1";
|
import { renderJobs, renderNewJob, stopJobPolling } from "./views/jobs.js?v=job-layout-1";
|
||||||
import {
|
import {
|
||||||
renderConfigureNew,
|
renderConfigureNew,
|
||||||
renderConfigureEdit,
|
renderConfigureEdit,
|
||||||
} from "./views/configure.js?v=security-fixes-1";
|
} from "./views/configure.js?v=job-layout-1";
|
||||||
|
|
||||||
const views = {
|
const views = {
|
||||||
remotes: renderRemotes,
|
remotes: renderRemotes,
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// views/browser.js — file/folder listing with breadcrumbs, mkdir, upload, delete, rename.
|
// views/browser.js — file/folder listing with breadcrumbs, mkdir, upload, delete, rename.
|
||||||
|
|
||||||
import { post, uploadFile, downloadURL } from "../rc.js?v=security-fixes-1";
|
import { post, uploadFile, downloadURL } from "../rc.js?v=job-layout-1";
|
||||||
import { escapeHtml, toast, formatBytes, formatTime } from "../state.js?v=security-fixes-1";
|
import { escapeHtml, toast, formatBytes, formatTime } from "../state.js?v=job-layout-1";
|
||||||
import { t } from "../i18n.js?v=security-fixes-1";
|
import { t } from "../i18n.js?v=job-layout-1";
|
||||||
|
|
||||||
export async function renderBrowse({ remote, path }) {
|
export async function renderBrowse({ remote, path }) {
|
||||||
const app = document.getElementById("app");
|
const app = document.getElementById("app");
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
// OAuth backends (option named "token" with IsPassword) get a banner
|
// OAuth backends (option named "token" with IsPassword) get a banner
|
||||||
// and disabled submit — user must run `rclone config` in a terminal.
|
// and disabled submit — user must run `rclone config` in a terminal.
|
||||||
|
|
||||||
import { post } from "../rc.js?v=security-fixes-1";
|
import { post } from "../rc.js?v=job-layout-1";
|
||||||
import { escapeHtml, getState, setState, toast } from "../state.js?v=security-fixes-1";
|
import { escapeHtml, getState, setState, toast } from "../state.js?v=job-layout-1";
|
||||||
import { t } from "../i18n.js?v=security-fixes-1";
|
import { t } from "../i18n.js?v=job-layout-1";
|
||||||
|
|
||||||
// --- Route entrypoints ---
|
// --- Route entrypoints ---
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// views/jobs.js — submit sync/copy/move jobs and manage recurring transfers.
|
// views/jobs.js — submit sync/copy/move jobs and manage recurring transfers.
|
||||||
|
|
||||||
import { post } from "../rc.js?v=security-fixes-1";
|
import { post } from "../rc.js?v=job-layout-1";
|
||||||
import {
|
import {
|
||||||
toast,
|
toast,
|
||||||
formatBytes,
|
formatBytes,
|
||||||
formatSpeed,
|
formatSpeed,
|
||||||
formatDuration,
|
formatDuration,
|
||||||
escapeHtml,
|
escapeHtml,
|
||||||
} from "../state.js?v=security-fixes-1";
|
} from "../state.js?v=job-layout-1";
|
||||||
import { t } from "../i18n.js?v=security-fixes-1";
|
import { t } from "../i18n.js?v=job-layout-1";
|
||||||
import {
|
import {
|
||||||
createOneTimeJob,
|
createOneTimeJob,
|
||||||
createRecurringJob,
|
createRecurringJob,
|
||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
runRecurringJobNow,
|
runRecurringJobNow,
|
||||||
stopOneTimeJob,
|
stopOneTimeJob,
|
||||||
stopRecurringJob,
|
stopRecurringJob,
|
||||||
} from "../jobs_api.js?v=security-fixes-1";
|
} from "../jobs_api.js?v=job-layout-1";
|
||||||
|
|
||||||
let pollTimer = null;
|
let pollTimer = null;
|
||||||
const LOCAL_FS_VALUE = "__local__";
|
const LOCAL_FS_VALUE = "__local__";
|
||||||
@@ -537,78 +537,37 @@ function renderJobTable(statuses) {
|
|||||||
return bTime - aTime;
|
return bTime - aTime;
|
||||||
});
|
});
|
||||||
|
|
||||||
const rows = statuses.map(renderJobRows).join("");
|
const rows = statuses.map(renderJobRow).join("");
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<table class="table">
|
<div class="job-list">
|
||||||
<thead>
|
${rows}
|
||||||
<tr>
|
</div>
|
||||||
<th>${t("jobs.col.id")}</th>
|
|
||||||
<th>${t("jobs.col.job")}</th>
|
|
||||||
<th>${t("jobs.col.status")}</th>
|
|
||||||
<th>${t("jobs.col.next_run")}</th>
|
|
||||||
<th class="col-num">${t("jobs.col.progress")}</th>
|
|
||||||
<th class="col-num">${t("jobs.col.speed")}</th>
|
|
||||||
<th class="col-num">${t("jobs.col.eta")}</th>
|
|
||||||
<th class="col-num">${t("jobs.col.files")}</th>
|
|
||||||
<th class="col-num">${t("jobs.col.errors")}</th>
|
|
||||||
<th class="col-num">${t("jobs.col.action")}</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
${rows}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderJobRows(s) {
|
|
||||||
const row = renderJobRow(s);
|
|
||||||
if (!expandedJobs.has(jobKey(s))) {
|
|
||||||
return row;
|
|
||||||
}
|
|
||||||
return row + renderJobDetailsRow(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderJobRow(job) {
|
function renderJobRow(job) {
|
||||||
const snapshot = job.statusSnapshot || {};
|
const snapshot = job.statusSnapshot || {};
|
||||||
const stats = transferStats(snapshot);
|
const stats = transferStats(snapshot);
|
||||||
const id = job.id;
|
|
||||||
const key = jobKey(job);
|
const key = jobKey(job);
|
||||||
const running = !!job.running;
|
const running = !!job.running;
|
||||||
const jobid = job.currentJobid || job.lastJobid;
|
const jobid = job.currentJobid || job.lastJobid;
|
||||||
const displayedSpeed = stats.speed || averageSpeed(stats);
|
const displayedSpeed = stats.speed || averageSpeed(stats);
|
||||||
const displayedEta = running ? stats.eta : 0;
|
const displayedEta = running ? stats.eta : 0;
|
||||||
|
const expanded = expandedJobs.has(key);
|
||||||
|
|
||||||
const badge = renderStatusBadge(job.status);
|
const badge = renderStatusBadge(job.status);
|
||||||
const jobCell = `
|
|
||||||
<div class="job-cell">
|
|
||||||
<span class="job-action">${escapeHtml(job.action)}</span>
|
|
||||||
<span class="job-paths">
|
|
||||||
<code>${escapeHtml(job.src)}</code>
|
|
||||||
<span class="arrow">→</span>
|
|
||||||
<code>${escapeHtml(job.dst)}</code>
|
|
||||||
</span>
|
|
||||||
</div>`;
|
|
||||||
|
|
||||||
const scheduleCell = `
|
|
||||||
<div class="job-cell">
|
|
||||||
<span class="job-action">${escapeHtml(job.kind === "once" ? t("jobs.schedule_once") : formatDateTime(job.nextRunAt))}</span>
|
|
||||||
<span class="job-paths">${escapeHtml(formatSchedule(job))}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const pct = stats.totalBytes > 0 ? Math.min(100, (stats.bytes / stats.totalBytes) * 100) : 0;
|
const pct = stats.totalBytes > 0 ? Math.min(100, (stats.bytes / stats.totalBytes) * 100) : 0;
|
||||||
const progress = `
|
const progress = `
|
||||||
<div style="display:flex;flex-direction:column;gap:4px;min-width:120px">
|
<div class="job-progress">
|
||||||
<div class="progress"><span style="width:${pct.toFixed(1)}%"></span></div>
|
<div class="progress"><span style="width:${pct.toFixed(1)}%"></span></div>
|
||||||
<span class="col-mono" style="font-size:11px;color:var(--color-muted)">
|
<span class="job-progress-text">
|
||||||
${formatBytes(stats.bytes)} / ${formatBytes(stats.totalBytes)}
|
${formatBytes(stats.bytes)} / ${formatBytes(stats.totalBytes)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const detailBtn = `<button class="btn btn-secondary btn-sm" data-detail="${escapeAttr(key)}">${expandedJobs.has(key) ? t("jobs.hide_details") : t("jobs.details")}</button>`;
|
const detailBtn = `<button class="btn btn-secondary btn-sm" data-detail="${escapeAttr(key)}">${expanded ? t("jobs.hide_details") : t("jobs.details")}</button>`;
|
||||||
const startBtn = running
|
const startBtn = running
|
||||||
? ""
|
? ""
|
||||||
: `<button class="btn btn-secondary btn-sm" data-start="${escapeAttr(key)}">${t("jobs.start_again")}</button>`;
|
: `<button class="btn btn-secondary btn-sm" data-start="${escapeAttr(key)}">${t("jobs.start_again")}</button>`;
|
||||||
@@ -618,28 +577,50 @@ function renderJobRow(job) {
|
|||||||
const deleteBtn = `<button class="btn btn-danger btn-sm" data-delete="${escapeAttr(key)}">${t("jobs.delete_record")}</button>`;
|
const deleteBtn = `<button class="btn btn-danger btn-sm" data-delete="${escapeAttr(key)}">${t("jobs.delete_record")}</button>`;
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr>
|
<article class="job-item">
|
||||||
<td class="col-mono">
|
<div class="job-main">
|
||||||
${escapeHtml(displayJobId(job))}
|
<div class="job-head">
|
||||||
${jobid ? `<br><span style="color:var(--color-muted-soft)">job ${escapeHtml(jobid)}</span>` : ""}
|
<div class="job-id">
|
||||||
</td>
|
<span>${escapeHtml(displayJobId(job))}</span>
|
||||||
<td>${jobCell}</td>
|
${jobid ? `<small>job ${escapeHtml(jobid)}</small>` : ""}
|
||||||
<td>${badge}</td>
|
</div>
|
||||||
<td>${scheduleCell}</td>
|
${badge}
|
||||||
<td class="col-num">${progress}</td>
|
</div>
|
||||||
<td class="col-num col-mono">${escapeHtml(formatSpeed(displayedSpeed))}</td>
|
<div class="job-route">
|
||||||
<td class="col-num col-mono">${escapeHtml(formatDuration(displayedEta))}</td>
|
<div class="job-route-line">
|
||||||
<td class="col-num col-mono">${escapeHtml(formatTransferCount(stats))}</td>
|
<span>${escapeHtml(job.action)}</span>
|
||||||
<td class="col-num col-mono">${escapeHtml(String(stats.errors || 0))}</td>
|
<code>${escapeHtml(job.src)}</code>
|
||||||
<td class="col-num">
|
</div>
|
||||||
|
<div class="job-route-line">
|
||||||
|
<span>→</span>
|
||||||
|
<code>${escapeHtml(job.dst)}</code>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="job-schedule">
|
||||||
|
<span>${escapeHtml(job.kind === "once" ? t("jobs.schedule_once") : formatDateTime(job.nextRunAt))}</span>
|
||||||
|
<span>${escapeHtml(formatSchedule(job))}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="job-side">
|
||||||
|
<div class="job-progress-wrap">
|
||||||
|
<span class="job-metric-label">${t("jobs.col.progress")}</span>
|
||||||
|
${progress}
|
||||||
|
</div>
|
||||||
|
<div class="job-metrics">
|
||||||
|
${metricHtml(t("jobs.col.speed"), formatSpeed(displayedSpeed))}
|
||||||
|
${metricHtml(t("jobs.col.eta"), formatDuration(displayedEta))}
|
||||||
|
${metricHtml(t("jobs.col.files"), formatTransferCount(stats))}
|
||||||
|
${metricHtml(t("jobs.col.errors"), String(stats.errors || 0))}
|
||||||
|
</div>
|
||||||
<div class="job-actions">
|
<div class="job-actions">
|
||||||
${detailBtn}
|
${detailBtn}
|
||||||
${startBtn}
|
${startBtn}
|
||||||
${stopBtn}
|
${stopBtn}
|
||||||
${deleteBtn}
|
${deleteBtn}
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
</tr>
|
${expanded ? renderJobDetails(job) : ""}
|
||||||
|
</article>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -661,7 +642,16 @@ function renderStatusBadge(status) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderJobDetailsRow(job) {
|
function metricHtml(label, value) {
|
||||||
|
return `
|
||||||
|
<div class="job-metric">
|
||||||
|
<span>${escapeHtml(label)}</span>
|
||||||
|
<strong>${escapeHtml(value)}</strong>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderJobDetails(job) {
|
||||||
const snapshot = job.statusSnapshot || {};
|
const snapshot = job.statusSnapshot || {};
|
||||||
const stats = transferStats(snapshot);
|
const stats = transferStats(snapshot);
|
||||||
const jobid = job.currentJobid || job.lastJobid || "";
|
const jobid = job.currentJobid || job.lastJobid || "";
|
||||||
@@ -688,27 +678,22 @@ function renderJobDetailsRow(job) {
|
|||||||
: "";
|
: "";
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<tr class="job-detail-row">
|
<div class="job-detail">
|
||||||
<td></td>
|
<dl>
|
||||||
<td colspan="9">
|
${details.map(([label, value]) => `
|
||||||
<div class="job-detail">
|
<div>
|
||||||
<dl>
|
<dt>${escapeHtml(label)}</dt>
|
||||||
${details.map(([label, value]) => `
|
<dd>${escapeHtml(value)}</dd>
|
||||||
<div>
|
</div>
|
||||||
<dt>${escapeHtml(label)}</dt>
|
`).join("")}
|
||||||
<dd>${escapeHtml(value)}</dd>
|
</dl>
|
||||||
</div>
|
${output ? `
|
||||||
`).join("")}
|
<div class="job-detail-output">
|
||||||
</dl>
|
<span>${t("jobs.detail_output")}</span>
|
||||||
${output ? `
|
<pre>${escapeHtml(output)}</pre>
|
||||||
<div class="job-detail-output">
|
|
||||||
<span>${t("jobs.detail_output")}</span>
|
|
||||||
<pre>${escapeHtml(output)}</pre>
|
|
||||||
</div>
|
|
||||||
` : ""}
|
|
||||||
</div>
|
</div>
|
||||||
</td>
|
` : ""}
|
||||||
</tr>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// views/remotes.js — connector-tile grid of configured remotes with CRUD.
|
// views/remotes.js — connector-tile grid of configured remotes with CRUD.
|
||||||
|
|
||||||
import { post } from "../rc.js?v=security-fixes-1";
|
import { post } from "../rc.js?v=job-layout-1";
|
||||||
import { escapeHtml, getState, setState, toast } from "../state.js?v=security-fixes-1";
|
import { escapeHtml, getState, setState, toast } from "../state.js?v=job-layout-1";
|
||||||
import { t } from "../i18n.js?v=security-fixes-1";
|
import { t } from "../i18n.js?v=job-layout-1";
|
||||||
|
|
||||||
export async function renderRemotes() {
|
export async function renderRemotes() {
|
||||||
const app = document.getElementById("app");
|
const app = document.getElementById("app");
|
||||||
|
|||||||
@@ -865,46 +865,148 @@
|
|||||||
margin-bottom: var(--space-md);
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------- Job cell with action + src→dst ---------- */
|
/* ---------- Jobs ---------- */
|
||||||
.job-cell {
|
.job-list {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(320px, 380px);
|
||||||
|
gap: var(--space-lg);
|
||||||
|
padding: var(--space-lg);
|
||||||
|
border-bottom: 1px solid var(--color-hairline-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-main {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto minmax(0, 1fr);
|
||||||
|
gap: var(--space-md);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-head {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 2px;
|
align-items: flex-start;
|
||||||
min-width: 200px;
|
gap: var(--space-xs);
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-cell .job-action {
|
.job-id {
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 58px;
|
||||||
|
color: var(--color-muted);
|
||||||
|
font: var(--typo-code);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-id small {
|
||||||
|
color: var(--color-muted-soft);
|
||||||
|
font: var(--typo-caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-route {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-route-line {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 44px minmax(0, 1fr);
|
||||||
|
align-items: start;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-route-line span {
|
||||||
|
color: var(--color-muted);
|
||||||
font: var(--typo-caption-uppercase);
|
font: var(--typo-caption-uppercase);
|
||||||
letter-spacing: var(--typo-caption-uppercase-tracking);
|
letter-spacing: var(--typo-caption-uppercase-tracking);
|
||||||
color: var(--color-muted);
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-cell .job-paths {
|
.job-route-line code {
|
||||||
font: var(--typo-body-sm);
|
|
||||||
color: var(--color-body);
|
color: var(--color-body);
|
||||||
word-break: break-all;
|
font: var(--typo-code);
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-cell .job-paths .arrow {
|
.job-schedule {
|
||||||
color: var(--color-muted-soft);
|
grid-column: 2;
|
||||||
margin: 0 var(--space-xxs);
|
display: flex;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
color: var(--color-muted);
|
||||||
|
font: var(--typo-caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-side {
|
||||||
|
display: grid;
|
||||||
|
align-content: start;
|
||||||
|
gap: var(--space-sm);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-progress-wrap {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-metric-label,
|
||||||
|
.job-metric span {
|
||||||
|
color: var(--color-muted);
|
||||||
|
font: var(--typo-caption);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-progress {
|
||||||
|
display: grid;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-progress-text {
|
||||||
|
color: var(--color-muted);
|
||||||
|
font: var(--typo-code);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-metrics {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-metric {
|
||||||
|
display: grid;
|
||||||
|
gap: 2px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.job-metric strong {
|
||||||
|
color: var(--color-body);
|
||||||
|
font: var(--typo-code);
|
||||||
|
font-weight: 400;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-actions {
|
.job-actions {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
gap: var(--space-xs);
|
gap: var(--space-xs);
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-detail-row {
|
|
||||||
background-color: var(--color-surface-soft);
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-detail {
|
.job-detail {
|
||||||
|
grid-column: 1 / -1;
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--space-sm);
|
gap: var(--space-sm);
|
||||||
padding: var(--space-sm) 0;
|
padding: var(--space-md);
|
||||||
|
background-color: var(--color-surface-soft);
|
||||||
|
border-radius: var(--radius-sm);
|
||||||
}
|
}
|
||||||
|
|
||||||
.job-detail dl {
|
.job-detail dl {
|
||||||
@@ -964,6 +1066,15 @@
|
|||||||
.form-grid {
|
.form-grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
.job-item {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.job-side {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.job-actions {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
@@ -982,4 +1093,20 @@
|
|||||||
.top-nav nav {
|
.top-nav nav {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.job-item {
|
||||||
|
padding: var(--space-md);
|
||||||
|
}
|
||||||
|
.job-main {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
.job-schedule {
|
||||||
|
grid-column: 1;
|
||||||
|
}
|
||||||
|
.job-metrics {
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
.job-detail dl,
|
||||||
|
.job-detail dl div {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
|
<link rel="icon" type="image/svg+xml" href="/assets/favicon.svg">
|
||||||
<link rel="stylesheet" href="/assets/styles/tokens.css">
|
<link rel="stylesheet" href="/assets/styles/tokens.css">
|
||||||
<link rel="stylesheet" href="/assets/styles/base.css">
|
<link rel="stylesheet" href="/assets/styles/base.css">
|
||||||
<link rel="stylesheet" href="/assets/styles/components.css?v=security-fixes-1">
|
<link rel="stylesheet" href="/assets/styles/components.css?v=job-layout-1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="top-nav">
|
<header class="top-nav">
|
||||||
@@ -80,6 +80,6 @@
|
|||||||
<div id="toast-stack" class="toast-stack"></div>
|
<div id="toast-stack" class="toast-stack"></div>
|
||||||
<div id="modal-root"></div>
|
<div id="modal-root"></div>
|
||||||
|
|
||||||
<script type="module" src="/assets/js/app.js?v=security-fixes-1"></script>
|
<script type="module" src="/assets/js/app.js?v=job-layout-1"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
Reference in New Issue
Block a user