fix: refresh public dashboard and BI telemetry
This commit is contained in:
@@ -20,6 +20,9 @@
|
||||
companyReportConfirmRequest: null,
|
||||
health: { database_ready: false, processing_ready: false, monthly_ready: false, download_ready: false, company_reports_ready: false, company_source_upload_ready: false },
|
||||
analytics: null,
|
||||
analyticsLoading: false,
|
||||
biPollTimer: null,
|
||||
biPollLoading: false,
|
||||
toastTimer: null,
|
||||
jobs: [],
|
||||
selectedJobId: "",
|
||||
@@ -55,6 +58,7 @@
|
||||
const I18N = window.ARRI18n;
|
||||
const TRACE_POLL_INTERVAL = 4000;
|
||||
const MONTHLY_POLL_INTERVAL = 4000;
|
||||
const BI_POLL_INTERVAL = 5000;
|
||||
const HISTORY_PAGE_SIZE = 50;
|
||||
const COMPANY_REVIEW_PAGE_SIZE = 50;
|
||||
const COMPANY_REPORT_NAMES = ["LianTai", "QBD", "DY-AI-Easy-KB", "FengRun", "HanaTour"];
|
||||
@@ -303,7 +307,12 @@
|
||||
panel.classList.toggle("is-active", active);
|
||||
});
|
||||
if (updateHash) history.replaceState(null, "", `#${valid}`);
|
||||
if (valid === "bi" && !state.analytics) loadAnalytics();
|
||||
if (valid === "bi") {
|
||||
if (!state.analytics) loadAnalytics();
|
||||
scheduleBiPoll(250);
|
||||
} else {
|
||||
clearBiPoll();
|
||||
}
|
||||
if (valid === "usage" && state.csrf) {
|
||||
loadCompanySource();
|
||||
loadCompanyDraft();
|
||||
@@ -672,6 +681,21 @@
|
||||
state.monthlyPollTimer = null;
|
||||
}
|
||||
|
||||
function clearBiPoll() {
|
||||
window.clearTimeout(state.biPollTimer);
|
||||
state.biPollTimer = null;
|
||||
}
|
||||
|
||||
function biViewActive() {
|
||||
return !document.hidden && $("#panel-bi")?.classList.contains("is-active");
|
||||
}
|
||||
|
||||
function scheduleBiPoll(delay = BI_POLL_INTERVAL) {
|
||||
clearBiPoll();
|
||||
if (!biViewActive()) return;
|
||||
state.biPollTimer = window.setTimeout(checkBiFreshness, delay);
|
||||
}
|
||||
|
||||
function monthlyViewActive() {
|
||||
return !document.hidden && $("#panel-monthly")?.classList.contains("is-active");
|
||||
}
|
||||
@@ -763,6 +787,30 @@
|
||||
if (months.some((item) => item.month_key === current)) select.value = current;
|
||||
}
|
||||
|
||||
async function checkBiFreshness() {
|
||||
if (state.biPollLoading || !biViewActive()) {
|
||||
scheduleBiPoll();
|
||||
return;
|
||||
}
|
||||
state.biPollLoading = true;
|
||||
try {
|
||||
const months = await api("/api/months");
|
||||
populateMonths(months);
|
||||
const month = $("#bi-month").value || localMonth();
|
||||
const metadata = months.find((item) => item.month_key === month);
|
||||
const current = state.analytics;
|
||||
const changed = !current
|
||||
|| current.month_key !== month
|
||||
|| Boolean(metadata && metadata.updated_at !== current.updated_at);
|
||||
if (changed && !state.analyticsLoading) await loadAnalytics(false, Boolean(current));
|
||||
} catch (_) {
|
||||
// Keep the last good BI snapshot during a transient background failure.
|
||||
} finally {
|
||||
state.biPollLoading = false;
|
||||
scheduleBiPoll();
|
||||
}
|
||||
}
|
||||
|
||||
function renderCompanySales(channels) {
|
||||
const sorted = [...channels].sort((a, b) => Number(b.totals.total_price) - Number(a.totals.total_price));
|
||||
const maximum = Math.max(1, ...sorted.map((item) => Number(item.totals.total_price || 0)));
|
||||
@@ -832,13 +880,17 @@
|
||||
$("#room-matrix").innerHTML = `<p class="empty-state">${escapeHtml(message)}</p>`;
|
||||
}
|
||||
|
||||
async function loadAnalytics(showErrors = false) {
|
||||
async function loadAnalytics(showErrors = false, preserveOnError = false) {
|
||||
if (state.analyticsLoading) return;
|
||||
state.analyticsLoading = true;
|
||||
const month = $("#bi-month").value || localMonth();
|
||||
try {
|
||||
renderAnalytics(await api(`/api/analytics?month=${encodeURIComponent(month)}`));
|
||||
} catch (error) {
|
||||
resetAnalytics(I18N?.t("bi.empty") || "该月份暂无可用看板数据");
|
||||
if (!preserveOnError) resetAnalytics(I18N?.t("bi.empty") || "该月份暂无可用看板数据");
|
||||
if (showErrors) showToast(error.message, true);
|
||||
} finally {
|
||||
state.analyticsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1012,14 +1064,6 @@
|
||||
|| `批次 #${batch} · ${rowCount} 条记录 · ${roomCount} 间 · 启用于 ${activated}`;
|
||||
}
|
||||
|
||||
function companyJobSourceMeta(source) {
|
||||
const batchId = Number(source?.source_batch_id);
|
||||
const batch = Number.isInteger(batchId) && batchId > 0 ? formatInteger(batchId) : "—";
|
||||
const activated = source?.activated_at ? formatDate(source.activated_at, true) : (I18N?.t("company.review_no_record") || "暂无");
|
||||
return I18N?.t("company.job_source_meta", { batch, activated })
|
||||
|| `批次 #${batch} · 启用于 ${activated}`;
|
||||
}
|
||||
|
||||
function renderCompanySource(source) {
|
||||
const panel = $("#company-source-current");
|
||||
if (!panel) return;
|
||||
@@ -1655,6 +1699,26 @@
|
||||
return I18N?.t(key || "problem.review_result") || "需复核处理结果";
|
||||
}
|
||||
|
||||
function companyProblemDetail(problem) {
|
||||
if (problem?.code !== "COMPANY_REPORT_PUBLISH_FAILED") return "";
|
||||
return I18N?.t("company.publish_failed_detail")
|
||||
|| "数据已处理,但正式 Excel 保存失败。请重启报表服务后再生成。";
|
||||
}
|
||||
|
||||
function companyPublishFailureSummary(job) {
|
||||
const failedResults = (job?.company_results || []).filter((result) => result?.status === "failed");
|
||||
const publishFailures = failedResults.filter((result) =>
|
||||
(result.errors || []).some((problem) => problem?.code === "COMPANY_REPORT_PUBLISH_FAILED"),
|
||||
);
|
||||
if (!publishFailures.length) return "";
|
||||
if (publishFailures.length === COMPANY_REPORT_NAMES.length) {
|
||||
return I18N?.t("company.publish_failed_summary_all")
|
||||
|| "数据已处理,但 5 家公司的正式 Excel 保存失败。请重启报表服务后再生成。";
|
||||
}
|
||||
return I18N?.t("company.publish_failed_summary_partial")
|
||||
|| "数据已处理,但部分公司的正式 Excel 保存失败。请重启报表服务后再生成。";
|
||||
}
|
||||
|
||||
function uniqueCompanyProblems(problems) {
|
||||
const seen = new Set();
|
||||
return (problems || []).filter((item) => {
|
||||
@@ -1715,7 +1779,10 @@
|
||||
}
|
||||
const problems = companyProblemsForResult(result);
|
||||
const problemHtml = problems.length
|
||||
? problems.map((item) => `<span class="company-problem is-${item.kind}">${escapeHtml(companyProblemLabel(item.code))}<small>${escapeHtml(item.period || I18N?.t("company.review_no_record") || "暂无")}</small></span>`).join("")
|
||||
? problems.map((item) => {
|
||||
const detail = companyProblemDetail(item);
|
||||
return `<span class="company-problem is-${item.kind}">${escapeHtml(companyProblemLabel(item.code))}<small>${escapeHtml(item.period || I18N?.t("company.review_no_record") || "暂无")}</small>${detail ? `<small class="company-problem-detail">${escapeHtml(detail)}</small>` : ""}</span>`;
|
||||
}).join("")
|
||||
: `<span class="cell-subtle">${escapeHtml(I18N?.t("company.review_no_record") || "暂无")}</span>`;
|
||||
const download = result?.download_url?.startsWith("/api/company-reports/")
|
||||
? `<a class="download-link" href="${escapeHtml(result.download_url)}">${escapeHtml(I18N?.t("common.download") || "下载")}</a>`
|
||||
@@ -1746,7 +1813,7 @@
|
||||
kicker.hidden = terminal;
|
||||
$("#company-report-job-title").textContent = `${monthLabel(job.report_month)} · ${companyCutoffLabel(job)}`;
|
||||
const message = $("#company-report-job-message");
|
||||
const messageText = job.state === "failed" && !job.failure_code ? "" : (I18N?.errorMessage(job.failure_code, job.message) || I18N?.text(job.message || "") || job.message || "");
|
||||
const messageText = (job.state === "failed" && !job.failure_code ? "" : (I18N?.errorMessage(job.failure_code, job.message) || I18N?.text(job.message || "") || job.message || "")) || companyPublishFailureSummary(job);
|
||||
message.textContent = messageText;
|
||||
message.hidden = !messageText;
|
||||
const status = $("#company-report-job-state");
|
||||
@@ -1762,16 +1829,6 @@
|
||||
$("#company-report-job-as-of").textContent = job.as_of_date ? formatDate(job.as_of_date) : (I18N?.t("company.review_no_record") || "暂无");
|
||||
$("#company-report-job-duration").textContent = formatDurationSeconds(job.duration_seconds);
|
||||
$("#company-report-warning-count").textContent = I18N?.t("company.report_warning_count", { count: formatInteger(companyWarningCount(job)) }) || `${companyWarningCount(job)} 项`;
|
||||
const source = job?.source;
|
||||
const sourcePanel = $("#company-report-job-source");
|
||||
sourcePanel.classList.toggle("is-missing", !source);
|
||||
$("#company-report-job-source-file").textContent = source
|
||||
? companySourceFilename(source)
|
||||
: (I18N?.t("company.job_source_missing") || "历史任务未记录来源");
|
||||
$("#company-report-job-source-file").title = source ? companySourceFilename(source) : "";
|
||||
$("#company-report-job-source-meta").textContent = source
|
||||
? companyJobSourceMeta(source)
|
||||
: (I18N?.t("company.review_no_record") || "暂无");
|
||||
$("#company-result-month-end-header").textContent = companyPeriodRange(job.report_month, "21-month-end");
|
||||
renderCompanyResultRows(job);
|
||||
localStorage.setItem("arr:last-company-report-job", job.job_id);
|
||||
@@ -1783,13 +1840,10 @@
|
||||
state.companyReportJobs = jobs;
|
||||
const body = $("#company-history-rows");
|
||||
if (!jobs.length) {
|
||||
body.innerHTML = `<tr><td class="empty-cell" colspan="8">${historyEmptyMarkup("company", "history.company_empty", "暂无公司渠道明细任务")}</td></tr>`;
|
||||
body.innerHTML = `<tr><td class="empty-cell" colspan="7">${historyEmptyMarkup("company", "history.company_empty", "暂无公司渠道明细任务")}</td></tr>`;
|
||||
return;
|
||||
}
|
||||
body.innerHTML = jobs.map((job) => {
|
||||
const source = job?.source;
|
||||
const sourceFilename = source ? companySourceFilename(source) : (I18N?.t("company.job_source_missing") || "历史任务未记录来源");
|
||||
const sourceMeta = source ? companyJobSourceMeta(source) : (I18N?.t("company.review_no_record") || "暂无");
|
||||
return `<tr>
|
||||
<td>${formatDate(job.created_at, true)}</td>
|
||||
<td><strong>${escapeHtml(job.report_month ? monthLabel(job.report_month) : (I18N?.t("company.review_no_record") || "暂无"))}</strong></td>
|
||||
@@ -1797,7 +1851,6 @@
|
||||
<td><span class="status-chip ${companyStateStyle(job.state)}">${escapeHtml(companyStateLabel(job.state))}</span></td>
|
||||
<td>${companySuccessCount(job)} / 5</td>
|
||||
<td>${companyWarningCount(job)}</td>
|
||||
<td><div class="company-history-source"><strong title="${escapeHtml(sourceFilename)}">${escapeHtml(sourceFilename)}</strong><small>${escapeHtml(sourceMeta)}</small></div></td>
|
||||
<td><button class="company-detail-button" type="button" data-company-job-id="${escapeHtml(job.job_id)}">${escapeHtml(I18N?.t("common.view") || "查看")}</button></td>
|
||||
</tr>`;
|
||||
}).join("");
|
||||
@@ -2221,9 +2274,11 @@
|
||||
if (document.hidden) {
|
||||
clearTracePoll();
|
||||
clearMonthlyPoll();
|
||||
clearBiPoll();
|
||||
} else {
|
||||
scheduleTracePoll(250);
|
||||
if ($("#panel-monthly")?.classList.contains("is-active")) loadMonthly(false, true);
|
||||
if ($("#panel-bi")?.classList.contains("is-active")) checkBiFreshness();
|
||||
}
|
||||
});
|
||||
document.addEventListener("arr:locale-change", refreshLocalizedView);
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
const BUSINESS_TIME_ZONE = "Asia/Bangkok";
|
||||
const I18N = window.ARRI18n;
|
||||
const $ = (selector) => document.querySelector(selector);
|
||||
const BI_POLL_INTERVAL = 5000;
|
||||
let csrf = "";
|
||||
let lastData = null;
|
||||
let dataLoading = false;
|
||||
let biPollTimer = null;
|
||||
let biPollLoading = false;
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "").replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
||||
@@ -131,13 +135,60 @@
|
||||
renderMatrix(data);
|
||||
}
|
||||
|
||||
async function load() {
|
||||
function populateMonths(months) {
|
||||
const select = $("#h5-month");
|
||||
const current = select.value;
|
||||
select.innerHTML = months.length
|
||||
? months.map((item) => `<option value="${escapeHtml(item.month_key)}">${escapeHtml(item.month_key)}</option>`).join("")
|
||||
: `<option value="${monthNow()}">${monthNow()}</option>`;
|
||||
if (months.some((item) => item.month_key === current)) select.value = current;
|
||||
}
|
||||
|
||||
async function load(showErrors = true, preserveOnError = false) {
|
||||
if (dataLoading) return;
|
||||
dataLoading = true;
|
||||
const month = $("#h5-month").value || monthNow();
|
||||
try {
|
||||
render(await api(`/api/public/h5/analytics?month=${encodeURIComponent(month)}`));
|
||||
} catch (error) {
|
||||
reset(I18N?.t("bi.empty") || "该月份暂无可用看板数据");
|
||||
notify(error.message);
|
||||
if (!preserveOnError) reset(I18N?.t("bi.empty") || "该月份暂无可用看板数据");
|
||||
if (showErrors) notify(error.message);
|
||||
} finally {
|
||||
dataLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function clearBiPoll() {
|
||||
window.clearTimeout(biPollTimer);
|
||||
biPollTimer = null;
|
||||
}
|
||||
|
||||
function scheduleBiPoll(delay = BI_POLL_INTERVAL) {
|
||||
clearBiPoll();
|
||||
if (document.hidden) return;
|
||||
biPollTimer = window.setTimeout(checkBiFreshness, delay);
|
||||
}
|
||||
|
||||
async function checkBiFreshness() {
|
||||
if (biPollLoading || dataLoading || document.hidden) {
|
||||
scheduleBiPoll();
|
||||
return;
|
||||
}
|
||||
biPollLoading = true;
|
||||
try {
|
||||
const months = await api("/api/public/h5/months");
|
||||
populateMonths(months);
|
||||
const month = $("#h5-month").value || monthNow();
|
||||
const metadata = months.find((item) => item.month_key === month);
|
||||
const changed = !lastData
|
||||
|| lastData.month_key !== month
|
||||
|| Boolean(metadata && metadata.updated_at !== lastData.updated_at);
|
||||
if (changed) await load(false, Boolean(lastData));
|
||||
} catch (_) {
|
||||
// Keep the last good H5 snapshot during a transient background failure.
|
||||
} finally {
|
||||
biPollLoading = false;
|
||||
scheduleBiPoll();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,13 +249,17 @@
|
||||
}
|
||||
let months = [];
|
||||
try { months = await api("/api/public/h5/months"); } catch (_) { /* handled by load */ }
|
||||
populateMonths(months);
|
||||
const select = $("#h5-month");
|
||||
select.innerHTML = months.length
|
||||
? months.map((item) => `<option value="${escapeHtml(item.month_key)}">${escapeHtml(item.month_key)}</option>`).join("")
|
||||
: `<option value="${monthNow()}">${monthNow()}</option>`;
|
||||
select.addEventListener("change", load);
|
||||
select.addEventListener("change", () => load(true));
|
||||
await load();
|
||||
scheduleBiPoll(250);
|
||||
}
|
||||
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.hidden) clearBiPoll();
|
||||
else checkBiFreshness();
|
||||
});
|
||||
|
||||
boot();
|
||||
})();
|
||||
|
||||
@@ -270,10 +270,9 @@
|
||||
"company.source_meta": ["批次 #{batch} · {rows} 条记录 · {rooms} 间 · 启用于 {activated}", "Batch #{batch} · {rows} records · {rooms} rooms · Activated {activated}", "ชุดข้อมูล #{batch} · {rows} รายการ · {rooms} ห้อง · เปิดใช้งานเมื่อ {activated}"],
|
||||
"company.source_file_missing": ["未记录文件名", "File name not recorded", "ไม่ได้บันทึกชื่อไฟล์"],
|
||||
"company.source_already_active": ["这份 Excel 已是当前启用来源;请在下方按月份查看或生成对应报表", "This Excel is already the active source. Review or generate the corresponding report by month below.", "Excel นี้เป็นแหล่งข้อมูลที่เปิดใช้งานอยู่แล้ว ตรวจสอบหรือสร้างรายงานตามเดือนได้ด้านล่าง"],
|
||||
"company.job_source": ["来源 Excel(提交时)", "Source Excel (at submission)", "แหล่ง Excel (ขณะส่งงาน)"],
|
||||
"company.history_source": ["来源 Excel", "Source Excel", "แหล่ง Excel"],
|
||||
"company.job_source_missing": ["历史任务未记录来源", "Source not recorded for this historical job", "งานย้อนหลังนี้ไม่ได้บันทึกแหล่งข้อมูล"],
|
||||
"company.job_source_meta": ["批次 #{batch} · 启用于 {activated}", "Batch #{batch} · Activated {activated}", "ชุดข้อมูล #{batch} · เปิดใช้งานเมื่อ {activated}"],
|
||||
"company.publish_failed_summary_all": ["数据已处理,但 5 家公司的正式 Excel 保存失败。请重启报表服务后再生成。", "The data was processed, but the official Excel files for all five companies could not be saved. Restart the report service and generate again.", "ประมวลผลข้อมูลแล้ว แต่ไม่สามารถบันทึกไฟล์ Excel อย่างเป็นทางการของทั้ง 5 บริษัทได้ โปรดรีสตาร์ตบริการรายงานแล้วสร้างใหม่"],
|
||||
"company.publish_failed_summary_partial": ["数据已处理,但部分公司的正式 Excel 保存失败。请重启报表服务后再生成。", "The data was processed, but some official Excel files could not be saved. Restart the report service and generate again.", "ประมวลผลข้อมูลแล้ว แต่ไม่สามารถบันทึกไฟล์ Excel อย่างเป็นทางการของบางบริษัทได้ โปรดรีสตาร์ตบริการรายงานแล้วสร้างใหม่"],
|
||||
"company.publish_failed_detail": ["数据已处理,但正式 Excel 保存失败。请重启报表服务后再生成。", "The data was processed, but the official Excel could not be saved. Restart the report service and generate again.", "ประมวลผลข้อมูลแล้ว แต่ไม่สามารถบันทึกไฟล์ Excel อย่างเป็นทางการได้ โปรดรีสตาร์ตบริการรายงานแล้วสร้างใหม่"],
|
||||
"company.draft_error": ["当前提取结果暂时无法读取", "The current extraction is temporarily unavailable", "ไม่สามารถอ่านผลการแยกปัจจุบันได้ชั่วคราว"],
|
||||
"company.file_xlsx": ["请选择 .xlsx 格式的 Excel 文件", "Choose an .xlsx Excel file", "เลือกไฟล์ Excel รูปแบบ .xlsx"],
|
||||
"company.file_empty": ["Excel 文件为空", "The Excel file is empty", "ไฟล์ Excel ว่างเปล่า"],
|
||||
@@ -427,7 +426,7 @@
|
||||
"problem.nights_conflict": ["间夜数据冲突", "Room-night data conflict", "ข้อมูลคืนห้องพักขัดแย้งกัน"],
|
||||
"problem.total_price": ["静态价格异常", "Invalid static price", "ราคาคงที่ไม่ถูกต้อง"],
|
||||
"problem.output_validation": ["Excel 输出校验失败", "Excel output validation failed", "ตรวจสอบผลลัพธ์ Excel ไม่ผ่าน"],
|
||||
"problem.publish_failed": ["正式表格发布失败", "Official workbook publication failed", "เผยแพร่ไฟล์อย่างเป็นทางการไม่สำเร็จ"],
|
||||
"problem.publish_failed": ["正式 Excel 保存失败", "Official Excel save failed", "บันทึกไฟล์ Excel อย่างเป็นทางการไม่สำเร็จ"],
|
||||
"problem.internal": ["处理服务异常", "Processing service error", "บริการประมวลผลขัดข้อง"],
|
||||
"problem.execution": ["任务执行未完成", "Job execution did not complete", "การทำงานยังไม่เสร็จ"],
|
||||
"problem.interrupted": ["服务重启中断任务", "The service restart interrupted the job", "การรีสตาร์ตบริการขัดจังหวะงาน"],
|
||||
|
||||
@@ -338,13 +338,6 @@
|
||||
<strong id="company-report-success-count">0 / 5</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="company-job-source" id="company-report-job-source">
|
||||
<span class="company-job-source-label">来源 Excel(提交时)</span>
|
||||
<div>
|
||||
<strong id="company-report-job-source-file">历史任务未记录来源</strong>
|
||||
<small id="company-report-job-source-meta">—</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="company-progress-wrap">
|
||||
<div><span id="company-report-progress-label">等待生成</span><strong id="company-report-progress-percent">0%</strong></div>
|
||||
<div class="company-progress-track" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0" id="company-report-progress-track"><span id="company-report-progress-bar"></span></div>
|
||||
@@ -382,8 +375,8 @@
|
||||
</div>
|
||||
<div class="table-scroll">
|
||||
<table class="company-history-table">
|
||||
<thead><tr><th>提交时间(曼谷)</th><th>报表月份</th><th>截止期间</th><th>状态</th><th>已生成</th><th>需复核</th><th>来源 Excel</th><th>详情</th></tr></thead>
|
||||
<tbody id="company-history-rows"><tr><td class="empty-cell" colspan="8">正在读取公司渠道明细任务…</td></tr></tbody>
|
||||
<thead><tr><th>提交时间(曼谷)</th><th>报表月份</th><th>截止期间</th><th>状态</th><th>已生成</th><th>需复核</th><th>详情</th></tr></thead>
|
||||
<tbody id="company-history-rows"><tr><td class="empty-cell" colspan="7">正在读取公司渠道明细任务…</td></tr></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav class="pagination-bar" aria-label="生成记录分页">
|
||||
|
||||
@@ -514,11 +514,6 @@ input:focus, select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px rgb
|
||||
.company-progress-wrap > div:first-child strong { color: var(--ink); }
|
||||
.company-progress-track { height: 7px; margin-top: 7px; overflow: hidden; border-radius: 99px; background: #edf1f6; }
|
||||
.company-progress-track span { width: 100%; height: 100%; display: block; border-radius: inherit; background: var(--blue); transform: scaleX(0); transform-origin: left; transition: transform .25s ease; }
|
||||
.company-job-source { margin-top: 16px; padding: 11px 13px; display: flex; align-items: flex-start; gap: 10px; border: 1px solid #dbe3ef; border-radius: 10px; background: #fbfcfe; }
|
||||
.company-job-source.is-missing { background: #fafafa; }
|
||||
.company-job-source-label { flex: 0 0 auto; padding-top: 2px; color: var(--muted); font-size: 10px; font-weight: 750; }
|
||||
.company-job-source strong { display: block; color: var(--ink); font-size: 12px; line-height: 1.35; overflow-wrap: anywhere; }
|
||||
.company-job-source small { display: block; margin-top: 3px; color: var(--muted); font-size: 10px; line-height: 1.35; }
|
||||
.company-job-meta { margin: 17px 0 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 8px; }
|
||||
.company-job-meta > div { min-width: 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: 9px; background: var(--surface-soft); }
|
||||
.company-job-meta dt { color: var(--muted); font-size: 10px; }
|
||||
@@ -536,6 +531,7 @@ input:focus, select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px rgb
|
||||
.company-problem-list { max-width: 280px; display: grid; gap: 4px; white-space: normal; }
|
||||
.company-problem { font-size: 10px; font-weight: 750; line-height: 1.35; }
|
||||
.company-problem small { margin-left: 5px; color: inherit; opacity: .72; font-weight: 600; }
|
||||
.company-problem .company-problem-detail { display: block; margin: 3px 0 0; opacity: .86; font-weight: 500; }
|
||||
.company-problem.is-warning { color: #9a4b0a; }
|
||||
.company-problem.is-error { color: #b42318; }
|
||||
.company-generated-at-cell { font-size: 10px; font-weight: 750; white-space: nowrap; }
|
||||
@@ -546,9 +542,6 @@ input:focus, select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px rgb
|
||||
.company-history-panel { margin-top: 20px; }
|
||||
.company-history-count { color: var(--muted); font-size: 11px; }
|
||||
.company-history-table { min-width: 980px; }
|
||||
.company-history-source { min-width: 150px; display: grid; gap: 2px; }
|
||||
.company-history-source strong { font-size: 10px; line-height: 1.35; overflow-wrap: anywhere; }
|
||||
.company-history-source small { color: var(--muted); font-size: 9px; line-height: 1.35; }
|
||||
.company-detail-button { min-height: 30px; padding: 0 9px; border: 1px solid var(--line-strong); border-radius: 7px; color: var(--blue); background: #fff; font-size: 10px; font-weight: 750; cursor: pointer; }
|
||||
.company-detail-button:hover { border-color: #a9c1f5; background: var(--blue-soft); }
|
||||
.page-footer { width: min(1400px, calc(100% - 48px)); margin: -38px auto 28px; display: flex; justify-content: flex-start; gap: 20px; color: var(--muted); font-size: 10px; }
|
||||
@@ -706,7 +699,6 @@ input:focus, select:focus { border-color: var(--blue); box-shadow: 0 0 0 3px rgb
|
||||
.company-job-panel { padding-left: 18px; padding-right: 18px; }
|
||||
.company-job-header { align-items: flex-start; flex-direction: column; gap: 14px; }
|
||||
.company-job-summary { justify-items: start; }
|
||||
.company-job-source { align-items: flex-start; }
|
||||
.company-job-meta { grid-template-columns: 1fr 1fr; }
|
||||
.company-result-scroll { margin-left: 0; margin-right: 0; overflow: visible; }
|
||||
.company-result-table, .company-result-table tbody { min-width: 0; display: block; }
|
||||
|
||||
Reference in New Issue
Block a user