Skip to content

Commit

Permalink
Merge pull request #257 from CtrI-Alt-Del/supplierComponent
Browse files Browse the repository at this point in the history
[Front]: ✨ Feat: Supplier select component in product form
  • Loading branch information
JohnPetros authored Nov 27, 2024
2 parents d5d4dad + 2216e37 commit 1b17548
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 2 deletions.
6 changes: 6 additions & 0 deletions apps/web/src/constants/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,10 @@ export const CACHE = {
company: {
key: '/company',
},
supplier: {
key: '/supplier',
},
suppliers: {
key: '/suppliers',
},
}
81 changes: 81 additions & 0 deletions apps/web/src/ui/components/commons/supplier-select/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Button, Pagination, Spinner } from '@nextui-org/react'
import { Icon } from '@/ui/components/commons/icon'
import { Select } from '@/ui/components/commons/select'
import { Dialog } from '../dialog'
import { useSupplierSelect } from './use-supplier-select'

type SupplierSelectProps = {
defeaultSupplierId?: string
className?: string
onSelectChange: (supplierId: string) => void
}

export const SupplierSelect = ({
className,
defeaultSupplierId,
onSelectChange,
}: SupplierSelectProps) => {
const {
suppliers,
isFetching,
page,
totalPages,
selectedSupplierName,
handleSupplierIdChange,
handleSupplierPageChange,
} = useSupplierSelect(onSelectChange, defeaultSupplierId)

return isFetching ? (
<Spinner label='Carregando...' className='w-full h-full mx-auto' />
) : (
<div className='space-y-2'>
<Dialog
title='Selecione um fornecedor'
size='2xl'
trigger={
<Select className={className}>
{selectedSupplierName ? selectedSupplierName : 'Selecione fornecedor'}
</Select>
}
>
{(closeDrawer) =>
suppliers.length === 0 ? (
<p className='text-center text-bg-zinc-600 font-semibold my-12'>
Nenhum fornecedor registrado.
</p>
) : (
<>
<div className='space-y-4'>
{suppliers.map((supplier) => (
<div
key={supplier.id}
className='flex items-center justify-between p-4 border border-zinc-300 rounded-lg'
>
<span className='font-medium text-zinc-800'>{supplier.name}</span>
<Button
className='bg-transparent hover:bg-primary hover:text-white duration-1000 border-zinc-400 h-10 min-w-10'
onClick={() => {
handleSupplierIdChange(supplier.id)
closeDrawer()
}}
>
<Icon name='plus' size={18} />
</Button>
</div>
))}
</div>
{totalPages !== 1 && (
<Pagination
page={page}
total={totalPages}
onChange={handleSupplierPageChange}
showControls
/>
)}
</>
)
}
</Dialog>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { CACHE } from '@/constants'
import { useApi, useCache, useToast, useUrlParamNumber } from '@/ui/hooks'
import { Supplier } from '@stocker/core/entities'
import { useEffect, useState } from 'react'
import { useAuthContext } from '../../contexts/auth-context'

export function useSupplierSelect(
onSelectChange: (supplierId: string) => void,
defeaultSelectedSupplierId?: string,
) {
const [supplierId, setSupplierId] = useState(defeaultSelectedSupplierId)
const { suppliersService } = useApi()
const { company } = useAuthContext()
const { showError } = useToast()
const [page, setPage] = useUrlParamNumber('supplierPage', 1)
const [expandedItems, setExpandedItems] = useState<{ [key: string]: boolean }>({})

function handleSupplierIdChange(supplierId: string) {
setSupplierId(supplierId)
onSelectChange(supplierId)
}

async function fetchSupplier() {
if (!supplierId) return

const response = await suppliersService.getSupplier(supplierId)
if (response.isFailure) {
showError(response.errorMessage)
return
}
return response.body
}

async function fetchSuppliers() {
if (!company) return

const response = await suppliersService.listSuppliers({
page,
companyId: company.id,
})
if (response.isFailure) {
showError(response.errorMessage)
return
}
return response.body
}

const { data: supplierData } = useCache({
fetcher: fetchSupplier,
key: CACHE.supplier.key,
dependencies: [supplierId],
})

const { data: suppliersData, isFetching } = useCache({
fetcher: fetchSuppliers,
key: CACHE.suppliers.key,
dependencies: [page],
})

function handlePageChange(page: number) {
setPage(page)
}

function handleAccordionClick(id: string) {
setExpandedItems((prev) => ({
...prev,
[id]: !prev[id],
}))
}

const suppliers = suppliersData ? suppliersData.items.map(Supplier.create) : []
const itemsCount = suppliersData ? suppliersData.itemsCount : 0

return {
isFetching,
totalPages: Math.ceil(itemsCount / 10),
page,
suppliers,
selectedSupplierName: supplierData?.name,
expandedItems,
handleSupplierIdChange,
handleAccordionClick,
handleSupplierPageChange: handlePageChange,
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { useRegisterProductForm } from './use-register-product-form'
import { ImageInput } from '@/ui/components/commons/image-input'
import type { ImageInputRef } from '@/ui/components/commons/image-input/types'
import { CategorySelect } from '@/ui/components/commons/category-select'
import { SupplierSelect } from '@/ui/components/commons/supplier-select'


type RegisterProductFormProps = {
onCancel: VoidFunction
Expand Down Expand Up @@ -66,7 +68,18 @@ export const RegisterProductForm = ({ onSubmit, onCancel }: RegisterProductFormP
/>

<div className='grid grid-cols-2 gap-6'>
<Input label='Fornecedor' />
<Controller
name='supplierId'
control={control}
render={({ field: { onChange } }) => (
<div className='w-full '>
<SupplierSelect onSelectChange={onChange} className='w-full' />
{errors.supplierId && (
<p className='text-red-600 text-sm'>{errors.supplierId?.message}</p>
)}
</div>
)}
/>
<Input
label='Unidade'
isRequired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const registerProductFormSchema = z.object({
code: stringSchema,
minimumStock: nonZeroIntegerSchema,
categoryId: idSchema.optional(),
supplierId: idSchema.optional(),
model: z
.string()
.transform((value) => (value === '' ? undefined : value))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ImageInput } from '@/ui/components/commons/image-input'
import type { ImageInputRef } from '@/ui/components/commons/image-input/types'
import { useUpdateProductForm } from './use-update-product-form'
import { CategorySelect } from '@/ui/components/commons/category-select'
import { SupplierSelect } from '@/ui/components/commons/supplier-select'

type RegisterProductFormProps = {
product: Product
Expand Down Expand Up @@ -87,7 +88,18 @@ export const UpdateProductForm = ({
/>

<div className='grid grid-cols-1 md:grid-cols-2 gap-6'>
<Input label='Fornecedor' />
<Controller
name='supplierId'
control={control}
render={({ field: { onChange } }) => (
<div className='w-full '>
<SupplierSelect onSelectChange={onChange} className='w-full' />
{errors.supplierId && (
<p className='text-red-600 text-sm'>{errors.supplierId?.message}</p>
)}
</div>
)}
/>
<Input
label='Unidade'
isRequired
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ const updateProductFormSchema = z.object({
code: stringSchema,
minimumStock: nonZeroIntegerSchema,
categoryId: idSchema.optional(),
supplierId: idSchema.optional(),
model: z.string().optional(),
isActive: booleanSchema.default(true),
})
Expand Down

0 comments on commit 1b17548

Please sign in to comment.