import { useCallback, useEffect, useRef, useState } from 'react';
export function usePaginatedFetch(fetchPage, { pageSize = 20 } = {}) {
const [items, setItems] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
import { useMemo } from "react";
export type SortDir = "asc" | "desc";
export interface SortConfig<T> {
key: keyof T;
dir: SortDir;
import { useEffect, useState } from "react";
export function useDebouncedValue(value, delay = 300) {
const [debounced, setDebounced] = useState(value);
useEffect(() => {
export const initialCart = { items: {} };
export function cartReducer(state, action) {
switch (action.type) {
case 'ADD_ITEM': {
const { product, qty = 1 } = action;
import { createContext, useContext, useEffect, useMemo, useState } from 'react';
import { fetchCurrentUser, postLogin, postLogout } from './api';
const AuthContext = createContext(null);
export function AuthProvider({ children }) {
const BASE_URL = "/api/search";
export async function searchProducts(query, { signal } = {}) {
const params = new URLSearchParams({ q: query, limit: "10" });
const res = await fetch(`${BASE_URL}?${params}`, {
signal,
import { useCallback, useEffect, useReducer } from "react";
type State = { secondsLeft: number; isRunning: boolean };
type Action =
| { type: "start"; seconds: number }
| { type: "tick" }
export const MAX_VISIBLE = 4;
const DEFAULTS = {
variant: "info",
duration: 4000,
};
export interface UseClipboardResult {
copied: boolean;
error: Error | null;
copy: (value: string) => Promise<boolean>;
}
export interface Todo {
id: string;
title: string;
done: boolean;
pending: boolean;
}
import { useEffect, useRef } from "react";
const FOCUSABLE = [
"a[href]",
"button:not([disabled])",
"textarea:not([disabled])",
import { z } from 'zod';
export const signupSchema = z
.object({
email: z.string().min(1, 'Email is required').email('Enter a valid email'),
password: z