From 2b8bbee12e005c2fd11a2d0f9e1f610b60d1d969 Mon Sep 17 00:00:00 2001 From: Fredrik Moen Date: Mon, 11 May 2026 14:22:27 +0200 Subject: [PATCH] add HowToPage component with schema matching and versioning examples --- frontend/.dockerignore | 3 + frontend/src/App.jsx | 2 + frontend/src/components/NavBar.jsx | 1 + frontend/src/pages/HowToPage.jsx | 270 +++++++++++++++++++++++++++++ 4 files changed, 276 insertions(+) create mode 100644 frontend/.dockerignore create mode 100644 frontend/src/pages/HowToPage.jsx diff --git a/frontend/.dockerignore b/frontend/.dockerignore new file mode 100644 index 0000000..a0d218e --- /dev/null +++ b/frontend/.dockerignore @@ -0,0 +1,3 @@ +node_modules +dist +.env \ No newline at end of file diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index eaf7c18..db6a08a 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -5,6 +5,7 @@ import ChallengePage from './pages/ChallengePage' import AboutPage from './pages/AboutPage' import LoginPage from './pages/LoginPage' import RegisterPage from './pages/RegisterPage' +import HowToPage from './pages/HowToPage' function ProtectedRoute({ children }) { const { user } = useAuth() @@ -20,6 +21,7 @@ function AppRoutes() { } /> } /> } /> + } /> ) } diff --git a/frontend/src/components/NavBar.jsx b/frontend/src/components/NavBar.jsx index 0e5ff30..0bb77ca 100644 --- a/frontend/src/components/NavBar.jsx +++ b/frontend/src/components/NavBar.jsx @@ -24,6 +24,7 @@ export default function NavBar() { SchemaLab
+ How To About {user && ( <> diff --git a/frontend/src/pages/HowToPage.jsx b/frontend/src/pages/HowToPage.jsx new file mode 100644 index 0000000..d95150d --- /dev/null +++ b/frontend/src/pages/HowToPage.jsx @@ -0,0 +1,270 @@ +import { useState } from 'react' +import NavBar from '../components/NavBar' +import { Link } from 'react-router-dom' + +const box = { + background: '#1a202c', + border: '1px solid #2d3748', + borderRadius: 6, + padding: '14px 16px', +} + +const pre = { + background: '#0d1117', + border: '1px solid #2d3748', + borderRadius: 4, + padding: 10, + fontSize: 12, + color: '#e2e8f0', + margin: 0, + overflowX: 'auto', + whiteSpace: 'pre', +} + +const sectionTitle = { + fontSize: 16, + fontWeight: 600, + color: '#e2e8f0', + marginBottom: 12, +} + +const muted = { color: '#a0aec0', lineHeight: 1.7 } +const mutedSm = { color: '#a0aec0', lineHeight: 1.7, fontSize: 14 } + +function LangSwitcher({ lang, setLang }) { + return ( +
+ {['javascript', 'python'].map(l => ( + + ))} +
+ ) +} + +const matchingSolutions = { + javascript: `function matchSchemas(schemaA, schemaB) { + return [ + { source: 'order_id', target: 'orderId' }, + { source: 'client_name', target: 'customerName' }, + { source: 'dispatch_date', target: 'shipDate' }, + { source: 'total_cost', target: 'amount' }, + ] +}`, + python: `def match_schemas(schema_a, schema_b): + return [ + { 'source': 'order_id', 'target': 'orderId' }, + { 'source': 'client_name', 'target': 'customerName' }, + { 'source': 'dispatch_date', 'target': 'shipDate' }, + { 'source': 'total_cost', 'target': 'amount' }, + ]`, +} + +const versioningSolutions = { + javascript: `function migrate(record) { + const [username, domain] = record.displayName.split('@') + return { + id: record.id, + username, + domain, + role: record.role, + } +}`, + python: `def migrate(record): + username, domain = record['displayName'].split('@') + return { + 'id': record['id'], + 'username': username, + 'domain': domain, + 'role': record['role'], + }`, +} + +export default function HowToPage() { + const [matchLang, setMatchLang] = useState('javascript') + const [versionLang, setVersionLang] = useState('javascript') + + return ( + <> + +
+

+ How To +

+

+ A quick introduction to schema matching and schema versioning, with worked examples. +

+ + {/* ── SCHEMA MATCHING ── */} +
+

Schema Matching

+

+ Schema matching is the problem of identifying which fields in one schema correspond + to fields in another, even when they use different names or structures. Your function + receives both schemas and should return an array of {'{ source, target }'} pairs. +

+ + {/* Schemas side by side */} +
+ {[ + { + label: 'Schema A — Warehouse', + fields: ['order_id', 'client_name', 'dispatch_date', 'total_cost'], + }, + { + label: 'Schema B — Finance', + fields: ['orderId', 'customerName', 'shipDate', 'amount'], + }, + ].map(({ label, fields }) => ( +
+
{label}
+ {fields.map(f => ( +
{f}
+ ))} +
+ ))} +
+ + {/* Expected output */} +
+
Expected output
+ {[ + ['order_id', 'orderId'], + ['client_name', 'customerName'], + ['dispatch_date', 'shipDate'], + ['total_cost', 'amount'], + ].map(([src, tgt]) => ( +
+ {src} + + {tgt} +
+ ))} +
+ + {/* Example solution */} +
+
+
Example solution
+ +
+
{matchingSolutions[matchLang]}
+
+

+ In real challenges the mapping won't be this obvious. Fields may be abbreviated, + reordered, or use different conventions. Use the schema descriptions and field names + to reason about which fields represent the same concept. +

+
+ + {/* ── SCHEMA VERSIONING ── */} +
+

Schema Versioning

+

+ Schema versioning is about migrating records when a schema evolves over time. + Your function receives a single record in the old format and should return + it transformed into the new format. +

+ + {/* Schemas side by side */} +
+ {[ + { label: 'Version 1', fields: ['id', 'displayName', 'role'] }, + { label: 'Version 2', fields: ['id', 'username', 'domain', 'role'] }, + ].map(({ label, fields }) => ( +
+
{label}
+ {fields.map(f => ( +
{f}
+ ))} +
+ ))} +
+ + {/* Input / output */} +
+
Example record migration
+
+ {[ + { + label: 'Input (v1)', + code: `{ + "id": 1, + "displayName": "johndoe@company.com", + "role": "admin" +}`, + }, + { + label: 'Output (v2)', + code: `{ + "id": 1, + "username": "johndoe", + "domain": "company.com", + "role": "admin" +}`, + }, + ].map(({ label, code }) => ( +
+
{label}
+
{code}
+
+ ))} +
+
+ + {/* Example solution */} +
+
+
Example solution
+ +
+
{versioningSolutions[versionLang]}
+
+

+ Challenges may involve multiple transformations at once, such as merging values, + changing data types, or deriving new fields from existing ones. +

+
+ + {/* CTA */} +
+

Ready to start?

+ + Browse challenges → + +
+
+ + ) +} \ No newline at end of file