├── cua-server ├── .env.example ├── tsconfig.json ├── src │ ├── utils │ │ ├── logger.ts │ │ └── testCaseUtils.ts │ ├── handlers │ │ ├── test-case-update-handler.ts │ │ ├── user-messages-handler.ts │ │ ├── test-case-initiation-handler.ts │ │ ├── action-handler.ts │ │ └── cua-loop-handler.ts │ ├── agents │ │ └── test-case-agent.ts │ ├── services │ │ ├── login-service.ts │ │ └── openai-cua-client.ts │ ├── index.ts │ └── lib │ │ ├── constants.ts │ │ └── computer-use-loop.ts ├── package.json ├── .gitignore └── README.md ├── sample-test-app ├── .env.example ├── postcss.config.mjs ├── public │ ├── cover.webp │ ├── notfound.webp │ └── openai-logomark.svg ├── next.config.ts ├── lib │ ├── utils.ts │ ├── shopConfig.ts │ ├── orderService.ts │ ├── styleService.ts │ ├── authService.ts │ ├── styleCache.ts │ └── filterConfig.ts ├── app │ ├── api │ │ ├── store │ │ │ └── styles │ │ │ │ ├── route.ts │ │ │ │ └── [id] │ │ │ │ └── route.ts │ │ ├── auth │ │ │ └── login │ │ │ │ └── route.ts │ │ └── services │ │ │ └── orders │ │ │ └── route.ts │ ├── cart │ │ └── page.tsx │ ├── shop │ │ ├── [category] │ │ │ └── page.tsx │ │ └── page.tsx │ ├── order-success │ │ └── page.tsx │ ├── layout.tsx │ ├── login │ │ └── page.tsx │ ├── page.tsx │ └── globals.css ├── components │ ├── ui │ │ ├── skeleton.tsx │ │ ├── label.tsx │ │ ├── separator.tsx │ │ ├── input.tsx │ │ ├── qt-select.tsx │ │ ├── checkbox.tsx │ │ ├── button.tsx │ │ ├── card.tsx │ │ └── pagination.tsx │ ├── SkeletonCard.tsx │ ├── AuthGuard.tsx │ ├── ShopPageContent.tsx │ ├── Navbar.tsx │ ├── ItemCard.tsx │ ├── Footer.tsx │ ├── ItemsGrid.tsx │ ├── OrderSuccess.tsx │ ├── FilterSidebar.tsx │ └── CartView.tsx ├── eslint.config.mjs ├── stores │ ├── authStore.ts │ ├── styleFiltersStore.ts │ ├── stylesStore.ts │ ├── useAppStore.ts │ └── cartStore.ts ├── components.json ├── tsconfig.json ├── .gitignore ├── README.md ├── hooks │ └── use-toast.ts └── package.json ├── screenshot.jpg ├── frontend ├── app │ ├── favicon.ico │ ├── fonts │ │ ├── GeistVF.woff │ │ └── GeistMonoVF.woff │ ├── layout.tsx │ ├── globals.css │ └── page.tsx ├── next.config.mjs ├── types │ ├── cuaInputType.ts │ └── tanstack-table.d.ts ├── postcss.config.mjs ├── .env.example ├── components │ ├── AppHeader.tsx │ ├── ui │ │ ├── textarea.tsx │ │ ├── label.tsx │ │ ├── input.tsx │ │ ├── separator.tsx │ │ ├── switch.tsx │ │ ├── button.tsx │ │ ├── tabs.tsx │ │ └── card.tsx │ ├── SidePanel.tsx │ ├── SocketIOManager.tsx │ └── TaskSteps.tsx ├── lib │ ├── logger.ts │ ├── assistant.ts │ ├── utils.ts │ └── constants.ts ├── components.json ├── eslint.config.mjs ├── .gitignore ├── tsconfig.json ├── README.md ├── stores │ ├── useConversationStore.ts │ └── useTaskStore.ts ├── package.json ├── tailwind.config.ts └── public │ └── openai_logo.svg ├── package.json ├── .gitignore ├── README.md └── LICENSE /cua-server/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=your-openai-key 2 | -------------------------------------------------------------------------------- /sample-test-app/.env.example: -------------------------------------------------------------------------------- 1 | ADMIN_USERNAME=test_user_name 2 | ADMIN_PASSWORD=test_password -------------------------------------------------------------------------------- /screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/openai-testing-agent-demo/HEAD/screenshot.jpg -------------------------------------------------------------------------------- /frontend/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/openai-testing-agent-demo/HEAD/frontend/app/favicon.ico -------------------------------------------------------------------------------- /frontend/app/fonts/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/openai-testing-agent-demo/HEAD/frontend/app/fonts/GeistVF.woff -------------------------------------------------------------------------------- /frontend/app/fonts/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/openai-testing-agent-demo/HEAD/frontend/app/fonts/GeistMonoVF.woff -------------------------------------------------------------------------------- /sample-test-app/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | const config = { 2 | plugins: ["@tailwindcss/postcss"], 3 | }; 4 | 5 | export default config; 6 | -------------------------------------------------------------------------------- /sample-test-app/public/cover.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/openai-testing-agent-demo/HEAD/sample-test-app/public/cover.webp -------------------------------------------------------------------------------- /sample-test-app/public/notfound.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openai/openai-testing-agent-demo/HEAD/sample-test-app/public/notfound.webp -------------------------------------------------------------------------------- /frontend/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | devIndicators: false, 4 | }; 5 | 6 | export default nextConfig; 7 | -------------------------------------------------------------------------------- /frontend/types/cuaInputType.ts: -------------------------------------------------------------------------------- 1 | export interface ModelInput { 2 | screenshotBase64: string; 3 | previousResponseId?: string; 4 | lastCallId?: string; 5 | } -------------------------------------------------------------------------------- /sample-test-app/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | devIndicators: false, 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /frontend/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /sample-test-app/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /frontend/types/tanstack-table.d.ts: -------------------------------------------------------------------------------- 1 | import type * as React from "react"; 2 | 3 | declare module "@tanstack/react-table" { 4 | interface ColumnMeta { 5 | style?: React.CSSProperties; 6 | } 7 | } -------------------------------------------------------------------------------- /sample-test-app/app/api/store/styles/route.ts: -------------------------------------------------------------------------------- 1 | import { NextResponse } from "next/server"; 2 | import { getStylesCache } from "@/lib/styleCache"; 3 | 4 | export async function GET() { 5 | const styles = getStylesCache(); 6 | return NextResponse.json(styles); 7 | } 8 | -------------------------------------------------------------------------------- /cua-server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "CommonJS", 5 | "outDir": "dist", 6 | "rootDir": "src", 7 | "strict": true, 8 | "esModuleInterop": true, 9 | "resolveJsonModule": true, 10 | "moduleResolution": "node" 11 | }, 12 | "include": ["src"] 13 | } 14 | -------------------------------------------------------------------------------- /sample-test-app/app/cart/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | import CartView from "@/components/CartView"; 5 | 6 | export default function CartPage() { 7 | return ( 8 |
9 |

Shopping Cart

10 | 11 |
12 | ); 13 | } -------------------------------------------------------------------------------- /sample-test-app/components/ui/skeleton.tsx: -------------------------------------------------------------------------------- 1 | import { cn } from "@/lib/utils" 2 | 3 | function Skeleton({ className, ...props }: React.ComponentProps<"div">) { 4 | return ( 5 |
10 | ) 11 | } 12 | 13 | export { Skeleton } 14 | -------------------------------------------------------------------------------- /sample-test-app/lib/shopConfig.ts: -------------------------------------------------------------------------------- 1 | export const categoryFilter: Record< 2 | string, 3 | { key: keyof import("@/stores/styleFiltersStore").StyleFilters; value: string } | null 4 | > = { 5 | "just-in": null, 6 | clothes: { key: "masterCategory", value: "Apparel" }, 7 | shoes: { key: "masterCategory", value: "Footwear" }, 8 | accessories: { key: "masterCategory", value: "Accessories" }, 9 | offers: null, 10 | }; -------------------------------------------------------------------------------- /frontend/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=your-openai-key 2 | # Optional: Set the display size for the browser. 3 | DISPLAY_WIDTH=1024 4 | DISPLAY_HEIGHT=768 5 | # Optional: Add any environment specific instructions here. This could be used to specify the operating system, browser, specific instructions. 6 | ENV_SPECIFIC_INSTRUCTIONS="This is MacOS, so use CMD key for actions that are typically done with Control key on Windows, E.g. to select text use CMD + A for selecting text." -------------------------------------------------------------------------------- /sample-test-app/lib/orderService.ts: -------------------------------------------------------------------------------- 1 | export async function checkoutOrder(payload: unknown) { 2 | const res = await fetch("/api/services/orders", { 3 | method: "POST", 4 | headers: { "Content-Type": "application/json" }, 5 | body: JSON.stringify(payload), 6 | }); 7 | if (!res.ok) { 8 | throw new Error("Checkout failed"); 9 | } 10 | const data: { orderId: string; status?: string; arrivalDate?: string } = await res.json(); 11 | return data; 12 | } 13 | -------------------------------------------------------------------------------- /sample-test-app/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | ]; 15 | 16 | export default eslintConfig; 17 | -------------------------------------------------------------------------------- /frontend/components/AppHeader.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import React from "react"; 4 | 5 | export default function AppHeader() { 6 | return ( 7 |
8 |

9 | Automated Testing Agent 10 |

11 |

12 | Authors and executes tests on your behalf using the OpenAI CUA 13 | model. 14 |

15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /sample-test-app/stores/authStore.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | import { persist } from "zustand/middleware"; 3 | 4 | interface AuthState { 5 | token: string | null; 6 | setToken: (token: string | null) => void; 7 | logout: () => void; 8 | } 9 | 10 | export const useAuthStore = create()( 11 | persist( 12 | (set) => ({ 13 | token: null, 14 | setToken: (token) => set({ token }), 15 | logout: () => set({ token: null }), 16 | }), 17 | { name: "auth" } 18 | ) 19 | ); -------------------------------------------------------------------------------- /frontend/lib/logger.ts: -------------------------------------------------------------------------------- 1 | import pino from "pino"; 2 | 3 | const isProduction = process.env.NODE_ENV === "production"; 4 | 5 | const logger = pino({ 6 | level: process.env.LOG_LEVEL || "info", 7 | ...(isProduction 8 | ? {} 9 | : { 10 | transport: { 11 | target: "pino-pretty", 12 | options: { 13 | colorize: true, 14 | }, 15 | }, 16 | }), 17 | }); 18 | 19 | logger.info(`Logger initialized with log level: ${logger.level}`); 20 | 21 | export default logger; 22 | -------------------------------------------------------------------------------- /cua-server/src/utils/logger.ts: -------------------------------------------------------------------------------- 1 | import pino from "pino"; 2 | 3 | const isProduction = process.env.NODE_ENV === "production"; 4 | 5 | const logger = pino({ 6 | level: process.env.LOG_LEVEL || "info", 7 | ...(isProduction 8 | ? {} 9 | : { 10 | transport: { 11 | target: "pino-pretty", 12 | options: { 13 | colorize: true, 14 | }, 15 | }, 16 | }), 17 | }); 18 | 19 | logger.info(`Logger initialized with log level: ${logger.level}`); 20 | 21 | export default logger; 22 | -------------------------------------------------------------------------------- /sample-test-app/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "", 8 | "css": "app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "stone", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } 22 | -------------------------------------------------------------------------------- /cua-server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "tsc", 4 | "start": "node dist/index.js", 5 | "dev": "ts-node-dev --env-file=.env.development --respawn --transpile-only src/index.ts" 6 | }, 7 | "dependencies": { 8 | "socket.io": "^4.8.1", 9 | "pino": "^9.6.0", 10 | "pino-pretty": "^13.0.0", 11 | "playwright": "^1.52.0", 12 | "openai": "^4.87.3", 13 | "uuid": "^11.1.0" 14 | }, 15 | "devDependencies": { 16 | "typescript": "^5", 17 | "ts-node-dev": "^2.0.0", 18 | "@types/node": "^20" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import { dirname } from "path"; 2 | import { fileURLToPath } from "url"; 3 | import { FlatCompat } from "@eslint/eslintrc"; 4 | 5 | const __filename = fileURLToPath(import.meta.url); 6 | const __dirname = dirname(__filename); 7 | 8 | const compat = new FlatCompat({ 9 | baseDirectory: __dirname, 10 | }); 11 | 12 | const eslintConfig = [ 13 | ...compat.extends("next/core-web-vitals", "next/typescript"), 14 | { 15 | rules: { 16 | "@typescript-eslint/no-explicit-any": "off", 17 | }, 18 | }, 19 | ]; 20 | 21 | export default eslintConfig; 22 | -------------------------------------------------------------------------------- /sample-test-app/lib/styleService.ts: -------------------------------------------------------------------------------- 1 | export async function fetchAllStyles() { 2 | const res = await fetch("/api/store/styles"); 3 | if (!res.ok) { 4 | throw new Error("Styles not found"); 5 | } 6 | // The API returns a raw array of styles 7 | const data: unknown[] = await res.json(); 8 | return data; 9 | } 10 | 11 | export async function fetchStyleById(id: number) { 12 | const res = await fetch(`/api/store/styles/${id}`); 13 | if (!res.ok) { 14 | throw new Error("Style not found"); 15 | } 16 | const data: { style: unknown } = await res.json(); 17 | return data.style; 18 | } 19 | -------------------------------------------------------------------------------- /sample-test-app/lib/authService.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Client-side helper: sends username/password to the backend 3 | * and returns the JWT (or any token string your API issues). 4 | */ 5 | export async function login( 6 | username: string, 7 | password: string 8 | ): Promise { 9 | const res = await fetch("/api/auth/login", { 10 | method: "POST", 11 | headers: { "Content-Type": "application/json" }, 12 | body: JSON.stringify({ username, password }), 13 | }); 14 | if (!res.ok) { 15 | throw new Error("Login failed"); 16 | } 17 | const data: { token: string } = await res.json(); 18 | 19 | return data.token; 20 | } 21 | -------------------------------------------------------------------------------- /sample-test-app/app/api/auth/login/route.ts: -------------------------------------------------------------------------------- 1 | export async function POST(request: Request) { 2 | const { username, password } = (await request.json()) as { 3 | username?: string; 4 | password?: string; 5 | }; 6 | 7 | const expectedUser = process.env.ADMIN_USERNAME ?? ""; 8 | const expectedPass = process.env.ADMIN_PASSWORD ?? ""; 9 | 10 | if (username === expectedUser && password === expectedPass) { 11 | // TODO: replace with a proper JWT from your real auth provider 12 | return Response.json({ token: "dummy-jwt-token" }); 13 | } 14 | 15 | return Response.json({ error: "Invalid credentials" }, { status: 401 }); 16 | } 17 | -------------------------------------------------------------------------------- /sample-test-app/app/shop/[category]/page.tsx: -------------------------------------------------------------------------------- 1 | import { notFound } from "next/navigation"; 2 | import ShopPageContent from "@/components/ShopPageContent"; 3 | import { categoryFilter } from "@/lib/shopConfig"; 4 | 5 | interface ShopCategoryPageProps { 6 | params: Promise<{ category: string }>; 7 | } 8 | 9 | /** 10 | * Server component for /shop/[category] 11 | */ 12 | export default async function ShopCategoryPage({ 13 | params, 14 | }: ShopCategoryPageProps) { 15 | const { category } = await params; 16 | // Validate category 17 | if (!(category in categoryFilter)) notFound(); 18 | return ; 19 | } 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "testing-agent-monorepo", 3 | "private": true, 4 | "workspaces": [ 5 | "sample-test-app", 6 | "frontend", 7 | "cua-server" 8 | ], 9 | "scripts": { 10 | "dev:sample-test-app": "npm run dev --prefix sample-test-app", 11 | "dev:frontend": "npm run dev --prefix frontend", 12 | "dev:cua-server": "npm run dev --prefix cua-server", 13 | "dev": "concurrently \"npm:dev:sample-test-app\" \"npm:dev:frontend\" \"npm:dev:cua-server\"" 14 | }, 15 | "devDependencies": { 16 | "concurrently": "^8.2.2" 17 | }, 18 | "overrides": { 19 | "@types/react": "18.3.20", 20 | "@types/react-dom": "18.3.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | /.turbo 14 | 15 | # testing 16 | /coverage 17 | 18 | # next.js 19 | /.next/ 20 | /out/ 21 | 22 | # production 23 | /build 24 | 25 | # misc 26 | .DS_Store 27 | *.pem 28 | 29 | # debug 30 | npm-debug.log* 31 | yarn-debug.log* 32 | yarn-error.log* 33 | .pnpm-debug.log* 34 | 35 | # env files (can opt-in for committing if needed) 36 | .env* 37 | !.env.example 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /sample-test-app/app/shop/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import StyleCardContainer from "@/components/ItemsGrid"; 3 | import FilterSidebar from "@/components/FilterSidebar"; 4 | 5 | /** 6 | * Shop landing – shows all products with filters. 7 | */ 8 | export default function ShopHomePage() { 9 | return ( 10 |
11 |

Shop All

12 |
13 |
14 | 15 |
16 |
17 | 18 |
19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /cua-server/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | !.env.example 36 | 37 | # vercel 38 | .vercel 39 | 40 | # typescript 41 | *.tsbuildinfo 42 | next-env.d.ts 43 | 44 | screenshot.png -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | .env* 35 | !.env.example 36 | 37 | # vercel 38 | .vercel 39 | 40 | # typescript 41 | *.tsbuildinfo 42 | next-env.d.ts 43 | 44 | public/test_results 45 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /sample-test-app/app/order-success/page.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { useSearchParams } from "next/navigation"; 4 | import OrderSuccess from "@/components/OrderSuccess"; 5 | 6 | export default function OrderSuccessPage() { 7 | const searchParams = useSearchParams(); 8 | const orderId = searchParams.get("orderId") ?? ""; 9 | const arrivalDate = searchParams.get("arrivalDate") ?? ""; 10 | 11 | if (!orderId) { 12 | return

No order information available.

; 13 | } 14 | 15 | return ( 16 |
17 |
18 | 19 |
20 |
21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /sample-test-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | -------------------------------------------------------------------------------- /sample-test-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | .pnpm-debug.log* 32 | 33 | # env files (can opt-in for committing if needed) 34 | !.env.example 35 | .env* 36 | 37 | # vercel 38 | .vercel 39 | 40 | # typescript 41 | *.tsbuildinfo 42 | next-env.d.ts 43 | 44 | public/data/orders.csv 45 | -------------------------------------------------------------------------------- /sample-test-app/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as LabelPrimitive from "@radix-ui/react-label" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | function Label({ 9 | className, 10 | ...props 11 | }: React.ComponentProps) { 12 | return ( 13 | 21 | ) 22 | } 23 | 24 | export { Label } 25 | -------------------------------------------------------------------------------- /frontend/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react" 2 | 3 | import { cn } from "@/lib/utils" 4 | 5 | const Textarea = React.forwardRef< 6 | HTMLTextAreaElement, 7 | React.ComponentProps<"textarea"> 8 | >(({ className, ...props }, ref) => { 9 | return ( 10 |