import { useCallback, useState } from 'react'; import { Box, Button, Card, CardContent, CardDescription, CardHeader, CardTitle, Separator, Stack, Text } from '@jtl-software/platform-ui-react'; import { BarChart3, Database, Package, ShoppingCart } from 'lucide-react'; import { gql } from 'graphql-request'; /** The example ERP queries both demos run. They need the `items.read` and `salesorders.read` scopes. */ const QUERIES = { topItems: gql`query TopItems { QueryItems(first: 5, order: [{ stockInOrders: DESC }]) { totalCount nodes { sku name stockInOrders salesPriceGross } } }`, recentOrders: gql`query RecentOrders { QuerySalesOrders(first: 5, order: [{ salesOrderDate: DESC }]) { totalCount nodes { salesOrderNumber salesOrderDate totalGrossAmount currencyIso companyName } } }`, stockOverview: gql`query StockOverview { QueryItems(first: 5, order: [{ stockTotal: DESC }]) { totalCount nodes { sku name stockTotal stockAvailable stockInOrders } } }`, }; type QueryKey = keyof typeof QUERIES; const QUERY_META: Record }> = { topItems: { label: 'Top Items by Orders', icon: ShoppingCart }, recentOrders: { label: 'Recent Sales Orders', icon: BarChart3 }, stockOverview: { label: 'Stock Overview', icon: Package }, }; const formatValue = (value: unknown): string => { if (value == null) return '–'; if (typeof value === 'number') return value.toLocaleString('de-DE'); if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}/.test(value)) { return new Date(value).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric' }); } return String(value); }; /** Runs the shared ERP queries and renders the results. `request` is the transport each demo supplies: * the client-server demo posts to its backend, the serverless demo calls the JTL API directly. */ const QueryDemo: React.FC<{ title: string; description: React.ReactNode; request: (query: string) => Promise> }> = ({ title, description, request }) => { const [error, setError] = useState(null); const [loading, setLoading] = useState(null); const [results, setResults] = useState>({}); const runQuery = useCallback( async (key: QueryKey) => { setLoading(key); setError(null); try { const data = await request(QUERIES[key]); setResults(prev => ({ ...prev, [key]: data })); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setLoading(null); } }, [request], ); const renderResult = (key: QueryKey) => { const data = results[key] as Record[] }> | undefined; if (!data) return null; const queryResult = Object.values(data)[0]; if (!queryResult?.nodes?.length) { return ( No results found. ); } const columns = Object.keys(queryResult.nodes[0]); return ( {queryResult.totalCount} total results — showing first {queryResult.nodes.length} {columns.map(col => ( ))} {queryResult.nodes.map((node, i) => ( {columns.map(col => ( ))} ))}
{col}
{formatValue(node[col])}
); }; return ( {title} {description} {error && ( {error} )} {(Object.keys(QUERIES) as QueryKey[]).map(key => { const meta = QUERY_META[key]; const Icon = meta.icon; return ( {meta.label}
{QUERIES[key].trim()}