Allow linking an existing toolbox from the game drawer
All checks were successful
Deploy Sokko G / deploy (push) Successful in 19s

This commit is contained in:
Shinuwa 2026-10-01 09:33:18 +02:00
parent 962816bbb9
commit c3bab44a4b
7 changed files with 107 additions and 1 deletions

View file

@ -486,7 +486,7 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
<button className="drawer-close-button nds-icon-button" data-size="sm" onClick={() => actions.setDrawerGameId("")} aria-label="Fermer" title="Fermer"><Icon name="close" /></button>
</header>
{toolbox ? <ToolboxView siteContent={siteContent} toolbox={toolbox} toolboxGame={game} embedded actions={actions} storageUsage={storageUsage} storageReady={storageReady} getModuleData={getModuleData} updateToolbox={updateToolbox} updateModuleData={updateModuleData} addImageFiles={addImageFiles} guideChecklist={guideChecklist} /> : (
<div className="empty nds-empty-state"><p>Aucune toolbox associée à cette page jeu.</p><button className="nds-button" data-size="sm" data-variant="primary" onClick={() => actions.setCreateModal({ gameId })}>Créer et associer</button></div>
<ToolboxDrawerEmptyState gameId={gameId} toolboxes={toolboxes} links={links} actions={actions} content={siteContent.toolboxes.drawer} />
)}
<StorageQuota usage={storageUsage} />
</div>
@ -495,6 +495,42 @@ export function ToolboxDrawer({ gameId, game, siteContent, toolboxes, links, act
);
}
function ToolboxDrawerEmptyState({ gameId, toolboxes, links, actions, content }) {
const linkedToolboxIds = useMemo(() => new Set(Object.values(links).filter(Boolean)), [links]);
const availableToolboxes = useMemo(
() => toolboxes.filter((toolbox) => !linkedToolboxIds.has(toolbox.id)),
[linkedToolboxIds, toolboxes]
);
const [selectedId, setSelectedId] = useState("");
const validSelectedId = availableToolboxes.some((toolbox) => toolbox.id === selectedId) ? selectedId : "";
const hasAvailableToolboxes = availableToolboxes.length > 0;
return (
<div className="drawer-empty-toolbox">
<section className="empty nds-empty-state">
<p>{content.emptyText}</p>
<button className="nds-button" data-size="sm" data-variant="primary" type="button" onClick={() => actions.setCreateModal({ gameId })}>{content.createLabel}</button>
</section>
<section className="drawer-link-existing nds-empty-state" aria-labelledby="drawer-link-existing-title">
<h2 id="drawer-link-existing-title">{content.linkExistingTitle}</h2>
<form onSubmit={(event) => {
event.preventDefault();
if (validSelectedId) actions.linkToolboxToGame(gameId, validSelectedId);
}}>
<label className="drawer-link-existing-field">
<span>{content.selectLabel}</span>
<select className="nds-select" data-size="sm" value={validSelectedId} disabled={!hasAvailableToolboxes} onChange={(event) => setSelectedId(event.target.value)}>
<option value="">{hasAvailableToolboxes ? content.selectPlaceholder : content.noAvailable}</option>
{availableToolboxes.map((availableToolbox) => <option key={availableToolbox.id} value={availableToolbox.id}>{availableToolbox.name}</option>)}
</select>
</label>
<button className="nds-button" data-size="sm" data-variant="primary" type="submit" disabled={!validSelectedId}>{content.linkLabel}</button>
</form>
</section>
</div>
);
}
function EditableTitle({ value, fallback, onSave, className = "module-title", tutorialTarget = "" }) {
const inlineEdit = useInlineEdit({
value,

View file

@ -429,6 +429,10 @@
max-width: none;
}
.drawer-link-existing form {
grid-template-columns: 1fr;
}
.drawer-resize-handle {
display: none;
}

View file

@ -66,6 +66,32 @@ body.is-resizing-drawer * {
opacity: 0.56;
}
.drawer-empty-toolbox {
display: grid;
gap: var(--space-4);
}
.drawer-link-existing h2 {
font-size: var(--font-size-md);
}
.drawer-link-existing form {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: var(--space-3);
align-items: end;
}
.drawer-link-existing-field {
display: grid;
gap: var(--space-2);
}
.drawer-link-existing-field,
.drawer-link-existing .nds-select {
width: 100%;
}
.drawer-close-button {
flex: 0 0 40px;
}