Files

233 lines
11 KiB
TypeScript

'use client'
import React, { useState } from "react";
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@hanzo/ui";
import { Box } from '@hanzo/ui'
import { Grid } from '@hanzo/ui/grid'
import { Band, FADE, Head, Words } from '@/components/marketing/band'
import { Leaf } from '@hanzo/ui/marketing'
/**
* NO TAILWIND. The section frame is `Band` + `Head`, the panels and the tab
* strip are gui props, and each endpoint card is `Leaf` — the role
* app/globals.css already holds for a card whose edge lifts on approach, which
* is the one thing a style attribute cannot say. Its two knobs carry this
* card's resting and lifted edge; only the corner is stated beside it, because
* these are 12px and a leaf is 16.
*
* The band's ground is `FADE`, the site's named fade off the page ground. It is
* neutral-900 at 20% where this section wrote 30% — one wash, named once,
* instead of a fifth spelling of it.
*
* The active tab is drawn from the STATE this component already holds. It used
* to be a `data-[state=active]:` pair — a selector, so it could only be a class
* — and `activeCategory` was set on every change and read nowhere.
*/
interface EndpointProps {
path: string;
description: string;
}
/** The card's edge at rest and on approach, and the corner it turns. */
const LEAF = {
borderColor: 'var(--neutral-800)',
hoverStyle: { borderColor: 'var(--border)' },
rounded: 'var(--radius-lg)',
bg: 'color-mix(in oklab, var(--neutral-900) 40%, transparent)',
} as const
/** The ink the paths and the group heading share: the foreground at 60%. */
const DIM = 'color-mix(in oklab, var(--foreground) 60%, transparent)'
/** The showing tab's ground. */
const SHOWING = 'color-mix(in oklab, var(--primary) 5%, transparent)'
/** How narrow a column may get before the deck drops to one. */
const PAIR = { min: 320, max: 2 } as const
const EndpointCard = ({ path, description }: EndpointProps) => {
return (
<Leaf minW={0} {...LEAF}>
<Words render="div" mb={8} fontFamily="$mono" fontSize="$2" lineHeight="1.4286" color={DIM} $platform-web={{ wordBreak: 'break-all' }}>{path}</Words>
<Words render="p" fontSize="$2" lineHeight="1.4286" color="var(--muted-foreground)">{description}</Words>
</Leaf>
);
};
const APIEndpoints = () => {
const [activeCategory, setActiveCategory] = useState("commerce");
const endpointCategories = {
commerce: [
{ path: "/cart", description: "List all carts for the current session/user" },
{ path: "/cart/{id}", description: "Get details for a specific cart" },
{ path: "/cart/{id}/set", description: "Update cart items and quantities" },
{ path: "/cart/{id}/discard", description: "Remove a cart" },
{ path: "/checkout/capture/{id}", description: "Capture payment for a previously authorized checkout" },
{ path: "/checkout/charge", description: "Process a direct checkout charge" },
{ path: "/checkout/authorize", description: "Authorize payment for later capture" },
{ path: "/collection/{id}", description: "Get a specific product collection" },
{ path: "/collection", description: "List all product collections" },
{ path: "/coupon", description: "List all available coupons" },
{ path: "/coupon/{id}", description: "Get details for a specific coupon" },
{ path: "/discount", description: "List all available discounts" },
{ path: "/discount/{id}", description: "Get details for a specific discount" },
{ path: "/order/{id}", description: "Get a specific order" },
{ path: "/order", description: "List all orders" },
{ path: "/order/{id}/refund", description: "Process a refund for an order" },
{ path: "/order/{id}/payments", description: "View payment details for an order" },
{ path: "/order/{id}/capture", description: "Capture payment for a previously authorized order" },
{ path: "/product", description: "List all products" },
{ path: "/product/{id}", description: "Get a specific product" },
{ path: "/return/{id}", description: "Get details for a specific return" },
{ path: "/return", description: "List all returns" },
{ path: "/review", description: "List all product reviews" },
{ path: "/review/{id}", description: "Get a specific product review" },
{ path: "/store/{id}", description: "Get a specific store" },
{ path: "/store", description: "List all stores" },
{ path: "/site/{siteid}/deploy", description: "Deploy a site/storefront" },
{ path: "/store/{id}/product/{key}", description: "Get a specific product in a store" },
{ path: "/store/{id}/variant/{key}", description: "Get a specific variant in a store" },
{ path: "/variant/{id}", description: "Get a specific product variant" },
{ path: "/variant", description: "List all product variants" }
],
identity: [
{ path: "/account/login", description: "User authentication endpoint" },
{ path: "/account", description: "Manage user account details" },
{ path: "/account/create", description: "Create a new user account" },
{ path: "/user/{id}", description: "Get or update a specific user" },
{ path: "/user", description: "List all users (admin)" }
],
marketing: [
{ path: "/affiliate/{id}", description: "Get a specific affiliate" },
{ path: "/affiliate/{id}/connect", description: "Connect an affiliate to an account" },
{ path: "/affiliate", description: "List all affiliates" },
{ path: "/affiliate/{id}/transactions", description: "View affiliate transactions" },
{ path: "/referral", description: "List all referrals" },
{ path: "/referral/{id}", description: "Get a specific referral" },
{ path: "/campaign/{id}", description: "Get a specific marketing campaign" },
{ path: "/campaign", description: "List all marketing campaigns" },
{ path: "/form/{id}", description: "Get a specific marketing form" },
{ path: "/form", description: "List all marketing forms" },
{ path: "/form/{id}/submit", description: "Submit data to a marketing form" },
{ path: "/referrer/{id}", description: "Get a specific referrer" },
{ path: "/referrer/{id}/referrals", description: "List referrals for a specific referrer" },
{ path: "/referrer", description: "List all referrers" },
{ path: "/referrer/{id}/transactions", description: "View referrer transactions" },
{ path: "/submission/{id}", description: "Get a specific form submission" },
{ path: "/submission", description: "List all form submissions" },
{ path: "/subscriber/{id}", description: "Get a specific subscriber" },
{ path: "/subscriber", description: "List all subscribers" },
{ path: "/transaction/{id}", description: "Get a specific marketing transaction" }
],
payments: [
{ path: "/invoice/{id}", description: "Get a specific invoice" },
{ path: "/invoice", description: "List all invoices" },
{ path: "/payment/{id}", description: "Get a specific payment" },
{ path: "/payment", description: "List all payments" },
{ path: "/payment/{id}/refund", description: "Process a refund for a payment" },
{ path: "/plan/{id}", description: "Get a specific subscription plan" },
{ path: "/plan", description: "List all subscription plans" },
{ path: "/plan/{id}/refund", description: "Process a refund for a subscription plan" },
{ path: "/subscription/{id}", description: "Get a specific subscription" },
{ path: "/subscription", description: "List all subscriptions" }
],
platform: [
{ path: "/media", description: "List all media assets" },
{ path: "/media/{id}", description: "Get a specific media asset" },
{ path: "/organization/{id}", description: "Get a specific organization" },
{ path: "/organization", description: "List all organizations" },
{ path: "/organization/{id}/integrations", description: "List all integrations for an organization" },
{ path: "/organization/{id}/integrations/{integrationId}", description: "Get a specific integration" },
{ path: "/webhook", description: "List all webhooks" },
{ path: "/webhook/{id}", description: "Get a specific webhook" }
]
};
/**
* A tab, at rest and while its panel is the one showing. The showing one
* takes a faint primary ground in place of the trigger's own; `activeStyle`
* says it too, with the trigger's shadow, so whichever of the two gui applies
* paints the same. The label keeps the trigger's ink.
*/
const tab = (value: string) =>
activeCategory === value
? {
flex: 1,
borderBottomWidth: 0,
background: SHOWING,
activeStyle: { background: SHOWING, shadowColor: '$shadowColor', shadowOpacity: 0.12, shadowRadius: 3, shadowOffset: { width: 0, height: 1 } },
} as const
: { flex: 1, borderBottomWidth: 0 } as const
return (
<Band rule={false} pad="$10" ground={FADE}>
<Head
title="The endpoints"
lede="Grouped by what they are about. Every one of them is scoped to your organisation by the token you call with, so multi-tenancy is not something you implement on top."
/>
<Box width="100%" maxW={1024} mx="auto">
<Tabs defaultValue="commerce" width="100%" onValueChange={setActiveCategory}>
<TabsList
width="100%"
flexWrap="wrap"
height="auto"
marginBottom="$5"
padding="$1"
borderWidth={1}
borderColor="var(--neutral-800)"
borderRadius="var(--radius-lg)"
backgroundColor="$panel"
>
<TabsTrigger value="commerce" {...tab("commerce")}>
Commerce
</TabsTrigger>
<TabsTrigger value="identity" {...tab("identity")}>
Identity
</TabsTrigger>
<TabsTrigger value="marketing" {...tab("marketing")}>
Marketing
</TabsTrigger>
<TabsTrigger value="payments" {...tab("payments")}>
Payments
</TabsTrigger>
<TabsTrigger value="platform" {...tab("platform")}>
Platform
</TabsTrigger>
</TabsList>
{Object.keys(endpointCategories).map((category) => (
<TabsContent key={category} value={category} mt={0}>
<Box
p="$5"
borderWidth={1}
borderColor="var(--neutral-800)"
rounded="var(--radius-xl)"
bg="$panel"
>
<Words render="h3" mb={16} fontSize="$6" lineHeight="1.4" weight="600" color={DIM}>
Hanzo {category.charAt(0).toUpperCase() + category.slice(1)} API Endpoints
</Words>
<Grid columns={PAIR} gap={12}>
{endpointCategories[category as keyof typeof endpointCategories].map((endpoint, index) => (
<EndpointCard
key={index}
path={endpoint.path}
description={endpoint.description}
/>
))}
</Grid>
</Box>
</TabsContent>
))}
</Tabs>
</Box>
</Band>
);
};
export default APIEndpoints;