API layer for sandbox app 434 (three workflows: Order Booking, Store, Daily Reports) — ZinoClient, per-workflow client singletons, config ids, types, and the source API docs. Component library (Tailwind v4 + design tokens): buttons, reusable (Card/Badge/Input/Select/Pagination/Modal/Spinner/EmptyState), generic + wired record views (rv) and detail views (dv). Screens + routing (react-router v7): login gate, auth-guarded console shell with tab nav, and deep-linkable record/detail routes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
61 lines
1.9 KiB
TypeScript
61 lines
1.9 KiB
TypeScript
import type { InputHTMLAttributes, ReactNode } from 'react';
|
|
import { cn } from '../../lib/cn';
|
|
|
|
export interface InputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'prefix'> {
|
|
label?: string;
|
|
hint?: string;
|
|
error?: string;
|
|
/** Leading adornment, e.g. "₹". */
|
|
prefix?: ReactNode;
|
|
/** Trailing adornment, e.g. "/ year". */
|
|
suffix?: ReactNode;
|
|
/** Class for the outer label wrapper. */
|
|
className?: string;
|
|
}
|
|
|
|
/** Labeled text/number input with optional prefix, suffix, hint and error. */
|
|
export function Input({
|
|
label,
|
|
hint,
|
|
error,
|
|
prefix,
|
|
suffix,
|
|
required,
|
|
className,
|
|
disabled,
|
|
...rest
|
|
}: InputProps) {
|
|
return (
|
|
<label className={cn('flex flex-col gap-1.5 font-sans', disabled && 'cursor-not-allowed', className)}>
|
|
{label && (
|
|
<span className="text-sm font-medium text-muted">
|
|
{label}
|
|
{required && <span className="text-ruby-600"> *</span>}
|
|
</span>
|
|
)}
|
|
<div
|
|
className={cn(
|
|
'flex items-center gap-2 rounded-md px-3 h-[42px] border transition-[border-color,box-shadow] duration-150',
|
|
disabled ? 'bg-sunk border-border-subtle' : 'bg-card',
|
|
error ? 'border-ruby-600' : !disabled && 'border-border-default focus-ring',
|
|
)}
|
|
>
|
|
{prefix && <span className="text-base font-semibold text-faint">{prefix}</span>}
|
|
<input
|
|
required={required}
|
|
disabled={disabled}
|
|
className={cn(
|
|
'flex-1 w-full border-none outline-none bg-transparent font-sans text-base',
|
|
disabled ? 'text-muted cursor-not-allowed' : 'text-strong',
|
|
)}
|
|
{...rest}
|
|
/>
|
|
{suffix && <span className="text-faint text-sm">{suffix}</span>}
|
|
</div>
|
|
{(hint || error) && (
|
|
<span className={cn('text-xs', error ? 'text-ruby-600' : 'text-faint')}>{error || hint}</span>
|
|
)}
|
|
</label>
|
|
);
|
|
}
|