Skip to content
This repository was archived by the owner on Feb 4, 2026. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion tools/page-status/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,12 @@ <h1>Page Status</h1>
</div>
<div class="form-field site-field">
<label for="site">Site</label>
<input name="site" id="site" list="site-list" disabled required />
<input name="site" id="site" type="hidden" list="site-list" disabled required />
<datalist id="site-list"></datalist>
<input type="checkbox" id="toggle-all"/>
<label for="toggle-all">All</label>
<div class="form-field site-list" id="site-list-checkboxes">
</div>
</div>
</section>

Expand Down Expand Up @@ -68,6 +72,7 @@ <h1>Page Status</h1>
<caption aria-hidden="true">Page Status Results as of <span class="as-of"></span></caption>
<thead>
<tr>
<th id="page-path">Site</th>
<th id="page-path">Path</th>
<th id="page-status">Status</th>
<th id="page-source">Source Modified</th>
Expand Down
151 changes: 81 additions & 70 deletions tools/page-status/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ const RESULTS = TABLE.querySelector('.results');
const ERROR = TABLE.querySelector('.error');
const FILTER = document.getElementById('status-filter');
const DOWNLOADCSV = document.getElementById('download-csv');
const ORG = document.getElementById('org');
const TOGGLE_ALL = document.getElementById('toggle-all');
let intervalId;
const oneSecondFunction = () => loadingMessages[Math.floor(Math.random() * loadingMessages.length)];

Expand Down Expand Up @@ -127,51 +129,12 @@ function resetLoadingButton(button) {

// form management
/**
* Extracts and formats form data from given form element.
* Returns FormData object from given form element.
* @param {HTMLFormElement} form - Form element.
* @returns {Object} Form data (field names as keys, field values as values).
* @returns {FormData} Form data object.
*/
function getFormData(form) {
const data = {};
[...form.elements].forEach((field) => {
const { name, type, value } = field;
if (name && type && value) {
switch (type) {
// parse number and range as floats
case 'number':
case 'range':
data[name] = parseFloat(value, 10);
break;
// convert date and datetime-local to date objects
case 'date':
case 'datetime-local':
data[name] = new Date(value);
break;
// store checked checkbox values in array
case 'checkbox':
if (field.checked) {
if (data[name]) data[name].push(value);
else data[name] = [value];
}
break;
// only store checked radio
case 'radio':
if (field.checked) data[name] = value;
break;
// convert url to url object
case 'url':
data[name] = new URL(value);
break;
// store file filelist objects
case 'file':
data[name] = field.files;
break;
default:
data[name] = value;
}
}
});
return data;
return new FormData(form);
}

/**
Expand Down Expand Up @@ -307,6 +270,9 @@ function buildSequenceStatus(edit, preview, publish) {
let status;
if (!date(editDate)) {
status = 'No source';
if (date(publishDate)) {
status = 'No source, still published';
}
span.classList.add('negative');
} else if (date(editDate) && !date(previewDate) && !date(publishDate)) {
status = 'Not previewed';
Expand Down Expand Up @@ -335,14 +301,15 @@ function buildSequenceStatus(edit, preview, publish) {
* @param {string} resource.path - The resource's path.
* @returns {HTMLTableRowElement|null} `<tr>` element for resource, or `null` if no `path`.
*/
function buildResource(resource, live, preview) {
function buildResource(resource, live, preview, site) {
const {
path,
sourceLastModified,
previewLastModified,
publishLastModified,
} = resource;
const ignore = ['/helix-env.json', '/sitemap.json'];

const ignore = ['/helix-env.json', '/sitemap.json', '/sitemap.xml'];
if (path && !ignore.includes(path)) {
const row = document.createElement('tr');
const status = buildSequenceStatus(
Expand All @@ -351,6 +318,7 @@ function buildResource(resource, live, preview) {
publishLastModified,
);
const cols = [
site,
path,
status,
sourceLastModified ? toUTCDate(sourceLastModified) : '-',
Expand All @@ -374,9 +342,9 @@ function buildResource(resource, live, preview) {
* @param {string} live - Base URL for live links.
* @param {string} preview - Base URL for preview links.
*/
function displayResources(resources, live, preview) {
function displayResources(resources, live, preview, site) {
resources.forEach((resource) => {
const row = buildResource(resource, live, preview);
const row = buildResource(resource, live, preview, site);
if (row) RESULTS.append(row);
});
}
Expand Down Expand Up @@ -454,7 +422,7 @@ async function fetchJobUrl(org, site, path) {
throw error;
}
// update url param with job
if (json.job.name) updateJobParam(json.job.name);
// if (json.job.name) updateJobParam(json.job.name);
return json.links ? json.links.self : null;
} catch (error) {
updateTableError(error.status, null, `${org}/${site}${path}`);
Expand Down Expand Up @@ -496,12 +464,16 @@ async function runJob(url, retry = 2000) {
* @param {string} preview - Base URL for preview resources.
* @returns {Promise<>} Promise that resolves once job has run and results are displayed.
*/
async function runAndDisplayJob(jobUrl, live, preview) {
const paths = await runJob(jobUrl);
async function runAndDisplayJob(jobUrl, live, preview, site) {
const hasRedirect = (r) => r.previewConfigRedirectLocation || r.publishConfigRedirectLocation;
const paths = (await runJob(jobUrl)).filter((r) => {
console.log(r);
return !r.path.startsWith('/drafts/') && !hasRedirect(r);
});
if (!paths || paths.length === 0) {
throw new Error('No page status data found.');
}
displayResources(paths, live, preview);
displayResources(paths, live, preview, site);
updateTableDisplay('results');
}

Expand All @@ -512,8 +484,6 @@ async function runAndDisplayJob(jobUrl, live, preview) {
*/
function setupJob(form, button) {
disableForm(form, button);
clearTable(RESULTS);
updateTableDisplay('loading');
}

function downloadCSVFile(csvData) {
Expand All @@ -531,35 +501,76 @@ function downloadCSVFile(csvData) {
document.body.removeChild(tempLink);
}

async function addSitesToSiteList(org) {
const resp = await fetch(`https://admin.hlx.page/config/${org}/sites.json`);
const json = await resp.json();
const siteList = document.getElementById('site-list-checkboxes');
siteList.innerHTML = '';
json.sites.forEach((site) => {
const label = document.createElement('label');
label.textContent = site.name;
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.value = site.name;
checkbox.name = 'site';
checkbox.id = `site-${site.name}`;
label.prepend(checkbox);
siteList.append(label);
});
}

function init() {
initConfigField();

TOGGLE_ALL.addEventListener('change', () => {
const sites = document.querySelectorAll('input[name="site"][type="checkbox"]');
sites.forEach((site) => {
site.checked = TOGGLE_ALL.checked;
});
});

ORG.addEventListener('change', () => {
addSitesToSiteList(ORG.value);
});

FORM.addEventListener('reset', () => {
clearTable(RESULTS);
});

FORM.addEventListener('submit', async (e) => {
e.preventDefault();
const { target, submitter } = e;
try {
// initial setup
setupJob(target, submitter);
const data = getFormData(target);
const { org, site, path } = data;
// fetch host config
const { live, preview } = await validateHosts(org, site);
updateConfig();
// fetch page status and display results
const jobUrl = await fetchJobUrl(org, site, path);
if (!jobUrl) throw new Error('Failed to create page status job.');
await runAndDisplayJob(jobUrl, live, preview);
} catch (error) {
updateTableError('Job');
removeJobParam();
} finally {
enableForm(target, submitter);
const checked = FORM.querySelectorAll('input[name="site"]:checked');
const sites = [...checked].map((c) => c.value);

while (sites.length > 0) {
const site = sites.shift();
try {
// initial setup
const formData = getFormData(target);
setupJob(target, submitter);
const org = formData.get('org');
const path = formData.get('path');
// fetch host config
// eslint-disable-next-line no-await-in-loop
const { live, preview } = await validateHosts(org, site);
updateConfig();
// fetch page status and display results
// eslint-disable-next-line no-await-in-loop
const jobUrl = await fetchJobUrl(org, site, path);
if (!jobUrl) throw new Error('Failed to create page status job.');
// eslint-disable-next-line no-await-in-loop
await runAndDisplayJob(jobUrl, live, preview, site);
} catch (error) {
console.error('error running job for site:', site, error);
updateTableError('Job');
removeJobParam();
} finally {
enableForm(target, submitter);
}
}
});

DOWNLOADCSV.addEventListener('click', () => {
let csvData = [];
// Get the header data
Expand Down Expand Up @@ -641,7 +652,7 @@ async function runFromParams(search) {
updateConfig();
// fetch page status and display results
const jobUrl = `https://admin.hlx.page/job/${org}/${site}/main/status/${job}`;
await runAndDisplayJob(jobUrl, live, preview);
await runAndDisplayJob(jobUrl, live, preview, site);
updateJobParam(job);
} catch (error) {
updateTableError('Job');
Expand Down
2 changes: 1 addition & 1 deletion tools/site-admin/site-admin.css
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
.site-admin-oinw::after {
width: 10px;
height: 2px;
background: currentColor;
background: currentcolor;
transform: rotate(-45deg);
bottom: 8px;
}
Expand Down
1 change: 1 addition & 0 deletions utils/config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ function setFieldValue(field, value, type) {
field.value = value;
field.dataset.autofill = type;
field.dispatchEvent(new Event('input'));
field.dispatchEvent(new Event('change'));
}
}

Expand Down