/* settings-atoms.jsx — reusable form atoms for the settings page.
All components expect plain values + onChange callbacks (controlled mode). */
// ── Card with gradient accent + header ──────────────────────────────────
const SCard = ({ accent, iconBg, iconColor, iconName, title, subtitle, right, children, style }) => (
{title}
{subtitle &&
{subtitle}
}
{right &&
{right}
}
{children}
);
// ── Generic text/number input ───────────────────────────────────────────
const SField = ({label, help, value, onChange, type='text', unit, placeholder, readOnly, disabled, autoComplete, name, invalid}) => {
/* Talfält: parseFloat kördes på varje tangenttryck, så "12." normaliserades
tillbaka till 12 innan andra decimalsiffran hann skrivas — decimaler gick
inte att mata in alls (André 20/9, Ekonomi). Den råa strängen hålls därför
lokalt medan fältet redigeras och onChange får talet som förut. Komma
accepteras (svenskt tangentbord) och tolkas som decimaltecken.
draftFor speglar det vi senast skickade: ändrar föräldern värdet utifrån
(Återställ, omladdning) släpps utkastet och fältet visar propen. */
const [draft, setDraft] = React.useState(null);
const draftFor = React.useRef(null);
const handle = (e) => {
if (readOnly || disabled) return;
if (type === 'number'){
const raw = e.target.value;
if (!/^-?[0-9]*[.,]?[0-9]*$/.test(raw)) return;
setDraft(raw);
let out;
if (raw === '' || raw === '-') out = raw;
else { const n = parseFloat(raw.replace(',', '.')); out = isNaN(n) ? 0 : n; }
draftFor.current = out;
onChange(out);
} else {
onChange(e.target.value);
}
};
const shown = (type === 'number' && draft !== null && draftFor.current === value)
? draft : (value ?? '');
return (
);
};
/* SNav — kategorinav för inställningssidan (2026-06-11, flik-läge).
Kontrollerad komponent: föräldern äger aktiv kategori och visar BARA den
kategorins kort (egen "sida" per kategori — användarönskemål, ersätter
scrollspy-långrullen). Desktop (≥1160px): vertikal rail till vänster.
Smalare: horisontell chip-rad under topbaren.
OBS (bevarat lärdomsmönster, se DESIGN_STATE 7h): rAF och
scrollTo({behavior:'smooth'}) är opålitliga i inbäddade vyer — undvik
dem för all framtida scroll-logik här. ALDRIG scrollIntoView. */
const SNav = ({ sections, active, onSelect }) => (
);
/* ── BETA-märkning (André 7/9) ───────────────────────────────────────────────
Samma två delar som på dashboarden (GcBeta/GcBetaNote i gc-core): pill vid
rubriken är märkningen, notisen säger vad användaren ska göra åt den. */
const SBeta = ({ title }) => (
beta
);
const SBetaNote = ({ children }) => (