feat: enhance input showcase with new input components and status handling

This commit is contained in:
Firman Ramdhani
2026-01-26 12:23:26 +07:00
parent d4114702a0
commit 3c11b98153
8 changed files with 396 additions and 138 deletions
@@ -1,126 +1,193 @@
import { Input, InputNumber, InputStatusType, TextArea } from '@repo/ui';
import { Input, InputPassword, InputNumber, InputCurrency, InputStatusType, TextArea } from '@repo/ui';
// Helper untuk label kolom
const Label = ({ children }: { children: React.ReactNode }) => (
<div className="text-xs font-bold text-gray-400 uppercase mb-3 tracking-wider">{children}</div>
);
// Helper untuk prefix/suffix visual
const Fix = ({ children }: { children: React.ReactNode }) => <span className="text-gray-400 text-sm">{children}</span>;
export default function GroupComponent({ status }: { status?: InputStatusType }) {
return (
<div>
<div className="space-y-4 bg-white p-5 rounded-xl border border-gray-200 shadow-xs">
<h3 className="text-xs font-bold text-gray-400 uppercase">Default</h3>
<div className="grid grid-cols-4 gap-4 ">
<div className="space-y-4">
<div className="text-gray-500 ">Default</div>
<div className="space-y-2">
<Input status={status} placeholder="Outlined Placeholder" />
<InputNumber status={status} className="w-full" placeholder="OutlinedNumber Placeholder" />
<TextArea status={status} placeholder="Outlined TextArea Placeholder" rows={2} />
<Input status={status} variant="filled" placeholder="Filled Placeholder" />
<InputNumber
status={status}
variant="filled"
className="w-full"
placeholder="Filled Number Placeholder"
/>
<TextArea status={status} variant="filled" placeholder="Filled TextArea Placeholder" rows={2} />
<div className="space-y-12 p-8 rounded-2xl">
{/* --- SECTION 1: OUTLINED VARIANT (DEFAULT) --- */}
<section className="space-y-6">
<h2 className="text-lg font-bold text-gray-800 border-b pb-2">Outlined Variant (Standard)</h2>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="space-y-3">
<Label>Basic</Label>
<Input status={status} placeholder="Input text..." />
<InputPassword status={status} placeholder="Password..." />
<InputNumber status={status} className="w-full" placeholder="Number..." />
<InputCurrency status={status} className="w-full" placeholder="Currency..." />
<TextArea status={status} placeholder="TextArea..." rows={2} />
</div>
<div className="space-y-3">
<Label>With Prefix</Label>
<Input status={status} prefix={<Fix>@</Fix>} placeholder="username" />
<InputPassword status={status} prefix={<Fix>🔒</Fix>} placeholder="password" />
<InputNumber status={status} prefix={<Fix>#</Fix>} className="w-full" placeholder="id" />
<InputCurrency status={status} prefix="Rp " className="w-full" placeholder="price" />
</div>
<div className="space-y-3">
<Label>With Suffix</Label>
<Input status={status} suffix={<Fix>.com</Fix>} placeholder="website" />
<InputPassword status={status} suffix={<Fix>Required</Fix>} placeholder="password" />
<InputNumber status={status} suffix={<Fix>kg</Fix>} className="w-full" placeholder="weight" />
<InputCurrency status={status} suffix={<Fix>nett</Fix>} className="w-full" placeholder="total" />
</div>
<div className="space-y-3">
<Label>Both & Special</Label>
<Input status={status} prefix={<Fix>https://</Fix>} suffix={<Fix>.io</Fix>} placeholder="domain" />
{/* Password Tanpa Toggle */}
<InputPassword
status={status}
visibilityToggle={false}
prefix={<Fix>🔑</Fix>}
placeholder="Hidden Password"
/>
<InputNumber
status={status}
prefix={<Fix>+</Fix>}
suffix={<Fix>%</Fix>}
className="w-full"
placeholder="tax"
/>
<InputCurrency
status={status}
prefix="USD "
suffix={<Fix>cents</Fix>}
className="w-full"
placeholder="amount"
/>
</div>
</div>
</section>
{/* --- SECTION 2: FILLED VARIANT --- */}
<section className="space-y-6">
<h2 className="text-lg font-bold text-gray-800 border-b pb-2">Filled Variant</h2>
<div className="grid grid-cols-1 md:grid-cols-4 gap-6">
<div className="space-y-3">
<Label>Basic Filled</Label>
<Input variant="filled" status={status} placeholder="Filled Input" />
<InputPassword variant="filled" status={status} placeholder="Filled Password" />
<InputNumber variant="filled" status={status} className="w-full" placeholder="Filled Number" />
<InputCurrency variant="filled" status={status} className="w-full" placeholder="Filled Currency" />
<TextArea variant="filled" status={status} placeholder="Filled TextArea" rows={2} />
</div>
<div className="space-y-3">
<Label>Filled + Prefix</Label>
<Input variant="filled" status={status} prefix={<Fix>ID</Fix>} placeholder="12345" />
<InputPassword variant="filled" status={status} prefix={<Fix>🔑</Fix>} placeholder="password" />
<InputNumber variant="filled" status={status} prefix={<Fix>Qty:</Fix>} className="w-full" />
<InputCurrency variant="filled" status={status} prefix="Rp " className="w-full" />
</div>
<div className="space-y-3">
<Label>Filled + Suffix</Label>
<Input variant="filled" status={status} suffix={<Fix>Optional</Fix>} placeholder="Full Name" />
<InputPassword variant="filled" status={status} suffix={<Fix>?</Fix>} placeholder="password" />
<InputNumber variant="filled" status={status} suffix={<Fix>pts</Fix>} className="w-full" />
<InputCurrency variant="filled" status={status} suffix={<Fix>tax-incl</Fix>} className="w-full" />
</div>
<div className="space-y-3">
<Label>Both & Special</Label>
<Input
variant="filled"
status={status}
prefix={<Fix>https://</Fix>}
suffix={<Fix>.io</Fix>}
placeholder="domain"
/>
{/* Password Tanpa Toggle */}
<InputPassword
variant="filled"
status={status}
visibilityToggle={false}
prefix={<Fix>🔑</Fix>}
placeholder="Hidden Password"
/>
<InputNumber
variant="filled"
status={status}
prefix={<Fix>+</Fix>}
suffix={<Fix>%</Fix>}
className="w-full"
placeholder="tax"
/>
<InputCurrency
variant="filled"
status={status}
prefix="USD "
suffix={<Fix>cents</Fix>}
className="w-full"
placeholder="amount"
/>
</div>
</div>
</section>
{/* --- SECTION 3: Component States & Validation --- */}
<section className="space-y-6">
<h2 className="text-lg font-bold text-gray-800 border-b pb-2">Component States & Validation</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-8">
{/* COLUMN 1: DISABLED (Semua Component) */}
<div className="space-y-3">
<Label>Disabled State</Label>
<div className="p-4 bg-gray-50 rounded-lg space-y-3 border border-dashed border-gray-200">
<Input disabled placeholder="Disabled Input" prefix={<Fix>🚫</Fix>} />
<InputPassword disabled placeholder="Disabled Password" />
<InputNumber disabled className="w-full" placeholder="0" />
<InputCurrency disabled className="w-full" placeholder="Rp 0" />
<TextArea disabled placeholder="Disabled TextArea" rows={2} />
</div>
</div>
<div className="space-y-4">
<div className="text-gray-500 ">With Prefix</div>
<div className="space-y-2">
<Input
status={status}
placeholder="Outlined Placeholder"
prefix={<span className="text-gray-500">$</span>}
/>
<InputNumber
status={status}
className="w-full"
placeholder="OutlinedNumber Placeholder"
prefix={<span className="text-gray-500">$</span>}
/>
<TextArea status={status} placeholder="Outlined TextArea Placeholder" rows={2} />
<Input
status={status}
variant="filled"
placeholder="Filled Placeholder"
prefix={<span className="text-gray-500">$</span>}
/>
<InputNumber
status={status}
variant="filled"
className="w-full"
placeholder="Filled Number Placeholder"
prefix={<span className="text-gray-500">$</span>}
/>
<TextArea status={status} variant="filled" placeholder="Filled TextArea Placeholder" rows={2} />
{/* COLUMN 2: READ ONLY (Semua Component) */}
<div className="space-y-3">
<Label>Read Only State</Label>
<div className="p-4 bg-gray-50 rounded-lg space-y-3 border border-dashed border-gray-200">
<Input readOnly value="Read only text" prefix={<Fix>📖</Fix>} />
<InputPassword readOnly value="secretpassword" />
<InputNumber readOnly className="w-full" value={12345} />
<InputCurrency readOnly className="w-full" value={50000} />
<TextArea readOnly value="This content is read only and cannot be edited." rows={2} />
</div>
</div>
<div className="space-y-4">
<div className="text-gray-500 ">With Suffix</div>
<div className="space-y-2">
<Input
status={status}
placeholder="Outlined Placeholder"
suffix={<span className="text-gray-500">$</span>}
/>
<InputNumber
status={status}
className="w-full"
placeholder="OutlinedNumber Placeholder"
suffix={<span className="text-gray-500">$</span>}
/>
<TextArea status={status} placeholder="Outlined TextArea Placeholder" rows={2} />
<Input
status={status}
variant="filled"
placeholder="Filled Placeholder"
suffix={<span className="text-gray-500">$</span>}
/>
<InputNumber
status={status}
variant="filled"
className="w-full"
placeholder="Filled Number Placeholder"
suffix={<span className="text-gray-500">$</span>}
/>
<TextArea status={status} variant="filled" placeholder="Filled TextArea Placeholder" rows={2} />
{/* COLUMN 3: ERROR OUTLINED */}
<div className="space-y-3">
<Label>Error (Outlined)</Label>
<div className="space-y-3">
<Input status="error" placeholder="Invalid input" />
<InputPassword status="error" placeholder="Wrong password" />
<InputNumber status="error" className="w-full" placeholder="Out of range" />
<InputCurrency status="error" className="w-full" placeholder="Invalid amount" />
<TextArea status="error" placeholder="Error in message" rows={2} />
</div>
</div>
<div className="space-y-4">
<div className="text-gray-500 ">With Prefix and Suffix</div>
<div className="space-y-2">
<Input
status={status}
placeholder="Outlined Placeholder"
prefix={<span className="text-gray-500">$</span>}
suffix={<span className="text-gray-500">$</span>}
/>
<InputNumber
status={status}
className="w-full"
placeholder="OutlinedNumber Placeholder"
prefix={<span className="text-gray-500">$</span>}
suffix={<span className="text-gray-500">$</span>}
/>
<TextArea status={status} placeholder="Outlined TextArea Placeholder" rows={2} />
<Input
status={status}
variant="filled"
placeholder="Filled Placeholder"
prefix={<span className="text-gray-500">$</span>}
suffix={<span className="text-gray-500">$</span>}
/>
<InputNumber
status={status}
variant="filled"
className="w-full"
placeholder="Filled Number Placeholder"
prefix={<span className="text-gray-500">$</span>}
suffix={<span className="text-gray-500">$</span>}
/>
<TextArea status={status} variant="filled" placeholder="Filled TextArea Placeholder" rows={2} />
{/* COLUMN 4: ERROR FILLED */}
<div className="space-y-3">
<Label>Error (Filled)</Label>
<div className="space-y-3">
<Input variant="filled" status="error" placeholder="Invalid input" />
<InputPassword variant="filled" status="error" placeholder="Wrong password" />
<InputNumber variant="filled" status="error" className="w-full" placeholder="Out of range" />
<InputCurrency variant="filled" status="error" className="w-full" placeholder="Invalid amount" />
<TextArea variant="filled" status="error" placeholder="Error in message" rows={2} />
</div>
</div>
</div>
</div>
</section>
</div>
);
}
@@ -1,28 +1,69 @@
import { useState } from 'react';
import GroupComponent from './group-component';
import { InputStatusType } from '@repo/ui';
export default function InputShowCase() {
// State untuk menyimpan status yang dipilih
const [selectedStatus, setSelectedStatus] = useState<InputStatusType>('normal');
const statuses: { label: string; value: InputStatusType; color: string }[] = [
{ label: 'Normal', value: 'normal', color: 'bg-gray-500' },
{ label: 'Error', value: 'error', color: 'bg-red-500' },
{ label: 'Warning', value: 'warning', color: 'bg-amber-500' },
{ label: 'Success', value: 'success', color: 'bg-emerald-500' },
{ label: 'Info', value: 'info', color: 'bg-blue-500' },
];
return (
<div className="min-h-screen ">
<main className="mx-auto p-8 space-y-12">
<section className="space-y-6">
<div className="flex items-center gap-2">
<h2 className="text-lg font-bold">1. All Conditions Matrix</h2>
<span className="px-2 py-0.5 text-[10px] bg-gray-200 text-gray-600 rounded uppercase tracking-wider">
Visual Audit
</span>
<div className="min-h-screen bg-gray-50/50">
<main className="mx-auto p-8 space-y-8">
{/* HEADER & SELECTOR */}
<div className="flex flex-col md:flex-row md:items-center justify-between gap-4 bg-white p-6 rounded-2xl border border-gray-200 shadow-sm">
<div className="space-y-1">
<div className="flex items-center gap-2">
<h2 className="text-xl font-bold text-gray-800">Input Components</h2>
<span className="px-2 py-0.5 text-[10px] bg-blue-100 text-blue-600 rounded-full uppercase tracking-wider font-bold">
Interactive Lab
</span>
</div>
<p className="text-sm text-gray-500">
Pilih status untuk melihat perubahan validasi pada semua tipe input.
</p>
</div>
<div className="grid grid-cols-1 gap-6">
{/* COLUMN: ERROR */}
<GroupComponent status="error" />
{/* COLUMN: NORMAL */}
<GroupComponent />
{/* COLUMN: INFO */}
<GroupComponent status="info" />
{/* COLUMN: WARNING */}
<GroupComponent status="warning" />
{/* COLUMN: SUCCESS */}
<GroupComponent status="success" />
{/* TAB SELECTOR */}
<div className="flex bg-gray-100 p-1 rounded-xl w-fit border border-gray-200">
{statuses.map((item) => (
<button
key={item.value}
onClick={() => setSelectedStatus(item.value)}
className={`
px-4 py-2 text-sm font-medium rounded-lg transition-all duration-200
${
selectedStatus === item.value
? 'bg-white text-gray-900 shadow-sm'
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-50'
}
`}
>
<div className="flex items-center gap-2">
<span className={`w-2 h-2 rounded-full ${item.color}`} />
{item.label}
</div>
</button>
))}
</div>
</div>
{/* RENDER COMPONENT */}
<section className="animate-in fade-in slide-in-from-bottom-2 duration-500">
<div className="relative">
{/* Label Indikator Status Aktif */}
<div className="absolute -top-3 left-8 px-3 py-1 rounded-full text-[10px] font-bold uppercase tracking-widest bg-white border border-gray-200 shadow-sm z-10">
Active Status: <span className="text-blue-600">{selectedStatus}</span>
</div>
<GroupComponent status={selectedStatus} />
</div>
</section>
</main>