diff --git a/_data/tutorials.yml b/_data/tutorials.yml new file mode 100644 index 00000000..9ffcbd35 --- /dev/null +++ b/_data/tutorials.yml @@ -0,0 +1,35 @@ +- id: tutorial-1 + title: "Welcome to the SciX Training Hub" + description: "Get an overview of the SciX Training Hub and what you'll learn across this tutorial series." + video_id: jJ0ve_3ov0M + is_new: true + +- id: tutorial-2 + title: "Searching for a Specific Paper" + description: "Learn how to quickly find a specific paper in SciX using author names, titles, identifiers, and more." + video_id: 7ELEYN5L49U + is_new: true + +- id: tutorial-3 + title: "SciX Tutorial 3" + description: "Placeholder description — update this in _data/tutorials.yml." + video_id: WsYbgdaC2Es + is_new: false + +- id: tutorial-4 + title: "SciX Tutorial 4" + description: "Placeholder description — update this in _data/tutorials.yml." + video_id: aOq_JxQId4M + is_new: false + +- id: tutorial-5 + title: "SciX Tutorial 5" + description: "Placeholder description — update this in _data/tutorials.yml." + video_id: tZaLIAoavv8 + is_new: false + +- id: tutorial-6 + title: "SciX Tutorial 6" + description: "Placeholder description — update this in _data/tutorials.yml." + video_id: IPGv0vPxy4M + is_new: false diff --git a/_includes/head_default_scix.html b/_includes/head_default_scix.html index 25b30f92..db30bc83 100644 --- a/_includes/head_default_scix.html +++ b/_includes/head_default_scix.html @@ -78,5 +78,10 @@ ga('send', 'pageview'); + + {% if page.extra_css %} + + {% endif %} + diff --git a/_includes/left-nav_scix.html b/_includes/left-nav_scix.html index f57e0fe0..d6ff3559 100644 --- a/_includes/left-nav_scix.html +++ b/_includes/left-nav_scix.html @@ -162,6 +162,7 @@

+

Training Hub New

What's New

Frequently Asked Questions

Using the SciX API

diff --git a/_layouts/tutorials_layout_scix.html b/_layouts/tutorials_layout_scix.html new file mode 100644 index 00000000..5d128c92 --- /dev/null +++ b/_layouts/tutorials_layout_scix.html @@ -0,0 +1,122 @@ + + + {% include head_default_scix.html %} + + + {% include header_default_scix.html subtitle="help" subpath="scixhelp" %} + +
+
+ {% include search.html %} +
+
+ +
+
+ + +
+ + + {{ content }} +
+
+
+ + {% include footer_default_scix.html %} + +
+ + + + + + + + + + + + + + + + + diff --git a/help/common/css/tutorials.scss b/help/common/css/tutorials.scss new file mode 100644 index 00000000..1e425fc1 --- /dev/null +++ b/help/common/css/tutorials.scss @@ -0,0 +1,308 @@ +--- +--- + +// SciX Training Hub — tutorial page styles +// Uses the SciX brand palette to match the rest of the site. +// Scoped to .tutorials-hub. + +.tutorials-hub { + padding: 20px 0; + color: #333; + + // ── Header ──────────────────────────────────────────────────────────────── + + .hub-header { + margin-bottom: 28px; + + h1 { + color: #333; + font-size: 26px; + font-weight: 700; + margin: 0 0 18px; + } + + .progress-section { + .progress-label { + color: #666; + font-size: 14px; + margin-bottom: 6px; + display: flex; + align-items: baseline; + gap: 4px; + + .progress-pct-value { + color: #049DD9; + font-size: 20px; + font-weight: 700; + } + } + + .progress { + height: 10px; + border-radius: 5px; + background: #e0e0e0; + box-shadow: none; + + .progress-bar { + background: linear-gradient(90deg, #049DD9 0%, #5FBFAE 100%); + border-radius: 5px; + transition: width 0.5s ease; + box-shadow: none; + } + } + } + } + + // ── Tutorial rows ────────────────────────────────────────────────────────── + + .tutorial-row { + background: #fff; + border: 1px solid #5FBFAE; + border-radius: 8px; + margin-bottom: 10px; + overflow: hidden; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06); + transition: box-shadow 0.2s ease, border-color 0.2s ease; + + &:hover { + box-shadow: 0 4px 10px rgba(4, 157, 217, 0.15); + border-color: #049DD9; + } + + &.active { + border-color: #049DD9; + box-shadow: 0 4px 10px rgba(4, 157, 217, 0.15); + } + + &.completed { + border-color: #97BF41; + + .completion-indicator { + opacity: 1 !important; + color: #97BF41; + } + } + + // Row header (clickable title bar) + .tutorial-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 14px 18px; + cursor: pointer; + user-select: none; + background: #F2F2F2; + border-bottom: 1px solid #e0e0e0; + outline: none; + + &:hover { + background: #e8f7fc; + } + + &:focus-visible { + box-shadow: inset 0 0 0 2px #049DD9; + } + + .tutorial-title-area { + display: flex; + align-items: center; + flex-wrap: wrap; + gap: 10px; + + .tutorial-number { + color: #049DD9; + font-size: 11px; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 1px; + white-space: nowrap; + } + + .badge-new { + background: #5FBFAE; + color: #fff; + font-size: 10px; + padding: 2px 8px; + border-radius: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .tutorial-title { + margin: 0; + color: #333; + font-size: 16px; + font-weight: 600; + } + } + + .tutorial-meta { + display: flex; + align-items: center; + gap: 14px; + flex-shrink: 0; + margin-left: 12px; + + .completion-indicator { + color: #97BF41; + font-size: 18px; + opacity: 0; + transition: opacity 0.2s ease; + } + + .toggle-icon { + color: #049DD9; + font-size: 14px; + transition: transform 0.3s ease; + } + } + } + + // Chevron flips when row is open + &.active .tutorial-header .toggle-icon { + transform: rotate(180deg); + } + + // Active header gets a blue left accent + &.active .tutorial-header { + border-left: 3px solid #049DD9; + background: #e8f7fc; + } + + // Collapsed preview: thumbnail + description side by side + .tutorial-preview { + display: flex; + align-items: flex-start; + gap: 16px; + padding: 14px 18px 16px; + cursor: pointer; + + &:hover { + background: #fafafa; + } + + .tutorial-thumbnail { + flex-shrink: 0; + width: 200px; + height: auto; + border-radius: 4px; + object-fit: cover; + border: 1px solid #e0e0e0; + } + + .tutorial-description { + color: #666; + font-size: 14px; + line-height: 1.7; + margin: 0; + } + } + + // Hide preview when expanded + &.active .tutorial-preview { + display: none; + } + + // Expanded body: full-width video + mark complete + .tutorial-body { + display: none; + padding: 0 18px 18px; + + &.open { + display: block; + } + + .video-container { + position: relative; + width: 100%; + background: #000; + border-radius: 4px; + overflow: hidden; + margin-top: 14px; + margin-bottom: 14px; + + iframe, + > div { + width: 100% !important; + min-height: 360px; + border: none; + display: block; + } + } + + .tutorial-actions { + display: flex; + justify-content: flex-end; + + .btn-mark-complete { + background: transparent; + border: 2px solid #049DD9; + color: #049DD9; + padding: 8px 20px; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + font-weight: 600; + transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease; + line-height: 1.4; + + &:hover { + background: #e8f7fc; + } + + &.completed { + background: #97BF41; + border-color: #97BF41; + color: #fff; + + &:hover { + background: #82a836; + border-color: #82a836; + } + } + + i { + margin-right: 6px; + } + } + } + } + } +} + +// ── Responsive ──────────────────────────────────────────────────────────────── + +@media (max-width: 600px) { + .tutorials-hub { + .hub-header h1 { + font-size: 20px; + } + + .tutorial-row .tutorial-header { + padding: 12px 14px; + + .tutorial-title-area .tutorial-title { + font-size: 14px; + } + } + + .tutorial-row .tutorial-preview { + flex-direction: column; + padding: 12px 14px 14px; + + .tutorial-thumbnail { + width: 100%; + } + } + + .tutorial-row .tutorial-body { + padding: 0 14px 14px; + + .video-container iframe, + .video-container > div { + min-height: 220px; + } + } + } +} diff --git a/help/common/js/tutorials.js b/help/common/js/tutorials.js new file mode 100644 index 00000000..5b9bdd6e --- /dev/null +++ b/help/common/js/tutorials.js @@ -0,0 +1,181 @@ +// SciX Training Hub — tutorial progress & video management +// Uses the YouTube IFrame Player API for embedded video control. + +var STORAGE_KEY = 'scix-training-hub-progress'; +var players = {}; +var ytApiReady = false; +var pendingInit = null; + +// ── Progress tracking ───────────────────────────────────────────────────────── + +function getProgress() { + try { + return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); + } catch (e) { + return {}; + } +} + +function saveProgress(progress) { + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(progress)); + } catch (e) {} +} + +function updateProgressBar() { + var rows = document.querySelectorAll('.tutorial-row'); + var total = rows.length; + if (total === 0) return; + + var progress = getProgress(); + var completed = 0; + rows.forEach(function (row) { + if (progress[row.id] === true) completed++; + }); + + var pct = Math.round((completed / total) * 100); + + var bar = document.getElementById('progress-bar'); + var countEl = document.getElementById('completed-count'); + var pctEl = document.getElementById('progress-pct'); + + if (bar) { + bar.style.width = pct + '%'; + bar.setAttribute('aria-valuenow', pct); + } + if (countEl) countEl.textContent = completed; + if (pctEl) pctEl.textContent = pct; +} + +function setRowCompleted(tutorialId, isComplete) { + var row = document.getElementById(tutorialId); + var indicator = document.getElementById('indicator-' + tutorialId); + var btn = document.getElementById('btn-' + tutorialId); + + if (!row) return; + + if (isComplete) { + row.classList.add('completed'); + if (indicator) indicator.classList.add('completed'); + if (btn) { + btn.classList.add('completed'); + btn.innerHTML = ' Completed'; + } + } else { + row.classList.remove('completed'); + if (indicator) indicator.classList.remove('completed'); + if (btn) { + btn.classList.remove('completed'); + btn.innerHTML = ' Mark as Complete'; + } + } +} + +// Called by the "Mark as Complete" button +function toggleComplete(tutorialId) { + var progress = getProgress(); + progress[tutorialId] = !progress[tutorialId]; + saveProgress(progress); + setRowCompleted(tutorialId, progress[tutorialId]); + updateProgressBar(); +} + +// ── YouTube IFrame API ──────────────────────────────────────────────────────── + +// Called automatically by YouTube once its API script has loaded. +function onYouTubeIframeAPIReady() { + ytApiReady = true; + if (pendingInit) { + initPlayer(pendingInit.tutorialId, pendingInit.videoId); + pendingInit = null; + } +} + +function pauseAllPlayers(exceptId) { + Object.keys(players).forEach(function (id) { + if (id === exceptId) return; + try { + var p = players[id]; + if (p && typeof p.pauseVideo === 'function') { + p.pauseVideo(); + } + } catch (e) {} + }); +} + +function initPlayer(tutorialId, videoId) { + // Don't create a second player for the same row + if (players[tutorialId]) return; + + players[tutorialId] = new YT.Player('player-' + tutorialId, { + height: '360', + width: '100%', + videoId: videoId, + playerVars: { + playsinline: 1, + rel: 0, + modestbranding: 1 + }, + events: { + onStateChange: function (event) { + if (event.data === YT.PlayerState.PLAYING) { + // Pause every other player + pauseAllPlayers(tutorialId); + } + if (event.data === YT.PlayerState.ENDED) { + // Auto-mark complete when the video finishes + var progress = getProgress(); + if (!progress[tutorialId]) { + progress[tutorialId] = true; + saveProgress(progress); + setRowCompleted(tutorialId, true); + updateProgressBar(); + } + } + } + } + }); +} + +// ── Accordion ───────────────────────────────────────────────────────────────── + +function toggleTutorial(tutorialId) { + var row = document.getElementById(tutorialId); + var body = document.getElementById('body-' + tutorialId); + if (!row || !body) return; + + var isOpen = body.classList.contains('open'); + + // Collapse all rows and pause all players + document.querySelectorAll('.tutorial-row').forEach(function (r) { + r.classList.remove('active'); + }); + document.querySelectorAll('.tutorial-body').forEach(function (b) { + b.classList.remove('open'); + }); + pauseAllPlayers(null); + + if (!isOpen) { + row.classList.add('active'); + body.classList.add('open'); + + var videoId = row.getAttribute('data-video-id'); + if (videoId) { + if (ytApiReady) { + initPlayer(tutorialId, videoId); + } else { + pendingInit = { tutorialId: tutorialId, videoId: videoId }; + } + } + } +} + +// ── Initialise on page load ─────────────────────────────────────────────────── + +document.addEventListener('DOMContentLoaded', function () { + var progress = getProgress(); + document.querySelectorAll('.tutorial-row').forEach(function (row) { + setRowCompleted(row.id, progress[row.id] === true); + }); + updateProgressBar(); +}); diff --git a/scixhelp/tutorials/index.html b/scixhelp/tutorials/index.html new file mode 100644 index 00000000..92180386 --- /dev/null +++ b/scixhelp/tutorials/index.html @@ -0,0 +1,99 @@ +--- +layout: tutorials_layout_scix +title: "SciX Training Hub" +extra_css: /help/common/css/tutorials.css +--- + +
+ + +
+

Welcome to the SciX Training Hub

+
+
+ 0 + of {{ site.data.tutorials | size }} complete  —  + 0% +
+
+
+
+
+
+ + +
+ {% for tutorial in site.data.tutorials %} + {% assign idx = forloop.index %} + +
+ +
+
+ Tutorial {{ idx }} + {% if tutorial.is_new %} + New + {% endif %} +

{{ tutorial.title }}

+
+ +
+ + + + +
+
+ + +
+ Thumbnail for {{ tutorial.title }} +

{{ tutorial.description }}

+
+ + +
+
+
+
+ +
+ +
+
+
+ + {% endfor %} +
+ +