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 @@
-