CDCG Dash is a Next.js dashboard for a crab-processing / warehouse operations team. It combines inventory visibility, form-based data entry, local weather, and simple staff PIN authentication into a single internal tool. The application is designed around a Google Sheets-backed workflow, where team members submit operational data from the browser and the app appends that data to a spreadsheet used as the system of record.
This project is purpose-built for a specific business workflow, but it is also a good template for any small operation that wants a lightweight internal portal with protected pages and spreadsheet-backed reporting.
The dashboard includes:
- A protected home screen with:
- current inventory totals pulled from a Google Sheet
- estimated clock-in time for the next shift based on current stock and grading load
- local weather using Open-Meteo
- A searchable forms hub for operational paperwork
- Multiple form pages for business data collection
- Google Sheets writes for each form submission
- A simple staff login flow using a 4-digit PIN and bcrypt-hashed values stored in environment variables
- Minimal admin settings and theme support
The main operational forms are:
- Tuesday Breakdown Form
- End of Day Report
- Crab Invoice Entry
- Daily Dead Loss
At a high level:
- Users visit the site and are redirected to
/loginif no session cookie is present. - A user enters a 4-digit PIN.
- The app compares the PIN against
STAFF_JSON, which contains staff names mapped to bcrypt hashes. - On success, the app sets an
httpOnlysession cookie and grants access to protected routes. - Business forms submit to server actions in
src/lib/actions/crab-actions.ts. - Those actions authenticate to Google Sheets using the service account credentials in
.env.local. - Data is appended to specific tabs in one spreadsheet, such as:
InvoicesEoD_DataTues_BreakdownDaily_Dead_Loss
- The home dashboard reads the latest row from
EoD_Datato show inventory totals and estimate when the next shift should start.
- Next.js 16 (App Router)
- React 19
- TypeScript
- Tailwind CSS
- ShadCN-style UI primitives
- Google Sheets API via
googleapis - bcrypt via
bcryptjs - Open-Meteo for weather data
src/app/— app pages and route-level UIsrc/lib/actions/— server actions for login and Google Sheets writessrc/components/— shared UI components and sidebarsrc/proxy.ts— route protection middleware.env.example— sample environment variablespackage.json— scripts and dependencies
Before you host this project, you will need:
- Node.js 20+ recommended
- npm
- A Google Cloud project with a service account
- A Google Sheet shared with that service account
- A local machine or deployment provider such as Vercel, Railway, Render, or a Linux VM
- A location for weather data (
LATandLON)
git clone https://github.com/<your-user>/cdcg-dash.git
cd cdcg-dashnpm installCopy the example env file:
On macOS/Linux:
cp .env.example .env.localOn Windows PowerShell:
Copy-Item .env.example .env.localOpen .env.local and add the settings described below.
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"
GOOGLE_SERVICE_ACCOUNT_EMAIL="your-service-account@your-project.iam.gserviceaccount.com"
GOOGLE_SHEET_ID="your-google-sheet-id"
STAFF_JSON={"Employee Name":"$2a$10$...bcrypthash..."}
LAT=
LON=npm run devThen open:
http://localhost:3000
If you are not logged in, you will be redirected to /login.
This is the private key from the Google service account JSON. In the file, it normally looks like this:
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
In .env.local, you must replace the actual newlines with escaped \n sequences, because the app does:
process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, "\n")Example:
GOOGLE_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nabc123\nxyz456\n-----END PRIVATE KEY-----\n"This is the email address for the Google service account. It should look similar to:
your-service-account@your-project.iam.gserviceaccount.com
This is the spreadsheet ID from the Google Sheet URL:
https://docs.google.com/spreadsheets/d/<THIS_IS_THE_SHEET_ID>/edit
These are used by the dashboard to fetch local weather using Open-Meteo. These should be set to the location of any given store.
This is where the app stores allowed staff names and bcrypt-hashed PIN values.
Example structure:
{
"Alex": "$2a$10$B8M9f0i...",
"Jordan": "$2a$10$P7m6R2...",
"Sam": "$2a$10$L8V9c1..."
}The app checks each entry by trying to compare the entered 4-digit PIN with each bcrypt hash.
Important:
- The keys are staff names, not usernames.
- Values must be bcrypt hashes, not plaintext PINs.
- Because this is a simple app, it stores auth data in environment variables instead of a real database.
You can generate a hash locally with Node if you want a simple 4-digit staff PIN.
node -e "const bcrypt=require('bcryptjs'); const pin='1234'; bcrypt.hash(pin, 10).then(h=>console.log(h));"This prints a bcrypt string like:
$2a$10$zA0P2mQvV8xTzQ3P4n9L8uNn8gC7v.Df2L4L7M4E5m7VfO0jR6n6u
Then assign it in STAFF_JSON:
STAFF_JSON={"Alex":"$2a$10$zA0P2mQvV8xTzQ3P4n9L8uNn8gC7v.Df2L4L7M4E5m7VfO0jR6n6u"}If you want multiple staff users, include multiple entries in the JSON object.
In Google Cloud Console:
- Create a project
- Enable the Google Sheets API
- Create a service account
- Generate a JSON key
- Download the key and use the values for:
GOOGLE_PRIVATE_KEYGOOGLE_SERVICE_ACCOUNT_EMAIL
Create a Google Sheet that will act as your operational database.
Share it with the service account email address with edit access.
The app expects tabs with these names:
InvoicesEoD_DataTues_BreakdownDaily_Dead_Loss
The code in src/lib/actions/crab-actions.ts appends rows to these tabs. If your column layouts differ from the code expected, your sheet data may not read or calculate correctly.
The home dashboard reads the latest row from EoD_Data and expects values in roughly this structure:
- column A: date
- columns F-L: male totals
- columns M-P: female totals
- column Q: total bushels
- column R: ungraded boxes
This is not a universal schema; it is a business-specific spreadsheet layout, so you should verify the actual data layout before using it in production.
- Push the repo to GitHub.
- Create a new Vercel project linked to the repo.
- Framework preset: Next.js
- Add the environment variables listed above in the Vercel project settings.
- Deploy.
- Ensure the app is using a production environment with
NODE_ENV=production.
The app uses cookie-based auth and server actions, so it works best when deployed to a platform that supports Next.js server-side runtime.
You can also run it on any host that supports Node.js and Next.js:
npm install
npm run build
npm run startIf you are hosting behind Nginx, Caddy, or a reverse proxy, make sure you forward traffic to the app and allow WebSocket support where required.
This app is purposely lightweight and does not implement a full multi-user database or role system. It is best suited for an internal environment where security is handled by the hosting environment and access control.
Important limitations:
STAFF_JSONis not a secure database and is only intended for very small setups.- The app currently relies on a session cookie, which is okay for a simple internal portal but not a full enterprise access-control system.
- The Google service account key is highly sensitive; treat it as a secret.
For a production-grade deployment, consider moving staff data to a proper database and moving authentication away from environment-variable-only pin storage.
npm run dev # local development server
npm run build # production build
npm run start # run production build
npm run lint # lint source filesThis usually means the session cookie is not being set or is being removed. Make sure:
- you are using a browser that accepts cookies
- you are not on a subdomain that blocks same-site cookies
- you are not using a local environment with a proxy that strips cookies
Check that:
- the service account has access to the spreadsheet
- the spreadsheet ID is correct
- the private key is escaped correctly in
.env.local - the service account is not blocked by a Google Workspace policy
Ensure:
LATandLONare valid numeric values- your deployment environment has outbound internet access
- the app is not restricted by a firewall or proxy
This usually means the Google service account cannot write to the target sheet tab. Verify:
- the tab name matches the code exactly
- the sheet is shared with the service account email
- the script can append rows without restrictions
CDCG Dash is a lightweight internal operations dashboard built around Google Sheets and simple staff PIN login. It helps a team centralize inventory tracking, daily reporting, and form-driven operations without building a large custom database system.
If you are cloning this repository for your own instance, the most important setup steps are:
- install dependencies
- copy
.env.exampleto.env.local - add your Google service account details and spreadsheet ID
- generate bcrypt hashes for your staff employees in
STAFF_JSON - set
LATandLONfor weather data - deploy with a Next.js-capable host
Once those pieces are in place, the application is ready to run as a private internal dashboard.