ali 2 سال پیش
والد
کامیت
4346a35096

+ 15 - 0
src/api/models/Classroom.js

@@ -18,6 +18,7 @@ export default class ClassroomAPI extends APIRepository {
       register: (id) => '/lma/classrooms/' + id + '/register',
       leaders: (id) => '/lma/classrooms/' + id + '/leaders',
       announceResult: (id) => '/lma/classrooms/' + id + '/announce_result',
+      getClassroomAndCorrespondingRegistration: (classroomId, ownerId) => '/lma/classrooms/' + classroomId + '/get_classroom_and_corresponding_registration/' + ownerId,
       members: '/lma/registrations',
       activitySheet: '/lma/registrations/activity_sheet',
       enrollByAdmin: (classroomId, userId) => '/lma/classrooms/' + classroomId + '/enrolll?user_id=' + userId,
@@ -77,6 +78,20 @@ export default class ClassroomAPI extends APIRepository {
     })
   }
 
+  getClassroomAndCorrespondingRegistration (classroomId, ownerId) {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.getClassroomAndCorrespondingRegistration(classroomId, ownerId),
+      resolveCallback: (response) => {
+        return new Classroom(response.data)
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+
   register (id) {
     return this.sendRequest({
       apiMethod: 'put',

+ 968 - 0
src/components/Widgets/Classroom/AdminClassroomInfoOfUser/AdminClassroomInfoOfUser.vue

@@ -0,0 +1,968 @@
+<template>
+  <breadcrumbs 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-card class="q-mb-md q-pa-sm">
+    <q-linear-progress v-show="ownerInfo.loading"
+                       indeterminate />
+    <div class="row items-center justify-center">
+      <div class="col-md-1">
+        <q-img :src="ownerInfo.picture" />
+      </div>
+      <div class="col-md-11">
+        <div class="row items-center">
+          <div class="col-md-4">
+            {{ ownerInfo.gender === 'MALE' ? 'آقای' : 'خانم' }}
+            {{ ownerInfo.fullname() }}
+            <br>
+            کد ملی:
+            {{ ownerInfo.national_code }}
+          </div>
+          <div class="col-md-4">
+            تلفن همراه جهت تماس:
+            {{ ownerInfo.mobile_number }}
+            <br>
+            ایمیل:
+            {{ ownerInfo.email }}
+          </div>
+          <div class="col-md-4">
+            <q-btn color="primary"
+                   :to="{ name: 'Admin.User.Show', params: { id: ownerId }}"
+                   target="_blank">
+              مشاهده پروفایل
+            </q-btn>
+          </div>
+        </div>
+      </div>
+    </div>
+  </q-card>
+  <q-card>
+    <q-tabs v-model="tab"
+            dense
+            class="text-grey"
+            active-color="primary"
+            indicator-color="primary"
+            align="justify"
+            narrow-indicator>
+      <q-tab name="classroomInfo"
+             label="اطلاعات دوره آموزشی" />
+      <q-tab v-if="canShowOtherTabsOfClassroom"
+             name="educations"
+             label="جدول آموزشی" />
+      <q-tab v-if="canShowOtherTabsOfClassroom"
+             name="team"
+             label="گروه" />
+      <q-tab v-if="canShowOtherTabsOfClassroom"
+             name="tests"
+             label="آزمون" />
+      <q-tab v-if="canShowOtherTabsOfClassroom"
+             name="projects"
+             label="پروژه" />
+      <q-tab v-if="canShowOtherTabsOfClassroom && classroom.live_streaming_url"
+             name="live_streaming_url"
+             label="بخش آنلاین" />
+    </q-tabs>
+
+    <q-separator />
+
+    <q-tab-panels v-model="tab"
+                  animated>
+      <q-tab-panel name="classroomInfo">
+        <div v-if="mounted && classroom.current_user_register_info.transcript_sheet_info.status"
+             class="bg-light-green-1 q-pa-md q-mb-md">
+          <div class="row q-col-gutter-md">
+            <div class="col-md-6 col-12">
+              نمره حضور و غیاب: {{ classroom.current_user_register_info.transcript_sheet_info.attendance_score }}
+              <br>
+              نمره آزمون: {{ classroom.current_user_register_info.transcript_sheet_info.highest_test_score }}
+              <br>
+              نمره نهایی: {{ classroom.current_user_register_info.transcript_sheet_info.final_score }}
+            </div>
+            <div class="col-md-6 col-12">
+              وضعیت نهایی: {{ getTranscriptSheetStatus(classroom.current_user_register_info.transcript_sheet_info) }}
+              <br>
+              <q-badge :color="classroom.current_user_register_info.transcript_sheet_info.is_sent_by_post ? 'green' : 'red'"
+                       outline>
+                <template v-if="classroom.current_user_register_info.transcript_sheet_info.is_sent_by_post">
+                  ارسال شده با پست
+                </template>
+                <template v-else>
+                  پست نشده است
+                </template>
+              </q-badge>
+              <br>
+              <a v-if="classroom.current_user_register_info.transcript_sheet_info.certification"
+                 :href="classroom.current_user_register_info.transcript_sheet_info.certification"
+                 target="_blank">
+                <q-btn outline
+                       color="primary">
+                  دانلود مدرک
+                </q-btn>
+              </a>
+            </div>
+          </div>
+        </div>
+        <show-classroom-info v-if="classroom.id"
+                             :options="{ profileMode: true }"
+                             :classroom="classroom"
+                             @onDrop="getClassroom" />
+        <q-skeleton v-else
+                    type="circle"
+                    size="100px"
+                    class="q-mt-md" />
+      </q-tab-panel>
+
+      <q-tab-panel name="educations"
+                   class="q-pa-none">
+        <entity-index v-if="mounted"
+                      ref="entityIndex"
+                      v-model:value="sessionsInputs"
+                      title="لیست جلسات"
+                      :api="sessionsApi"
+                      :table="sessionsTable"
+                      :table-keys="sessionsTableKeys"
+                      :table-grid-size="$q.screen.lt.sm"
+                      :show-expand-button="false"
+                      :show-reload-button="false"
+                      :show-search-button="false">
+          <template #entity-index-table-cell="{inputData}">
+            <template v-if="inputData.col.name === 'number'">
+              {{ inputData.rowNumber }}
+            </template>
+            <template v-else-if="inputData.col.name === 'action'">
+              <q-btn size="md"
+                     color="primary"
+                     label="جزییات"
+                     :to="{name: 'UserPanel.Profile.SessionInfo', params: {id: inputData.props.row.id}}" />
+            </template>
+            <template v-else>
+              {{ inputData.col.value }}
+            </template>
+          </template>
+          <template #entity-index-table-item-cell="{inputData}">
+            <entity-index-grid-item :input-data="inputData">
+              <template #col="{col, row}">
+                <template v-if="col.name === 'number'">
+                  {{ inputData.rowNumber }}
+                </template>
+                <template v-else-if="col.name === 'action'">
+                  <q-btn size="md"
+                         color="primary"
+                         label="جزییات"
+                         :to="{name: 'UserPanel.Profile.SessionInfo', params: {id: row.id}}" />
+                </template>
+              </template>
+            </entity-index-grid-item>
+          </template>
+        </entity-index>
+      </q-tab-panel>
+
+      <q-tab-panel name="team"
+                   class="q-pa-none">
+        <entity-index v-if="mounted"
+                      ref="teamsList"
+                      v-model:value="teamsListInputs"
+                      title="لیست گروه ها"
+                      :api="teamsListApi"
+                      :table="teamsListTable"
+                      :table-keys="teamsListTableKeys"
+                      :show-search-button="false"
+                      :show-reload-button="false"
+                      :show-expand-button="false">
+          <template v-slot:entity-index-table-cell="{inputData}">
+            <template v-if="inputData.col.name === 'number'">
+              {{ inputData.rowNumber }}
+            </template>
+            <template v-else-if="inputData.col.name === 'action'">
+              <div class="action-column-entity-index">
+                <q-btn size="md"
+                       color="primary"
+                       :flat="inputData.props.row.join_status !== 'ENABLED'"
+                       :disable="inputData.props.row.join_status !== 'ENABLED'"
+                       :label="getTeamJoinStatusLabel(inputData.props.row)"
+                       class="q-mr-md"
+                       :loading="joinTeamLoading"
+                       @click="joinTeam(inputData.props.row)" />
+              </div>
+            </template>
+            <template v-else>
+              {{ inputData.col.value }}
+            </template>
+          </template>
+        </entity-index>
+        <q-skeleton v-else
+                    type="rect"
+                    height="200px" />
+      </q-tab-panel>
+
+      <q-tab-panel name="tests"
+                   class="q-pa-none">
+        <entity-index v-if="mounted"
+                      ref="entityIndex"
+                      v-model:value="testListInputs"
+                      title="آزمون ها"
+                      :api="testListApi"
+                      :table="testListTable"
+                      :table-keys="testListTableKeys"
+                      :table-grid-size="$q.screen.lt.sm"
+                      :show-expand-button="false"
+                      :show-reload-button="false"
+                      :show-search-button="false">
+          <template #entity-index-table-cell="{inputData}">
+            <template v-if="inputData.col.name === 'number'">
+              {{ inputData.rowNumber }}
+            </template>
+            <template v-else-if="inputData.col.name === 'action'">
+              <q-btn v-if="inputData.props.row.is_enabled_attending"
+                     size="md"
+                     color="primary"
+                     label="شرکت در آزمون"
+                     :to="{name: 'UserPanel.Test.AnswerBook.Confirmation', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}" />
+              <q-btn v-if="inputData.props.row.is_enabled_continuing"
+                     size="md"
+                     color="primary"
+                     label="ادامه آزمون"
+                     @click="showParticipateTypeDialog(inputData.props.row.test, inputData.props.row.id)" />
+              <q-btn v-if="inputData.props.row.is_enabled_objecting"
+                     size="md"
+                     color="primary"
+                     label="ثبت اعتراض"
+                     :to="{name: 'UserPanel.Test.AnswerBook.Show', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}, query: { send_objection: 1}}" />
+              <q-btn v-if="inputData.props.row.is_enabled_viewing"
+                     size="md"
+                     color="primary"
+                     label="مشاهده"
+                     :to="{name: 'UserPanel.Test.AnswerBook.Show', params: {test_id: inputData.props.row.test, answer_book_id: inputData.props.row.id}}" />
+            </template>
+            <template v-else>
+              {{ inputData.col.value }}
+            </template>
+          </template>
+          <template #entity-index-table-item-cell="{inputData}">
+            <entity-index-grid-item :input-data="inputData">
+              <template #col="{col, row}">
+                <template v-if="col.name === 'number'">
+                  {{ inputData.rowNumber }}
+                </template>
+                <template v-else-if="col.name === 'action'">
+                  <q-btn v-if="row.is_enabled_attending"
+                         size="md"
+                         color="primary"
+                         label="شرکت در آزمون"
+                         :to="{name: 'UserPanel.Test.AnswerBook.Confirmation', params: {test_id: row.test, answer_book_id: row.id}}" />
+                  <q-btn v-if="row.is_enabled_continuing"
+                         size="md"
+                         color="primary"
+                         label="ادامه آزمون"
+                         @click="showParticipateTypeDialog(row.test, row.id)" />
+                  <q-btn v-if="row.is_enabled_objecting"
+                         size="md"
+                         color="primary"
+                         label="ثبت اعتراض"
+                         :to="{name: 'UserPanel.Test.AnswerBook.Show', params: {test_id: row.test, answer_book_id: row.id}, query: { send_objection: 1}}" />
+                  <q-btn v-if="row.is_enabled_viewing"
+                         size="md"
+                         color="primary"
+                         label="مشاهده"
+                         :to="{name: 'UserPanel.Test.AnswerBook.Show', params: {test_id: row.test, answer_book_id: row.id}}" />
+                </template>
+              </template>
+            </entity-index-grid-item>
+          </template>
+        </entity-index>
+        <q-skeleton v-else
+                    type="rect"
+                    height="200px" />
+        <q-dialog v-model="participateTypeDialog">
+          <q-card>
+            <q-card-actions>
+              نحوه پاسخ به آزمون
+            </q-card-actions>
+            <q-separator />
+            <q-card-actions>
+              <div>
+                اندیشه جوی عزیز لطفا نحوه پاسخ به سوالات آزمون را مشخص کنید
+              </div>
+              <div>
+                <q-radio v-model="participateType"
+                         val="UserPanel.Test.AnswerBook.Participate.AllQuestions"
+                         label="مایلم پاسخ سوالات را به صورت یک فایل جامع بارگذاری کنم" />
+                <q-radio v-model="participateType"
+                         val="UserPanel.Test.AnswerBook.Participate.SingleQuestion"
+                         label="مایلم پاسخ سوالات را به صورت جداگانه ذیل همان سوال وارد کنم" />
+              </div>
+            </q-card-actions>
+            <q-card-actions>
+              <q-btn v-close-popup
+                     outline
+                     label="انصراف از آزمون"
+                     color="red" />
+              <q-btn :disable="!participateType"
+                     label="تایید و شروع آزمون"
+                     color="primary"
+                     @click="startTest" />
+            </q-card-actions>
+          </q-card>
+        </q-dialog>
+      </q-tab-panel>
+
+      <q-tab-panel name="projects"
+                   class="q-pa-none">
+        <entity-index v-if="mounted"
+                      ref="projectList"
+                      v-model:value="projectListInputs"
+                      title="لیست پروژه ها"
+                      :api="projectListApi"
+                      :table="projectListTable"
+                      :table-keys="projectListTableKeys"
+                      :show-search-button="false"
+                      :show-reload-button="false"
+                      :show-expand-button="false">
+          <template v-slot:entity-index-table-cell="{inputData}">
+            <template v-if="inputData.col.name === 'number'">
+              {{ inputData.rowNumber }}
+            </template>
+            <template v-else-if="inputData.col.name === 'actions'">
+              <div class="action-column-entity-index">
+                <q-btn size="md"
+                       color="primary"
+                       label="جزییات"
+                       :to="{name: 'UserPanel.Profile.Classroom.Project.Show', params: {classroom_id: classroomId, project_id: inputData.props.row.id}}"
+                       class="q-mr-md" />
+              </div>
+            </template>
+            <template v-else>
+              {{ inputData.col.value }}
+            </template>
+          </template>
+        </entity-index>
+        <q-skeleton v-else
+                    type="rect"
+                    height="200px" />
+      </q-tab-panel>
+      <q-tab-panel v-if="classroom.live_streaming_url"
+                   name="live_streaming_url">
+        <q-btn class="full-width q-mb-lg"
+               color="primary"
+               @click="goToLiveStreamUrl">
+          رفتن به کلاس
+        </q-btn>
+        <q-banner>
+          <div class="q-mb-md">
+            نام کاربری اسکای روم:
+            {{ user.national_code }}
+          </div>
+          <div>
+            کلمه عبور اسکای روم:
+            1234
+          </div>
+        </q-banner>
+        <iframe :src="classroom.live_streaming_url"
+                title="W3Schools Free Online Web Tutorials"
+                style="height: 80vh"
+                class="full-width" />
+      </q-tab-panel>
+    </q-tab-panels>
+  </q-card>
+</template>
+
+<script>
+import { EntityIndex } from 'quasar-crud'
+import { Team } from 'src/models/Team.js'
+import { Test } from 'src/models/Test.js'
+import Enums from 'src/assets/Enums/Enums.js'
+import { mixinAuth } from 'src/mixin/Mixins.js'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
+import { AnswerBook } from 'src/models/AnswerBook.js'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import ShowClassroomInfo from './components/ShowClassroomInfo.vue'
+import EntityIndexGridItem from 'src/components/EntityIndexGridItem.vue'
+import { RegistrationList } from 'src/models/Registration.js'
+import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import { ProjectAttendanceSheets } from 'src/models/ProjectAttendanceSheets.js'
+import { SessionAttendanceSheets } from 'src/models/SessionAttendanceSheets.js'
+import { TranscriptSheet } from 'src/models/TranscriptSheet'
+import { User } from 'src/models/User'
+
+export default {
+  name: 'UserPanel.Profile.ClassroomInfo',
+  components: {
+    EntityIndex,
+    Breadcrumbs,
+    ShowClassroomInfo,
+    EntityIndexGridItem
+  },
+  mixins: [mixinAuth],
+  data () {
+    const userId = this.$route.params.user_id
+    const classroomId = this.$route.params.classroom_id
+    return {
+      mounted: false,
+      ownerInfo: new User(),
+      selectedTest: {
+        testId: null,
+        answerBookId: null
+      },
+      participateTypeDialog: false,
+      participateType: null,
+      tab: 'classroomInfo',
+      inputs: [],
+      sessionsInputs: [
+        { type: 'hidden', name: 'owner', value: userId },
+        { type: 'hidden', name: 'classroom', value: classroomId }
+      ],
+      api: APIGateway.classroom.get(classroomId),
+      sessionsApi: APIGateway.session.APIAdresses.attendanceSheets,
+      classroom: new Classroom(),
+      table: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.id
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'عنوان',
+            align: 'left',
+            field: row => row.title
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'زمان شروع',
+            align: 'left',
+            field: row => row.beginning_time ? ShamsiDate.getDateTime(row.beginning_time) : '-'
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'زمان پایان',
+            align: 'left',
+            field: row => row.ending_time ? ShamsiDate.getDateTime(row.ending_time) : '-'
+          },
+          {
+            name: 'action',
+            required: true,
+            label: 'جزییات',
+            align: 'left',
+            field: ''
+          }
+        ]
+      },
+      sessionsTable: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.id
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'عنوان',
+            align: 'left',
+            field: row => row.title
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'زمان شروع',
+            align: 'left',
+            field: row => row.beginning_time ? ShamsiDate.getDateTime(row.beginning_time) : '-'
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'زمان پایان',
+            align: 'left',
+            field: row => row.ending_time ? ShamsiDate.getDateTime(row.ending_time) : '-'
+          },
+          {
+            name: 'assignment_status',
+            required: true,
+            label: 'وضعیت تکلیف',
+            align: 'left',
+            field: row => (new SessionAttendanceSheets(this.getCurrentUserAttendanceSheet(row))).assignment_status_info.label
+          },
+          {
+            name: 'attendance_status',
+            required: true,
+            label: 'وضعیت حضور و غیاب',
+            align: 'left',
+            field: row => (new SessionAttendanceSheets(this.getCurrentUserAttendanceSheet(row))).attendance_status_info.label
+          },
+          {
+            name: 'action',
+            required: true,
+            label: 'جزییات',
+            align: 'left',
+            field: ''
+          }
+        ]
+      },
+      sessionsTableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+      tableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+
+      projectListInputs: [
+        { type: 'hidden', name: 'owner', value: userId },
+        { type: 'hidden', name: 'classroom', value: classroomId }
+      ],
+      // projectListApi: APIGateway.projectAttendanceSheets.APIAdresses.base,
+      projectListApi: APIGateway.project.APIAdresses.attendanceSheetsList,
+      projectListTable: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.id
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'عنوان پروژه',
+            align: 'left',
+            field: row => row.title
+          },
+          {
+            name: 'creation_time',
+            required: true,
+            label: 'زمان شروع',
+            align: 'left',
+            field: row => row?.beginning_doing_period ? ShamsiDate.getDateTime(row.beginning_doing_period) : '-'
+          },
+          {
+            name: 'creation_time',
+            required: true,
+            label: 'زمان پایان',
+            align: 'left',
+            field: row => row?.ending_doing_period ? ShamsiDate.getDateTime(row.ending_doing_period) : '-'
+          },
+          {
+            name: 'status',
+            required: true,
+            label: 'وضعیت',
+            align: 'left',
+            field: row => (new ProjectAttendanceSheets(this.getProjectAttendanceSheets(row))).answer_status_info.label
+          },
+          {
+            name: 'actions',
+            required: true,
+            label: 'عملیات',
+            align: 'left',
+            field: ''
+          }
+        ]
+      },
+      projectListTableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+
+      registrations: new RegistrationList(),
+
+      joinTeamLoading: false,
+      teamsListInputs: [
+        { type: 'hidden', name: 'owner', value: userId },
+        { type: 'hidden', name: 'classroom', value: classroomId }
+      ],
+      teamsListApi: APIGateway.team.APIAdresses.classroomTeamsWithJoinStatus,
+      teamsListTable: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.id
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'عنوان گروه',
+            align: 'left',
+            field: row => row.title
+          },
+          {
+            name: 'leader_info',
+            required: true,
+            label: 'سرگروه',
+            align: 'left',
+            field: row => row.leader_info?.firstname + ' ' + row.leader_info?.lastname
+          },
+          {
+            name: 'used_capacity',
+            required: true,
+            label: 'ظرفیت استفاده شده',
+            align: 'left',
+            field: row => row.used_capacity
+          },
+          {
+            name: 'capacity',
+            required: true,
+            label: 'ظرفیت',
+            align: 'left',
+            field: row => row.capacity
+          },
+          {
+            name: 'action',
+            required: true,
+            label: ' ',
+            align: 'left',
+            field: ''
+          }
+        ],
+        data: []
+      },
+      teamsListTableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+
+      testListInputs: [
+        { type: 'hidden', name: 'owner', value: userId },
+        { type: 'hidden', name: 'test__classroom', value: classroomId }
+      ],
+      testListApi: APIGateway.answerBook.APIAdresses.base,
+      testListTable: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.id
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه آزمون',
+            align: 'left',
+            field: row => row.test_info.id
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'عنوان آزمون',
+            align: 'left',
+            field: row => row.test_info?.title
+          },
+          {
+            name: 'start_time',
+            required: true,
+            label: 'زمان شروع',
+            align: 'left',
+            field: row => row.test_info?.start_time ? ShamsiDate.getDateTime(row.test_info.start_time) : '-'
+          },
+          {
+            name: 'end_time',
+            required: true,
+            label: 'زمان پایان',
+            align: 'left',
+            field: row => row.test_info?.end_time ? ShamsiDate.getDateTime(row.test_info.end_time) : '-'
+          },
+          {
+            name: 'duration_deadline',
+            required: true,
+            label: 'مدت آزمون(دقیقه)',
+            align: 'left',
+            field: row => row.test_info.duration_deadline
+          },
+          {
+            name: 'test_questions.length',
+            required: true,
+            label: 'تعداد سوالات',
+            align: 'left',
+            field: row => row.test_info?.test_questions_length
+          },
+          {
+            name: 'grade',
+            required: true,
+            label: 'نمره نهایی',
+            align: 'left',
+            field: row => (row.status === 'GRADED' || row.status === 'OBJECTED' || row.status === 'FINALIZED') ? row.total_score : ''
+          },
+          {
+            name: 'test_info_status',
+            required: true,
+            label: 'وضعیت آزمون',
+            align: 'left',
+            field: row => (new Test(row.test_info)).status_info.label
+          },
+          {
+            name: 'result_status',
+            required: true,
+            label: 'وضعیت تصحیح',
+            align: 'left',
+            field: row => (new AnswerBook(row)).status_info.label
+          },
+          {
+            name: 'action',
+            required: true,
+            label: 'عملیات',
+            align: 'left',
+            field: ''
+          }
+        ]
+      },
+      testListTableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      }
+    }
+  },
+  computed: {
+    ownerId () {
+      return this.$route.params.user_id
+    },
+    classroomId () {
+      return this.$route.params.classroom_id
+    },
+    canShowOtherTabsOfClassroom () {
+      return !this.classroom.user_register_info.loading && (this.classroom.user_register_info.status === 'REGISTERED' || this.classroom.user_register_info.status === 'ENROLLED')
+    },
+    selectedCategoryId () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'category')?.value
+    }
+  },
+  watch: {
+    selectedCategoryId () {
+      this.setInputValue('unit', null)
+      this.getUnits(this.selectedCategoryId)
+    }
+  },
+  mounted () {
+    this.$store.commit('Test/updateAnswerBook', null)
+    this.getUserInfo()
+    this.getClassroom()
+    this.mounted = true
+    FormBuilderAssist.setAttributeByName(this.testListInputs, 'owner', 'value', this.user.id)
+  },
+  methods: {
+    getClassroom () {
+      this.classroom.loading = true
+      APIGateway.classroom.getClassroomAndCorrespondingRegistration(this.classroomId, this.ownerId)
+        .then((classroom) => {
+          this.classroom = new Classroom(classroom)
+          if (!this.classroom.user_register_info.id) {
+            this.getRegistrationInfo()
+          }
+          this.onloadnClassroom()
+        })
+        .finally(() => {
+          this.classroom.loading = false
+        })
+    },
+    getUserInfo () {
+      this.ownerInfo.loading = true
+      APIGateway.user.get({ data: { id: this.ownerId } })
+        .then((user) => {
+          this.ownerInfo = new User(user)
+        })
+        .finally(() => {
+          this.ownerInfo.loading = false
+        })
+    },
+    showParticipateTypeDialog (testId, answerBookId) {
+      this.$store.commit('Test/updateAnswerBook', null)
+      this.selectedTest.testId = testId
+      this.selectedTest.answerBookId = answerBookId
+      this.participateTypeDialog = true
+    },
+    startTest () {
+      this.$store.commit('Test/updateAnswerBook', null)
+      this.$router.push({ name: this.participateType, params: { test_id: this.selectedTest.testId, answer_book_id: this.selectedTest.answerBookId, question_number: 1 } })
+    },
+    getTeamJoinStatusLabel (team) {
+      if (team.join_status === 'ENABLED') {
+        return 'عضو شوید'
+      }
+      return (new Team(team)).join_status_info.label
+    },
+    joinTeam (team) {
+      this.joinTeamLoading = true
+      APIGateway.team.register(team.id)
+        .then(() => {
+          this.joinTeamLoading = false
+          this.$refs.teamsList.search()
+        })
+        .catch(() => {
+          this.joinTeamLoading = false
+          this.$refs.teamsList.search()
+        })
+    },
+    getRegistrationInfo () {
+      this.registrations.loading = true
+      this.classroom.user_register_info.loading = true
+      APIGateway.registration.index({ classroom: this.classroomId })
+        .then(({ list }) => {
+          this.registrations = new RegistrationList(list)
+          if (this.registrations.list.length > 0) {
+            this.classroom.user_register_info = this.registrations.list[0]
+          }
+          this.registrations.loading = false
+          this.classroom.user_register_info.loading = false
+          this.onloadnClassroom()
+        })
+        .catch(() => {
+          this.registrations.loading = false
+          this.classroom.user_register_info.loading = false
+        })
+    },
+    getCurrentUserAttendanceSheet (session) {
+      if (!session?.session_attendance_sheets || session.session_attendance_sheets.length === 0) {
+        return null
+      }
+
+      return session.session_attendance_sheets[0]
+    },
+    getProjectAttendanceSheets (project) {
+      if (!project?.project_attendance_sheets || project.project_attendance_sheets.length === 0) {
+        return null
+      }
+
+      return project.project_attendance_sheets[0]
+    },
+    goToLiveStreamUrl () {
+      window.open(this.classroom.live_streaming_url, '_blank')
+    },
+    onloadnClassroom () {
+      this.$store.commit('AppLayout/updateBreadcrumbs', {
+        visible: true,
+        loading: false,
+        path: [
+          {
+            label: 'دوره های من',
+            to: { name: 'UserPanel.Profile.AllClassrooms' }
+          },
+          {
+            label: this.classroom.title,
+            to: { name: 'UserPanel.Profile.ClassroomInfo', params: { id: this.classroom.id } }
+          }
+        ]
+      })
+    },
+    search () {
+      this.$refs.entityIndex.search()
+    },
+    getClassroomHoldingTypeTitle (type) {
+      const target = Enums.classroomHoldingTypes.find(item => item.value === type)
+      if (!target) {
+        return '-'
+      }
+
+      return target.label
+    },
+    getClassroomStatusTitle (type) {
+      const target = Enums.classroomStatuses.find(item => item.value === type)
+      if (!target) {
+        return '-'
+      }
+
+      return target.label
+    },
+    getInvoiceStatusTitle (type) {
+      const target = Enums.invoiceStatus.find(item => item.value === type)
+      if (!target) {
+        return '-'
+      }
+
+      return target.label
+    },
+    loadInputDataOptions () {
+      this.getCategories()
+      this.getUnits()
+    },
+    setInputValue (name, value) {
+      const inputIndex = this.inputs.findIndex(input => input.name === name)
+      this.inputs[inputIndex].value = value
+    },
+    getSelectOptions (result, value, label) {
+      return result.map(item => {
+        return {
+          value: item[value],
+          label: item[label]
+        }
+      })
+    },
+    loadSelectOptions (name, value) {
+      const inputIndex = this.inputs.findIndex(input => input.name === name)
+      this.inputs[inputIndex].options = value
+    },
+    getTranscriptSheetStatus (transcriptSheet) {
+      return new TranscriptSheet(transcriptSheet).status_info.label
+    }
+  }
+}
+</script>

+ 518 - 0
src/components/Widgets/Classroom/AdminClassroomInfoOfUser/components/ShowClassroomInfo.vue

@@ -0,0 +1,518 @@
+<template>
+  <div class="classroom">
+    <q-banner v-if="!defaultOptions.profileMode"
+              class="banner classroom-banner">
+      {{ classroomTypeTitle }}
+      {{classroom.title}}
+    </q-banner>
+    <q-card flat
+            class="classroom-card">
+      <q-card-section class="info-box">
+
+        <q-banner v-if="classroom.getUserRegisterInfoLabel()"
+                  inline-actions
+                  rounded
+                  class="bg-blue-3 text-white text-center q-mb-md">
+          {{ classroom.getUserRegisterInfoLabel() }}
+        </q-banner>
+
+        <div class="row">
+          <div class="col-md-4 col-12 classroom-thumbnail">
+            <q-img :src="classroom.thumbnail" />
+          </div>
+          <div class="col-md-4 col-12 classroom-top-attributes">
+            <div class="title">
+              {{ classroomTypeTitle }}
+              {{classroom.title}}:
+            </div>
+            <div class="attributes">
+              <div class="attribute-item">{{getAudienceGenderType(classroom.audience_gender_type)}}</div>
+              <!--              <div class="attribute-item">{{getHoldingType(classroom.holding_type)}}</div>-->
+            </div>
+          </div>
+          <div class="col-md-4 col-12 classroom-action-col">
+            <q-btn v-if="classroom.is_enabled_adding"
+                   color="primary"
+                   class="btn-register q-ml-md"
+                   @click="openRegisterDialog">
+              <svg data-name="Add User"
+                   xmlns="http://www.w3.org/2000/svg"
+                   width="20"
+                   height="19.795"
+                   viewBox="0 0 20 19.795"
+                   class="q-mr-md">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M0,16.115c0-3.281,4.5-3.659,7.877-3.659,1.949,0,7.877,0,7.877,3.681,0,3.279-4.5,3.658-7.877,3.658C5.928,19.795,0,19.795,0,16.115Zm1.5,0c0,1.446,2.146,2.18,6.377,2.18s6.377-.726,6.377-2.158c0-1.447-2.146-2.181-6.377-2.181S1.5,14.683,1.5,16.115Zm14.954-5.436V9.424H15.16a.75.75,0,0,1,0-1.5h1.294V6.669a.75.75,0,1,1,1.5,0V7.924h1.3a.75.75,0,0,1,0,1.5h-1.3v1.255a.75.75,0,1,1-1.5,0Zm-8.609-.043a5.334,5.334,0,1,1,.031,0ZM4.06,5.318A3.8,3.8,0,0,0,7.849,9.136l.028.75v-.75A3.818,3.818,0,1,0,4.06,5.318Z"
+                      transform="translate(0)"
+                      fill="#fff" />
+              </svg>
+              ثبت نام
+            </q-btn>
+            <q-btn v-if="classroom.is_enabled_enrolment"
+                   color="primary"
+                   class="btn-register q-ml-md"
+                   @click="onEnrolment">
+              <svg data-name="Add User"
+                   xmlns="http://www.w3.org/2000/svg"
+                   width="20"
+                   height="19.795"
+                   viewBox="0 0 20 19.795"
+                   class="q-mr-md">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M0,16.115c0-3.281,4.5-3.659,7.877-3.659,1.949,0,7.877,0,7.877,3.681,0,3.279-4.5,3.658-7.877,3.658C5.928,19.795,0,19.795,0,16.115Zm1.5,0c0,1.446,2.146,2.18,6.377,2.18s6.377-.726,6.377-2.158c0-1.447-2.146-2.181-6.377-2.181S1.5,14.683,1.5,16.115Zm14.954-5.436V9.424H15.16a.75.75,0,0,1,0-1.5h1.294V6.669a.75.75,0,1,1,1.5,0V7.924h1.3a.75.75,0,0,1,0,1.5h-1.3v1.255a.75.75,0,1,1-1.5,0Zm-8.609-.043a5.334,5.334,0,1,1,.031,0ZM4.06,5.318A3.8,3.8,0,0,0,7.849,9.136l.028.75v-.75A3.818,3.818,0,1,0,4.06,5.318Z"
+                      transform="translate(0)"
+                      fill="#fff" />
+              </svg>
+              پیش ثبت نام
+            </q-btn>
+            <q-btn v-if="!defaultOptions.profileMode && classroom.is_enabled_viewing"
+                   color="primary"
+                   class="btn-register q-ml-md"
+                   :to="{name: 'UserPanel.Profile.ClassroomInfo', params: {id: classroom.id}}">
+              مشاهده
+              {{ classroomTypeTitle }}
+            </q-btn>
+            <q-btn v-if="classroom.is_enabled_dropping"
+                   outline
+                   color="red"
+                   class="btn-register q-ml-md"
+                   :loading="dropLoading"
+                   @click="onDropping">
+              انصراف
+            </q-btn>
+            <q-btn v-if="classroom.current_user_register_info?.status !== 'REGISTERED' && classroom.current_user_register_info?.status !== 'ENROLLED'"
+                   outline
+                   color="primary"
+                   class="btn-register q-ml-md"
+                   :loading="fetchCodesLoading"
+                   @click="openCodeLink">
+              آیین نامه
+              {{ classroomTypeTitle }}
+            </q-btn>
+          </div>
+        </div>
+      </q-card-section>
+      <q-card-section class="info-box">
+        <div class="row second-section">
+          <div class="col-md-4 col-sm-6 col-12 attribute-column">
+            <div class="icon">
+              <svg xmlns="http://www.w3.org/2000/svg"
+                   width="17.058"
+                   height="21.399"
+                   viewBox="0 0 17.058 21.399">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M0,17.418c0-3.55,4.868-3.96,8.53-3.96,2.109,0,8.528,0,8.528,3.982,0,3.548-4.867,3.959-8.528,3.959C6.42,21.4,0,21.4,0,17.418Zm1.616,0c0,1.57,2.326,2.366,6.914,2.366s6.913-.789,6.913-2.345-2.326-2.366-6.913-2.366S1.616,15.862,1.616,17.418ZM8.5,11.436A5.718,5.718,0,0,1,8.53,0a5.718,5.718,0,0,1,0,11.436ZM4.349,5.718A4.171,4.171,0,0,0,8.5,9.9l.031.77V9.9a4.18,4.18,0,1,0-4.181-4.18Z"
+                      transform="translate(0 0)"
+                      fill="#aaa095" />
+              </svg>
+            </div>
+            <div class="title">
+              {{ classroom.professor_info.firstname }}
+              {{ classroom.professor_info.lastname }}
+            </div>
+          </div>
+          <div class="col-md-4 col-sm-6 col-12 attribute-column">
+            <div class="icon">
+              <q-icon name="cast_for_education"
+                      size="20px"
+                      color="grey" />
+            </div>
+            <div class="title">
+              <!--              {{ getTerm(classroom) }}-->
+              {{getHoldingType(classroom.holding_type)}}
+            </div>
+          </div>
+          <div class="col-md-4 col-sm-6 col-12 attribute-column">
+            <div class="icon">
+              <svg data-name="Group 11306"
+                   xmlns="http://www.w3.org/2000/svg"
+                   width="21.539"
+                   height="21.044"
+                   viewBox="0 0 21.539 21.044">
+                <g id="Buy"
+                   transform="translate(0 0)">
+                  <path id="Combined_Shape"
+                        data-name="Combined Shape"
+                        d="M16.3,19.649A1.395,1.395,0,1,1,17.7,21.044,1.4,1.4,0,0,1,16.3,19.649Zm-12.147,0a1.394,1.394,0,1,1,1.394,1.395A1.4,1.4,0,0,1,4.157,19.649ZM17.77,16.139H6.014A2.76,2.76,0,0,1,3.28,13.615L2.294,1.883.671,1.6A.812.812,0,0,1,.012.668.811.811,0,0,1,.815,0a.829.829,0,0,1,.13.011L3.186.4a.808.808,0,0,1,.666.729L4.1,4.143c15.367.007,15.424.014,15.5.022a2.254,2.254,0,0,1,1.912,2.553l-1.023,7.062a2.76,2.76,0,0,1-2.717,2.358ZM4.89,13.483a1.123,1.123,0,0,0,1.127,1.04H17.771a1.139,1.139,0,0,0,1.122-.972l1.024-7.064a.632.632,0,0,0-.121-.472.625.625,0,0,0-.418-.249c-.052,0-.6,0-1.677,0-1.693,0-4.455,0-7.291,0l-6.17,0ZM13.059,9.74a.808.808,0,0,1,0-1.616h2.985a.808.808,0,0,1,0,1.616Z"
+                        transform="translate(0)"
+                        fill="#aaa095" />
+                </g>
+              </svg>
+            </div>
+            <div class="title">
+              {{ classroom.price ? classroom.price.toLocaleString('fa') : 0 }}
+              ریال
+            </div>
+          </div>
+        </div>
+      </q-card-section>
+      <div class="row q-col-gutter-lg four-sections">
+        <div class="col-md-6 col-12">
+          <div class="info-box detailes">
+            <div class="info-box-title">
+              <svg xmlns="http://www.w3.org/2000/svg"
+                   width="17.054"
+                   height="19.866"
+                   viewBox="0 0 17.054 19.866">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M4.475,19.866A4.585,4.585,0,0,1,0,15.188V4.491A4.606,4.606,0,0,1,4.57.012h6.271a.758.758,0,0,1,.265,0h.131a.755.755,0,0,1,.541.23l5.065,5.277a.753.753,0,0,1,.209.519V15.2A4.615,4.615,0,0,1,12.6,19.865H4.475ZM1.5,4.509V15.2a3.091,3.091,0,0,0,3.009,3.162h8.064a3.109,3.109,0,0,0,2.979-3.155V6.984h-2.01a3.332,3.332,0,0,1-3.318-3.325V1.512H4.572A3.089,3.089,0,0,0,1.5,4.509Zm10.224-.85a1.828,1.828,0,0,0,1.82,1.824H14.73L11.724,2.353ZM5.387,14.108a.75.75,0,0,1,0-1.5h5.4a.75.75,0,0,1,0,1.5Zm0-3.752a.75.75,0,0,1,0-1.5H8.743a.75.75,0,0,1,0,1.5Z"
+                      transform="translate(0.001)"
+                      fill="#aaa095" />
+              </svg>
+              مشخصات
+            </div>
+            <div class="info-box-body"
+                 v-html="classroom.specification" />
+          </div>
+          <div class="info-box rules">
+            <div class="info-box-title">
+              <svg xmlns="http://www.w3.org/2000/svg"
+                   width="16.968"
+                   height="20.356"
+                   viewBox="0 0 16.968 20.356">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M8.266,20.324c-.335-.1-8.205-2.55-8.205-9.445C.06,9,.036,7.621.019,6.616l0-.058c-.046-2.544-.055-3.074.6-3.73C1.4,2.04,7.349,0,8.483,0s7.078,2.04,7.865,2.828c.656.656.646,1.184.6,3.722l0,.062C16.929,7.619,16.9,9,16.9,10.879c0,6.895-7.87,9.344-8.205,9.445a.711.711,0,0,1-.217.032A.719.719,0,0,1,8.266,20.324ZM1.668,3.9c-.182.184-.189.5-.15,2.63l0,.065c.017,1.011.041,2.395.041,4.286,0,5.217,5.765,7.525,6.921,7.934C9.637,18.4,15.4,16.086,15.4,10.879c0-1.959.027-3.4.044-4.353.038-2.127.031-2.445-.162-2.637A30.988,30.988,0,0,0,8.483,1.5,31.027,31.027,0,0,0,1.668,3.9Zm5.577,8.4L5.353,10.406A.751.751,0,0,1,6.415,9.345l1.361,1.363,3.369-3.368A.75.75,0,0,1,12.2,8.4l-3.9,3.9a.749.749,0,0,1-1.061,0Z"
+                      transform="translate(0.001)"
+                      fill="#aaa095" />
+              </svg>
+              قوانین
+            </div>
+            <div class="info-box-body"
+                 v-html="classroom.rules" />
+          </div>
+        </div>
+        <div class="col-md-6 col-12">
+          <div class="info-box class-time">
+            <div class="info-box-title">
+              <svg id="Time_Circle"
+                   data-name="Time Circle"
+                   xmlns="http://www.w3.org/2000/svg"
+                   width="20"
+                   height="20"
+                   viewBox="0 0 20 20">
+                <path id="Combined_Shape"
+                      data-name="Combined Shape"
+                      d="M0,10A10,10,0,1,1,10,20,10.011,10.011,0,0,1,0,10Zm1.5,0A8.5,8.5,0,1,0,10,1.5,8.51,8.51,0,0,0,1.5,10Zm11.547,3.587-3.77-2.249a.754.754,0,0,1-.366-.644V5.845a.75.75,0,0,1,1.5,0v4.422l3.4,2.03a.751.751,0,0,1-.769,1.29Z"
+                      fill="#aaa095" />
+              </svg>
+              تابلو اعلانات
+            </div>
+            <div class="info-box-body"
+                 v-html="classroom.bulletin" />
+          </div>
+          <div class="info-box books">
+            <div class="info-box-title">
+              <svg xmlns="http://www.w3.org/2000/svg"
+                   width="21.968"
+                   height="15.702"
+                   viewBox="0 0 21.968 15.702">
+                <path id="import_contacts_FILL0_wght400_GRAD0_opsz48"
+                      d="M7.192,20.482a10.817,10.817,0,0,1,2.609.312,11.579,11.579,0,0,1,2.484.936V11.07A9.5,9.5,0,0,0,9.851,9.91,8.819,8.819,0,0,0,7.192,9.5a7.353,7.353,0,0,0-1.86.237,17.989,17.989,0,0,0-1.835.587h0V21.156h0a7.045,7.045,0,0,1,1.76-.512A11.9,11.9,0,0,1,7.192,20.482Zm6.59,1.248a12.562,12.562,0,0,1,2.446-.936,10.07,10.07,0,0,1,2.546-.312,13.37,13.37,0,0,1,1.96.15,9.769,9.769,0,0,1,1.735.4h0V10.322h0a7.418,7.418,0,0,0-1.8-.624,9.187,9.187,0,0,0-1.9-.2,8.346,8.346,0,0,0-2.609.412,9.727,9.727,0,0,0-2.384,1.161ZM13.034,23.7a1.613,1.613,0,0,1-.362-.037.708.708,0,0,1-.287-.137,10.528,10.528,0,0,0-2.5-1.123,9.28,9.28,0,0,0-2.7-.4,7.188,7.188,0,0,0-1.8.225,17.621,17.621,0,0,0-1.747.549A1.07,1.07,0,0,1,2.537,22.7,1.122,1.122,0,0,1,2,21.7V10.147a1.381,1.381,0,0,1,.175-.686A1.192,1.192,0,0,1,2.7,8.974a9.65,9.65,0,0,1,2.184-.736A11.268,11.268,0,0,1,7.192,8a11.077,11.077,0,0,1,3.058.424,9.758,9.758,0,0,1,2.783,1.3,9.477,9.477,0,0,1,2.733-1.3A10.725,10.725,0,0,1,18.775,8a11.088,11.088,0,0,1,2.3.237,9.691,9.691,0,0,1,2.172.736,1.272,1.272,0,0,1,.537.487,1.309,1.309,0,0,1,.187.686V21.7a1.183,1.183,0,0,1-.562,1.061.956.956,0,0,1-1.111.012,11.887,11.887,0,0,0-1.722-.562,7.589,7.589,0,0,0-1.8-.212,8.949,8.949,0,0,0-2.646.4,10.183,10.183,0,0,0-2.446,1.123.708.708,0,0,1-.287.137A1.613,1.613,0,0,1,13.034,23.7ZM7.891,15.764Z"
+                      transform="translate(-2 -8)"
+                      fill="#aaa095" />
+              </svg>
+              توضیحات
+            </div>
+            <div class="info-box-body"
+                 v-html="classroom.description" />
+          </div>
+        </div>
+      </div>
+    </q-card>
+
+    <q-dialog v-model="rulesDialog"
+              class="rulesDialog"
+              full-width>
+      <q-card>
+        <q-card-section>
+          <div class="text-h6">قوانین</div>
+        </q-card-section>
+
+        <q-separator />
+
+        <q-card-section style="max-height: 50vh"
+                        class="scroll">
+          <div v-html="classroom.rules" />
+          <div v-if="classroom.unit_info && classroom.unit_info.codes"
+               v-html="classroom.unit_info.codes" />
+        </q-card-section>
+
+        <q-separator />
+
+        <q-card-actions class="rulesDialog-actions">
+          <div v-if="(!classroom.is_enabled_enrolment || !classroom.is_enabled_adding) && acceptClassType !== 'rules'"
+               class="rulesDialog-accept-chk">
+            <q-checkbox v-model="rulesAccept"
+                        :label="'قوانین '+classroomTypeTitle+' را خواندم و قصد ثبت نام در '+classroomTypeTitle+' را دارم'" />
+          </div>
+          <div class="rulesDialog-btns q-gutter-md">
+            <q-btn v-if="acceptClassType === 'rules'"
+                   v-close-popup
+                   label="بستن"
+                   color="primary"
+                   outline />
+            <q-btn v-if="(!classroom.is_enabled_enrolment || !classroom.is_enabled_adding) && acceptClassType !== 'rules'"
+                   v-close-popup
+                   label="انصراف"
+                   color="primary"
+                   outline />
+            <q-btn v-if="(!classroom.is_enabled_enrolment || !classroom.is_enabled_adding) && acceptClassType !== 'rules'"
+                   label="تایید و ادامه"
+                   color="primary"
+                   :disable="!rulesAccept"
+                   @click="acceptAndContinue" />
+          </div>
+        </q-card-actions>
+      </q-card>
+    </q-dialog>
+  </div>
+</template>
+
+<script>
+import Enums from 'assets/Enums/Enums.js'
+import ShamsiDate from 'assets/ShamsiDate.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
+import { UnitCategory } from 'src/models/UnitCategory'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'ShowClassroomInfo',
+  mixins: [mixinWidget],
+  props: {
+    options: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    classroom: {
+      type: Classroom,
+      default: new Classroom()
+    },
+    classroomId: {
+      type: Number,
+      default: null
+    },
+    ownerId: {
+      type: Number,
+      default: null
+    },
+    classroomType: {
+      type: String,
+      default: 'TRAINING'
+    }
+  },
+  emits: ['onDrop'],
+  data: () => ({
+    defaultOptions: {
+      profileMode: false,
+      classroomType: 'TRAINING'
+    },
+    acceptClassType: 'register', // accept - enrollment - rules
+    fetchCodesLoading: false,
+    dropLoading: false,
+    rulesAccept: false,
+    rulesDialog: false,
+    loading: true
+  }),
+  computed: {
+    classroomTypeTitle () {
+      const unitCategory = new UnitCategory({ type: this.localOptions.classroomType })
+      return unitCategory.type_info.label
+    }
+  },
+  methods: {
+    getTitledDateTime (dateTime) {
+      return ShamsiDate.getTitledDateTime(dateTime)
+    },
+    acceptAndContinue () {
+      this.$store.commit('Shop/updateOnRegisterClassroom', this.classroom)
+      this.$router.push({ name: 'UserPanel.ShopCompleteInfo', query: { type: this.acceptClassType, source: this.classroomType === 'EVENT' ? 'event' : 'classroom' } })
+    },
+    onEnrolment () {
+      this.acceptClassType = 'enrollment'
+      this.rulesDialog = true
+    },
+    onDropping () {
+      this.$q.dialog({
+        title: 'توجه',
+        message: 'آیا از حذف نام این دوره اطمینان دارید؟',
+        cancel: true,
+        persistent: true
+      }).onOk(() => {
+        this.dropLoading = true
+        APIGateway.classroom.dropByUser(this.classroom.id)
+          .then(() => {
+            this.dropLoading = false
+            this.$emit('onDrop')
+          })
+          .catch(() => {
+            this.dropLoading = false
+          })
+      })
+    },
+    openRulesDialog () {
+      this.acceptClassType = 'rules'
+      this.rulesDialog = true
+    },
+    openCodeLink () {
+      if (!this.classroom.codes) {
+        return
+      }
+
+      const fileName = (this.classroom.codes.match(/(?<=\/)[^/?#]+(?=[^/]*$)/gm) || [this.classroom.title])[0]
+      this.fetchCodesLoading = true
+      fetch(this.classroom.codes)
+        .then(response => response.blob())
+        .then(blob => {
+          const link = document.createElement('a')
+          link.href = URL.createObjectURL(blob)
+          link.download = fileName
+          link.click()
+          this.fetchCodesLoading = false
+        })
+        .catch(() => {
+          this.fetchCodesLoading = false
+        })
+    },
+    openRegisterDialog () {
+      this.acceptClassType = 'register'
+      this.rulesDialog = true
+    },
+    getHoldingType (type) {
+      const target = Enums.classroomHoldingTypes.find(item => item.value === type)
+      if (target) {
+        return target.label
+      }
+      return '-'
+    },
+    getAudienceGenderType (type) {
+      const target = Enums.classroomGenders.find(item => item.value === type)
+      if (target) {
+        return target.label
+      }
+      return '-'
+    },
+    getTime (time) {
+      return ShamsiDate.getDateTime(time)
+    },
+    getTerm (classroom) {
+      if (!classroom.beginning_registration_period) {
+        return '-'
+      }
+      return ShamsiDate.getTerm(classroom.beginning_registration_period)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.classroom {
+  .classroom-banner {
+    margin-top: 52px;
+    margin-bottom: 42px;
+  }
+  .classroom-card {
+    background-color: #F5F5F5;
+    padding: 20px 30px 40px 30px;
+    .four-sections {
+      .info-box {
+        height: 353px;
+        overflow: auto;
+        &.detailes {
+          //margin-right: 24px;
+        }
+        &.rules {
+          //margin-right: 24px;
+        }
+        .info-box-title {
+          margin-bottom: 29px;
+          display: flex;
+          flex-flow: row;
+          svg {
+            margin-right: 8px;
+          }
+        }
+        .info-box-body {
+          .info-item {
+            margin-bottom: 27px;
+          }
+        }
+      }
+    }
+    .info-box {
+      background-color: #FFFFFF;
+      border-radius: 8px;
+      padding: 26px;
+      margin-bottom: 24px;
+      .classroom-thumbnail {
+        padding-right: 42px;
+      }
+      .classroom-top-attributes {
+        .attributes {
+          padding-top: 27px;
+          .attribute-item {
+            margin-bottom: 20px;
+            position: relative;
+            padding-left: 15px;
+            &:before {
+              content: ' ';
+              background-color: #EAC38A;
+              width: 8px;
+              height: 8px;
+              border-radius: 100%;
+              position: absolute;
+              left: 0;
+              top: 10px;
+            }
+          }
+        }
+      }
+      .classroom-action-col {
+        display: flex;
+        justify-content: flex-end;
+        align-items: flex-end;
+        flex-wrap: wrap;
+        .btn-register {
+          width: 220px;
+        }
+        @media screen and (max-width: 700px) {
+          width: 100%;
+          .q-btn {
+            width: 100% !important;
+            margin-left: 0;
+            margin-bottom: 8px;
+          }
+        }
+      }
+      .second-section {
+        .attribute-column {
+          display: flex;
+          flex-flow: row;
+          .icon {
+            margin-right: 20px;
+          }
+        }
+      }
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.rulesDialog {
+  .rulesDialog-actions {
+    display: flex;
+    flex-flow: row;
+    justify-content: space-between;
+    .rulesDialog-accept-chk {}
+    .rulesDialog-btns {}
+  }
+}
+</style>

+ 5 - 0
src/components/Widgets/Classroom/AdminClassroomShow/components/MembersList.vue

@@ -22,6 +22,11 @@
       </template>
       <template v-else-if="inputData.col.name === 'action'">
         <div class="action-column-entity-index">
+          <q-btn color="primary"
+                 class="q-mr-md"
+                 :to="{ name: 'Admin.Classroom.UserClass', params: { classroom_id: classroomId, user_id: inputData.props.row.owner }}">
+            مشاهده دوره
+          </q-btn>
           <q-btn v-if="inputData.props.row.status === 'DROPPED_FOR_CONDITION'"
                  color="primary"
                  class="q-mr-md"

+ 4 - 0
src/models/Classroom.js

@@ -24,6 +24,10 @@ class Classroom extends Model {
       { key: 'professor' },
       { key: 'specification' },
       { key: 'bulletin' },
+      {
+        key: 'user_register_info',
+        relatedModel: Registration
+      },
       {
         key: 'current_user_register_info',
         relatedModel: Registration

+ 23 - 0
src/pages/Admin/Classroom/showUserClass.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.Classroom.Show',
+  mixins: [mixinPageOptions],
+  data: () => {
+    return {
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'AdminClassroomInfoOfUser', options: { style: {}, classroomType: 'TRAINING' } }], 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>

+ 1 - 0
src/router/routes.js

@@ -658,6 +658,7 @@ 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.UserClass', path: ':classroom_id/user/:user_id', component: () => import('src/pages/Admin/Classroom/showUserClass.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') },