add copyright & disclaime and remove double content
All checks were successful
Deploy Sokko G / deploy (push) Successful in 6s
All checks were successful
Deploy Sokko G / deploy (push) Successful in 6s
This commit is contained in:
parent
15a904c008
commit
e65c74ea0a
5 changed files with 188 additions and 34 deletions
|
|
@ -2,6 +2,85 @@ import { readFile } from "node:fs/promises";
|
||||||
import { test } from "node:test";
|
import { test } from "node:test";
|
||||||
import assert from "node:assert/strict";
|
import assert from "node:assert/strict";
|
||||||
|
|
||||||
|
function valueAt(object, path) {
|
||||||
|
return path.split(".").reduce((value, key) => value?.[key], object);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertNonEmptyString(object, path) {
|
||||||
|
const value = valueAt(object, path);
|
||||||
|
assert.equal(typeof value, "string", `${path} must be a string`);
|
||||||
|
assert.ok(value.trim(), `${path} must not be empty`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertStringArray(object, path, minLength = 1) {
|
||||||
|
const value = valueAt(object, path);
|
||||||
|
assert.ok(Array.isArray(value), `${path} must be an array`);
|
||||||
|
assert.ok(value.length >= minLength, `${path} must contain at least ${minLength} item(s)`);
|
||||||
|
value.forEach((item, index) => {
|
||||||
|
assert.equal(typeof item, "string", `${path}[${index}] must be a string`);
|
||||||
|
assert.ok(item.trim(), `${path}[${index}] must not be empty`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateSiteContent(site) {
|
||||||
|
[
|
||||||
|
"brand.name",
|
||||||
|
"brand.homeAriaLabel",
|
||||||
|
"navigation.home",
|
||||||
|
"navigation.toolboxes",
|
||||||
|
"navigation.games",
|
||||||
|
"navigation.mobileGames",
|
||||||
|
"sidebar.badge",
|
||||||
|
"sidebar.note",
|
||||||
|
"topbar.dashboard",
|
||||||
|
"topbar.toolbox",
|
||||||
|
"topbar.games",
|
||||||
|
"gamesPage.eyebrow",
|
||||||
|
"gamesPage.title",
|
||||||
|
"gamesPage.description",
|
||||||
|
"gamesPage.emptyTitle",
|
||||||
|
"gamesPage.emptyText",
|
||||||
|
"home.hero.eyebrow",
|
||||||
|
"home.hero.title",
|
||||||
|
"home.hero.description",
|
||||||
|
"home.hero.primaryAction",
|
||||||
|
"home.hero.secondaryAction",
|
||||||
|
"home.stats.toolboxSingular",
|
||||||
|
"home.stats.toolboxPlural",
|
||||||
|
"home.stats.toolSingular",
|
||||||
|
"home.stats.toolPlural",
|
||||||
|
"home.origin.eyebrow",
|
||||||
|
"home.origin.title",
|
||||||
|
"home.origin.visualAlt",
|
||||||
|
"home.origin.dialogueAriaLabel",
|
||||||
|
"home.origin.caption",
|
||||||
|
"home.legal.copyright",
|
||||||
|
"home.legal.disclaimer",
|
||||||
|
"toolboxes.eyebrow",
|
||||||
|
"toolboxes.title",
|
||||||
|
"toolboxes.newButton",
|
||||||
|
"toolboxes.importAll",
|
||||||
|
"toolboxes.exportAll",
|
||||||
|
"toolboxes.importOne",
|
||||||
|
"toolboxes.summary",
|
||||||
|
"toolboxes.storageHelp.title",
|
||||||
|
"toolboxes.storageHelp.text",
|
||||||
|
"toolboxes.emptyTitle",
|
||||||
|
"toolboxes.emptyText"
|
||||||
|
].forEach((path) => assertNonEmptyString(site, path));
|
||||||
|
|
||||||
|
assertStringArray(site, "toolboxes.storageHelp.items", 3);
|
||||||
|
|
||||||
|
const lines = valueAt(site, "home.origin.lines");
|
||||||
|
assert.ok(Array.isArray(lines), "home.origin.lines must be an array");
|
||||||
|
assert.ok(lines.length >= 2, "home.origin.lines must contain at least 2 items");
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
assert.ok(["user", "app"].includes(line?.speaker), `home.origin.lines[${index}].speaker must be "user" or "app"`);
|
||||||
|
assert.equal(typeof line?.text, "string", `home.origin.lines[${index}].text must be a string`);
|
||||||
|
assert.ok(line.text.trim(), `home.origin.lines[${index}].text must not be empty`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
test("vite entrypoint loads the react application", async () => {
|
test("vite entrypoint loads the react application", async () => {
|
||||||
const html = await readFile("website/index.html", "utf8");
|
const html = await readFile("website/index.html", "utf8");
|
||||||
|
|
||||||
|
|
@ -150,6 +229,11 @@ test("react application defines the expected local toolbox primitives", async ()
|
||||||
assert.match(screenshotsModule, /dataTransfer\.files/);
|
assert.match(screenshotsModule, /dataTransfer\.files/);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("site content json is complete", async () => {
|
||||||
|
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
||||||
|
validateSiteContent(site);
|
||||||
|
});
|
||||||
|
|
||||||
test("mhwilds data and assets are available", async () => {
|
test("mhwilds data and assets are available", async () => {
|
||||||
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
const site = JSON.parse(await readFile("website/public/data/site.json", "utf8"));
|
||||||
const games = JSON.parse(await readFile("website/public/data/games.json", "utf8"));
|
const games = JSON.parse(await readFile("website/public/data/games.json", "utf8"));
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,10 @@
|
||||||
"text": "Merci ! :)"
|
"text": "Merci ! :)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"legal": {
|
||||||
|
"copyright": "© 2026 Shinuwa / Sokko G. Tous droits réservés.",
|
||||||
|
"disclaimer": "Sokko G est un projet non officiel. Les noms, logos, images, illustrations et contenus liés aux jeux présentés appartiennent à leurs éditeurs et ayants droit respectifs. Les visuels officiels utilisés sur le site restent la propriété de leurs détenteurs."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"toolboxes": {
|
"toolboxes": {
|
||||||
|
|
|
||||||
|
|
@ -44,57 +44,57 @@ const DEFAULT_TOOLBOX_ICON = `${TOOLBOX_ICON_BASE}toolbox.png`;
|
||||||
const DEFAULT_SITE_CONTENT = {
|
const DEFAULT_SITE_CONTENT = {
|
||||||
brand: { name: "Sokko G", homeAriaLabel: "Accueil Sokko G" },
|
brand: { name: "Sokko G", homeAriaLabel: "Accueil Sokko G" },
|
||||||
navigation: { home: "Accueil", toolboxes: "Toolboxes", games: "Jeux", mobileGames: "Infos" },
|
navigation: { home: "Accueil", toolboxes: "Toolboxes", games: "Jeux", mobileGames: "Infos" },
|
||||||
sidebar: { badge: "Local only", note: "Données stockées dans ce navigateur." },
|
sidebar: { badge: "", note: "" },
|
||||||
topbar: { dashboard: "Dashboard", toolbox: "Toolbox active", games: "Informations jeu" },
|
topbar: { dashboard: "", toolbox: "", games: "" },
|
||||||
gamesPage: {
|
gamesPage: {
|
||||||
eyebrow: "Pages informatives",
|
eyebrow: "",
|
||||||
title: "Jeux disponibles",
|
title: "Jeux",
|
||||||
description: "Choisissez un jeu pour consulter ses données maintenues et associer une toolbox locale.",
|
description: "",
|
||||||
emptyTitle: "Aucun jeu disponible",
|
emptyTitle: "Aucun jeu disponible",
|
||||||
emptyText: "Ajoutez des entrées dans /data/games.json."
|
emptyText: ""
|
||||||
},
|
},
|
||||||
home: {
|
home: {
|
||||||
hero: {
|
hero: {
|
||||||
eyebrow: "Session gaming efficace",
|
eyebrow: "",
|
||||||
title: "Sokko G",
|
title: "Sokko G",
|
||||||
description: "Centralisez vos outils et repères de jeu dans une interface locale, rapide à consulter, pensée pour accompagner vos sessions sans interrompre l’action.",
|
description: "",
|
||||||
primaryAction: "Voir les toolboxes",
|
primaryAction: "Toolboxes",
|
||||||
secondaryAction: "Voir les jeux"
|
secondaryAction: "Jeux"
|
||||||
},
|
},
|
||||||
stats: {
|
stats: {
|
||||||
toolboxSingular: "toolbox créée",
|
toolboxSingular: "toolbox",
|
||||||
toolboxPlural: "toolboxs créées",
|
toolboxPlural: "toolboxes",
|
||||||
toolSingular: "outil disponible",
|
toolSingular: "outil",
|
||||||
toolPlural: "outils disponibles"
|
toolPlural: "outils"
|
||||||
},
|
},
|
||||||
origin: {
|
origin: {
|
||||||
eyebrow: "Origine du nom",
|
eyebrow: "",
|
||||||
title: "Pourquoi Sokko G ?",
|
title: "",
|
||||||
visualAlt: "Dragon Sokko G",
|
visualAlt: "",
|
||||||
dialogueAriaLabel: "Dialogue d’origine du nom Sokko G",
|
dialogueAriaLabel: "",
|
||||||
caption: "\"G\" c'est pour Gaming",
|
caption: "",
|
||||||
lines: []
|
lines: []
|
||||||
|
},
|
||||||
|
legal: {
|
||||||
|
copyright: "",
|
||||||
|
disclaimer: ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
toolboxes: {
|
toolboxes: {
|
||||||
eyebrow: "Données locales",
|
eyebrow: "",
|
||||||
title: "Toolboxes",
|
title: "Toolboxes",
|
||||||
newButton: "Nouvelle toolbox",
|
newButton: "Nouvelle toolbox",
|
||||||
importAll: "Importer tout",
|
importAll: "Importer",
|
||||||
exportAll: "Exporter tout",
|
exportAll: "Exporter",
|
||||||
importOne: "Importer une toolbox",
|
importOne: "Importer",
|
||||||
summary: "Créez des espaces modulaires pour regrouper vos outils de session : notes, suivis, références, captures et futurs modules restent accessibles rapidement, en local, par jeu ou par usage.",
|
summary: "",
|
||||||
storageHelp: {
|
storageHelp: {
|
||||||
title: "Sauvegardes locales",
|
title: "",
|
||||||
text: "Vos toolboxes restent sur cet appareil, dans ce navigateur. Rien n’est envoyé sur un serveur.",
|
text: "",
|
||||||
items: [
|
items: []
|
||||||
"Un nettoyage du navigateur ou un changement d’appareil peut supprimer les données.",
|
|
||||||
"Surveillez le quota, surtout si vous ajoutez des screenshots.",
|
|
||||||
"Faites un export global régulier pour garder une sauvegarde."
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
emptyTitle: "Aucune toolbox",
|
emptyTitle: "Aucune toolbox",
|
||||||
emptyText: "Créez une première toolbox pour stocker vos outils dans ce navigateur."
|
emptyText: ""
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -901,8 +901,8 @@ function Shell({ route, content, games, toolboxes, links, actions, children }) {
|
||||||
<span className="badge">{content.sidebar.badge}</span>
|
<span className="badge">{content.sidebar.badge}</span>
|
||||||
<p>{content.sidebar.note}</p>
|
<p>{content.sidebar.note}</p>
|
||||||
<div className="sidebar-storage-actions" aria-label="Actions globales de stockage">
|
<div className="sidebar-storage-actions" aria-label="Actions globales de stockage">
|
||||||
|
<button className="sidebar-action-button primary" onClick={actions.exportAllToolboxes} aria-label="Exporter toutes les toolboxes" title="Exporter tout"><Icon name="export" /></button>
|
||||||
<ImportButton className="sidebar-action-button import-icon-button" title="Importer tout" ariaLabel="Importer toutes les toolboxes" onFile={actions.importAllToolboxes} icon="import" />
|
<ImportButton className="sidebar-action-button import-icon-button" title="Importer tout" ariaLabel="Importer toutes les toolboxes" onFile={actions.importAllToolboxes} icon="import" />
|
||||||
<button className="sidebar-action-button" onClick={actions.exportAllToolboxes} aria-label="Exporter toutes les toolboxes" title="Exporter tout"><Icon name="export" /></button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
@ -973,6 +973,10 @@ function HomePage({ siteContent, toolboxes }) {
|
||||||
<img src="/static/img/dragon.png" alt={content.origin.visualAlt} />
|
<img src="/static/img/dragon.png" alt={content.origin.visualAlt} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<footer className="home-legal">
|
||||||
|
<p>{content.legal.copyright}</p>
|
||||||
|
<p>{content.legal.disclaimer}</p>
|
||||||
|
</footer>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1012,8 +1016,8 @@ function ToolboxesPage({ siteContent, toolboxes, getToolboxGame, actions }) {
|
||||||
<ImportButton className="import-button" label={content.importOne} onFile={actions.importToolbox} />
|
<ImportButton className="import-button" label={content.importOne} onFile={actions.importToolbox} />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<button className="primary" onClick={actions.exportAllToolboxes}>{content.exportAll}</button>
|
||||||
<ImportButton className="import-button" label={content.importAll} onFile={actions.importAllToolboxes} />
|
<ImportButton className="import-button" label={content.importAll} onFile={actions.importAllToolboxes} />
|
||||||
<button onClick={actions.exportAllToolboxes}>{content.exportAll}</button>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section className="cards">
|
<section className="cards">
|
||||||
|
|
|
||||||
|
|
@ -296,6 +296,52 @@ input[type="checkbox"]:focus-visible {
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-action-button.primary,
|
||||||
|
.import-icon-button.primary,
|
||||||
|
.toolbox-icon-button.primary,
|
||||||
|
.card-icon-button.primary,
|
||||||
|
.drawer-action-button.primary,
|
||||||
|
.drawer-close-button.primary,
|
||||||
|
.filter-reset-button.primary,
|
||||||
|
.screenshot-viewer-button.primary,
|
||||||
|
.module-edit-button.primary,
|
||||||
|
.module-delete-button.primary,
|
||||||
|
.shot-delete-button.primary,
|
||||||
|
.tool-quick-add-button.primary {
|
||||||
|
border-color: rgba(246, 196, 83, 0.34);
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(72, 43, 160, 0.96), rgba(45, 52, 146, 0.94)) padding-box,
|
||||||
|
linear-gradient(135deg, rgba(246, 196, 83, 0.66), rgba(139, 92, 246, 0.36)) border-box;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow:
|
||||||
|
0 8px 22px rgba(5, 7, 17, 0.22),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-action-button.primary:hover,
|
||||||
|
.import-icon-button.primary:hover,
|
||||||
|
.toolbox-icon-button.primary:hover,
|
||||||
|
.card-icon-button.primary:hover,
|
||||||
|
.drawer-action-button.primary:hover,
|
||||||
|
.drawer-close-button.primary:hover,
|
||||||
|
.filter-reset-button.primary:hover,
|
||||||
|
.screenshot-viewer-button.primary:hover,
|
||||||
|
.module-edit-button.primary:hover,
|
||||||
|
.module-delete-button.primary:hover,
|
||||||
|
.shot-delete-button.primary:hover,
|
||||||
|
.tool-quick-add-button.primary:hover {
|
||||||
|
border-color: rgba(246, 196, 83, 0.72);
|
||||||
|
background:
|
||||||
|
linear-gradient(135deg, rgba(109, 40, 217, 0.98), rgba(67, 56, 202, 0.96)) padding-box,
|
||||||
|
linear-gradient(135deg, rgba(246, 196, 83, 0.96), rgba(196, 181, 253, 0.68)) border-box;
|
||||||
|
color: #fff;
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(246, 196, 83, 0.16),
|
||||||
|
0 0 18px rgba(139, 92, 246, 0.3),
|
||||||
|
0 0 30px rgba(246, 196, 83, 0.16),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.18);
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-action-button:hover .ui-icon,
|
.sidebar-action-button:hover .ui-icon,
|
||||||
.import-icon-button:hover .ui-icon,
|
.import-icon-button:hover .ui-icon,
|
||||||
.toolbox-icon-button:hover .ui-icon,
|
.toolbox-icon-button:hover .ui-icon,
|
||||||
|
|
|
||||||
|
|
@ -321,3 +321,19 @@ span {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 1.35;
|
line-height: 1.35;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-legal {
|
||||||
|
display: grid;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: var(--space-5);
|
||||||
|
padding-top: var(--space-4);
|
||||||
|
border-top: 1px solid rgba(150, 165, 205, 0.14);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
line-height: 1.45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-legal p {
|
||||||
|
max-width: 920px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue