parent
3e8736f0d8
commit
a9d2b22a6c
18 changed files with 476 additions and 68 deletions
@ -0,0 +1,32 @@ |
|||||||
|
import proxy from "../proxy"; |
||||||
|
const comment = { |
||||||
|
list: |
||||||
|
(data = {}) => |
||||||
|
async (dispatch) => |
||||||
|
await proxy.get("comment/list", data, { dispatch }), |
||||||
|
info: |
||||||
|
(data = {}) => |
||||||
|
async (dispatch) => { |
||||||
|
await proxy.get("comment/info", data, { dispatch }); |
||||||
|
}, |
||||||
|
add: |
||||||
|
(data = {}, data2) => |
||||||
|
async (dispatch) => { |
||||||
|
await proxy.post("comment/add", data, { dispatch }); |
||||||
|
if (data2) await proxy.get("book/info", data2, { dispatch }); |
||||||
|
}, |
||||||
|
del: |
||||||
|
(data = {}, data2 = {}) => |
||||||
|
async (dispatch) => { |
||||||
|
await proxy.delete("ar/delete", data); |
||||||
|
await proxy.get("ar/list", data2, { dispatch }); |
||||||
|
}, |
||||||
|
update: |
||||||
|
(data = {}, data2 = {}) => |
||||||
|
async (dispatch) => { |
||||||
|
await proxy.put("ar/update", data); |
||||||
|
await proxy.get("ar/list", data2, { dispatch }); |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export default comment; |
@ -0,0 +1,8 @@ |
|||||||
|
const dialog = { |
||||||
|
set: |
||||||
|
(data = {}) => |
||||||
|
async (dispatch) => |
||||||
|
await dispatch({ type: "dialog/set", data }), |
||||||
|
}; |
||||||
|
|
||||||
|
export default dialog; |
@ -0,0 +1,8 @@ |
|||||||
|
const form = { |
||||||
|
setTicket: |
||||||
|
(data = {}) => |
||||||
|
async (dispatch) => |
||||||
|
await dispatch({ type: "form/ticket", data }), |
||||||
|
}; |
||||||
|
|
||||||
|
export default form; |
@ -0,0 +1,36 @@ |
|||||||
|
import {toast} from 'react-toastify'; |
||||||
|
|
||||||
|
const initialState = { |
||||||
|
loading: false, |
||||||
|
error: null, |
||||||
|
list: null, |
||||||
|
}; |
||||||
|
|
||||||
|
export default function comment(state = initialState, action) { |
||||||
|
let { type, data } = action; |
||||||
|
switch (type) { |
||||||
|
case "comment/list": |
||||||
|
return { ...state, loading: false, error: null }; |
||||||
|
case "comment/update": |
||||||
|
return { ...state, loading: false, error: null }; |
||||||
|
case "comment/add": |
||||||
|
toast.success("ثبت درخواست با موفقیت انجام شد"); |
||||||
|
return { ...state, loading: false, error: null }; |
||||||
|
case "comment/delete": |
||||||
|
return { ...state, loading: false, error: null }; |
||||||
|
case "comment/info": |
||||||
|
return { |
||||||
|
...state, |
||||||
|
loading: false, |
||||||
|
list: data, |
||||||
|
error: null, |
||||||
|
}; |
||||||
|
case "comment/loading": |
||||||
|
return { ...state, loading: true }; |
||||||
|
case "comment/error": |
||||||
|
toast.error(data.message); |
||||||
|
return { ...state, loading: false, error: data.message }; |
||||||
|
default: |
||||||
|
return state; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
const initialState = { |
||||||
|
dialog: { |
||||||
|
text: null, |
||||||
|
type: null, |
||||||
|
accept: null, |
||||||
|
open: null, |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
export default function dialog(state = initialState, action) { |
||||||
|
let { type, data } = action; |
||||||
|
switch (type) { |
||||||
|
case "dialog/set": |
||||||
|
return { |
||||||
|
...state, |
||||||
|
dialog : {...data}, |
||||||
|
}; |
||||||
|
default: |
||||||
|
return state; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
import { toast } from "react-toastify"; |
||||||
|
|
||||||
|
const initialState = { |
||||||
|
loading: false, |
||||||
|
error: null, |
||||||
|
ticket: {}, |
||||||
|
}; |
||||||
|
export default function form(state = initialState, action) { |
||||||
|
let { type, data } = action; |
||||||
|
switch (type) { |
||||||
|
case "form/ticket": |
||||||
|
return { |
||||||
|
...state, |
||||||
|
loading: false, |
||||||
|
ticket: { ...state.ticket, [data.name]: data.value }, |
||||||
|
error: null, |
||||||
|
}; |
||||||
|
case "form/loading": |
||||||
|
return { ...state, loading: true }; |
||||||
|
case "form/error": |
||||||
|
toast.error(data.message); |
||||||
|
return { ...state, loading: false, error: data.message }; |
||||||
|
default: |
||||||
|
return state; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,180 @@ |
|||||||
|
import React, { useMemo, useEffect, useCallback } from "react"; |
||||||
|
import { connect } from "react-redux"; |
||||||
|
import { |
||||||
|
userProduct, |
||||||
|
comment, |
||||||
|
file, |
||||||
|
form, |
||||||
|
dialog, |
||||||
|
} from "../../../redux/actions"; |
||||||
|
import onInput from "../../../utils/onInput"; |
||||||
|
|
||||||
|
//components
|
||||||
|
import Input from "../../../components/Input"; |
||||||
|
import Textarea from "../../../components/Textarea"; |
||||||
|
import Button from "../../../components/Button"; |
||||||
|
import Select from "../../../components/Select"; |
||||||
|
import Upload from "./Upload"; |
||||||
|
|
||||||
|
export const Ticket = ({ |
||||||
|
isDark, |
||||||
|
myProductList, |
||||||
|
status, |
||||||
|
ticket, |
||||||
|
getUserProductList, |
||||||
|
submit, |
||||||
|
upload, |
||||||
|
setTicket, |
||||||
|
grades, |
||||||
|
setDialog, |
||||||
|
}) => { |
||||||
|
let opt = useMemo( |
||||||
|
() => ["واحد مورد نظر", "واحد فروش", "واحد فنی", "واحد محتوایی"], |
||||||
|
[status] |
||||||
|
); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
if (myProductList.length === 0 && status) { |
||||||
|
getUserProductList(); |
||||||
|
} |
||||||
|
}, []); |
||||||
|
|
||||||
|
const addFile = useCallback( |
||||||
|
(name, value) => { |
||||||
|
upload({ file: value, target: name }); |
||||||
|
setTicket({ name, value }); |
||||||
|
}, |
||||||
|
[status] |
||||||
|
); |
||||||
|
|
||||||
|
const send = useCallback(async () => { |
||||||
|
if (!ticket?.subject) { |
||||||
|
setDialog({ |
||||||
|
text: "موضوع پیام خود را وارد کنید.", |
||||||
|
type: "alert", |
||||||
|
accept: () => {}, |
||||||
|
open: true, |
||||||
|
}); |
||||||
|
}else if(!ticket?.unit){ |
||||||
|
setDialog({ |
||||||
|
text: "واحد مورد نظر خود را وارد نکرده اید.", |
||||||
|
type: "alert", |
||||||
|
accept: () => {}, |
||||||
|
open: true, |
||||||
|
}); |
||||||
|
} else { |
||||||
|
const data = { |
||||||
|
fileId: null, |
||||||
|
text: ticket?.text, |
||||||
|
name: !status ? ticket?.name : status.firstName + " " + status.lastName, |
||||||
|
cellphone: !status ? ticket?.cellphone : status?.cellphone, |
||||||
|
unit: ticket?.unit, |
||||||
|
bookId: ticket?.bookId, |
||||||
|
subject: ticket?.subject, |
||||||
|
userId: status?.userId, |
||||||
|
}; |
||||||
|
await submit(data); |
||||||
|
} |
||||||
|
}, [status]); |
||||||
|
|
||||||
|
return ( |
||||||
|
<div className="flex flex-col flex-1"> |
||||||
|
{!status && ( |
||||||
|
<> |
||||||
|
<Input |
||||||
|
width="55%" |
||||||
|
label="نام و نام خانوادگی" |
||||||
|
name="name" |
||||||
|
onChange={(name, value) => setTicket({ name, value })} |
||||||
|
value={ticket?.name || ""} |
||||||
|
regex={(val) => val} |
||||||
|
/> |
||||||
|
<Input |
||||||
|
width="55%" |
||||||
|
label="شماره تماس" |
||||||
|
name="cellphone" |
||||||
|
onChange={(name, value) => setTicket({ name, value })} |
||||||
|
value={ticket?.cellphone || ""} |
||||||
|
regex={(val) => onInput.phonenumber(val)} |
||||||
|
maxLength="11" |
||||||
|
/> |
||||||
|
</> |
||||||
|
)} |
||||||
|
<Select |
||||||
|
options={status && myProductList?.length ? opt : opt.slice(0, 3)} |
||||||
|
name="unit" |
||||||
|
onChange={(name, value) => setTicket({ name, value })} |
||||||
|
width={"w-10/12"} |
||||||
|
backgroundColor={isDark ? "dark:bg-transparent dark:text-white" : ""} |
||||||
|
fill={isDark ? "fill-white" : "fill-gray-600"} |
||||||
|
/> |
||||||
|
{ticket?.unit === "واحد محتوایی" && ( |
||||||
|
<Select |
||||||
|
name="bookId" |
||||||
|
options={[ |
||||||
|
...new Set( |
||||||
|
myProductList?.map((item) => [ |
||||||
|
item?.product?.book?.id, |
||||||
|
`${item?.product?.book.name} پایه ${ |
||||||
|
Object.values(grades)[item.product.book.gradeId] |
||||||
|
}`,
|
||||||
|
]) || [] |
||||||
|
), |
||||||
|
]} |
||||||
|
onChange={(name, value) => setTicket({ name, value })} |
||||||
|
width={"w-10/12"} |
||||||
|
backgroundColor={isDark ? "dark:bg-transparent dark:text-white" : ""} |
||||||
|
fill={isDark ? "fill-white" : "fill-gray-600"} |
||||||
|
/> |
||||||
|
)} |
||||||
|
<Input |
||||||
|
width="85%" |
||||||
|
name="subject" |
||||||
|
onChange={(name, value) => setTicket({ name, value })} |
||||||
|
value={ticket?.subject || ""} |
||||||
|
regex={(val) => val} |
||||||
|
label="موضوع" |
||||||
|
/> |
||||||
|
<Textarea |
||||||
|
placeholder="پیام خود را اینجا بنویسید" |
||||||
|
name="text" |
||||||
|
onChange={(name, value) => setTicket({ name, value })} |
||||||
|
value={ticket?.text || ""} |
||||||
|
/> |
||||||
|
<br /> |
||||||
|
<Upload |
||||||
|
onChange={(name, value) => addFile(name, value)} |
||||||
|
name="file" |
||||||
|
value={ticket?.file} |
||||||
|
/> |
||||||
|
<br /> |
||||||
|
<Button |
||||||
|
title="ارسال پیام" |
||||||
|
backgroundColor={isDark ? "bg-blueC0" : "bg-blueC0"} |
||||||
|
border={{ color: isDark ? "#ffffff55" : "#196EC055", width: "0px" }} |
||||||
|
width="100%" |
||||||
|
color={"text-white"} |
||||||
|
selectable={true} |
||||||
|
onClick={() => send()} |
||||||
|
/> |
||||||
|
</div> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({ |
||||||
|
isDark: state.publicApi.isDark, |
||||||
|
myProductList: state.userProduct.myList, |
||||||
|
status: state.user.status, |
||||||
|
ticket: state.form.ticket, |
||||||
|
grades: state.publicApi.grades, |
||||||
|
}); |
||||||
|
|
||||||
|
const mapDispatchToProps = { |
||||||
|
getUserProductList: userProduct.myList, |
||||||
|
submit: comment.add, |
||||||
|
upload: file.upload, |
||||||
|
setTicket: form.setTicket, |
||||||
|
setDialog: dialog.set, |
||||||
|
}; |
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(Ticket); |
@ -0,0 +1,68 @@ |
|||||||
|
import React from "react"; |
||||||
|
import { connect } from "react-redux"; |
||||||
|
|
||||||
|
import "./index.scss"; |
||||||
|
|
||||||
|
function Document({ onChange, name, value, isMobile }) { |
||||||
|
return ( |
||||||
|
<> |
||||||
|
{isMobile ? ( |
||||||
|
<div className="support__document flex justify-center items-center mb-3"> |
||||||
|
<input |
||||||
|
type="file" |
||||||
|
name={name} |
||||||
|
onChange={(e) => onChange(e.target.name, e.target.files[0])} |
||||||
|
/> |
||||||
|
{value ? ( |
||||||
|
<span |
||||||
|
style={{ |
||||||
|
width: "100%", |
||||||
|
textAlign: "center", |
||||||
|
margin: "auto", |
||||||
|
}} |
||||||
|
> |
||||||
|
{value?.name || "تصویر بارگزاری شد"} |
||||||
|
</span> |
||||||
|
) : ( |
||||||
|
<span>در صورت نیاز تصویر مرتبط را انتخاب کنید.</span> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
) : ( |
||||||
|
<div className="support__document flex justify-center items-center"> |
||||||
|
<input |
||||||
|
type="file" |
||||||
|
name={name} |
||||||
|
onChange={(e) => onChange(e.target.name, e.target.files[0])} |
||||||
|
/>{" "} |
||||||
|
{value ? ( |
||||||
|
<span |
||||||
|
style={{ |
||||||
|
width: "100%", |
||||||
|
textAlign: "center", |
||||||
|
margin: "auto", |
||||||
|
}} |
||||||
|
> |
||||||
|
{value?.name || "فایل ثبت شد"} |
||||||
|
</span> |
||||||
|
) : ( |
||||||
|
<span |
||||||
|
style={{ |
||||||
|
width: "100%", |
||||||
|
textAlign: "center", |
||||||
|
margin: "auto", |
||||||
|
}} |
||||||
|
> |
||||||
|
در صورت نیاز تصویر مرتبط را انتخاب کنید. |
||||||
|
</span> |
||||||
|
)} |
||||||
|
</div> |
||||||
|
)} |
||||||
|
</> |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
const mapStateToProps = (state) => ({ |
||||||
|
isMobile: state.publicApi.isMobile, |
||||||
|
}); |
||||||
|
|
||||||
|
export default connect(mapStateToProps)(Document); |
@ -0,0 +1,28 @@ |
|||||||
|
.support { |
||||||
|
&__document { |
||||||
|
color: gray !important; |
||||||
|
margin: 10px auto; |
||||||
|
width: 100%; |
||||||
|
height: 50px; |
||||||
|
border-radius: 10px; |
||||||
|
border: 2px dashed #d1d1d1; |
||||||
|
position: relative; |
||||||
|
input { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
position: absolute; |
||||||
|
left: 0px; |
||||||
|
top: 0px; |
||||||
|
opacity: 0; |
||||||
|
} |
||||||
|
img { |
||||||
|
width: 100%; |
||||||
|
height: 100%; |
||||||
|
object-fit: cover; |
||||||
|
border-radius: 50%; |
||||||
|
} |
||||||
|
span { |
||||||
|
color: #afafaf; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue