DEV Community

Roberto Luna
Roberto Luna

Posted on

Building CraveView: A Technical Deep-Dive into the Crave T3 Tablet Inventory Dashboard

Building CraveView: A Technical Deep-Dive into the Crave T3 Tablet Inventory Dashboard

TL;DR

I built CraveView, a tablet inventory dashboard, using Next.js 15, TypeScript, and Tailwind v4, with a Prisma schema for data modeling. This technical deep-dive explores the architecture decisions, code changes, and lessons learned during development.

The Problem

The goal was to create a dashboard for managing Crave T3 tablet inventory. The initial problem was setting up a robust tech stack for handling data synchronization from Google Sheets (CSV export) to a database, and then to the dashboard.

What I Tried First

Initially, I scaffolded the project using create-next-app with TypeScript and Tailwind CSS. I then focused on setting up Prisma for data modeling and database connection. The first challenge was configuring Prisma to work with a Neon database.

The Implementation

The implementation involved several key steps:

Setting Up the Database and Prisma

First, I added the database URL to .env.example:

# --- Database (Neon) ---
DATABASE_URL="postgresql://user:password@ep-xxxx-pooler.region.aws.neon.tech/craveview?sslmode=require"
DATABASE_URL_UNPOOLED="postgresql://user:password@ep-xxxx-pooler.region.aws.neon.tech/craveview?sslmode=require"
Enter fullscreen mode Exit fullscreen mode

Then, I defined the Prisma schema in prisma/schema.prisma:

model CraveDevice {
  id       String   @id @default(cuid())
  clientId String   @unique
  // Additional fields...
}
Enter fullscreen mode Exit fullscreen mode

Configuring Next.js and Tailwind

I updated next.config.ts to include Tailwind CSS:

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  // Config options...
};

export default nextConfig;
Enter fullscreen mode Exit fullscreen mode

In eslint.config.mjs, I added ESLint configuration:

import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const compat = new FlatCompat(__dirname, {
  // Compat options...
});

// ESLint rules...
Enter fullscreen mode Exit fullscreen mode

Building the Dashboard

The dashboard page is located at src/app/(dashboard)/inventory/page.tsx:

import { useState, useEffect } from 'react';

const InventoryPage = () => {
  const [devices, setDevices] = useState([]);

  useEffect(() => {
    // Fetch devices from API or database...
  }, []);

  return (
    <div>
      <h1>Inventory Dashboard</h1>
      <ul>
        {devices.map((device) => (
          <li key={device.id}>{device.clientId}</li>
        ))}
      </ul>
    </div>
  );
};

export default InventoryPage;
Enter fullscreen mode Exit fullscreen mode

Key Takeaway

One key lesson learned was the importance of configuring lint-staged and Husky for automated code quality checks. I added .husky/pre-commit with:

npx lint-staged
Enter fullscreen mode Exit fullscreen mode

And configured .lintstagedrc.json:

{
  "*.{ts,tsx,js,jsx}": ["eslint --fix", "prettier --write"],
  "*.{json,css,md}": ["prettier --write"]
}
Enter fullscreen mode Exit fullscreen mode

What's Next

The next step is to implement data synchronization from Google Sheets to the dashboard. I plan to create an API endpoint for handling CSV uploads and integrating it with the Prisma schema.

vibecoding #buildinpublic #Nextjs #TypeScript #TailwindCSS #Prisma


Part of my Build in Public series — sharing the real process of building SaaS projects from Playa del Carmen, México.

Repo: zaerohell/craveview · 2026-07-10

#playadev #buildinpublic

Top comments (0)