@liiift-studio/sales-portal
v3.1.9
Published
Sales dashboard and analytics components for Next.js type foundry sites — Stripe integration, MUI DataGrid, charts, CSV export, and multi-designer reporting.
Readme
@liiift-studio/sales-portal
Drop-in sales dashboard and analytics components for Next.js type-foundry sites — designers log into their foundry's site and see their own typeface sales and revenue, pulled live from Stripe, with designer authentication via Sanity. Build the dashboard once, reuse it across every foundry site.
Internal package.
UNLICENSED— published public on npm for convenient installation across Liiift Studio foundry sites, but all rights reserved; it is not licensed for use outside Liiift Studio projects.
Features
- Designer authentication via Sanity CMS
- Admin multi-designer view with sequential loading
- Monthly and full-year sales data from Stripe (invoices + payment intents)
- Interactive charts and data visualization (MUI X-Charts)
- Spreadsheet-style data tables with sorting, filtering, and CSV export (MUI DataGrid)
- Date range selection for custom export reports
- Stripe balance reconciliation checks
- Top performers analysis (typefaces and designers)
- Typeface and license type breakdowns
- Summary cards with key metrics
- Login modal with two-column layout
How it works
The package ships React/MUI components plus Next.js API handlers. Your site
re-exports the handlers as API routes and renders <SalesPortalPage />. All
Stripe and Sanity secrets stay server-side inside the API handlers — they are
never exposed to the browser.
Installation
npm install @liiift-studio/sales-portalPages Router only. This package wires into
pages/api/...route handlers and assumes the Next.js Pages Router.
Prerequisites
Peer dependencies required in your project:
{
"@mui/material": "^5.10.0",
"@mui/x-data-grid": "^8.0.0",
"@mui/x-date-pickers": "^7.0.0",
"@mui/x-charts": "^7.0.0",
"react": "^18.0.0",
"react-dom": "^18.0.0",
"next": "^14.0.0",
"dayjs": "^1.11.0",
"slugify": "^1.6.0"
}Compatibility
| Peer | Version |
|------|---------|
| @mui/material | ^5.10.0 (MUI v5; uses Box/flexbox, not Grid, so v5–v7 work) |
| @mui/x-data-grid | ^8.0.0 |
| @mui/x-date-pickers | ^7.0.0 |
| @mui/x-charts | ^7.0.0 |
| react / react-dom | ^18.0.0 |
| next | ^14.0.0 (Pages Router) |
| dayjs | ^1.11.0 |
| slugify | ^1.6.0 |
Note
@mui/x-data-gridis on major v8 while the other MUI X packages are on v7 — install the matching majors as listed above.
Quick Start
Everything below is for developers integrating the package. See
SETUP.mdfor the full setup guide and a step-by-step checklist.
1. Create API route wrappers
Create these 5 files — all are required. The dashboard calls every endpoint,
so a missing wrapper produces a 404 at runtime (the year-over-year view needs
getYearSales):
pages/api/sales-portal/
getDesignerInfo.js
getDesigners.js
getSales.js
getYearSales.js
getBalanceTransactions.jsEach file is a one-line re-export:
// pages/api/sales-portal/getSales.js
export { default, config } from '@liiift-studio/sales-portal/api/getSales';2. Create the page
SalesPortalPage wraps its own MUI ThemeProvider with the exported salesTheme
by default (pass includeTheme={false} to supply your own).
// pages/sales-portal.js
import { SalesPortalPage } from '@liiift-studio/sales-portal';
export default function SalesPortal() {
return (
<SalesPortalPage
texts={{
title: 'Sales Portal',
dashboardTitle: 'Sales',
dashboardSubtitle: 'by designer'
}}
/>
);
}3. Configure Next.js
// next.config.js
const nextConfig = {
transpilePackages: ['@mui/x-charts', '@liiift-studio/sales-portal'],
};4. Set environment variables
# .env.local
SANITY_STUDIO_PROJECT_ID=your_project_id
SANITY_STUDIO_DATASET=production
SANITY_STUDIO_TOKEN=your_sanity_token
STRIPE_SECRET_KEY=your_stripe_secret_key
# Optional — defaults to 2022-04-01 if unset
SANITY_STUDIO_API_VERSION=2022-04-01
# Optional kill switch — set to the literal string "false" to disable the portal.
# Any other value (or unset) leaves it enabled.
SALES_PORTAL_ENABLED=trueDesigner accounts (Sanity)
Authentication matches an account document in your Sanity dataset by email,
password, and isDesigner. Each designer needs an account document with those
fields for login to succeed. To grant access to the admin multi-designer view,
set isAdmin: true on that designer's account document.
API Endpoints
| Endpoint | Method | Purpose |
|----------|--------|---------|
| getDesignerInfo | POST | Authenticate designer, return account data |
| getDesigners | GET | List all designers (admin) |
| getSales | POST | Fetch sales data for a month or date range |
| getYearSales | POST | Fetch a full year of sales with year-level aggregates |
| getBalanceTransactions | POST | Fetch Stripe balance transactions for reconciliation |
Component & Hook Exports
import {
SalesPortalPage, // Full page with login modal + dashboard
LoginForm, // Login modal component
Sales, // Individual designer sales dashboard
SalesTable, // Sales data table with CSV export
DateRangeSalesTable, // Custom date range export table
SalesChart, // Revenue trend charts
YearOverview, // Full-year overview view
SummaryCards, // Key metrics cards (a.k.a. Insights)
TopPerformers, // Top typefaces/designers
TypefaceList, // Typeface breakdown
LicenseTypeList, // License type analysis
PeriodComparison, // Period comparison charts
SalesErrorBoundary, // Error boundary for dashboard sections
salesTheme, // MUI theme
// Table helpers
TableRowCells, getCellValue, COLUMNS,
// Hooks
useSalesDateQuery, useColumnSelection, useReconciliation,
} from '@liiift-studio/sales-portal';API handlers are imported individually from the ./api/* subpath, e.g.
import handler from '@liiift-studio/sales-portal/api/getSales'.
Architecture
@liiift-studio/sales-portal
api/
getDesignerInfo.ts # Authenticate designer
getDesigners.ts # List designers (admin)
getSales.ts # Month / date-range sales
getYearSales.ts # Full-year sales + aggregates
getBalanceTransactions.ts # Stripe balance transactions
utils/
clients.ts # Shared Sanity + Stripe clients
apiResponse.ts # Standardized error/success responses
authMiddleware.ts # Request authentication
dateUtils.ts # UTC date range utilities
salesDataProcessor.ts # Core sales data pipeline
stripeFetcher.ts # Stripe API pagination
feeCalculator.ts # Fee distribution + rounding
processors/ # Stripe invoice / payment-intent processors
components/ # React components (Box/flexbox, no MUI Grid)
styles/ # SCSS + MUI theme
hooks/ # Custom React hooks
utils/ # Client-side utilitiesCurrently deployed on
Contributing / Local Development
This is a published package built with tsup and tested
with vitest. Type definitions are hand-maintained in
index.d.ts (tsup runs with dts: false).
npm install # install dev + peer tooling
npm run build # bundle with tsup -> dist/
npm run dev # tsup --watch (rebuild on change)
npm test # run the vitest suite
npm run capture # regenerate the README diagrams in assets/When adding a new component/hook/util, register it as an entry in
tsup.config.ts (or it won't be built) and add the matching export to index.ts
and index.d.ts.
Test against a consumer site
Use the link:* scripts to npm link the package into a consumer for local
testing before publishing (run npm run dev alongside so changes rebuild):
npm run link:tdf # link into sites/tdf
npm run link:darden # link into sites/darden
npm run link:positype # link into sites/positypePublishing
- Make and verify your changes (
npm test, then test against a consumer site) - Bump the version in
package.json - Commit and push to
master - Publish:
npm publish(prepublishOnlyruns the build automatically;publishConfig.accessis alreadypublic) - Foundry sites pick up the new version on their next deploy (they use
^semver)
Before bumping, check the currently published version with
npm view @liiift-studio/sales-portal versionso the new number is ahead of npm.
License
UNLICENSED — internal Liiift Studio package. All rights reserved.
