delete and edit category attribute

This commit is contained in:
hamid zarghami
2025-10-25 15:38:12 +03:30
parent cde35da32a
commit 1683cb9ffe
5 changed files with 110 additions and 34 deletions
+15 -17
View File
@@ -6,7 +6,8 @@ import { AttributesModal } from './components'
import { useAttributesLogic } from './hooks/useAttributesLogic' import { useAttributesLogic } from './hooks/useAttributesLogic'
import { useGetCategoryAttributes } from './hooks/useCategoryData' import { useGetCategoryAttributes } from './hooks/useCategoryData'
import { type CategoryAttributeType } from './types/Types' import { type CategoryAttributeType } from './types/Types'
import { Add, Edit, Trash } from 'iconsax-react' import { Add, Edit } from 'iconsax-react'
import TrashWithConfrim from '../../components/TrashWithConfrim'
const Attributes: FC = () => { const Attributes: FC = () => {
const { id } = useParams() const { id } = useParams()
@@ -16,13 +17,17 @@ const Attributes: FC = () => {
isLoading, isLoading,
isModalOpen, isModalOpen,
newAttribute, newAttribute,
editingAttributeId,
openModal, openModal,
closeModal, closeModal,
openEditModal,
setNewAttribute, setNewAttribute,
addAttribute, addAttribute,
deleteAttribute,
addValueToAttribute, addValueToAttribute,
removeValueFromAttribute, removeValueFromAttribute,
isAddingLoading isAddingLoading,
isDeletingLoading
} = useAttributesLogic({ id }) } = useAttributesLogic({ id })
const attributes = attributesData?.results?.data?.category?.attributes || [] const attributes = attributesData?.results?.data?.category?.attributes || []
@@ -83,7 +88,7 @@ const Attributes: FC = () => {
} }
return ( return (
<div className="min-h-screen bg-gray-50"> <div className="min-h-screen">
<PageTitle /> <PageTitle />
<div className="max-w-6xl mx-auto p-6"> <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"> <div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 mb-6">
@@ -157,20 +162,12 @@ const Attributes: FC = () => {
)} )}
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Button
variant="ghost" <Edit onClick={() => openEditModal(attribute)} color='#8C90A3' size={16} />
size="sm" <TrashWithConfrim
className="text-blue-500 hover:text-blue-700" onDelete={() => deleteAttribute(attribute._id)}
> isLoading={isDeletingLoading}
<Edit size={16} /> />
</Button>
<Button
variant="ghost"
size="sm"
className="text-red-500 hover:text-red-700"
>
<Trash size={16} />
</Button>
</div> </div>
</div> </div>
{attribute.hint && ( {attribute.hint && (
@@ -204,6 +201,7 @@ const Attributes: FC = () => {
onAddValue={addValueToAttribute} onAddValue={addValueToAttribute}
onRemoveValue={removeValueFromAttribute} onRemoveValue={removeValueFromAttribute}
isLoading={isAddingLoading} isLoading={isAddingLoading}
isEditing={!!editingAttributeId}
/> />
</div> </div>
) )
@@ -17,6 +17,7 @@ interface AttributesModalProps {
onAddValue: (text: string) => void onAddValue: (text: string) => void
onRemoveValue: (index: number) => void onRemoveValue: (index: number) => void
isLoading?: boolean isLoading?: boolean
isEditing?: boolean
} }
const AttributesModal: FC<AttributesModalProps> = ({ const AttributesModal: FC<AttributesModalProps> = ({
@@ -27,7 +28,8 @@ const AttributesModal: FC<AttributesModalProps> = ({
onAdd, onAdd,
onAddValue, onAddValue,
onRemoveValue, onRemoveValue,
isLoading = false isLoading = false,
isEditing = false
}) => { }) => {
const [newValue, setNewValue] = useState('') const [newValue, setNewValue] = useState('')
@@ -55,7 +57,7 @@ const AttributesModal: FC<AttributesModalProps> = ({
open={isOpen} open={isOpen}
close={onClose} close={onClose}
isHeader={true} isHeader={true}
title_header="اضافه کردن ویژگی جدید" title_header={isEditing ? "ویرایش ویژگی" : "اضافه کردن ویژگی جدید"}
width={600} width={600}
> >
<div className="space-y-6"> <div className="space-y-6">
@@ -150,7 +152,7 @@ const AttributesModal: FC<AttributesModalProps> = ({
onClick={onAdd} onClick={onAdd}
disabled={!isFormValid() || isLoading} disabled={!isFormValid() || isLoading}
> >
{isLoading ? 'در حال اضافه کردن...' : 'اضافه کردن'} {isLoading ? (isEditing ? 'در حال ویرایش...' : 'در حال اضافه کردن...') : (isEditing ? 'ویرایش' : 'اضافه کردن')}
</Button> </Button>
</div> </div>
</div> </div>
+67 -14
View File
@@ -1,6 +1,6 @@
import { useState } from 'react' import { useState } from 'react'
import { useQueryClient } from '@tanstack/react-query' import { useQueryClient } from '@tanstack/react-query'
import { useGetCategoryById, useCreateCategoryAttribute } from './useCategoryData' import { useGetCategoryById, useCreateCategoryAttribute, useUpdateCategoryAttribute, useDeleteCategoryAttribute } from './useCategoryData'
import { type CreateCategoryAttributeType } from '../types/Types' import { type CreateCategoryAttributeType } from '../types/Types'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { extractErrorMessage } from '../../../helpers/utils' import { extractErrorMessage } from '../../../helpers/utils'
@@ -13,8 +13,11 @@ export const useAttributesLogic = ({ id }: UseAttributesLogicProps) => {
const queryClient = useQueryClient() const queryClient = useQueryClient()
const { data, isLoading } = useGetCategoryById(id!) const { data, isLoading } = useGetCategoryById(id!)
const createAttributeMutation = useCreateCategoryAttribute() const createAttributeMutation = useCreateCategoryAttribute()
const updateAttributeMutation = useUpdateCategoryAttribute()
const deleteAttributeMutation = useDeleteCategoryAttribute()
const [isModalOpen, setIsModalOpen] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false)
const [editingAttributeId, setEditingAttributeId] = useState<number | null>(null)
const [newAttribute, setNewAttribute] = useState<CreateCategoryAttributeType>({ const [newAttribute, setNewAttribute] = useState<CreateCategoryAttributeType>({
title: '', title: '',
type: 'text', type: 'text',
@@ -27,6 +30,7 @@ export const useAttributesLogic = ({ id }: UseAttributesLogicProps) => {
const resetModal = () => { const resetModal = () => {
setIsModalOpen(false) setIsModalOpen(false)
setEditingAttributeId(null)
setNewAttribute({ setNewAttribute({
title: '', title: '',
type: 'text', type: 'text',
@@ -38,23 +42,68 @@ export const useAttributesLogic = ({ id }: UseAttributesLogicProps) => {
const openModal = () => setIsModalOpen(true) const openModal = () => setIsModalOpen(true)
const closeModal = () => resetModal() const closeModal = () => resetModal()
const openEditModal = (attribute: any) => {
setEditingAttributeId(attribute._id)
setNewAttribute({
title: attribute.title,
type: attribute.type,
multiple: attribute.multiple,
hint: attribute.hint || '',
required: attribute.required,
values: attribute.values.map((v: any) => ({ text: v.text }))
})
setIsModalOpen(true)
}
const addAttribute = async () => { const addAttribute = async () => {
if (!id || !newAttribute.title.trim()) return if (!id || !newAttribute.title.trim()) return
createAttributeMutation.mutate( if (editingAttributeId) {
{ id, data: newAttribute }, // ویرایش ویژگی
{ updateAttributeMutation.mutate(
onSuccess: () => { { attrId: editingAttributeId, ...newAttribute },
toast.success('ویژگی با موفقیت اضافه شد') {
resetModal() onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['category', id] }) toast.success('ویژگی با موفقیت ویرایش شد')
queryClient.invalidateQueries({ queryKey: ['category', 'attributes', id] }) resetModal()
}, queryClient.invalidateQueries({ queryKey: ['category', id] })
onError: (error) => { queryClient.invalidateQueries({ queryKey: ['category', 'attributes', id] })
toast.error(extractErrorMessage(error)) },
onError: (error) => {
toast.error(extractErrorMessage(error))
}
} }
)
} else {
// ایجاد ویژگی جدید
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 deleteAttribute = async (attributeId: number) => {
deleteAttributeMutation.mutate(attributeId, {
onSuccess: () => {
toast.success('ویژگی با موفقیت حذف شد')
queryClient.invalidateQueries({ queryKey: ['category', id] })
queryClient.invalidateQueries({ queryKey: ['category', 'attributes', id] })
},
onError: (error) => {
toast.error(extractErrorMessage(error))
} }
) })
} }
const updateNewAttribute = (field: keyof CreateCategoryAttributeType, value: any) => { const updateNewAttribute = (field: keyof CreateCategoryAttributeType, value: any) => {
@@ -87,16 +136,20 @@ export const useAttributesLogic = ({ id }: UseAttributesLogicProps) => {
// Modal state // Modal state
isModalOpen, isModalOpen,
newAttribute, newAttribute,
editingAttributeId,
// Actions // Actions
openModal, openModal,
closeModal, closeModal,
openEditModal,
setNewAttribute: updateNewAttribute, setNewAttribute: updateNewAttribute,
addAttribute, addAttribute,
deleteAttribute,
addValueToAttribute, addValueToAttribute,
removeValueFromAttribute, removeValueFromAttribute,
// Loading states // Loading states
isAddingLoading: createAttributeMutation.isPending isAddingLoading: createAttributeMutation.isPending || updateAttributeMutation.isPending,
isDeletingLoading: deleteAttributeMutation.isPending
} }
} }
@@ -83,4 +83,17 @@ export const useGetCategoryAttributes = (id: string) => {
queryFn: () => api.getCategoryAttributes(id), queryFn: () => api.getCategoryAttributes(id),
enabled: !!id, enabled: !!id,
}); });
};
export const useUpdateCategoryAttribute = () => {
return useMutation({
mutationFn: (params: CreateCategoryAttributeType & { attrId: number }) =>
api.updateCategoryAttribute(params),
});
};
export const useDeleteCategoryAttribute = () => {
return useMutation({
mutationFn: (id: number) => api.deleteCategoryAttribute(id),
});
}; };
@@ -66,4 +66,14 @@ export const createCategoryAttribute = async (id: string, params: CreateCategory
export const getCategoryAttributes = async (id: string): Promise<CategoryAttributesResponseType> => { export const getCategoryAttributes = async (id: string): Promise<CategoryAttributesResponseType> => {
const { data } = await axios.get(`/category/${id}/attributes`); const { data } = await axios.get(`/category/${id}/attributes`);
return data; return data;
};
export const updateCategoryAttribute = async (params: CreateCategoryAttributeType & { attrId: number }) => {
const { data } = await axios.patch(`/admin/category/attributes`, params);
return data;
};
export const deleteCategoryAttribute = async (id: number) => {
const { data } = await axios.delete(`/admin/category/attributes/${id}/delete`);
return data;
}; };