Update static/js/06-pages.js
This commit is contained in:
parent
463a3a1fcb
commit
35b6dc53d9
@ -1,6 +1,7 @@
|
|||||||
// static/js/06-pages.js
|
// static/js/06-pages.js
|
||||||
"use strict";
|
"use strict";
|
||||||
window.Glow = window.Glow || {};
|
window.Glow = window.Glow || {};
|
||||||
|
// comments, version history, and share links intentionally disabled in production until backend support complete
|
||||||
|
|
||||||
Glow.renderPagesEmptyState = message => {
|
Glow.renderPagesEmptyState = message => {
|
||||||
Glow.els.pagesBox.replaceChildren();
|
Glow.els.pagesBox.replaceChildren();
|
||||||
@ -15,49 +16,21 @@ Glow.renderPagesEmptyState = message => {
|
|||||||
|
|
||||||
Glow.pageTypeLabel = type => {
|
Glow.pageTypeLabel = type => {
|
||||||
const labels = {
|
const labels = {
|
||||||
project: "Project",
|
project: "project",
|
||||||
document: "Document",
|
document: "document",
|
||||||
update: "Update",
|
update: "update",
|
||||||
resource: "Resource"
|
resource: "resource"
|
||||||
};
|
};
|
||||||
return labels[type] || "Page";
|
return labels[type] || "page";
|
||||||
};
|
};
|
||||||
|
|
||||||
Glow.apiListPageVersions = (teamSlug, pageSlug) =>
|
|
||||||
Glow.apiRequest(`/api/teams/${encodeURIComponent(teamSlug)}/pages/${encodeURIComponent(pageSlug)}/versions`);
|
|
||||||
Glow.apiGetPageVersion = versionId =>
|
|
||||||
Glow.apiRequest(`/api/page-versions/${encodeURIComponent(versionId)}`);
|
|
||||||
Glow.apiRestorePageVersion = versionId =>
|
|
||||||
Glow.apiRequest(`/api/page-versions/${encodeURIComponent(versionId)}/restore`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({})
|
|
||||||
});
|
|
||||||
Glow.apiListPageComments = (teamSlug, pageSlug) =>
|
|
||||||
Glow.apiRequest(`/api/teams/${encodeURIComponent(teamSlug)}/pages/${encodeURIComponent(pageSlug)}/comments`);
|
|
||||||
Glow.apiCreatePageComment = (teamSlug, pageSlug, payload) =>
|
|
||||||
Glow.apiRequest(`/api/teams/${encodeURIComponent(teamSlug)}/pages/${encodeURIComponent(pageSlug)}/comments`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify(payload)
|
|
||||||
});
|
|
||||||
Glow.apiDeletePageComment = commentId =>
|
|
||||||
Glow.apiRequest(`/api/page-comments/${encodeURIComponent(commentId)}`, {
|
|
||||||
method: "DELETE"
|
|
||||||
});
|
|
||||||
Glow.apiCreatePageShare = (teamSlug, pageSlug) =>
|
|
||||||
Glow.apiRequest(`/api/teams/${encodeURIComponent(teamSlug)}/pages/${encodeURIComponent(pageSlug)}/share`, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({})
|
|
||||||
});
|
|
||||||
|
|
||||||
Glow.loadPages = async (pageType = Glow.state.pageTypeFilter) => {
|
Glow.loadPages = async (pageType = Glow.state.pageTypeFilter) => {
|
||||||
const team = Glow.state.activeTeam;
|
const team = Glow.state.activeTeam;
|
||||||
if (!team) {
|
if (!team) {
|
||||||
Glow.renderPagesEmptyState("Select a team to view pages.");
|
Glow.renderPagesEmptyState("select a team to view pages.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Glow.els.pagesBox.setAttribute("aria-busy", "true");
|
Glow.els.pagesBox.setAttribute("aria-busy", "true");
|
||||||
try {
|
try {
|
||||||
const query = pageType ? `?type=${encodeURIComponent(pageType)}` : "";
|
const query = pageType ? `?type=${encodeURIComponent(pageType)}` : "";
|
||||||
@ -69,8 +42,8 @@ Glow.loadPages = async (pageType = Glow.state.pageTypeFilter) => {
|
|||||||
Glow.state.pageTypeFilter = pageType || "";
|
Glow.state.pageTypeFilter = pageType || "";
|
||||||
Glow.renderPages();
|
Glow.renderPages();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Glow.renderPagesEmptyState(error.message || "Could not load pages.");
|
Glow.renderPagesEmptyState(error.message || "could not load pages.");
|
||||||
Glow.showToast(error.message || "Could not load pages.", "error");
|
Glow.showToast(error.message || "could not load pages.", "error");
|
||||||
} finally {
|
} finally {
|
||||||
Glow.els.pagesBox.setAttribute("aria-busy", "false");
|
Glow.els.pagesBox.setAttribute("aria-busy", "false");
|
||||||
}
|
}
|
||||||
@ -84,11 +57,12 @@ Glow.renderPages = () => {
|
|||||||
|
|
||||||
const header = document.createElement("div");
|
const header = document.createElement("div");
|
||||||
header.className = "pages-header";
|
header.className = "pages-header";
|
||||||
|
|
||||||
const copy = document.createElement("div");
|
const copy = document.createElement("div");
|
||||||
const title = document.createElement("h2");
|
const title = document.createElement("h2");
|
||||||
title.textContent = `${team?.name || "Team"} pages`;
|
title.textContent = `${team?.name || "team"} pages`;
|
||||||
const subtitle = document.createElement("p");
|
const subtitle = document.createElement("p");
|
||||||
subtitle.textContent = "Shared team knowledge and public resources.";
|
subtitle.textContent = "shared team knowledge and public resources.";
|
||||||
copy.append(title, subtitle);
|
copy.append(title, subtitle);
|
||||||
header.append(copy);
|
header.append(copy);
|
||||||
|
|
||||||
@ -96,21 +70,23 @@ Glow.renderPages = () => {
|
|||||||
const create = document.createElement("button");
|
const create = document.createElement("button");
|
||||||
create.className = "button";
|
create.className = "button";
|
||||||
create.type = "button";
|
create.type = "button";
|
||||||
create.textContent = "New page";
|
create.textContent = "new page";
|
||||||
create.addEventListener("click", Glow.createPage);
|
create.addEventListener("click", Glow.createPage);
|
||||||
header.append(create);
|
header.append(create);
|
||||||
}
|
}
|
||||||
|
|
||||||
box.append(header);
|
box.append(header);
|
||||||
|
|
||||||
const filters = document.createElement("div");
|
const filters = document.createElement("div");
|
||||||
filters.className = "page-filters compact";
|
filters.className = "page-filters compact";
|
||||||
const filterOptions = [
|
const filterOptions = [
|
||||||
["", "All"],
|
["", "all"],
|
||||||
["project", "Projects"],
|
["project", "projects"],
|
||||||
["document", "Documents"],
|
["document", "documents"],
|
||||||
["update", "Updates"],
|
["update", "updates"],
|
||||||
["resource", "Resources"]
|
["resource", "resources"]
|
||||||
];
|
];
|
||||||
|
|
||||||
for (const [value, label] of filterOptions) {
|
for (const [value, label] of filterOptions) {
|
||||||
const button = document.createElement("button");
|
const button = document.createElement("button");
|
||||||
button.className = "page-filter";
|
button.className = "page-filter";
|
||||||
@ -120,6 +96,7 @@ Glow.renderPages = () => {
|
|||||||
button.addEventListener("click", () => Glow.loadPages(value));
|
button.addEventListener("click", () => Glow.loadPages(value));
|
||||||
filters.append(button);
|
filters.append(button);
|
||||||
}
|
}
|
||||||
|
|
||||||
box.append(filters);
|
box.append(filters);
|
||||||
|
|
||||||
if (!pages.length) {
|
if (!pages.length) {
|
||||||
@ -127,7 +104,7 @@ Glow.renderPages = () => {
|
|||||||
empty.className = "empty-state page-empty-state";
|
empty.className = "empty-state page-empty-state";
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "empty-card";
|
card.className = "empty-card";
|
||||||
card.textContent = "No pages match this filter.";
|
card.textContent = "no pages match this filter.";
|
||||||
empty.append(card);
|
empty.append(card);
|
||||||
box.append(empty);
|
box.append(empty);
|
||||||
return;
|
return;
|
||||||
@ -135,9 +112,11 @@ Glow.renderPages = () => {
|
|||||||
|
|
||||||
const list = document.createElement("div");
|
const list = document.createElement("div");
|
||||||
list.className = "pages-list";
|
list.className = "pages-list";
|
||||||
|
|
||||||
for (const page of pages) {
|
for (const page of pages) {
|
||||||
list.append(Glow.buildPageCard(page));
|
list.append(Glow.buildPageCard(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
box.append(list);
|
box.append(list);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -164,17 +143,17 @@ Glow.buildPageCard = page => {
|
|||||||
|
|
||||||
const footer = document.createElement("div");
|
const footer = document.createElement("div");
|
||||||
footer.className = "page-card-footer";
|
footer.className = "page-card-footer";
|
||||||
|
|
||||||
const context = document.createElement("span");
|
const context = document.createElement("span");
|
||||||
context.textContent = page.parent_page_title
|
context.textContent = page.channel_name
|
||||||
? `Child of ${page.parent_page_title}`
|
|
||||||
: page.channel_name
|
|
||||||
? `#${page.channel_name}`
|
? `#${page.channel_name}`
|
||||||
: "Team page";
|
: "team page";
|
||||||
|
|
||||||
const updated = document.createElement("time");
|
const updated = document.createElement("time");
|
||||||
updated.dateTime = page.updated_at;
|
updated.dateTime = page.updated_at;
|
||||||
updated.title = new Date(page.updated_at).toLocaleString();
|
updated.title = new Date(page.updated_at).toLocaleString();
|
||||||
updated.textContent = `Updated ${Glow.relativeTime(page.updated_at)}`;
|
updated.textContent = `updated ${Glow.relativeTime(page.updated_at)}`;
|
||||||
|
|
||||||
footer.append(context, updated);
|
footer.append(context, updated);
|
||||||
|
|
||||||
const open = () => Glow.openPage(page.slug);
|
const open = () => Glow.openPage(page.slug);
|
||||||
@ -201,10 +180,12 @@ Glow.openPages = async () => {
|
|||||||
Glow.openPage = async pageSlug => {
|
Glow.openPage = async pageSlug => {
|
||||||
const team = Glow.state.activeTeam;
|
const team = Glow.state.activeTeam;
|
||||||
if (!team) return;
|
if (!team) return;
|
||||||
|
|
||||||
Glow.state.activeView = "page";
|
Glow.state.activeView = "page";
|
||||||
Glow.state.activePage = null;
|
Glow.state.activePage = null;
|
||||||
Glow.updateViewUI();
|
Glow.updateViewUI();
|
||||||
Glow.els.pageDetailBox.setAttribute("aria-busy", "true");
|
Glow.els.pageDetailBox.setAttribute("aria-busy", "true");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await Glow.apiRequest(
|
const data = await Glow.apiRequest(
|
||||||
`/api/teams/${encodeURIComponent(team.slug)}/pages/${encodeURIComponent(pageSlug)}`
|
`/api/teams/${encodeURIComponent(team.slug)}/pages/${encodeURIComponent(pageSlug)}`
|
||||||
@ -212,244 +193,13 @@ Glow.openPage = async pageSlug => {
|
|||||||
Glow.state.activePage = data.page;
|
Glow.state.activePage = data.page;
|
||||||
Glow.renderPageDetail();
|
Glow.renderPageDetail();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Glow.showToast(error.message || "Could not load page.", "error");
|
Glow.showToast(error.message || "could not load page.", "error");
|
||||||
Glow.openPages();
|
Glow.openPages();
|
||||||
} finally {
|
} finally {
|
||||||
Glow.els.pageDetailBox.setAttribute("aria-busy", "false");
|
Glow.els.pageDetailBox.setAttribute("aria-busy", "false");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Glow.renderPageComments = page => {
|
|
||||||
const section = document.createElement("section");
|
|
||||||
section.className = "profile-section";
|
|
||||||
const heading = document.createElement("h3");
|
|
||||||
heading.textContent = "Comments";
|
|
||||||
section.append(heading);
|
|
||||||
|
|
||||||
const list = document.createElement("div");
|
|
||||||
list.className = "page-comments-list";
|
|
||||||
|
|
||||||
const comments = Array.isArray(page.comments) ? page.comments : [];
|
|
||||||
if (!comments.length) {
|
|
||||||
const empty = document.createElement("div");
|
|
||||||
empty.className = "profile-empty";
|
|
||||||
empty.textContent = "No comments yet.";
|
|
||||||
list.append(empty);
|
|
||||||
} else {
|
|
||||||
for (const comment of comments) {
|
|
||||||
const item = document.createElement("article");
|
|
||||||
item.className = "page-comment";
|
|
||||||
|
|
||||||
const top = document.createElement("div");
|
|
||||||
top.className = "page-comment-meta";
|
|
||||||
const author = document.createElement("strong");
|
|
||||||
author.textContent = comment.username;
|
|
||||||
author.style.color = comment.user_name_color || "";
|
|
||||||
const time = document.createElement("time");
|
|
||||||
time.dateTime = comment.created_at;
|
|
||||||
time.textContent = new Date(comment.created_at).toLocaleString();
|
|
||||||
top.append(author, time);
|
|
||||||
|
|
||||||
const body = document.createElement("div");
|
|
||||||
body.className = "page-comment-body";
|
|
||||||
body.textContent = comment.body;
|
|
||||||
|
|
||||||
item.append(top, body);
|
|
||||||
|
|
||||||
const canDelete =
|
|
||||||
Glow.state.currentUser &&
|
|
||||||
(Number(Glow.state.currentUser.id) === Number(comment.user_id) ||
|
|
||||||
Glow.state.currentUser.role === "admin" ||
|
|
||||||
page.can_edit);
|
|
||||||
if (canDelete) {
|
|
||||||
const remove = document.createElement("button");
|
|
||||||
remove.className = "small-button danger";
|
|
||||||
remove.type = "button";
|
|
||||||
remove.textContent = "Delete";
|
|
||||||
remove.addEventListener("click", async () => {
|
|
||||||
try {
|
|
||||||
await Glow.apiDeletePageComment(comment.id);
|
|
||||||
await Glow.openPage(page.slug);
|
|
||||||
Glow.showToast("Comment deleted.");
|
|
||||||
} catch (error) {
|
|
||||||
Glow.showToast(error.message || "Could not delete comment.", "error");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
item.append(remove);
|
|
||||||
}
|
|
||||||
|
|
||||||
list.append(item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
section.append(list);
|
|
||||||
|
|
||||||
if (Glow.state.currentUser) {
|
|
||||||
const form = document.createElement("form");
|
|
||||||
form.className = "page-comment-form";
|
|
||||||
|
|
||||||
const area = document.createElement("textarea");
|
|
||||||
area.className = "field";
|
|
||||||
area.rows = 4;
|
|
||||||
area.maxLength = 3000;
|
|
||||||
area.placeholder = "Add a comment";
|
|
||||||
|
|
||||||
const submit = document.createElement("button");
|
|
||||||
submit.className = "button";
|
|
||||||
submit.type = "submit";
|
|
||||||
submit.textContent = "Post comment";
|
|
||||||
|
|
||||||
form.append(area, submit);
|
|
||||||
form.addEventListener("submit", async event => {
|
|
||||||
event.preventDefault();
|
|
||||||
const body = area.value.trim();
|
|
||||||
if (!body) return;
|
|
||||||
try {
|
|
||||||
await Glow.apiCreatePageComment(page.team_slug, page.slug, { body });
|
|
||||||
await Glow.openPage(page.slug);
|
|
||||||
Glow.showToast("Comment added.");
|
|
||||||
} catch (error) {
|
|
||||||
Glow.showToast(error.message || "Could not add comment.", "error");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
section.append(form);
|
|
||||||
}
|
|
||||||
|
|
||||||
return section;
|
|
||||||
};
|
|
||||||
|
|
||||||
Glow.renderPageBacklinks = page => {
|
|
||||||
const section = document.createElement("section");
|
|
||||||
section.className = "profile-section";
|
|
||||||
const heading = document.createElement("h3");
|
|
||||||
heading.textContent = "Backlinks";
|
|
||||||
section.append(heading);
|
|
||||||
|
|
||||||
const backlinks = Array.isArray(page.backlinks) ? page.backlinks : [];
|
|
||||||
if (!backlinks.length) {
|
|
||||||
const empty = document.createElement("div");
|
|
||||||
empty.className = "profile-empty";
|
|
||||||
empty.textContent = "No backlinks yet.";
|
|
||||||
section.append(empty);
|
|
||||||
return section;
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = document.createElement("div");
|
|
||||||
list.className = "page-related-list";
|
|
||||||
for (const link of backlinks) {
|
|
||||||
const button = document.createElement("button");
|
|
||||||
button.className = "small-button";
|
|
||||||
button.type = "button";
|
|
||||||
button.textContent = link.title;
|
|
||||||
button.addEventListener("click", () => Glow.openPage(link.slug));
|
|
||||||
list.append(button);
|
|
||||||
}
|
|
||||||
section.append(list);
|
|
||||||
return section;
|
|
||||||
};
|
|
||||||
|
|
||||||
Glow.renderPageChildren = page => {
|
|
||||||
const section = document.createElement("section");
|
|
||||||
section.className = "profile-section";
|
|
||||||
const heading = document.createElement("h3");
|
|
||||||
heading.textContent = "Child pages";
|
|
||||||
section.append(heading);
|
|
||||||
|
|
||||||
const children = Array.isArray(page.children) ? page.children : [];
|
|
||||||
if (!children.length) {
|
|
||||||
const empty = document.createElement("div");
|
|
||||||
empty.className = "profile-empty";
|
|
||||||
empty.textContent = "No child pages.";
|
|
||||||
section.append(empty);
|
|
||||||
return section;
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = document.createElement("div");
|
|
||||||
list.className = "page-related-list";
|
|
||||||
for (const child of children) {
|
|
||||||
const button = document.createElement("button");
|
|
||||||
button.className = "small-button";
|
|
||||||
button.type = "button";
|
|
||||||
button.textContent = child.title;
|
|
||||||
button.addEventListener("click", () => Glow.openPage(child.slug));
|
|
||||||
list.append(button);
|
|
||||||
}
|
|
||||||
section.append(list);
|
|
||||||
return section;
|
|
||||||
};
|
|
||||||
|
|
||||||
Glow.openPageVersionHistory = async page => {
|
|
||||||
try {
|
|
||||||
const data = await Glow.apiListPageVersions(page.team_slug, page.slug);
|
|
||||||
const container = document.createElement("div");
|
|
||||||
container.className = "member-list";
|
|
||||||
|
|
||||||
const versions = data.versions || [];
|
|
||||||
if (!versions.length) {
|
|
||||||
const empty = document.createElement("div");
|
|
||||||
empty.className = "member-row";
|
|
||||||
empty.textContent = "No version history yet.";
|
|
||||||
container.append(empty);
|
|
||||||
} else {
|
|
||||||
for (const version of versions) {
|
|
||||||
const row = document.createElement("div");
|
|
||||||
row.className = "member-row";
|
|
||||||
const left = document.createElement("button");
|
|
||||||
left.className = "small-button";
|
|
||||||
left.type = "button";
|
|
||||||
left.textContent = `v${version.version_number} · ${version.title}`;
|
|
||||||
left.addEventListener("click", async () => {
|
|
||||||
try {
|
|
||||||
const detail = await Glow.apiGetPageVersion(version.id);
|
|
||||||
const body = document.createElement("div");
|
|
||||||
body.className = "page-body markdown-body";
|
|
||||||
body.innerHTML = detail.version.body_html || "<p>No content.</p>";
|
|
||||||
const result = await Glow.openDialog({
|
|
||||||
title: `Version ${detail.version.version_number}`,
|
|
||||||
body,
|
|
||||||
confirmText: page.can_edit ? "Restore version" : "Close",
|
|
||||||
cancelText: "Close",
|
|
||||||
danger: page.can_edit
|
|
||||||
});
|
|
||||||
if (result && page.can_edit) {
|
|
||||||
await Glow.apiRestorePageVersion(version.id);
|
|
||||||
Glow.showToast("Version restored.");
|
|
||||||
await Glow.openPage(page.slug);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
Glow.showToast(error.message || "Could not load version.", "error");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const right = document.createElement("span");
|
|
||||||
right.textContent = new Date(version.created_at).toLocaleString();
|
|
||||||
row.append(left, right);
|
|
||||||
container.append(row);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await Glow.openDialog({
|
|
||||||
title: "Version history",
|
|
||||||
body: container,
|
|
||||||
confirmText: "Close",
|
|
||||||
cancelText: "Close"
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
Glow.showToast(error.message || "Could not load version history.", "error");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Glow.shareCurrentPage = async page => {
|
|
||||||
try {
|
|
||||||
const data = await Glow.apiCreatePageShare(page.team_slug, page.slug);
|
|
||||||
const url = new URL(data.share.url, window.location.origin).toString();
|
|
||||||
await navigator.clipboard.writeText(url);
|
|
||||||
Glow.showToast("Page share link copied.");
|
|
||||||
} catch (error) {
|
|
||||||
Glow.showToast(error.message || "Could not create page share.", "error");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Glow.renderPageDetail = () => {
|
Glow.renderPageDetail = () => {
|
||||||
const box = Glow.els.pageDetailBox;
|
const box = Glow.els.pageDetailBox;
|
||||||
const page = Glow.state.activePage;
|
const page = Glow.state.activePage;
|
||||||
@ -460,7 +210,7 @@ Glow.renderPageDetail = () => {
|
|||||||
state.className = "empty-state";
|
state.className = "empty-state";
|
||||||
const card = document.createElement("div");
|
const card = document.createElement("div");
|
||||||
card.className = "empty-card";
|
card.className = "empty-card";
|
||||||
card.textContent = "Page not found.";
|
card.textContent = "page not found.";
|
||||||
state.append(card);
|
state.append(card);
|
||||||
box.append(state);
|
box.append(state);
|
||||||
return;
|
return;
|
||||||
@ -484,7 +234,7 @@ Glow.renderPageDetail = () => {
|
|||||||
updated.className = "page-updated";
|
updated.className = "page-updated";
|
||||||
updated.dateTime = page.updated_at;
|
updated.dateTime = page.updated_at;
|
||||||
updated.title = new Date(page.updated_at).toLocaleString();
|
updated.title = new Date(page.updated_at).toLocaleString();
|
||||||
updated.textContent = `Updated ${Glow.relativeTime(page.updated_at)}`;
|
updated.textContent = `updated ${Glow.relativeTime(page.updated_at)}`;
|
||||||
|
|
||||||
meta.append(type, visibility, updated);
|
meta.append(type, visibility, updated);
|
||||||
|
|
||||||
@ -493,10 +243,8 @@ Glow.renderPageDetail = () => {
|
|||||||
|
|
||||||
const context = document.createElement("p");
|
const context = document.createElement("p");
|
||||||
context.className = "page-detail-context";
|
context.className = "page-detail-context";
|
||||||
context.textContent = page.parent_page_title
|
context.textContent = page.channel_name
|
||||||
? `${page.team_name} · Child of ${page.parent_page_title}`
|
? `${page.team_name} · #${page.channel_name}`
|
||||||
: page.channel_name
|
|
||||||
? `${page.team_name} · #${page.channel_name}`
|
|
||||||
: page.team_name;
|
: page.team_name;
|
||||||
|
|
||||||
header.append(meta, title, context);
|
header.append(meta, title, context);
|
||||||
@ -504,46 +252,23 @@ Glow.renderPageDetail = () => {
|
|||||||
|
|
||||||
const body = document.createElement("article");
|
const body = document.createElement("article");
|
||||||
body.className = "page-body markdown-body";
|
body.className = "page-body markdown-body";
|
||||||
body.innerHTML = page.body_html || "<p>No content yet.</p>";
|
body.innerHTML = page.body_html || "<p>no content yet.</p>";
|
||||||
box.append(body);
|
box.append(body);
|
||||||
|
|
||||||
const related = document.createElement("div");
|
|
||||||
related.className = "profile-grid";
|
|
||||||
related.append(
|
|
||||||
Glow.renderPageChildren(page),
|
|
||||||
Glow.renderPageBacklinks(page),
|
|
||||||
Glow.renderPageComments(page)
|
|
||||||
);
|
|
||||||
box.append(related);
|
|
||||||
|
|
||||||
const actions = document.createElement("div");
|
const actions = document.createElement("div");
|
||||||
actions.className = "page-detail-actions";
|
actions.className = "page-detail-actions";
|
||||||
|
|
||||||
const history = document.createElement("button");
|
|
||||||
history.className = "button secondary";
|
|
||||||
history.type = "button";
|
|
||||||
history.textContent = "Version history";
|
|
||||||
history.addEventListener("click", () => Glow.openPageVersionHistory(page));
|
|
||||||
actions.append(history);
|
|
||||||
|
|
||||||
const share = document.createElement("button");
|
|
||||||
share.className = "button secondary";
|
|
||||||
share.type = "button";
|
|
||||||
share.textContent = "Share";
|
|
||||||
share.addEventListener("click", () => Glow.shareCurrentPage(page));
|
|
||||||
actions.append(share);
|
|
||||||
|
|
||||||
if (page.can_edit) {
|
if (page.can_edit) {
|
||||||
const edit = document.createElement("button");
|
const edit = document.createElement("button");
|
||||||
edit.className = "button";
|
edit.className = "button";
|
||||||
edit.type = "button";
|
edit.type = "button";
|
||||||
edit.textContent = "Edit";
|
edit.textContent = "edit";
|
||||||
edit.addEventListener("click", () => Glow.editPage(page));
|
edit.addEventListener("click", () => Glow.editPage(page));
|
||||||
|
|
||||||
const remove = document.createElement("button");
|
const remove = document.createElement("button");
|
||||||
remove.className = "danger-button";
|
remove.className = "danger-button";
|
||||||
remove.type = "button";
|
remove.type = "button";
|
||||||
remove.textContent = "Delete";
|
remove.textContent = "delete";
|
||||||
remove.addEventListener("click", () => Glow.deletePage(page));
|
remove.addEventListener("click", () => Glow.deletePage(page));
|
||||||
|
|
||||||
actions.append(edit, remove);
|
actions.append(edit, remove);
|
||||||
@ -555,40 +280,33 @@ Glow.renderPageDetail = () => {
|
|||||||
Glow.pageDialogFields = (page = null) => [
|
Glow.pageDialogFields = (page = null) => [
|
||||||
{
|
{
|
||||||
name: "title",
|
name: "title",
|
||||||
label: "Title",
|
label: "title",
|
||||||
value: page?.title || "",
|
value: page?.title || "",
|
||||||
placeholder: "Page title",
|
placeholder: "page title",
|
||||||
maxLength: 160
|
maxLength: 160
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "page_type",
|
name: "page_type",
|
||||||
label: "Type: project, document, update, or resource",
|
label: "type: project, document, update, or resource",
|
||||||
value: page?.page_type || "document",
|
value: page?.page_type || "document",
|
||||||
maxLength: 12
|
maxLength: 12
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "visibility",
|
name: "visibility",
|
||||||
label: "Visibility: team or public",
|
label: "visibility: team or public",
|
||||||
value: page?.visibility || "team",
|
value: page?.visibility || "team",
|
||||||
maxLength: 6
|
maxLength: 6
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "channel_slug",
|
name: "channel_slug",
|
||||||
label: "Channel slug (optional)",
|
label: "channel slug (optional)",
|
||||||
value: page?.channel_slug || "",
|
value: page?.channel_slug || "",
|
||||||
placeholder: "general",
|
placeholder: "general",
|
||||||
maxLength: 48
|
maxLength: 48
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "parent_page_slug",
|
|
||||||
label: "Parent page slug (optional)",
|
|
||||||
value: page?.parent_page_slug || "",
|
|
||||||
placeholder: "project-overview",
|
|
||||||
maxLength: 48
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "body",
|
name: "body",
|
||||||
label: "Markdown content",
|
label: "markdown content",
|
||||||
value: page?.body || "",
|
value: page?.body || "",
|
||||||
multiline: true,
|
multiline: true,
|
||||||
rows: 14,
|
rows: 14,
|
||||||
@ -598,41 +316,46 @@ Glow.pageDialogFields = (page = null) => [
|
|||||||
|
|
||||||
Glow.validatePageValues = values => {
|
Glow.validatePageValues = values => {
|
||||||
if (!values.title) {
|
if (!values.title) {
|
||||||
Glow.showToast("Page title is required.", "error");
|
Glow.showToast("page title is required.", "error");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageType = values.page_type.toLowerCase();
|
const pageType = values.page_type.toLowerCase();
|
||||||
if (!["project", "document", "update", "resource"].includes(pageType)) {
|
if (!["project", "document", "update", "resource"].includes(pageType)) {
|
||||||
Glow.showToast("Choose a valid page type.", "error");
|
Glow.showToast("choose a valid page type.", "error");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const visibility = values.visibility.toLowerCase();
|
const visibility = values.visibility.toLowerCase();
|
||||||
if (!["team", "public"].includes(visibility)) {
|
if (!["team", "public"].includes(visibility)) {
|
||||||
Glow.showToast("Visibility must be team or public.", "error");
|
Glow.showToast("visibility must be team or public.", "error");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: values.title,
|
title: values.title,
|
||||||
body: values.body,
|
body: values.body,
|
||||||
page_type: pageType,
|
page_type: pageType,
|
||||||
visibility,
|
visibility,
|
||||||
channel_slug: values.channel_slug.toLowerCase(),
|
channel_slug: values.channel_slug.toLowerCase()
|
||||||
parent_page_slug: values.parent_page_slug.toLowerCase()
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
Glow.createPage = async () => {
|
Glow.createPage = async () => {
|
||||||
const team = Glow.state.activeTeam;
|
const team = Glow.state.activeTeam;
|
||||||
if (!team?.can_manage) return;
|
if (!team?.can_manage) return;
|
||||||
|
|
||||||
const values = await Glow.openDialog({
|
const values = await Glow.openDialog({
|
||||||
title: "Create page",
|
title: "create page",
|
||||||
body: "Create a shared team page or a public resource. Markdown is supported.",
|
body: "create a shared team page or a public resource. markdown is supported.",
|
||||||
confirmText: "Create page",
|
confirmText: "create page",
|
||||||
fields: Glow.pageDialogFields()
|
fields: Glow.pageDialogFields()
|
||||||
});
|
});
|
||||||
if (!values) return;
|
if (!values) return;
|
||||||
|
|
||||||
const payload = Glow.validatePageValues(values);
|
const payload = Glow.validatePageValues(values);
|
||||||
if (!payload) return;
|
if (!payload) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await Glow.apiRequest(
|
const data = await Glow.apiRequest(
|
||||||
`/api/teams/${encodeURIComponent(team.slug)}/pages`,
|
`/api/teams/${encodeURIComponent(team.slug)}/pages`,
|
||||||
@ -642,23 +365,26 @@ Glow.createPage = async () => {
|
|||||||
body: JSON.stringify(payload)
|
body: JSON.stringify(payload)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
Glow.showToast("Page created.");
|
Glow.showToast("page created.");
|
||||||
await Glow.openPage(data.page.slug);
|
await Glow.openPage(data.page.slug);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Glow.showToast(error.message || "Could not create page.", "error");
|
Glow.showToast(error.message || "could not create page.", "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Glow.editPage = async page => {
|
Glow.editPage = async page => {
|
||||||
if (!page.can_edit) return;
|
if (!page.can_edit) return;
|
||||||
|
|
||||||
const values = await Glow.openDialog({
|
const values = await Glow.openDialog({
|
||||||
title: `Edit ${page.title}`,
|
title: `edit ${page.title}`,
|
||||||
confirmText: "Save",
|
confirmText: "save",
|
||||||
fields: Glow.pageDialogFields(page)
|
fields: Glow.pageDialogFields(page)
|
||||||
});
|
});
|
||||||
if (!values) return;
|
if (!values) return;
|
||||||
|
|
||||||
const payload = Glow.validatePageValues(values);
|
const payload = Glow.validatePageValues(values);
|
||||||
if (!payload) return;
|
if (!payload) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data = await Glow.apiRequest(
|
const data = await Glow.apiRequest(
|
||||||
`/api/teams/${encodeURIComponent(page.team_slug)}/pages/${encodeURIComponent(page.slug)}`,
|
`/api/teams/${encodeURIComponent(page.team_slug)}/pages/${encodeURIComponent(page.slug)}`,
|
||||||
@ -670,29 +396,31 @@ Glow.editPage = async page => {
|
|||||||
);
|
);
|
||||||
Glow.state.activePage = data.page;
|
Glow.state.activePage = data.page;
|
||||||
Glow.renderPageDetail();
|
Glow.renderPageDetail();
|
||||||
Glow.showToast("Page updated.");
|
Glow.showToast("page updated.");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Glow.showToast(error.message || "Could not update page.", "error");
|
Glow.showToast(error.message || "could not update page.", "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Glow.deletePage = async page => {
|
Glow.deletePage = async page => {
|
||||||
if (!page.can_edit) return;
|
if (!page.can_edit) return;
|
||||||
|
|
||||||
const result = await Glow.openDialog({
|
const result = await Glow.openDialog({
|
||||||
title: `Delete ${page.title}?`,
|
title: `delete ${page.title}?`,
|
||||||
body: "This page will no longer be available.",
|
body: "this page will no longer be available.",
|
||||||
confirmText: "Delete page",
|
confirmText: "delete page",
|
||||||
danger: true
|
danger: true
|
||||||
});
|
});
|
||||||
if (!result) return;
|
if (!result) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Glow.apiRequest(
|
await Glow.apiRequest(
|
||||||
`/api/teams/${encodeURIComponent(page.team_slug)}/pages/${encodeURIComponent(page.slug)}`,
|
`/api/teams/${encodeURIComponent(page.team_slug)}/pages/${encodeURIComponent(page.slug)}`,
|
||||||
{ method: "DELETE" }
|
{ method: "DELETE" }
|
||||||
);
|
);
|
||||||
Glow.showToast("Page deleted.");
|
Glow.showToast("page deleted.");
|
||||||
await Glow.openPages();
|
await Glow.openPages();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Glow.showToast(error.message || "Could not delete page.", "error");
|
Glow.showToast(error.message || "could not delete page.", "error");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Loading…
x
Reference in New Issue
Block a user