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
132 changes: 132 additions & 0 deletions _layouts/workshop.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
---
layout: default
---

{%- comment -%}
Layout for the workshop and task-explorer pages: a nav rail on the left,
content on the right.

Inherits the site's `default` layout, so the header and footer still come from
MicrosoftLearning/Jekyll-Theme. Jekyll resolves local _layouts/ ahead of
remote_theme, and only pages that set `layout: workshop` use this one, so the
lab pages are unaffected.
{%- endcomment -%}

<style>
:root {
--wk-border: #e4e4e7;
--wk-muted: #6b7280;
--wk-accent: #1a45a5;
--wk-accent-bg: #e8f0fe;
--wk-surface: #fafafa;
}

.wk-shell { display: grid; grid-template-columns: 16rem minmax(0, 1fr); gap: 2.5rem;
align-items: start; }
@media (max-width: 900px) { .wk-shell { grid-template-columns: 1fr; gap: 1rem; } }

/* Nav rail */
.wk-rail { position: sticky; top: 1rem; font-size: .9em; padding-right: 1rem;
border-right: 1px solid var(--wk-border); }
@media (max-width: 900px) {
.wk-rail { position: static; border-right: none;
border-bottom: 1px solid var(--wk-border); padding: 0 0 1rem; }
}
.wk-rail h2 { font-size: .72em; letter-spacing: .09em; text-transform: uppercase;
color: var(--wk-muted); margin: 1.2rem 0 .5rem; border: none; padding: 0; }
.wk-rail h2:first-child { margin-top: 0; }
.wk-rail ul { list-style: none; margin: 0; padding: 0; }
.wk-rail li { margin: 0 0 .3rem; line-height: 1.35; }
.wk-rail a { text-decoration: none; color: inherit; display: block;
padding: .25rem .5rem; border-radius: 6px; }
.wk-rail a:hover { background: var(--wk-accent-bg); color: var(--wk-accent); }
.wk-rail a[aria-current="page"] { background: var(--wk-accent-bg); color: var(--wk-accent);
font-weight: 600; }
.wk-rail .wk-sub { color: var(--wk-muted); font-size: .85em; padding-left: .5rem;
display: block; }

/* Content */
.wk-content { min-width: 0; }
.wk-content h1 { margin-top: 0; }
.wk-content h2 { margin-top: 2.2rem; padding-bottom: .3rem;
border-bottom: 1px solid var(--wk-border); }
.wk-content table { border-collapse: collapse; }
.wk-content th, .wk-content td { border: 1px solid var(--wk-border);
padding: .4rem .7rem; font-size: .93em; }
.wk-content th { background: var(--wk-surface); text-align: left; }

/* Session cards, used by the workshop agenda */
.wk-lab { border: 1px solid var(--wk-border); border-radius: 8px;
padding: 1rem 1.25rem; margin: 0 0 1.25rem; }
.wk-lab h3 { margin-top: 0; }
.wk-meta { font-size: .85em; color: var(--wk-muted); margin: -.4rem 0 .9rem; }
.wk-step { display: grid; grid-template-columns: 1fr auto auto; gap: .75rem;
padding: .45rem 0; border-top: 1px solid #f1f1f3; align-items: baseline; }
.wk-step:first-of-type { border-top: none; }
.wk-badge { font-size: .72em; padding: .12em .5em; border-radius: 999px; white-space: nowrap; }
.wk-core { background: var(--wk-accent-bg); color: var(--wk-accent); }
.wk-setup { background: #f1f1f3; color: #52525b; }
.wk-locked { background: #fef3c7; color: #92400e; }
.wk-elapsed { color: var(--wk-muted); font-size: .85em; white-space: nowrap; }
.wk-optional-list { font-size: .92em; color: #3f3f46; margin-top: .85rem; }

/* Task explorer cards */
.ex-filters { display: flex; flex-wrap: wrap; gap: 1.25rem; margin: 1rem 0 1.5rem;
padding: .9rem 1rem; border: 1px solid var(--wk-border);
border-radius: 8px; font-size: .9em; }
.ex-filters label { display: block; font-size: .78em; text-transform: uppercase;
letter-spacing: .06em; color: var(--wk-muted); margin-bottom: .25rem; }
.ex-filters select { padding: .3rem .5rem; border: 1px solid #d4d4d8; border-radius: 6px; }
.ex-count { margin: 0 0 1rem; color: var(--wk-muted); font-size: .9em; }
.ex-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(19rem, 1fr)); gap: 1rem; }
.ex-card { border: 1px solid var(--wk-border); border-radius: 8px; padding: .9rem 1rem; }
.ex-card h4 { margin: 0 0 .35rem; font-size: 1em; }
.ex-card p { margin: .35rem 0 .6rem; font-size: .88em; color: #3f3f46; }
.ex-tags { display: flex; flex-wrap: wrap; gap: .3rem; }
.ex-tag { font-size: .72em; padding: .12em .5em; border-radius: 999px;
background: #f1f1f3; color: #52525b; }
.ex-tag.core { background: var(--wk-accent-bg); color: var(--wk-accent); }
.ex-tag.gated { background: #fef3c7; color: #92400e; }
.ex-bars { letter-spacing: .1em; }
.ex-empty { padding: 2rem; text-align: center; color: var(--wk-muted); }
</style>

<div class="wk-shell">
<nav class="wk-rail" aria-label="Workshop navigation">
{%- assign consolidated = site.pages | where_exp: "p", "p.url contains '/Instructions/Consolidated/'" -%}
{%- assign labs = consolidated | where_exp: "p", "p.lab.type == 'lab'" | sort: "lab.order" -%}
{%- assign all_tasks = consolidated | where_exp: "p", "p.lab.type == 'task'" -%}

<h2>Views</h2>
<ul>
<li><a href="{{ '/workshop.html' | relative_url }}"
{% if page.url contains 'workshop' %}aria-current="page"{% endif %}>Workshop agenda</a></li>
<li><a href="{{ '/explore.html' | relative_url }}"
{% if page.url contains 'explore' %}aria-current="page"{% endif %}>Task explorer</a></li>
<li><a href="{{ '/index.html' | relative_url }}">Lab catalogue</a></li>
</ul>

<h2>Labs</h2>
<ul>
{%- for lab in labs -%}
{%- assign lab_id = lab.lab.id -%}
{%- assign core = all_tasks | where_exp: "p", "p.lab.parent == lab_id" | where_exp: "p", "p.lab.section == 'core'" -%}
{%- assign mins = 0 -%}
{%- for t in core %}{% assign mins = mins | plus: t.lab.duration %}{% endfor -%}
<li>
<a href="{{ lab.url | relative_url }}">{{ lab.lab.title }}</a>
<span class="wk-sub">{{ mins }} min core</span>
</li>
{%- endfor -%}
</ul>

<h2>Data</h2>
<ul>
<li><a href="{{ '/labs.json' | relative_url }}">labs.json</a></li>
</ul>
</nav>

<main class="wk-content" id="main-content">
{{ content }}
</main>
</div>
129 changes: 129 additions & 0 deletions explore.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
---
title: Task explorer
permalink: explore.html
layout: workshop
sitemap: false
---

{%- comment -%}
Task explorer. Lists every consolidated task and filters client-side using the
data- attributes on each card.
{%- endcomment -%}

{%- assign consolidated = site.pages | where_exp: "p", "p.url contains '/Instructions/Consolidated/'" -%}
{%- assign labs = consolidated | where_exp: "p", "p.lab.type == 'lab'" | sort: "lab.order" -%}
{%- assign tasks = consolidated | where_exp: "p", "p.lab.type == 'task'" | sort: "lab.order" -%}

# Task explorer

Every task across the consolidated labs. Filter by lab, section, level, time or access to
find the ones that fit.

<div class="ex-filters" markdown="0">
<div>
<label for="f-lab">Lab</label>
<select id="f-lab">
<option value="">All labs</option>
{%- for lab in labs %}
<option value="{{ lab.lab.id }}">{{ lab.lab.title }}</option>
{%- endfor %}
</select>
</div>
<div>
<label for="f-section">Section</label>
<select id="f-section">
<option value="">Core and optional</option>
<option value="core">Core only</option>
<option value="optional">Optional only</option>
<option value="setup">Setup</option>
</select>
</div>
<div>
<label for="f-level">Level</label>
<select id="f-level">
<option value="">Any level</option>
<option value="200">L200</option>
<option value="300">L300</option>
<option value="400">L400</option>
</select>
</div>
<div>
<label for="f-time">Time</label>
<select id="f-time">
<option value="">Any length</option>
<option value="20">20 min or less</option>
<option value="30">30 min or less</option>
</select>
</div>
<div>
<label for="f-access">Access</label>
<select id="f-access">
<option value="">All tasks</option>
<option value="open">No extra access needed</option>
<option value="gated">Needs extra access</option>
</select>
</div>
</div>

<p class="ex-count" id="ex-count"></p>

<div class="ex-grid" id="ex-grid" markdown="0">
{%- for t in tasks -%}
{%- assign parent = labs | where_exp: "l", "l.lab.id == t.lab.parent" | first -%}
<article class="ex-card"
data-lab="{{ t.lab.parent }}"
data-section="{{ t.lab.section }}"
data-level="{{ t.lab.level }}"
data-duration="{{ t.lab.duration | default: 0 }}"
data-access="{{ t.lab.access }}">
<h4><a href="{{ t.url | relative_url }}">{{ t.lab.title }}</a></h4>
<p>{{ t.lab.description }}</p>
<div class="ex-tags">
<span class="ex-tag">{{ parent.lab.title }}</span>
<span class="ex-tag {% if t.lab.section == 'core' %}core{% endif %}">{{ t.lab.section }}</span>
{%- if t.lab.difficulty %}
<span class="ex-tag ex-bars">{% for i in (1..5) %}{% if i <= t.lab.difficulty %}&#9648;{% else %}&#9649;{% endif %}{% endfor %} L{{ t.lab.level }}</span>
{%- endif %}
{%- if t.lab.duration %}<span class="ex-tag">{{ t.lab.duration }} min</span>{% endif %}
{%- if t.lab.access == 'gated' %}<span class="ex-tag gated">needs access</span>{% endif %}
</div>
</article>
{%- endfor %}
</div>

<p class="ex-empty" id="ex-empty" style="display:none">No tasks match those filters.</p>

<script>
(function () {
var grid = document.getElementById('ex-grid');
var cards = Array.prototype.slice.call(grid.querySelectorAll('.ex-card'));
var count = document.getElementById('ex-count');
var empty = document.getElementById('ex-empty');
var filters = ['lab', 'section', 'level', 'time', 'access'].map(function (id) {
return document.getElementById('f-' + id);
});

function apply() {
var lab = filters[0].value, section = filters[1].value, level = filters[2].value;
var time = filters[3].value, access = filters[4].value;
var shown = 0;

cards.forEach(function (card) {
var d = card.dataset;
var ok = (!lab || d.lab === lab)
&& (!section || d.section === section)
&& (!level || d.level === level)
&& (!time || (parseInt(d.duration, 10) > 0 && parseInt(d.duration, 10) <= parseInt(time, 10)))
&& (!access || d.access === access);
card.style.display = ok ? '' : 'none';
if (ok) { shown++; }
});

count.textContent = shown + ' of ' + cards.length + ' tasks';
empty.style.display = shown === 0 ? '' : 'none';
}

filters.forEach(function (f) { f.addEventListener('change', apply); });
apply();
})();
</script>
59 changes: 59 additions & 0 deletions labs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
permalink: labs.json
layout: null
sitemap: false
---
{%- comment -%}
Machine-readable index of the consolidated labs, for anything that needs the
catalogue without parsing the pages.
{%- endcomment -%}
{%- assign consolidated = site.pages | where_exp: "p", "p.url contains '/Instructions/Consolidated/'" -%}
{%- assign labs = consolidated | where_exp: "p", "p.lab.type == 'lab'" | sort: "lab.order" -%}
{%- assign all_tasks = consolidated | where_exp: "p", "p.lab.type == 'task'" -%}
{
"labs": [
{%- for lab in labs -%}
{%- assign lab_id = lab.lab.id -%}
{%- assign tasks = all_tasks | where_exp: "p", "p.lab.parent == lab_id" | sort: "lab.order" -%}
{%- assign core_total = 0 -%}
{%- assign full_total = 0 -%}
{%- for t in tasks -%}
{%- if t.lab.duration -%}
{%- assign full_total = full_total | plus: t.lab.duration -%}
{%- if t.lab.section == 'core' %}{% assign core_total = core_total | plus: t.lab.duration %}{% endif -%}
{%- endif -%}
{%- endfor %}
{
"id": {{ lab.lab.id | jsonify }},
"order": {{ lab.lab.order }},
"title": {{ lab.lab.title | jsonify }},
"description": {{ lab.lab.description | jsonify }},
"level": {{ lab.lab.level }},
"difficulty": {{ lab.lab.difficulty }},
"status": {{ lab.lab.status | jsonify }},
"concepts": {{ lab.lab.concepts | split: ", " | jsonify }},
"url": {{ lab.url | relative_url | jsonify }},
"duration_core_minutes": {{ core_total }},
"duration_total_minutes": {{ full_total }},
"tasks": [
{%- for t in tasks %}
{
"order": {{ t.lab.order }},
"title": {{ t.lab.title | jsonify }},
"description": {{ t.lab.description | jsonify }},
"section": {{ t.lab.section | jsonify }},
"level": {{ t.lab.level }},
{% if t.lab.difficulty %}"difficulty": {{ t.lab.difficulty }},{% else %}"difficulty": null,{% endif %}
{% if t.lab.duration %}"duration_minutes": {{ t.lab.duration }},{% else %}"duration_minutes": null,{% endif %}
"access": {{ t.lab.access | jsonify }},
{% if t.lab.requires %}"requires": {{ t.lab.requires | jsonify }},{% else %}"requires": null,{% endif %}
{% if t.lab.verify %}"verify": {{ t.lab.verify | jsonify }},{% else %}"verify": null,{% endif %}
"concepts": {{ t.lab.concepts | split: ", " | jsonify }},
"url": {{ t.url | relative_url | jsonify }}
}{% unless forloop.last %},{% endunless %}
{%- endfor %}
]
}{% unless forloop.last %},{% endunless %}
{%- endfor %}
]
}
Loading
Loading