feat: add struts learning tracker
This commit is contained in:
111
web/index.jsp
111
web/index.jsp
@@ -199,6 +199,28 @@
|
||||
color: var(--muted);
|
||||
text-align: center;
|
||||
}
|
||||
.tracker {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.tracker-item {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-start;
|
||||
padding: 12px 14px;
|
||||
border-radius: 16px;
|
||||
border: 1px solid var(--line);
|
||||
background: rgba(255,255,255,0.5);
|
||||
}
|
||||
.tracker-item input {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.tracker-item strong {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
@media (max-width: 1100px) {
|
||||
.layout { grid-template-columns: 1fr; }
|
||||
.grid, .pipeline, .metrics { grid-template-columns: 1fr 1fr; }
|
||||
@@ -229,6 +251,8 @@
|
||||
<div class="metric"><span>Guide pages</span><strong>4</strong></div>
|
||||
<div class="metric"><span>Demo forms</span><strong>3</strong></div>
|
||||
<div class="metric"><span>JSON endpoints</span><strong>2</strong></div>
|
||||
<div class="metric"><span>Core lab track</span><strong>4</strong></div>
|
||||
<div class="metric"><span>Completed labs</span><strong id="completedLabs">0</strong></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -257,9 +281,67 @@
|
||||
<a class="link-btn" href="api/users">JSON endpoint</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="eyebrow">Lab tracker</div>
|
||||
<h2>Mark the demos you have finished</h2>
|
||||
<div class="tracker">
|
||||
<label class="tracker-item">
|
||||
<input type="checkbox" data-track="hello">
|
||||
<span>
|
||||
<strong>Hello action</strong>
|
||||
See the smallest request -> action -> result path.
|
||||
</span>
|
||||
</label>
|
||||
<label class="tracker-item">
|
||||
<input type="checkbox" data-track="login">
|
||||
<span>
|
||||
<strong>Login flow</strong>
|
||||
Follow validation, action errors, and success routing.
|
||||
</span>
|
||||
</label>
|
||||
<label class="tracker-item">
|
||||
<input type="checkbox" data-track="validation">
|
||||
<span>
|
||||
<strong>Validation flow</strong>
|
||||
Compare invalid and valid submissions.
|
||||
</span>
|
||||
</label>
|
||||
<label class="tracker-item">
|
||||
<input type="checkbox" data-track="json">
|
||||
<span>
|
||||
<strong>JSON and AJAX</strong>
|
||||
Finish by explaining how Struts can return API-style payloads.
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
|
||||
<main class="content">
|
||||
<section class="card">
|
||||
<div class="eyebrow">Request lifecycle</div>
|
||||
<h2>How to explain Struts2 with one mental model</h2>
|
||||
<div class="pipeline">
|
||||
<div class="step">
|
||||
<strong>1. Request enters</strong>
|
||||
<p>The browser sends a URL and optional form fields or query params.</p>
|
||||
</div>
|
||||
<div class="step">
|
||||
<strong>2. Action mapping</strong>
|
||||
<p><code>struts.xml</code> resolves the action name and target class.</p>
|
||||
</div>
|
||||
<div class="step">
|
||||
<strong>3. Parameter binding</strong>
|
||||
<p>Struts populates action properties before <code>execute()</code> runs.</p>
|
||||
</div>
|
||||
<div class="step">
|
||||
<strong>4. Result routing</strong>
|
||||
<p>The returned result name decides which JSP or JSON renderer will respond.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="eyebrow">Catalog</div>
|
||||
<h2>Interactive demos and guides</h2>
|
||||
@@ -385,6 +467,33 @@
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const cards = Array.from(document.querySelectorAll('.demo-card'));
|
||||
const emptyState = document.getElementById('emptyState');
|
||||
const trackerItems = Array.from(document.querySelectorAll('[data-track]'));
|
||||
const trackerKey = 'struts_demo_tracker';
|
||||
|
||||
function getTrackerState() {
|
||||
return JSON.parse(localStorage.getItem(trackerKey) || '{}');
|
||||
}
|
||||
|
||||
function renderTracker() {
|
||||
const state = getTrackerState();
|
||||
let completed = 0;
|
||||
trackerItems.forEach((item) => {
|
||||
item.checked = Boolean(state[item.dataset.track]);
|
||||
if (item.checked) {
|
||||
completed += 1;
|
||||
}
|
||||
});
|
||||
document.getElementById('completedLabs').textContent = completed;
|
||||
}
|
||||
|
||||
trackerItems.forEach((item) => {
|
||||
item.addEventListener('change', () => {
|
||||
const state = getTrackerState();
|
||||
state[item.dataset.track] = item.checked;
|
||||
localStorage.setItem(trackerKey, JSON.stringify(state));
|
||||
renderTracker();
|
||||
});
|
||||
});
|
||||
|
||||
searchInput.addEventListener('input', () => {
|
||||
const keyword = searchInput.value.trim().toLowerCase();
|
||||
@@ -398,6 +507,8 @@
|
||||
});
|
||||
emptyState.style.display = visible ? 'none' : 'block';
|
||||
});
|
||||
|
||||
renderTracker();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -106,6 +106,28 @@
|
||||
<a class="link-btn" href="../upload/index.jsp">Open upload demo</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card">
|
||||
<div class="eyebrow">What happened</div>
|
||||
<h2>Map this result back to the Struts stack</h2>
|
||||
<div class="stats">
|
||||
<div class="stat">
|
||||
<span>Action mapping</span>
|
||||
<strong>login</strong>
|
||||
<p>The request matched the <code>login</code> action in <code>struts.xml</code>.</p>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span>Execute result</span>
|
||||
<strong>SUCCESS</strong>
|
||||
<p>The action returned the success result instead of redisplaying the form.</p>
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span>Rendered view</span>
|
||||
<strong>/user/success.jsp</strong>
|
||||
<p>The result mapping selected this JSP to present the post-action summary.</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -81,6 +81,19 @@
|
||||
<a class="link-btn" href="form.jsp">Try validation again</a>
|
||||
<a class="link-btn" href="../index.jsp">Back to portal</a>
|
||||
</div>
|
||||
|
||||
<div class="stats">
|
||||
<div class="stat">
|
||||
<span>Rule path</span>
|
||||
<strong>validate()</strong>
|
||||
The action accepted every field, so execution continued to the success result.
|
||||
</div>
|
||||
<div class="stat">
|
||||
<span>Teaching focus</span>
|
||||
<strong>Input before logic</strong>
|
||||
Compare this page with the form error state to explain why validation runs before business output.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user