Add accounts, presets, live sheet panel and equipment tiles
- Login with registration code, sessions, rate limiting, per-user characters - Share characters with other users (read-only), admin CLI - Rules presets with a Presets tab, applied/pending changes with diff in a side sheet - Equipment as tiles with amounts, starting kit removed - Rename nav tab to "Character", add "New character" button - Stop tracking _to_delete/ Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HJhXuLD2Z6hv3Wqeq9UL9K
This commit is contained in:
@@ -1,20 +1,15 @@
|
||||
import { useState } from 'react'
|
||||
|
||||
const KEY = 'aw_presets' // {presetId: editToken}, only for presets created in this browser
|
||||
const loadTokens = () => { try { return JSON.parse(localStorage.getItem(KEY)) || {} } catch { return {} } }
|
||||
const saveTokens = (t) => { try { localStorage.setItem(KEY, JSON.stringify(t)) } catch { /* storage blocked */ } }
|
||||
|
||||
const BLANK = { id: null, name: '', desc: '', creation: {} }
|
||||
|
||||
export default function Presets({ rules, reload }) {
|
||||
const [tokens, setTokens] = useState(loadTokens)
|
||||
const [sel, setSel] = useState(rules.default_preset || 'standard')
|
||||
const [draft, setDraft] = useState(null) // preset being edited/created, null = viewing
|
||||
const [msg, setMsg] = useState('')
|
||||
const presets = rules.presets || []
|
||||
const current = draft || presets.find((p) => p.id === sel) || presets[0]
|
||||
const editable = !!draft
|
||||
const own = !!current?.id && !!tokens[current.id]
|
||||
const own = !!current?.mine
|
||||
const base = rules.creation
|
||||
const groups = [...new Set((rules.tunables || []).map((x) => x.group))]
|
||||
const value = (k) => (current.creation?.[k] ?? base[k])
|
||||
@@ -36,17 +31,13 @@ export default function Presets({ rules, reload }) {
|
||||
try {
|
||||
const res = await fetch(draft.id ? `/api/presets/${draft.id}` : '/api/presets', {
|
||||
method: draft.id ? 'PUT' : 'POST',
|
||||
headers: { 'Content-Type': 'application/json', ...(draft.id ? { 'X-Edit-Token': tokens[draft.id] } : {}) },
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
if (!res.ok) throw new Error((await res.json().catch(() => ({}))).detail || 'Saving failed')
|
||||
const data = await res.json()
|
||||
let id = draft.id
|
||||
if (!id) {
|
||||
id = data.id
|
||||
const t = { ...tokens, [id]: data.token }
|
||||
saveTokens(t); setTokens(t)
|
||||
}
|
||||
if (!id) id = data.id
|
||||
await reload()
|
||||
setSel(id); setDraft(null); flash('Preset saved.')
|
||||
} catch (e) { flash(String(e.message || e)) }
|
||||
@@ -55,22 +46,20 @@ export default function Presets({ rules, reload }) {
|
||||
const remove = async () => {
|
||||
if (!window.confirm(`Delete the preset "${current.name}"? Characters that already use it keep their numbers.`)) return
|
||||
try {
|
||||
const res = await fetch(`/api/presets/${current.id}`, { method: 'DELETE', headers: { 'X-Edit-Token': tokens[current.id] } })
|
||||
const res = await fetch(`/api/presets/${current.id}`, { method: 'DELETE' })
|
||||
if (!res.ok) throw new Error('Deleting failed')
|
||||
const t = { ...tokens }; delete t[current.id]
|
||||
saveTokens(t); setTokens(t)
|
||||
await reload(); setSel(rules.default_preset || 'standard')
|
||||
} catch (e) { flash(String(e.message || e)) }
|
||||
}
|
||||
|
||||
const kind = (p) => (p.builtin ? 'Built in' : tokens[p.id] ? 'Yours' : 'Shared')
|
||||
const kind = (p) => (p.builtin ? 'Built in' : p.mine ? 'Yours' : p.author ? `By ${p.author}` : 'Shared')
|
||||
|
||||
return (
|
||||
<section className="page">
|
||||
<h1>Rules presets</h1>
|
||||
<p className="muted narrow">
|
||||
A preset is a named set of creation numbers (points for attributes and skills, talents, proficiency ...). Pick one on the Concept page of the creator.
|
||||
Presets are stored on the server, so everyone using this site can use them; only the browser that created a preset can change or delete it.
|
||||
Presets are stored on the server, so everyone using this site can use them; only the account that created a preset can change or delete it.
|
||||
Built-in presets come from <code>rules.json</code>. Duplicate one to make your own version.
|
||||
</p>
|
||||
<div className="presets-layout">
|
||||
|
||||
Reference in New Issue
Block a user