next-site: fix badge/contrast/save-feedback bugs
This commit is contained in:
parent
09e0b6e49b
commit
a84aabb33b
3 changed files with 34 additions and 10 deletions
|
|
@ -10,7 +10,7 @@ export function renderAdminPage(): string {
|
||||||
<div id="gate">
|
<div id="gate">
|
||||||
<div class="page-sub">Admin token</div>
|
<div class="page-sub">Admin token</div>
|
||||||
<input id="token" type="password" style="${INPUT_STYLE}max-width:280px;">
|
<input id="token" type="password" style="${INPUT_STYLE}max-width:280px;">
|
||||||
<button id="unlock" class="btn" style="border:none;cursor:pointer;margin-left:8px;">Unlock</button>
|
<button id="unlock" class="btn" style="margin-left:8px;">Unlock</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="panel" style="display:none;"></div>
|
<div id="panel" style="display:none;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -38,6 +38,17 @@ export function renderAdminPage(): string {
|
||||||
</div>\`
|
</div>\`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function flashSaved(btn) {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const original = btn.textContent
|
||||||
|
btn.textContent = 'Saved'
|
||||||
|
setTimeout(() => {
|
||||||
|
btn.textContent = original
|
||||||
|
resolve()
|
||||||
|
}, 700)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async function loadState() {
|
async function loadState() {
|
||||||
const res = await fetch('/api/admin/state', {
|
const res = await fetch('/api/admin/state', {
|
||||||
headers: { authorization: \`Bearer \${token}\` },
|
headers: { authorization: \`Bearer \${token}\` },
|
||||||
|
|
@ -51,10 +62,10 @@ export function renderAdminPage(): string {
|
||||||
<div class="card-name">Site settings</div>
|
<div class="card-name">Site settings</div>
|
||||||
\${fieldRow('site', 'name', 'Site name', state.site?.name)}
|
\${fieldRow('site', 'name', 'Site name', state.site?.name)}
|
||||||
\${fieldRow('site', 'description', 'Site description', state.site?.description, true)}
|
\${fieldRow('site', 'description', 'Site description', state.site?.description, true)}
|
||||||
<button id="save-site" class="btn" style="border:none;cursor:pointer;">Save site settings</button>
|
<button id="save-site" class="btn">Save site settings</button>
|
||||||
</div>\`
|
</div>\`
|
||||||
|
|
||||||
document.getElementById('save-site').addEventListener('click', async () => {
|
document.getElementById('save-site').addEventListener('click', async (e) => {
|
||||||
const name = document.querySelector('[data-id="site"][data-field="name"]').value
|
const name = document.querySelector('[data-id="site"][data-field="name"]').value
|
||||||
const description = document.querySelector('[data-id="site"][data-field="description"]').value
|
const description = document.querySelector('[data-id="site"][data-field="description"]').value
|
||||||
await fetch('/api/admin/site', {
|
await fetch('/api/admin/site', {
|
||||||
|
|
@ -62,6 +73,7 @@ export function renderAdminPage(): string {
|
||||||
headers: { 'content-type': 'application/json', authorization: \`Bearer \${token}\` },
|
headers: { 'content-type': 'application/json', authorization: \`Bearer \${token}\` },
|
||||||
body: JSON.stringify({ name, description }),
|
body: JSON.stringify({ name, description }),
|
||||||
})
|
})
|
||||||
|
await flashSaved(e.currentTarget)
|
||||||
init()
|
init()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +85,7 @@ export function renderAdminPage(): string {
|
||||||
<div style="display:flex;gap:8px;margin-bottom:6px;" class="channel-row">
|
<div style="display:flex;gap:8px;margin-bottom:6px;" class="channel-row">
|
||||||
<input placeholder="channel name" value="\${channel}" data-channel-name style="\${INPUT_STYLE}width:140px;flex:none;">
|
<input placeholder="channel name" value="\${channel}" data-channel-name style="\${INPUT_STYLE}width:140px;flex:none;">
|
||||||
<input placeholder="version" value="\${version}" data-channel-version style="\${INPUT_STYLE}width:100px;flex:none;">
|
<input placeholder="version" value="\${version}" data-channel-version style="\${INPUT_STYLE}width:100px;flex:none;">
|
||||||
<button data-id="\${id}" class="btn-ghost set-channel" style="border-width:1px;cursor:pointer;">Set</button>
|
<button data-id="\${id}" class="btn-ghost set-channel">Set</button>
|
||||||
</div>\`).join('')
|
</div>\`).join('')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -92,12 +104,12 @@ export function renderAdminPage(): string {
|
||||||
<label style="font-size:11px;display:flex;align-items:center;gap:4px;margin-bottom:10px;">
|
<label style="font-size:11px;display:flex;align-items:center;gap:4px;margin-bottom:10px;">
|
||||||
<input type="checkbox" data-id="\${id}" data-field="hidden" \${o.hidden ? 'checked' : ''}> hidden
|
<input type="checkbox" data-id="\${id}" data-field="hidden" \${o.hidden ? 'checked' : ''}> hidden
|
||||||
</label>
|
</label>
|
||||||
<button data-id="\${id}" class="btn save" style="border:none;cursor:pointer;">Save</button>
|
<button data-id="\${id}" class="btn save">Save</button>
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top:14px;padding-top:14px;border-top:1px solid rgba(255,255,255,0.08);">
|
<div style="margin-top:14px;padding-top:14px;border-top:1px solid rgba(255,255,255,0.08);">
|
||||||
<div style="font-size:10.5px;color:rgba(255,255,255,0.4);margin-bottom:6px;">Channels (any name - Next's client lists these for the user to pick)</div>
|
<div style="font-size:10.5px;color:rgba(255,255,255,0.4);margin-bottom:6px;">Channels (any name - Next's client lists these for the user to pick)</div>
|
||||||
<div class="channels" data-id="\${id}">\${channelRows(id, state.channels[id])}</div>
|
<div class="channels" data-id="\${id}">\${channelRows(id, state.channels[id])}</div>
|
||||||
<button data-id="\${id}" class="btn-ghost add-channel-row" style="border-width:1px;cursor:pointer;margin-top:4px;">+ Add channel</button>
|
<button data-id="\${id}" class="btn-ghost add-channel-row" style="margin-top:4px;">+ Add channel</button>
|
||||||
</div>
|
</div>
|
||||||
</div>\`
|
</div>\`
|
||||||
}).join('')
|
}).join('')
|
||||||
|
|
@ -125,6 +137,7 @@ export function renderAdminPage(): string {
|
||||||
headers: { 'content-type': 'application/json', authorization: \`Bearer \${token}\` },
|
headers: { 'content-type': 'application/json', authorization: \`Bearer \${token}\` },
|
||||||
body: JSON.stringify(patch),
|
body: JSON.stringify(patch),
|
||||||
})
|
})
|
||||||
|
await flashSaved(btn)
|
||||||
init()
|
init()
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
@ -157,6 +170,7 @@ export function renderAdminPage(): string {
|
||||||
alert(data.error)
|
alert(data.error)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
await flashSaved(btn)
|
||||||
init()
|
init()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,17 @@ export function renderBrowsePage(
|
||||||
const override = overrides[id]
|
const override = overrides[id]
|
||||||
const version = plugin.channels.latest ?? Object.keys(plugin.versions)[0]
|
const version = plugin.channels.latest ?? Object.keys(plugin.versions)[0]
|
||||||
const tagline = override?.tagline ?? plugin.description
|
const tagline = override?.tagline ?? plugin.description
|
||||||
|
const badges = Object.keys(plugin.channels)
|
||||||
|
.map(
|
||||||
|
channel =>
|
||||||
|
`<div class="badge">${escapeHtml(channel.toUpperCase())}</div>`,
|
||||||
|
)
|
||||||
|
.join('')
|
||||||
return `
|
return `
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-top">
|
<div class="card-top">
|
||||||
<div class="card-icon">${plugin.icon ? escapeHtml(plugin.icon) : '\u{1F9E9}'}</div>
|
<div class="card-icon">${plugin.icon ? escapeHtml(plugin.icon) : '\u{1F9E9}'}</div>
|
||||||
<div class="badge">STABLE</div>
|
<div class="badge-row">${badges}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-name"><a href="/plugins/${encodeURIComponent(id)}">${escapeHtml(plugin.name)}</a></div>
|
<div class="card-name"><a href="/plugins/${encodeURIComponent(id)}">${escapeHtml(plugin.name)}</a></div>
|
||||||
<div class="card-desc">${escapeHtml(tagline)}</div>
|
<div class="card-desc">${escapeHtml(tagline)}</div>
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,7 @@ export const PAGE_CSS = `
|
||||||
border: 1px solid rgba(255,255,255,0.1); display: flex; align-items: center;
|
border: 1px solid rgba(255,255,255,0.1); display: flex; align-items: center;
|
||||||
justify-content: center; font-size: 15px;
|
justify-content: center; font-size: 15px;
|
||||||
}
|
}
|
||||||
|
.badge-row { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }
|
||||||
.badge {
|
.badge {
|
||||||
background: rgba(154,140,255,0.12); color: #b6acff; border: 1px solid rgba(154,140,255,0.25);
|
background: rgba(154,140,255,0.12); color: #b6acff; border: 1px solid rgba(154,140,255,0.25);
|
||||||
font-size: 10px; font-weight: 650; padding: 3px 8px; border-radius: 5px;
|
font-size: 10px; font-weight: 650; padding: 3px 8px; border-radius: 5px;
|
||||||
|
|
@ -98,16 +99,19 @@ export const PAGE_CSS = `
|
||||||
.card-desc { position: relative; font-size: 13.5px; color: rgba(255,255,255,0.55); margin-top: 6px; line-height: 1.5; }
|
.card-desc { position: relative; font-size: 13.5px; color: rgba(255,255,255,0.55); margin-top: 6px; line-height: 1.5; }
|
||||||
.card-foot { position: relative; display: flex; align-items: center; justify-content: space-between; margin-top: 18px; }
|
.card-foot { position: relative; display: flex; align-items: center; justify-content: space-between; margin-top: 18px; }
|
||||||
.card-meta { font-size: 11.5px; color: rgba(255,255,255,0.4); }
|
.card-meta { font-size: 11.5px; color: rgba(255,255,255,0.4); }
|
||||||
|
button { font-family: inherit; }
|
||||||
.btn {
|
.btn {
|
||||||
background: #e8e9ed; color: #0b0c10; font-size: 13px; font-weight: 650;
|
background: #e8e9ed; color: #0b0c10; font-size: 13px; font-weight: 650;
|
||||||
padding: 8px 16px; border-radius: 8px; display: inline-block;
|
padding: 8px 16px; border-radius: 8px; display: inline-block; border: none; cursor: pointer;
|
||||||
transition: transform 0.2s ease, opacity 0.2s ease;
|
transition: transform 0.2s ease, opacity 0.2s ease;
|
||||||
}
|
}
|
||||||
.btn:hover { transform: translateY(-2px); opacity: 0.92; }
|
.btn:hover { transform: translateY(-2px); opacity: 0.92; }
|
||||||
.btn-ghost {
|
.btn-ghost {
|
||||||
border: 1px solid rgba(255,255,255,0.16); color: #fff; font-size: 13px; font-weight: 600;
|
background: transparent; border: 1px solid rgba(255,255,255,0.16); color: #fff;
|
||||||
padding: 8px 16px; border-radius: 8px; display: inline-block;
|
font-size: 13px; font-weight: 600; padding: 8px 16px; border-radius: 8px;
|
||||||
|
display: inline-block; cursor: pointer; transition: border-color 0.2s ease, background 0.2s ease;
|
||||||
}
|
}
|
||||||
|
.btn-ghost:hover { border-color: rgba(255,255,255,0.32); background: rgba(255,255,255,0.04); }
|
||||||
`
|
`
|
||||||
|
|
||||||
export const SOURCE_URL =
|
export const SOURCE_URL =
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue