App Client Package

React client components package for the main app

Overview

@zooly/app-client is a React client package that provides reusable UI components and screens for the main application. This package contains pure React code that doesn't assume a specific framework (like Next.js), but uses Tailwind CSS for styling.

Package Details

  • Package Name: @zooly/app-client
  • Location: packages/client
  • Type: Client-side React components

Key Features

  • Pure React components that are framework-agnostic
  • Uses Tailwind CSS for styling
  • Includes reusable UI components and screens
  • All React element divs include data-testid attributes for automated testing

Components

The package includes:

  • Components: M3Button, Sidebar, UserHome
  • Screens: DashboardScreen, LicenseImageScreen, LicenseMerchScreen, LicenseVoiceScreen, ProfileSetupScreen, SetupScreen

Usage

This package is used by Next.js apps and Vite apps as a shared component library. It provides the core UI building blocks for the application.

Utilities and HTTP Client

fetchJson

@zooly/app-client exports fetchJson (formerly zFetch), a lightweight wrapper around standard fetch tailored for JSON API endpoints:

  • Automatically serializes JSON objects passed in options.body (JSON.stringify(body)).
  • Sets Content-Type: application/json header by default.
  • Throws an informative Error if response.ok is false.
  • Automatically parses and returns the JSON payload response.json().
import { fetchJson } from "@zooly/app-client";

// GET request with JSON parsing & error checking
const user = await fetchJson<UserProfile>("/api/account/profile");

// POST request with automatic body serialization
const result = await fetchJson<SaveResult>("/api/account/settings", {
  method: "POST",
  body: { theme: "dark", notifications: true },
});

Act-As & Account-Scoped Fetch Architecture

In Zooly2, "Acting As" another account (e.g. from the admin dashboard) does not require manual injection of client IDs in individual API functions.

How it Works

  1. Storage & Scope: The active client ID is stored in sessionStorage (zly_acting_as_account_id) via packages/offers/client/src/lib/client-id.ts. It is isolated to a single browser tab and automatically disappears when the tab is closed.
  2. Fetch Interceptor (installActAsFetchInterceptor):
    • The offers/dashboard client installs actAsFetch by monkey-patching window.fetch.
    • Every outbound request targeting /api/ automatically receives the x-client-id: <accountId> header.
    • Non-API requests (S3 uploads, Stripe Elements) pass through untouched.
  3. Transparent to Callers: Whether a component calls fetchJson(...), typed @zooly/api-client, or third-party client packages using global fetch, the x-client-id header is attached without manual parameter passing.
  4. Server Middleware: Next.js middleware reads x-client-id and converts it into a per-request cookie (zly_acting_as_account_id) for downstream auth handlers.

Summary of Networking Functions

Function / SymbolPackagePurpose
fetchJson(url, options)@zooly/app-clientJSON request helper (auto-stringifies body, sets headers, handles HTTP errors).
actAsFetch(input, init)packages/offers/clientLow-level fetch wrapper adhering to typeof fetch that injects x-client-id.
installActAsFetchInterceptor()packages/offers/clientBootstraps actAsFetch on window.fetch at dashboard startup.
createOffersApi()@zooly/api-clientStrongly-typed API client contracts for offer endpoints.

Dependencies

  • React 18+
  • Tailwind CSS
  • Framer Motion
  • Class Variance Authority
  • Tailwind Merge