diff --git a/installer/renderer.js b/installer/renderer.js index 36bbf08..40aac2f 100644 --- a/installer/renderer.js +++ b/installer/renderer.js @@ -44,7 +44,7 @@ function renderPackList(packs) { const label = document.createElement('label') label.className = 'packOption' label.innerHTML = ` - +
${pack.name} ${pack.file} @@ -52,15 +52,6 @@ function renderPackList(packs) { ` packList.appendChild(label) }) - - packList.addEventListener('change', () => { - const checked = packList.querySelector('input[name="packChoice"]:checked') - if (checked == null) { - state.selectedPack = null - return - } - state.selectedPack = packs.find((pack) => pack.file === checked.value) ?? null - }) } async function bootstrap() { @@ -71,6 +62,18 @@ async function bootstrap() { window.installerApi.onLog(appendLog) +packList.addEventListener('change', () => { + const checked = packList.querySelector('input[name="packChoice"]:checked') + if (checked == null) { + state.selectedPack = null + return + } + state.selectedPack = { + file: checked.value, + name: checked.dataset.packName ?? checked.value + } +}) + document.querySelectorAll('[data-back]').forEach((button) => { button.addEventListener('click', () => { setActiveStep(button.dataset.back) diff --git a/public/styles.css b/public/styles.css index 150bfe9..2145a14 100644 --- a/public/styles.css +++ b/public/styles.css @@ -191,21 +191,60 @@ button { gap: 18px; } +.deleteToolbar { + display: flex; + align-items: center; + gap: 12px; +} + +.deleteActions { + display: flex; + align-items: center; + gap: 12px; +} + .selectableCard { position: relative; } -.selectableCard input[type="checkbox"] { +.selectionBox { position: absolute; top: 18px; right: 18px; } +.ghostButton { + min-height: 46px; + padding: 0 20px; + border-radius: 999px; + cursor: pointer; + border: 1px solid var(--line); + background: transparent; + color: var(--text); +} + .selectionTitle { font-size: 24px; font-weight: 700; } +.warningBadge { + display: inline-flex; + align-items: center; + width: fit-content; + min-height: 30px; + padding: 0 12px; + border-radius: 999px; + background: rgba(240, 191, 87, 0.16); + color: var(--accent); + font-size: 13px; + font-weight: 700; +} + +.hidden { + display: none !important; +} + .adminLoginBody { display: flex; align-items: center; diff --git a/src/server/routes/op.ts b/src/server/routes/op.ts index 5c573ce..4a2f9a8 100644 --- a/src/server/routes/op.ts +++ b/src/server/routes/op.ts @@ -3,6 +3,7 @@ import { fetchReleaseVersions } from '../../shared/mojang' import { createNewPack, deletePacks, + listDashboardPacks, loadAccounts, loadPackDefinition, loadRootManifest, @@ -59,10 +60,10 @@ opRouter.post('/op/logout', requireAuth, (req, res) => { opRouter.get('/op/dashboard', requireAuth, async (_req, res, next) => { try { - const manifest = await loadRootManifest() + const packs = await listDashboardPacks() res.render('op/dashboard', { userId: _req.session.userId, - packs: manifest.packs + packs }) } catch (error) { next(error) diff --git a/src/shared/store.ts b/src/shared/store.ts index 354474c..1e851ea 100644 --- a/src/shared/store.ts +++ b/src/shared/store.ts @@ -2,7 +2,7 @@ import fs from 'node:fs' import fsp from 'node:fs/promises' import path from 'node:path' import { accountPath, fileDir, manifestDir, manifestRootPath } from './paths' -import { AccountEntry, PackDefinition, PackListEntry, RootManifest } from './types' +import { AccountEntry, DashboardPackEntry, PackDefinition, PackListEntry, RootManifest } from './types' const defaultRootManifest: RootManifest = { packs: [ @@ -93,6 +93,22 @@ export async function listManifestFiles(): Promise { .sort((left, right) => left.localeCompare(right)) } +export async function listDashboardPacks(): Promise { + const [manifestFiles, rootManifest] = await Promise.all([ + listManifestFiles(), + loadRootManifest() + ]) + + return manifestFiles.map((file) => { + const registeredPack = rootManifest.packs.find((entry) => entry.file === file) + return { + file, + name: registeredPack?.name ?? file, + registered: registeredPack != null + } + }) +} + export async function loadPackDefinition(packKey: string): Promise { await ensureProjectFiles() const safeKey = sanitizePackKey(packKey) diff --git a/src/shared/types.ts b/src/shared/types.ts index 571189c..e982726 100644 --- a/src/shared/types.ts +++ b/src/shared/types.ts @@ -3,6 +3,10 @@ export interface PackListEntry { file: string } +export interface DashboardPackEntry extends PackListEntry { + registered: boolean +} + export interface RootManifest { packs: PackListEntry[] } diff --git a/views/op/dashboard.ejs b/views/op/dashboard.ejs index 4d9eb96..055d64b 100644 --- a/views/op/dashboard.ejs +++ b/views/op/dashboard.ejs @@ -14,14 +14,23 @@
-
- + +
+ + +
<% packs.forEach((pack) => { %> <% }) %> @@ -29,5 +38,33 @@ +