Explorar el Código

user events in panel

ali hace 3 años
padre
commit
87006a7cc4

+ 354 - 0
src/components/Widgets/Classroom/ClassroomList/ClassroomList.vue

@@ -0,0 +1,354 @@
+<template>
+  <breadcrumbs style="margin-top: 29px; margin-bottom: 19px;" />
+  <entity-index v-if="mounted"
+                ref="entityIndex"
+                v-model:value="inputs"
+                :title="listTitle"
+                :api="api"
+                :table="table"
+                :table-keys="tableKeys"
+                :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: showPageRouteName, params: {id: inputData.props.row.classroom_info.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: showPageRouteName, params: {id: row.classroom_info.id}}" />
+          </template>
+        </template>
+      </entity-index-grid-item>
+    </template>
+  </entity-index>
+</template>
+
+<script>
+import { shallowRef } from 'vue'
+import { EntityIndex } from 'quasar-crud'
+import Enums from 'src/assets/Enums/Enums.js'
+import { Invoice } from 'src/models/Invoice.js'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import BtnControl from 'src/components/Control/btn.vue'
+import { Registration } from 'src/models/Registration.js'
+import { mixinAuth, mixinWidget } from 'src/mixin/Mixins.js'
+import EntityIndexGridItem from 'src/components/EntityIndexGridItem.vue'
+import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+
+const BtnControlComp = shallowRef(BtnControl)
+
+export default {
+  name: 'UserPanel.Profile.AllClassrooms',
+  components: {
+    EntityIndex,
+    Breadcrumbs,
+    EntityIndexGridItem
+  },
+  mixins: [mixinAuth, mixinWidget],
+  data () {
+    return {
+      mounted: false,
+      inputs: [
+        {
+          type: 'select',
+          name: 'classroom__unit__category__type',
+          options: [
+            {
+              label: 'دوره های آموزشی',
+              value: 'TRAINING'
+            },
+            {
+              label: 'حلقه های مباحثاتی',
+              value: 'DISCUSSION_CIRCLE'
+            }
+          ],
+          value: 'TRAINING',
+          label: 'نوع',
+          placeholder: ' ',
+          col: 'col-md-2 col-12'
+        },
+        {
+          type: 'select',
+          name: 'status',
+          options: Enums.classroomStatuses,
+          value: null,
+          label: 'وضعیت دوره',
+          placeholder: ' ',
+          col: 'col-md-2 col-12'
+        },
+        {
+          type: 'select',
+          name: 'category',
+          options: [],
+          value: null,
+          label: 'گروه آموزشی',
+          placeholder: ' ',
+          col: 'col-md-2 col-12'
+        },
+        {
+          type: 'select',
+          name: 'unit',
+          responseKey: 'unit',
+          options: [],
+          value: null,
+          label: 'درس',
+          placeholder: ' ',
+          col: 'col-md-2 col-12'
+        },
+        { type: 'hidden', name: 'owner', value: null },
+        { type: 'input', name: 'search', label: 'جستجو', placeholder: ' ', col: 'col-md-2 col-12' },
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'جستجو', placeholder: ' ', atClick: () => {}, col: 'col-md-2 col-12' }
+      ],
+      api: APIGateway.registration.APIAdresses.base,
+      table: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.classroom_info.id
+          },
+          {
+            name: 'classroom_info.title',
+            required: true,
+            label: 'عنوان',
+            align: 'left',
+            field: row => row.classroom_info?.title
+          },
+          {
+            name: 'audience_gender_type',
+            required: true,
+            label: 'جنسیت',
+            align: 'left',
+            field: row => (new Classroom(row.classroom_info)).audience_gender_type_info.label
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'نوع برگزاری',
+            align: 'left',
+            field: row => this.getClassroomHoldingTypeTitle(row.classroom_info.holding_type)
+          },
+          {
+            name: 'professor_info',
+            required: true,
+            label: 'استاد',
+            align: 'left',
+            field: row => row.classroom_info?.professor_info?.firstname + ' ' + row.classroom_info?.professor_info?.lastname
+          },
+          {
+            name: 'sessions_info',
+            required: true,
+            label: 'تعداد جلسات',
+            align: 'left',
+            field: row => row.classroom_info?.sessions_info?.length
+          },
+          {
+            name: 'status',
+            required: true,
+            label: 'وضعیت',
+            align: 'left',
+            field: row => (new Registration(row)).status_info.label
+            // field: row => this.getClassroomStatusTitle(row.classroom_info.status)
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'زمان ثبت نام',
+            align: 'left',
+            field: row => ShamsiDate.getDateTime(row.creation_time)
+          },
+          {
+            name: 'status',
+            required: true,
+            label: 'وضعیت مالی',
+            align: 'left',
+            field: row => (new Invoice(row.invoice_info)).status_info.label ? (new Invoice(row.invoice_info)).status_info.label : '-'
+          },
+          {
+            name: 'action',
+            required: true,
+            label: 'جزییات',
+            align: 'left',
+            field: ''
+          }
+        ]
+      },
+      defaultOptions: {
+        classroomType: 'TRAINING'
+      },
+      tableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      }
+    }
+  },
+  computed: {
+    listTitle () {
+      if (this.localOptions.classroomType === 'TRAINING') {
+        return 'لیست دوره های آموزشی'
+      }
+      if (this.localOptions.classroomType === 'EVENT') {
+        return 'لیست رویدادها'
+      }
+      return 'لیست دوره های آموزشی'
+    },
+    showPageRouteName () {
+      if (this.localOptions.classroomType === 'TRAINING') {
+        return 'UserPanel.Profile.ClassroomInfo'
+      }
+      if (this.localOptions.classroomType === 'EVENT') {
+        return 'UserPanel.Profile.EventInfo'
+      }
+      return 'UserPanel.Profile.ClassroomInfo'
+    },
+    selectedCategoryId () {
+      return this.getInputValue('category')
+    }
+  },
+  watch: {
+    selectedCategoryId () {
+      this.setInputValue('unit', null)
+      this.getUnits(this.selectedCategoryId)
+    }
+  },
+  mounted () {
+    this.setClassroomTypeOfInputs()
+    this.setOwner()
+    this.setActionBtn()
+    this.loadInputDataOptions()
+    this.mounted = true
+  },
+  methods: {
+    setClassroomTypeOfInputs () {
+      FormBuilderAssist.setAttributeByName(this.inputs, 'classroom__unit__category__type', 'value', this.localOptions.classroomType)
+
+      // if (this.localOptions.classroomType === 'TRAINING') {
+      // }
+      if (this.localOptions.classroomType === 'EVENT') {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'classroom__unit__category__type', 'type', 'hidden')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'category', 'type', 'hidden')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'unit', 'label', 'دسته')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'status', 'label', 'وضعیت رویداد')
+
+        this.table.columns.forEach(col => {
+          if (col.name === 'professor_info') {
+            col.label = 'برگزار کننده'
+          }
+          if (col.name === 'sessions_info') {
+            col.label = 'تعداد برنامه ها'
+          }
+        })
+      }
+    },
+    setOwner () {
+      const user = this.$store.getters['Auth/user']
+      FormBuilderAssist.setAttributeByName(this.inputs, 'owner', 'value', user.id)
+    },
+    setActionBtn () {
+      FormBuilderAssist.setAttributeByName(this.inputs, 'btn', 'atClick', this.search)
+    },
+    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()
+    },
+    getCategories () {
+      APIGateway.classroom.index({ per_page: 9999 })
+        .then(classroomList => {
+          this.loadSelectOptions('category', this.getSelectOptions(classroomList.list.list, 'id', 'title'))
+        })
+        .catch(() => {
+        })
+    },
+    getUnits (selectedcategoryId) {
+      APIGateway.unit.index({ category: selectedcategoryId })
+        .then(unitList => {
+          this.loadSelectOptions('unit', this.getSelectOptions(unitList.list.list, 'id', 'title'))
+        })
+        .catch(() => {
+        })
+    },
+    getInputValue (name) {
+      const inputIndex = this.inputs.findIndex(input => input.name === name)
+      return this.inputs[inputIndex].value
+    },
+    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
+    }
+  }
+}
+</script>

+ 28 - 0
src/components/Widgets/Classroom/ClassroomList/OptionPanel.vue

@@ -0,0 +1,28 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <q-option-group v-model="localOptions.classroomType"
+                      :options="classroomTypeOptions" />
+    </template>
+  </option-panel-tabs>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { UnitCategory } from 'src/models/UnitCategory.js'
+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],
+  data () {
+    return {
+      classroomTypeOptions: (new UnitCategory()).typeEnums
+    }
+  }
+})
+</script>

+ 707 - 0
src/components/Widgets/Classroom/ClassroomShow/ClassroomShow.vue

@@ -0,0 +1,707 @@
+<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>
+    <q-tabs v-model="tab"
+            dense
+            class="text-grey"
+            active-color="primary"
+            indicator-color="primary"
+            align="justify"
+            narrow-indicator>
+      <q-tab v-if="canShowTabPageForClassroomType('classroomInfo')"
+             name="classroomInfo"
+             label="اطلاعات دوره آموزشی" />
+      <q-tab v-if="canShowOtherTabsOfClassroom && canShowTabPageForClassroomType('educations')"
+             name="educations"
+             :label="educationsTabTitle" />
+      <q-tab v-if="canShowOtherTabsOfClassroom && canShowTabPageForClassroomType('educations')"
+             name="team"
+             label="گروه" />
+      <q-tab v-if="canShowOtherTabsOfClassroom && canShowTabPageForClassroomType('educations')"
+             name="exams"
+             label="آزمون" />
+      <q-tab v-if="canShowOtherTabsOfClassroom && canShowTabPageForClassroomType('educations')"
+             name="projects"
+             label="پروژه" />
+      <q-tab v-if="canShowOtherTabsOfClassroom && classroom.live_streaming_url && canShowTabPageForClassroomType('educations')"
+             name="live_streaming_url"
+             label="بخش آنلاین" />
+    </q-tabs>
+
+    <q-separator />
+
+    <q-tab-panels v-model="tab"
+                  animated>
+      <q-tab-panel name="classroomInfo">
+        <show-classroom-info v-if="mounted"
+                             :options="{ profileMode: true }"
+                             :classroom-type="localOptions.classroomType"
+                             @onloadn="onloadnClassroom" />
+      </q-tab-panel>
+
+      <q-tab-panel name="educations"
+                   class="q-pa-none">
+        <entity-index v-if="mounted"
+                      ref="entityIndex"
+                      v-model:value="sessionsInputs"
+                      :title="educationsListTitle"
+                      :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="exams">
+        آزمون
+      </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: $route.params.id, 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 Enums from 'src/assets/Enums/Enums.js'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import { mixinAuth, mixinWidget } from 'src/mixin/Mixins.js'
+import EntityIndexGridItem from 'src/components/EntityIndexGridItem.vue'
+import { Registration, 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 ShowClassroomInfo from 'src/components/Widgets/Other/ShowClassroomInfo/ShowClassroomInfo.vue'
+
+export default {
+  name: 'UserPanel.Profile.ClassroomInfo',
+  components: {
+    EntityIndex,
+    Breadcrumbs,
+    ShowClassroomInfo,
+    EntityIndexGridItem
+  },
+  mixins: [mixinAuth, mixinWidget],
+  data () {
+    const classroomId = this.$route.params.id
+    // const userId = this.$store.getters['Auth/user'].id
+    return {
+      mounted: false,
+      tab: 'classroomInfo',
+      inputs: [],
+      sessionsInputs: [
+        { type: 'hidden', name: 'classroom', value: classroomId }
+      ],
+      defaultOptions: {
+        classroomType: 'TRAINING'
+      },
+      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: 'beginning_time',
+            required: true,
+            label: 'زمان شروع',
+            align: 'left',
+            field: row => row.beginning_time ? ShamsiDate.getDateTime(row.beginning_time) : '-'
+          },
+          {
+            name: 'ending_time',
+            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: 'beginning_time',
+            required: true,
+            label: 'زمان شروع',
+            align: 'left',
+            field: row => row.beginning_time ? ShamsiDate.getDateTime(row.beginning_time) : '-'
+          },
+          {
+            name: 'ending_time',
+            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: 'classroom', value: classroomId }
+        // { type: 'hidden', name: 'owner', value: userId }
+      ],
+      // 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(),
+      classroomRegistration: new Registration(),
+
+      joinTeamLoading: false,
+      teamsListInputs: [
+        { 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'
+      }
+    }
+  },
+  computed: {
+    listTitle () {
+      if (this.localOptions.classroomType === 'TRAINING') {
+        return 'لیست دوره های آموزشی'
+      }
+      if (this.localOptions.classroomType === 'EVENT') {
+        return 'لیست رویدادها'
+      }
+      return 'لیست دوره های آموزشی'
+    },
+    educationsListTitle () {
+      if (this.localOptions.classroomType === 'TRAINING') {
+        return 'لیست جلسات'
+      }
+      if (this.localOptions.classroomType === 'EVENT') {
+        return 'لیست برنامه ها'
+      }
+      return 'لیست جلسات'
+    },
+    educationsTabTitle () {
+      if (this.localOptions.classroomType === 'TRAINING') {
+        return 'جدول آموزشی'
+      }
+      if (this.localOptions.classroomType === 'EVENT') {
+        return 'لیست برنامه ها'
+      }
+      return 'جدول آموزشی'
+    },
+    canShowTabPageForClassroomType () {
+      return (tabName) => {
+        const tabs = {
+          TRAINING: [
+            'classroomInfo',
+            'educations',
+            'team',
+            'exams',
+            'projects',
+            'live_streaming_url'
+          ],
+          EVENT: [
+            'classroomInfo',
+            'educations',
+            'live_streaming_url'
+          ]
+        }
+
+        return tabs[this.localOptions.classroomType].includes(tabName)
+      }
+    },
+    classroomId () {
+      return this.$route.params.id
+    },
+    canShowOtherTabsOfClassroom () {
+      return !this.classroomRegistration.loading && (this.classroomRegistration.status === 'REGISTERED' || this.classroomRegistration.status === 'ENROLLED')
+    },
+    selectedCategoryId () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'category')?.value
+    }
+  },
+  watch: {
+    selectedCategoryId () {
+      this.setInputValue('unit', null)
+      this.getUnits(this.selectedCategoryId)
+    }
+  },
+  mounted () {
+    this.setClassroomTypeOfInputs()
+    this.getRegistrationInfo()
+    this.mounted = true
+  },
+  methods: {
+    setClassroomTypeOfInputs () {
+      FormBuilderAssist.setAttributeByName(this.inputs, 'classroom__unit__category__type', 'value', this.localOptions.classroomType)
+
+      // if (this.localOptions.classroomType === 'TRAINING') {
+      // }
+      if (this.localOptions.classroomType === 'EVENT') {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'classroom__unit__category__type', 'type', 'hidden')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'category', 'type', 'hidden')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'unit', 'label', 'دسته')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'status', 'label', 'وضعیت رویداد')
+
+        this.table.columns.forEach(col => {
+          if (col.name === 'professor_info') {
+            col.label = 'برگزار کننده'
+          }
+          if (col.name === 'sessions_info') {
+            col.label = 'تعداد برنامه ها'
+          }
+        })
+      }
+    },
+    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.classroomRegistration.loading = true
+      APIGateway.registration.index({ classroom: this.$route.params.id })
+        .then(({ list }) => {
+          this.registrations = new RegistrationList(list)
+          if (this.registrations.list.length > 0) {
+            this.classroomRegistration = this.registrations.list[0]
+          }
+          this.registrations.loading = false
+          this.classroomRegistration.loading = false
+        })
+        .catch(() => title{
+          this.registrations.loading = false
+          this.classroomRegistration.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 (classroom) {
+      this.classroom = new Classroom(classroom)
+      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
+    }
+  }
+}
+</script>

+ 28 - 0
src/components/Widgets/Classroom/ClassroomShow/OptionPanel.vue

@@ -0,0 +1,28 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <q-option-group v-model="localOptions.classroomType"
+                      :options="classroomTypeOptions" />
+    </template>
+  </option-panel-tabs>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { UnitCategory } from 'src/models/UnitCategory.js'
+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],
+  data () {
+    return {
+      classroomTypeOptions: (new UnitCategory()).typeEnums
+    }
+  }
+})
+</script>

+ 18 - 5
src/components/Widgets/Other/ShowClassroomInfo/ShowClassroomInfo.vue

@@ -2,7 +2,7 @@
   <div class="classroom">
     <q-banner v-if="!loading && !defaultOptions.profileMode"
               class="banner classroom-banner">
-      دوره های آموزشی
+      {{ classroomTypeTitle }}
       {{classroom.title}}
     </q-banner>
     <q-card v-if="!loading"
@@ -23,7 +23,7 @@
           </div>
           <div class="col-md-4 col-12 classroom-top-attributes">
             <div class="title">
-              دوره آموزشی
+              {{ classroomTypeTitle }}
               {{classroom.title}}:
             </div>
             <div class="attributes">
@@ -72,7 +72,8 @@
                    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
@@ -88,7 +89,8 @@
                    class="btn-register q-ml-md"
                    :loading="fetchCodesLoading"
                    @click="openCodeLink">
-              آیین نامه دوره
+              آیین نامه
+              {{ classroomTypeTitle }}
             </q-btn>
           </div>
         </div>
@@ -258,7 +260,7 @@
           <div v-if="(!classroom.is_enabled_enrolment || !classroom.is_enabled_adding) && acceptClassType !== 'rules'"
                class="rulesDialog-accept-chk">
             <q-checkbox v-model="rulesAccept"
-                        label="قوانین دوره آموزشی را خواندم و قصد ثبت نام در دوره را دارم" />
+                        :label="'قوانین '+classroomTypeTitle+' را خواندم و قصد ثبت نام در '+classroomTypeTitle+' را دارم'" />
           </div>
           <div class="rulesDialog-btns q-gutter-md">
             <q-btn v-if="acceptClassType === 'rules'"
@@ -289,6 +291,7 @@ import ShamsiDate from 'src/assets/ShamsiDate.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { Classroom } from 'src/models/Classroom.js'
 import { mixinPrefetchServerData, mixinWidget } from 'src/mixin/Mixins.js'
+import { UnitCategory } from 'src/models/UnitCategory'
 
 export default {
   name: 'ShowClassroomInfo',
@@ -299,6 +302,10 @@ export default {
       default: () => {
         return {}
       }
+    },
+    classroomType: {
+      type: String,
+      default: 'TRAINING'
     }
   },
   emits: ['onloadn'],
@@ -314,6 +321,12 @@ export default {
     loading: true,
     classroom: new Classroom()
   }),
+  computed: {
+    classroomTypeTitle () {
+      const unitCategory = new UnitCategory({ type: this.localOptions.classroomType })
+      return unitCategory.type_info.label
+    }
+  },
   methods: {
     prefetchServerDataPromise () {
       return this.getClassroom()

+ 10 - 227
src/pages/User/Profile/EventInfo.vue

@@ -1,240 +1,23 @@
 <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>
-    <q-tabs v-model="tab"
-            dense
-            class="text-grey"
-            active-color="primary"
-            indicator-color="primary"
-            align="justify"
-            narrow-indicator>
-      <q-tab name="eventInfo"
-             label="اطلاعات رویداد" />
-      <q-tab v-if="event.live_streaming_url"
-             name="live_streaming_url"
-             label="بخش آنلاین" />
-    </q-tabs>
-
-    <q-separator />
-
-    <q-tab-panels v-model="tab"
-                  animated>
-      <q-tab-panel name="eventInfo">
-        <show-event-info v-if="mounted"
-                         :options="{ profileMode: true }"
-                         @onloadn="onloadnEvent" />
-      </q-tab-panel>
-      <q-tab-panel v-if="event.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="event.live_streaming_url"
-                title="W3Schools Free Online Web Tutorials"
-                style="height: 80vh"
-                class="full-width" />
-      </q-tab-panel>
-    </q-tab-panels>
-  </q-card>
+  <q-page-builder v-model:sections="currenSections"
+                  v-model:options="pageConfig"
+                  :editable="pageBuilderEditable"
+                  :loading="pageBuilderLoading" />
 </template>
 
 <script>
-import { Event } from 'src/models/Event.js'
-import Enums from 'src/assets/Enums/Enums.js'
-import { Invoice } from 'src/models/Invoice.js'
-import { mixinAuth } from 'src/mixin/Mixins.js'
-import ShamsiDate from 'src/assets/ShamsiDate.js'
-import { APIGateway } from 'src/api/APIGateway.js'
-import { FormBuilderAssist } from 'quasar-form-builder'
-import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
-import ShowEventInfo from 'src/components/Widgets/Event/ShowEventInfo/ShowEventInfo.vue'
+import { mixinPageOptions } from 'src/mixin/Mixins.js'
 
 export default {
   name: 'UserPanel.Profile.EventInfo',
-  components: {
-    Breadcrumbs,
-    ShowEventInfo
-  },
-  mixins: [mixinAuth],
-  data () {
-    const eventId = this.$route.params.id
+  mixins: [mixinPageOptions],
+  data: () => {
     return {
-      mounted: false,
-      tab: 'eventInfo',
-      inputs: [],
-      api: APIGateway.event.get(eventId),
-      event: new Event(),
-      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: ''
-          }
-        ]
-      },
-      tableKeys: {
-        data: 'results',
-        total: 'count',
-        currentPage: 'current',
-        perPage: 'per_page',
-        pageKey: 'page'
-      }
-    }
-  },
-  computed: {
-    selectedCategoryId () {
-      return FormBuilderAssist.getInputsByName(this.inputs, 'category')?.value
-    }
-  },
-  watch: {
-    selectedCategoryId () {
-      this.setInputValue('unit', null)
-      this.getUnits(this.selectedCategoryId)
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'ClassroomShow', options: { style: {}, classroomType: 'EVENT' } }], 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' } } }] } }]
     }
   },
-  mounted () {
-    this.mounted = true
-  },
-  methods: {
-    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.event.live_streaming_url, '_blank')
-    },
-    onloadnEvent (event) {
-      this.event = new Event(event)
-      this.$store.commit('AppLayout/updateBreadcrumbs', {
-        visible: true,
-        loading: false,
-        path: [
-          {
-            label: 'رویدادهای من',
-            to: { name: 'UserPanel.Profile.AllEvents' }
-          },
-          {
-            label: this.event.title,
-            to: { name: 'UserPanel.Profile.EventInfo', params: { id: this.event.id } }
-          }
-        ]
-      })
-    },
-    search () {
-      this.$refs.entityIndex.search()
-    },
-    getEventHoldingTypeTitle (type) {
-      const target = Enums.eventHoldingTypes.find(item => item.value === type)
-      if (!target) {
-        return '-'
-      }
-
-      return target.label
-    },
-    getEventStatusTitle (type) {
-      const target = (new Event()).statusEnums.find(item => item.value === type)
-      if (!target) {
-        return '-'
-      }
-
-      return target.label
-    },
-    getInvoiceStatusTitle (type) {
-      const target = (new Invoice()).statusEnums.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
-    }
+  created() {
+    this.currenSections = this.sections
   }
 }
 </script>

+ 10 - 231
src/pages/User/Profile/Events.vue

@@ -1,244 +1,23 @@
 <template>
-  <breadcrumbs style="margin-top: 29px; margin-bottom: 19px;" />
-  <entity-index v-if="mounted"
-                ref="entityIndex"
-                v-model:value="inputs"
-                title="لیست رویداد ها"
-                :api="api"
-                :table="table"
-                :table-keys="tableKeys"
-                :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.EventInfo', params: {id: inputData.props.row.event_info.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.EventInfo', params: {id: row.event_info.id}}" />
-          </template>
-        </template>
-      </entity-index-grid-item>
-    </template>
-  </entity-index>
+  <q-page-builder v-model:sections="currenSections"
+                  v-model:options="pageConfig"
+                  :editable="pageBuilderEditable"
+                  :loading="pageBuilderLoading" />
 </template>
 
 <script>
-import { shallowRef } from 'vue'
-import { EntityIndex } from 'quasar-crud'
-import { Event } from 'src/models/Event.js'
-import Enums from 'src/assets/Enums/Enums.js'
-import { Invoice } from 'src/models/Invoice.js'
-import { mixinAuth } from 'src/mixin/Mixins.js'
-import ShamsiDate from 'src/assets/ShamsiDate.js'
-import { APIGateway } from 'src/api/APIGateway.js'
-import { FormBuilderAssist } from 'quasar-form-builder'
-import BtnControl from 'src/components/Control/btn.vue'
-import { EventRegistration } from 'src/models/EventRegistration.js'
-import EntityIndexGridItem from 'src/components/EntityIndexGridItem.vue'
-import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
-
-const BtnControlComp = shallowRef(BtnControl)
+import { mixinPageOptions } from 'src/mixin/Mixins.js'
 
 export default {
   name: 'UserPanel.Profile.AllEvents',
-  components: {
-    EntityIndex,
-    Breadcrumbs,
-    EntityIndexGridItem
-  },
-  mixins: [mixinAuth],
-  data () {
+  mixins: [mixinPageOptions],
+  data: () => {
     return {
-      mounted: false,
-      inputs: [
-        {
-          type: 'select',
-          name: 'status',
-          options: (new Event()).statusEnums,
-          value: null,
-          label: 'وضعیت رویداد',
-          placeholder: ' ',
-          col: 'col-md-2 col-12'
-        },
-        {
-          type: 'select',
-          name: 'category',
-          options: (new Event()).categoryEnums,
-          value: null,
-          label: 'گروه آموزشی',
-          placeholder: ' ',
-          col: 'col-md-2 col-12'
-        },
-        { type: 'hidden', name: 'owner', value: null },
-        { type: 'input', name: 'search', label: 'جستجو', placeholder: ' ', col: 'col-md-2 col-12' },
-        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'جستجو', placeholder: ' ', atClick: () => {}, col: 'col-md-2 col-12' }
-      ],
-      api: APIGateway.eventRegistration.APIAdresses.base,
-      table: {
-        columns: [
-          {
-            name: 'number',
-            required: true,
-            label: 'شماره',
-            align: 'left',
-            field: () => ''
-          },
-          {
-            name: 'id',
-            required: true,
-            label: 'شناسه',
-            align: 'left',
-            field: row => row.event_info.id
-          },
-          {
-            name: 'event_info.title',
-            required: true,
-            label: 'عنوان',
-            align: 'left',
-            field: row => row.event_info?.title
-          },
-          {
-            name: 'audience_gender_type',
-            required: true,
-            label: 'جنسیت',
-            align: 'left',
-            field: row => (new Event(row.event_info)).audience_gender_type_info.label
-          },
-          {
-            name: 'category',
-            required: true,
-            label: 'دسته بندی',
-            align: 'left',
-            field: row => (new Event(row.event_info)).category_info.label
-          },
-          {
-            name: 'title',
-            required: true,
-            label: 'نوع برگزاری',
-            align: 'left',
-            field: row => this.getEventHoldingTypeTitle(row.event_info.holding_type)
-          },
-          {
-            name: 'status',
-            required: true,
-            label: 'وضعیت',
-            align: 'left',
-            field: row => (new EventRegistration(row)).status_info.label
-            // field: row => this.getEventStatusTitle(row.event_info.status)
-          },
-          {
-            name: 'title',
-            required: true,
-            label: 'زمان ثبت نام',
-            align: 'left',
-            field: row => ShamsiDate.getDateTime(row.creation_time)
-          },
-          {
-            name: 'status',
-            required: true,
-            label: 'وضعیت مالی',
-            align: 'left',
-            field: row => (new Invoice(row.invoice_info)).status_info.label ? (new Invoice(row.invoice_info)).status_info.label : '-'
-          },
-          {
-            name: 'action',
-            required: true,
-            label: 'جزییات',
-            align: 'left',
-            field: ''
-          }
-        ]
-      },
-      tableKeys: {
-        data: 'results',
-        total: 'count',
-        currentPage: 'current',
-        perPage: 'per_page',
-        pageKey: 'page'
-      }
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'ClassroomList', options: { style: {}, classroomType: 'EVENT' } }], 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' } } }] } }]
     }
   },
-  mounted () {
-    this.setOwner()
-    this.setActionBtn()
-    this.mounted = true
-  },
-  methods: {
-    setOwner () {
-      const user = this.$store.getters['Auth/user']
-      FormBuilderAssist.setAttributeByName(this.inputs, 'owner', 'value', user.id)
-    },
-    setActionBtn () {
-      FormBuilderAssist.setAttributeByName(this.inputs, 'btn', 'atClick', this.search)
-    },
-    search () {
-      this.$refs.entityIndex.search()
-    },
-    getEventHoldingTypeTitle (type) {
-      const target = (new Event()).holding_typeEnums.find(item => item.value === type)
-      if (!target) {
-        return '-'
-      }
-
-      return target.label
-    },
-    getEventStatusTitle (type) {
-      const target = (new Event()).statusEnums.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
-    },
-    getInputValue (name) {
-      const inputIndex = this.inputs.findIndex(input => input.name === name)
-      return this.inputs[inputIndex].value
-    },
-    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
-    }
+  created() {
+    this.currenSections = this.sections
   }
 }
 </script>

+ 10 - 312
src/pages/User/Profile/classrooms.vue

@@ -1,325 +1,23 @@
 <template>
-  <breadcrumbs style="margin-top: 29px; margin-bottom: 19px;" />
-  <entity-index v-if="mounted"
-                ref="entityIndex"
-                v-model:value="inputs"
-                title="لیست دوره ها"
-                :api="api"
-                :table="table"
-                :table-keys="tableKeys"
-                :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.ClassroomInfo', params: {id: inputData.props.row.classroom_info.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.ClassroomInfo', params: {id: row.classroom_info.id}}" />
-          </template>
-        </template>
-      </entity-index-grid-item>
-    </template>
-  </entity-index>
+  <q-page-builder v-model:sections="currenSections"
+                  v-model:options="pageConfig"
+                  :editable="pageBuilderEditable"
+                  :loading="pageBuilderLoading" />
 </template>
 
 <script>
-import { shallowRef } from 'vue'
-import { EntityIndex } from 'quasar-crud'
-import Enums from 'src/assets/Enums/Enums.js'
-import { Invoice } from 'src/models/Invoice.js'
-import { mixinAuth } from 'src/mixin/Mixins.js'
-import ShamsiDate from 'src/assets/ShamsiDate.js'
-import { APIGateway } from 'src/api/APIGateway.js'
-import { FormBuilderAssist } from 'quasar-form-builder'
-import BtnControl from 'src/components/Control/btn.vue'
-import { Registration } from 'src/models/Registration.js'
-import EntityIndexGridItem from 'src/components/EntityIndexGridItem.vue'
-import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
-import { Classroom } from 'src/models/Classroom'
-
-const BtnControlComp = shallowRef(BtnControl)
+import { mixinPageOptions } from 'src/mixin/Mixins.js'
 
 export default {
   name: 'UserPanel.Profile.AllClassrooms',
-  components: {
-    EntityIndex,
-    Breadcrumbs,
-    EntityIndexGridItem
-  },
-  mixins: [mixinAuth],
-  data () {
+  mixins: [mixinPageOptions],
+  data: () => {
     return {
-      mounted: false,
-      inputs: [
-        {
-          type: 'select',
-          name: 'classroom__unit__category__type',
-          options: [
-            {
-              label: 'دوره های آموزشی',
-              value: 'TRAINING'
-            },
-            {
-              label: 'حلقه های مباحثاتی',
-              value: 'DISCUSSION_CIRCLE'
-            }
-          ],
-          value: 'TRAINING',
-          label: 'نوع',
-          placeholder: ' ',
-          col: 'col-md-2 col-12'
-        },
-        {
-          type: 'select',
-          name: 'status',
-          options: Enums.classroomStatuses,
-          value: null,
-          label: 'وضعیت دوره',
-          placeholder: ' ',
-          col: 'col-md-2 col-12'
-        },
-        {
-          type: 'select',
-          name: 'category',
-          options: [],
-          value: null,
-          label: 'گروه آموزشی',
-          placeholder: ' ',
-          col: 'col-md-2 col-12'
-        },
-        {
-          type: 'select',
-          name: 'unit',
-          responseKey: 'unit',
-          options: [],
-          value: null,
-          label: 'درس',
-          placeholder: ' ',
-          col: 'col-md-2 col-12'
-        },
-        { type: 'hidden', name: 'owner', value: null },
-        { type: 'input', name: 'search', label: 'جستجو', placeholder: ' ', col: 'col-md-2 col-12' },
-        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'جستجو', placeholder: ' ', atClick: () => {}, col: 'col-md-2 col-12' }
-      ],
-      api: APIGateway.registration.APIAdresses.base,
-      table: {
-        columns: [
-          {
-            name: 'number',
-            required: true,
-            label: 'شماره',
-            align: 'left',
-            field: () => ''
-          },
-          {
-            name: 'id',
-            required: true,
-            label: 'شناسه',
-            align: 'left',
-            field: row => row.classroom_info.id
-          },
-          {
-            name: 'classroom_info.title',
-            required: true,
-            label: 'عنوان',
-            align: 'left',
-            field: row => row.classroom_info?.title
-          },
-          // {
-          //   name: 'title',
-          //   required: true,
-          //   label: 'نام درس',
-          //   align: 'left',
-          //   field: row => row.classroom_info.unit_info.title
-          // },
-          {
-            name: 'audience_gender_type',
-            required: true,
-            label: 'جنسیت',
-            align: 'left',
-            field: row => (new Classroom(row.classroom_info)).audience_gender_type_info.label
-          },
-          // {
-          //   name: 'title',
-          //   required: true,
-          //   label: 'نوع',
-          //   align: 'left',
-          //   field: row => (row.classroom_info?.unit_info?.category_info?.type === 'TRAINING') ? 'دوره آموزشی' : (row.classroom_info?.unit_info?.category_info?.type === 'DISCUSSION_CIRCLE') ? 'حلقه مباحثاتی' : '-'
-          // },
-          {
-            name: 'title',
-            required: true,
-            label: 'نوع برگزاری',
-            align: 'left',
-            field: row => this.getClassroomHoldingTypeTitle(row.classroom_info.holding_type)
-          },
-          {
-            name: 'title',
-            required: true,
-            label: 'استاد',
-            align: 'left',
-            field: row => row.classroom_info?.professor_info?.firstname + ' ' + row.classroom_info?.professor_info?.lastname
-          },
-          {
-            name: 'status',
-            required: true,
-            label: 'تعداد جلسات',
-            align: 'left',
-            field: row => row.classroom_info?.sessions_info?.length
-          },
-          {
-            name: 'status',
-            required: true,
-            label: 'وضعیت',
-            align: 'left',
-            field: row => (new Registration(row)).status_info.label
-            // field: row => this.getClassroomStatusTitle(row.classroom_info.status)
-          },
-          {
-            name: 'title',
-            required: true,
-            label: 'زمان ثبت نام',
-            align: 'left',
-            field: row => ShamsiDate.getDateTime(row.creation_time)
-          },
-          {
-            name: 'status',
-            required: true,
-            label: 'وضعیت مالی',
-            align: 'left',
-            field: row => (new Invoice(row.invoice_info)).status_info.label ? (new Invoice(row.invoice_info)).status_info.label : '-'
-          },
-          {
-            name: 'action',
-            required: true,
-            label: 'جزییات',
-            align: 'left',
-            field: ''
-          }
-        ]
-      },
-      tableKeys: {
-        data: 'results',
-        total: 'count',
-        currentPage: 'current',
-        perPage: 'per_page',
-        pageKey: 'page'
-      }
-    }
-  },
-  computed: {
-    selectedCategoryId () {
-      return this.getInputValue('category')
-    }
-  },
-  watch: {
-    selectedCategoryId () {
-      this.setInputValue('unit', null)
-      this.getUnits(this.selectedCategoryId)
+      sections: [{ data: { rows: [{ cols: [{ widgets: [{ name: 'ClassroomList', 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' } } }] } }]
     }
   },
-  mounted () {
-    this.setOwner()
-    this.setActionBtn()
-    this.loadInputDataOptions()
-    this.mounted = true
-  },
-  methods: {
-    setOwner () {
-      const user = this.$store.getters['Auth/user']
-      FormBuilderAssist.setAttributeByName(this.inputs, 'owner', 'value', user.id)
-    },
-    setActionBtn () {
-      FormBuilderAssist.setAttributeByName(this.inputs, 'btn', 'atClick', this.search)
-    },
-    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()
-    },
-    getCategories () {
-      APIGateway.classroom.index({ per_page: 9999 })
-        .then(classroomList => {
-          this.loadSelectOptions('category', this.getSelectOptions(classroomList.list.list, 'id', 'title'))
-        })
-        .catch(() => {
-        })
-    },
-    getUnits (selectedcategoryId) {
-      APIGateway.unit.index({ category: selectedcategoryId })
-        .then(unitList => {
-          this.loadSelectOptions('unit', this.getSelectOptions(unitList.list.list, 'id', 'title'))
-        })
-        .catch(() => {
-        })
-    },
-    getInputValue (name) {
-      const inputIndex = this.inputs.findIndex(input => input.name === name)
-      return this.inputs[inputIndex].value
-    },
-    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
-    }
+  created() {
+    this.currenSections = this.sections
   }
 }
 </script>