feat(components): shared grid, detail panel, stats tiles and entity modals - #7
Open
laksamanakeris wants to merge 7 commits into
Open
laksamanakeris wants to merge 7 commits into
laksamanakeris wants to merge 7 commits into
Conversation
One tabular renderer for the seam's ResultSet: column headers from the result, one keyed row per RecordRow, one cell per string. Pure presentation. Every wire scalar already arrives as a display string, so the grid never parses. Sorting or typed comparison cannot be recovered from display strings like "14,820" or "—", so that waits on the model carrying raw values rather than on this component. ResultSet is now rectangular and uniquely keyed by construction. Its fields are private and ResultSet::new rejects a row whose width differs from the header, or a duplicate row id, with UnexpectedColumns, the same error the seam decoder raises for a short row. Without the width check a decoder that dropped a cell would render every later value under the wrong heading, which a user cannot distinguish from what the database holds. Without the id check a decoder keying rows off a data column would hand Dioxus duplicate sibling keys, which panic in debug and silently reuse the wrong node in release. Both checks fail their tests when removed. Deserialize is dropped from ResultSet since it would bypass the constructor and nothing deserializes a model. Headers are keyed by ordinal, not by name: SQL allows duplicate column names and the same duplicate-key panic applies. The Query workbench is the first consumer. Its inline table had hardcoded headers and an unkeyed row loop, and its toolbar and footer repeated the row count, elapsed time and scan size as literals. The rows are now a ResultSet with seam-shaped ids and the counters derive from it. The view is still a static placeholder: reading run_query through the seam also needs the loading, empty and error states this view does not render, so the placeholder carries an unwired marker naming that condition, and a render test pins it at seven rows so a ragged edit fails the suite instead of rendering a blank grid. Render tests assert whole rows verbatim and contiguous, so column order, row order and header-to-cell alignment are pinned rather than presence, plus zero rows and duplicate column names.
One header/body/footer shell for the Explorer's 8 engine viewers. The header (title, freshness chip, close) is identical across the mockup; body and footer actions differ per viewer and arrive as slots, the same shape LiveTail uses. Freshness is a display string plus a stale flag: the staleness rule needs a fetch time the seam does not carry yet. The close control is a button, not the mockup's span, for keyboard and screen-reader access.
ModalKind gains the seven entity-form variants (document, strict, vector, graph node, graph edge, KV, spatial) and ConfirmDelete, each with a ModalHost arm. Bodies match the mockup and show its fixture record. Save and Delete only close the modal: no seam method writes a record yet, and nothing selects one until the Explorer's master-detail lands. The variants carry no payload until then. The Explorer header's create buttons now follow the storage mode: + Insert for document/strict/vector/KV, + Node and + Edge for graph, + Feature for spatial, none for timeseries (append-only) or FTS (writes happen on the parent). The mapping is an exhaustive match in insert_actions, so a new StorageMode must decide its create flow. Confirm-delete keeps Delete permanently disabled until the typed word is "delete"; surrounding whitespace is forgiven, case is not. Modal takes an optional width in px for the mockup's per-form widths. The edge form's From/type/To row uses a three-column form-row; the mockup's two-column row wrapped To onto its own line.
ModalKind::Preferences now carries a PrefsPane, so a trigger picks the pane it opens. The avatar popover's "Keyboard shortcuts" and "About" items land on those panes instead of Appearance; every other trigger (⌘, palette, connection manager) opens Appearance as before. The open pane lives in the modal signal alone. A sidebar click re-sets ModalKind::Preferences(pane) rather than updating a second, local signal, so a deep-link and a click cannot disagree. The string keys and their _ => fallback give way to exhaustive matches on PrefsPane.
The mockup's engine viewers share one stats pane: a grid of stat cards (label, value, sub-line) and titled sections that frame a chart or a breakdown. StatCard and StatSection own that markup and CSS once. The chart inside a section differs per viewer and arrives as its body; the viewers adopt these when their stats vtab lands.
Adding insert_actions_match_the_mockup_per_mode split the doc comment from selection_is_kept_only_while_the_collection_exists, leaving the new test with both explanations and the old one with none.
ResultSet::new and the decoder's Row::field raised UnexpectedColumns
for a ragged row or a duplicate row id. That variant describes a
missing column (the SHOW session-variable fallback), so the message
pointed the user at the columns when the fault was in the rows:
"unexpected result columns: expected [unique row ids], got [...]".
MalformedRows { reason } now carries those cases, non-retriable, with
a message that names the row: "result rows do not match their header:
row r2 has 1 cells, expected 2". Missing-column checks keep
UnexpectedColumns. Its dead_code allow is dropped: ResultSet no
longer constructs it, but clippy passes without the allow.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds the shared pieces every screen tier composes from, once: a data grid, a detail panel, stats tiles, and the 8 entity and confirm-delete modals. Still 100% mock. No modal writes anything yet.
-D warnings, nextest, all cleanResultSet(fields now private),ModalKind::Preferences(now carries a pane), and a newStudioError::MalformedRowsvariant. The compiler flags every site of all three.The design choice to accept or reject
The modals are static, and their Save and Delete buttons only close them.
Two things a real submit needs do not exist yet:
mark_all_readis the only write onConnectionService. Addingupsert_record/delete_recordnow would fix their shape before any screen selects a record, and the seam is an ask-first change.ModalKindare unit variants, and each body shows the mockup's fixture record. For example, the Strict form reads "orders / 442003" whichever collection is open.Both arrive with the Explorer work. The record then travels in
ModalKind, and submit goes throughuse_action. Every placeholder is markedSEAM-UNWIRED. If you want the write methods designed now instead, say so here, because the Explorer builds on this.Where to spend review time
1.
models/workbench.rs,ResultSet::new, and the newMalformedRowserror. Fields are private now. The constructor rejects a row whose width differs from the header, and a duplicate row id. Without the width check, a decoder that drops one cell renders every later value under the wrong heading. A user cannot tell that from real data. Without the id check, duplicate Dioxus keys panic in debug builds and reuse the wrong node in release builds.Deserializeis gone because it would bypass the check.Both rejections raise a new
StudioError::MalformedRows { reason }, non-retriable.UnexpectedColumnsmeans a missing column (theSHOWsession-variable fallback), and reusing it pointed the user at the columns when the fault was in the rows. The decoder's short-row check inservices/decode.rsmoves toMalformedRowsfor the same reason. Missing-column checks keepUnexpectedColumns. A newStudioErrorvariant touches the type every seam method returns, so confirm you are fine with it.2.
views/explorer/view.rs,insert_actions. The header's create buttons now follow the storage mode, as the mockup does:+ Insert+ Nodeand+ Edge+ FeatureIt is an exhaustive match, so a new
StorageModedoes not compile until someone decides its create flow.3.
modals/preferences.rs, one source of truth for the open pane.ModalKind::Preferences(PrefsPane)carries the pane, and a sidebar click re-sets the modal signal. No second local signal exists that a deep-link could disagree with. The avatar popover's "Keyboard shortcuts" and "About" items now land on those panes. The old string keys and their_ =>fallback are gone.One thing I want your call on
Where do the create buttons live? This PR puts them in the viewer header, next to Schema, Indexes and Export, matching the mockup's per-engine toolbars. The plan for the Explorer folds header actions into a single ⋯ more-actions button. If that stands, only the render site of
insert_actionsmoves. The mapping stays. Worth settling before the Explorer work starts.Smaller changes
Modaltakes an optionalwidthin px for the mockup's per-form widths (540 / 560 / 640 / 420).delete. Surrounding whitespace is forgiven. Case is not.form-row. The mockup's two-column row wrapped "To" onto its own line. This is the one deliberate divergence from the mockup.DetailPanelandStatCard/StatSectionare not used by a screen yet. The engine viewers adopt them when their list-detail layout and stats tab land.cargo run -p nodedb-studioto review. Runningtarget/debug/nodedb-studiodirectly from the workspace root rendered with no stylesheet. The cause is unconfirmed, and this PR does not change it.