cat
deploy to danak / build_and_deploy (push) Has been cancelled

This commit is contained in:
2026-07-09 17:35:24 +03:30
parent adad81467c
commit 1e6504771a
4 changed files with 76 additions and 44 deletions
+16 -7
View File
@@ -12,27 +12,36 @@ type Props = {
XOR<{ children: ReactNode }, { label: string }>;
const Button: FC<Props> = memo((props: Props) => {
const {
className,
disabled,
isLoading,
percentage,
label,
children,
...buttonProps
} = props
const buttonClass = clx(
'flex rounded-xl items-center justify-center text-center h-10 text-sm bg-primary w-full',
props.disabled && 'cursor-not-allowed opacity-60',
props.className
disabled && 'cursor-not-allowed opacity-60',
className
);
return (
<button {...props} className={`${buttonClass} ${props.className}`} disabled={props.disabled || props.isLoading}>
<button {...buttonProps} className={`${buttonClass} ${className}`} disabled={disabled || isLoading}>
{
props.isLoading ?
isLoading ?
<div className='flex gap-2 items-center'>
{
!props.percentage ?
!percentage ?
<MoonLoader size={20} color='#000' />
:
<span>{props.percentage}%</span>
<span>{percentage}%</span>
}
</div>
:
props.label || props.children
label || children
}
</button>
)
+21 -11
View File
@@ -17,27 +17,37 @@ type Props = {
} & SelectHTMLAttributes<HTMLSelectElement>
const Select: FC<Props> = (props: Props) => {
const {
className,
items,
error_text,
placeholder,
label,
readOnly,
...selectProps
} = props
return (
<div className='w-full'>
{
props.label &&
label &&
<label className='text-sm text-primary-content'>
{props.label}
{label}
</label>
}
<div className='relative'>
<select {...props} className={clx(
<select {...selectProps} className={clx(
'w-full bg-white border border-border input-surface relative block appearance-none px-2.5 h-10 text-sm rounded-[10px] transition-colors',
props.readOnly && 'bg-muted border-0 text-muted-foreground',
props.className,
props.label && 'mt-1'
readOnly && 'bg-muted border-0 text-muted-foreground',
className,
label && 'mt-1'
)}>
{
props.placeholder &&
<option value="" disabled selected>{props.placeholder}</option>
placeholder &&
<option value="" disabled>{placeholder}</option>
}
{
props.items?.map((item) => {
items?.map((item) => {
return (
<option key={item.value} value={item.value} disabled={item.disabled}>
{item.label}
@@ -51,9 +61,9 @@ const Select: FC<Props> = (props: Props) => {
/>
</div>
{
props.error_text && props.error_text !== '' ?
error_text && error_text !== '' ?
<div className='text-xs text-right text-red-600 mt-2 mr-2 font-medium'>
{props.error_text}
{error_text}
</div>
: null
}
+37 -24
View File
@@ -54,12 +54,24 @@ const getCategoriesAtLevel = (
return parent?.children ?? []
}
const extractList = <T,>(payload: unknown): T[] => {
if (Array.isArray(payload)) return payload
if (payload && typeof payload === 'object' && 'data' in payload) {
const maybeData = (payload as { data?: unknown }).data
return Array.isArray(maybeData) ? (maybeData as T[]) : []
}
return []
}
const ProductsSelect: FC<Props> = (props) => {
const { error_text, onProductSelect, value, onChange, ...rest } = props
const [selectedPath, setSelectedPath] = useState<string[]>([])
const { data: categoriesData } = useGetCategories()
const categories = categoriesData?.data ?? []
const categories = useMemo(
() => extractList<CategoryType>(categoriesData),
[categoriesData],
)
const levelsToShow = useMemo(() => {
const levels = [0]
@@ -75,11 +87,7 @@ const ProductsSelect: FC<Props> = (props) => {
}, [categories, selectedPath])
const activeCategoryId = selectedPath.at(-1) ?? ''
const activeCategory = activeCategoryId
? findCategoryById(categories, activeCategoryId)
: null
const isLeafCategory = !!activeCategory && !(activeCategory.children?.length)
const canLoadProducts = isLeafCategory
const canLoadProducts = !!activeCategoryId
const { data: productsData } = useGetProducts(activeCategoryId || undefined, {
enabled: canLoadProducts,
@@ -90,23 +98,28 @@ const ProductsSelect: FC<Props> = (props) => {
enabled: needsResolve,
})
const productItems = useMemo(
() => (productsData?.data ?? []).map((product) => ({
label: product.title,
value: product.id,
})),
const products = useMemo(
() => extractList<ProductType>(productsData),
[productsData],
)
const resolveProducts = useMemo(
() => extractList<ProductType>(resolveProductsData),
[resolveProductsData],
)
const productItems = useMemo(
() => products.map((product) => ({
label: product.title,
value: product.id,
})),
[products],
)
useEffect(() => {
if (!value) {
setSelectedPath([])
return
}
if (!value || selectedPath.length || !categories.length) return
if (selectedPath.length || !categories.length) return
const product = resolveProductsData?.data?.find((item) => item.id === value)
const product = resolveProducts.find((item) => item.id === value)
if (!product?.category) return
const categoryId = typeof product.category === 'string'
@@ -117,7 +130,7 @@ const ProductsSelect: FC<Props> = (props) => {
if (!path) return
setSelectedPath(path)
}, [value, resolveProductsData, categories, selectedPath.length])
}, [value, resolveProducts, categories, selectedPath.length])
useEffect(() => {
if (!value) {
@@ -125,11 +138,11 @@ const ProductsSelect: FC<Props> = (props) => {
return
}
const product = productsData?.data?.find((item) => item.id === value)
?? resolveProductsData?.data?.find((item) => item.id === value)
const product = products.find((item) => item.id === value)
?? resolveProducts.find((item) => item.id === value)
onProductSelect?.(product)
}, [value, productsData, resolveProductsData, onProductSelect])
}, [value, products, resolveProducts, onProductSelect])
const emitProductChange = (nextValue: string) => {
onChange?.({
@@ -145,7 +158,7 @@ const ProductsSelect: FC<Props> = (props) => {
const handleProductChange = (e: ChangeEvent<HTMLSelectElement>) => {
onChange?.(e)
const product = productsData?.data?.find((item) => item.id === e.target.value)
const product = products.find((item) => item.id === e.target.value)
onProductSelect?.(product)
}
@@ -175,7 +188,7 @@ const ProductsSelect: FC<Props> = (props) => {
value={value}
onChange={handleProductChange}
error_text={error_text}
disabled={!canLoadProducts}
disabled={!activeCategoryId}
{...rest}
/>
</div>
+2 -2
View File
@@ -2,7 +2,7 @@ import axios from "@/config/axios";
import type {
AddTicketType,
AttributeResponseType,
CategoriesResponseType,
CategoryType,
MyRequestsResponseType,
RequestDetailResponseType,
RequestType,
@@ -11,7 +11,7 @@ import type {
} from "../type/Types";
export const getCategories = async () => {
const { data } = await axios.get<CategoriesResponseType>(`/public/categories`);
const { data } = await axios.get<CategoryType[]>(`/public/category/tree?isActive=true`);
return data;
};