{data?.title || 'Article'}
-Article view - To be implemented
+diff --git a/site/blueprints/pages/blog.yml b/site/blueprints/pages/blog.yml index 9daf500..fef197e 100644 --- a/site/blueprints/pages/blog.yml +++ b/site/blueprints/pages/blog.yml @@ -43,17 +43,12 @@ tabs: cover: true ratio: 16/9 info: "{{ page.date.toDate('d/m/Y') }}" - articlesList: + articles: label: Articles type: pages - headline: Liste des articles layout: cards - size: full + sortBy: published desc image: - query: page.cover.toFile ratio: 16/9 cover: true - template: article - info: "{{ page.date.toDate('d/m/Y') }}" - create: article - sortBy: date desc \ No newline at end of file + info: "{{ page.published.toDate('d/m/Y') }}" diff --git a/site/templates/article.json.php b/site/templates/article.json.php index 20a8f29..b247fe9 100644 --- a/site/templates/article.json.php +++ b/site/templates/article.json.php @@ -6,26 +6,19 @@ if ($related->isEmpty()) { } $specificData = [ - 'date' => $page->date()->toDate('Y-m-d'), - 'dateFormatted' => $page->date()->toDate('d/m/Y'), - 'intro' => $page->intro()->value(), - 'author' => [ - 'name' => $page->authorName()->value(), - 'role' => $page->authorRole()->value(), - 'photo' => $page->authorPhoto()->toFile()?->url() - ], - 'cover' => $page->cover()->toFile()?->url(), - 'content' => $page->articleContent()->toBlocks(), - 'tags' => $page->tags()->split(), - 'related' => $related->map(function($rec) { + 'date' => $page->date()->toDate('d/m/Y'), + 'intro' => $page->intro()->value(), + 'cover' => $page->cover()->toFile()?->url(), + 'body' => (string) $page->body()->toBlocks(), + 'related' => $related->map(function($rec) { return [ 'title' => $rec->title()->value(), - 'url' => $rec->url(), - 'category' => $rec->category()->value(), - 'cover' => $rec->cover()->toFile()?->thumb(['width' => 400])->url() + 'slug' => $rec->slug(), + 'date' => $rec->date()->toDate('d/m/Y'), + 'cover' => $rec->cover()->toFile()?->url(), ]; })->values(), - 'parentUrl' => $page->parent()->url() + 'parentUrl' => $page->parent()->url(), ]; $pageData = array_merge($genericData, $specificData); diff --git a/site/templates/blog.json.php b/site/templates/blog.json.php index f24873f..16c3db7 100644 --- a/site/templates/blog.json.php +++ b/site/templates/blog.json.php @@ -1,23 +1,25 @@ featured()->toPages()->first(); + +$mapArticle = function($article) { + return [ + 'title' => $article->title()->value(), + 'slug' => $article->slug(), + 'date' => $article->date()->toDate('d/m/Y'), + 'intro' => $article->intro()->excerpt(200), + 'cover' => $article->cover()->toFile()?->url(), + ]; +}; + +$articles = $page->children()->listed()->sortBy('date', 'desc'); + $specificData = [ - 'intro' => [ - 'title' => $page->introTitle()->value(), - 'text' => $page->introText()->value() - ], - 'articles' => $page->children()->listed()->sortBy('date', 'desc')->map(function($article) { - return [ - 'title' => $article->title()->value(), - 'slug' => $article->slug(), - 'url' => $article->url(), - 'date' => $article->date()->toDate('Y-m-d'), - 'dateFormatted' => $article->date()->toDate('d/m/Y'), - 'intro' => $article->intro()->excerpt(200), - 'cover' => $article->cover()->toFile()?->url(), - 'authorName' => $article->authorName()->value(), - 'authorPhoto' => $article->authorPhoto()->toFile()?->url() - ]; - })->values() + 'intro' => $page->intro()->value(), + 'featured' => $featured ? $mapArticle($featured) : null, + 'articles' => ($featured ? $articles->not($featured) : $articles) + ->map($mapArticle) + ->values(), ]; $pageData = array_merge($genericData, $specificData); diff --git a/src/router/index.js b/src/router/index.js index edf64d0..6e931f9 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -8,16 +8,39 @@ function normalizePath(path) { return path === "/" ? "/home" : path; } +/** + * Trouve l'index de la slide correspondant au path. + * Si le path exact n'existe pas, essaie le chemin parent + * (ex: /blog/article-slug → /blog). + */ +function findSlideIndex(path) { + let idx = slides.getIndexByPath(path); + if (idx !== -1) return idx; + const parentPath = path.replace(/\/[^/]+$/, ""); + if (parentPath) return slides.getIndexByPath(parentPath); + return -1; +} + async function loadSlide(path) { - const idx = slides.getIndexByPath(path); + let slidePath = path; + let idx = slides.getIndexByPath(slidePath); + + // Sub-page: resolve to parent slide (ex: /blog/slug → /blog) + if (idx === -1) { + const parentPath = path.replace(/\/[^/]+$/, ""); + idx = slides.getIndexByPath(parentPath); + if (idx !== -1) slidePath = parentPath; + } + if (idx !== -1) { const slide = slides.all[idx]; if (slide.loaded || slide.loading) return; - slides.setLoading(path, true); + slides.setLoading(slidePath, true); } try { - const response = await fetch(`${path}.json`); + // Fetch the actual slide path (parent), not the sub-page + const response = await fetch(`${slidePath}.json`); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); @@ -28,10 +51,10 @@ async function loadSlide(path) { siteInitialized = true; } - slides.setData(path, data); + slides.setData(slidePath, data); } catch (error) { - console.error(`[router] Failed to load slide ${path}:`, error); - slides.setLoading(path, false); + console.error(`[router] Failed to load slide ${slidePath}:`, error); + slides.setLoading(slidePath, false); } } @@ -48,15 +71,17 @@ export function slideTo(path, { skipHistory = false } = {}) { history.pushState({}, "", path === "/home" ? "/" : path); } - const idx = slides.getIndexByPath(path); + const idx = findSlideIndex(path); + const slidePath = idx !== -1 ? slides.all[idx].path : path; + if (idx !== -1 && slides.all[idx].title) { document.title = `${slides.all[idx].title} — World Game`; } - slides.slideTo(path); + slides.slideTo(slidePath); if (idx !== -1 && !slides.all[idx].loaded) { - loadSlide(path); + loadSlide(slidePath); } } @@ -65,12 +90,14 @@ export async function initRouter() { await loadSlide(initialPath); - const idx = slides.getIndexByPath(initialPath); + const idx = findSlideIndex(initialPath); if (idx !== -1) { slides.setActiveIndex(idx); } - loadAllSlidesInBackground(initialPath); + loadAllSlidesInBackground( + idx !== -1 ? slides.all[idx].path : initialPath + ); window.addEventListener("popstate", () => { const path = normalizePath(window.location.pathname); diff --git a/src/styles/layout.css b/src/styles/layout.css index 7462016..f93e2a5 100644 --- a/src/styles/layout.css +++ b/src/styles/layout.css @@ -13,6 +13,18 @@ overflow-y: hidden; } +/* Pages scrollables (blog, article, etc.) */ +.page-scrollable { + height: 100%; + overflow-y: auto; +} + +.page-container { + max-width: 1200px; + margin: 0 auto; + padding: 0 50px; +} + /* Vertical Lines */ .vertical-line { z-index: var(--z-base); diff --git a/src/views/Article.svelte b/src/views/Article.svelte index 7e356f7..46cdc8d 100644 --- a/src/views/Article.svelte +++ b/src/views/Article.svelte @@ -1,24 +1,262 @@ -
Article view - To be implemented
+Blog view - To be implemented
-Chargement…
+ {/if} + +