O7W4FZVRKDQDAAXEW4T7P262PPRILRCSSACODMUTQZ6VNR36PVCQC import * as React from "react"import { cn } from "@/lib/utils"function Table({ className, ...props }: React.ComponentProps<"table">) {return (<divdata-slot="table-container"className="relative w-full overflow-x-auto"><tabledata-slot="table"className={cn("w-full caption-bottom text-sm", className)}{...props}/></div>)}function TableHeader({ className, ...props }: React.ComponentProps<"thead">) {return (<theaddata-slot="table-header"className={cn("[&_tr]:border-b", className)}{...props}/>)}function TableBody({ className, ...props }: React.ComponentProps<"tbody">) {return (<tbodydata-slot="table-body"className={cn("[&_tr:last-child]:border-0", className)}{...props}/>)}function TableFooter({ className, ...props }: React.ComponentProps<"tfoot">) {return (<tfootdata-slot="table-footer"className={cn("bg-muted/50 border-t font-medium [&>tr]:last:border-b-0",className)}{...props}/>)}function TableRow({ className, ...props }: React.ComponentProps<"tr">) {return (<trdata-slot="table-row"className={cn("hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",className)}{...props}/>)}function TableHead({ className, ...props }: React.ComponentProps<"th">) {return (<thdata-slot="table-head"className={cn("text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",className)}{...props}/>)}function TableCell({ className, ...props }: React.ComponentProps<"td">) {return (<tddata-slot="table-cell"className={cn("p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",className)}{...props}/>)}function TableCaption({className,...props}: React.ComponentProps<"caption">) {return (<captiondata-slot="table-caption"className={cn("text-muted-foreground mt-4 text-sm", className)}{...props}/>)}export {Table,TableHeader,TableBody,TableFooter,TableHead,TableRow,TableCell,TableCaption,}
? (<div><h2>{user?.givenName} {user?.familyName}</h2><p>{user?.email}</p><p>{user?.id}</p>
&& (<div className="flex items-center justify-between py-4 mb-6 border-b"><div className="flex items-center gap-3">{user?.picture? (<imgsrc={user.picture}alt="User avatar"className="w-8 h-8 rounded-full"/>): (<div className="w-8 h-8 rounded-full bg-stone-200 flex items-center justify-center text-stone-600 text-sm font-medium">{user?.givenName?.[0] || user?.email?.[0] || "U"}</div>)}<div><h2 className="text-base font-medium">{user?.givenName ? `${user.givenName} ${user?.familyName || ""}` : user?.email}</h2>{user?.givenName && user?.email && <p className="text-xs text-muted-foreground">{user.email}</p>}</div>
import { useKindeAuth } from "@kinde-oss/kinde-auth-react";import React from "react";import Authenticate from "./Authenticate";const PublicContent: React.FC = () => {const { isAuthenticated, isLoading } = useKindeAuth();if (isLoading) {return (<div className="h-screen flex items-center justify-center"><p className="text-lg text-gray-500">Loading...</p></div>);}return (!isAuthenticated && (<div className="h-screen flex flex-col items-center justify-center"><div className="text-center mb-10"><h1 className="text-4xl font-bold mb-2">Skraak</h1><p className="text-lg text-gray-500">Organise and label bioacoustic data</p></div><Authenticate /></div>));};export default PublicContent;
import { useKindeAuth } from "@kinde-oss/kinde-auth-react";import React, { useState, useEffect } from "react";import {Table,TableBody,TableCell,TableHead,TableHeader,TableRow,} from "./ui/table";interface Dataset {id: string;name: string;public: boolean;description?: string;createdBy: string;createdAt: string;lastModified: string;modifiedBy: string;owner: string;active: boolean;type: string;}interface DatasetsResponse {data: Dataset[];}const Datasets: React.FC = () => {const { isAuthenticated, isLoading: authLoading, getAccessToken } = useKindeAuth();const [datasets, setDatasets] = useState<Dataset[]>([]);const [loading, setLoading] = useState<boolean>(true);const [error, setError] = useState<string | null>(null);useEffect(() => {const fetchDatasets = async () => {if (!isAuthenticated) {if (!authLoading) {setLoading(false);}return;}setLoading(true);setError(null);try {const accessToken = await getAccessToken();const response = await fetch("/api/datasets", {headers: {Authorization: `Bearer ${accessToken}`,},});if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}const data = await response.json() as DatasetsResponse;setDatasets(data.data);} catch (err) {setError(err instanceof Error ? err.message : "Failed to fetch datasets");console.error("Error fetching datasets:", err);} finally {setLoading(false);}};if (isAuthenticated && !authLoading) {fetchDatasets();}}, [isAuthenticated, authLoading, getAccessToken]);return (<div className="card p-6 bg-white shadow-sm rounded-lg"><h2 className="text-xl font-bold mb-4">Datasets</h2>{loading && <p className="text-gray-500">Loading datasets...</p>}{!isAuthenticated && !authLoading &&<p className="text-amber-600 mb-4">Please log in to access datasets</p>}{error && <p className="text-red-600 mb-4">Error: {error}</p>}{!loading && !error && isAuthenticated && datasets.length > 0 && (<div className="w-full overflow-visible"><Table><TableHeader className="bg-muted"><TableRow className="border-b-2 border-primary/20"><TableHead className="w-[180px] py-3 font-bold text-sm uppercase">Name</TableHead><TableHead className="w-[100px] py-3"></TableHead><TableHead className="py-3 font-bold text-sm uppercase">Description</TableHead></TableRow></TableHeader><TableBody>{datasets.map((dataset) => (<TableRow key={dataset.id}><TableCell className="font-medium whitespace-normal break-words">{dataset.name}</TableCell><TableCell className="text-center"><span className={`inline-block px-2 py-1 rounded-full text-xs font-medium ${dataset.type === 'organize' ? 'bg-stone-300 text-stone-800' :dataset.type === 'train' ? 'bg-stone-200 text-stone-700' :'bg-stone-100 text-stone-600'}`}>{dataset.type}</span></TableCell><TableCell className="whitespace-normal break-words">{dataset.description || "—"}</TableCell></TableRow>))}</TableBody></Table></div>)}{!loading && !error && isAuthenticated && datasets.length === 0 && (<p className="text-gray-500">No datasets available</p>)}</div>);};export default Datasets;
import React, { useState } from "react";import { Button } from "./ui/button";const Count: React.FC = () => {const [count, setCount] = useState(0);return (<div className="card"><ButtononClick={() => setCount((count) => count + 1)}aria-label="increment"variant="default">count is {count}</Button></div>);};export default Count;
const { isAuthenticated, isLoading: authLoading, getAccessToken } = useKindeAuth();const [count, setCount] = useState(0);const [datasets, setDatasets] = useState<string>("No data loaded");const [loading, setLoading] = useState<boolean>(false);const [error, setError] = useState<string | null>(null);
const { isAuthenticated, isLoading } = useKindeAuth();
const fetchDatasets = async () => {if (!isAuthenticated) {setError("Please log in to access datasets");return;}setLoading(true);setError(null);try {// Get the access tokenconst accessToken = await getAccessToken();console.log("Auth status:", isAuthenticated ? "Authenticated" : "Not authenticated");console.log("Token obtained:", accessToken ? "Yes" : "No");// Include the token in the request headerconst response = await fetch("/api/datasets", {headers: {Authorization: `Bearer ${accessToken}`}});console.log("Response status:", response.status);if (!response.ok) {throw new Error(`HTTP error! Status: ${response.status}`);}const data = await response.json() as DatasetsResponse;setDatasets(JSON.stringify(data, null, 2));} catch (err) {setError(err instanceof Error ? err.message : "Failed to fetch datasets");console.error("Error fetching datasets:", err);} finally {setLoading(false);}};
if (isLoading) {return <p>Loading</p>;}
<div className="container mx-auto text-xl px-5 space-y-2"><div className="card"><ButtononClick={() => setCount((count) => count + 1)}aria-label="increment"variant="default">count is {count}</Button><p>Edit <code>src/components/AuthorisedContent.tsx</code> and save to test HMR</p></div><div className="card"><h2>Datasets</h2><ButtononClick={fetchDatasets}disabled={loading || authLoading}aria-label="fetch datasets"variant="secondary">{loading ? "Loading..." : "Fetch Datasets"}</Button>{!isAuthenticated && !authLoading &&<p className="text-amber-600 mt-2">Please log in to access datasets</p>}{error && <p className="error">Error: {error}</p>}<pre className="json-display">{datasets}</pre>
isAuthenticated&& (<div className="container mx-auto text-xl px-5 space-y-2"><UserProfile /><DataSets />
- TypeScript strict mode with noUnusedLocals and noUnusedParameters flags- React functional components with hooks (follow react-hooks plugin rules)- Project structure:- src/react-app: Frontend React code- src/worker: Cloudflare Workers backend- src/components: UI components (including shadcn)- src/lib: Utility functions- db: Database schema and migrations
- TypeScript strict mode with noUnusedLocals, noUnusedParameters, and noUncheckedSideEffects- React functional components with hooks (follow react-hooks/exhaustive-deps rules)- Path aliases: @/ (src), @react/ (src/react-app), @worker/ (src/worker), @db/ (db)- Import order: React/external libraries first, then internal modules- Formatting: Use dprint for code formatting- Naming: PascalCase for components/types, camelCase for functions/variables- Error handling: try/catch with proper error typing for async operations- API: Hono (4.x) for backend routes with typed request/response interfaces
- Authentication: Kinde Auth (components in @kinde-oss/kinde-auth-react)- Import order: React/external libraries first, then internal modules (@/ path aliases available)- Naming: PascalCase for components/types, camelCase for functions/variables- Error handling: use try/catch blocks for async operations with proper error typing- API: Hono for backend routes with typed request/response interfaces- Avoid any type; use explicit interfaces for data structures
- Avoid any; use explicit interfaces and type narrowing- Cloudflare Workers: Follow Workers runtime constraints and use proper typing