Architecture
Architecture
Data Flow
The zpreorder cannot directly access Netlify Blobs from local development. Instead, it fetches data through the main site’s admin API endpoints.
graph TB
subgraph "Local Development"
PM[zpreorder<br/>localhost:9876]
end
subgraph "Netlify (Deployed)"
API[Admin API<br/>/api/admin/*]
BLOB[(Netlify Blobs<br/>preorder-data-preview)]
end
PM -->|"HTTPS + Auth Token"| API
API <-->|Read/Write| BLOB
Development Modes
Mock Mode (Default)
For rapid UI development without network dependencies:
# No configuration needed - mock mode is the default
pnpm zpreorder:dev
- Uses local JSON files in
mock-data/ - Fast iteration, no network latency
- Predictable test data
- Mutations persist in memory during session
Local API Mode
For testing with local Netlify Functions and offline blob storage:
pnpm zpreorder:dev:full
- Starts Netlify Functions server on port 9999 with
--offlineflag - Uses file-based blob storage (
.netlify/blobs-serve/) - Data stored locally in
preorder-data-previewstore - No network dependency — fully offline
Remote Preview Mode
For testing with real data from deployed preview:
pnpm zpreorder:dev:preview
- Fetches from deployed preview site
- Real blob storage data (preview store)
- Requires
PREORDER_API_TOKEN(set in.env)
Remote Production Mode
For testing with real production data (read-only recommended):
pnpm zpreorder:dev:prod
- Fetches from production site
- Real production blob storage data
- Requires
PREORDER_API_TOKEN(set in.env)
Manual Remote Configuration
For custom API endpoints, set environment variables in .env:
# .env (in sub-packages/zpreorder/)
VITE_BLOB_API_URL=https://preview--takazudomodular.netlify.app/api/admin
VITE_PREORDER_API_TOKEN=your-preorder-api-token-here
- Requires
ADMIN_API_TOKEN(set on Netlify)
Environment variables use VITE_ prefix because this sub-app uses Vite (not Next.js). Only VITE_-prefixed variables are exposed to client-side code.
File Structure
sub-packages/zpreorder/
├── src/
│ ├── App.jsx # Main app with routing
│ ├── main.jsx # Entry point
│ ├── index.css # Global styles with CSS vars
│ ├── components/
│ │ ├── Dashboard.jsx # Dashboard page
│ │ ├── NotifyList.jsx # Notify subscriptions list
│ │ ├── ReservationList.jsx # Reservations list
│ │ ├── SendNotification.jsx # Send notification page
│ │ ├── Nav.jsx # Navigation component
│ │ └── shared/
│ │ └── ConfirmSendModal.jsx # Notification confirmation modal
│ ├── utils/
│ │ ├── api-client.js # Dual-mode API client (mock/remote)
│ │ ├── product-lookup.js # Product name lookup from master data
│ │ └── date.js # Date formatting utility
│ └── mock-data/
│ ├── subscriptions.json
│ └── reservations.json
├── tests/
│ └── api/ # API integration tests (Vitest)
│ ├── helpers.js # Shared test utilities
│ ├── notify-list.test.js
│ ├── notify-send.test.js
│ ├── notify-status.test.js
│ ├── reservations.test.js
│ └── stats.test.js
├── package.json
├── vite.config.js
├── vitest.config.js # Vitest configuration
├── tailwind.config.js
└── index.html
Authentication
Main Site Admin API
Admin endpoints on the main site require ADMIN_API_TOKEN in the Authorization header:
// src/utils/api-client.js
async function fetchFromRemote(endpoint, body = {}) {
const response = await fetch(`${API_URL}${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${AUTH_TOKEN}`,
},
body: JSON.stringify(body),
});
return response.json();
}
Sub-App Access
The sub-app itself does not require authentication in development. In production deployment (if any), consider adding basic auth or Netlify Identity.
API Client
The API client automatically switches between mock and remote modes:
// src/utils/api-client.js
const API_MODE = import.meta.env.VITE_BLOB_API_MODE || 'mock';
export function isMockMode() {
return API_MODE === 'mock';
}
export async function listNotifySubscriptions(filters = {}) {
if (isMockMode()) {
// Return filtered mock data
return { success: true, subscriptions: filteredMockData };
}
return fetchFromRemote('/admin-notify-list', filters);
}
Send Notification Feature
The Send Notification page allows admins to send restock notification emails to pending subscribers.
Workflow
- Select a product with pending subscribers from dropdown
- Email title and body auto-fill with templates:
- Title:
Takazudo Modular 入荷のお知らせ: {product name} - Body: Product announcement with link to product page
- Preview email and confirm recipient count
- Click Send to mark all pending subscribers as notified
Product Name Lookup
The app uses the @data alias to access product master data:
// vite.config.js
resolve: {
alias: {
'@data': path.resolve(__dirname, '../../src/data'),
},
}
// src/utils/product-lookup.js
import { allProducts } from '@data/product-master-data.mjs';
export function getProductName(slug) {
const product = allProducts.find(p => p.slug === slug);
return product?.name || slug;
}
API Endpoint
The notification is sent via POST /api/admin/notify/send. See Admin Notify Send for details.
Currently, notifications are logged and subscribers are marked as notified, but actual email sending via Resend is planned for Issue #501.
API Testing
The zpreorder includes API tests using Vitest that make real HTTP requests to the admin endpoints. Tests can run against both local development (netlify dev) and remote preview environments.
Test Structure
tests/api/
├── helpers.js # Shared utilities (fetch wrapper, auth)
├── notify-list.test.js # POST /api/admin-notify-list
├── notify-send.test.js # POST /api/admin/notify/send
├── notify-status.test.js # POST /api/admin-notify-status
├── reservations.test.js # POST /api/admin-reservation-list
└── stats.test.js # POST /api/admin-stats
Running Tests
# Run against local functions server (http://localhost:9999)
# First start the functions server: cd <project-root> && pnpm functions:serve
pnpm test:local
# Run against preview environment
pnpm test:preview
# Watch mode for development
pnpm test:watch
Environment Variables
| Variable | Description | Default |
|---|---|---|
API_BASE_URL | Base URL for API requests | http://localhost:9999 |
PREORDER_API_TOKEN | Bearer token for authentication (dedicated token, not Netlify PAT) | Required |
RUN_DESTRUCTIVE_TESTS | Enable tests that modify data | false |
Test Categories
Tests are designed to be non-destructive by default:
| Category | Safe to Run | Description |
|---|---|---|
| List endpoints | Yes | Read-only, no data modification |
| Validation errors | Yes | Tests error handling for invalid input |
| Auth errors | Yes | Tests 401 responses without token |
| Status updates | No* | Modifies subscription status |
| Send notification | No* | Marks subscribers as notified |
*Enable with RUN_DESTRUCTIVE_TESTS=true
Example: Running Full Test Suite
# Local testing with functions server
cd <project-root>
pnpm functions:serve & # Start in background (port 9999)
cd sub-packages/zpreorder
PREORDER_API_TOKEN=your-token pnpm test:local
# Preview testing
PREORDER_API_TOKEN=your-token pnpm test:preview
Port Assignment
| Port | Domain | Service |
|---|---|---|
| 9876 | zpreorder.localhost | zpreorder |
Add to CLAUDE.md port mapping when implementing.