Skip to main content

Overview

This guide covers setting up a local development environment for building custom integrations, extending the Etienne Intelligence platform, or contributing to the codebase.
For SaaS users: If you’re using Etienne Intelligence as a hosted SaaS platform, you don’t need this guide. See the Quickstart instead.

Prerequisites

Ensure you have the following installed on your machine:

Node.js 18+

Download from nodejs.org. Verify with node --version.

npm or pnpm

Comes with Node.js. Alternatively, install pnpm for faster installs.

Git

Version control required for cloning the repository. Get it from git-scm.com.

Zenoti account

Admin access needed to generate API credentials for local testing.

Step 1: Clone the repository

git clone https://github.com/your-org/etienne-intelligence.git
cd etienne-intelligence
Repository structure:
  • /src — React application source code (TypeScript + Vite)
  • /src/modules — Feature modules (dashboard, command-center, intelligence, scheduling)
  • /src/integrations/zenoti — Zenoti API client and data mappers
  • /src/components — Shared UI components (MetricCard, ActivityFeed, etc.)

Step 2: Install dependencies

The platform uses React 19, TypeScript, Vite, TanStack Query, and Recharts for visualization.
npm install
Key dependencies:
  • react react-dom — React 19 framework
  • react-router-dom — Client-side routing for dashboard navigation
  • @tanstack/react-query — Server state management and data fetching
  • recharts — Revenue charts and visualizations
  • framer-motion — Animation library for smooth transitions
  • zustand — Lightweight state management for auth and location filters
  • lucide-react — Icon library
  • tailwindcss — Utility-first CSS framework (v4)
Installation typically takes 30-60 seconds. If you encounter peer dependency warnings, they’re usually safe to ignore.

Step 3: Configure environment variables

Create a local environment file to store Zenoti API credentials.
1

Copy example file

cp .env.example .env.local
2

Generate Zenoti credentials

Follow the same process as the Quickstart guide:
  1. Log into Zenoti Admin > Setup > Apps
  2. Create a backend application named “Etienne Dev”
  3. Copy Application ID, Secret Key, and API Key
3

Edit .env.local

Open .env.local and fill in your credentials:
# Zenoti API Configuration
VITE_ZENOTI_BASE_URL=https://api.zenoti.com
VITE_ZENOTI_API_KEY=your_api_key_here
VITE_ZENOTI_APP_ID=your_app_id_here
VITE_ZENOTI_SECRET_KEY=your_secret_key_here
VITE_ZENOTI_ACCOUNT_NAME=your_organization_name
Security: Never commit .env.local to version control. It’s already in .gitignore, but double-check before pushing code.

Environment variable reference

VariableDescriptionExample
VITE_ZENOTI_BASE_URLZenoti API endpoint (US or EU)https://api.zenoti.com
VITE_ZENOTI_API_KEYLong-lived API key (~1 year validity)zapi_abc123...
VITE_ZENOTI_APP_IDApplication identifier from Zenotiapp-12345
VITE_ZENOTI_SECRET_KEYAuthentication secret (treat as password)sk_live_...
VITE_ZENOTI_ACCOUNT_NAMEYour organization name in ZenotiAcme Wellness Spa

Step 4: Start development server

Run the Vite development server with hot module replacement (HMR).
npm run dev
Expected output:
VITE v7.3.1  ready in 423 ms

  Local:   http://localhost:5173/
  Network: use --host to expose
  press h to show help
Open http://localhost:5173 in your browser. You should see the Etienne Intelligence dashboard.
Hot reload: Changes to .tsx or .ts files trigger instant browser updates without losing state. CSS changes apply immediately.

Step 5: Verify Zenoti connection

Test the integration to ensure data flows correctly.
1

Check sync badge

Look for the Zenoti sync badge in the top-right corner of the dashboard. It should show “Synced” with a green indicator.
2

Inspect network requests

Open browser DevTools (F12) and go to the Network tab. Filter by zenoti. You should see API calls to endpoints like:
  • /v1/appointments
  • /v1/guests
  • /v1/invoices
3

Review console logs

Check the Console tab for any authentication errors. A successful connection shows:
[Zenoti] Connected to https://api.zenoti.com
[Zenoti] Synced 247 appointments, 1,832 guests
Common errors:
  • 401 Unauthorized: Check your API Key and Secret Key
  • 404 Not Found: Verify Base URL matches your Zenoti region (US vs EU)
  • CORS errors: Zenoti API doesn’t allow direct browser calls. Use a proxy in production.

Project structure

Understanding the codebase organization helps you navigate efficiently.
etienne-intelligence/
├── src/
│   ├── App.tsx                    # Root component with routing
│   ├── main.tsx                   # Vite entry point
│   ├── components/                # Shared UI components
│   │   ├── MetricCard.tsx         # Revenue metric display cards
│   │   ├── ActivityFeed.tsx       # Live activity stream
│   │   ├── AgentStatusBadge.tsx   # AI agent status indicators
│   │   └── ZenotiSyncBadge.tsx    # Real-time sync status
│   ├── modules/                   # Feature modules
│   │   ├── dashboard/             # Main dashboard (DashboardHome.tsx)
│   │   ├── command-center/        # Conversation intelligence
│   │   ├── intelligence/          # Revenue analytics + AI analyst
│   │   └── scheduling/            # Smart scheduling engine
│   ├── integrations/              # External API clients
│   │   └── zenoti/
│   │       ├── client.ts          # HTTP client with auth
│   │       ├── endpoints.ts       # API endpoint definitions
│   │       ├── hooks.ts           # React Query hooks (useAppointments, etc.)
│   │       ├── mappers.ts         # Zenoti → app data transformers
│   │       └── types.ts           # TypeScript interfaces for Zenoti data
│   ├── stores/                    # Zustand state management
│   │   ├── useAuthStore.ts        # User role and authentication
│   │   ├── useLocationStore.ts    # Selected location filter
│   │   └── useChatStore.ts        # AI analyst conversation state
│   ├── layouts/
│   │   └── DashboardLayout.tsx    # Shared sidebar + navigation
│   ├── lib/
│   │   └── utils.ts               # Helper functions (formatCurrency, etc.)
│   └── types/
│       └── index.ts               # Global TypeScript types
├── .env.example                   # Environment variable template
├── package.json                   # Dependencies and scripts
├── vite.config.ts                 # Vite build configuration
├── tsconfig.json                  # TypeScript compiler options
└── tailwind.config.js             # Tailwind CSS configuration

Development workflow

Running tests

npm run test
Test suite uses Vitest and React Testing Library. Coverage reports generate in /coverage.

Building for production

npm run build
Optimized build outputs to /dist. Vite automatically:
  • Minifies JavaScript and CSS
  • Tree-shakes unused code
  • Generates source maps
  • Splits vendor and app bundles

Linting and formatting

npm run lint
ESLint checks for code quality issues. Auto-fix with:
npm run lint -- --fix

Preview production build

npm run preview
Serves the /dist folder locally at http://localhost:4173 to test production builds before deployment.

Common development tasks

  1. Create folder in /src/modules/[module-name]/
  2. Build Overview.tsx component with module layout
  3. Add route to App.tsx:
<Route path="/module-name" element={<ModuleOverview />} />
  1. Update sidebar navigation in DashboardLayout.tsx
  1. Define endpoint in /src/integrations/zenoti/endpoints.ts:
export const getServices = async () => {
  return zenotiClient.get('/v1/services')
}
  1. Create React Query hook in hooks.ts:
export const useServices = () => {
  return useQuery({
    queryKey: ['zenoti', 'services'],
    queryFn: getServices,
  })
}
  1. Use hook in component:
const { data: services } = useServices()
Use the MetricCard component with these props:
<MetricCard
  label="Your Metric Name"
  value={12345}
  format="currency" // or "percent", "time", "number"
  trend={15.8} // percentage change
  trendLabel="vs last month"
  delay={0} // animation delay in ms
  dataSource="Zenoti"
/>
Edit /src/lib/ai-responses.ts. The generateAIResponse() function maps user queries to response templates. Add new patterns:
if (query.includes('staff performance')) {
  return `## Staff Performance Analysis\n\nTop performers this month:...`
}

Troubleshooting

Another process is using the default Vite port. Either:
  • Kill the process: lsof -ti:5173 | xargs kill
  • Or specify a different port: vite --port 3000
Dependency mismatch. Reinstall:
rm -rf node_modules package-lock.json
npm install
Zenoti allows 100 requests/minute. Use React Query’s caching:
useQuery({
  queryKey: ['appointments'],
  queryFn: getAppointments,
  staleTime: 5 * 60 * 1000, // 5 min cache
})
Tailwind rebuild issue. Restart dev server:
# Stop server (Ctrl+C)
npm run dev

Next steps

API reference

Explore Etienne’s REST API for building custom integrations

Component library

Documentation for all reusable UI components

Zenoti integration guide

Deep dive into Zenoti API client architecture

Contributing guidelines

How to contribute code, report bugs, and request features
Development environment ready! You can now build custom features, extend integrations, or contribute to the Etienne Intelligence platform.

Build docs developers (and LLMs) love