Skip to content

Commit 2e6c22d

Browse files
committed
Repair DEV identity roles and add platform banner settings - PR_26167_187-live-db-cleanup-and-platform-banner
1 parent 09c7c14 commit 2e6c22d

29 files changed

Lines changed: 3405 additions & 4718 deletions

admin/platform-settings.html

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,19 @@
66
<base href="/">
77
<meta name="viewport" content="width=device-width, initial-scale=1">
88
<title>Platform Settings - GameFoundryStudio</title>
9-
<meta name="description" content="Admin wireframe for platform settings, feature flags, and publishing defaults.">
9+
<meta name="description" content="Admin controls for platform settings and operational notice banners.">
1010
<link rel="icon" href="/favicon.svg">
1111
<link rel="stylesheet" href="assets/theme-v2/css/theme.css">
1212
</head>
1313

1414
<body class="meaning-molten-orange">
1515
<div data-partial="header-nav"></div>
16-
<main data-admin-only="true" data-admin-wireframe="platform-settings">
16+
<main data-admin-only="true" data-admin-platform-settings data-admin-wireframe="platform-settings">
1717
<section class="page-title">
1818
<div class="container">
19-
<div class="kicker">Admin Wireframe</div>
19+
<div class="kicker">Admin</div>
2020
<h1>Platform Settings</h1>
21-
<p class="lede">Plan platform-wide settings, flags, and release controls without connecting settings persistence.</p>
21+
<p class="lede">Manage platform-wide settings, flags, and operational notice banners.</p>
2222
</div>
2323
</section>
2424
<section class="section">
@@ -40,11 +40,26 @@ <h2>Admin</h2>
4040
</nav>
4141
</details>
4242
<details class="vertical-accordion" open>
43-
<summary>Setting Preview</summary>
43+
<summary>Banner Controls</summary>
4444
<div class="accordion-body content-stack">
45-
<label>Setting Area <select disabled><option>Feature flags</option><option>Publishing defaults</option><option>Maintenance notices</option></select></label>
46-
<label>Visibility <select disabled><option>Admin only</option><option>Creator visible</option><option>Public notice</option></select></label>
47-
<button class="btn btn--compact" type="button" disabled>Preview Setting</button>
45+
<label class="field-inline">
46+
<input type="checkbox" data-platform-banner-enabled>
47+
<span>Show banner</span>
48+
</label>
49+
<label class="field-group">Notice kind
50+
<select data-platform-banner-kind>
51+
<option value="general">General notice</option>
52+
<option value="temporary-data">Temporary data notice</option>
53+
<option value="outage">Outage notice</option>
54+
</select>
55+
</label>
56+
<label class="field-group">Tone
57+
<select data-platform-banner-tone>
58+
<option value="info">Info</option>
59+
<option value="warning">Warning</option>
60+
<option value="danger">Urgent</option>
61+
</select>
62+
</label>
4863
</div>
4964
</details>
5065
</div>
@@ -53,25 +68,15 @@ <h2>Admin</h2>
5368
<section class="tool-center-panel content-stack" aria-labelledby="admin-platform-settings-title">
5469
<div>
5570
<div class="kicker">Work Surface</div>
56-
<h2 id="admin-platform-settings-title">Settings Board</h2>
71+
<h2 id="admin-platform-settings-title">Platform Banner</h2>
5772
</div>
58-
<p>This wireframe reserves the future board for platform defaults, release toggles, and operational notices.</p>
59-
<div class="table-wrapper">
60-
<table class="data-table" aria-label="Platform settings wireframe">
61-
<caption>Platform Settings Planning</caption>
62-
<thead>
63-
<tr>
64-
<th scope="col">Area</th>
65-
<th scope="col">Example Control</th>
66-
<th scope="col">Current State</th>
67-
</tr>
68-
</thead>
69-
<tbody>
70-
<tr><td>Feature Flags</td><td>Enable beta tools</td><td>Wireframe</td></tr>
71-
<tr><td>Publishing Defaults</td><td>Default public visibility</td><td>Wireframe</td></tr>
72-
<tr><td>Maintenance</td><td>Notice banner</td><td>Wireframe</td></tr>
73-
</tbody>
74-
</table>
73+
<p>Publish a platform banner from the shared settings contract.</p>
74+
<label class="field-group">Banner message
75+
<textarea data-platform-banner-message maxlength="240" rows="5"></textarea>
76+
</label>
77+
<div class="callout" data-platform-banner-preview role="status">No active banner.</div>
78+
<div class="content-cluster">
79+
<button class="btn primary" type="button" data-platform-banner-save>Save Banner</button>
7580
</div>
7681
</section>
7782
<aside class="tool-column tool-group-platform">
@@ -82,13 +87,13 @@ <h2>Inspector</h2>
8287
<details class="vertical-accordion" open>
8388
<summary>Status</summary>
8489
<div class="accordion-body content-stack">
85-
<p class="status" role="status">Wireframe only. Platform settings are not read or written.</p>
90+
<p class="status" role="status" data-platform-settings-status>Loading platform banner settings...</p>
8691
</div>
8792
</details>
8893
<details class="vertical-accordion" open>
8994
<summary>Logging</summary>
9095
<div class="accordion-body content-stack">
91-
<p>Future logs will show setting previews, publish gate changes, and audit review entries.</p>
96+
<p>Banner updates are saved through the server settings contract.</p>
9297
</div>
9398
</details>
9499
</div>
@@ -100,6 +105,7 @@ <h2>Inspector</h2>
100105
<div data-partial="footer"></div>
101106
<script src="assets/theme-v2/js/gamefoundry-partials.js" defer></script>
102107
<script src="assets/theme-v2/js/tool-display-mode.js" defer></script>
108+
<script type="module" src="assets/theme-v2/js/admin-platform-settings.js"></script>
103109
</body>
104110

105111
</html>

assets/theme-v2/css/status.css

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,35 @@
3535
line-height: var(--line-height-copy)
3636
}
3737

38+
.platform-banner {
39+
width: 100%;
40+
border-bottom: var(--border-standard);
41+
background: var(--panel-overlay-strong);
42+
color: var(--text)
43+
}
44+
45+
.platform-banner__inner {
46+
width: var(--container-width);
47+
margin: var(--space-0) auto;
48+
padding: var(--space-12) var(--space-0);
49+
text-align: center
50+
}
51+
52+
.platform-banner__message {
53+
margin: var(--space-0);
54+
overflow-wrap: anywhere
55+
}
56+
57+
.platform-banner--warning {
58+
background: var(--gold-soft);
59+
border-bottom-color: var(--gold-border-muted)
60+
}
61+
62+
.platform-banner--danger {
63+
background: color-mix(in srgb, var(--red) 14%, var(--panel));
64+
border-bottom-color: var(--red)
65+
}
66+
3867
.swatch-label {
3968
border: var(--border-standard);
4069
border-color: var(--swatch-border);
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import {
2+
readAdminPlatformBanner,
3+
updateAdminPlatformBanner
4+
} from "../../../src/engine/api/platform-settings-api-client.js";
5+
6+
class AdminPlatformSettingsController {
7+
constructor(root) {
8+
this.root = root;
9+
this.enabledInput = root.querySelector("[data-platform-banner-enabled]");
10+
this.kindInput = root.querySelector("[data-platform-banner-kind]");
11+
this.messageInput = root.querySelector("[data-platform-banner-message]");
12+
this.preview = root.querySelector("[data-platform-banner-preview]");
13+
this.status = root.querySelector("[data-platform-settings-status]");
14+
this.toneInput = root.querySelector("[data-platform-banner-tone]");
15+
this.saveButton = root.querySelector("[data-platform-banner-save]");
16+
}
17+
18+
init() {
19+
if (!this.enabledInput || !this.kindInput || !this.messageInput || !this.preview || !this.status || !this.toneInput || !this.saveButton) {
20+
return;
21+
}
22+
this.saveButton.addEventListener("click", () => this.save());
23+
[this.enabledInput, this.kindInput, this.messageInput, this.toneInput].forEach((control) => {
24+
control.addEventListener("input", () => this.renderPreview(this.currentBanner()));
25+
control.addEventListener("change", () => this.renderPreview(this.currentBanner()));
26+
});
27+
this.load();
28+
}
29+
30+
setStatus(message) {
31+
this.status.textContent = message;
32+
}
33+
34+
currentBanner() {
35+
return {
36+
enabled: this.enabledInput.checked,
37+
kind: this.kindInput.value,
38+
message: this.messageInput.value.trim(),
39+
tone: this.toneInput.value
40+
};
41+
}
42+
43+
applyBanner(banner) {
44+
this.enabledInput.checked = banner.enabled === true;
45+
this.kindInput.value = banner.kind || "general";
46+
this.messageInput.value = banner.message || "";
47+
this.toneInput.value = banner.tone || "info";
48+
this.renderPreview(this.currentBanner());
49+
}
50+
51+
renderPreview(banner) {
52+
this.preview.dataset.platformBannerPreviewTone = banner.tone || "info";
53+
this.preview.textContent = banner.enabled && banner.message
54+
? banner.message
55+
: "No active banner.";
56+
}
57+
58+
load() {
59+
try {
60+
const payload = readAdminPlatformBanner();
61+
this.applyBanner(payload.banner || {});
62+
this.setStatus("Platform banner settings loaded.");
63+
} catch (error) {
64+
this.setStatus(error instanceof Error ? error.message : "Platform banner settings are unavailable.");
65+
}
66+
}
67+
68+
save() {
69+
try {
70+
const payload = updateAdminPlatformBanner(this.currentBanner());
71+
this.applyBanner(payload.banner || {});
72+
this.setStatus("Platform banner settings saved.");
73+
window.dispatchEvent(new CustomEvent("gamefoundry:platform-settings-changed"));
74+
} catch (error) {
75+
this.setStatus(error instanceof Error ? error.message : "Platform banner settings could not be saved.");
76+
}
77+
}
78+
}
79+
80+
document.addEventListener("DOMContentLoaded", () => {
81+
const root = document.querySelector("[data-admin-platform-settings]");
82+
if (!root) {
83+
return;
84+
}
85+
new AdminPlatformSettingsController(root).init();
86+
});

assets/theme-v2/js/gamefoundry-partials.js

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,74 @@
382382
}
383383
}
384384

385+
function normalizedPlatformBanner(data) {
386+
const banner = data?.banner && typeof data.banner === "object" ? data.banner : {};
387+
const tone = ["info", "warning", "danger"].includes(banner.tone) ? banner.tone : "info";
388+
return {
389+
active: banner.active === true,
390+
message: typeof banner.message === "string" ? banner.message.trim() : "",
391+
tone
392+
};
393+
}
394+
395+
async function requestPlatformBanner() {
396+
const response = await fetch("/api/platform-settings/banner", {
397+
headers: { "Accept": "application/json" },
398+
method: "GET"
399+
});
400+
const payload = await response.json().catch(function () {
401+
return null;
402+
});
403+
if (!response.ok || payload?.ok === false) {
404+
throw new Error(payload?.error || "Platform banner settings are unavailable.");
405+
}
406+
return normalizedPlatformBanner(payload?.data);
407+
}
408+
409+
function removePlatformBanner() {
410+
document.querySelectorAll("[data-platform-banner]").forEach(function (banner) {
411+
banner.remove();
412+
});
413+
}
414+
415+
function createPlatformBanner(banner) {
416+
const section = document.createElement("section");
417+
section.className = "platform-banner platform-banner--" + banner.tone;
418+
section.dataset.platformBanner = "";
419+
section.setAttribute("aria-label", "Platform notice");
420+
const inner = document.createElement("div");
421+
inner.className = "platform-banner__inner";
422+
const message = document.createElement("p");
423+
message.className = "platform-banner__message";
424+
message.textContent = banner.message;
425+
inner.append(message);
426+
section.append(inner);
427+
return section;
428+
}
429+
430+
async function renderPlatformBanner() {
431+
try {
432+
const banner = await requestPlatformBanner();
433+
removePlatformBanner();
434+
if (!banner.active || !banner.message) {
435+
return;
436+
}
437+
const element = createPlatformBanner(banner);
438+
const header = document.querySelector("header.site-header");
439+
if (header) {
440+
header.after(element);
441+
return;
442+
}
443+
const main = document.querySelector("main");
444+
if (main?.parentNode) {
445+
main.parentNode.insertBefore(element, main);
446+
}
447+
} catch (error) {
448+
removePlatformBanner();
449+
console.warn("[platform-settings/operator] Platform banner unavailable:", error instanceof Error ? error.message : String(error || ""));
450+
}
451+
}
452+
385453
function directSubMenu(navItem) {
386454
return Array.from(navItem?.children || []).find(function (child) {
387455
return child.classList?.contains("sub-menu");
@@ -694,10 +762,11 @@
694762
replaceExisting("header-nav", "header.site-header"),
695763
replaceExisting("footer", "footer.footer")
696764
];
697-
Promise.all(tasks).catch(function (error) {
765+
Promise.all(tasks).then(renderPlatformBanner).catch(function (error) {
698766
console.error(error);
699767
});
700768
});
701769
window.addEventListener("gamefoundry:session-user-changed", refreshHeaderLoginState);
702770
window.addEventListener("gamefoundry:data-changed", refreshHeaderOnly);
771+
window.addEventListener("gamefoundry:platform-settings-changed", renderPlatformBanner);
703772
}());

assets/theme-v2/partials/footer.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ <h2 id="footer-company">Company</h2>
6161
</nav>
6262
<div class="footer__brand">
6363
<span>&copy; 2026</span>
64-
<span>GameFoundryStudio</span>
64+
<span>GameFoundryStudio LLC</span>
6565
</div>
6666
<button class="return-to-top" type="button" data-return-to-top aria-label="Return to top" title="Return to top">
6767
<span aria-hidden="true">^</span>

docs_build/database/dml/admin.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@
1515
-- Direct SQL setup for this group remains deferred until a later migration-runner PR explicitly owns it.
1616
-- Starter server seed rows:
1717
-- platform_settings.settingKey = site.setup.status
18+
-- platform_settings.settingKey = platform.banner.enabled
19+
-- platform_settings.settingKey = platform.banner.message
20+
-- platform_settings.settingKey = platform.banner.tone
21+
-- platform_settings.settingKey = platform.banner.kind

docs_build/database/seed/admin.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,26 @@
1212
"settingKey": "site.setup.status",
1313
"settingType": "string",
1414
"settingValue": "ready"
15+
},
16+
{
17+
"settingKey": "platform.banner.enabled",
18+
"settingType": "boolean",
19+
"settingValue": "false"
20+
},
21+
{
22+
"settingKey": "platform.banner.message",
23+
"settingType": "string",
24+
"settingValue": ""
25+
},
26+
{
27+
"settingKey": "platform.banner.tone",
28+
"settingType": "string",
29+
"settingValue": "info"
30+
},
31+
{
32+
"settingKey": "platform.banner.kind",
33+
"settingType": "string",
34+
"settingValue": "general"
1535
}
1636
]
1737
}

docs_build/dev/admin-notes/index.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22
[.] Start the server
33
npm run dev:local-api
44

5+
- Update DEV Supabase identity seed so only these test identities remain:
6+
- User 1, email user1@example.invalid, password GFS1
7+
- User 2, email user2@example.invalid, password GFS2
8+
- User 3, email user3@example.invalid, password GFS3
9+
- DavidQ, email qbytes.dq@gmail.com, password GFS$2026
10+
511
[x] move
612
src\assets\fonts\0xProtoNerdFont\ to assets\theme-v2\fonts\
713
src\assets\fonts\vector_battle\ to assets\theme-v2\fonts\

0 commit comments

Comments
 (0)