reza added contact us

temp
Reza_ashrafi 3 years ago
parent 3e8736f0d8
commit a9d2b22a6c
  1. 34
      src/components/Dialog/index.js
  2. 5
      src/components/Textarea/index.js
  3. 32
      src/redux/actions/comment.js
  4. 8
      src/redux/actions/dialog.js
  5. 8
      src/redux/actions/form.js
  6. 5
      src/redux/actions/index.js
  7. 5
      src/redux/actions/userProduct.js
  8. 36
      src/redux/reducers/comment.js
  9. 21
      src/redux/reducers/dialog.js
  10. 26
      src/redux/reducers/form.js
  11. 5
      src/redux/reducers/index.js
  12. 3
      src/redux/reducers/userProduct.js
  13. 50
      src/router.js
  14. 2
      src/views/Address/index.js
  15. 28
      src/views/Contact/index.js
  16. 180
      src/views/Contact/layouts/Ticket.js
  17. 68
      src/views/Contact/layouts/Upload.js
  18. 28
      src/views/Contact/layouts/index.scss

@ -1,18 +1,26 @@
import React from "react";
import { connect } from "react-redux";
import { dialog } from "../../redux/actions";
export const Dialog = ({ dialog }) => {
const { text, type, setOpen, accept, bg, color } = dialog;
export const Dialog = ({ dialog, setDialog }) => {
const { text, type, accept } = dialog;
return (
<div
className="z-50 w-screen h-full fixed top-0 right-0 backdrop-filter blur-sm flex justify-center items-center"
onClick={() => setOpen(false)}
onClick={() => setDialog({ ...dialog, open: !dialog.open })}
>
<div className={`flex flex-col items-center ${bg} backdrop-filter backdrop-blur-xl rounded-md p-5 shadow-2xl`} style={{minWidth: 300}}>
<div
className={`flex flex-col items-center bg-white dark:bg-black dark:bg-opacity-30 backdrop-filter backdrop-blur-xl rounded-md p-5 shadow-2xl`}
style={{ minWidth: 300 }}
>
{/* {type === "confirm" && (
<div className="w-full flex justify-end text-sm text-bla">exit</div>
)} */}
<p className={`text-tiny ${color} w-full mb-8 leading-7`}>{text}</p>
<p
className={`text-tiny text-gray70 dark:text-white w-full mb-8 leading-7`}
>
{text}
</p>
{type === "confirm" && (
<div className="flex flex-row gap-4 w-full">
<button
@ -23,7 +31,7 @@ export const Dialog = ({ dialog }) => {
</button>
<button
className="py-2.5 w-1/2 shadow-md rounded-sm bg-redFF1 text-white"
onClick={() => setOpen(false)}
onClick={() => setDialog({ ...dialog, open: !dialog.open })}
>
بیخیال
</button>
@ -32,8 +40,8 @@ export const Dialog = ({ dialog }) => {
{type === "alert" && (
<div className="flex flex-row gap-4 w-full">
<button
className="py-2.5 w-full shadow-md rounded-sm bg-blueC0 text-white"
onClick={() => setOpen(false)}
className="py-2.5 w-full shadow-md rounded-sm bg-blue-400 text-white"
onClick={() => setDialog({ ...dialog, open: !dialog.open })}
>
باشه
</button>
@ -44,6 +52,12 @@ export const Dialog = ({ dialog }) => {
);
};
const mapStateToProps = (state) => ({});
const mapStateToProps = (state) => ({
dialog: state.dialog.dialog,
});
export default connect(mapStateToProps)(Dialog);
const mapDispatchToProps = {
setDialog: dialog.set,
};
export default connect(mapStateToProps, mapDispatchToProps)(Dialog);

@ -1,7 +1,7 @@
import React from "react";
import _ from "lodash";
export default function Textarea({ placeholder }) {
export default function Textarea({ placeholder, onChange, value, name }) {
return (
<>
<textarea
@ -12,6 +12,9 @@ export default function Textarea({ placeholder }) {
],
" "
)}
vale={value}
name={name}
onChange={(e) => onChange(e.target.name, e.target.value)}
rows={10}
placeholder={placeholder}
/>

@ -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;

@ -12,4 +12,7 @@ export { default as content } from "./content";
export { default as faq } from "./faq";
export { default as file } from "./file";
export {default as scrollAction} from "./scroll";
export {default as qr} from "./qr";
export {default as qr} from "./qr";
export {default as comment} from "./comment";
export {default as form} from "./form";
export {default as dialog} from "./dialog";

@ -28,6 +28,11 @@ const userProduct = {
await proxy.delete("userProduct/delete", data);
await proxy.get("userProduct/list", data2, { dispatch });
},
myList:
(data = {}) =>
async (dispatch) => {
await proxy.get("userProduct/myList", data, { dispatch });
},
};
export default userProduct;

@ -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;
}
}

@ -12,4 +12,7 @@ export { default as file } from "./file";
export { default as userFactor } from "./userFactor";
export { default as userProduct } from "./userProduct";
export {default as scrollAction} from "./scroll";
export {default as qr} from "./qr";
export {default as qr} from "./qr";
export {default as comment} from "./comment";
export {default as form} from "./form";
export {default as dialog} from "./dialog";

@ -6,12 +6,15 @@ const initialState = {
list: [],
info: null,
sum: null,
myList : [],
checkEmpty: false,
hasPhysical: false,
};
export default function userFactor(state = initialState, action) {
let { type, data } = action;
switch (type) {
case "userProduct/myList":
return { ...state, loading: false, myList: data, error: null };
case "userProduct/list":
const checkEmpty = data.filter(
(item) => item.condition === 2 && item.product.productType === (1 || 3)

@ -30,15 +30,11 @@ import FooterDesign1 from "./components/Footer";
import Blogs from "./views/Blogs";
import Blog from "./views/Blog";
import About from "./views/About";
import Dialog from "./components/Dialog";
function Router({
isDark,
isMobile,
headerOptions,
}) {
function Router({ isDark, isMobile, headerOptions, dialog }) {
useEffect(() => {
window.addEventListener("scroll", () => {
});
window.addEventListener("scroll", () => {});
}, []);
return (
@ -53,6 +49,7 @@ function Router({
" "
)}
>
{dialog.open && <Dialog />}
<BrowserRouter>
{headerOptions?.shown && <Navbar headerOptions={headerOptions} />}
@ -323,7 +320,7 @@ function Router({
/>
}
/>
<Route
<Route
path="/about-us"
exact
element={
@ -363,15 +360,15 @@ function Router({
exact
element={
<Blogs
// headerOptions={{
// logo: true,
// hamburgerMenu: true,
// qr: true,
// title: false,
// back: false,
// shown: true,
// menu: false,
// }}
// headerOptions={{
// logo: true,
// hamburgerMenu: true,
// qr: true,
// title: false,
// back: false,
// shown: true,
// menu: false,
// }}
/>
}
/>
@ -380,15 +377,15 @@ function Router({
exact
element={
<Blog
// headerOptions={{
// logo: true,
// hamburgerMenu: true,
// qr: true,
// title: false,
// back: false,
// shown: true,
// menu: false,
// }}
// headerOptions={{
// logo: true,
// hamburgerMenu: true,
// qr: true,
// title: false,
// back: false,
// shown: true,
// menu: false,
// }}
/>
}
/>
@ -422,6 +419,7 @@ const mapStateToProps = (state) => ({
isMobile: state.publicApi.isMobile,
isLogin: state.user.status,
headerOptions: state.publicApi.headerOptions,
dialog: state.dialog.dialog,
});
const mapDispatchToProps = {

@ -6,7 +6,6 @@ import Input from "../../components/Input";
import Select from "../../components/Select";
import Button from "../../components/Button";
import ReverseMap from "../../components/ReverseMap";
import Dialog from "../../components/Dialog";
import Loading from "../../components/Loading";
//assets
@ -405,7 +404,6 @@ export const Address = ({
/>
</div>
)}
{dialog.open && <Dialog dialog={dialog} />}
</div>
);
};

@ -2,12 +2,10 @@ import React, { useEffect } from "react";
import { connect } from "react-redux";
import { publicApi } from "../../redux/actions";
import Ticket from './layouts/Ticket';
import Header from "../Activation/components/Header";
import NotFound from "../Faq/components/NotFound";
import Info from "./layouts/Info";
import Input from "../../components/Input";
import Textarea from "../../components/Textarea";
import Button from "../../components/Button";
import Map from "../../components/Map";
//assets
@ -22,30 +20,6 @@ function Contact({ headerOptions, setHeaderOptions, info, isMobile, isDark }) {
setHeaderOptions(headerOptions);
}, []);
const Ticket = ({}) => {
return (
<div className="flex flex-col flex-1">
<Input width="55%" label="نام شما" backgroundColor="" />
<Input width="55%" label="شماره تماس" backgroundColor="" />
<Input width="85%" label="موضوع" backgroundColor="" />
<Textarea
backgroundColor=""
placeholder="پیام خود را اینجا بنویسید"
/>
<br />
<Button
title="ارسال پیام"
backgroundColor={isDark ? "bg-blueC0" : "bg-blueC0"}
border={{ color: isDark ? "#ffffff55" : "#196EC055", width: "0px" }}
width="100%"
color={"text-white"}
selectable={true}
onClick={() => {}}
/>
</div>
);
};
return (
<div
className="w-full flex h-auto flex-col bg-transparent lg:pt-36 lg:pb-24"

@ -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…
Cancel
Save