468 lines
17 KiB
TypeScript
468 lines
17 KiB
TypeScript
'use client'
|
|
import Link from 'next/link'
|
|
import { Input } from "@/components/ui/input"
|
|
import { Textarea } from "@/components/ui/textarea"
|
|
import { Label } from "@/components/ui/label"
|
|
import { useEffect, useState } from 'react'
|
|
import { Button } from '@/components/ui/button'
|
|
import LSearchCustomers from '../components/l-search-customers'
|
|
import axiosInstance from '@/axios/axiosInstance'
|
|
import { useQuery, useQueryClient } from '@tanstack/react-query'
|
|
import * as z from "zod"
|
|
import { useRouter } from 'next/navigation'
|
|
import { IoMdArrowRoundBack } from "react-icons/io";
|
|
import { AlertCircle } from "lucide-react"
|
|
import {
|
|
Alert,
|
|
AlertDescription,
|
|
AlertTitle,
|
|
} from "@/components/ui/alert"
|
|
import { ILead } from './interfaces'
|
|
import { toast } from "sonner"
|
|
import { FaFaceGrinStars, FaFaceMeh, FaFaceFrown, FaFaceSmile } from "react-icons/fa6";
|
|
import { TbTargetArrow } from "react-icons/tb";
|
|
import { HiBellAlert } from "react-icons/hi2";
|
|
import { useForm, Controller, SubmitHandler } from "react-hook-form"
|
|
import LeadComment from '../components/lead-comment'
|
|
import LeadCommentAdd from '../components/lead-comment-add'
|
|
import RemainderModal from '../../components/ui/reminder-modal'
|
|
|
|
|
|
|
|
|
|
interface IFormValues {
|
|
lead_types: [
|
|
{
|
|
id: Number,
|
|
title: String
|
|
},
|
|
],
|
|
product_types: [
|
|
{
|
|
id: Number,
|
|
name: String
|
|
},
|
|
],
|
|
lead_source: [
|
|
{
|
|
id: Number,
|
|
title: String
|
|
},
|
|
],
|
|
lead_steps: [
|
|
{
|
|
id: Number,
|
|
name: String
|
|
},
|
|
],
|
|
shahr: [
|
|
{
|
|
id: Number,
|
|
name: String
|
|
is_ostan: boolean,
|
|
"ostan": any
|
|
},
|
|
]
|
|
}
|
|
|
|
interface IShahr {
|
|
id: Number,
|
|
name: String
|
|
is_ostan: boolean,
|
|
"ostan": any
|
|
}
|
|
|
|
interface IFormNewLead {
|
|
title: string,
|
|
customer_id: number,
|
|
desc: string,
|
|
product_type_id: number ,
|
|
lead_type_id: number,
|
|
lead_source_id: number,
|
|
ostan: number,
|
|
city_id: number,
|
|
lead_step_id: number,
|
|
}
|
|
|
|
const formSchema = z.object({
|
|
title: z.string().min(5, {
|
|
message: "Username must be at least 2 characters.",
|
|
}),
|
|
})
|
|
|
|
const LeadIdPage = ({params}:{params: {id: number} })=>{
|
|
|
|
const [remainderModalHidden, setRemainderModalHidden] = useState<boolean>(true)
|
|
const [cSearchOpen, setCSearchOpen] = useState<boolean>(false)
|
|
const [statusButtonState, setStatusButtonState] = useState<{su:boolean, fa:boolean, un: boolean}>({su:false, fa:false, un: false})
|
|
const [customerName, setCustomerName] = useState<any>()
|
|
const [formValues, setFormValues] = useState<IFormValues | null>(null)
|
|
const [shahr, setShahr] = useState<IShahr[] | []>([])
|
|
const [leadForm, setLeadForm] = useState<IFormNewLead | null>(null)
|
|
const [submitButtonEnable, setSubmitButtonEnable] = useState<boolean>(true)
|
|
const [lead, setLead] = useState<ILead | null>(null)
|
|
const router = useRouter()
|
|
|
|
useEffect(()=>{
|
|
|
|
setValue('title', lead?.title)
|
|
setValue('customer_id', lead?.customer.id)
|
|
setCustomerName(lead?.customer.name_family)
|
|
setValue('desc', lead?.desc)
|
|
setValue('product_type_id', lead?.product_type.id)
|
|
setValue('lead_type_id', lead?.type.id)
|
|
setValue('lead_source_id', lead?.source.id)
|
|
setValue('ostan', lead?.city.ostan.id)
|
|
setValue('lead_step_id', lead?.step.id)
|
|
|
|
changeOstanById(lead?.city.ostan.id, lead?.city.id)
|
|
|
|
|
|
}, [lead, ])
|
|
|
|
|
|
const {
|
|
register,
|
|
control,
|
|
handleSubmit,
|
|
watch,
|
|
setValue,
|
|
formState: { errors },
|
|
} = useForm<IFormNewLead>({
|
|
shouldFocusError: false,
|
|
})
|
|
|
|
const onSubmit: SubmitHandler<IFormNewLead> = async (data) => {
|
|
|
|
await setSubmitButtonEnable(false)
|
|
|
|
try {
|
|
const result = await axiosInstance.patch(`lead/${lead?.id}/edit/`, data)
|
|
|
|
if(result.status == 201){
|
|
setSubmitButtonEnable(true)
|
|
queryClient.invalidateQueries({ queryKey: ['form-items-and-lead'] })
|
|
|
|
toast("ویرایش شد", {
|
|
description: "سرنخ با موفقیت ویرایش شد",
|
|
action: {
|
|
label: "بستن",
|
|
onClick: () => console.log("Undo"),
|
|
},
|
|
})
|
|
|
|
} else {
|
|
alert("خطا در ثبت")
|
|
}
|
|
|
|
} catch (error) {
|
|
console.log(error)
|
|
}
|
|
|
|
}
|
|
|
|
const onChangeStatus = async (status:string)=>{
|
|
const editStatusRs = await axiosInstance.patch(`lead/${lead?.id}/edit-status/`, {
|
|
status: status
|
|
})
|
|
|
|
if(editStatusRs.status=201){
|
|
queryClient.invalidateQueries({ queryKey: ['form-items-and-lead'] });
|
|
setTimeout(()=>{
|
|
alert("تغییر وضعیت سرنخ انجام شد");
|
|
}, 400)
|
|
}
|
|
|
|
}
|
|
|
|
const changeOstanById = async (ostan_id:any, city_id:any)=>{
|
|
|
|
const shahrha = await formValues?.shahr
|
|
|
|
if(shahrha){
|
|
|
|
const shahrF = await shahrha.filter((item, key)=>{
|
|
return item.ostan == ostan_id
|
|
})
|
|
|
|
setShahr(shahrF)
|
|
|
|
setTimeout(()=>{ setValue('city_id', city_id) }, 500)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
const changeOstan = async (e:any)=>{
|
|
const ostan_id = await e.target.value
|
|
const shahrha = await formValues?.shahr
|
|
const shahr = await shahrha.filter((item, key)=>{
|
|
return item.ostan == ostan_id
|
|
})
|
|
setShahr(shahr)
|
|
|
|
setLeadForm((prevLeadForm)=>({
|
|
...prevLeadForm!,
|
|
ostan: ostan_id
|
|
}));
|
|
|
|
console.log(leadForm)
|
|
}
|
|
|
|
const queryClient = useQueryClient()
|
|
|
|
const result = useQuery({ queryKey:['form-items-and-lead'], queryFn: async ()=>{
|
|
|
|
const leadData = await axiosInstance.get(`lead/${params.id}/`)
|
|
const formData = await axiosInstance.get('lead/form-items/')
|
|
|
|
return {
|
|
leadData: leadData,
|
|
formData: formData
|
|
}
|
|
|
|
},onSuccess: async (result)=>{
|
|
await setFormValues(result.formData.data)
|
|
await setLead(result.leadData.data)
|
|
},})
|
|
|
|
if (result.isLoading){
|
|
return (<p className='text-center mt-5'>
|
|
درحال دریافت اطلاعات...
|
|
</p>)
|
|
}
|
|
|
|
|
|
return (
|
|
<div className="w-full md:w-[60%] lg:w-[70%] m-auto " dir="rtl">
|
|
|
|
<div className="bg-white rounded-md p-4 flex flex-row mt-7 mb-3 justify-between items-center">
|
|
|
|
<div className="text-md flex flex-row gap-x-2">
|
|
<span>
|
|
وضعیت سرنخ:
|
|
</span>
|
|
|
|
{lead?.status == 'SU' ? <FaFaceGrinStars size={26} color={'green'} /> : null}
|
|
{lead?.status == 'FA' ? <FaFaceFrown size={26} color={'red'} /> : null}
|
|
{lead?.status == 'UN' || null ? <FaFaceSmile size={26} color={'#757575'} /> : null}
|
|
|
|
</div>
|
|
|
|
<div className="flex flex-row gap-x-2">
|
|
<div className='flex flex-row gap-x-4 border p-2 rounded-md'>
|
|
|
|
<div onClick={()=>{onChangeStatus('SU')}} className='' onMouseLeave={()=>{ setStatusButtonState( (prev) => ({...prev, su:false}) ) }} onMouseOver={()=>{ setStatusButtonState( (prev) => ({...prev, su:true}) ) }}>
|
|
<FaFaceGrinStars size={26} color={statusButtonState.su ? 'green': '#757575'} />
|
|
</div>
|
|
|
|
<div onClick={()=>{onChangeStatus("FA")}} className='' onMouseLeave={()=>{ setStatusButtonState( (prev) => ({...prev, fa:false}) ) }} onMouseOver={()=>{ setStatusButtonState( (prev) => ({...prev, fa:true}) ) }}>
|
|
<FaFaceFrown size={26} color={statusButtonState.fa ? 'red': '#757575'} />
|
|
</div>
|
|
|
|
<div onClick={()=>{onChangeStatus("UN")}} className='' onMouseLeave={()=>{ setStatusButtonState( (prev) => ({...prev, un:false}) ) }} onMouseOver={()=>{ setStatusButtonState( (prev) => ({...prev, un:true}) ) }}>
|
|
<FaFaceSmile size={26} color={statusButtonState.un ? 'black': '#757575'} />
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<div className='flex items-center flex-row gap-x-2'>
|
|
|
|
<div className='relative'>
|
|
|
|
<div onClick={()=>{setRemainderModalHidden(false)}} className='bg-gray-100 border pr-3 pl-3 pt-2 pb-2 rounded-md text-white'>
|
|
<HiBellAlert size={25} color={lead?.lead_reminder?.id ? 'red': 'black'} />
|
|
</div>
|
|
|
|
|
|
<RemainderModal
|
|
objID={lead?.id}
|
|
reminder_data={lead?.lead_reminder}
|
|
onOpenClose={setRemainderModalHidden} hidden={remainderModalHidden} />
|
|
|
|
</div>
|
|
|
|
|
|
<div onClick={()=>{router.back()}} className="bg-gray-100 border pr-3 pl-3 pt-2 pb-2 rounded-md text-white">
|
|
<IoMdArrowRoundBack color={"black"} size={"25"} />
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div className="mt-0 bg-white p-4 rounded-md mb-2">
|
|
|
|
<form onSubmit={handleSubmit(onSubmit)} className="formNewLead flex flex-col gap-y-4">
|
|
|
|
<div className='title'>
|
|
<Label htmlFor="title">عنوان سرنخ</Label>
|
|
<Input {...register("title", { required: 'عنوان ضروری است', minLength: {value: 5, message: 'عنوان مناسب تری بنویسید'} } )} id="title" />
|
|
</div>
|
|
|
|
<div className='customer relative w-full'>
|
|
|
|
<Label htmlFor="title">مشتری</Label>
|
|
|
|
<Controller
|
|
name="customer_id"
|
|
control={control}
|
|
rules={{ required: 'انتخاب مشتری ضروری است' }}
|
|
render={ ({ field: { onChange, onBlur, value, ref } }) => (
|
|
<>
|
|
<span
|
|
className="w-[100%] justify-between block border rounded-md p-2 cursor-pointer"
|
|
onClick={()=>setCSearchOpen(!cSearchOpen)}
|
|
ref={ref}
|
|
>
|
|
{customerName ? customerName : 'برای انتخاب مشتری کلیک کنید'}
|
|
</span>
|
|
|
|
<div className={`mt-1 customer-select ${cSearchOpen ? null : 'hidden'} h-auto absolute bg-white border-red-800 border w-full rounded-md p-4`}>
|
|
<LSearchCustomers onSelect={onChange} searchBoxState={setCSearchOpen} changeCustomerName={setCustomerName} />
|
|
</div>
|
|
</>
|
|
|
|
)}
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div className='desc w-full'>
|
|
<Label htmlFor="desc">توضیحات</Label>
|
|
<Textarea {...register("desc", { required: 'توضیحات سرنخ ضروری است', minLength: {value: 30, message: 'توضیحات کامل تری بنویسید'} } )} id="desc" />
|
|
</div>
|
|
|
|
|
|
<div className='types grid grid-cols-3 gap-x-4 gap-y-4'>
|
|
|
|
|
|
<div className='prod-type'>
|
|
<Label htmlFor="desc">نوع محصول</Label>
|
|
|
|
<select {...register("product_type_id", { required: 'نوع محصول را انتخاب کنید'} )} className='border rounded-sm w-full h-10'>
|
|
<option value={""}>انتخاب کنید</option>
|
|
|
|
{formValues?.product_types.map((item, key)=>{
|
|
return (<option value={item.id} key={key}>{item.name}</option>)
|
|
})}
|
|
|
|
|
|
</select>
|
|
</div>
|
|
|
|
<div className='lead-type'>
|
|
<Label htmlFor="desc">نوع سرنخ</Label>
|
|
|
|
<select {...register("lead_type_id", { required: 'نوع سرنخ را انتخاب کنید'} )} className='border rounded-sm w-full h-10'>
|
|
<option value={''}>انتخاب کنید</option>
|
|
|
|
{formValues?.lead_types.map((item, key)=>{
|
|
return (<option value={item.id} key={key}>{item.title}</option>)
|
|
})}
|
|
|
|
</select>
|
|
</div>
|
|
|
|
|
|
<div className='lead-source'>
|
|
<Label htmlFor="desc">منبع سرنخ</Label>
|
|
|
|
<select {...register("lead_source_id", { required: 'منبع سرنخ را انتخاب کنید'} )} className='border rounded-sm w-full h-10'>
|
|
<option value={''} >انتخاب کنید</option>
|
|
{formValues?.lead_source.map((item, key)=>{
|
|
return (<option value={item.id} key={key}>{item.title}</option>)
|
|
})}
|
|
</select>
|
|
</div>
|
|
|
|
|
|
|
|
<div className='ostan'>
|
|
<Label htmlFor="desc">استان</Label>
|
|
|
|
<select {...register("ostan", { required: false} )} onChange={(e:any)=>{changeOstan(e)}} className='border rounded-sm w-full h-10'>
|
|
<option>انتخاب کنید</option>
|
|
{formValues?.shahr.map((item, key)=>{
|
|
if(item.is_ostan==true){
|
|
return (<option value={item.id} key={key}>{item.name}</option>)
|
|
}
|
|
})}
|
|
</select>
|
|
</div>
|
|
|
|
|
|
<div className='shahr'>
|
|
<Label htmlFor="desc">شهر</Label>
|
|
|
|
<select {...register("city_id", { required: 'شهر را انتخاب کنید'} )} className='border rounded-sm w-full h-10'>
|
|
<option value={''}>انتخاب کنید</option>
|
|
{shahr.map((item, key)=>{
|
|
return (<option value={item.id} key={key}>{item.name}</option>)
|
|
})}
|
|
</select>
|
|
</div>
|
|
|
|
<div className='lead-step'>
|
|
<Label htmlFor="desc">مرحله</Label>
|
|
|
|
<select {...register("lead_step_id", { required: 'مرحله سرنخ را مشخص کنید'} )} className='border rounded-sm w-full h-10'>
|
|
<option value={''}>انتخاب کنید</option>
|
|
{formValues?.lead_steps.map((item, key)=>{
|
|
return (<option value={item.id} key={key}>{item.name}</option>)
|
|
})}
|
|
</select>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{ Object.keys(errors).length ? (
|
|
|
|
<Alert variant="destructive">
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertTitle>خطا</AlertTitle>
|
|
<AlertDescription>
|
|
{Object.keys(errors).map((item, key)=>{
|
|
return <p key={key}>{errors[item]?.message}</p>
|
|
})}
|
|
</AlertDescription>
|
|
</Alert>
|
|
|
|
) : null}
|
|
|
|
|
|
<div className='flex flex-row justify-center mt-4'>
|
|
{!submitButtonEnable ? (<p className='text-center text-red-600'>در حال ثبت اطلاعات....</p>) : (
|
|
<input disabled={submitButtonEnable ? false : true } onClick={()=>{ console.log(Object.keys(errors)) } } className={'bg-gray-400 text-white pr-4 pl-4 pt-2 pb-2 rounded-md'} type='submit' value={'ویرایش'} />
|
|
)}
|
|
</div>
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<div className='flex flex-col w-full border-b-[1px] border-gray-400 pb-2 mb-4'>
|
|
|
|
<LeadCommentAdd lead_id={lead?.id} />
|
|
|
|
</div>
|
|
|
|
<div className='flex flex-col gap-y-2'>
|
|
|
|
{lead?.lead_comment.map((item, key)=>{
|
|
return <LeadComment data={item} key={key} />
|
|
})}
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default LeadIdPage |