category attibute
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
import { type FC } from 'react'
|
||||
import { useParams } from 'react-router-dom'
|
||||
import TitleLine from '../../components/TitleLine'
|
||||
import Button from '../../components/Button'
|
||||
import { AttributesModal } from './components'
|
||||
import { useAttributesLogic } from './hooks/useAttributesLogic'
|
||||
import { useGetCategoryAttributes } from './hooks/useCategoryData'
|
||||
import { type CategoryAttributeType } from './types/Types'
|
||||
import { Add, Edit, Trash } from 'iconsax-react'
|
||||
|
||||
const Attributes: FC = () => {
|
||||
const { id } = useParams()
|
||||
const { data: attributesData, isLoading: attributesLoading } = useGetCategoryAttributes(id!)
|
||||
const {
|
||||
category,
|
||||
isLoading,
|
||||
isModalOpen,
|
||||
newAttribute,
|
||||
openModal,
|
||||
closeModal,
|
||||
setNewAttribute,
|
||||
addAttribute,
|
||||
addValueToAttribute,
|
||||
removeValueFromAttribute,
|
||||
isAddingLoading
|
||||
} = useAttributesLogic({ id })
|
||||
|
||||
const attributes = attributesData?.results?.data?.category?.attributes || []
|
||||
|
||||
if (isLoading) {
|
||||
return <div>در حال بارگذاری...</div>
|
||||
}
|
||||
|
||||
if (!category) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="max-w-6xl mx-auto p-6">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
|
||||
<TitleLine title="مدیریت ویژگیهای دستهبندی" />
|
||||
<div className="mt-4 space-y-2">
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
دستهبندی یافت نشد
|
||||
</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (!category.leaf) {
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="max-w-6xl mx-auto p-6">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
|
||||
<TitleLine title="مدیریت ویژگیهای دستهبندی" />
|
||||
<div className="mt-4 space-y-2">
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
{category.title_fa}
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
این دستهبندی امکان داشتن ویژگی ندارد
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex flex-col items-center justify-center py-12 px-6 border-2 border-dashed border-gray-300 rounded-lg bg-gray-50">
|
||||
<div className="text-center space-y-4">
|
||||
<div className="w-16 h-16 mx-auto bg-gray-200 rounded-full flex items-center justify-center">
|
||||
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.732-.833-2.5 0L4.268 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-1">امکان ساخت ویژگی وجود ندارد</h3>
|
||||
<p className="text-gray-500">ویژگیها فقط برای دستهبندیهای نهایی (leaf) قابل تعریف هستند</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50">
|
||||
<div className="max-w-6xl mx-auto p-6">
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
|
||||
<TitleLine title="مدیریت ویژگیهای دستهبندی" />
|
||||
<div className="mt-4 space-y-2">
|
||||
<h1 className="text-2xl font-bold text-gray-900">
|
||||
{category.title_fa}
|
||||
</h1>
|
||||
<p className="text-gray-600">
|
||||
مدیریت ویژگیهای دستهبندی
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h3 className="text-lg font-semibold text-gray-900">
|
||||
ویژگیها ({attributes.length})
|
||||
</h3>
|
||||
<Button
|
||||
onClick={openModal}
|
||||
className="flex w-fit items-center gap-2"
|
||||
>
|
||||
<Add color='#fff' size={16} />
|
||||
اضافه کردن ویژگی
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{attributesLoading ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="text-gray-500">در حال بارگذاری ویژگیها...</div>
|
||||
</div>
|
||||
) : attributes.length === 0 ? (
|
||||
<div className="text-center py-12">
|
||||
<div className="w-16 h-16 mx-auto bg-gray-200 rounded-full flex items-center justify-center mb-4">
|
||||
<svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
||||
</svg>
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-gray-900 mb-1">هنوز ویژگیای اضافه نشده</h3>
|
||||
<p className="text-gray-500 mb-4">ویژگیهای دستهبندی به شما کمک میکنند محصولات را بهتر توصیف کنید</p>
|
||||
<Button
|
||||
onClick={openModal}
|
||||
variant="outline"
|
||||
className="flex items-center gap-2 mx-auto"
|
||||
>
|
||||
<Add size={16} />
|
||||
اضافه کردن اولین ویژگی
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
{attributes.map((attribute: CategoryAttributeType) => (
|
||||
<div key={attribute._id} className="border border-gray-200 rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<h4 className="font-medium text-gray-900">{attribute.title}</h4>
|
||||
<span className={`px-2 py-1 text-xs rounded-full ${attribute.type === 'text' ? 'bg-blue-100 text-blue-800' :
|
||||
attribute.type === 'number' ? 'bg-green-100 text-green-800' :
|
||||
attribute.type === 'select' ? 'bg-purple-100 text-purple-800' :
|
||||
'bg-gray-100 text-gray-800'
|
||||
}`}>
|
||||
{attribute.type === 'text' ? 'متن' :
|
||||
attribute.type === 'number' ? 'عدد' :
|
||||
attribute.type === 'select' ? 'انتخابی' :
|
||||
attribute.type === 'boolean' ? 'بله/خیر' : attribute.type}
|
||||
</span>
|
||||
{attribute.required && (
|
||||
<span className="px-2 py-1 text-xs bg-red-100 text-red-800 rounded-full">
|
||||
اجباری
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-blue-500 hover:text-blue-700"
|
||||
>
|
||||
<Edit size={16} />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="text-red-500 hover:text-red-700"
|
||||
>
|
||||
<Trash size={16} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
{attribute.hint && (
|
||||
<p className="text-sm text-gray-600 mb-2">{attribute.hint}</p>
|
||||
)}
|
||||
{attribute.type === 'select' && attribute.values.length > 0 && (
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{attribute.values.map((value, index) => (
|
||||
<span
|
||||
key={index}
|
||||
className="px-2 py-1 bg-gray-100 text-gray-700 text-xs rounded"
|
||||
>
|
||||
{value.text}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AttributesModal
|
||||
isOpen={isModalOpen}
|
||||
onClose={closeModal}
|
||||
newAttribute={newAttribute}
|
||||
onAttributeChange={setNewAttribute}
|
||||
onAdd={addAttribute}
|
||||
onAddValue={addValueToAttribute}
|
||||
onRemoveValue={removeValueFromAttribute}
|
||||
isLoading={isAddingLoading}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Attributes
|
||||
@@ -68,6 +68,13 @@ const CategoryList: FC = () => {
|
||||
<Button className='h-7 text-xs w-fit px-4' label='افزودن تنوع' />
|
||||
</Link> : <Lock size={20} color='#8C90A3' />}
|
||||
</Td>
|
||||
<Td text="">
|
||||
<div className="flex items-center gap-2">
|
||||
{category.leaf ? <Link to={`${Pages.category.attributes}${category._id}`}>
|
||||
<Button className='h-7 text-xs w-fit px-4' label='ویژگیها' variant='outline' />
|
||||
</Link> : <Lock size={20} color='#8C90A3' />}
|
||||
</div>
|
||||
</Td>
|
||||
<Td text="">
|
||||
<div className="flex items-center gap-2">
|
||||
<Link to={`${Pages.category.update}${category._id}`} state={{ from: 'list' }}>
|
||||
@@ -127,6 +134,7 @@ const CategoryList: FC = () => {
|
||||
<Td text={'تصویر'} />
|
||||
<Td text={'توضیحات'} />
|
||||
<Td text={'تنوع'} />
|
||||
<Td text={'ویژگیها'} />
|
||||
<Td text={'عملیات'} />
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -0,0 +1,162 @@
|
||||
import { type FC, useState } from 'react'
|
||||
import Button from '../../../components/Button'
|
||||
import DefaulModal from '../../../components/DefaulModal'
|
||||
import Input from '../../../components/Input'
|
||||
import Textarea from '../../../components/Textarea'
|
||||
import RadioGroup from '../../../components/RadioGroup'
|
||||
import Switch from '../../../components/Switch'
|
||||
import { type CreateCategoryAttributeType } from '../types/Types'
|
||||
import { Add, Trash } from 'iconsax-react'
|
||||
|
||||
interface AttributesModalProps {
|
||||
isOpen: boolean
|
||||
onClose: () => void
|
||||
newAttribute: CreateCategoryAttributeType
|
||||
onAttributeChange: (field: keyof CreateCategoryAttributeType, value: string | boolean | { text: string }[]) => void
|
||||
onAdd: () => void
|
||||
onAddValue: (text: string) => void
|
||||
onRemoveValue: (index: number) => void
|
||||
isLoading?: boolean
|
||||
}
|
||||
|
||||
const AttributesModal: FC<AttributesModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
newAttribute,
|
||||
onAttributeChange,
|
||||
onAdd,
|
||||
onAddValue,
|
||||
onRemoveValue,
|
||||
isLoading = false
|
||||
}) => {
|
||||
const [newValue, setNewValue] = useState('')
|
||||
|
||||
const attributeTypes = [
|
||||
{ value: 'text', label: 'متن' },
|
||||
{ value: 'number', label: 'عدد' },
|
||||
{ value: 'select', label: 'انتخابی' },
|
||||
{ value: 'boolean', label: 'بله/خیر' }
|
||||
]
|
||||
|
||||
const handleAddValue = () => {
|
||||
if (newValue.trim()) {
|
||||
onAddValue(newValue.trim())
|
||||
setNewValue('')
|
||||
}
|
||||
}
|
||||
|
||||
const isFormValid = () => {
|
||||
return !!newAttribute.title.trim() &&
|
||||
(newAttribute.type !== 'select' || newAttribute.values.length > 0)
|
||||
}
|
||||
|
||||
return (
|
||||
<DefaulModal
|
||||
open={isOpen}
|
||||
close={onClose}
|
||||
isHeader={true}
|
||||
title_header="اضافه کردن ویژگی جدید"
|
||||
width={600}
|
||||
>
|
||||
<div className="space-y-6">
|
||||
<Input
|
||||
label="عنوان ویژگی"
|
||||
placeholder="مثال: وزن, رنگ, سایز"
|
||||
value={newAttribute.title}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => onAttributeChange('title', e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">نوع ویژگی</label>
|
||||
<RadioGroup
|
||||
items={attributeTypes}
|
||||
selected={newAttribute.type}
|
||||
onChange={(value) => onAttributeChange('type', value)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
label="راهنما (اختیاری)"
|
||||
placeholder="توضیحات برای کاربر در مورد این ویژگی"
|
||||
value={newAttribute.hint}
|
||||
onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onAttributeChange('hint', e.target.value)}
|
||||
rows={3}
|
||||
/>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="space-y-1">
|
||||
<label className="text-sm font-medium">اجباری</label>
|
||||
<p className="text-xs text-gray-500">آیا این ویژگی اجباری است؟</p>
|
||||
</div>
|
||||
<Switch
|
||||
active={newAttribute.required}
|
||||
onChange={(checked) => onAttributeChange('required', checked)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{newAttribute.type === 'select' && (
|
||||
<div className="space-y-3">
|
||||
<label className="text-sm font-medium">مقادیر قابل انتخاب</label>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Input
|
||||
placeholder="مقدار جدید اضافه کنید"
|
||||
value={newValue}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setNewValue(e.target.value)}
|
||||
onKeyPress={(e) => e.key === 'Enter' && handleAddValue()}
|
||||
/>
|
||||
<Button
|
||||
onClick={handleAddValue}
|
||||
variant="outline"
|
||||
// size="sm"
|
||||
disabled={!newValue.trim()}
|
||||
className='w-fit mt-1'
|
||||
>
|
||||
<Add size={16} color='#000' />
|
||||
</Button>
|
||||
</div>
|
||||
{newAttribute.values.length > 0 && (
|
||||
<div className="space-y-2 flex gap-3 items-center flex-wrap max-h-32 overflow-y-auto">
|
||||
{newAttribute.values.map((value, index) => (
|
||||
<div key={index} className="flex min-w-[30%] flex-1 items-center justify-between bg-gray-50 p-2 rounded">
|
||||
<div className="text-sm">{value.text}</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => onRemoveValue(index)}
|
||||
className="text-red-500 w-fit hover:text-red-700"
|
||||
>
|
||||
<Trash color='red' size={20} />
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
<div className='min-w-[30%] flex-1 px-2'></div>
|
||||
<div className='min-w-[30%] flex-1 px-2'></div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex gap-3 pt-4 border-t border-border">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="flex-1"
|
||||
onClick={onClose}
|
||||
>
|
||||
انصراف
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="flex-1"
|
||||
onClick={onAdd}
|
||||
disabled={!isFormValid() || isLoading}
|
||||
>
|
||||
{isLoading ? 'در حال اضافه کردن...' : 'اضافه کردن'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DefaulModal>
|
||||
)
|
||||
}
|
||||
|
||||
export default AttributesModal
|
||||
@@ -8,3 +8,4 @@ export { default as VariantSizesList } from "./VariantSizesList";
|
||||
export { default as VariantColorsList } from "./VariantColorsList";
|
||||
export { default as VariantMeteragesList } from "./VariantMeteragesList";
|
||||
export { default as VariantModal } from "./VariantModal";
|
||||
export { default as AttributesModal } from "./AttributesModal";
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
import { useState } from 'react'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useGetCategoryById, useCreateCategoryAttribute } from './useCategoryData'
|
||||
import { type CreateCategoryAttributeType } from '../types/Types'
|
||||
import { toast } from 'react-toastify'
|
||||
import { extractErrorMessage } from '../../../helpers/utils'
|
||||
|
||||
interface UseAttributesLogicProps {
|
||||
id: string | undefined
|
||||
}
|
||||
|
||||
export const useAttributesLogic = ({ id }: UseAttributesLogicProps) => {
|
||||
const queryClient = useQueryClient()
|
||||
const { data, isLoading } = useGetCategoryById(id!)
|
||||
const createAttributeMutation = useCreateCategoryAttribute()
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false)
|
||||
const [newAttribute, setNewAttribute] = useState<CreateCategoryAttributeType>({
|
||||
title: '',
|
||||
type: 'text',
|
||||
required: false,
|
||||
values: []
|
||||
})
|
||||
|
||||
const categoryData = data?.results?.data
|
||||
const category = categoryData
|
||||
|
||||
const resetModal = () => {
|
||||
setIsModalOpen(false)
|
||||
setNewAttribute({
|
||||
title: '',
|
||||
type: 'text',
|
||||
required: false,
|
||||
values: []
|
||||
})
|
||||
}
|
||||
|
||||
const openModal = () => setIsModalOpen(true)
|
||||
const closeModal = () => resetModal()
|
||||
|
||||
const addAttribute = async () => {
|
||||
if (!id || !newAttribute.title.trim()) return
|
||||
|
||||
createAttributeMutation.mutate(
|
||||
{ id, data: newAttribute },
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success('ویژگی با موفقیت اضافه شد')
|
||||
resetModal()
|
||||
queryClient.invalidateQueries({ queryKey: ['category', id] })
|
||||
queryClient.invalidateQueries({ queryKey: ['category', 'attributes', id] })
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(extractErrorMessage(error))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
const updateNewAttribute = (field: keyof CreateCategoryAttributeType, value: any) => {
|
||||
setNewAttribute(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}))
|
||||
}
|
||||
|
||||
const addValueToAttribute = (text: string) => {
|
||||
if (!text.trim()) return
|
||||
setNewAttribute(prev => ({
|
||||
...prev,
|
||||
values: [...prev.values, { text: text.trim() }]
|
||||
}))
|
||||
}
|
||||
|
||||
const removeValueFromAttribute = (index: number) => {
|
||||
setNewAttribute(prev => ({
|
||||
...prev,
|
||||
values: prev.values.filter((_, i) => i !== index)
|
||||
}))
|
||||
}
|
||||
|
||||
return {
|
||||
// Data
|
||||
category,
|
||||
isLoading,
|
||||
|
||||
// Modal state
|
||||
isModalOpen,
|
||||
newAttribute,
|
||||
|
||||
// Actions
|
||||
openModal,
|
||||
closeModal,
|
||||
setNewAttribute: updateNewAttribute,
|
||||
addAttribute,
|
||||
addValueToAttribute,
|
||||
removeValueFromAttribute,
|
||||
|
||||
// Loading states
|
||||
isAddingLoading: createAttributeMutation.isPending
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import * as api from '../service/CategoryService';
|
||||
import { type CategoryType } from '../types/Types';
|
||||
import { type CategoryType, type CreateCategoryAttributeType, type CategoryAttributesResponseType } from '../types/Types';
|
||||
|
||||
export const useCreateCategory = () => {
|
||||
return useMutation({
|
||||
@@ -66,4 +66,19 @@ export const useDeleteCategoryVariant = () => {
|
||||
return useMutation({
|
||||
mutationFn: api.deleteCategoryVariant,
|
||||
});
|
||||
};
|
||||
|
||||
export const useCreateCategoryAttribute = () => {
|
||||
return useMutation({
|
||||
mutationFn: ({ id, data }: { id: string; data: CreateCategoryAttributeType }) =>
|
||||
api.createCategoryAttribute(id, data),
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetCategoryAttributes = (id: string) => {
|
||||
return useQuery<CategoryAttributesResponseType>({
|
||||
queryKey: ['category', 'attributes', id],
|
||||
queryFn: () => api.getCategoryAttributes(id),
|
||||
enabled: !!id,
|
||||
});
|
||||
};
|
||||
@@ -1,4 +1,5 @@
|
||||
export { default as CreateCategory } from "./Create";
|
||||
export { default as Attributes } from "./Attributes";
|
||||
export * from "./types/Types";
|
||||
export * from "./hooks/useCategoryData";
|
||||
export * from "./store/CategoryStore";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import axios from "../../../config/axios";
|
||||
import { type CategoryType, type SingleUploadResponseType, type CategoriesResponseType, type CategoryByIdResponseType, type CategoryVariantsResponseType, type CategoryVariantsType } from "../types/Types";
|
||||
import { type CategoryType, type SingleUploadResponseType, type CategoriesResponseType, type CategoryByIdResponseType, type CategoryVariantsResponseType, type CategoryVariantsType, type CreateCategoryAttributeType, type CategoryAttributesResponseType } from "../types/Types";
|
||||
|
||||
export const createCategory = async (params: CategoryType) => {
|
||||
const { data } = await axios.post(`/admin/category`, params);
|
||||
@@ -56,4 +56,14 @@ export const deleteCategoryVariant = async (params: { id: string; variantType: '
|
||||
}
|
||||
});
|
||||
return data;
|
||||
};
|
||||
|
||||
export const createCategoryAttribute = async (id: string, params: CreateCategoryAttributeType) => {
|
||||
const { data } = await axios.post(`/admin/category/${id}/attributes`, params);
|
||||
return data;
|
||||
};
|
||||
|
||||
export const getCategoryAttributes = async (id: string): Promise<CategoryAttributesResponseType> => {
|
||||
const { data } = await axios.get(`/category/${id}/attributes`);
|
||||
return data;
|
||||
};
|
||||
@@ -136,3 +136,40 @@ export type CategoryVariantsResponseType = {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
export type CreateCategoryAttributeType = {
|
||||
title: string;
|
||||
type: string;
|
||||
multiple?: boolean;
|
||||
hint?: string;
|
||||
required: boolean;
|
||||
values: {
|
||||
text: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type CategoryAttributeType = {
|
||||
_id: number;
|
||||
title: string;
|
||||
type: string;
|
||||
multiple: boolean;
|
||||
hint: string;
|
||||
required: boolean;
|
||||
values: {
|
||||
_id: number;
|
||||
text: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type CategoryAttributesResponseType = {
|
||||
status: number;
|
||||
success: boolean;
|
||||
results: {
|
||||
data: {
|
||||
category: {
|
||||
category_title_fa: string[];
|
||||
attributes: CategoryAttributeType[];
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user