Browse Source

fixed #150 & #115

ali 3 years ago
parent
commit
14a177edd0

+ 2 - 2
quasar.config.js

@@ -48,8 +48,8 @@ module.exports = configure(function (ctx) {
       'registerQPageBuilder',
       'routesLayoutConfigs',
       'GetRouteSettingFromServer',
-      'enums',
-      'gtm'
+      'enums'
+      // 'gtm'
     ],
 
     // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css

+ 16 - 1
src/api/models/Session.js

@@ -8,7 +8,8 @@ export default class SessionAPI extends APIRepository {
     this.APIAdresses = {
       base: '/lma/sessions',
       attendanceSheets: '/lma/sessions/attendance_sheets',
-      byId: (id) => '/lma/sessions/' + id
+      byId: (id) => '/lma/sessions/' + id,
+      sessionAndCurrentUserAttendanceSheet: (id) => '/lma/sessions/' + id + '/session_and_current_user_attendance_sheet'
     }
     this.CacheList = {
       base: this.name + this.APIAdresses.base,
@@ -68,4 +69,18 @@ export default class SessionAPI extends APIRepository {
       }
     })
   }
+
+  get (id) {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.byId(id),
+      resolveCallback: (response) => {
+        return new Session(response.data)
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
 }

+ 45 - 1
src/api/models/SessionAttendanceSheets.js

@@ -9,7 +9,9 @@ export default class SessionAttendanceSheetsAPI extends APIRepository {
       base: '/lma/session_attendance_sheets',
       submitAssignment: '/lma/session_attendance_sheets/submit_assignment',
       submitAttendanceStatus: '/lma/session_attendance_sheets/submit_attendance_status',
-      byId: (id) => '/lma/project_attendance_sheets/' + id
+      verify: (id) => '/lma/session_attendance_sheets/' + id + '/verify',
+      notVerify: (id) => '/lma/session_attendance_sheets/' + id + '/not_verify',
+      byId: (id) => '/lma/session_attendance_sheets/' + id
     }
     this.CacheList = {
       base: this.name + this.APIAdresses.base,
@@ -49,4 +51,46 @@ export default class SessionAttendanceSheetsAPI extends APIRepository {
       }
     })
   }
+
+  get (id) {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.byId(id),
+      resolveCallback: (response) => {
+        return new SessionAttendanceSheets(response.data)
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+
+  verify (sessionAttendanceSheetsId) {
+    return this.sendRequest({
+      apiMethod: 'put',
+      api: this.api,
+      request: this.APIAdresses.verify(sessionAttendanceSheetsId),
+      resolveCallback: (response) => {
+        return new SessionAttendanceSheets(response.data)
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+
+  notVerify (sessionAttendanceSheetsId) {
+    return this.sendRequest({
+      apiMethod: 'put',
+      api: this.api,
+      request: this.APIAdresses.notVerify(sessionAttendanceSheetsId),
+      resolveCallback: (response) => {
+        return new SessionAttendanceSheets(response.data)
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
 }

+ 3 - 2
src/components/Widgets/SessionAttendanceSheet/AdminSessionAttendanceSheetList/AdminSessionAttendanceSheetList.vue

@@ -13,7 +13,7 @@
     </div>
     <entity-index v-if="mounted"
                   v-model:value="inputs"
-                  title="لیست محتواها"
+                  title="لیست حضور و غیاب"
                   :api="api"
                   :table="table"
                   :table-keys="tableKeys"
@@ -28,6 +28,7 @@
         <template v-else-if="inputData.col.name === 'action'">
           <q-btn color="primary"
                  outline
+                 class="q-mr-md"
                  :to="{name: 'Admin.Classroom.Session.AttendanceSheet.Attendance', params: {classroom_id: $route.params.classroom_id, session_id: $route.params.session_id, session_attendance_sheet_id: inputData.props.row.id}}">
             حضور و غیاب
           </q-btn>
@@ -124,7 +125,7 @@ export default {
           {
             name: 'action',
             required: true,
-            label: 'جزییات',
+            label: 'عملیات',
             align: 'left',
             field: row => ''
           }

+ 357 - 0
src/components/Widgets/SessionAttendanceSheet/AdminSessionAttendanceSheetShowAssignment/AdminSessionAttendanceSheetShowAssignment.vue

@@ -0,0 +1,357 @@
+<template>
+  <div>
+    <q-skeleton v-if="classroom.loading"
+                type="text"
+                width="200px" />
+    <breadcrumbs v-else
+                 style="margin-top: 29px; margin-bottom: 19px;" />
+    <div class="flex justify-end">
+      <q-btn flat
+             color="grey"
+             @click="$router.go(-1)">
+        بازگشت
+        >
+      </q-btn>
+    </div>
+    <entity-show v-if="mounted"
+                 v-model:value="sessionInfoInputs"
+                 title="جزییات جلسه"
+                 :api="api"
+                 :show-expand-button="false"
+                 :show-edit-button="false"
+                 :show-index-button="false"
+                 :show-reload-button="false"
+                 :before-load-input-data="beforeLoadInputData">
+      <template #before-form-builder>
+        <div class="session-title-and-time-row">
+          <div class="item">
+            <div class="icon">
+              <q-icon name="person" />
+            </div>
+            <div class="title">
+              {{ sessionAttendanceSheets.owner_info.firstname }}
+              {{ sessionAttendanceSheets.owner_info.lastname }}
+            </div>
+          </div>
+          <div class="item">
+            <div class="icon">
+              <svg xmlns="http://www.w3.org/2000/svg"
+                   width="18.165"
+                   height="19.91"
+                   viewBox="0 0 18.165 19.91">
+                <g id="Document"
+                   transform="translate(0)">
+                  <path id="Combined_Shape"
+                        data-name="Combined Shape"
+                        d="M0,14.553v-9.2A4.973,4.973,0,0,1,5.215,0l7.693,0a5.025,5.025,0,0,1,5.257,5.357v9.2a4.973,4.973,0,0,1-5.215,5.354l-7.693,0A5.025,5.025,0,0,1,0,14.553ZM5.22,1.5A3.494,3.494,0,0,0,1.5,5.357v9.2A3.5,3.5,0,0,0,5.256,18.41l7.689,0a3.494,3.494,0,0,0,3.721-3.854v-9.2A3.5,3.5,0,0,0,12.909,1.5ZM5.5,14.973a.75.75,0,1,1,0-1.5h7.22a.75.75,0,1,1,0,1.5Zm0-4.186a.75.75,0,1,1,0-1.5h7.22a.75.75,0,0,1,0,1.5Zm0-4.177a.75.75,0,0,1,0-1.5H8.251a.75.75,0,0,1,0,1.5Z"
+                        transform="translate(0 0)"
+                        fill="#aaa095" />
+                </g>
+              </svg>
+            </div>
+            <div class="title">
+              {{ title }}
+            </div>
+          </div>
+          <div class="item">
+            <div class="icon">
+              <svg id="Calendar"
+                   xmlns="http://www.w3.org/2000/svg"
+                   width="19.5"
+                   height="21.5"
+                   viewBox="0 0 19.5 21.5">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M5.521,21.5C2.115,21.5,0,19.4,0,16.023V6.973A5,5,0,0,1,4.965,1.6V.75a.75.75,0,0,1,1.5,0v.83h6.578V.75a.75.75,0,0,1,1.5,0V1.6a5.382,5.382,0,0,1,3.532,1.46A5.379,5.379,0,0,1,19.5,6.976V16.1c0,3.332-2.116,5.4-5.522,5.4ZM1.5,16.023C1.5,18.588,2.928,20,5.521,20h8.458C16.572,20,18,18.615,18,16.1V8.9H1.5ZM18,7.4V6.973a3.862,3.862,0,0,0-.987-2.855A3.867,3.867,0,0,0,14.543,3.1v.938a.75.75,0,1,1-1.5,0V3.08H6.466v.961a.75.75,0,0,1-1.5,0V3.1A3.486,3.486,0,0,0,1.5,6.973V7.4Zm-4.553,8.542a.746.746,0,0,1,.745-.75H14.2a.75.75,0,1,1-.754.75Zm-4.437,0a.746.746,0,0,1,.745-.75h.009a.75.75,0,1,1-.754.75Zm-4.448,0a.747.747,0,0,1,.746-.75h.009a.75.75,0,1,1-.755.75Zm8.885-3.886a.747.747,0,0,1,.745-.751H14.2a.75.75,0,1,1-.754.751Zm-4.437,0a.747.747,0,0,1,.745-.751h.009a.75.75,0,1,1-.754.751Zm-4.448,0a.747.747,0,0,1,.746-.751h.009a.75.75,0,1,1-.755.751Z"
+                      transform="translate(0 0)"
+                      fill="#aaa095" />
+              </svg>
+            </div>
+            <div class="title">
+              شروع:
+              {{ startingTime }}
+            </div>
+          </div>
+          <div class="item">
+            <div class="icon">
+              <svg id="Calendar"
+                   xmlns="http://www.w3.org/2000/svg"
+                   width="19.5"
+                   height="21.5"
+                   viewBox="0 0 19.5 21.5">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M5.521,21.5C2.115,21.5,0,19.4,0,16.023V6.973A5,5,0,0,1,4.965,1.6V.75a.75.75,0,0,1,1.5,0v.83h6.578V.75a.75.75,0,0,1,1.5,0V1.6a5.382,5.382,0,0,1,3.532,1.46A5.379,5.379,0,0,1,19.5,6.976V16.1c0,3.332-2.116,5.4-5.522,5.4ZM1.5,16.023C1.5,18.588,2.928,20,5.521,20h8.458C16.572,20,18,18.615,18,16.1V8.9H1.5ZM18,7.4V6.973a3.862,3.862,0,0,0-.987-2.855A3.867,3.867,0,0,0,14.543,3.1v.938a.75.75,0,1,1-1.5,0V3.08H6.466v.961a.75.75,0,0,1-1.5,0V3.1A3.486,3.486,0,0,0,1.5,6.973V7.4Zm-4.553,8.542a.746.746,0,0,1,.745-.75H14.2a.75.75,0,1,1-.754.75Zm-4.437,0a.746.746,0,0,1,.745-.75h.009a.75.75,0,1,1-.754.75Zm-4.448,0a.747.747,0,0,1,.746-.75h.009a.75.75,0,1,1-.755.75Zm8.885-3.886a.747.747,0,0,1,.745-.751H14.2a.75.75,0,1,1-.754.751Zm-4.437,0a.747.747,0,0,1,.745-.751h.009a.75.75,0,1,1-.754.751Zm-4.448,0a.747.747,0,0,1,.746-.751h.009a.75.75,0,1,1-.755.751Z"
+                      transform="translate(0 0)"
+                      fill="#aaa095" />
+              </svg>
+            </div>
+            <div class="title">
+              پایان:
+              {{ endingTime }}
+            </div>
+          </div>
+        </div>
+      </template>
+      <template #after-form-builder>
+        <div>
+          <div v-for="(question, questionIndex) in session.questions_info"
+               :key="questionIndex"
+               class="row">
+            <div class="col-12">
+              <div>
+                سوال:
+              </div>
+              <div v-html="question.text" />
+              <div v-if="question.attachment">
+                <q-btn color="primary"
+                       :href="question.attachment"
+                       target="_blank">ضمیمه سوال</q-btn>
+              </div>
+              <q-separator />
+              <div>
+                پاسخ صحیح:
+              </div>
+              <div v-html="question.correct_answer" />
+            </div>
+          </div>
+        </div>
+        <entity-show v-if="mounted && sessionAttendanceSheets.assignment_status !== null"
+                     :key="entityShowSessionAssignment"
+                     v-model:value="sessionAssignmentInfoInputs"
+                     title="پاسخ کاربر"
+                     :loaded-data="sessionAttendanceSheets"
+                     :default-layout="false"
+                     :show-expand-button="false"
+                     :show-edit-button="false"
+                     :show-index-button="false"
+                     :show-reload-button="false" />
+
+        <div class="flex justify-end">
+          <q-btn color="red"
+                 outline
+                 :loading="verifyLoading"
+                 class="q-mr-md"
+                 @click="notVerify">
+            تایید نشده
+          </q-btn>
+          <q-btn color="primary"
+                 :loading="verifyLoading"
+                 @click="verify">
+            تایید شده
+          </q-btn>
+        </div>
+      </template>
+    </entity-show>
+  </div>
+</template>
+
+<script>
+import { Session } from 'src/models/Session.js'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
+import { EntityShow } from 'quasar-crud'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import { SessionAttendanceSheets } from 'src/models/SessionAttendanceSheets'
+import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+
+export default {
+  name: 'UserPanel.Profile.SessionInfo',
+  components: {
+    EntityShow,
+    Breadcrumbs
+  },
+  data () {
+    const sessionId = parseInt(this.$route.params.session_id)
+    return {
+      classroom: new Classroom(),
+      session: new Session(),
+      sessionAttendanceSheets: new SessionAttendanceSheets(),
+      entityShowSessionAssignment: Date.now(),
+      mounted: false,
+      api: APIGateway.session.APIAdresses.byId(sessionId),
+      sessionInfoInputs: [
+        { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12' },
+        { type: 'separator', name: 'space', size: '1px', col: 'col-md-12' },
+        { type: 'inputEditor', name: 'syllabus', responseKey: 'syllabus', label: 'مقرری جلسه', col: 'col-md-12' },
+        { type: 'separator', name: 'space', size: '1px', col: 'col-md-12' },
+        { type: 'inputEditor', name: 'assignment_description', responseKey: 'assignment_description', label: 'تکلیف جلسه', col: 'col-md-12' },
+
+        { type: 'hidden', name: 'title', responseKey: 'title', label: 'نام جلسه', col: 'col-md-1' },
+        { type: 'hidden', name: 'beginning_time', responseKey: 'beginning_time', label: 'زمان شروع جلسه', col: 'col-md-1' },
+        { type: 'hidden', name: 'ending_time', responseKey: 'ending_time', label: 'زمان پایان جلسه', col: 'col-md-1' },
+        { type: 'hidden', name: 'id', responseKey: 'id', label: 'id', col: 'col-md-1' }
+      ],
+      verifyLoading: false,
+
+      sessionAssignmentInfoInputs: [
+        { type: 'InputEditor', name: 'answer_text', responseKey: 'answer_text', label: 'پاسخ کاربر', placeholder: ' ', col: 'col-md-12 col-12' },
+        { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'فایل ضمیمه', placeholder: ' ', col: 'col-md-3 col-12' },
+        { type: 'select', name: 'assignment_status', responseKey: 'assignment_status', label: 'وضعیت تکلیف', placeholder: ' ', options: (new SessionAttendanceSheets()).assignment_statusEnums, col: 'col-md-3 col-12' },
+        { type: 'dateTime', name: 'creation_time', responseKey: 'creation_time', label: 'زمان ارسال تکلیف', placeholder: ' ', col: 'col-md-6 col-12' }
+      ]
+    }
+  },
+  computed: {
+    title () {
+      return this.getSessionInfoInputValue('title')
+    },
+    endingTime () {
+      const data = this.getSessionInfoInputValue('ending_time')
+      if (!data) {
+        return '-'
+      }
+
+      return ShamsiDate.getDateTime(data)
+    },
+    startingTime () {
+      const data = this.getSessionInfoInputValue('beginning_time')
+      if (!data) {
+        return '-'
+      }
+
+      return ShamsiDate.getDateTime(data)
+    }
+  },
+  mounted() {
+    this.getSessionAttendanceSheetsInfo(this.$route.params.session_attendance_sheet_id)
+      .then(() => {
+        this.mounted = true
+      })
+      .catch(() => {
+      })
+  },
+  methods: {
+    updateBreadcrumbs () {
+      this.$store.commit('AppLayout/updateBreadcrumbs', {
+        visible: true,
+        loading: false,
+        path: [
+          {
+            label: 'دوره های آموزشی',
+            to: { name: 'Admin.Classroom.Index' }
+          },
+          {
+            label: this.classroom.title,
+            to: { name: 'Admin.Classroom.Show', params: { id: this.$route.params.classroom_id } }
+          },
+          {
+            label: 'لیست حضور و غیاب',
+            to: { name: 'Admin.Classroom.Session.AttendanceSheetList', params: { classroom_id: this.$route.params.classroom_id, session_id: this.$route.params.session_id } }
+          },
+          {
+            label: 'تکلیف',
+            to: { name: 'Admin.Classroom.Session.AttendanceSheet.Attendance', params: { classroom_id: this.$route.params.classroom_id, session_id: this.$route.params.session_id, session_attendance_sheet_id: this.$route.params.session_attendance_sheet_id } }
+          }
+        ]
+      })
+    },
+    getSessionInfoInputValue (name) {
+      const target = FormBuilderAssist.getInputsByName(this.sessionInfoInputs, name)
+      if (!target) {
+        return null
+      }
+
+      return target.value
+    },
+    beforeLoadInputData (data) {
+      this.session = new Session(data)
+      this.classroom.loading = true
+      this.getClassInfo(this.session.classroom)
+    },
+    getClassInfo (classroomId) {
+      this.classroom.loading = true
+      APIGateway.classroom.get(classroomId)
+        .then(classroom => {
+          this.classroom = new Classroom(classroom)
+          this.updateBreadcrumbs()
+          this.classroom.loading = false
+        })
+        .catch(() => {
+          this.classroom.loading = false
+        })
+    },
+    getSessionAttendanceSheetsInfo (sessionAttendanceSheetsId) {
+      return new Promise((resolve, reject) => {
+        this.sessionAttendanceSheets.loading = true
+        APIGateway.sessionAttendanceSheets.get(sessionAttendanceSheetsId)
+          .then(sessionAttendanceSheets => {
+            this.sessionAttendanceSheets = new SessionAttendanceSheets(sessionAttendanceSheets)
+            this.sessionAttendanceSheets.loading = false
+            resolve(this.sessionAttendanceSheets)
+          })
+          .catch(() => {
+            this.sessionAttendanceSheets.loading = false
+            reject()
+          })
+      })
+    },
+    verify () {
+      this.verifyLoading = true
+      APIGateway.sessionAttendanceSheets.verify(this.sessionAttendanceSheets.id)
+        .then(() => {
+          this.getSessionAttendanceSheetsInfo(this.$route.params.session_attendance_sheet_id)
+            .then(() => {
+              this.verifyLoading = false
+              this.entityShowSessionAssignment = Date.now()
+            })
+            .catch(() => {
+              this.verifyLoading = false
+            })
+        })
+        .catch(() => {
+          this.verifyLoading = false
+        })
+    },
+    notVerify () {
+      this.verifyLoading = true
+      APIGateway.sessionAttendanceSheets.notVerify(this.sessionAttendanceSheets.id)
+        .then(() => {
+          this.getSessionAttendanceSheetsInfo(this.$route.params.session_attendance_sheet_id)
+            .then(() => {
+              this.verifyLoading = false
+              this.entityShowSessionAssignment = Date.now()
+            })
+            .catch(() => {
+              this.verifyLoading = false
+            })
+        })
+        .catch(() => {
+          this.verifyLoading = false
+        })
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.entity-show {
+  .session-title-and-time-row {
+    background-color: #FCF9F4;
+    margin: -16px;
+    width: calc( 100% + 32px );
+    max-width: calc( 100% + 32px );
+    display: flex;
+    flex-flow: row;
+    justify-content: flex-start;
+    align-items: center;
+    padding: 16px;
+    border-bottom: solid 1px #DFE1EC;
+    margin-bottom: 24px;
+    .item {
+      display: flex;
+      flex-flow: row;
+      justify-content: flex-start;
+      width: 25%;
+      align-items: center;
+      .icon {
+        padding-right: 11px;
+      }
+      .title {
+        padding-right: 11px;
+      }
+    }
+  }
+}
+</style>

+ 21 - 0
src/components/Widgets/SessionAttendanceSheet/AdminSessionAttendanceSheetShowAssignment/OptionPanel.vue

@@ -0,0 +1,21 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      ...
+    </template>
+  </option-panel-tabs>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
+import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
+
+export default defineComponent({
+  name: 'OptionPanel',
+  components: {
+    OptionPanelTabs
+  },
+  mixins: [mixinOptionPanel]
+})
+</script>

+ 217 - 0
src/components/Widgets/SessionAttendanceSheet/AdminSessionAttendanceSheetShowAttendance/AdminSessionAttendanceSheetShowAttendance.vue

@@ -0,0 +1,217 @@
+<template>
+  <div class="AdminContentList"
+       :style="localOptions.style">
+    <breadcrumbs class="q-mb-xl"
+                 style="margin-top: 29px; margin-bottom: 19px;" />
+    <div class="flex justify-end">
+      <q-btn flat
+             color="grey"
+             @click="$router.go(-1)">
+        بازگشت
+        >
+      </q-btn>
+    </div>
+    <q-inner-loading :showing="loading">
+      <q-spinner-ball color="primary"
+                      size="50px" />
+    </q-inner-loading>
+    <entity-create v-if="mounted"
+                   ref="entityCreateSubmitAttendanceStatus"
+                   v-model:value="submitAttendanceStatusInputs"
+                   :api="submitAttendanceStatusApi"
+                   :default-layout="false"
+                   :show-expand-button="false"
+                   :show-edit-button="false"
+                   :show-index-button="false"
+                   :show-reload-button="false" />
+  </div>
+</template>
+
+<script>
+import { shallowRef } from 'vue'
+import { EntityCreate } from 'quasar-crud'
+import { Session } from 'src/models/Session.js'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
+import BtnControl from 'src/components/Control/btn.vue'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import { SessionAttendanceSheets } from 'src/models/SessionAttendanceSheets.js'
+
+const BtnControlComp = shallowRef(BtnControl)
+
+export default {
+  name: 'AdminSessionAttendanceSheetList',
+  components: { EntityCreate, Breadcrumbs },
+  mixins: [mixinWidget],
+  data () {
+    const sessionId = parseInt(this.$route.params.session_id)
+    return {
+      classroom: new Classroom(),
+      session: new Session(),
+      sessionAttendanceSheets: new SessionAttendanceSheets(),
+      loading: false,
+      submitAttendanceStatusInputsReadPart: {
+        type: 'formBuilder',
+        name: 'is_read_part',
+        value: [
+          { type: 'separator', name: 'space', label: 'مطالعه', className: 'custom-separator', col: 'col-12' },
+          { type: 'checkbox', name: 'is_read_part1', responseKey: 'is_read_part1', value: false, label: 'نیمه اول را مطالعه کردم', col: 'col-md-12' },
+          { type: 'checkbox', name: 'is_read_part2', responseKey: 'is_read_part2', value: false, label: 'نیمه دوم را مطالعه کردم', col: 'col-md-12' }
+        ],
+        col: 'col-md-4 col-12'
+      },
+      submitAttendanceStatusInputs: [
+        {
+          type: 'formBuilder',
+          name: 'is_present_listen_part',
+          value: [
+            { type: 'separator', name: 'space', label: 'حضور', className: 'custom-separator', col: 'col-12' },
+            { type: 'checkbox', name: 'is_present_listen_part1', responseKey: 'is_present_listen_part1', value: false, label: 'حضور نیمه اول', col: 'col-md-12' },
+            { type: 'checkbox', name: 'is_present_listen_part2', responseKey: 'is_present_listen_part2', value: false, label: 'حضور نیمه دوم', col: 'col-md-12' }
+          ],
+          col: 'col-md-4 col-12'
+        },
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'ثبت حضور و غیاب', placeholder: ' ', customClass: 'flex justify-end', atClick: this.submitAttendanceStatus, col: 'col-12' },
+        { type: 'hidden', name: 'session', responseKey: 'session', value: sessionId },
+        { type: 'hidden', name: 'owner', responseKey: 'owner', value: null }
+      ],
+      submitAttendanceStatusApi: APIGateway.sessionAttendanceSheets.APIAdresses.base,
+      mounted: false
+    }
+  },
+  mounted() {
+    this.getClassroomAndSession()
+  },
+  methods: {
+    getClassroomAndSession () {
+      const sessionId = parseInt(this.$route.params.session_id)
+      const classroomId = parseInt(this.$route.params.classroom_id)
+      const sessionAttendanceSheetsId = parseInt(this.$route.params.session_attendance_sheet_id)
+      this.loading = true
+      Promise.all([this.getClassInfo(classroomId), this.getSessionInfo(sessionId), this.getSessionAttendanceSheetsInfo(sessionAttendanceSheetsId)])
+        .then(() => {
+          this.updateBreadcrumbs()
+          this.updateAttendanceInputs()
+          this.mounted = true
+          this.loading = false
+        })
+        .catch(() => {
+          this.loading = false
+        })
+    },
+    updateAttendanceInputs () {
+      const isDefinedSyllabus = this.session.is_defined_syllabus
+      const classroomHoldingType = this.classroom.holding_type
+      FormBuilderAssist.setAttributeByName(this.submitAttendanceStatusInputs, 'owner', 'value', this.sessionAttendanceSheets.owner_info.id)
+      this.updateAttendancePresentInputs(classroomHoldingType)
+      this.updateAttendanceReadInputs(isDefinedSyllabus)
+    },
+    updateAttendanceReadInputs (isDefinedSyllabus) {
+      if (isDefinedSyllabus) {
+        this.submitAttendanceStatusInputs.unshift(this.submitAttendanceStatusInputsReadPart)
+      } else {
+        this.submitAttendanceStatusInputs.unshift({ type: 'hidden', name: 'is_read_part1', value: null })
+        this.submitAttendanceStatusInputs.unshift({ type: 'hidden', name: 'is_read_part2', value: null })
+      }
+    },
+    updateAttendancePresentInputs (classroomHoldingType) {
+      if (classroomHoldingType === 'OFFLINE') {
+        FormBuilderAssist.setAttributeByName(this.submitAttendanceStatusInputs, 'is_present_listen_part1', 'label', 'صوت نیمه اول')
+        FormBuilderAssist.setAttributeByName(this.submitAttendanceStatusInputs, 'is_present_listen_part2', 'label', 'صوت نیمه دوم')
+      }
+    },
+    submitAttendanceStatus () {
+      this.loading = true
+      this.$refs.entityCreateSubmitAttendanceStatus.createEntity(false)
+        .then(() => {
+          this.loading = false
+        })
+        .catch(() => {
+          this.loading = false
+        })
+    },
+    updateBreadcrumbs () {
+      this.$store.commit('AppLayout/updateBreadcrumbs', {
+        visible: true,
+        loading: false,
+        path: [
+          {
+            label: 'دوره های آموزشی',
+            to: { name: 'Admin.Classroom.Index' }
+          },
+          {
+            label: this.classroom.title,
+            to: { name: 'Admin.Classroom.Show', params: { id: this.$route.params.classroom_id } }
+          },
+          {
+            label: 'لیست حضور و غیاب',
+            to: { name: 'Admin.Classroom.Session.AttendanceSheetList', params: { classroom_id: this.$route.params.classroom_id, session_id: this.$route.params.session_id } }
+          },
+          {
+            label: 'حضور و غیاب',
+            to: { name: 'Admin.Classroom.Session.AttendanceSheet.Attendance', params: { classroom_id: this.$route.params.classroom_id, session_id: this.$route.params.session_id, session_attendance_sheet_id: this.$route.params.session_attendance_sheet_id } }
+          }
+        ]
+      })
+    },
+    getClassInfo (classroomId) {
+      return new Promise((resolve, reject) => {
+        this.classroom.loading = true
+        APIGateway.classroom.get(classroomId)
+          .then(classroom => {
+            this.classroom = new Classroom(classroom)
+            this.classroom.loading = false
+            resolve(this.classroom)
+          })
+          .catch(() => {
+            this.classroom.loading = false
+            reject()
+          })
+      })
+    },
+    getSessionInfo (sessionId) {
+      return new Promise((resolve, reject) => {
+        this.session.loading = true
+        APIGateway.session.get(sessionId)
+          .then(session => {
+            this.session = new Session(session)
+            this.session.loading = false
+            resolve(this.session)
+          })
+          .catch(() => {
+            this.session.loading = false
+            reject()
+          })
+      })
+    },
+    getSessionAttendanceSheetsInfo (sessionAttendanceSheetsId) {
+      return new Promise((resolve, reject) => {
+        this.sessionAttendanceSheets.loading = true
+        APIGateway.sessionAttendanceSheets.get(sessionAttendanceSheetsId)
+          .then(sessionAttendanceSheets => {
+            this.sessionAttendanceSheets = new SessionAttendanceSheets(sessionAttendanceSheets)
+            this.sessionAttendanceSheets.loading = false
+            resolve(this.sessionAttendanceSheets)
+          })
+          .catch(() => {
+            this.sessionAttendanceSheets.loading = false
+            reject()
+          })
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.AdminContentList {
+  .more-action {
+    display: flex;
+    flex-flow: row;
+    justify-content: flex-end;
+    margin-bottom: 10px;
+  }
+}
+</style>

+ 21 - 0
src/components/Widgets/SessionAttendanceSheet/AdminSessionAttendanceSheetShowAttendance/OptionPanel.vue

@@ -0,0 +1,21 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      ...
+    </template>
+  </option-panel-tabs>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
+import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
+
+export default defineComponent({
+  name: 'OptionPanel',
+  components: {
+    OptionPanelTabs
+  },
+  mixins: [mixinOptionPanel]
+})
+</script>

+ 4 - 3
src/models/Session.js

@@ -16,10 +16,11 @@ class Session extends Model {
       { key: 'description' },
       { key: 'beginning_time' },
       { key: 'ending_time' },
-      { key: 'question' },
-      { key: 'question_info' },
+      { key: 'questions' },
+      { key: 'questions_info' },
       { key: 'creation_time' },
-      { key: 'last_modification_time' }
+      { key: 'last_modification_time' },
+      { key: 'current_user_attendance_sheet' }
     ])
   }
 }

+ 11 - 1
src/models/SessionAttendanceSheets.js

@@ -6,13 +6,23 @@ class SessionAttendanceSheets extends Model {
       { key: 'id' },
       { key: 'is_answer_verified' },
       { key: 'answer_status' },
+      { key: 'answer_text' },
+      { key: 'answer_attachment' },
+      { key: 'answer_info' },
       { key: 'session' },
       { key: 'assignment_status' },
       { key: 'attendance_status' },
       { key: 'is_listen_part1' },
       { key: 'is_listen_part2' },
       { key: 'is_present_read_part1' },
-      { key: 'is_present_read_part2' }
+      { key: 'is_present_read_part2' },
+      { key: 'owner' },
+      { key: 'owner_info' },
+      { key: 'creator' },
+      { key: 'creator_info' },
+      { key: 'session' },
+      { key: 'session_info' },
+      { key: 'creation_time' }
     ], {
       status: {
         infoKey: 'is_answer_verified_info',

+ 23 - 0
src/pages/Admin/Classroom/SessionActivitySheets/Assignment.vue

@@ -0,0 +1,23 @@
+<template>
+  <q-page-builder v-model:sections="currenSections"
+                  v-model:options="pageConfig"
+                  :editable="pageBuilderEditable"
+                  :loading="pageBuilderLoading" />
+</template>
+
+<script>
+import { mixinPageOptions } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'Admin.Content.Show',
+  mixins: [mixinPageOptions],
+  data: () => {
+    return {
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'AdminSessionAttendanceSheetShowAssignment' }], options: { style: { paddingTop: '' } } }], options: { className: '', height: 'auto', boxed: true, boxedWidth: 1200, gutterXSize: 'md', gutterYSize: 'md', absolute: 'none', paddingOfBoxedInFullWidth: '10px', alignment: { justifyContent: { global: null, xl: null, lg: null, md: null, sm: null, xs: null }, alignItems: { global: null, xl: null, lg: null, md: null, sm: null, xs: null }, alignContent: { global: null, xl: null, lg: null, md: null, sm: null, xs: null } }, style: { maxWidth: '1200px', width: '1200px' } } }] } }]
+    }
+  },
+  created() {
+    this.currenSections = this.sections
+  }
+}
+</script>

+ 23 - 0
src/pages/Admin/Classroom/SessionActivitySheets/Attendance.vue

@@ -0,0 +1,23 @@
+<template>
+  <q-page-builder v-model:sections="currenSections"
+                  v-model:options="pageConfig"
+                  :editable="pageBuilderEditable"
+                  :loading="pageBuilderLoading" />
+</template>
+
+<script>
+import { mixinPageOptions } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'Admin.Content.Show',
+  mixins: [mixinPageOptions],
+  data: () => {
+    return {
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'AdminSessionAttendanceSheetShowAttendance' }], options: { style: { paddingTop: '' } } }], options: { className: '', height: 'auto', boxed: true, boxedWidth: 1200, gutterXSize: 'md', gutterYSize: 'md', absolute: 'none', paddingOfBoxedInFullWidth: '10px', alignment: { justifyContent: { global: null, xl: null, lg: null, md: null, sm: null, xs: null }, alignItems: { global: null, xl: null, lg: null, md: null, sm: null, xs: null }, alignContent: { global: null, xl: null, lg: null, md: null, sm: null, xs: null } }, style: { maxWidth: '1200px', width: '1200px' } } }] } }]
+    }
+  },
+  created() {
+    this.currenSections = this.sections
+  }
+}
+</script>

+ 0 - 0
src/pages/Admin/Classroom/sessionActivitySheets.vue → src/pages/Admin/Classroom/SessionActivitySheets/sessionActivitySheets.vue


+ 24 - 6
src/pages/User/Profile/sessionInfo.vue

@@ -89,7 +89,8 @@
           <q-spinner-ball color="primary"
                           size="50px" />
         </q-inner-loading>
-        <entity-create v-if="submitAttendanceStatusMounted"
+        <q-separator class="q-my-lg" />
+        <entity-create v-if="submitAttendanceStatusMounted && session.current_user_attendance_sheet?.assignment_status === null"
                        ref="entityCreateSubmitAssignment"
                        v-model:value="submitAssignmentInputs"
                        :api="submitAssignmentApi"
@@ -98,6 +99,15 @@
                        :show-edit-button="false"
                        :show-index-button="false"
                        :show-reload-button="false" />
+        <entity-show v-if="submitAttendanceStatusMounted && session.current_user_attendance_sheet?.assignment_status !== null"
+                     v-model:value="sessionAssignmentInfoInputs"
+                     title="پاسخ کاربر"
+                     :loaded-data="session.current_user_attendance_sheet"
+                     :default-layout="false"
+                     :show-expand-button="false"
+                     :show-edit-button="false"
+                     :show-index-button="false"
+                     :show-reload-button="false" />
         <q-separator class="q-my-lg" />
         <entity-create v-if="mounted"
                        ref="entityCreateSubmitAttendanceStatus"
@@ -123,6 +133,7 @@ import { EntityShow, EntityCreate } from 'quasar-crud'
 import BtnControl from 'src/components/Control/btn.vue'
 import { FormBuilderAssist } from 'quasar-form-builder'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import { SessionAttendanceSheets } from 'src/models/SessionAttendanceSheets.js'
 
 const BtnControlComp = shallowRef(BtnControl)
 
@@ -140,7 +151,7 @@ export default {
       session: new Session(),
       mounted: false,
       submitAttendanceStatusMounted: false,
-      api: APIGateway.session.APIAdresses.byId(sessionId),
+      api: APIGateway.session.APIAdresses.sessionAndCurrentUserAttendanceSheet(sessionId),
       sessionInfoInputs: [
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12' },
         { type: 'separator', name: 'space', size: '1px', col: 'col-md-12' },
@@ -182,12 +193,19 @@ export default {
 
       submitAssignmentLoading: false,
       submitAssignmentInputs: [
-        { type: 'InputEditor', name: 'answer_text', responseKey: 'answer_text', label: 'پاسخ به تکلیف', col: 'col-md-12 col-12' },
-        { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'انتخاب فایل ضمیمه', col: 'col-md-3 col-12' },
+        { type: 'InputEditor', name: 'answer_text', responseKey: 'answer_text', label: 'پاسخ به تکلیف', placeholder: ' ', col: 'col-md-12 col-12' },
+        { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'انتخاب فایل ضمیمه', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'ارسال تکلیف', customClass: 'flex justify-end', atClick: this.submitAssignment, col: 'col-md-9 col-12' },
         { type: 'hidden', name: 'session', responseKey: 'session', value: sessionId }
       ],
-      submitAssignmentApi: APIGateway.sessionAttendanceSheets.APIAdresses.submitAssignment
+      submitAssignmentApi: APIGateway.sessionAttendanceSheets.APIAdresses.submitAssignment,
+
+      sessionAssignmentInfoInputs: [
+        { type: 'InputEditor', name: 'answer_text', responseKey: 'answer_text', label: 'پاسخ کاربر', placeholder: ' ', col: 'col-md-12 col-12' },
+        { type: 'file', name: 'answer_attachment', responseKey: 'answer_attachment', label: 'فایل ضمیمه', placeholder: ' ', col: 'col-md-3 col-12' },
+        { type: 'select', name: 'assignment_status', responseKey: 'assignment_status', label: 'وضعیت تکلیف', placeholder: ' ', options: (new SessionAttendanceSheets()).assignment_statusEnums, col: 'col-md-3 col-12' },
+        { type: 'dateTime', name: 'creation_time', responseKey: 'creation_time', label: 'زمان ارسال تکلیف', placeholder: ' ', col: 'col-md-6 col-12' }
+      ]
     }
   },
   computed: {
@@ -292,6 +310,7 @@ export default {
     beforeLoadInputData (data) {
       this.session = new Session(data)
       this.classroom.loading = true
+      this.submitAttendanceStatusMounted = true
       this.getClassInfo(this.session.classroom)
     },
     getClassInfo (classroomId) {
@@ -301,7 +320,6 @@ export default {
           this.classroom = new Classroom(classroom)
           this.updateBreadcrumbs()
           this.classroom.loading = false
-          this.submitAttendanceStatusMounted = true
           this.updateAttendanceInputs(this.session.is_defined_syllabus, this.classroom.holding_type)
         })
         .catch(() => {

+ 3 - 3
src/router/routes.js

@@ -557,9 +557,9 @@ const routes = [
               { name: 'Admin.Classroom.Index', path: '', component: () => import('src/pages/Admin/Classroom/index.vue') },
               { name: 'Admin.Classroom.Create', path: 'create', component: () => import('src/pages/Admin/Classroom/create.vue'), breadcrumbs: { title: 'ساخت دوره آموزشی' } },
               { name: 'Admin.Classroom.Show', path: ':id', component: () => import('src/pages/Admin/Classroom/show.vue') },
-              { name: 'Admin.Classroom.Session.AttendanceSheetList', path: ':classroom_id/session/:session_id/session_attendance_sheets', component: () => import('src/pages/Admin/Classroom/sessionActivitySheets.vue') },
-              { name: 'Admin.Classroom.Session.AttendanceSheet.Assignment', path: ':classroom_id/session/:session_id/session_attendance_sheet/:session_attendance_sheet_id/assignment', component: () => import('src/pages/Admin/Classroom/sessionActivitySheets.vue') },
-              { name: 'Admin.Classroom.Session.AttendanceSheet.Attendance', path: ':classroom_id/session/:session_id/session_attendance_sheet/:session_attendance_sheet_id/attendance', component: () => import('src/pages/Admin/Classroom/sessionActivitySheets.vue') },
+              { name: 'Admin.Classroom.Session.AttendanceSheetList', path: ':classroom_id/session/:session_id/session_attendance_sheets', component: () => import('pages/Admin/Classroom/SessionActivitySheets/sessionActivitySheets.vue') },
+              { name: 'Admin.Classroom.Session.AttendanceSheet.Assignment', path: ':classroom_id/session/:session_id/session_attendance_sheet/:session_attendance_sheet_id/assignment', component: () => import('pages/Admin/Classroom/SessionActivitySheets/Assignment.vue') },
+              { name: 'Admin.Classroom.Session.AttendanceSheet.Attendance', path: ':classroom_id/session/:session_id/session_attendance_sheet/:session_attendance_sheet_id/attendance', component: () => import('pages/Admin/Classroom/SessionActivitySheets/Attendance.vue') },
               { name: 'Admin.Classroom.Project.Create', path: ':classroom_id/project/create', component: () => import('src/pages/Admin/Classroom/project/create.vue'), breadcrumbs: { title: 'ساخت دوره آموزشی' } },
               { name: 'Admin.Classroom.Project.Show', path: ':classroom_id/project/:project_id', component: () => import('src/pages/Admin/Classroom/project/show.vue') },
               { name: 'Admin.Classroom.Project.Members', path: ':classroom_id/project/:project_id/members', component: () => import('src/pages/Admin/Classroom/project/members.vue') },