fix: redesign SQL console UX, add data model context, fix lab bugs#49
Conversation
- Redesign SQL Console: always-visible terminal-style panel with Primary/Replica toggle buttons, mysql> prompt, clear button, green-on-black output theme - Add Data Model section to LAB explaining the university enrollment schema before Task 1 - Strip \G suffix from SQL queries (mysql CLI feature, not valid SQL) - Fix wrong table alias in LAB SQL example (c.code -> code) - Use approximate row count (~10,000) in LAB text
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughThe SQL console interface was restructured from a collapsible details section with radio-button target selection to a non-collapsible layout with toggle-button targets and dedicated header controls. Corresponding backend query sanitization was added to strip trailing punctuation and whitespace, and adjust multi-statement detection. Documentation was updated to describe the shared dataset. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR modernizes the Module 10 database visualizer’s SQL console UI and aligns the lab content with the visualizer’s behavior, while also addressing a couple of SQL-console-related bugs.
Changes:
- Redesigned the SQL console UI (header, target toggle buttons, prompt, clear button) with updated styling.
- Improved SQL execution handling in the backend by stripping MySQL CLI
\Gformatting. - Updated lab documentation (new Data Model section + corrected example SQL and row-count narrative).
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| 10-databases/visualizer/style.css | Restyled bottom bar + rebuilt SQL console layout and colors. |
| 10-databases/visualizer/index.html | Replaced collapsible <details> console with a persistent console header/body/input layout. |
| 10-databases/visualizer/app.js | Updated console target selection logic; added target toggle and clear-output behavior. |
| 10-databases/visualizer/server.py | Added \G suffix stripping and adjusted multi-statement rejection logic. |
| 10-databases/visualizer/LAB-VISUALIZER.md | Added data model context + fixed SQL examples and approximate row-count text. |
| # Strip mysql CLI formatting suffix (\G) -- not valid SQL | ||
| query = query.rstrip(";").rstrip() | ||
| if query.endswith("\\G"): | ||
| query = query[:-2].rstrip() | ||
|
|
||
| # Reject multi-statement queries | ||
| if ";" in query.rstrip(";"): | ||
| if ";" in query: | ||
| return {"error": "Multi-statement queries are not allowed"} |
There was a problem hiding this comment.
Current \G stripping happens after stripping only trailing ';', so a query like SELECT 1;\G becomes SELECT 1; after removing \G and then gets rejected by the multi-statement check. Strip the \\G suffix first (or strip it and then remove a trailing semicolon) before checking for internal semicolons.
| /* --- Bottom Bar --- */ | ||
| .bottom-bar { | ||
| background: var(--color-bg-surface); | ||
| border-top: 1px solid var(--color-border); | ||
| padding: 0.75rem 1rem; | ||
| max-height: var(--bottom-bar-height); | ||
| height: 260px; | ||
| } |
There was a problem hiding this comment.
.bottom-bar now uses a hard-coded height: 260px, but --bottom-bar-height is still defined at the top of the file and no longer used anywhere. Consider switching back to height/max-height based on var(--bottom-bar-height) (and updating the variable if needed) to keep sizing configurable and avoid an unused design token.
| .sql-console { | ||
| flex: 1; | ||
| min-width: 0; | ||
| display: flex; | ||
| flex-direction: column; | ||
| background: #0a0e17; | ||
| border-radius: var(--radius-lg); | ||
| border: 1px solid var(--color-border); | ||
| overflow: hidden; | ||
| } | ||
|
|
||
| .sql-console details { | ||
| height: 100%; | ||
| .console-header { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 0.75rem; | ||
| padding: 6px 12px; | ||
| background: rgba(255, 255, 255, 0.03); | ||
| border-bottom: 1px solid var(--color-border); | ||
| } | ||
|
|
||
| .sql-console summary { | ||
| cursor: pointer; | ||
| font-size: 0.9rem; | ||
| .console-title { | ||
| font-size: 0.8rem; | ||
| font-weight: 600; | ||
| color: var(--color-text-muted); | ||
| text-transform: uppercase; | ||
| letter-spacing: 0.05em; | ||
| margin-bottom: 0.5rem; | ||
| } | ||
|
|
||
| .console-body { | ||
| .console-target-toggle { | ||
| display: flex; | ||
| flex-direction: column; | ||
| gap: 0.5rem; | ||
| background: var(--color-bg); | ||
| border-radius: 6px; | ||
| padding: 2px; | ||
| gap: 2px; | ||
| } | ||
|
|
||
| .console-target { | ||
| display: flex; | ||
| gap: 1rem; | ||
| font-size: 0.8rem; | ||
| .target-btn { | ||
| padding: 3px 12px; | ||
| border: none; | ||
| border-radius: 4px; | ||
| background: transparent; | ||
| color: var(--color-text-muted); | ||
| font-size: 0.75rem; | ||
| font-weight: 500; | ||
| cursor: pointer; | ||
| transition: all 0.15s ease; | ||
| } | ||
|
|
||
| .console-target label { | ||
| display: flex; | ||
| align-items: center; | ||
| gap: 4px; | ||
| .target-btn.active { | ||
| background: var(--color-primary); | ||
| color: #fff; | ||
| } | ||
|
|
||
| .target-btn:hover:not(.active) { | ||
| color: var(--color-text); | ||
| } | ||
|
|
||
| .console-clear-btn { | ||
| margin-left: auto; | ||
| padding: 2px 8px; | ||
| border: 1px solid var(--color-border); | ||
| border-radius: 4px; | ||
| background: transparent; | ||
| color: var(--color-text-muted); | ||
| font-size: 0.7rem; | ||
| cursor: pointer; | ||
| transition: all 0.15s ease; | ||
| } | ||
|
|
||
| .console-clear-btn:hover { | ||
| color: var(--color-error); | ||
| border-color: var(--color-error); | ||
| } | ||
|
|
||
| .console-output { | ||
| font-family: var(--font-mono); | ||
| font-size: 0.75rem; | ||
| background: var(--color-bg); | ||
| border-radius: var(--radius); | ||
| padding: 0.5rem; | ||
| max-height: 100px; | ||
| background: #0a0e17; | ||
| padding: 8px 12px; | ||
| flex: 1; | ||
| overflow-y: auto; | ||
| white-space: pre-wrap; | ||
| color: var(--color-text); | ||
| color: #a3e635; | ||
| } |
There was a problem hiding this comment.
This redesign introduces several hard-coded hex colors (e.g. #0a0e17, #a3e635, #22d3ee) while the rest of the theme uses CSS variables (e.g. --color-*). To keep the theme consistent and easier to adjust later, consider defining console-specific variables (or reusing existing tokens) instead of embedding raw hex values throughout these rules.
| <div class="console-target-toggle"> | ||
| <button class="target-btn active" data-target="primary">Primary</button> | ||
| <button class="target-btn" data-target="replica">Replica</button> |
There was a problem hiding this comment.
The Primary/Replica toggle is implemented as two buttons with only a visual "active" state. For accessibility, consider adding proper pressed/selected semantics (e.g. aria-pressed on each button, or a radiogroup pattern) so screen readers can determine which target is selected.
| <div class="console-target-toggle"> | |
| <button class="target-btn active" data-target="primary">Primary</button> | |
| <button class="target-btn" data-target="replica">Replica</button> | |
| <div class="console-target-toggle" role="group" aria-label="SQL console target"> | |
| <button | |
| type="button" | |
| class="target-btn active" | |
| data-target="primary" | |
| aria-pressed="true" | |
| > | |
| Primary | |
| </button> | |
| <button | |
| type="button" | |
| class="target-btn" | |
| data-target="replica" | |
| aria-pressed="false" | |
| > | |
| Replica | |
| </button> |
| <div id="console-output" class="console-output"></div> | ||
| <div class="console-input-row"> | ||
| <span class="console-prompt">mysql></span> | ||
| <input type="text" id="console-input" placeholder="SELECT * FROM students LIMIT 5;" /> |
There was a problem hiding this comment.
The SQL input relies on placeholder text but has no associated <label>/accessible name. Add a <label for="console-input"> (can be visually hidden) or an aria-label so the field is discoverable for screen readers.
| <input type="text" id="console-input" placeholder="SELECT * FROM students LIMIT 5;" /> | |
| <input | |
| type="text" | |
| id="console-input" | |
| aria-label="SQL query input" | |
| placeholder="SELECT * FROM students LIMIT 5;" | |
| /> |
| $$('.target-btn').forEach(btn => { | ||
| btn.addEventListener('click', () => { | ||
| $$('.target-btn').forEach(b => b.classList.remove('active')); | ||
| btn.classList.add('active'); | ||
| const target = btn.dataset.target; | ||
| const prompt = $('.console-prompt'); | ||
| if (prompt) { | ||
| prompt.textContent = target === 'replica' ? 'replica>' : 'mysql>'; | ||
| } |
There was a problem hiding this comment.
When switching targets, the code updates CSS classes and the prompt text but does not expose the selected state to assistive tech. If you add aria-pressed/radiogroup semantics to the target buttons, also update the corresponding ARIA attributes here when toggling.
| $$('.target-btn').forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| $$('.target-btn').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| const target = btn.dataset.target; | |
| const prompt = $('.console-prompt'); | |
| if (prompt) { | |
| prompt.textContent = target === 'replica' ? 'replica>' : 'mysql>'; | |
| } | |
| const setConsoleTarget = (selectedBtn) => { | |
| $$('.target-btn').forEach(b => { | |
| const isSelected = b === selectedBtn; | |
| b.classList.toggle('active', isSelected); | |
| b.setAttribute('aria-pressed', isSelected ? 'true' : 'false'); | |
| }); | |
| const target = selectedBtn.dataset.target; | |
| const prompt = $('.console-prompt'); | |
| if (prompt) { | |
| prompt.textContent = target === 'replica' ? 'replica>' : 'mysql>'; | |
| } | |
| }; | |
| const activeTargetBtn = $('.target-btn.active'); | |
| $$('.target-btn').forEach(btn => { | |
| btn.setAttribute('aria-pressed', btn === activeTargetBtn ? 'true' : 'false'); | |
| btn.addEventListener('click', () => { | |
| setConsoleTarget(btn); |
| ## Data Model | ||
|
|
||
| The lab uses a **university enrollment** scenario pre-loaded with | ||
| sample data: | ||
|
|
||
| | Table | Rows | Description | | ||
| | --- | --- | --- | | ||
| | `students` | 10 | Name, email, major (e.g., Alice Johnson, Computer Science) | | ||
| | `courses` | 4 | CS101, CS201, MATH101, PHYS101 with capacity and enrolled count | | ||
| | `enrollments` | 10 | Links students to courses (foreign keys with unique constraint) | | ||
| | `access_log` | 10,000 | Simulated resource access records for indexing exercises | | ||
|
|
||
| This is the same data model used across all Module 10 labs (MySQL, | ||
| MongoDB, Cassandra), so you can compare how each database handles the | ||
| same scenario differently. |
There was a problem hiding this comment.
The UI change removed the collapsible <details> SQL Console, but the lab instructions still say "click SQL Console to expand it" (later in the doc). Please update those steps to match the new always-visible console so students don’t get stuck looking for an expand control.
Redesigned console, added Data Model section, fixed \G handling, SQL alias bug, and approximate row counts.
Summary by CodeRabbit
New Features
Documentation
UI/Style