Merge branch 'Feature_Directory' of https://git.marcoaiot.com/admin/marco.pms.web into pramod_Task-#355
This commit is contained in:
commit
c4d647f361
126
src/components/master/EditContactCategory.jsx
Normal file
126
src/components/master/EditContactCategory.jsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import React, { useEffect,useState } from 'react'
|
||||||
|
import { useForm } from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { MasterRespository } from '../../repositories/MastersRepository';
|
||||||
|
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||||
|
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||||
|
import showToast from '../../services/toastService';
|
||||||
|
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
name: z.string().min(1, { message: "Category name is required" }),
|
||||||
|
description: z.string().min(1, { message: "Description is required" })
|
||||||
|
.max(255, { message: "Description cannot exceed 255 characters" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const EditContactCategory= ({data,onClose}) => {
|
||||||
|
|
||||||
|
const[isLoading,setIsLoading] = useState(false)
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },reset
|
||||||
|
|
||||||
|
} = useForm({
|
||||||
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
name: data?.name || "",
|
||||||
|
description:data?.description || "",
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = (formdata) => {
|
||||||
|
setIsLoading(true)
|
||||||
|
const result = {
|
||||||
|
id:data?.id,
|
||||||
|
name: formdata?.name,
|
||||||
|
description: formdata.description,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MasterRespository.updateContactCategory(data?.id,result).then((resp)=>{
|
||||||
|
setIsLoading(false)
|
||||||
|
showToast("Contact Category Updated successfully.", "success");
|
||||||
|
const cachedData = getCachedData("Contact Category");
|
||||||
|
if (cachedData) {
|
||||||
|
|
||||||
|
const updatedData = cachedData.map((category) =>
|
||||||
|
category.id === data?.id ? { ...category, ...resp.data } : category
|
||||||
|
);
|
||||||
|
cacheData("Contact Category", updatedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose()
|
||||||
|
}).catch((error)=>{
|
||||||
|
showToast(error?.response?.data?.message, "error")
|
||||||
|
setIsLoading(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
};
|
||||||
|
const resetForm = () => {
|
||||||
|
reset({
|
||||||
|
name: "",
|
||||||
|
description: ""
|
||||||
|
});
|
||||||
|
setDescriptionLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
return ()=>resetForm()
|
||||||
|
},[])
|
||||||
|
|
||||||
|
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||||
|
const maxDescriptionLength = 255;
|
||||||
|
return (<>
|
||||||
|
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="col-12 col-md-12">
|
||||||
|
<label className="form-label">Category Name</label>
|
||||||
|
<input type="text"
|
||||||
|
{...register("name")}
|
||||||
|
className={`form-control ${errors.name ? 'is-invalids' : ''}`}
|
||||||
|
/>
|
||||||
|
{errors.name && <p className="text-danger">{errors.name.message}</p>}
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-md-12">
|
||||||
|
<label className="form-label" htmlFor="description">Description</label>
|
||||||
|
<textarea
|
||||||
|
rows="3"
|
||||||
|
{...register("description")}
|
||||||
|
className={`form-control ${errors.description ? 'is-invalids' : ''}`}
|
||||||
|
onChange={(e) => {
|
||||||
|
setDescriptionLength(e.target.value.length);
|
||||||
|
register("description").onChange(e);
|
||||||
|
}}
|
||||||
|
></textarea>
|
||||||
|
<div className="text-end small text-muted">
|
||||||
|
{maxDescriptionLength - descriptionLength} characters left
|
||||||
|
</div>
|
||||||
|
{errors.description && (
|
||||||
|
<p className="text-danger">{errors.description.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12 text-center">
|
||||||
|
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||||
|
{isLoading? "Please Wait...":"Submit"}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="reset"
|
||||||
|
className="btn btn-sm btn-label-secondary "
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditContactCategory;
|
||||||
126
src/components/master/EditContactTag.jsx
Normal file
126
src/components/master/EditContactTag.jsx
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
import React,{useState,useEffect} from 'react'
|
||||||
|
import {useForm} from 'react-hook-form';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
|
import { MasterRespository } from '../../repositories/MastersRepository';
|
||||||
|
import { clearApiCacheKey } from '../../slices/apiCacheSlice';
|
||||||
|
import { getCachedData,cacheData } from '../../slices/apiDataManager';
|
||||||
|
import showToast from '../../services/toastService';
|
||||||
|
|
||||||
|
|
||||||
|
const schema = z.object({
|
||||||
|
name: z.string().min(1, { message: "Tag name is required" }),
|
||||||
|
description: z.string().min(1, { message: "Description is required" })
|
||||||
|
.max(255, { message: "Description cannot exceed 255 characters" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
const EditContactTag= ({data,onClose}) => {
|
||||||
|
|
||||||
|
const[isLoading,setIsLoading] = useState(false)
|
||||||
|
const {
|
||||||
|
register,
|
||||||
|
handleSubmit,
|
||||||
|
formState: { errors },reset
|
||||||
|
|
||||||
|
} = useForm({
|
||||||
|
resolver: zodResolver(schema),
|
||||||
|
defaultValues: {
|
||||||
|
name: data?.name || "",
|
||||||
|
description:data?.description || "",
|
||||||
|
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const onSubmit = (formdata) => {
|
||||||
|
setIsLoading(true)
|
||||||
|
const result = {
|
||||||
|
id:data?.id,
|
||||||
|
name: formdata?.name,
|
||||||
|
description: formdata.description,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
MasterRespository.updateContactTag(data?.id,result).then((resp)=>{
|
||||||
|
setIsLoading(false)
|
||||||
|
showToast("Contact Tag Updated successfully.", "success");
|
||||||
|
const cachedData = getCachedData("Contact Tag");
|
||||||
|
if (cachedData) {
|
||||||
|
|
||||||
|
const updatedData = cachedData.map((category) =>
|
||||||
|
category.id === data?.id ? { ...category, ...resp.data } : category
|
||||||
|
);
|
||||||
|
cacheData("Contact Tag", updatedData);
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose()
|
||||||
|
}).catch((error)=>{
|
||||||
|
showToast(error?.response?.data?.message, "error")
|
||||||
|
setIsLoading(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
};
|
||||||
|
const resetForm = () => {
|
||||||
|
reset({
|
||||||
|
name: "",
|
||||||
|
description: ""
|
||||||
|
});
|
||||||
|
setDescriptionLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(()=>{
|
||||||
|
return ()=>resetForm()
|
||||||
|
},[])
|
||||||
|
|
||||||
|
const [descriptionLength, setDescriptionLength] = useState(0);
|
||||||
|
const maxDescriptionLength = 255;
|
||||||
|
return (<>
|
||||||
|
<form className="row g-2" onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<div className="col-12 col-md-12">
|
||||||
|
<label className="form-label">Tag Name</label>
|
||||||
|
<input type="text"
|
||||||
|
{...register("name")}
|
||||||
|
className={`form-control ${errors.name ? 'is-invalids' : ''}`}
|
||||||
|
/>
|
||||||
|
{errors.name && <p className="text-danger">{errors.name.message}</p>}
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-md-12">
|
||||||
|
<label className="form-label" htmlFor="description">Description</label>
|
||||||
|
<textarea
|
||||||
|
rows="3"
|
||||||
|
{...register("description")}
|
||||||
|
className={`form-control ${errors.description ? 'is-invalids' : ''}`}
|
||||||
|
onChange={(e) => {
|
||||||
|
setDescriptionLength(e.target.value.length);
|
||||||
|
register("description").onChange(e);
|
||||||
|
}}
|
||||||
|
></textarea>
|
||||||
|
<div className="text-end small text-muted">
|
||||||
|
{maxDescriptionLength - descriptionLength} characters left
|
||||||
|
</div>
|
||||||
|
{errors.description && (
|
||||||
|
<p className="text-danger">{errors.description.message}</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-12 text-center">
|
||||||
|
<button type="submit" className="btn btn-sm btn-primary me-3">
|
||||||
|
{isLoading? "Please Wait...":"Submit"}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="reset"
|
||||||
|
className="btn btn-sm btn-label-secondary "
|
||||||
|
data-bs-dismiss="modal"
|
||||||
|
aria-label="Close"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EditContactTag;
|
||||||
@ -15,6 +15,7 @@ import CreateWorkCategory from "./CreateWorkCategory";
|
|||||||
import EditWorkCategory from "./EditWorkCategory";
|
import EditWorkCategory from "./EditWorkCategory";
|
||||||
import CreateCategory from "./CreateContactCategory";
|
import CreateCategory from "./CreateContactCategory";
|
||||||
import CreateContactTag from "./CreateContactTag";
|
import CreateContactTag from "./CreateContactTag";
|
||||||
|
import EditContactCategory from "./EditContactCategory";
|
||||||
|
|
||||||
|
|
||||||
const MasterModal = ({ modaldata, closeModal }) => {
|
const MasterModal = ({ modaldata, closeModal }) => {
|
||||||
@ -131,6 +132,9 @@ const MasterModal = ({ modaldata, closeModal }) => {
|
|||||||
)}
|
)}
|
||||||
{modaldata.modalType === "Contact Category" && (
|
{modaldata.modalType === "Contact Category" && (
|
||||||
<CreateCategory data={modaldata.item} onClose={closeModal} />
|
<CreateCategory data={modaldata.item} onClose={closeModal} />
|
||||||
|
)}
|
||||||
|
{modaldata.modalType === "Edit-Contact Category" && (
|
||||||
|
<EditContactCategory data={modaldata.item} onClose={closeModal} />
|
||||||
)}
|
)}
|
||||||
{modaldata.modalType === "Contact Tag" && (
|
{modaldata.modalType === "Contact Tag" && (
|
||||||
<CreateContactTag data={modaldata.item} onClose={closeModal} />
|
<CreateContactTag data={modaldata.item} onClose={closeModal} />
|
||||||
|
|||||||
@ -233,3 +233,206 @@ const DirectoryPageHeader = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default DirectoryPageHeader;
|
export default DirectoryPageHeader;
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const DirectoryPageHeader = ({
|
||||||
|
searchText,
|
||||||
|
setSearchText,
|
||||||
|
setIsActive,
|
||||||
|
listView,
|
||||||
|
setListView,
|
||||||
|
filteredBuckets,
|
||||||
|
tempSelectedBucketIds,
|
||||||
|
handleTempBucketChange,
|
||||||
|
filteredCategories,
|
||||||
|
tempSelectedCategoryIds,
|
||||||
|
handleTempCategoryChange,
|
||||||
|
clearFilter,
|
||||||
|
applyFilter,
|
||||||
|
loading,
|
||||||
|
IsActive,
|
||||||
|
setIsOpenModal,
|
||||||
|
setOpenBucketModal
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="row">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="row mx-0 px-0 align-items-center">
|
||||||
|
<div className="col-12 col-md-4 mb-2 px-1 d-flex align-items-center ">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
className="form-control form-control-sm me-2"
|
||||||
|
placeholder="Search Contact..."
|
||||||
|
value={searchText}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
/>
|
||||||
|
<div className="d-flex gap-2 ">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn btn-xs ${
|
||||||
|
!listView ? "btn-primary" : "btn-outline-primary"
|
||||||
|
}`}
|
||||||
|
onClick={() => setListView(false)}
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="Card View"
|
||||||
|
>
|
||||||
|
<i className="bx bx-grid-alt bx-sm"></i>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`btn btn-xs ${
|
||||||
|
listView ? "btn-primary" : "btn-outline-primary"
|
||||||
|
}`}
|
||||||
|
onClick={() => setListView(true)}
|
||||||
|
data-bs-toggle="tooltip"
|
||||||
|
data-bs-offset="0,8"
|
||||||
|
data-bs-placement="top"
|
||||||
|
data-bs-custom-class="tooltip"
|
||||||
|
title="List View"
|
||||||
|
>
|
||||||
|
<i className="bx bx-list-ul bx-sm"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="dropdown" style={{ width: "fit-content" }}>
|
||||||
|
<div className="dropdown" style={{ width: "fit-content" }}>
|
||||||
|
<a
|
||||||
|
className="dropdown-toggle hide-arrow cursor-pointer d-flex align-items-center"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<i className="fa-solid fa-filter ms-1 fs-5"></i>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<ul className="dropdown-menu p-3" style={{ width: "320px" }}>
|
||||||
|
<div>
|
||||||
|
<p className="small-text fw-semibold text-muted m-0">
|
||||||
|
Filter by
|
||||||
|
</p>
|
||||||
|
|
||||||
|
{/* Bucket Filter */}
|
||||||
|
<div className="mt-1">
|
||||||
|
<p className="small-text mb-1 fw-semibold">Buckets</p>
|
||||||
|
<div className="d-flex flex-wrap">
|
||||||
|
{filteredBuckets.map(({ id, name }) => (
|
||||||
|
<div
|
||||||
|
className="form-check me-1 mb-1"
|
||||||
|
style={{ minWidth: "33.33%" }}
|
||||||
|
key={id}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id={`bucket-${id}`}
|
||||||
|
checked={tempSelectedBucketIds.includes(id)}
|
||||||
|
onChange={() => handleTempBucketChange(id)}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="form-check-label text-nowrap small-text "
|
||||||
|
htmlFor={`bucket-${id}`}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<hr className="m-0" />
|
||||||
|
{/* Category Filter */}
|
||||||
|
<div className="mt-1">
|
||||||
|
<p className="small-text mb-1 fw-semibold">Categories</p>
|
||||||
|
<div className="d-flex flex-wrap">
|
||||||
|
{filteredCategories.map(({ id, name }) => (
|
||||||
|
<div
|
||||||
|
className="form-check me-1 mb-1"
|
||||||
|
style={{ minWidth: "33.33%" }}
|
||||||
|
key={id}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id={`cat-${id}`}
|
||||||
|
checked={tempSelectedCategoryIds.includes(id)}
|
||||||
|
onChange={() => handleTempCategoryChange(id)}
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
className="form-check-label text-nowrap small-text"
|
||||||
|
htmlFor={`cat-${id}`}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="d-flex justify-content-end gap-2 mt-1">
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-secondary"
|
||||||
|
onClick={clearFilter}
|
||||||
|
>
|
||||||
|
Clear
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-xs btn-primary"
|
||||||
|
onClick={applyFilter}
|
||||||
|
>
|
||||||
|
Apply Filter
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-md-8 mb-2 px-1 text-md-end text-end">
|
||||||
|
<label className="switch switch-primary">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="switch-input"
|
||||||
|
onChange={() => setIsActive(!IsActive)}
|
||||||
|
value={IsActive}
|
||||||
|
disabled={loading}
|
||||||
|
/>
|
||||||
|
<span className="switch-toggle-slider">
|
||||||
|
<span className="switch-on">
|
||||||
|
{/* <i class="icon-base bx bx-check"></i> */}
|
||||||
|
</span>
|
||||||
|
<span className="switch-off">
|
||||||
|
{/* <i class="icon-base bx bx-x"></i> */}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<span className="switch-label small-text">
|
||||||
|
Show Inactive Contacts
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-xs btn-primary"
|
||||||
|
onClick={() => setIsOpenModal(true)}
|
||||||
|
>
|
||||||
|
<i className="bx bx-plus-circle me-2"></i>
|
||||||
|
New Contact
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-12 d-flex justify-content-end">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="btn btn-xs btn-primary"
|
||||||
|
onClick={()=>setOpenBucketModal(true)}
|
||||||
|
>
|
||||||
|
<i className="bx bx-plus-circle me-2"></i>
|
||||||
|
Manage Buckets
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DirectoryPageHeader;
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export const MasterRespository = {
|
|||||||
"Activity": ( id ) => api.delete( `/api/master/activity/delete/${ id }` ),
|
"Activity": ( id ) => api.delete( `/api/master/activity/delete/${ id }` ),
|
||||||
"Application Role":(id)=>api.delete(`/api/roles/${id}`),
|
"Application Role":(id)=>api.delete(`/api/roles/${id}`),
|
||||||
"Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ),
|
"Work Category": ( id ) => api.delete( `api/master/work-category/${ id }` ),
|
||||||
"Contact Category": ( id ) => api.delete( `/api/master/contact-category` ),
|
"Contact Category": ( id ) => api.delete( `/api/master/contact-category/${id}` ),
|
||||||
"Conatct Tag" :(id)=>api.delete("/api/master/contact-tag"),
|
"Conatct Tag" :(id)=>api.delete("/api/master/contact-tag"),
|
||||||
|
|
||||||
getWorkCategory:() => api.get(`/api/master/work-categories`),
|
getWorkCategory:() => api.get(`/api/master/work-categories`),
|
||||||
@ -50,7 +50,7 @@ export const MasterRespository = {
|
|||||||
|
|
||||||
getContactCategory: () => api.get( `/api/master/contact-categories` ),
|
getContactCategory: () => api.get( `/api/master/contact-categories` ),
|
||||||
createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ),
|
createContactCategory: (data ) => api.post( `/api/master/contact-category`, data ),
|
||||||
updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/${ id }`, data ),
|
updateContactCategory: ( id, data ) => api.post( `/api/master/contact-category/edit/${ id }`, data ),
|
||||||
|
|
||||||
getContactTag: () => api.get( `/api/master/contact-tags` ),
|
getContactTag: () => api.get( `/api/master/contact-tags` ),
|
||||||
createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ),
|
createContactTag: (data ) => api.post( `/api/master/contact-tag`, data ),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user