Skip to content

feat(sambawiz): replace MUI with shadcn/ui + SWR data caching#11

Open
soufianechami wants to merge 9 commits into
sambanova:mainfrom
soufianechami:feat/remove-mui
Open

feat(sambawiz): replace MUI with shadcn/ui + SWR data caching#11
soufianechami wants to merge 9 commits into
sambanova:mainfrom
soufianechami:feat/remove-mui

Conversation

@soufianechami

Copy link
Copy Markdown

What this PR does

This PR replaces Material UI with shadcn/ui across the entire sambawiz frontend and introduces SWR-based data caching to eliminate redundant Kubernetes API calls. The business logic — every API route, kubectl/helm command, deployment flow, and data interface — is unchanged. This is a UI layer and data-fetching layer change only.


Why we did this

The current MUI implementation has a few real problems that motivated this work:

1. Redundant API calls on every tab switch
Every time a user switches between Bundle Deployment and Playground, the app fires fresh kubectl calls to re-fetch data it already has. With slow clusters this means 2–5 second loading spinners for data that hasn't changed. Users reported this as laggy.

2. Double /api/kubeconfig-validate on startup
AppContext and Home.tsx both independently fetched the same endpoint on mount — two kubectl invocations for the same data, every page load.

3. Hydration mismatches in production
The <Suspense> boundary wrapping the entire Home page caused base-ui useId() to generate different IDs on the server vs client, producing console errors and potential flicker on every load.

4. MUI bundle weight
MUI + Emotion adds ~150–200KB gzipped to the client bundle. For a management tool that runs inside enterprise clusters (sometimes with restricted bandwidth), this matters.


What improved

Performance

  • SWR stable cache (revalidateOnFocus: false, revalidateOnReconnect: false, revalidateIfStale: false): data is fetched once per session and served from cache on tab switches. K8s cluster state doesn't change every second — polling it constantly added latency without value.
  • Shared cache keys: /api/bundle-deployment is used by both Bundle Deployment and Playground. Visiting one tab pre-populates the other — instant switch, zero extra API calls.
  • Deduped startup call: AppContext now owns /api/kubeconfig-validate via SWR. Any component that needs the same data reads from the same cache key automatically. One network call, many consumers.
  • Manual refresh is always available via dedicated Refresh buttons (mutate()) — users stay in control.

UI

  • Replaced MUI with shadcn/ui (base-nova style, Tailwind CSS v4 with OKLCH color space)
  • Brand purple #622B86 applied consistently via CSS variables
  • Sidebar restructured to match shadcn dashboard-01 pattern:
    • Main tools: Bundle Builder, Bundle Deployment, Playground
    • Separate Configuration section with an Environment tab (was the hidden "Home" page)
    • External links at the bottom: Documentation, Community, Contact Us
  • Footer shows active environment name, version, and namespace — informational only

Bug fixes

  • Fixed hydration mismatch: isolated useSearchParams() into a narrow <Suspense> boundary
  • Removed 'use client' from providers.tsx — no longer needed, was causing the hydration issue
  • Fixed base-nova onValueChange: string | null typing across all Select handlers

Tradeoffs to be aware of

Stale data by design
With SWR caching, tab switches show cached data rather than live data. This is intentional — the Refresh button gives users explicit control. If the team prefers always-fresh data on tab switch, revalidateIfStale can be set back to true in one place (SWR_STABLE constant) without touching any other code.

Learning curve for contributors
MUI's sx prop and theme system are replaced by Tailwind utility classes and shadcn's component API. New contributors extending the UI will need familiarity with Tailwind v4 and shadcn conventions. The shadcn docs are excellent and components live as readable source files in components/ui/.

package.json conflict with PR #10
PR #10 (CLI tool) also modifies package.json. The conflict is in different sections (CLI deps vs UI deps) and is straightforward to resolve during merge.


Risk assessment

Area Risk Notes
Business logic None API routes, kubectl commands, data interfaces unchanged
Visual regression Low All functionality preserved; UI looks different by design
SWR caching behavior Low–Medium Intentional tradeoff; one flag to revert if needed
Bundle size Positive ~150KB gzipped reduction (removed MUI + Emotion runtime)
Open PRs conflict Low Only package.json overlap with PR #10, different sections
Contributor onboarding Low–Medium Tailwind/shadcn learning curve, mitigated by excellent docs

Testing checklist

  • Switch between all 4 tabs — no redundant API calls in server logs after initial load
  • Click Refresh button in Bundle Deployment — triggers a single fresh fetch
  • Browser console — no hydration mismatch errors
  • Full deployment flow: configure env → build bundle → deploy → test in Playground
  • Sidebar collapses to icon mode correctly
  • Environment tab navigates to environment setup page
  • External links open in new tab

Files changed (summary)

  • package.json — removed @mui/material, @mui/icons-material, @emotion/react, @emotion/styled, @emotion/cache; added swr, shadcn/ui components
  • app/globals.css — Tailwind v4 theme with brand OKLCH color variables
  • app/components/AppLayout.tsx — sidebar rewrite
  • app/components/Home.tsx, BundleDeploymentManager.tsx, Playground.tsx — MUI → shadcn, SWR caching
  • context/AppContext.tsx — migrated to SWR
  • components/ui/ — shadcn component library (source files, no external runtime)

🤖 Generated with Claude Code

soufianechami and others added 9 commits April 6, 2026 19:03
…rformance

- Initialize shadcn/ui with brand colors (primary #622B86 → oklch(0.38 0.18 296))
  mapped to CSS variables in globals.css; all 21 UI components installed
- Create AppContext (context/AppContext.tsx) to run kubeconfig-validate and
  app-version fetches once at startup instead of on every tab navigation,
  eliminating 100-500ms latency per route change
- Wrap layout with AppProvider + TooltipProvider; AppLayout now consumes
  context instead of firing its own useEffect fetches
- Fix pre-existing useSearchParams build error on /home and /bundle-deployment
  pages by wrapping components in Suspense boundaries
- Update README: correct tech stack, expand project structure, add Architecture
  Notes section documenting startup flow and navigation model

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace MUI Drawer + nav with shadcn Sidebar (collapsible="icon") — sidebar
now collapses to icon-only mode via SidebarTrigger in the content header.
Nav items use SidebarMenuButton with isActive state; MUI icons replaced with
lucide-react equivalents (Wrench, Rocket, Bot, Home).

Migrate 4 dialog components to shadcn Dialog + Input + Label + Button.
Key decisions:
- MUI severity="warning" has no shadcn equivalent — use amber Tailwind palette
- MUI severity="error" → Alert variant="destructive"
- ViewCodeDialog: MUI integer-indexed Tabs → shadcn string-value Tabs;
  custom TabPanel helper removed (shadcn TabsContent handles visibility natively)
- DialogContent auto-includes close button; showCloseButton=false where dialogs
  manage their own close flow

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Home.tsx: Field/FieldLabel forms, SWR caching for environments & helm data, shadcn Dialogs/Card/Alert
- BundleDeploymentManager.tsx: Table/Badge/Progress composition, StatusBadge helper, SWR polling
- Playground.tsx: chat bubbles, Select onValueChange, Textarea ref fix, SWR model fetching
- BundleForm.tsx: checkbox grid for multi-model select, config tables, YAML editor, Tooltip fixes
- Add field.tsx shadcn component (FieldGroup, Field, FieldLabel, FieldError etc.)
- Install swr dependency for stale-while-revalidate caching (instant tab switching)
- Fix: onValueChange handlers typed as string | null per base-nova Select API
- Fix: remove asChild from TooltipTrigger (not supported in base-nova)
- Fix: add missing handleCancelSave function in BundleDeploymentManager

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove @mui/material, @mui/icons-material, @mui/material-nextjs, @emotion/react, @emotion/styled from package.json
- Delete app/theme.ts (MUI theme no longer needed — colors live in globals.css CSS vars)
- providers.tsx: strip MUI ThemeProvider/CssBaseline/AppRouterCacheProvider, render children directly
- bundle-deployment/page.tsx: replace Box/Typography with plain div/h1/p
- bundle-builder/page.tsx: rewrite Dialog with shadcn Dialog/Select/Alert, native radio inputs for source
- add-environment/page.tsx: rewrite with Card/Field/FieldLabel/Input/Textarea/Button/Dialog/Alert
- DocumentationPanel.tsx: replace MUI Drawer/IconButton with shadcn Sheet/Button (BookOpen icon)
- test-utils.tsx: remove MUI ThemeProvider wrapper (no longer needed)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Apply shadcn b0 preset (clean base) then restore purple brand colors
- globals.css: radius 0.625rem, purple OKLCH palette (#622B86 = oklch(0.38 0.18 296)),
  background #fcf9fe = oklch(0.99 0.005 300), improved dark mode borders
- layout.tsx: wire --font-sans to Inter font variable for consistent typography
- providers.tsx: remove 'use client' (pure pass-through, no client logic)
- AppContext: migrate to SWR for /api/kubeconfig-validate + /api/app-version
  — eliminates double API call, shares cache with any component using same key
  — exposes fullHelmVersion + minimumHelmVersion to all consumers
- Home.tsx: remove redundant useSWR('/api/kubeconfig-validate'), read from AppContext
  — extract useSearchParams into SearchParamsWatcher child component
  — wrap only that child in Suspense (not the whole Home), fixes hydration ID mismatch
- page.tsx: remove Suspense wrapper (moved inside Home), fixes base-ui ID hydration errors

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
BundleDeploymentManager + Playground: replace useEffect+fetch with useSWR
using revalidateIfStale:false + revalidateOnFocus:false + revalidateOnReconnect:false
- Data loads once on first visit; subsequent tab switches serve from cache immediately
- Refresh button calls mutateDeployments() to force a fresh fetch on demand
- /api/bundle-deployment, /api/bundles, /api/bundle-deployment-state keys are shared
  across BundleDeploymentManager so a single fetch populates all subscribers
- Playground shares /api/bundle-deployment + /api/environments + /api/checkpoint-mapping
  keys with BundleDeploymentManager/Home — if already cached, rendered instantly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Restructure AppLayout sidebar to match shadcn dashboard-01 design:
- Group main nav under "Platform" SidebarGroupLabel
- Add Documentation, Community, and Contact Us links at the bottom
  (mt-auto pushes them above the footer, opens in new tab)
- Collapsible icon state preserved for all sections
- Logo button navigates to Home and collapses to Home icon
- Add avatar and dropdown-menu UI components (deps for future nav-user)
- Move home page (/) into a dedicated 'Configuration' sidebar group
  as 'Environment' tab with SlidersHorizontal icon
- Remove fallback home button hack — Environment tab is always visible
- Footer env card is now a plain div (non-clickable, display only)
- Replace Home icon with Server icon in footer for semantic clarity
- Collapsed sidebar shows favicon instead of Home icon in header
@snova-varunkrishna

Copy link
Copy Markdown
Collaborator

Please fix the environment update page (it should not bleed over the sides of the dialog):
image

@snova-varunkrishna

Copy link
Copy Markdown
Collaborator

The line below the SambaNova logo is bleeding out of the left nav bar and there isn't enough padding below the "Load" button in the Bundle Builder:
image

@snova-varunkrishna

snova-varunkrishna commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator

The "All" checkbox automatically gets checked when you select configs. "All" should only be checked when all configs are checked. It should be unchecked even if one config is unchecked. When you select "All", it selects all configs, which is correct behavior.

image

@snova-varunkrishna

Copy link
Copy Markdown
Collaborator

Expand the dropdown to fit the names:

image

@snova-varunkrishna

Copy link
Copy Markdown
Collaborator

In v1.4.0 and earlier versions, the Bundle Deployment page states are cached so that a user can see the deployment progress when they return to the page. In your version, the page gets reset and the user needs to click on "Status" each time they return to the page to see the deployment status.

image

@snova-varunkrishna

Copy link
Copy Markdown
Collaborator

On the Bundle Deployment page, even when all pods are deployed and ready, when I click on the "Refresh" button the top left, it does not seem to refresh the status of the deployment to "Deployed".

image

@snova-varunkrishna

Copy link
Copy Markdown
Collaborator

In v1.4.0, when you copy code from the Playground, it copies the actual key (even though the actual key is not shown in the UI), which makes it super convenient to paste into your terminal or code. In your implementation, it copies the dots for the key (the key itself is absent).
image

@snova-varunkrishna

Copy link
Copy Markdown
Collaborator

I saw that you had fixed a hydration error in 1.4.0, on which page was that? Your implementation seems to have a hydration error on the Playground:

image

@snova-varunkrishna snova-varunkrishna self-requested a review April 9, 2026 23:53

@snova-varunkrishna snova-varunkrishna left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Submitted a few comments based on initial testing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants