feat: enhance error handling in forms by introducing FieldError component for consistent error display
This commit is contained in:
parent
beafccf974
commit
703ceefddf
@ -1,17 +1,64 @@
|
||||
import type { HTMLAttributes } from 'react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
interface InputErrorProps extends HTMLAttributes<HTMLParagraphElement> {
|
||||
message?: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export default function InputError({
|
||||
message,
|
||||
label,
|
||||
className = '',
|
||||
...props
|
||||
}: HTMLAttributes<HTMLParagraphElement> & { message?: string }) {
|
||||
return message ? (
|
||||
}: InputErrorProps) {
|
||||
const formattedError = useMemo(() => {
|
||||
if (!message) return null;
|
||||
if (!label) return message;
|
||||
|
||||
// Preserve all-caps labels (like NIK), otherwise capitalize first letter and lowercase the rest
|
||||
const isAllOptionsCaps = label === label.toUpperCase() && label.length > 1;
|
||||
const formattedLabel = isAllOptionsCaps
|
||||
? label
|
||||
: label.charAt(0).toUpperCase() + label.slice(1).toLowerCase();
|
||||
|
||||
const words = message.split(' ');
|
||||
|
||||
// List of common Indonesian and English validation verbs/connectors that follow the attribute
|
||||
const verbs = [
|
||||
'wajib', 'harus', 'berupa', 'adalah', 'minimal', 'maksimal', 'tidak', 'kurang', 'lebih', 'antara', 'sudah',
|
||||
'is', 'must', 'field', 'has', 'was', 'should', 'cannot', 'required', 'invalid'
|
||||
];
|
||||
|
||||
// Find the first occurrence of a verb
|
||||
let verbIndex = -1;
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
if (verbs.includes(words[i].toLowerCase())) {
|
||||
verbIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (verbIndex !== -1) {
|
||||
// Replace everything before the verb with the label
|
||||
return `${formattedLabel} ${words.slice(verbIndex).join(' ')}`;
|
||||
}
|
||||
|
||||
// Fallback to replacing only the first word if no verb found
|
||||
if (words.length > 0) {
|
||||
return `${formattedLabel} ${words.slice(1).join(' ')}`;
|
||||
}
|
||||
|
||||
return message;
|
||||
}, [message, label]);
|
||||
|
||||
return formattedError ? (
|
||||
<p
|
||||
{...props}
|
||||
className={cn('text-sm text-red-600 dark:text-red-400', className)}
|
||||
>
|
||||
{message}
|
||||
{formattedError}
|
||||
</p>
|
||||
) : null;
|
||||
}
|
||||
|
||||
@ -184,37 +184,59 @@ function FieldSeparator({
|
||||
function FieldError({
|
||||
className,
|
||||
children,
|
||||
errors,
|
||||
error,
|
||||
label,
|
||||
...props
|
||||
}: React.ComponentProps<"div"> & {
|
||||
errors?: Array<{ message?: string } | undefined>
|
||||
error?: string | string[]
|
||||
label?: string
|
||||
}) {
|
||||
const content = useMemo(() => {
|
||||
if (children) {
|
||||
return children
|
||||
}
|
||||
|
||||
if (!errors?.length) {
|
||||
const rawError = Array.isArray(error) ? error[0] : error
|
||||
|
||||
if (!rawError) {
|
||||
return null
|
||||
}
|
||||
|
||||
const uniqueErrors = [
|
||||
...new Map(errors.map((error) => [error?.message, error])).values(),
|
||||
]
|
||||
if (label) {
|
||||
const isAllUppercase = label === label.toUpperCase() && label.length > 1
|
||||
const formattedLabel = isAllUppercase
|
||||
? label
|
||||
: label.charAt(0).toUpperCase() + label.slice(1).toLowerCase()
|
||||
|
||||
const words = rawError.split(" ")
|
||||
|
||||
// List of common Indonesian and English validation verbs/connectors that follow the attribute
|
||||
const verbs = [
|
||||
"wajib", "harus", "berupa", "adalah", "minimal", "maksimal", "tidak", "kurang", "lebih", "antara", "sudah",
|
||||
"is", "must", "field", "has", "was", "should", "cannot", "required", "invalid"
|
||||
]
|
||||
|
||||
// Find the first occurrence of a verb
|
||||
let verbIndex = -1
|
||||
for (let i = 0; i < words.length; i++) {
|
||||
if (verbs.includes(words[i].toLowerCase())) {
|
||||
verbIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (verbIndex !== -1) {
|
||||
// Replace everything before the verb with the label
|
||||
// If the first word was "The" or "Isian", we replace them too
|
||||
return `${formattedLabel} ${words.slice(verbIndex).join(" ")}`
|
||||
}
|
||||
|
||||
if (uniqueErrors?.length == 1) {
|
||||
return uniqueErrors[0]?.message
|
||||
// Fallback to replacing only the first word if no verb found
|
||||
return `${formattedLabel} ${words.slice(1).join(" ")}`
|
||||
}
|
||||
|
||||
return (
|
||||
<ul className="ml-4 flex list-disc flex-col gap-1">
|
||||
{uniqueErrors.map(
|
||||
(error, index) =>
|
||||
error?.message && <li key={index}>{error.message}</li>
|
||||
)}
|
||||
</ul>
|
||||
)
|
||||
}, [children, errors])
|
||||
return rawError
|
||||
}, [children, error, label])
|
||||
|
||||
if (!content) {
|
||||
return null
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Field, FieldGroup } from "@/components/ui/field"
|
||||
import { Field, FieldError, FieldGroup } from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Expense } from '@/types';
|
||||
@ -122,7 +122,7 @@ export function ExpenseFormModal({ isOpen, onClose, expense }: ExpenseFormModalP
|
||||
placeholder='Contoh: Bayar Listrik'
|
||||
maxLength={100}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
||||
<FieldError error={errors.name} label="Nama" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -140,7 +140,7 @@ export function ExpenseFormModal({ isOpen, onClose, expense }: ExpenseFormModalP
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors.amount && <p className="text-xs text-red-500">{errors.amount}</p>}
|
||||
<FieldError error={errors.amount} label="Nominal" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -179,7 +179,7 @@ export function ExpenseFormModal({ isOpen, onClose, expense }: ExpenseFormModalP
|
||||
onChange={onImageChange}
|
||||
/>
|
||||
</div>
|
||||
{errors.image && <p className="text-xs text-red-500 mt-1">{errors.image}</p>}
|
||||
<FieldError error={errors.image} label="Bukti" className="text-xs mt-1" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<DialogFooter className="mt-6">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Head, Link, useForm, router } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field";
|
||||
import { Field, FieldError } from "@/components/ui/field";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import * as orderRoutes from '@/routes/order';
|
||||
@ -268,7 +268,7 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
<Field>
|
||||
<Label htmlFor='customer_name' className="text-xs text-muted-foreground" required>Pelanggan</Label>
|
||||
<Input id="customer_name" className="h-8 text-xs" value={data.customer_name} onChange={e => setData('customer_name', e.target.value)} placeholder="Nama Pelanggan" />
|
||||
{errors.customer_name && <p className="text-xs text-red-500">{errors.customer_name}</p>}
|
||||
<FieldError error={errors.customer_name} label="Pelanggan" className="text-xs" />
|
||||
</Field>
|
||||
<Field>
|
||||
<Label className="text-xs text-muted-foreground" required>Channel</Label>
|
||||
@ -282,7 +282,7 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors.order_channel && <p className="text-xs text-red-500">{errors.order_channel}</p>}
|
||||
<FieldError error={errors.order_channel} label="Channel" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@ -299,7 +299,7 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors.payment_method && <p className="text-xs text-red-500">{errors.payment_method}</p>}
|
||||
<FieldError error={errors.payment_method} label="Metode Bayar" className="text-xs" />
|
||||
</Field>
|
||||
<Field>
|
||||
<Label className="text-xs text-muted-foreground" required>Status</Label>
|
||||
@ -313,7 +313,7 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors.order_status && <p className="text-xs text-red-500">{errors.order_status}</p>}
|
||||
<FieldError error={errors.order_status} label="Status" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@ -338,7 +338,7 @@ export default function OrderCreate({ products, cartItems, orderStatus, orderCha
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors.discount && <p className="text-xs text-red-500">{errors.discount}</p>}
|
||||
<FieldError error={errors.discount} label="Potongan / Diskon" className="text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Head, Link, useForm, router } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field";
|
||||
import { Field, FieldError } from "@/components/ui/field";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import * as orderRoutes from '@/routes/order';
|
||||
@ -270,7 +270,7 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
<Field>
|
||||
<Label htmlFor='customer_name' className="text-xs text-muted-foreground" required>Pelanggan</Label>
|
||||
<Input id='customer_name' className="h-8 text-xs" value={data.customer_name} onChange={e => setData('customer_name', e.target.value)} />
|
||||
{errors.customer_name && <p className="text-xs text-red-500">{errors.customer_name}</p>}
|
||||
<FieldError error={errors.customer_name} label="Pelanggan" className="text-xs" />
|
||||
</Field>
|
||||
<Field>
|
||||
<Label className="text-xs text-muted-foreground" required>Channel</Label>
|
||||
@ -282,7 +282,7 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors.order_channel && <p className="text-xs text-red-500">{errors.order_channel}</p>}
|
||||
<FieldError error={errors.order_channel} label="Channel" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@ -297,7 +297,7 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors.payment_method && <p className="text-xs text-red-500">{errors.payment_method}</p>}
|
||||
<FieldError error={errors.payment_method} label="Metode Bayar" className="text-xs" />
|
||||
</Field>
|
||||
<Field>
|
||||
<Label className="text-xs text-muted-foreground" required>Status</Label>
|
||||
@ -309,7 +309,7 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{errors.order_status && <p className="text-xs text-red-500">{errors.order_status}</p>}
|
||||
<FieldError error={errors.order_status} label="Status" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@ -335,7 +335,7 @@ export default function OrderEdit({ order, products, orderStatus, orderChannels,
|
||||
autoComplete='off'
|
||||
/>
|
||||
</div>
|
||||
{errors.discount && <p className="text-xs text-red-500">{errors.discount}</p>}
|
||||
<FieldError error={errors.discount} label="Potongan / Diskon" className="text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Head, Link, useForm, router } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field";
|
||||
import { Field, FieldError } from "@/components/ui/field";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import purchaseRoutes from '@/routes/purchase';
|
||||
@ -272,11 +272,13 @@ export default function PurchaseCreate({ products, cartItems }: { products: Prod
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FieldError error={errors.purchase_date} label="Tanggal" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor='note' className="text-xs text-muted-foreground">Catatan</Label>
|
||||
<Input id='note' className="h-8 text-xs" value={data.note} onChange={e => setData('note', e.target.value)} placeholder="...." />
|
||||
<FieldError error={errors.note} label="Catatan" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
@ -577,6 +579,7 @@ export default function PurchaseCreate({ products, cartItems }: { products: Prod
|
||||
<Field>
|
||||
<Label htmlFor="note" className="text-xs text-muted-foreground">Catatan</Label>
|
||||
<Input id="note" className="h-8 text-xs" value={data.note} onChange={e => setData('note', e.target.value)} placeholder="...." />
|
||||
<FieldError error={errors.note} label="Catatan" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
@ -752,6 +755,7 @@ export default function PurchaseCreate({ products, cartItems }: { products: Prod
|
||||
<Field>
|
||||
<Label htmlFor="note_mobile" className="text-xs text-muted-foreground">Catatan</Label>
|
||||
<Input id="note_mobile" className="h-8 text-xs" value={data.note} onChange={e => setData('note', e.target.value)} placeholder="...." />
|
||||
<FieldError error={errors.note} label="Catatan" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Head, Link, useForm, router } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field";
|
||||
import { Field, FieldError } from "@/components/ui/field";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import purchaseRoutes from '@/routes/purchase';
|
||||
@ -276,11 +276,13 @@ export default function PurchaseEdit({ purchase, products }: { purchase: Purchas
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FieldError error={errors.purchase_date} label="Tanggal" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor='note' className="text-xs text-muted-foreground">Catatan</Label>
|
||||
<Input id='note' className="h-8 text-xs" value={data.note} onChange={e => setData('note', e.target.value)} placeholder="...." />
|
||||
<FieldError error={errors.note} label="Catatan" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
@ -576,11 +578,13 @@ export default function PurchaseEdit({ purchase, products }: { purchase: Purchas
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
<FieldError error={errors.purchase_date} label="Tanggal" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor="note" className="text-xs text-muted-foreground">Catatan</Label>
|
||||
<Input id="note" className="h-8 text-xs" value={data.note} onChange={e => setData('note', e.target.value)} placeholder="...." />
|
||||
<FieldError error={errors.note} label="Catatan" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
@ -756,6 +760,7 @@ export default function PurchaseEdit({ purchase, products }: { purchase: Purchas
|
||||
<Field>
|
||||
<Label htmlFor="note_mobile" className="text-xs text-muted-foreground">Catatan</Label>
|
||||
<Input id="note_mobile" className="h-8 text-xs" value={data.note} onChange={e => setData('note', e.target.value)} placeholder="...." />
|
||||
<FieldError error={errors.note} label="Catatan" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<div className="rounded-xl bg-primary/10 border border-primary/20 p-3 flex items-center justify-between">
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Field, FieldGroup } from "@/components/ui/field"
|
||||
import { Field, FieldError, FieldGroup } from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Category } from '@/types';
|
||||
@ -85,7 +85,7 @@ export function CategoryFormModal({ isOpen, onClose, category }: CategoryFormMod
|
||||
placeholder='Contoh: Gamis'
|
||||
maxLength={50}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
||||
<FieldError error={errors.name} label="Nama" className="text-xs" />
|
||||
</Field>
|
||||
</FieldGroup>
|
||||
<DialogFooter className="mt-6">
|
||||
|
||||
@ -2,7 +2,7 @@ import { Head, Link } from '@inertiajs/react';
|
||||
import { useForm } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field"
|
||||
import { Field, FieldError } from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import productRoutes from '@/routes/product';
|
||||
@ -143,7 +143,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
placeholder='Contoh: Gamis Wanita'
|
||||
maxLength={100}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
||||
<FieldError error={errors.name} label="Nama" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -185,7 +185,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
{errors.category_ids && <p className="text-xs text-red-500 mt-1">{errors.category_ids}</p>}
|
||||
<FieldError error={errors.category_ids} label="Kategori" className="text-xs mt-1" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -194,7 +194,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
value={data.description}
|
||||
onChange={(val) => setData('description', val)}
|
||||
/>
|
||||
{errors.description && <p className="text-xs text-red-500 mt-1">{errors.description}</p>}
|
||||
<FieldError error={errors.description} label="Deskripsi" className="text-xs mt-1" />
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -223,7 +223,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.purchase'] && <p className="text-xs text-red-500">{errors['prices.purchase']}</p>}
|
||||
<FieldError error={errors['prices.purchase']} label="Harga Beli" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -244,7 +244,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.distributor'] && <p className="text-xs text-red-500">{errors['prices.distributor']}</p>}
|
||||
<FieldError error={errors['prices.distributor']} label="Harga Distributor" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -265,7 +265,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.agent'] && <p className="text-xs text-red-500">{errors['prices.agent']}</p>}
|
||||
<FieldError error={errors['prices.agent']} label="Harga Agen" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -286,7 +286,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.reseller'] && <p className="text-xs text-red-500">{errors['prices.reseller']}</p>}
|
||||
<FieldError error={errors['prices.reseller']} label="Harga Reseller" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field className="md:col-span-2">
|
||||
@ -307,7 +307,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.retail'] && <p className="text-xs text-red-500">{errors['prices.retail']}</p>}
|
||||
<FieldError error={errors['prices.retail']} label="Harga Retail" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
</CardContent>
|
||||
@ -353,7 +353,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
className="hidden"
|
||||
onChange={handleThumbnailChange}
|
||||
/>
|
||||
{errors.thumbnail && <p className="text-xs text-red-500 mt-2">{errors.thumbnail}</p>}
|
||||
<FieldError error={errors.thumbnail} label="Thumbnail" className="text-xs mt-2" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -397,7 +397,7 @@ export default function ProductCreate({ categories }: { categories: Category[] }
|
||||
className="hidden"
|
||||
onChange={handleImagesChange}
|
||||
/>
|
||||
{errors['images'] && <p className="text-xs text-red-500">{errors['images']}</p>}
|
||||
<FieldError error={errors['images']} label="Galeri Gambar" className="text-xs" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import type { Product, ProductPrice } from '@/types';
|
||||
import { Category } from '@/types/category';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field"
|
||||
import { Field, FieldError } from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { toast } from 'sonner';
|
||||
@ -194,7 +194,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
placeholder='Contoh: Gamis Wanita'
|
||||
maxLength={100}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
||||
<FieldError error={errors.name} label="Nama" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -209,7 +209,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
placeholder="0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors.stock && <p className="text-xs text-red-500">{errors.stock}</p>}
|
||||
<FieldError error={errors.stock} label="Stok" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@ -252,7 +252,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
</ComboboxList>
|
||||
</ComboboxContent>
|
||||
</Combobox>
|
||||
{errors.category_ids && <p className="text-xs text-red-500 mt-1">{errors.category_ids}</p>}
|
||||
<FieldError error={errors.category_ids} label="Kategori" className="text-xs mt-1" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -261,7 +261,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
value={data.description}
|
||||
onChange={(val) => setData('description', val)}
|
||||
/>
|
||||
{errors.description && <p className="text-xs text-red-500 mt-1">{errors.description}</p>}
|
||||
<FieldError error={errors.description} label="Deskripsi" className="text-xs mt-1" />
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -290,7 +290,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.purchase'] && <p className="text-xs text-red-500">{errors['prices.purchase']}</p>}
|
||||
<FieldError error={errors['prices.purchase']} label="Harga Beli" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -311,7 +311,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.distributor'] && <p className="text-xs text-red-500">{errors['prices.distributor']}</p>}
|
||||
<FieldError error={errors['prices.distributor']} label="Harga Distributor" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -332,7 +332,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.agent'] && <p className="text-xs text-red-500">{errors['prices.agent']}</p>}
|
||||
<FieldError error={errors['prices.agent']} label="Harga Agen" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -353,7 +353,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.reseller'] && <p className="text-xs text-red-500">{errors['prices.reseller']}</p>}
|
||||
<FieldError error={errors['prices.reseller']} label="Harga Reseller" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field className="md:col-span-2">
|
||||
@ -374,7 +374,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors['prices.retail'] && <p className="text-xs text-red-500">{errors['prices.retail']}</p>}
|
||||
<FieldError error={errors['prices.retail']} label="Harga Retail" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
</CardContent>
|
||||
@ -420,7 +420,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
className="hidden"
|
||||
onChange={handleThumbnailChange}
|
||||
/>
|
||||
{errors.thumbnail && <p className="text-xs text-red-500 mt-2">{errors.thumbnail}</p>}
|
||||
<FieldError error={errors.thumbnail} label="Thumbnail" className="text-xs mt-2" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -464,7 +464,7 @@ export default function ProductEdit({ product, categories }: { product: Product,
|
||||
className="hidden"
|
||||
onChange={handleImagesChange}
|
||||
/>
|
||||
{errors['images'] && <p className="text-xs text-red-500">{errors['images']}</p>}
|
||||
<FieldError error={errors['images']} label="Galeri Gambar" className="text-xs" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ import { Head, Link } from '@inertiajs/react';
|
||||
import { useForm } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field"
|
||||
import { Field, FieldError } from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import userRoutes from '@/routes/user';
|
||||
@ -77,7 +77,7 @@ export default function UserCreate() {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: John Doe'
|
||||
/>
|
||||
{errors.full_name && <p className="text-xs text-red-500">{errors.full_name}</p>}
|
||||
<FieldError error={errors.full_name} label="Nama Lengkap" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -91,7 +91,7 @@ export default function UserCreate() {
|
||||
placeholder='Contoh: 3213051307900001'
|
||||
maxLength={16}
|
||||
/>
|
||||
{errors.nik && <p className="text-xs text-red-500">{errors.nik}</p>}
|
||||
<FieldError error={errors.nik} label="NIK" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -104,7 +104,7 @@ export default function UserCreate() {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: 08123456789'
|
||||
/>
|
||||
{errors.phone_number && <p className="text-xs text-red-500">{errors.phone_number}</p>}
|
||||
<FieldError error={errors.phone_number} label="Nomor Telepon" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -122,7 +122,7 @@ export default function UserCreate() {
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors.base_salary && <p className="text-xs text-red-500">{errors.base_salary}</p>}
|
||||
<FieldError error={errors.base_salary} label="Gaji Pokok" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -135,7 +135,7 @@ export default function UserCreate() {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: Jakarta'
|
||||
/>
|
||||
{errors.birth_place && <p className="text-xs text-red-500">{errors.birth_place}</p>}
|
||||
<FieldError error={errors.birth_place} label="Tempat Lahir" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -175,14 +175,14 @@ export default function UserCreate() {
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{errors.birth_date && <p className="text-xs text-red-500">{errors.birth_date}</p>}
|
||||
<FieldError error={errors.birth_date} label="Tanggal Lahir" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor="address" required>Alamat</Label>
|
||||
<Textarea id='address' name='address' value={data.address} onChange={e => setData('address', e.target.value)} placeholder='Contoh: Kp. Bakan Sampeu' />
|
||||
{errors.address && <p className="text-xs text-red-500">{errors.address}</p>}
|
||||
<FieldError error={errors.address} label="Alamat" className="text-xs" />
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -205,7 +205,7 @@ export default function UserCreate() {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: john@example.com'
|
||||
/>
|
||||
{errors.email && <p className="text-xs text-red-500">{errors.email}</p>}
|
||||
<FieldError error={errors.email} label="Alamat Surel" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -218,7 +218,7 @@ export default function UserCreate() {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: johndoe'
|
||||
/>
|
||||
{errors.username && <p className="text-xs text-red-500">{errors.username}</p>}
|
||||
<FieldError error={errors.username} label="Nama Pengguna" className="text-xs" />
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -2,7 +2,7 @@ import { Head, Link } from '@inertiajs/react';
|
||||
import { useForm } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field"
|
||||
import { Field, FieldError } from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import userRoutes from '@/routes/user';
|
||||
@ -79,7 +79,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
placeholder='Contoh: 3213051307900001'
|
||||
maxLength={16}
|
||||
/>
|
||||
{errors.nik && <p className="text-xs text-red-500">{errors.nik}</p>}
|
||||
<FieldError error={errors.nik} label="NIK" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -92,7 +92,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: John Doe'
|
||||
/>
|
||||
{errors.full_name && <p className="text-xs text-red-500">{errors.full_name}</p>}
|
||||
<FieldError error={errors.full_name} label="Nama Lengkap" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -105,7 +105,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: 08123456789'
|
||||
/>
|
||||
{errors.phone_number && <p className="text-xs text-red-500">{errors.phone_number}</p>}
|
||||
<FieldError error={errors.phone_number} label="Nomor Telepon" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -123,7 +123,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
placeholder="Rp 0"
|
||||
autoComplete='off'
|
||||
/>
|
||||
{errors.base_salary && <p className="text-xs text-red-500">{errors.base_salary}</p>}
|
||||
<FieldError error={errors.base_salary} label="Gaji Pokok" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -136,7 +136,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: Jakarta'
|
||||
/>
|
||||
{errors.birth_place && <p className="text-xs text-red-500">{errors.birth_place}</p>}
|
||||
<FieldError error={errors.birth_place} label="Tempat Lahir" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -176,14 +176,14 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
{errors.birth_date && <p className="text-xs text-red-500">{errors.birth_date}</p>}
|
||||
<FieldError error={errors.birth_date} label="Tanggal Lahir" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<Field>
|
||||
<Label htmlFor="address" required>Alamat</Label>
|
||||
<Textarea id='address' name='address' value={data.address} onChange={e => setData('address', e.target.value)} placeholder='Contoh: Kp. Bakan Sampeu' />
|
||||
{errors.address && <p className="text-xs text-red-500">{errors.address}</p>}
|
||||
<FieldError error={errors.address} label="Alamat" className="text-xs" />
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -206,7 +206,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: john@example.com'
|
||||
/>
|
||||
{errors.email && <p className="text-xs text-red-500">{errors.email}</p>}
|
||||
<FieldError error={errors.email} label="Alamat Surel" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -219,7 +219,7 @@ export default function UserEdit({ user }: { user: any }) {
|
||||
autoComplete='off'
|
||||
placeholder='Contoh: johndoe'
|
||||
/>
|
||||
{errors.username && <p className="text-xs text-red-500">{errors.username}</p>}
|
||||
<FieldError error={errors.username} label="Nama Pengguna" className="text-xs" />
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { Head, useForm } from '@inertiajs/react';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Field } from "@/components/ui/field"
|
||||
import { Field, FieldError } from "@/components/ui/field"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
@ -96,7 +96,7 @@ export default function SettingIndex({ setting }: { setting: GeneralSetting | nu
|
||||
placeholder='Contoh: VN Grup Dress'
|
||||
maxLength={100}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
||||
<FieldError error={errors.name} label="Nama Aplikasi" className="text-xs" />
|
||||
</Field>
|
||||
|
||||
<Field>
|
||||
@ -108,7 +108,7 @@ export default function SettingIndex({ setting }: { setting: GeneralSetting | nu
|
||||
placeholder="Deskripsi singkat aplikasi"
|
||||
rows={4}
|
||||
/>
|
||||
{errors.description && <p className="text-xs text-red-500 mt-1">{errors.description}</p>}
|
||||
<FieldError error={errors.description} label="Deskripsi" className="text-xs mt-1" />
|
||||
</Field>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
@ -120,7 +120,7 @@ export default function SettingIndex({ setting }: { setting: GeneralSetting | nu
|
||||
onChange={(e) => setData('phone', e.target.value)}
|
||||
placeholder="0812xxxx"
|
||||
/>
|
||||
{errors.phone && <p className="text-xs text-red-500">{errors.phone}</p>}
|
||||
<FieldError error={errors.phone} label="No. Telepon" className="text-xs" />
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
@ -133,7 +133,7 @@ export default function SettingIndex({ setting }: { setting: GeneralSetting | nu
|
||||
placeholder="Alamat lengkap toko"
|
||||
rows={3}
|
||||
/>
|
||||
{errors.address && <p className="text-xs text-red-500 mt-1">{errors.address}</p>}
|
||||
<FieldError error={errors.address} label="Alamat" className="text-xs mt-1" />
|
||||
</Field>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@ -178,7 +178,7 @@ export default function SettingIndex({ setting }: { setting: GeneralSetting | nu
|
||||
className="hidden"
|
||||
onChange={handleLogoChange}
|
||||
/>
|
||||
{errors.logo && <p className="text-xs text-red-500 mt-2">{errors.logo}</p>}
|
||||
<FieldError error={errors.logo} label="Logo Aplikasi" className="text-xs mt-2" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@ -222,7 +222,7 @@ export default function SettingIndex({ setting }: { setting: GeneralSetting | nu
|
||||
className="hidden"
|
||||
onChange={handleIconChange}
|
||||
/>
|
||||
{errors.icon && <p className="text-xs text-red-500 mt-2">{errors.icon}</p>}
|
||||
<FieldError error={errors.icon} label="Favicon / Icon" className="text-xs mt-2" />
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
|
||||
@ -24,7 +24,7 @@ export default function ConfirmPassword() {
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
<InputError message={errors.password} />
|
||||
<InputError message={errors.password} label="Password" />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center">
|
||||
|
||||
@ -35,7 +35,7 @@ export default function ForgotPassword({ status }: { status?: string }) {
|
||||
placeholder="email@example.com"
|
||||
/>
|
||||
|
||||
<InputError message={errors.email} />
|
||||
<InputError message={errors.email} label="Email address" />
|
||||
</div>
|
||||
|
||||
<div className="my-6 flex items-center justify-start">
|
||||
|
||||
@ -39,7 +39,7 @@ export default function Login({
|
||||
autoComplete="off"
|
||||
placeholder="johndoe@example.com atau johndoe"
|
||||
/>
|
||||
<InputError message={errors.login} />
|
||||
<InputError message={errors.login} label="Alamat surel atau nama pengguna" />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
@ -54,7 +54,7 @@ export default function Login({
|
||||
autoComplete="off"
|
||||
placeholder="********"
|
||||
/>
|
||||
<InputError message={errors.password} />
|
||||
<InputError message={errors.password} label="Kata sandi" />
|
||||
</div>
|
||||
|
||||
<div className="flex items-center space-x-3">
|
||||
|
||||
@ -36,6 +36,7 @@ export default function Register() {
|
||||
/>
|
||||
<InputError
|
||||
message={errors.name}
|
||||
label="Name"
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
@ -51,7 +52,7 @@ export default function Register() {
|
||||
name="email"
|
||||
placeholder="email@example.com"
|
||||
/>
|
||||
<InputError message={errors.email} />
|
||||
<InputError message={errors.email} label="Email address" />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
@ -64,7 +65,7 @@ export default function Register() {
|
||||
name="password"
|
||||
placeholder="Password"
|
||||
/>
|
||||
<InputError message={errors.password} />
|
||||
<InputError message={errors.password} label="Password" />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
@ -81,6 +82,7 @@ export default function Register() {
|
||||
/>
|
||||
<InputError
|
||||
message={errors.password_confirmation}
|
||||
label="Confirm password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ export default function ResetPassword({ token, email }: Props) {
|
||||
/>
|
||||
<InputError
|
||||
message={errors.email}
|
||||
label="Email"
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
@ -51,7 +52,7 @@ export default function ResetPassword({ token, email }: Props) {
|
||||
autoFocus
|
||||
placeholder="Password"
|
||||
/>
|
||||
<InputError message={errors.password} />
|
||||
<InputError message={errors.password} label="Password" />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
@ -67,6 +68,7 @@ export default function ResetPassword({ token, email }: Props) {
|
||||
/>
|
||||
<InputError
|
||||
message={errors.password_confirmation}
|
||||
label="Confirm password"
|
||||
className="mt-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -73,6 +73,7 @@ export default function TwoFactorChallenge() {
|
||||
/>
|
||||
<InputError
|
||||
message={errors.recovery_code}
|
||||
label="Recovery code"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
@ -99,7 +100,7 @@ export default function TwoFactorChallenge() {
|
||||
</InputOTPGroup>
|
||||
</InputOTP>
|
||||
</div>
|
||||
<InputError message={errors.code} />
|
||||
<InputError message={errors.code} label="Code" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@ -56,6 +56,7 @@ export default function Profile({
|
||||
<InputError
|
||||
className="mt-2"
|
||||
message={errors.name}
|
||||
label="Name"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -76,6 +77,7 @@ export default function Profile({
|
||||
<InputError
|
||||
className="mt-2"
|
||||
message={errors.email}
|
||||
label="Email address"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -100,7 +100,7 @@ export default function Security({
|
||||
placeholder="Current password"
|
||||
/>
|
||||
|
||||
<InputError message={errors.current_password} />
|
||||
<InputError message={errors.current_password} label="Current password" />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
@ -115,7 +115,7 @@ export default function Security({
|
||||
placeholder="New password"
|
||||
/>
|
||||
|
||||
<InputError message={errors.password} />
|
||||
<InputError message={errors.password} label="New password" />
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
@ -133,6 +133,7 @@ export default function Security({
|
||||
|
||||
<InputError
|
||||
message={errors.password_confirmation}
|
||||
label="Confirm password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user