add error
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
import { useRequest, useToggle } from 'ahooks'
|
||||
import { debounce } from 'lodash'
|
||||
import { CheckIcon, UserIcon } from 'lucide-react'
|
||||
import { initials, cn } from '@repo/ui/lib/utils'
|
||||
import { formatCPF } from '@brazilian-utils/brazilian-utils'
|
||||
|
||||
import { Avatar, AvatarFallback } from '@repo/ui/components/ui/avatar'
|
||||
import { Abbr } from '@repo/ui/components/abbr'
|
||||
import {
|
||||
@@ -10,7 +9,7 @@ import {
|
||||
InputGroupAddon,
|
||||
InputGroupInput
|
||||
} from '@repo/ui/components/ui/input-group'
|
||||
|
||||
import { initials, cn } from '@repo/ui/lib/utils'
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
@@ -31,19 +30,25 @@ interface AsyncComboboxProps {
|
||||
title: string
|
||||
onChange: (props: any) => void
|
||||
onSearch: (search: string) => Promise<any[]>
|
||||
error?: any
|
||||
}
|
||||
export function AsyncCombobox({
|
||||
title,
|
||||
value,
|
||||
onSearch,
|
||||
onChange
|
||||
onChange,
|
||||
error
|
||||
}: AsyncComboboxProps) {
|
||||
const [open, { set }] = useToggle()
|
||||
const {
|
||||
data = [],
|
||||
loading,
|
||||
runAsync
|
||||
} = useRequest(onSearch, { manual: true, defaultParams: [''] })
|
||||
} = useRequest(onSearch, {
|
||||
manual: true,
|
||||
debounceWait: 300,
|
||||
defaultParams: ['']
|
||||
})
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={set}>
|
||||
@@ -55,6 +60,7 @@ export function AsyncCombobox({
|
||||
placeholder={title}
|
||||
className="cursor-pointer"
|
||||
autoComplete="off"
|
||||
aria-invalid={!!error}
|
||||
/>
|
||||
<InputGroupAddon>
|
||||
<UserIcon />
|
||||
@@ -73,11 +79,12 @@ export function AsyncCombobox({
|
||||
<CommandInput
|
||||
placeholder={title}
|
||||
autoComplete="off"
|
||||
onValueChange={debounce(runAsync, 300)}
|
||||
onValueChange={runAsync}
|
||||
/>
|
||||
|
||||
<CommandList>
|
||||
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
|
||||
|
||||
<CommandGroup>
|
||||
{data.map(({ id, name, email, cpf }) => (
|
||||
<CommandItem
|
||||
|
||||
@@ -2,20 +2,26 @@ import { z } from 'zod'
|
||||
|
||||
export const enrollment = z.object({
|
||||
user: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
cpf: z.string()
|
||||
})
|
||||
.object(
|
||||
{
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
email: z.string(),
|
||||
cpf: z.string()
|
||||
},
|
||||
{ error: 'Escolhe um colaborador' }
|
||||
)
|
||||
.required(),
|
||||
course: z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
access_period: z.number(),
|
||||
unit_price: z.number()
|
||||
})
|
||||
.object(
|
||||
{
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
access_period: z.number(),
|
||||
unit_price: z.number()
|
||||
},
|
||||
{ error: 'Escolha um curso' }
|
||||
)
|
||||
.required(),
|
||||
deduplication_window: z
|
||||
.object({
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Route } from './+types/route'
|
||||
|
||||
import { useRequest, useToggle } from 'ahooks'
|
||||
import { useToggle } from 'ahooks'
|
||||
import { ErrorMessage } from '@hookform/error-message'
|
||||
import {
|
||||
CalendarIcon,
|
||||
CopyIcon,
|
||||
@@ -187,25 +188,55 @@ export default function Route({
|
||||
<Controller
|
||||
control={control}
|
||||
name={`enrollments.${index}.user`}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<AsyncCombobox
|
||||
value={value}
|
||||
title="Colaborador"
|
||||
onChange={onChange}
|
||||
onSearch={onSearch}
|
||||
/>
|
||||
render={({
|
||||
field: { name, value, onChange },
|
||||
fieldState
|
||||
}) => (
|
||||
<div className="grid gap-1">
|
||||
<AsyncCombobox
|
||||
value={value}
|
||||
title="Colaborador"
|
||||
onChange={onChange}
|
||||
onSearch={onSearch}
|
||||
error={fieldState.error}
|
||||
/>
|
||||
<ErrorMessage
|
||||
errors={formState.errors}
|
||||
name={name}
|
||||
render={({ message }) => (
|
||||
<p className="text-destructive text-sm">
|
||||
{message}
|
||||
</p>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
control={control}
|
||||
name={`enrollments.${index}.course`}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<FacetedFilter
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
options={courses}
|
||||
/>
|
||||
render={({
|
||||
field: { name, value, onChange },
|
||||
fieldState
|
||||
}) => (
|
||||
<div className="grid gap-1">
|
||||
<FacetedFilter
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
options={courses}
|
||||
error={fieldState.error}
|
||||
/>
|
||||
<ErrorMessage
|
||||
errors={formState.errors}
|
||||
name={name}
|
||||
render={({ message }) => (
|
||||
<p className="text-destructive text-sm">
|
||||
{message}
|
||||
</p>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
||||
@@ -296,9 +327,15 @@ interface FacetedFilterProps {
|
||||
value?: Course
|
||||
options: Promise<{ hits: any[] }>
|
||||
onChange?: (value: any) => void
|
||||
error?: any
|
||||
}
|
||||
|
||||
function FacetedFilter({ value, onChange, options }: FacetedFilterProps) {
|
||||
function FacetedFilter({
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
error
|
||||
}: FacetedFilterProps) {
|
||||
const [search, setSearch] = useState<string>('')
|
||||
const [open, { set }] = useToggle()
|
||||
const { hits } = use(options)
|
||||
@@ -326,6 +363,7 @@ function FacetedFilter({ value, onChange, options }: FacetedFilterProps) {
|
||||
readOnly
|
||||
placeholder="Curso"
|
||||
value={value?.name || ''}
|
||||
aria-invalid={!!error}
|
||||
/>
|
||||
<InputGroupAddon>
|
||||
<BookIcon />
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1913,6 +1913,17 @@
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@hookform/error-message": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/error-message/-/error-message-2.0.1.tgz",
|
||||
"integrity": "sha512-U410sAr92xgxT1idlu9WWOVjndxLdgPUHEB8Schr27C9eh7/xUnITWpCMF93s+lGiG++D4JnbSnrb5A21AdSNg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0",
|
||||
"react-hook-form": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@hookform/resolvers": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz",
|
||||
@@ -7326,6 +7337,7 @@
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@brazilian-utils/brazilian-utils": "^1.1.0",
|
||||
"@hookform/error-message": "^2.0.1",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-avatar": "^1.1.11",
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@brazilian-utils/brazilian-utils": "^1.1.0",
|
||||
"@hookform/error-message": "^2.0.1",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.15",
|
||||
"@radix-ui/react-avatar": "^1.1.11",
|
||||
|
||||
Reference in New Issue
Block a user