ali 3 лет назад
Родитель
Сommit
2e4823f1c4

+ 3 - 1
src/api/APIGateway.js

@@ -25,6 +25,7 @@ import SessionTemplateAPI from 'src/api/models/SessionTemplate.js'
 import ContentCategoryAPI from 'src/api/models/ContentCategory.js'
 import ContentCategoryAPI from 'src/api/models/ContentCategory.js'
 import InstalmentOfferAPI from 'src/api/models/InstalmentOffer.js'
 import InstalmentOfferAPI from 'src/api/models/InstalmentOffer.js'
 import ClassroomRegistrationAPI from 'src/api/models/ClassroomRegistration.js'
 import ClassroomRegistrationAPI from 'src/api/models/ClassroomRegistration.js'
+import ProjectAttendanceSheetsAPI from 'src/api/models/ProjectAttendanceSheets.js'
 
 
 /* Exporting the APIGateway object. */
 /* Exporting the APIGateway object. */
 export const APIGateway = {
 export const APIGateway = {
@@ -54,5 +55,6 @@ export const APIGateway = {
   sessionTemplate: new SessionTemplateAPI(),
   sessionTemplate: new SessionTemplateAPI(),
   contentCategory: new ContentCategoryAPI(),
   contentCategory: new ContentCategoryAPI(),
   instalmentOffer: new InstalmentOfferAPI(),
   instalmentOffer: new InstalmentOfferAPI(),
-  classroomRegistration: new ClassroomRegistrationAPI()
+  classroomRegistration: new ClassroomRegistrationAPI(),
+  projectAttendanceSheets: new ProjectAttendanceSheetsAPI()
 }
 }

+ 50 - 0
src/api/models/ProjectAttendanceSheets.js

@@ -0,0 +1,50 @@
+import { appApi } from 'src/boot/axios.js'
+import APIRepository from '../classes/APIRepository.js'
+import { ProjectAttendanceSheets, ProjectAttendanceSheetsList } from 'src/models/ProjectAttendanceSheets.js'
+
+export default class ProjectAttendanceSheetsAPI extends APIRepository {
+  constructor() {
+    super('project', appApi, '/lma/projects', ProjectAttendanceSheets)
+    this.APIAdresses = {
+      base: '/lma/project_attendance_sheets',
+      byId: (id) => '/lma/project_attendance_sheets/' + id
+    }
+    this.CacheList = {
+      base: this.name + this.APIAdresses.base,
+      byId: (id) => this.name + this.APIAdresses.byId(id)
+    }
+  }
+
+  index(data) {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.base,
+      data: this.getNormalizedSendData({
+        category: null, // Number
+        per_page: 10, // Number
+        page: 1 // Number
+      }, data),
+      resolveCallback: (response) => {
+        const paginate = response.data
+        const results = response.data.results
+        delete paginate.results
+        return {
+          list: new ProjectAttendanceSheetsList(results),
+          paginate
+          // {
+          //   "count": 1,
+          //   "num_pages": 1,
+          //   "per_page": 10,
+          //   "current": 1,
+          //   "next": null,
+          //   "previous": null,
+          // }
+        }
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+}

+ 9 - 2
src/components/FormBuilderCustumComponents/ContentsSelector/ContentItem.vue

@@ -11,21 +11,24 @@
                        width="100px" />
                        width="100px" />
               </div>
               </div>
               <div class="title ellipsis-2-lines">
               <div class="title ellipsis-2-lines">
+                عنوان محتوا:
                 {{ content.title }}
                 {{ content.title }}
               </div>
               </div>
             </div>
             </div>
             <div class="actions">
             <div class="actions">
-              <q-btn icon="isax:eye"
+              <q-btn icon="visibility"
                      outline
                      outline
                      color="primary"
                      color="primary"
                      class="viewBtn"
                      class="viewBtn"
                      @click="onView(content)" />
                      @click="onView(content)" />
-              <q-btn icon="isax:trash4"
+              <q-btn v-if="!readonly"
+                     icon="clear"
                      outline
                      outline
                      color="red"
                      color="red"
                      @click="onDelete(content)" />
                      @click="onDelete(content)" />
             </div>
             </div>
           </div>
           </div>
+          <q-separator />
           <div class="content-medias">
           <div class="content-medias">
             <content-medias :value="content.medias_info"
             <content-medias :value="content.medias_info"
                             :can-delete="false"
                             :can-delete="false"
@@ -48,6 +51,10 @@ export default {
     content: {
     content: {
       type: Content,
       type: Content,
       default: new Content()
       default: new Content()
+    },
+    readonly: {
+      default: false,
+      type: Boolean
     }
     }
   },
   },
   emits: ['update:value'],
   emits: ['update:value'],

+ 7 - 2
src/components/FormBuilderCustumComponents/ContentsSelector/ContentsSelector.vue

@@ -1,6 +1,10 @@
 <template>
 <template>
   <div class="ContentsSelector">
   <div class="ContentsSelector">
-    <div class="ContentCategorySelector">
+    <q-banner>
+      منابع
+    </q-banner>
+    <div v-if="!readonly"
+         class="ContentCategorySelector">
       <selector-component v-model:contents="selectedContents"
       <selector-component v-model:contents="selectedContents"
                           @update:contents="onSelectContent" />
                           @update:contents="onSelectContent" />
     </div>
     </div>
@@ -8,6 +12,7 @@
       <content-item v-for="(selectedContent, selectedContentIndex) in selectedContents"
       <content-item v-for="(selectedContent, selectedContentIndex) in selectedContents"
                     :key="selectedContentIndex"
                     :key="selectedContentIndex"
                     :content="selectedContent"
                     :content="selectedContent"
+                    :readonly="readonly"
                     @onView="onView"
                     @onView="onView"
                     @onDelete="onDelete" />
                     @onDelete="onDelete" />
     </div>
     </div>
@@ -30,7 +35,7 @@ export default {
       default: () => [],
       default: () => [],
       type: [Array, String, Number, Boolean]
       type: [Array, String, Number, Boolean]
     },
     },
-    disable: {
+    readonly: {
       default: false,
       default: false,
       type: Boolean
       type: Boolean
     }
     }

+ 19 - 0
src/models/ProjectAttendanceSheets.js

@@ -0,0 +1,19 @@
+import { Model, Collection } from 'js-abstract-model'
+
+class ProjectAttendanceSheets extends Model {
+  constructor (data) {
+    super(data, [
+      { key: 'id' },
+      { key: 'project' },
+      { key: 'answer_text' },
+      { key: 'answer_attachment' }
+    ])
+  }
+}
+
+class ProjectAttendanceSheetsList extends Collection {
+  model () {
+    return ProjectAttendanceSheets
+  }
+}
+export { ProjectAttendanceSheets, ProjectAttendanceSheetsList }

+ 0 - 7
src/pages/Admin/Classroom/project/show.vue

@@ -12,8 +12,6 @@
                v-model:value="inputs"
                v-model:value="inputs"
                title="جزییات جلسه"
                title="جزییات جلسه"
                :api="api"
                :api="api"
-               :entity-id-key="entityIdKey"
-               :entity-param-key="entityParamKey"
                :show-close-button="false"
                :show-close-button="false"
                :show-edit-button="false"
                :show-edit-button="false"
                :show-expand-button="false"
                :show-expand-button="false"
@@ -51,8 +49,6 @@ export default {
       newUnitName: null,
       newUnitName: null,
       newUnitSessionCount: null,
       newUnitSessionCount: null,
       api: APIGateway.project.APIAdresses.byId(projectId),
       api: APIGateway.project.APIAdresses.byId(projectId),
-      entityIdKey: 'id',
-      entityParamKey: 'id',
       inputs: [
       inputs: [
         { type: 'input', name: 'title', responseKey: 'title', label: 'عنوان پروژه', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'input', name: 'title', responseKey: 'title', label: 'عنوان پروژه', placeholder: ' ', col: 'col-md-6 col-12' },
         {
         {
@@ -83,9 +79,6 @@ export default {
     },
     },
     getRemoveMessage (row) {
     getRemoveMessage (row) {
       return 'آیا از حذف ' + row.title + ' اطمینان دارید؟'
       return 'آیا از حذف ' + row.title + ' اطمینان دارید؟'
-    },
-    reloadUnits () {
-      this.$refs.unitEntityIndex.reload()
     }
     }
   }
   }
 }
 }

+ 102 - 3
src/pages/User/Profile/classroomInfo.vue

@@ -22,7 +22,7 @@
              label="جدول آموزشی" />
              label="جدول آموزشی" />
       <q-tab name="movies1"
       <q-tab name="movies1"
              label="آزمون" />
              label="آزمون" />
-      <q-tab name="movies2"
+      <q-tab name="projects"
              label="پروژه" />
              label="پروژه" />
       <q-tab v-if="classroom.live_streaming_url"
       <q-tab v-if="classroom.live_streaming_url"
              name="live_streaming_url"
              name="live_streaming_url"
@@ -80,8 +80,39 @@
       <q-tab-panel name="movies1">
       <q-tab-panel name="movies1">
         آزمون
         آزمون
       </q-tab-panel>
       </q-tab-panel>
-      <q-tab-panel name="movies2">
-        پروژه
+      <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>
       <q-tab-panel v-if="classroom.live_streaming_url"
       <q-tab-panel v-if="classroom.live_streaming_url"
                    name="live_streaming_url">
                    name="live_streaming_url">
@@ -131,6 +162,8 @@ export default {
   },
   },
   mixins: [mixinAuth],
   mixins: [mixinAuth],
   data () {
   data () {
+    const classroomId = this.$route.params.id
+    const userId = this.$store.getters['Auth/user'].id
     return {
     return {
       mounted: false,
       mounted: false,
       tab: 'classroomInfo',
       tab: 'classroomInfo',
@@ -183,6 +216,72 @@ export default {
         currentPage: 'current',
         currentPage: 'current',
         perPage: 'per_page',
         perPage: 'per_page',
         pageKey: 'page'
         pageKey: 'page'
+      },
+
+      projectListInputs: [
+        { type: 'hidden', name: 'classroom', value: classroomId },
+        { type: 'hidden', name: 'owner', value: userId }
+      ],
+      projectListApi: APIGateway.project.APIAdresses.base,
+      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: '-'
+          },
+          {
+            name: 'actions',
+            required: true,
+            label: 'عملیات',
+            align: 'left',
+            field: ''
+          }
+        ]
+      },
+      projectListTableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
       }
       }
     }
     }
   },
   },

+ 160 - 0
src/pages/User/Profile/projectInfo.vue

@@ -0,0 +1,160 @@
+<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>
+  <entity-show v-if="mounted"
+               ref="projectEntityEdit"
+               v-model:value="inputs"
+               title="جزییات پروژه"
+               :api="api"
+               :show-index-button="false"
+               :show-close-button="false"
+               :show-edit-button="false"
+               :show-expand-button="false"
+               :show-save-button="false"
+               :show-reload-button="false"
+               :after-load-input-data="afterLoadProject">
+    <template #after-form-builder>
+      <q-separator class="q-my-lg" />
+      <entity-create ref="entityCreate"
+                     v-model:value="attendanceSheetsInputs"
+                     :api="attendanceSheetsApi"
+                     :default-layout="false" />
+    </template>
+  </entity-show>
+</template>
+
+<script>
+import { shallowRef } from 'vue'
+import { Project } from 'src/models/Project.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
+import { EntityShow, EntityCreate } from 'quasar-crud'
+import BtnControl from 'src/components/Control/btn.vue'
+import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import ContentsSelector from 'src/components/FormBuilderCustumComponents/ContentsSelector/ContentsSelector.vue'
+import { FormBuilderAssist } from 'quasar-form-builder'
+
+const BtnControlComp = shallowRef(BtnControl)
+const ContentsSelectorComp = shallowRef(ContentsSelector)
+
+export default {
+  name: 'UserPanel.Profile.ClassroomInfo',
+  components: {
+    Breadcrumbs,
+    EntityShow,
+    EntityCreate
+  },
+  data () {
+    const projectId = this.$route.params.project_id
+    return {
+      mounted: false,
+      projectLoaded: false,
+      classroomLoaded: false,
+      project: new Project(),
+      classroom: new Classroom(),
+      api: APIGateway.project.APIAdresses.byId(projectId),
+      inputs: [
+        { type: 'input', name: 'title', responseKey: 'title', label: 'عنوان پروژه', placeholder: ' ', col: 'col-md-6 col-12' },
+        {
+          type: 'select',
+          name: 'type',
+          responseKey: 'type',
+          label: 'نوع پروژه',
+          options: (new Project()).typeEnums,
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        { type: 'dateTime', name: 'beginning_doing_period', responseKey: 'beginning_doing_period', label: 'زمان شروع', placeholder: ' ', col: 'col-md-6 col-12' },
+        { type: 'dateTime', name: 'ending_doing_period', responseKey: 'ending_doing_period', label: 'زمان پایان', placeholder: ' ', col: 'col-md-6 col-12' },
+        { type: 'separator', name: 'space', size: '0', col: 'col-md-12' },
+        { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات پروژه', placeholder: ' ', col: 'col-12' },
+        { type: ContentsSelectorComp, name: 'contents', responseKey: 'contents_info', col: 'col-12' },
+        { type: 'hidden', name: 'id', responseKey: 'id' },
+        { type: 'hidden', name: 'classroom', responseKey: 'classroom' }
+      ],
+
+      attendanceSheetsApi: APIGateway.projectAttendanceSheets.APIAdresses.base,
+      attendanceSheetsInputs: [
+        { type: 'separator', name: 'space', label: 'انجام پروژه', className: 'custom-separator', col: 'col-12' },
+        { type: 'inputEditor', name: 'answer_text', label: 'متن پاسخ', placeholder: ' ', col: 'col-12' },
+        { type: 'file', name: 'answer_attachment', label: 'فای ضمیمه', placeholder: ' ', col: 'col-md-4 col-12' },
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'ارسال پروژه', placeholder: ' ', atClick: () => {}, col: 'col-12' },
+        { type: 'hidden', name: 'project', value: projectId }
+      ]
+    }
+  },
+  computed: {
+    projectAndClassroomLoaded () {
+      return this.projectLoaded && this.classroomLoaded
+    }
+  },
+  watch: {
+    projectAndClassroomLoaded (newValue) {
+      if (!newValue) {
+        return
+      }
+      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 } }
+          },
+          {
+            label: this.classroom.title,
+            to: { name: 'UserPanel.Profile.ClassroomInfo', params: { classroom_id: this.classroom.id, project_id: this.project.id } }
+          }
+        ]
+      })
+    }
+  },
+  mounted () {
+    this.mounted = true
+    this.getClassroomInfo()
+    FormBuilderAssist.setAttributeByName(this.attendanceSheetsInputs, 'btn', 'atClick', this.createAttendanceSheet)
+  },
+  methods: {
+    createAttendanceSheet () {
+      this.$refs.entityCreate.createEntity(false)
+        .then(() => {
+          this.reloadProject()
+        })
+        .catch(() => {
+          this.reloadProject()
+        })
+    },
+    getClassroomInfo () {
+      const classroomId = this.$route.params.classroom_id
+      this.classroom.loading = true
+      APIGateway.classroom.get(classroomId)
+        .then((classroom) => {
+          this.classroom = new Classroom(classroom)
+          this.classroom.loading = false
+          this.classroomLoaded = true
+        })
+        .catch(() => {
+          this.classroom.loading = false
+        })
+    },
+    afterLoadProject (data) {
+      this.project = new Project(data)
+      this.projectLoaded = true
+    },
+    reloadProject () {
+      this.$refs.projectEntityEdit.getData()
+    }
+  }
+}
+</script>

+ 1 - 0
src/router/routes.js

@@ -225,6 +225,7 @@ const routes = [
                   middlewares: [HasOverdueInstalment]
                   middlewares: [HasOverdueInstalment]
                 }
                 }
               },
               },
+              { name: 'UserPanel.Profile.Classroom.Project.Show', path: ':classroom_id/project/:project_id', component: () => import('src/pages/User/Profile/projectInfo.vue'), breadcrumbs: { title: 'مشاهده اطلاعات دوره آموزشی' } },
               {
               {
                 name: 'UserPanel.Profile.SessionInfo',
                 name: 'UserPanel.Profile.SessionInfo',
                 path: 'session/:id',
                 path: 'session/:id',