fix many bugs

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-30 19:03:31 +03:00
co-authored by Copilot
parent 0c43c8edd2
commit e1ef6fa2ea
24 changed files with 333 additions and 82 deletions
@@ -131,7 +131,7 @@ export function CrudDialog<TClient extends CrudDialogClient>({
Add {title}
</Button>
</div>
<DataTable
<DataTable
columns={allColumns}
data={crud.items}
pagination={crud.pagination}
@@ -21,6 +21,42 @@ export type UseCrudDialogOptions<TClient extends CrudDialogClient> = {
resourceLabel?: string
}
type CrudListShape = {
data?: unknown
meta?: {
last_page?: number
total?: number
}
pagination?: {
last_page?: number
total?: number
}
}
function normalizeCrudListResponse(response: unknown): {
items: any[]
meta?: { last_page?: number; total?: number }
} {
const root = (response ?? {}) as CrudListShape
const directData = root.data
if (Array.isArray(directData)) {
return { items: directData, meta: root.meta ?? root.pagination }
}
if (directData && typeof directData === "object") {
const nested = directData as CrudListShape
if (Array.isArray(nested.data)) {
return {
items: nested.data,
meta: nested.meta ?? nested.pagination ?? root.meta ?? root.pagination,
}
}
}
return { items: [], meta: root.meta ?? root.pagination }
}
// ── Hook ──
export function useCrudDialog<TClient extends CrudDialogClient>({
@@ -46,17 +82,23 @@ export function useCrudDialog<TClient extends CrudDialogClient>({
const { data, isLoading } = useQuery({
queryKey: fullQueryKey,
queryFn: () => {
queryFn: async () => {
const params: Record<string, unknown> = { page, per_page: pageSize }
if (sortBy) params.sort_by = sortBy
if (sortOrder) params.sort_order = sortOrder
return client.list(params)
try {
return await client.list(params)
} catch {
// Some endpoints ignore/reject pagination params; retry without params.
return client.list()
}
},
})
const responseData = (data as any)?.data ?? []
const items = Array.isArray(responseData) ? responseData : []
const meta = (data as any)?.meta
const normalized = normalizeCrudListResponse(data)
const items = normalized.items
const meta = normalized.meta
const pagination: DataViewPaginationState = {
page,
@@ -5,6 +5,7 @@ export type TextInputFieldProps = BaseFieldControlProps<string> & {
placeholder?: string
type?: React.HTMLInputTypeAttribute
step?: React.InputHTMLAttributes<HTMLInputElement>["step"]
formatter?: (value: string) => string
}
export function TextInputField({
@@ -17,11 +18,12 @@ export function TextInputField({
placeholder,
type = "text",
step,
formatter,
}: TextInputFieldProps) {
return (
<Input
value={value}
onChange={(e) => onChange(e.target.value)}
onChange={(e) => onChange(formatter ? formatter(e.target.value) : e.target.value)}
onBlur={onBlur}
name={name}
disabled={disabled}
@@ -38,7 +38,7 @@ export function TimePickerField({
disabled,
invalid,
placeholder = "Pick a time",
withSeconds = true,
withSeconds = false,
}: TimePickerFieldProps) {
const { hours, minutes, seconds } = parseTime(value ?? "")
const hasValue = !!value
@@ -20,5 +20,5 @@ export function RhfTimeField<
TValues extends FieldValues,
TName extends FieldPath<TValues>,
>(props: RhfTimeFieldProps<TValues, TName>) {
return <RhfField {...props} component={TimePickerField} />
return <RhfField {...props} component={TimePickerField} />
}
@@ -99,3 +99,11 @@ export function formatCurrency(
if (isNaN(num)) return "—"
return new Intl.NumberFormat(locale, { style: "currency", currency }).format(num)
}
/**
* Format text to uppercase.
*/
export function formatUppercase(value?: string | null): string {
if (value == null) return ""
return value.toUpperCase()
}