Skip to content

leanscript/supernova

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Supernova

Supernova is a small library that plugs into a TanStack Start app and generates an admin dashboard from a Quark REST API.

It does not own your application. You keep a normal TanStack Start router, mount the Supernova route factories where you want the admin dashboard, and add custom CRUD views from your app code through a Vite glob registry.

Example App

pnpm install
pnpm example:dev

The example listens on http://localhost:3015 and targets Quark on http://localhost:3014 by default.

VITE_QUARK_API_URL=http://localhost:3014 pnpm example:dev

Install In A TanStack Start App

Import the CSS once from your app stylesheet:

@import 'tailwindcss' source('../');
@source '../../node_modules/@leanscript/supernova/src';
@import '@leanscript/supernova/styles.css';
@import './supernova.theme.css';

You can also use one of the packaged themes instead of your local theme file:

@import 'tailwindcss' source('../');
@source '../../node_modules/@leanscript/supernova/src';
@import '@leanscript/supernova/styles.css';
@import '@leanscript/supernova/themes/tasty-crousty.css';

Available packaged themes:

  • @leanscript/supernova/themes/tasty-crousty.css
  • @leanscript/supernova/themes/neo-brutalism.css

Create a theme file next to your app stylesheet and override the Supernova UI tokens there. Because the theme is imported after Supernova, these values drive the glass surfaces, controls, focus states, and colors without changing the components:

/* src/styles/supernova.theme.css */
@layer base {
  :root {
    --supernova-background:
      linear-gradient(135deg, #eef7fb 0%, #f8fbff 46%, #f4f2ff 100%);
    --supernova-surface: rgb(255 255 255 / 0.66);
    --supernova-surface-soft: rgb(255 255 255 / 0.44);
    --supernova-line: rgb(83 101 132 / 0.24);
    --supernova-blur: 20px;
    --supernova-radius: 8px;
    --supernova-button-radius: 999px;
    --supernova-accent: #2563eb;
    --supernova-success: #059669;
    --supernova-danger: #e11d48;
  }
}

Keep TanStack Start's generated route tree out of your app source. Start still expects a tiny internal root for its manifest, but your app router can use the manual route-tree.tsx below:

// vite.config.ts
tanstackStart({
  srcDirectory: 'src',
  router: {
    routesDirectory: 'start-routes',
    generatedRouteTree: '../.tanstack/routeTree.gen.ts',
  },
})
// src/start-routes/__root.tsx
import { createRootRoute } from '@tanstack/react-router'

export const Route = createRootRoute()

Create the Supernova router context. The API client and custom view registry are created for you:

import { createRouter } from '@tanstack/react-router'
import {
  createSupernovaRouterContext,
  type SupernovaRouterContext,
} from '@leanscript/supernova'
import { routeTree } from './route-tree'

export function getRouter() {
  return createRouter({
    routeTree,
    context: createSupernovaRouterContext({
      views: import.meta.glob('./supernova/**/*.tsx', { eager: true }),
    }) satisfies SupernovaRouterContext,
  })
}

Create the app shell, then let Supernova register its dashboard routes:

// src/route-tree.tsx
import {
  Outlet,
  createRootRouteWithContext,
} from '@tanstack/react-router'
import {
  SupernovaShell,
  loadSupernovaShell,
  registerSupernovaDashboard,
  type SupernovaRouterContext,
  type SupernovaShellData,
} from '@leanscript/supernova'

const rootRoute = createRootRouteWithContext<SupernovaRouterContext>()({
  loader: ({ context }) => loadSupernovaShell(context.api),
  component: RootRoute,
})

export const routeTree = registerSupernovaDashboard(rootRoute)

function RootRoute() {
  const shell = rootRoute.useLoaderData() as SupernovaShellData
  return (
    <SupernovaShell {...shell}>
      <Outlet />
    </SupernovaShell>
  )
}

Then add app-owned custom CRUD views by convention:

// src/supernova/users/users.create.tsx
import { z } from 'zod'
import {
  SupernovaForm,
  createZodFormValidator,
  getRecordId,
  type QuarkRecord,
  type SupernovaFieldDefinition,
  type SupernovaResourceCreateData,
  type SupernovaResourceViewProps,
} from '@leanscript/supernova'

const userFields = [
  { name: 'name', label: 'Nom', type: 'text', required: true },
  { name: 'email', label: 'Email', type: 'email', required: true },
  { name: 'bio', label: 'Bio', type: 'textarea' },
  { name: 'score', label: 'Score', type: 'number', defaultValue: 0 },
  { name: 'birthDate', label: 'Date', type: 'date' },
  { name: 'avatar', label: 'Avatar', type: 'upload', accept: 'image/*' },
] satisfies SupernovaFieldDefinition[]

const userFormValidator = createZodFormValidator(
  z
    .object({
      name: z.string().trim().min(1, 'Le nom est requis.'),
      email: z.string().trim().email('Email invalide.'),
      score: z.number().min(0, 'Le score doit être positif.'),
    })
    .passthrough(),
)

export default function UsersCreateView({
  api,
  resourceId,
  actions,
}: SupernovaResourceViewProps<SupernovaResourceCreateData>) {
  async function createUser(payload: QuarkRecord) {
    const created = await api.resource(resourceId).create(payload)
    const createdId = getRecordId(created)
    await (createdId === null
      ? actions.navigateToIndex()
      : actions.navigateToDetail(String(createdId)))
  }

  return (
    <SupernovaForm
      title="Utilisateur"
      fields={userFields}
      submitLabel="Créer"
      variant="create"
      validationAdapter={userFormValidator}
      onSubmit={createUser}
    />
  )
}

Supported resource view filenames:

  • users/users.index.tsx or users/users.list.tsx
  • users/users.create.tsx, users/users.add.tsx, or users/users.new.tsx
  • users/users.detail.tsx or users/users.show.tsx
  • users/users.edit.tsx

Field components:

  • SupernovaForm renders create/edit forms from field definitions.
  • SupernovaField renders one controlled field.
  • SupernovaFieldValue renders one display value.
  • SupernovaRecordFields renders a display grid from a record.

Supported field types are text, email, url, password, textarea, number, date, datetime, upload, select, checkbox, and json. The upload field stores selected files as JSON data URLs, which is useful for small admin payloads or prototyping before adding a dedicated file backend.

Form validation:

  • SupernovaForm accepts validationAdapter.
  • createZodFormValidator(schema) adapts a Zod schema to Supernova.
  • Zod issues are mapped to field errors by their first path segment.
  • On success, onSubmit receives the validated payload.

Available helpers include:

  • context.api.openApi()
  • context.api.discoverResources()
  • context.api.resource('users').list({ page, filters, sort, with, fields })
  • context.api.resource('users').get(id)
  • context.api.resource('users').create(payload)
  • context.api.resource('users').update(id, payload)
  • context.api.resource('users').delete(id)
  • context.api.request('/custom/path', { method, query, body })

About

💫 Easy Tanstack start admin 💫

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors