Ver Fonte

work on exams and edit pages

mahyar há 5 anos atrás
pai
commit
7ad92da4b5

+ 82 - 0
src/components/exams/examDetails.js

@@ -0,0 +1,82 @@
+import React from 'react';
+import { Radio, Button} from "antd";
+import {fetcher} from "../../utils/common";
+import { error, success } from "../../utils/message";
+import {SETS_DETAIL, ANSWER_SET} from "../../utils/constants";
+
+
+
+const Questions = (props) => {
+
+    const handleAnswer = (id, e) => {
+        const answerList = (list) =>{
+            let index = list.findIndex( x => x.question === id )
+            if (index === -1){
+                list = [...list, {question: id, answer: e.target.value}]
+            }else {
+                list = [
+                    ...list.slice(0,index),
+                    Object.assign({}, list[index], {answer: e.target.value}),
+                    ...list.slice(index+1)
+                ]
+            }
+            return list
+        }
+        props.setAnswers( state => answerList(state) )
+    }
+
+    return (
+        <div className="p-4">
+            {props.item?.map( (i,k) => (
+                <div className="mt-3" key={k}>
+                    <p><span>سوال {k+1} ) </span>  {i.question} </p>
+                    <Radio.Group onChange={ (e) =>handleAnswer(i.id, e) }>
+                        <Radio className="px-2" value={i.ans1}>{i.ans1}</Radio>
+                        <Radio className="px-2" value={i.ans2}>{i.ans2}</Radio>
+                        <Radio className="px-2" value={i.ans3}>{i.ans3}</Radio>
+                        <Radio className="px-2" value={i.ans4}>{i.ans4}</Radio>
+                    </Radio.Group>
+                </div>
+            ) )}
+        </div>
+    )
+}
+
+
+export default function ExamDetails(props) {
+
+    const [setItem, setSetItem] = React.useState({});
+
+    const [answers, setAnswers] = React.useState([]);
+
+    React.useEffect( () => {
+        fetchSets()
+    }, [])
+
+    const fetchSets = () => {
+        fetcher(SETS_DETAIL(props.match.params.examId))
+        .then( response => setSetItem(response))
+        .catch( e => error(String(e)))
+    }
+
+    const sendAnswer = () => {
+        if (answers.length) {
+            fetcher(ANSWER_SET(props.match.params.examId), {
+                method: "POST",
+                body: JSON.stringify({answers})
+            })
+            .then( response => success("جواب ها ارسال شد "))
+            .catch( e => error(String(e.text)))
+        }
+    }
+
+    return (
+        <div className="p-5">
+            <div className="p-3">
+                <h6>آزمون : {setItem.title} </h6>
+            </div>
+                <Questions item={setItem.questions_info} setAnswers={setAnswers} />
+                <Button type="primary" onClick={sendAnswer} >ارسال پاسخ</Button>
+        </div>
+    )
+}

+ 72 - 0
src/components/exams/exams.js

@@ -0,0 +1,72 @@
+import React from 'react';
+import {NavLink} from "react-router-dom";
+import {Descriptions, Divider } from "antd";
+import {fetcher} from "../../utils/common";
+import { error } from "../../utils/message";
+import {SETS_LIST} from "../../utils/constants";
+
+
+const TakenDescription = (props) => (
+    <div className="my-3">
+        <Descriptions 
+            title={"آزمون های شرکت کرده"}
+            column={{ xxl: 4, xl: 4, lg: 4, md: 3, sm: 2, xs: 1 }}
+        >
+            {
+                props.item.map( (i,k) => (
+                    <Descriptions.Item span={4}><NavLink className="text-decoration-none" to={`/exams/${i.id}`} >{i.title}</NavLink></Descriptions.Item>
+                ) )
+            }
+        </Descriptions>
+    </div>
+)
+
+const EnrolledDescription = (props) => (
+    <div className="my-3">
+        <Descriptions 
+            title={"آزمون های فعال"}
+            column={{ xxl: 4, xl: 4, lg: 4, md: 3, sm: 2, xs: 1 }}
+        >
+            {
+                props.item.map( (i,k) => (
+                    <Descriptions.Item span={4}><NavLink className="text-decoration-none" to={`/exams/${i.id}`} >{i.title}</NavLink></Descriptions.Item>
+                ) )
+            }
+        </Descriptions>
+    </div>
+)
+
+
+export default function Exams(props) {
+
+    const [enrolled, setEnrolled] = React.useState([]);
+    const [taken, setTaken] = React.useState([]);
+
+    React.useEffect( () => {
+        fetchTaken();
+        fetchEnrolled();
+    }, [])
+
+    const fetchTaken = () => {
+        fetcher(SETS_LIST+"?type=taken")
+        .then( response => setTaken(response))
+        .catch( e => error(String(e)))
+    }
+
+    const fetchEnrolled = () => {
+        fetcher(SETS_LIST+"?type=enrolled")
+        .then( response => setEnrolled(response))
+        .catch( e => error(String(e)))
+    }
+
+    return (
+        <div className="p-5">
+            <div className="d-flex justify-content-between">
+                <h5>لیست آزمون ها</h5>
+            </div>
+            <TakenDescription item={taken} />
+            <Divider />
+            <EnrolledDescription item={enrolled} />
+        </div>
+    )
+}

+ 8 - 4
src/components/public/routes.js

@@ -1,9 +1,11 @@
 import Home from "../public/home";
 import Login from "../login/login";
-import Sets from "../sets/sets"
-import SetDetails from "../sets/setsDetails"
-import Questions from "../questions/questions"
-import Tenants from "../tenants/tenants"
+import Sets from "../sets/sets";
+import SetDetails from "../sets/setsDetails";
+import Questions from "../questions/questions";
+import Tenants from "../tenants/tenants";
+import Exams from "../exams/exams";
+import ExamDetails from "../exams/examDetails";
 
 
 export default [
@@ -13,4 +15,6 @@ export default [
     { path: "/sets/:setId(\\d+)", name: "امتحان", Component: SetDetails },
     { path: "/questions", name: "سوالات", Component: Questions },
     { path: "/tenants", name: "مدارس", Component: Tenants },
+    { path: "/exams", name: "آزمون ها", Component: Exams },
+    { path: "/exams/:examId(\\d+)", name: "آزمون ", Component: ExamDetails },
   ];

+ 7 - 2
src/components/public/sider.js

@@ -35,16 +35,21 @@ export default function SideMenu(props) {
                 style={{ height: '100%', borderRight: 0, backgroundColor: "#002329", color: "white" }}
             >
                 <Menu.Item key="1">
+                    <NavLinkRouter className="text-white text-decoration-none" to="/exams">
+                        آزمون ها
+                    </NavLinkRouter>
+                </Menu.Item>
+                <Menu.Item key="2">
                     <NavLinkRouter className="text-white text-decoration-none" to="/sets">
                         امتحانات
                     </NavLinkRouter>
                 </Menu.Item>
-                <Menu.Item key="2">
+                <Menu.Item key="3">
                     <NavLinkRouter className="text-white text-decoration-none" to="/questions">
                         سوالات
                     </NavLinkRouter>
                 </Menu.Item>
-                <Menu.Item key="3">
+                <Menu.Item key="4">
                     <NavLinkRouter className="text-white text-decoration-none" to="/tenants">
                         مدارس
                     </NavLinkRouter>

+ 33 - 6
src/components/questions/questions.js

@@ -2,7 +2,8 @@ import React from 'react';
 import {Descriptions, Divider, Form, Input, Button, Select } from "antd";
 import {fetcher} from "../../utils/common";
 import { error, success } from "../../utils/message";
-import {SETS_LIST, QUESTIONS_LIST} from "../../utils/constants";
+import {SETS_LIST, QUESTIONS_LIST, QUESTIONS_DETAIL} from "../../utils/constants";
+import {FaEdit} from "react-icons/fa";
 
 const { Option } = Select;
 
@@ -10,7 +11,7 @@ const { Option } = Select;
 const QuestionDescription = (props) => (
     <div className="my-3">
         <Descriptions 
-            title={props.item.question}
+            title={<div> <h6>{props.item.question}</h6> <span className="pointer" onClick={ () => {props.setEditedItem(props.item); props.setIsEditing(true) }}>ویرایش <FaEdit /> </span> </div>}
             column={{ xxl: 4, xl: 4, lg: 4, md: 3, sm: 2, xs: 1 }}
         >
             <Descriptions.Item label="مربوط به امتحان" span={4}>{props.item.test_set}</Descriptions.Item>
@@ -33,9 +34,22 @@ const QuestionCreateForm = (props) => {
     const [form] = Form.useForm();
 
     const onFinish = (values) =>{
-        addQuestion(values)
+        if (props.isEditing){
+            let payload = {...props.editedItem, ...values}
+            editQuestion(payload)
+        }else{
+            addQuestion(values)
+        }
     }
 
+    React.useEffect(() => {
+        if (props.isEditing){
+             form.setFieldsValue(props.editedItem)
+        }else{
+            form.resetFields()
+        }
+    },[props.isEditing] );
+
     const addQuestion = (payload) => {
         fetcher(QUESTIONS_LIST,{
             method: "POST",
@@ -46,6 +60,16 @@ const QuestionCreateForm = (props) => {
         }).catch( e => error(String(e)) )
     }
 
+    const editQuestion = (payload) => {
+        fetcher(QUESTIONS_DETAIL(props.editedItem.id),{
+            method: "PUT",
+            body: JSON.stringify(payload)
+        }).then( response => {
+            success("ویرایش شد");
+            setTimeout( () => window.location.reload(), 1000);
+        }).catch( e => error(String(e)) )
+    }
+
     return (
         <Form
             {...formLayout}
@@ -81,6 +105,7 @@ const QuestionCreateForm = (props) => {
             </Form.Item>
             <Form.Item >
                 <Button type="primary" htmlType="submit">ثبت</Button>
+                { props.isEditing && <Button type="reset" htmlType="reset" onClick={() => props.setIsEditing(false)}>انصراف</Button>}
             </Form.Item>
         </Form>
     )
@@ -91,6 +116,8 @@ export default function Questions(props) {
 
     const [setItems, setSetItems] = React.useState([]);
     const [questions, setQuestions] = React.useState([]);
+    const [isEditing, setIsEditing] = React.useState(false);
+    const [editedItem, setEditedItem] = React.useState({})
 
     React.useEffect( () => {
         fetchSets();
@@ -115,11 +142,11 @@ export default function Questions(props) {
                 <h5>لیست سوالات</h5>
             </div>
             <div className="p-3">
-                { questions.map( (item, index) => <QuestionDescription item={item} key={index} /> ) }
+                { questions.map( (item, index) => <QuestionDescription item={item} key={index} setIsEditing={setIsEditing} setEditedItem={setEditedItem} /> ) }
             </div>
             <Divider />
-            <h5>ساخت سوال جدید</h5>
-            <QuestionCreateForm setItems={setItems} />
+            { isEditing ? <h5>ویرایش سوال</h5> : <h5>ساخت سوال جدید</h5>}
+            <QuestionCreateForm editedItem={editedItem} setItems={setItems} isEditing={isEditing} setIsEditing={setIsEditing} />
         </div>
     )
 }

+ 80 - 5
src/components/sets/setsDetails.js

@@ -1,16 +1,18 @@
 import React from 'react';
 import {NavLink} from "react-router-dom";
-import {Descriptions, Divider, Form, Input, Button, Checkbox, Collapse } from "antd";
+import {Descriptions, Divider, Form, Input, Button, Checkbox, Collapse, Upload} from "antd";
 import {fetcher} from "../../utils/common";
 import { error, success } from "../../utils/message";
 import {SETS_DETAIL, QUESTIONS_LIST} from "../../utils/constants";
+import {getTokenObject} from "../../utils/utils";
+import {FaEdit, FaFileUpload} from "react-icons/fa";
 
 const { Panel } = Collapse;
 
 
 const SetItemDescription = (props) => (
     <div className="my-2">
-        <Descriptions title={props.item.title}>
+        <Descriptions title={<div> <h6>{props.item.title}</h6> <span className="pointer" onClick={ () => props.setIsEditing(true)}>ویرایش <FaEdit /> </span> </div>}>
             <Descriptions.Item label="توضیحات">{props.item.text}</Descriptions.Item>
             <Descriptions.Item label="شماره">{props.item.number}</Descriptions.Item>
             <Descriptions.Item label="منتشر شده">{props.item.is_draft}</Descriptions.Item>
@@ -42,8 +44,76 @@ const QuestionItemDescription = (props) => (
     </Collapse>
 )
 
+const EditSet = (props) => {
+    const formLayout = {
+        labelCol: { span: 4, offset: 4 },
+        wrapperCol: { span: 14, offset: 4 },
+    }
+
+    const [form] = Form.useForm();
+
+    const onFinish = (values) =>{
+        values["is_draft"] = Boolean(values["is_draft"]);
+        let payload = {...props.item, ...values}
+        editSetItem(payload)
+    }
+
+    const editSetItem = (payload) => {
+        fetcher(SETS_DETAIL(props.setId),{
+            method: "PUT",
+            body: JSON.stringify(payload)
+        }).then( response => {
+            success("ویرایش شد");
+            setTimeout( () => window.location.reload(), 1000);
+        }).catch( e => error(String(e.text)) )
+    }
+
+    return (
+        <>
+            <Form
+                {...formLayout}
+                layout={'vertical'}
+                form={form}
+                size="large"
+                name="set-add" 
+                onFinish={onFinish}
+                initialValues={{
+                    ...props.item
+                }}
+            >
+                <Form.Item name="title" label="نام" rules={[{ required: true }]}>
+                    <Input />
+                </Form.Item>
+                <Form.Item name="text" label="متن" rules={[{ required: true }]}>
+                    <Input.TextArea/>
+                </Form.Item>
+                <Form.Item name="number" label="شماره" rules={[{ required: true }]}>
+                    <Input />
+                </Form.Item>
+                <Form.Item name="is_draft" label="منتشر شده" valuePropName="checked">
+                    <Checkbox />
+                </Form.Item>
+                <Form.Item >
+                    <Button type="primary" htmlType="submit">ثبت</Button>
+                    <Button type="reset" htmlType="reset" onClick={() => props.setIsEditing(false) }>انصراف</Button>
+                </Form.Item>
+            </Form>
+            <Upload
+                name='receiver_users_excel'
+                method="PUT"
+                action='http://questionary.ir/api/portal/sets/4/append_receiver_users'
+                headers={
+                    {authorization: `Bearer ${getTokenObject().Authorization}`}
+                }
+            >
+                <Button icon={ <FaFileUpload /> }>  لیست کاربران شرکت کننده  </Button>
+            </Upload>
+        </>
+    )
+}
 
-const SetCreateForm = (props) => {
+
+const QuestionCreateForm = (props) => {
 
     const formLayout = {
         labelCol: { span: 4, offset: 4 },
@@ -103,6 +173,8 @@ export default function SetDetails(props) {
 
     const [setItem, setSetItem] = React.useState({});
 
+    const [isEditing, setIsEditing] = React.useState(false);
+
     React.useEffect( () => {
         fetchSets()
     }, [])
@@ -116,8 +188,11 @@ export default function SetDetails(props) {
     return (
         <div className="p-5">
             <div className="p-3">
-                <SetItemDescription item={setItem} />
+                <SetItemDescription item={setItem} setIsEditing={setIsEditing} />
             </div>
+            {
+                isEditing && <div><EditSet item={setItem} setIsEditing={setIsEditing} setId={props.match.params.setId} /></div>
+            }
             <Divider />
             <div className="my-2">
                 <h5 className="mb-3">لیست سوالات</h5>
@@ -127,7 +202,7 @@ export default function SetDetails(props) {
             </div>
             <Divider />
             <h5>اضافه کردن سوال جدید</h5>
-            <SetCreateForm setId={props.match.params.setId} />
+            <QuestionCreateForm setId={props.match.params.setId} />
         </div>
     )
 }

+ 2 - 0
src/utils/constants.js

@@ -12,7 +12,9 @@ module.exports = {
     SETS_LIST: "/portal/sets",
     SETS_DETAIL: (id) => `/portal/sets/${id}`,
     QUESTIONS_LIST: "/portal/questions",
+    QUESTIONS_DETAIL: (id) => `/portal/questions/${id}`,
     TENANTS_LIST: "/portal/tenants",
+    ANSWER_SET: (id) => `/portal/sets/${id}/answer`,
 
     // PARAMS:
     COOKIE_EXPIRES: 1 // IN DAYS