Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions approuter/xs-app.json
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,7 @@
{ "source": "^/learn/?(\\?.*)?$", "target": "/content/pages/learn/$1", "destination": "srv-api", "authenticationType": "none" },
{ "source": "^/model/?(\\?.*)?$", "target": "/content/pages/model/$1", "destination": "srv-api", "authenticationType": "none" },
{ "source": "^/operate/?(\\?.*)?$", "target": "/content/pages/operate/$1", "destination": "srv-api", "authenticationType": "none" },
{ "source": "^/teched/?(\\?.*)?$", "target": "/content/pages/teched/$1", "destination": "srv-api", "authenticationType": "none" },
{ "source": "^/sitemap\\.xml(\\?.*)?$", "target": "/content/pages/sitemap.xml$1", "destination": "srv-api", "authenticationType": "none" },
{ "source": "^/index\\.xml(\\?.*)?$", "target": "/content/pages/index.xml$1", "destination": "srv-api", "authenticationType": "none" },
{ "source": "^/llms\\.txt(\\?.*)?$", "target": "/content/pages/llms.txt$1", "destination": "srv-api", "authenticationType": "none" },
Expand Down
6 changes: 6 additions & 0 deletions hugo/content/teched/_index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
title: "SAP TechEd 2026 Sessions"
description: "Browse the SAP TechEd 2026 session catalog — in-person in Berlin and online via the Virtual experience."
type: teched
layout: list
---
1 change: 1 addition & 0 deletions hugo/data/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
browse.json
author_index.json
channels_stats.json
teched.json
57 changes: 57 additions & 0 deletions hugo/layouts/teched/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{{ define "main" }}
{{- $sessions := (.Site.Data.teched.sessions) | default slice -}}
{{- $berlin := where $sessions "venue" "BERLIN" -}}
{{- $virtual := where $sessions "venue" "VIRTUAL" -}}
{{/* baseof.html already wraps this block in a single <main> landmark, so this
shell uses a plain <div> (the channels-page pattern) rather than nesting a
second <main> — a duplicate-main landmark trips axe landmark-no-duplicate-main. */}}
<div class="teched-page">
<header class="teched-page__intro">
<h1>{{ .Title }}</h1>
{{ with .Description }}<p>{{ . }}</p>{{ end }}
</header>

{{/* Full session payload for the island (Unit 8), which mounts into
#teched-sessions-grid-mount and renders the interactive Berlin/Virtual
grids. The static section headings + per-venue <noscript> lists below
keep the page useful without JavaScript (fail-open — teched.json is
written empty on a build-time fetch error). */}}
<script id="teched-sessions-data" type="application/json">{{ $sessions | jsonify | safeJS }}</script>

<section class="teched-page__venue teched-page__venue--berlin" aria-labelledby="teched-berlin-heading">
<h2 id="teched-berlin-heading">Berlin</h2>
<noscript>
{{- if $berlin }}
<ul>
{{- range $berlin }}
<li><a href="{{ .url }}" target="_blank" rel="noopener">{{ .title }}</a>{{ with .room }} — {{ . }}{{ end }}</li>
{{- end }}
</ul>
{{- else }}
<p>No Berlin sessions published yet.</p>
{{- end }}
</noscript>
</section>

<section class="teched-page__venue teched-page__venue--virtual" aria-labelledby="teched-virtual-heading">
<h2 id="teched-virtual-heading">Virtual</h2>
<noscript>
{{- if $virtual }}
<ul>
{{- range $virtual }}
<li><a href="{{ .url }}" target="_blank" rel="noopener">{{ .title }}</a>{{ with .room }} — {{ . }}{{ end }}</li>
{{- end }}
</ul>
{{- else }}
<p>No Virtual sessions published yet.</p>
{{- end }}
</noscript>
</section>

<div id="teched-sessions-grid-mount"
data-berlin-count="{{ len $berlin }}"
data-virtual-count="{{ len $virtual }}"></div>
<noscript>The interactive SAP TechEd session browser requires JavaScript. The Berlin and Virtual session lists above are the no-JavaScript fallback.</noscript>
</div>
<script type="module" src="{{ partial "island-src.html" "teched-sessions-grid" | relURL }}"></script>
{{ end }}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"fetch-tutorials": "tsx scripts/fetch-tutorials.ts --target hugo",
"fetch-homepage-shelves": "tsx scripts/fetch-homepage-shelves.ts",
"fetch-channels": "tsx scripts/fetch-channels.ts",
"fetch-teched": "tsx scripts/fetch-teched.ts",
"fetch-channel-collections": "tsx scripts/fetch-channel-collections.ts",
"fetch-channels-stats": "tsx scripts/fetch-channels-stats.ts",
"fetch-verb-definitions": "tsx scripts/fetch-verb-definitions.ts",
Expand Down Expand Up @@ -103,7 +104,7 @@
"build:display": "cd app/display-app && npm install && npm run build",
"copy-joule-vendor": "node scripts/copy-joule-vendor.mjs",
"check-deploy-cap-target": "node scripts/check-deploy-cap-target.cjs",
"build:all": "npm run prebuild && npm run fetch-tutorials -- --regenerate && npm run fetch-advocates && npm run fetch-homepage-shelves && npm run fetch-channels && npm run fetch-channel-atlas && npm run fetch-channel-collections && npm run fetch-channels-stats && npm run fetch-verb-definitions && npm run fetch-tags && npm run fetch-shelf-definitions && npm run fetch-featured-topics && npm run fetch-topic-clusters && npm run fetch-sitemap-catalog && npm run generate-ktt-lessons && npm run build:icon-subset && npm run build:css && npm run build:apps && npm run build:island-manifest && npm run check:ui5-single-copy && npx tsx scripts/check-ui5-entry-coverage.ts && npm run build:analytics-explorer && npm run copy-joule-vendor && npm run build:explore && npm run build:channel-atlas && npm run build:hugo && npm run build:page-fallback && npm run build:whats-new-snapshot && npm run retain:assets && npm run build:publish-island-manifest && npm run build:highlight && npm run build:display && npm run build:sdl",
"build:all": "npm run prebuild && npm run fetch-tutorials -- --regenerate && npm run fetch-advocates && npm run fetch-homepage-shelves && npm run fetch-channels && npm run fetch-channel-atlas && npm run fetch-channel-collections && npm run fetch-channels-stats && npm run fetch-verb-definitions && npm run fetch-tags && npm run fetch-shelf-definitions && npm run fetch-featured-topics && npm run fetch-topic-clusters && npm run fetch-sitemap-catalog && npm run fetch-teched && npm run generate-ktt-lessons && npm run build:icon-subset && npm run build:css && npm run build:apps && npm run build:island-manifest && npm run check:ui5-single-copy && npx tsx scripts/check-ui5-entry-coverage.ts && npm run build:analytics-explorer && npm run copy-joule-vendor && npm run build:explore && npm run build:channel-atlas && npm run build:hugo && npm run build:page-fallback && npm run build:whats-new-snapshot && npm run retain:assets && npm run build:publish-island-manifest && npm run build:highlight && npm run build:display && npm run build:sdl",
"build:deploy": "npm run check-deploy-cap-target && npm run build:all",
"deploy": "node scripts/deploy-mta.cjs",
"build:admin": "npm --prefix app/admin-shell run build",
Expand Down
26 changes: 26 additions & 0 deletions scripts/fetch-teched.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { mkdirSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';

const CAP_BASE = process.env.CAP_BASE_URL || 'http://localhost:4004';
const OUT_PATH = join('hugo', 'data', 'teched.json');

let payload: {
sessions: unknown[];
speakers: unknown[];
tracks: unknown[];
buildAt: string;
error: string | null;
} = {
sessions: [], speakers: [], tracks: [], buildAt: new Date().toISOString(), error: null,
};
try {
const res = await fetch(`${CAP_BASE}/build/teched`);
if (!res.ok) throw new Error(`status ${res.status}`);
payload = { ...payload, ...(await res.json()) };
} catch (err) {
payload.error = err instanceof Error ? err.message : String(err);
console.warn(`[fetch-teched] warn: ${payload.error} — writing empty payload`);
}
mkdirSync(join('hugo', 'data'), { recursive: true });
writeFileSync(OUT_PATH, JSON.stringify(payload, null, 2), 'utf-8');
console.log(`[fetch-teched] wrote ${payload.sessions.length} sessions → ${OUT_PATH}`);
1 change: 1 addition & 0 deletions srv/lib/page-key-map.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const IN_SCOPE_PAGES = [
{ route: '/tutorial-navigator/', key: 'page-tutorial-navigator', file: 'tutorial-navigator/index.html', mimeType: 'text/html' },
{ route: '/developer-advocates/', key: 'page-developer-advocates', file: 'developer-advocates/index.html', mimeType: 'text/html' },
{ route: '/devtoberfest/', key: 'page-devtoberfest', file: 'devtoberfest/index.html', mimeType: 'text/html' },
{ route: '/teched/', key: 'page-teched', file: 'teched/index.html', mimeType: 'text/html' },
// Verb hub landing pages (#1659 Phase 2) — each is a single Hugo page
// rendered by layouts/verb/list.html at /<verb>/index.html.
{ route: '/ai/', key: 'page-ai', file: 'ai/index.html', mimeType: 'text/html' },
Expand Down
1 change: 1 addition & 0 deletions test/smoke/pages-routes.smoke.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ const HTML_ROUTES = [
{ path: '/tutorial-navigator/', marker: 'id="tutorial-navigator"' },
{ path: '/developer-advocates/', marker: 'id="advocates-mount"' },
{ path: '/devtoberfest/', marker: 'id="devtoberfest-mount"' },
{ path: '/teched/', marker: 'SAP TechEd' },
{ path: '/ai/', marker: 'data-verb="ai"' },
{ path: '/build/', marker: 'data-verb="build"' },
{ path: '/connect/', marker: 'data-verb="connect"' },
Expand Down
Loading