4 - Build your audit app
Write your HTML
- /tools/tag-audit/tag-audit.html (markup)
<!DOCTYPE html>
<html>
<head>
<title>Tag Audit</title>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<link rel="icon" href="data:,">
<script type="importmap">
{ "imports": { "da-lit": "/deps/lit/dist/index.js" } }
</script>
<!-- Import DA App SDK -->
<script src="https://da.live/nx/utils/sdk.js" type="module"></script>
<!-- Project App Logic -->
<script src="/tools/tag-audit/tag-audit.js" type="module"></script>
</head>
<body>
</body>
</html>
Write some basic JavaScript
- /tools/tag-audit/tag-audit.js (JavaScript)
import DA_SDK from 'https://da.live/nx/utils/sdk.js';
(async function init() {
const { context, token } = await DA_SDK;
const { org, repo, path } = context;
console.log(org, repo, path, token);
// const cmp = document.createElement('adl-tag-audit');
// cmp.path = `/${org}/${repo}`;
// cmp.token = token;
// document.body.append(cmp);
}());
Write a few styles
- /tools/tag-audit/tag-audit.css (css)
:host {
display: block;
max-width: var(--grid-container-width);
margin: 32px auto;
}
ul {
list-style: none;
margin: 0;
padding: 0;
.page-list {
display: none;
}
.title-area {
display: flex;
justify-content: space-between;
align-items: center;
}
.title {
line-height: 1;
font-weight: 700;
margin: 0;
}
.title-num-actions {
display: flex;
gap: 8px;
}
.count {
display: flex;
justify-content: center;
align-items: center;
height: 28px;
min-width: 28px;
border: 2px solid var(--s2-blue-200);
padding: 0 12px;
border-radius: 15px;
background-color: var(--s2-blue-200);
}
.tag-item {
padding: 18px;
border-top: 1px solid #efefef;
border-bottom: 1px solid #efefef;
margin: -1px 0;
transition: background-color 0.25s ease-in-out;
a {
padding: 18px 0;
line-height: 1;
}
&.is-open {
.page-list {
display: initial;
}
.title-area {
padding-bottom: 18px;
border-bottom: 1px solid #efefef;
margin-bottom: 18px;
}
}
&:hover {
background-color: var(--s2-blue-100);
}
}
&.tags-list {
border: 2px solid #efefef;
border-radius: 16px;
overflow: hidden;
}
}
Run & test your application
https://da.live/app/{ORG}/{SITE}/tools/tag-audit/tag-audit?ref=local
You should see a console.log of the DA SDK properties.
Update your JavaScript
Main UI
- /tools/tag-audit/tag-audit.js (JavaScript)
import DA_SDK from 'https://da.live/nx/utils/sdk.js';
import { LitElement, html, nothing } from 'da-lit';
import loadTags from './utils.js';
// Super Lite components
import 'https://da.live/nx/public/sl/components.js';
// Application styles
import loadStyle from '../../scripts/utils/styles.js';
const styles = await loadStyle(import.meta.url);
class ADLTagAudit extends LitElement {
static properties = {
path: { attribute: false },
token: { attribute: false },
_status: { state: true },
_tags: { state: true },
};
connectedCallback() {
super.connectedCallback();
this.shadowRoot.adoptedStyleSheets = [styles];
this.getTags();
}
async getTags() {
const setStatus = (message) => {
this._status = message;
};
this._tags = await loadTags(this.path, this.token, setStatus);
console.log(this._tags);
this._status = undefined;
}
toggleOpen(tag) {
tag.open = !tag.open;
this.requestUpdate();
}
renderPages(pages) {
return html`
<ul class="page-list">
${pages.map((page) => html`
<li>
<a href="https://da.live/edit#${page.uiPath}" target="_blank">${page.uiPath}</a>
</li>`)}
</ul>`
}
renderTag(tag) {
const noun = tag.pages.length === 1 ? 'Page' : 'Pages';
return html`
<li class="tag-item ${tag.open ? 'is-open' : ''}">
<div class="title-area">
<span class="title">${tag.name}</span>
<div class="title-num-actions">
<span class="count">${tag.pages.length} ${noun}</span>
<sl-button
class="primary outline"
@click=${() => this.toggleOpen(tag)}>${tag.open ? 'Close' : 'View pages'}</sl-button>
</div>
</div>
${this.renderPages(tag.pages)}
</li>`;
}
renderTags() {
if (!this._tags) return nothing;
return html`
<ul class="tags-list">
${this._tags.map((tag) => this.renderTag(tag))}
</ul>
`;
}
renderStatus() {
return html`<p class="status">${this._status}</p>`;
}
render() {
return html`
<h1>Tag Audit</h1>
${this._status ? this.renderStatus() : this.renderTags()}
`;
}
}
customElements.define('adl-tag-audit', ADLTagAudit);
(async function init() {
const { context, token } = await DA_SDK;
const { org, repo, path } = context;
const cmp = document.createElement('adl-tag-audit');
cmp.path = `/${org}/${repo}`;
cmp.token = token;
document.body.append(cmp);
}());
Utils
- /tools/tag-audit/utils.js (JavaScript)
import { crawl } from 'https://da.live/nx/public/utils/tree.js';
function getOpts(token, method = 'GET') {
return {
method,
headers: { Authorization: `Bearer ${token}` },
};
}
const getMetadata = (el) => [...el.childNodes].reduce((rdx, row) => {
if (row.children) {
const key = row.children[0].textContent.trim().toLowerCase();
const content = row.children[1];
const text = content.textContent.trim().toLowerCase();
if (key && text) rdx[key] = { text };
}
return rdx;
}, {});
async function fetchDoc(path, token) {
const opts = getOpts(token);
const resp = await fetch(`https://admin.da.live/source${path}`, opts);
if (!resp.ok) return { message: 'Could not fetch doc.', status: resp.status };
const html = await resp.text();
return { html };
}
async function loadPageTags(path, token) {
const { html } = await fetchDoc(path, token);
if (!html) return [];
const doc = new DOMParser().parseFromString(html, 'text/html');
const metaEl = doc.querySelector('.metadata');
if (metaEl) {
const { tags } = getMetadata(metaEl);
if (tags) {
return tags.text.split(',').map((tag) => tag.trim().toLowerCase());
}
}
return [];
}
export default async function loadTags(path, token, setStatus) {
const callback = async (item) => {
if (item.ext !== 'html') return;
item.uiPath = item.path.replace('.html', '');
setStatus(`Loading ${item.uiPath}`);
item.tags = await loadPageTags(item.path, token);
};
const { results } = crawl({ path, callback, throttle: 10 });
// Wait for results to finish
const fullfilled = await results;
// Reduce down to our desired format
return fullfilled.reduce((acc, item) => {
if (item.tags?.length > 0) {
// De-dupe tags
const uniqueTags = [...new Set(item.tags)];
// loop through each page tag
uniqueTags.forEach((pageTag) => {
// find the tag in the existing accumulator
const foundTag = acc.find((tag) => tag.name === pageTag);
if (foundTag) {
foundTag.pages.push(item);
} else {
const newTag = { name: pageTag, pages: [item] };
acc.push(newTag);
}
});
}
return acc;
}, []);
}
Test again
Refresh https://da.live/app/{ORG}/{SITE}/tools/tag-audit/tag-audit?ref=local
You should see a list of tags that can expand to show the associated pages.
Add to your project
Create a new "apps" tab in your site config sheet.
Add a new row:
| title | description | image | path | ref |
| Tags Audit | Audit page tags | /app/{ORG}/{SITE}/tools/tag-audit/tag-audit?ref=local | local |
Save your config:
Visit your site apps
https://da.live/apps?ref=local#/{ORG}/{SITE}