%PDF- <> %âãÏÓ endobj 2 0 obj <> endobj 3 0 obj <>/ExtGState<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI] >>/Annots[ 28 0 R 29 0 R] /MediaBox[ 0 0 595.5 842.25] /Contents 4 0 R/Group<>/Tabs/S>> endobj ºaâÚÎΞ-ÌE1ÍØÄ÷{òò2ÿ ÛÖ^ÔÀá TÎ{¦?§®¥kuµùÕ5sLOšuY>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<>endobj 2 0 obj<> endobj 2 0 obj<>endobj 2 0 obj<>es 3 0 R>> endobj 2 0 obj<> ox[ 0.000000 0.000000 609.600000 935.600000]/Fi endobj 3 0 obj<> endobj 7 1 obj<>/ProcSet[/PDF/Text/ImageB/ImageC/ImageI]>>/Subtype/Form>> stream
/* eslint-disable react-hooks/exhaustive-deps */ import React, { useState, useEffect, useRef } from "react"; import FileSaver from 'file-saver'; import AuthService from "../services/AuthService"; import ConsultantLeadsService from "../services/ConsultantLeadsService"; import { AntdPaging, AntdSelect, AntdDatepicker } from "../components"; import util from "../util"; import { Input, Button, message, Modal, Card, Table, Row, Col, Divider, Alert } from 'antd'; import { ExclamationCircleOutlined, } from '@ant-design/icons'; const { confirm } = Modal; //const $=window.$; export default function ConsultantLeads() { const [result, setResult] = useState({ data: [], page: {} }); const [loading, setLoading] = useState(false); const [lookups, setLookups] = useState({ consultants: [], acs: [], programs: [], plans: [], states: [], cities: [], }); const formRef = useRef(); const actionFormRef = useRef(); const sdataRef = useRef({ p: 1, ps: 25 }); const modules = AuthService.getModules(); const cols = [ { title: "Name", dataIndex: "name", render: (name, row) => ( <div> <div className="bold600"> {name} {row.application_no && <span> ({row.application_no})</span> } </div> <div className="fs11 text-secondary pt5"> <div className="d-flex"> <div className="w70">State</div> <div className="bold600">: {row.state}</div> </div> <div className="d-flex pt2"> <div className="w70">City</div> <div className="bold600">: {row.city}</div> </div> <div className="d-flex pt2"> <div className="w70">Acad. Career</div> <div className="bold600">: {row.ac}</div> </div> <div className="d-flex pt2"> <div className="w70">Program</div> <div className="bold600">: {row.program}</div> </div> <div className="d-flex pt2"> <div className="w70">Plan</div> <div className="bold600">: {row.plan}</div> </div> <div className="d-flex pt2"> <div className="w70">Created On</div> <div className="bold600">: {util.getDate(row.created, 'DD MMM YYYY @ hh:mm A')}</div> </div> {!AuthService.isConsultant() && <div className="d-flex pt2" style={{ color: '#8E44AD' }}> <div className="w70">Consultant</div> <div className="bold600">: {row.consultant}</div> </div> } {row.status !== 'Pending' && <div className="pt10"> <div className={`pb2 ${row.status === 'Approved' ? 'text-success' : 'text-danger'}`}> {row.status} by {row.action_by_name} on {util.getDate(row.action_on, 'DD MMM YYYY @ hh:mm A')} </div> <div> Remarks: <span className="bold600">{row.remarks}</span> </div> </div> } </div> </div> ) }, { title: "Email", dataIndex: "email", width: 200, ellipsis: true }, { title: "Mobile", dataIndex: "mob", width: 100, ellipsis: true }, { title: "Gender", dataIndex: "gender", width: 80, ellipsis: true }, { title: 'Status', dataIndex: 'status', width: 100, render: (status, row) => ( <div> {/* {status==='Pending' && <AntdTag type="warning" style={{width:'100%', textAlign:'center'}}>{status}</AntdTag>} {status==='Approved' && <AntdTag type="success" style={{width:'100%', textAlign:'center'}}>{status}</AntdTag>} {status==='Rejected' && <AntdTag type="danger" style={{width:'100%', textAlign:'center'}}>{status}</AntdTag>} */} <div className={`uc bold600 ${status === 'Pending' ? 'text-secondary' : (status === 'Approved' ? 'text-success' : 'text-danger')} `}> {status} </div> {status === 'Pending' && !AuthService.isConsultant() && modules['approve_consultant_leads'] && <div> <Divider style={{ margin: '8px 0' }} /> <div> <Button type="primary" size="small" block onClick={() => actionFormRef.current.open({ ...row, action: 'Approve' })}>Approve</Button> </div> <div className="pt5"> <Button type="danger" size="small" block onClick={() => actionFormRef.current.open({ ...row, action: 'Reject' })}>Reject</Button> </div> </div> } </div> ) }, { title: '', dataIndex: 'id', width: "86px", render: (id, row) => ( <div className="text-center"> <Button.Group size="small"> <Button type="default" onClick={() => formRef.current.open(row)}> <i className="fa fa-edit"></i> </Button> <Button type="default" onClick={() => deleteRecord(id)}> <i className="fa fa-times-circle text-danger"></i> </Button> </Button.Group> </div> ) } ].filter(v => { if (!AuthService.isConsultant()) { return v.dataIndex !== 'id'; } return true; }); const list = (p, ps) => { setLoading(true); sdataRef.current.p = p || 1; sdataRef.current.ps = ps || sdataRef.current.ps; ConsultantLeadsService.list(sdataRef.current).then(({ data }) => { data.result.data.forEach((v, i) => { v.key = i; }); setResult(data.result); }).catch(e => { message.error(e.message); }).finally(() => { setLoading(false); }) } const downloadCsv = () => { let header = { name: 'NAME', email: 'EMAIL', mob: 'MOBILE', gender: 'GENDER', state: 'STATE ', city: 'CITY', ac: 'ACADEMIC CAREER', program: 'PROGRAM', plan: 'PLAN', created: 'CREATED ON', status: 'STATUS', remarks: 'REMARKS', }; if (!AuthService.isConsultant()) { header.consultant = 'CONSULTANT'; } header.application_no = 'SYSTEM ID'; setLoading(true); ConsultantLeadsService.all(sdataRef.current).then(({ data }) => { let records = data.result.data; records.forEach(v => { }) let blob = util.convertToCSVBlob(records, header); FileSaver.saveAs(blob, 'consultant_leads.csv'); }).catch(e => { message.error(e.message); }).finally(() => { setLoading(false); }) } const deleteRecord = (id) => { message.destroy(); confirm({ title: `Are you sure to delete this lead?`, icon: <ExclamationCircleOutlined />, content: '', okText: 'Yes', okType: 'danger', cancelText: 'No', onOk() { util.showLoader(); ConsultantLeadsService.delete(id).then(({ data }) => { message.success(data.message || 'Deleted'); list(); }).catch(e => { message.error(e.message); }).finally(() => { util.hideLoader(); }) }, onCancel() { }, }) } const updateRow = (id, rowData) => { let list = [...result.data]; let ind = list.findIndex(v => v.id === id); if (ind >= 0) { list[ind] = { ...rowData }; } setResult({ ...result, data: list }); } useEffect(() => { list(); ConsultantLeadsService.lookups().then(res => { setLookups({ ...res.data.result }) }); }, []); //const wh=$(window).height(); return ( <div> <div className="page-heading d-flex align-items-center justify-content-between mb15"> <div>Consultant Leads</div> {AuthService.isConsultant() && <div> <Button type="primary" size="small" onClick={() => formRef.current.open()}>+ Add New</Button> </div> } </div> <Card size="small"> <SearchForm dataRef={sdataRef} onSearch={list} lookups={lookups} /> </Card> <div className="pt10"> {result.data.length > 0 && <div className="d-flex align-items-center justify-content-between mb8"> <div> Showing {result.page.start + 1} - {result.page.start + result.page.total} of {result.page.total_records} records. </div> <div> <Button type="dashed" size="small" onClick={downloadCsv}>Download CSV</Button> </div> </div> } <Card size="small" bodyStyle={{ padding: 0 }}> <Table size="small" bordered={false} dataSource={result.data} columns={cols} loading={loading} //scroll={{y:wh-270}} pagination={false} className="stripped" /> <div className="paging-box"> <AntdPaging onChange={list} total={result.page.total_records} current={result.page.cur_page} pageSize={sdataRef.current.ps} showSizeChanger /> </div> </Card> </div> <AddForm refOb={formRef} callback={list} pageno={sdataRef.current.p} lookups={lookups} /> <ActionForm refOb={actionFormRef} updateRow={updateRow} /> </div> ) } const SearchForm = (props) => { let { dataRef, onSearch } = props; let [data, setData] = useState({ ...dataRef.current }); const [lookups, setLookups] = useState({ consultants: [], acs: [], programs: [], plans: [], states: [], cities: [], }); const handleChange = (v, k) => { data[k] = v; setData({ ...data }); } useEffect(() => { dataRef.current = { ...data }; }, [data]); useEffect(() => { setData({ ...data, p: dataRef.current.p, ps: dataRef.current.ps }); }, [dataRef.current.p, dataRef.current.ps]); useEffect(() => { setLookups({ ...props.lookups }); }, [props.lookups]); return ( <form onSubmit={e => e.preventDefault()} autoComplete="off" spellCheck="false"> <div className="d-flex flex-wrap align-items-center"> {!AuthService.isConsultant() && <div className="w170 mr5 mb5"> <div className="fs11 text-secondary mb3">Consultant</div> <AntdSelect showSearch allowClear placeholder="All" size="small" options={lookups.consultants} value={data.consultant_id} onChange={v => { handleChange(v, 'consultant_id'); }} /> </div>} <div className="w120 mr5 mb5"> <div className="fs11 text-secondary mb3">Status</div> <AntdSelect showSearch allowClear placeholder="All" size="small" options={['Pending', 'Approved', 'Rejected']} value={data.status} onChange={v => { handleChange(v, 'status'); }} /> </div> <div className="w150 mr5 mb5"> <div className="fs11 text-secondary mb3">State</div> <AntdSelect showSearch allowClear placeholder="All" size="small" options={lookups.states} value={data.state_id} onChange={v => { handleChange(v, 'state_id'); }} /> </div> <div className="w150 mr5 mb5"> <div className="fs11 text-secondary mb3">Acad. Career</div> <AntdSelect showSearch allowClear placeholder="All" size="small" options={lookups.acs} value={data.ac_id} onChange={v => { data.program_id = null; data.plan_id = null; handleChange(v, 'ac_id'); }} /> </div> <div className="w150 mr5 mb5"> <div className="fs11 text-secondary mb3">Program</div> <AntdSelect showSearch allowClear sort placeholder="All" size="small" options={lookups.programs.filter(v => v.ac_id === data.ac_id)} value={data.program_id} onChange={v => { data.plan_id = null; handleChange(v, 'program_id'); }} /> </div> <div className="w150 mr5 mb5"> <div className="fs11 text-secondary mb3">Plan</div> <AntdSelect showSearch allowClear sort placeholder="All" size="small" options={lookups.plans.filter(v => v.program_id === data.program_id)} value={data.plan_id} onChange={v => { handleChange(v, 'plan_id'); }} /> </div> <div className="w120 mr5 mb5"> <div className="fs11 text-secondary mb3">From Date</div> <AntdDatepicker placeholder="From" size="small" onChange={dt => handleChange(dt, 'from_date')} value={data.from_date} /> </div> <div className="w120 mr5 mb5"> <div className="fs11 text-secondary mb3">To Date</div> <AntdDatepicker placeholder="To" size="small" onChange={dt => handleChange(dt, 'to_date')} value={data.to_date} /> </div> <div className="w150 mr5 mb5"> <div className="fs11 text-secondary mb3">Keyword</div> <Input placeholder="Name/Email/Mob" size="small" allowClear value={data.k} onChange={e => handleChange(e.target.value, 'k')} /> </div> <div className="pt15 mb5"> <div className="pt3"> <Button type="primary" size="small" onClick={() => onSearch()}>Search</Button> </div> </div> </div> </form> ) } const AddForm = (props) => { let { callback, pageno, refOb, lookups } = props; const [showModal, setShowModal] = useState(false); let [data, setData] = useState({}); const handleChange = (v, k) => { data[k] = v; setData({ ...data }); } const handleOk = () => { message.destroy(); util.showLoader("Saving..."); data.city = lookups.cities.find(v => v.id === data.city_id)?.name; ConsultantLeadsService.save(data).then(({ data }) => { message.success(data.message || 'Saved'); callback(data.id ? pageno : 1); setShowModal(false); }).catch(e => { message.error(e.message); }).finally(() => { util.hideLoader(); }) } const handleCancel = () => { setShowModal(false); } refOb.current = { ...refOb.current, open: (dtl) => { setData(dtl ? { ...dtl } : {}); setShowModal(true); } } return ( <Modal title={`${data.id ? 'Edit' : 'Add'} Lead`} open={showModal} okText="Save" onOk={handleOk} onCancel={handleCancel} destroyOnClose maskClosable={false} width={700} style={{ top: 20 }} > <form onSubmit={e => { e.preventDefault(); handleOk() }} autoComplete="off" spellCheck="false"> <Row gutter={[16, 16]}> <Col span={12}> <label className="req">Name</label> <Input value={data.name || ''} onChange={e => handleChange(e.target.value, 'name')} /> </Col> <Col span={12}> <label className="req">Email</label> <Input value={data.email || ''} onChange={e => handleChange(e.target.value, 'email')} /> </Col> <Col span={12}> <label className="req">Mobile Number (10 digit numeric)</label> <Input value={data.mob || ''} maxLength="10" onChange={e => handleChange(e.target.value, 'mob')} /> </Col> <Col span={12}> <label className="req">Gender</label> <AntdSelect placeholder="Select" options={['Male', 'Female', 'Other']} value={data.gender} onChange={v => { handleChange(v, 'gender'); }} /> </Col> <Col span={12}> <label className="req">State</label> <AntdSelect showSearch allowClear sort placeholder="Select" options={lookups.states} value={data.state_id} onChange={v => { data.city_id = null; handleChange(v, 'state_id'); }} /> </Col> <Col span={12}> <label className="req">City</label> <AntdSelect showSearch allowClear sort placeholder="Select" options={lookups.cities.filter(v => v.state_id === data.state_id)} value={data.city_id} onChange={v => { handleChange(v, 'city_id'); }} /> </Col> </Row> <Divider /> <Row gutter={[16, 16]}> <Col span={24}> <label className="req">Academic Career</label> <AntdSelect showSearch allowClear placeholder="Select" options={lookups.acs} value={data.ac_id} onChange={v => { data.program_id = null; data.plan_id = null; handleChange(v, 'ac_id'); }} /> </Col> <Col span={24}> <label className="req">Program</label> <AntdSelect showSearch allowClear sort placeholder="Select" options={lookups.programs.filter(v => v.ac_id === data.ac_id)} value={data.program_id} onChange={v => { data.plan_id = null; handleChange(v, 'program_id'); }} /> </Col> <Col span={24}> <label className="req">Plan/Specialization</label> <AntdSelect showSearch allowClear sort placeholder="Select" options={lookups.plans.filter(v => v.program_id === data.program_id)} value={data.plan_id} onChange={v => { handleChange(v, 'plan_id'); }} /> </Col> </Row> </form> </Modal> ) } const ActionForm = (props) => { let { refOb, updateRow } = props; const [showModal, setShowModal] = useState(false); let [dtl, setDtl] = useState({}); let [data, setData] = useState({ remarks: '' }); const handleChange = (v, k) => { data[k] = v; setData({ ...data }); } const handleOk = () => { message.destroy(); util.showLoader("Wait..."); let fn = dtl.action === 'Reject' ? ConsultantLeadsService.reject : ConsultantLeadsService.approve; fn(dtl.id, data.remarks).then(({ data }) => { message.success(data.message || 'Updated'); updateRow(dtl.id, data.row_dtl); setShowModal(false); }).catch(e => { message.error(e.message); }).finally(() => { util.hideLoader(); }) } const handleCancel = () => { setShowModal(false); } refOb.current = { ...refOb.current, open: (dtl) => { setData({ remarks: '' }); setDtl(dtl ? { ...dtl } : {}); setShowModal(true); } } return ( <Modal title={`${dtl.action} Lead`} open={showModal} okText={`${dtl.action} Lead`} onOk={handleOk} cancelText="Close" onCancel={handleCancel} destroyOnClose maskClosable={false} width={600} > <div className="mb15"> <Alert message={`Note: You are going to ${dtl.action} this Lead`} type="warning" /> </div> <div className="mb15"> <div className="d-flex mb3"> <div className="w70">Name</div> <div className="bold600">: {dtl.name}</div> </div> <div className="d-flex mb3"> <div className="w70">Email</div> <div className="bold600">: {dtl.email}</div> </div> <div className="d-flex mb3"> <div className="w70">Mobile</div> <div className="bold600">: {dtl.mob}</div> </div> <div className="d-flex mb3"> <div className="w70">Course</div> <div className="bold600">: {dtl.plan} ({dtl.program})</div> </div> </div> <form onSubmit={e => { e.preventDefault(); handleOk() }} autoComplete="off" spellCheck="false"> <Row gutter={[16, 16]}> <Col span={24}> <label className="req">Remarks</label> <Input.TextArea rows="4" value={data.remarks || ''} onChange={e => handleChange(e.target.value, 'remarks')} /> </Col> </Row> </form> </Modal> ) }