Getting Started

Getting Started

Install SentinelGrid, run the dev server, and ship your first engineering ops page.

Getting Started

SentinelGrid is a production-ready Next.js 16 template for engineering operations, observability, and incident response. This guide walks you through installation, the project structure, and shipping your first page in under five minutes.

Installation

SentinelGrid ships as a complete Next.js project. Clone the repository, install dependencies, and you are ready to go. The project uses bun by default but works with any modern package manager.

Terminal
# Clone the template
git clone https://github.com/sentinelgrid/sentinelgrid.git my-console
cd my-console

# Install dependencies
bun install

# Initialize the database (SQLite via Prisma)
bun run db:push

# Start the dev server
bun run dev

Requirements

Node.js 20+, Bun 1.0+ (or npm/yarn/pnpm), and Git. The project targets Next.js 16 with Turbopack enabled.

Verify your install

Open the preview panel and you should see the SentinelGrid dashboard load with the default dark theme. The sidebar contains every route the template ships with.

Project Structure

SentinelGrid uses the Next.js App Router with route groups to separate concerns. Here is the high-level layout:

Directory layout
src/
├── app/
│   ├── (dashboard)/         # Dashboard shell (sidebar + header)
│   │   ├── dashboard/       # Engineering overview + sub-dashboards
│   │   ├── incidents/       # Incident management
│   │   ├── services/        # Service catalog
│   │   ├── reliability/     # SLOs, error budgets, uptime
│   │   ├── deployments/     # Release management
│   │   ├── docs/            # This documentation site
│   │   └── admin/           # Admin & settings
│   ├── (auth)/              # Sign in, sign up, onboarding
│   ├── landing/             # Public marketing pages
│   ├── errors/              # 401, 403, 404, 500, 503, ...
│   ├── layout.tsx           # Root layout, fonts, ThemeProvider
│   └── page.tsx             # Redirects to /dashboard
├── components/
│   ├── charts/              # Theme-aware Recharts wrappers
│   ├── tables/              # DataTable system
│   ├── common/              # KpiCard, badges, page-header
│   ├── layout/              # Sidebar, header, right-panel
│   └── ui/                  # shadcn/ui primitives
├── lib/
│   ├── data.ts              # In-memory mock data layer
│   ├── types.ts             # Domain types
│   ├── nav.ts               # Sidebar navigation config
│   └── store.ts             # Zustand UI stores

Convention

Route groups in parentheses like (dashboard) do not appear in the URL but let you share layouts. This keeps the dashboard shell isolated from public marketing routes.

Running the Dev Server

The dev server runs on port 3000 with Turbopack for fast HMR. The sandbox auto-starts the server for you, but here are the commands you will use locally:

npm scripts
bun run dev          # Start dev server with Turbopack
bun run lint         # Run ESLint
bun run db:push      # Push Prisma schema to SQLite
bun run db:studio    # Open Prisma Studio

Port 3000 only

The sandbox exposes a single port externally. If you spin up mini-services (e.g., a websocket service), use the gateway query parameter ?XTransformPort=3030 instead of a direct localhost URL.

Your First Page

Every dashboard page follows the same pattern: PageContainer for spacing, PageHeader for the title and breadcrumbs, then content cards.

src/app/(dashboard)/my-team/page.tsx
"use client";

import { PageContainer, PageHeader } from "@/components/common/page-header";
import { KpiCard } from "@/components/common/kpi-card";
import { Card } from "@/components/ui/card";
import { Users } from "lucide-react";

export default function MyTeamPage() {
  return (
    <PageContainer>
      <PageHeader
        title="My Team"
        description="Manage your engineering team."
        breadcrumbs={[{ label: "Dashboard", href: "/dashboard" }, { label: "My Team" }]}
        icon={<Users className="w-5 h-5" />}
      />
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
        <KpiCard label="Members" value={12} icon={<Users className="w-4 h-4" />} />
      </div>
      <Card className="p-6">
        <p className="text-sm text-muted-foreground">Team details go here.</p>
      </Card>
    </PageContainer>
  );
}

Add the new route to src/lib/nav.ts so it appears in the sidebar:

src/lib/nav.ts
{
  label: "My Team",
  href: "/my-team",
  icon: "Users",
}

Adding Data

SentinelGrid ships with an in-memory mock data layer at src/lib/data.ts. To add a new domain entity, define a type in src/lib/types.ts, then export a typed array from data.ts.

src/lib/types.ts
export interface Deployment {
  id: string;
  serviceId: string;
  version: string;
  status: "succeeded" | "failed" | "rolling_out" | "rolled_back";
  startedAt: string;
  endedAt?: string;
  authorId: string;
}
src/lib/data.ts
export const deployments: Deployment[] = [
  {
    id: "d1",
    serviceId: "s1",
    version: "v2.14.0",
    status: "succeeded",
    startedAt: "2024-09-12T10:00:00Z",
    endedAt: "2024-09-12T10:04:32Z",
    authorId: "u2",
  },
  // ...
];

Tip

Use the person(id) and team(id) helpers to resolve references without writing joins. The mock layer is intentionally simple so you can swap it for a real API later.

You are ready

  • Project installed and dev server running
  • Understand the route group layout
  • Created your first dashboard page
  • Added mock data for a new domain entity

Command Palette

Search for a command to run...