Jelajahi Sumber

update styles

ali 3 tahun lalu
induk
melakukan
185206f06a

+ 1 - 1
src/api/models/Wallet.js

@@ -7,7 +7,7 @@ export default class WalletAPI extends APIRepository {
     super('wallets', appApi)
     this.APIAdresses = {
       base: '/accounting/wallets',
-      deposit: '/accounting/wallets/deposit'
+      deposit: '/accounting/transactions/deposit'
     }
     this.restUrl = (id) => this.url + '/' + id
     /* Setting the callback functions for the CRUD operations. */

+ 25 - 311
src/components/Menu/SideMenu/MenuItem.vue

@@ -22,10 +22,8 @@
             <q-item v-else
                     v-ripple
                     clickable
-                    :active="isActive(subItem)"
                     :to="redirectRoute(subItem)"
-                    class="list-child-item"
-                    exact-active-class="active-route"
+                    exact
                     @click="itemSelected(subItem)">
               <q-tooltip v-if="showChildItemTooltip"
                          anchor="top middle"
@@ -51,11 +49,8 @@
     <q-item v-else-if="!loading && !item.children"
             v-ripple
             clickable
-            :active="isActive(item)"
             :to="redirectRoute(item)"
-            class="item-list"
-            :class="{ 'alone-item': !item.children }"
-            exact-active-class="active-route"
+            exact
             @click="itemSelected(item)">
       <div class="section-title">
         <q-item-section class="list-section title-icon"
@@ -161,316 +156,35 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.side-menu-main-layout {
-  display: flex;
-  flex-direction: column;
-  width: 280px;
-  min-height: 840px;
-  border-radius: 30px;
-  margin: 40px 24px;
-  @media screen and (max-width: 1919px) {
-    width: 260px;
-    min-height: 740px;
-    margin: 30px 30px 30px 24px;
-  }
-  @media screen and (max-width: 1439px) {
-    margin: 0 !important;
-    border-radius: 0;
-    width: 280px;
-    min-height: 680px;
-    height: 100%;
-  }
-  @media screen and (max-width: 599px) {
-    width: 100%;
-  }
-
-  .side-logo {
-    display: flex;
-    height: 167px;
-    align-items: center;
-    justify-content: center;
-    @media screen and (max-width: 1919px) {
-      height: 136px;
-    }
-    @media screen and (max-width: 1439px) {
-      height: 100px;
-    }
-    @media screen and (max-width: 599px) {
-      height: 110px;
-    }
-
-    .logo-image {
-      width: 140px;
-      height: 95px;
-      @media screen and (max-width: 1919px) {
-        height: 76px;
-      }
-      @media screen and (max-width: 1439px) {
-        width: 100px;
-        height: 50px;
-      }
-
-      .q-img__container {
-        width: 140px;
-        height: 95px;
-        @media screen and (max-width: 1919px) {
-          height: 76px;
-        }
-        @media screen and (max-width: 1439px) {
-          width: 100px;
-          height: 50px;
+.menu-item {
+  :deep(.q-expansion-item) {
+    .q-item {
+      .q-item__section {
+        &.q-item__section--avatar {
+          min-width: 0;
+          padding-right: 13px;
         }
       }
     }
-  }
-
-  .q-list {
-    padding: 0;
-
-    &.side-menu-list {
-      margin: 0 24px 109px 24px;
-      @media screen and (max-width: 1919px) {
-        margin: 0 24px 34px 24px;
-      }
-      @media screen and (max-width: 1439px) {
-        margin: 0 21px 26px 21px;
-      }
-      @media screen and (max-width: 599px) {
-        margin: 0 18px 8px 18px;
-      }
-
-      .top-separator {
-        margin: 0 40px 32px 40px;
-        @media screen and (max-width: 1919px) {
-          margin: 0 30px 25px 30px;
-        }
-        @media screen and (max-width: 1439px) {
-          margin: 0 45px 22px 45px;
-        }
-      }
-
-      .q-item {
-        padding: 0;
-        min-height: 0;
-
-        &.item-list {
-          display: flex;
-          flex-direction: column;
-          justify-content: center;
-          font-style: normal;
-          font-weight: 400;
-          font-size: 15px;
-          line-height: 28px;
-          cursor: pointer;
-          padding: 0 14px 0 10px;
-          border-radius: 14px;
-
-          &.alone-item {
-            height: 40px;
-
-            &.active-route {
-              .indicator {
-                height: 8px;
-                width: 8px;
-                background-color: white;
-                border-radius: 50%;
-                margin: auto;
-              }
-            }
-          }
-
-          .section-title {
-            height: 30px;
-            display: flex;
-            flex-direction: row;
-            align-items: center;
-
-            .title-icon {
-              margin-right: 12px;
-            }
-
-            .q-item__section--side {
-              padding: 0;
-            }
-          }
-
-          .list-section {
-            display: flex;
-            flex-direction: row;
-            justify-content: left;
-
-            .q-avatar {
-              height: 22px;
-              width: 22px;
-            }
-          }
-        }
-      }
-
-      .side-expansion-list {
-        margin-left: 12px;
-
-        :deep(.q-item) {
-          padding-left: 40px;
-        }
-        &.top-expansion {
-          margin-bottom: 10px;
-        }
-
-        .expansion-body {
-          color: v-bind('menuItemsColor');
-        }
-
-        .q-expansion-item__content {
-          .vertical-separator {
-            margin: 6px 9px 9px 9px;
-            @media screen and (max-width: 599px) {
-              margin: 0 10px 12px 10px;
-            }
-          }
-        }
-
-        .q-list {
-          &.list-expansion {
-            margin-bottom: 0;
-
-            .item-list-expansion {
-              height: 30px;
-              margin: 5px;
-
-              .item-list-expansion-title {
-                justify-content: start;
-                font-weight: 400;
-                font-size: 20px;
-                line-height: 28px;
-              }
-            }
-
-            .top-expansion-separator {
-              margin: 0 40px 5px 40px;
-              @media screen and (max-width: 1439px) {
-                margin: 0 30px 5px 30px;
-              }
-              @media screen and (max-width: 599px) {
-                margin: 0 45px 5px 45px;
-              }
-            }
-
-            .list-child-item {
-              height: 40px;
-              justify-content: right;
-              margin-bottom: 8px;
-              margin-left: 20px;
-              //width: 157px;
-              border-radius: 10px;
-              padding: 0 14px;
-              display: flex;
-              align-items: center;
-              .list-child-section {
-                font-weight: 400;
-                font-size: 15px;
-                line-height: 25px;
-                margin-right: 5px;
-              }
-              @media screen and (max-width: 1439px) {
-                width: 148px;
-              }
-              @media screen and (max-width: 599px) {
-                width: 160px;
-                margin-bottom: 5px;
-                padding: 0 10px 0 12px;
-              }
-
-              &:last-child {
-                margin-bottom: 0;
-              }
-
-              .list-child-section {
-                //font-size: 16px !important;
-                justify-content: center;
-              }
-            }
-          }
-        }
-      }
-
-      .active-route {
-        background-color: #ffe9cc;
-        .indicator {
-          height: 6px;
-          width: 6px;
-          background-color: white;
-          border-radius: 50%;
-          margin: auto;
-        }
-      }
-
-    }
-
-    .q-item__section--avatar {
-      min-width: 0 !important;
-    }
-
-    a {
-      text-decoration: none;
-      padding: 0;
+    .q-expansion-item__content {
+      //padding-left: 15px;
     }
   }
-
-  .log-out {
-    color: white;
-    font-size: 16px;
-    font-weight: 500;
-    cursor: pointer;
-    height: 40px !important;
-    //width: 232px;
-    border-radius: 14px;
-    display: flex;
-    align-items: center;
-    padding: 0 14px 0 10px;
-    margin: 0 0 36px 27px;
-    @media screen and (max-width: 1439px) {
-      margin: 0 31px 33px 31px;
-    }
-    @media screen and (max-width: 599px) {
-      margin: 0 30px 30px 30px;
-      //padding: 0 0 0 10px;
-    }
-
-    &:hover {
-      background-color: rgba(255, 255, 255, 0.1);
-    }
-
-    .q-avatar {
-      height: 22px;
-      width: 22px;
-      margin-right: 12px;
-      transform: matrix(-1, 0, 0, 1, 0, 0);
+  .q-item {
+    &.q-router-link--exact-active {
+      background: rgba(91, 122, 95, 0.08);
+      position: relative;
+      &:before {
+        content: ' ';
+        position: absolute;
+        left: 0;
+        top: 0;
+        width: 6px;
+        height: 100%;
+        background: #5B7A5F;
+        border-radius: 0 5px 5px 0;
+      }
     }
   }
 }
-.expansion-header {
-  font-size: 20px;
-  line-height: 28px;
-}
-</style>
-
-<style lang="css" scoped>
-.badge{
-  animation: badge 1s infinite;
-}
-@keyframes badge {
-  0% {
-    -moz-box-shadow:0 0 0 0 rgba(55, 55, 55, 0.68);
-    box-shadow:0 0 0 0 rgba(55, 55, 55, 0.68);
-  }
-  70% {
-    -moz-box-shadow:0 0 0 10px rgba(0,0,0,0);
-    box-shadow:0 0 0 10px rgba(0,0,0,0);
-  }
-  100% {
-    -moz-box-shadow:0 0 0 0 rgba(0,0,0,0);
-    box-shadow:0 0 0 0 rgba(0,0,0,0);
-  }
-}
 </style>

+ 5 - 5
src/components/Template/Footer/FooterTemplate.vue

@@ -1,17 +1,17 @@
 <template>
-  <div v-if="type === 'main'"
-       class="FooterTemplate">
-    <main-footer />
+  <div class="FooterTemplate">
+    <main-footer v-if="type === 'main'" />
+    <panel-footer v-if="type === 'panel'" />
   </div>
-  <div v-else />
 </template>
 
 <script>
 import MainFooter from 'src/components/Template/Footer/MainFooter.vue'
+import PanelFooter from 'src/components/Template/Footer/PanelFooter.vue'
 
 export default {
   name: 'FooterTemplate',
-  components: { MainFooter },
+  components: { MainFooter, PanelFooter },
   props: {
     type: [String, Boolean, null],
     default: () => 'main'

+ 37 - 0
src/components/Template/Footer/PanelFooter.vue

@@ -0,0 +1,37 @@
+<template>
+  <div class="PanelFooter">
+    <div class="copy-right">
+      تمام حقوق این سایت متعلق به
+      <span class="brand-text">
+        بنیاد فرهنگی شهید پالیزوانی
+      </span>
+      می باشد
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'PanelFooter'
+}
+</script>
+
+<style scoped lang="scss">
+.PanelFooter {
+  border-top: 1px solid #DFE1EC;
+  background: #FFFFFF;
+  height: 60px;
+  display: flex;
+  flex-flow: row;
+  justify-content: center;
+  align-items: center;
+  .copy-right {
+    font-size: 16px;
+    color: #212121;
+    letter-spacing: 0;
+    .brand-text {
+      color: #5B7A5F;
+    }
+  }
+}
+</style>

+ 2 - 0
src/components/Template/Header/AdminPanelHeader.vue

@@ -11,6 +11,7 @@
         <div class="right-side">
           <q-btn flat
                  icon="menu"
+                 class="lt-md"
                  @click="updateLayoutLeftDrawerVisible(true)" />
           <q-btn flat
                  :to="{name: 'Public.Home'}">
@@ -228,6 +229,7 @@ export default {
 <style scoped lang="scss">
 .header-inside {
   height: 100%;
+  border-bottom: 1px solid #DFE1EC;
   .admin-top-menu {
     display: flex;
     flex-flow: row;

+ 2 - 0
src/components/Template/Header/UserPanelHeader.vue

@@ -11,6 +11,7 @@
         <div class="right-side">
           <q-btn flat
                  icon="menu"
+                 class="lt-md"
                  @click="updateLayoutLeftDrawerVisible(true)" />
           <q-btn flat
                  :to="{name: 'Public.Home'}">
@@ -178,6 +179,7 @@ export default {
 
 .header-inside {
   height: 70px;
+  border-bottom: 1px solid #DFE1EC;
   .admin-top-menu {
     display: flex;
     flex-flow: row;

+ 4 - 171
src/components/Template/SideBard/AdminPanelSideBar.vue

@@ -1,25 +1,11 @@
 <template>
   <q-list class="side-menu-list"
           padding>
-    <q-input v-if="false"
-             v-model="searchText"
-             dense
-             standout="bg-deep-purple-5 text-white"
-             placeholder="جست و جو"
-             @update:model-value ="search(adminMenuItems)">
-      <template v-slot:append>
-        <q-icon name="search" />
-      </template>
-    </q-input>
-    <menu-item v-if="isAdminRoute"
-               :menu="adminMenuItems" />
-    <menu-item v-if="isProfilePage"
-               :menu="userProfileMenuItems" />
+    <menu-item :menu="adminMenuItems" />
   </q-list>
 </template>
 
 <script>
-import { User } from 'src/models/User.js'
 import menuItem from 'src/components/Menu/SideMenu/MenuItem.vue'
 
 export default {
@@ -27,10 +13,6 @@ export default {
   components: { menuItem },
   data () {
     return {
-      clickedItem: null,
-      searchText: '',
-      user: new User(),
-
       adminMenuItems: [
         {
           title: 'کاربران',
@@ -40,9 +22,10 @@ export default {
         },
         {
           title: 'سامانه آموزشی',
+          icon: 'isax:book',
           routeName: null,
           show: true,
-          open: false,
+          open: true,
           active: false,
           children: [
             {
@@ -59,157 +42,7 @@ export default {
             }
           ]
         }
-      ],
-      userProfileMenuItems: [
-        {
-          title: 'پیشخوان',
-          // routeName: 'Admin.User.Index',
-          scrollTo: '#introSection',
-          show: true,
-          active: false
-        },
-        {
-          title: 'مدیریت حلقه ها',
-          // routeName: 'Admin.User.Index',
-          scrollTo: '#introSection',
-          show: true,
-          active: false
-        },
-        {
-          title: 'دوره های آموزشی',
-          routeName: null,
-          show: true,
-          open: false,
-          active: false,
-          children: [
-            {
-              title: 'دوره های من',
-              routeName: 'UserPanel.Profile.AllClassrooms',
-              show: true,
-              active: false
-            },
-            {
-              title: 'کارنامه',
-              scrollTo: '#introSection',
-              // routeName: 'Admin.User.Index',
-              show: true,
-              active: false
-            }
-          ]
-        },
-        {
-          title: 'درخواست ها',
-          // routeName: 'Admin.User.Index',
-          scrollTo: '#introSection',
-          show: true,
-          active: false
-        },
-        {
-          title: 'بخش مالی',
-          routeName: null,
-          show: true,
-          open: false,
-          active: false,
-          children: [
-            {
-              title: 'فروشگاه',
-              scrollTo: '#introSection',
-              // routeName: 'Admin.User.Index',
-              show: true,
-              active: false
-            },
-            {
-              title: 'سوابق مالی',
-              scrollTo: '#introSection',
-              // routeName: 'Admin.User.Index',
-              show: true,
-              active: false
-            },
-            {
-              title: 'کیف پول الکترونیک',
-              scrollTo: '#introSection',
-              // routeName: 'Admin.User.Index',
-              show: true,
-              active: false
-            }
-          ]
-        }
-      ],
-
-      titlesList: []
-    }
-  },
-  computed: {
-    isAdminRoute () {
-      return this.$route.name && this.$route.name.includes('Admin.')
-    },
-    isProfilePage () {
-      return this.$route.name && this.$route.name.includes('UserPanel.Profile.')
-    }
-  },
-  mounted () {
-    this.loadAuthData()
-  },
-  methods: {
-    loadAuthData () {
-      this.user = this.$store.getters['Auth/user']
-    },
-    loadUserItems () {
-      if (this.user.isSuperUser()) {
-        this.titlesList.unshift(...[
-          {
-            title: 'کاربران',
-            routeName: 'Admin.User.Index',
-            show: true,
-            active: false
-          },
-          {
-            title: 'سامانه آموزشی',
-            routeName: null,
-            show: true,
-            open: false,
-            active: false,
-            children: [
-              {
-                title: 'دسته بندی ها',
-                routeName: 'Admin.Category.Index',
-                show: true,
-                active: false
-              },
-              {
-                title: 'دوره های آموزشی',
-                routeName: 'Admin.Classroom.Index',
-                show: true,
-                active: false
-              }
-            ]
-          }
-        ])
-      }
-    },
-    search (list, parentContain = false) {
-      if (!list || list.length === 0) {
-        return false
-      }
-      if (parentContain) {
-        return true
-      }
-      let flag = false
-      list.forEach(item => {
-        const contain = item.title.includes(this.searchText)
-        if (this.search(item.children, contain) || contain) {
-          flag = true
-          item.show = true
-          item.open = true
-        } else {
-          item.open = false
-          item.show = false
-        }
-      })
-      return flag
-    },
-    logOut () {
-      return this.$store.dispatch('Auth/logOut')
+      ]
     }
   }
 }

+ 2 - 9
src/components/Template/SideBard/UserPanelSideBar.vue

@@ -96,16 +96,9 @@ export default {
           active: false,
           children: [
             {
-              title: 'فروشگاه',
+              title: 'صورتحساب ها',
               scrollTo: '#introSection',
-              // routeName: 'Admin.User.Index',
-              show: true,
-              active: false
-            },
-            {
-              title: 'سوابق مالی',
-              scrollTo: '#introSection',
-              // routeName: 'Admin.User.Index',
+              routeName: 'UserPanel.Invoice.List',
               show: true,
               active: false
             },

+ 74 - 45
src/components/Widgets/Invoice/InvoiceList/InvoiceList.vue

@@ -1,45 +1,43 @@
 <template>
   <div class="InvoiceList"
        :style="localOptions.style">
-    <div class="title">
-      صورت‌حساب‌ها
-    </div>
-    <q-card class="list"
-            flat>
-      <entity-index v-if="mounted"
-                    v-model:value="inputs"
-                    title=""
-                    :api="api"
-                    :table="table"
-                    :table-keys="tableKeys"
-                    :create-route-name="createRouteName"
-                    :show-search-button="false"
-                    :show-expand-button="false"
-                    :show-reload-button="false"
-                    :default-layout="false">
-        <template #entity-index-table-cell="{inputData}">
-          <template v-if="inputData.col.name === 'action'">
-            <q-btn flat
-                   color="primary"
-                   :to="{name: 'UserPanel.Invoice.Show', params: {id: inputData.props.row.id}}">
-              مشاهده جزییات
-            </q-btn>
-          </template>
-          <template v-else>
-            {{ inputData.col.value }}
-          </template>
+    <entity-index v-if="mounted"
+                  ref="entityIndex"
+                  v-model:value="inputs"
+                  title="لیست صورتحساب ها"
+                  :api="api"
+                  :table="table"
+                  :table-keys="tableKeys"
+                  :create-route-name="createRouteName"
+                  :show-search-button="false"
+                  :show-expand-button="false"
+                  :show-reload-button="false">
+      <template #entity-index-table-cell="{inputData}">
+        <template v-if="inputData.col.name === 'action'">
+          <q-btn flat
+                 color="primary"
+                 :to="{name: 'UserPanel.Invoice.Show', params: {id: inputData.props.row.id}}">
+            مشاهده جزییات
+          </q-btn>
         </template>
-      </entity-index>
-    </q-card>
+        <template v-else>
+          {{ inputData.col.value }}
+        </template>
+      </template>
+    </entity-index>
   </div>
 </template>
 
 <script>
+import { shallowRef } from 'vue'
 import Assist from 'assets/js/Assist.js'
 import { EntityIndex } from 'quasar-crud'
 import { Invoice } from 'src/models/Invoice.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
+import BtnControl from 'src/components/Control/btn.vue'
+
+const BtnControlComp = shallowRef(BtnControl)
 
 export default {
   name: 'InvoiceList',
@@ -47,7 +45,7 @@ export default {
   mixins: [mixinWidget],
   data: () => {
     return {
-      api: APIGateway.invoice.APIAdresses.base,
+      api: APIGateway.payment.APIAdresses.base,
       tableKeys: {
         data: 'results',
         total: 'count',
@@ -55,50 +53,68 @@ export default {
         perPage: 'per_page',
         pageKey: 'page'
       },
-      inputs: [],
+      inputs: [
+        {
+          type: 'date',
+          name: 'creation_time',
+          label: 'از تاریخ',
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        {
+          type: 'date',
+          name: 'creation_time',
+          label: 'تا تاریخ',
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        { type: 'select', name: 'category', options: [], label: 'وضعیت تراکنش', placeholder: ' ', col: 'col-md-2 col-12' },
+        { type: 'input', name: 'category', label: 'نوع صورتحساب', placeholder: ' ', col: 'col-md-2 col-12' },
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'جستجو', placeholder: ' ', atClick: () => {}, col: 'col-md-2 col-12' }
+      ],
       table: {
         columns: [
           {
             name: 'id',
             required: true,
-            label: 'صورت‌حساب',
+            label: 'شناسه',
             align: 'left',
             field: row => row.id
           },
           {
             name: 'title',
             required: true,
-            label: 'پکیج یا خدمت',
+            label: 'عنوان',
             align: 'left',
             field: row => row.title
           },
           {
-            name: 'creation_time',
+            name: 'amount',
             required: true,
-            label: 'تاریخ صورت‌حساب',
+            label: 'مبلغ تراکنش (تومان)',
             align: 'left',
-            field: row => Assist.miladiToShamsi(row.creation_time)
+            field: row => row.amount.toLocaleString('fa')
           },
           {
-            name: 'creation_time',
+            name: 'invoice_info.status',
             required: true,
-            label: 'تاریخ سررسید',
+            label: 'وضعیت تراکنش',
             align: 'left',
-            field: row => Assist.miladiToShamsi(row.creation_time)
+            field: row => (new Invoice(row.invoice_info)).status_info.label
           },
           {
-            name: 'amount',
+            name: 'creation_time',
             required: true,
-            label: 'کل',
+            label: 'زمان ثبت',
             align: 'left',
-            field: row => row.amount.toLocaleString('fa')
+            field: row => Assist.miladiToShamsi(row.creation_time)
           },
           {
-            name: 'status',
+            name: 'creation_time',
             required: true,
-            label: 'وضعیت',
+            label: 'زمان آخرین بروز رسانی',
             align: 'left',
-            field: row => (new Invoice(row)).status_info.label
+            field: row => Assist.miladiToShamsi(row.creation_time)
           },
           {
             name: 'action',
@@ -114,7 +130,20 @@ export default {
     }
   },
   mounted() {
+    this.setActionBtn()
     this.mounted = true
+  },
+  methods: {
+    setActionBtn () {
+      this.inputs.forEach((item, index) => {
+        if (item.name === 'btn') {
+          this.inputs[index].atClick = this.search
+        }
+      })
+    },
+    search () {
+      this.$refs.entityIndex.search()
+    }
   }
 }
 </script>

+ 130 - 0
src/components/Widgets/User/AdminUserList/AdminUserList.vue

@@ -0,0 +1,130 @@
+<template>
+  <div class="AdminUserList"
+       :style="localOptions.style">
+    <div class="title">
+      پکیج‌ها
+    </div>
+    <q-card class="list"
+            flat>
+      <entity-index v-if="mounted"
+                    v-model:value="inputs"
+                    title=""
+                    :api="api"
+                    :table="table"
+                    :table-keys="tableKeys"
+                    :create-route-name="createRouteName"
+                    :show-search-button="false"
+                    :show-expand-button="false"
+                    :show-reload-button="false"
+                    :default-layout="false">
+        <template #entity-index-table-cell="{inputData}">
+          <template v-if="inputData.col.name === 'action'">
+            <q-btn flat
+                   color="primary"
+                   :to="{name: 'AdminPanel.User.Show', params: {id: inputData.props.row.id}}">
+              مشاهده جزییات
+            </q-btn>
+          </template>
+          <template v-else>
+            {{ inputData.col.value }}
+          </template>
+        </template>
+      </entity-index>
+    </q-card>
+  </div>
+</template>
+
+<script>
+import { EntityIndex } from 'quasar-crud'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+
+export default {
+  name: 'AdminUserList',
+  components: { EntityIndex },
+  mixins: [mixinWidget],
+  data: () => {
+    return {
+      api: APIGateway.user.APIAdresses.base,
+      tableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+      inputs: [],
+      table: {
+        columns: [
+          {
+            name: 'firstname',
+            required: true,
+            label: 'نام',
+            align: 'left',
+            field: row => row.firstname
+          },
+          {
+            name: 'lastname',
+            required: true,
+            label: 'نام خانوادگی',
+            align: 'left',
+            field: row => row.lastname
+          },
+          {
+            name: 'mobile_number',
+            required: true,
+            label: 'شماره موبایل',
+            align: 'left',
+            field: row => row.mobile_number
+          },
+          {
+            name: 'roles',
+            required: true,
+            label: 'نقش',
+            align: 'left',
+            field: row => row.roles.join(', ')
+          },
+          {
+            name: 'action',
+            required: true,
+            label: 'جزییات',
+            align: 'left',
+            field: row => ''
+          }
+        ]
+      },
+      mounted: false,
+      createRouteName: ''
+    }
+  },
+  mounted() {
+    this.mounted = true
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.AdminUserList {
+  .title {
+    font-style: normal;
+    font-weight: 700;
+    font-size: 24px;
+    line-height: 140%;
+    color: #424242;
+    margin-bottom: 27px;
+  }
+  :deep(.list) {
+    .quasar-crud-index-table {
+      padding: 0;
+      .q-table__container {
+        background-color: transparent;
+        box-shadow: none;
+        border: none;
+        .q-table__top {
+          display: none;
+        }
+      }
+    }
+  }
+}
+</style>

+ 21 - 0
src/components/Widgets/User/AdminUserList/OptionPanel.vue

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

+ 191 - 0
src/components/Widgets/User/AdminUserShow/AdminUserShow.vue

@@ -0,0 +1,191 @@
+<template>
+  <div class="AdminUserShow"
+       :style="localOptions.style">
+    <div class="title">
+      <div class="static-title">
+        ویرایش
+      </div>
+      <div class="dynamic-title">
+        <template v-if="entityLoading">
+          <q-skeleton type="text"
+                      class="q-ml-lg"
+                      width="100px" />
+        </template>
+        <template v-else>
+          {{ entityTitle }}
+        </template>
+      </div>
+      <div class="back-action">
+        <q-btn flat
+               :to="{name: 'AdminPanel.User.List'}"
+               color="grey">
+          بازگشت
+          >
+        </q-btn>
+      </div>
+    </div>
+    <q-card class="form"
+            flat>
+      <entity-edit v-if="mounted"
+                   ref="entityEdit"
+                   v-model:value="inputs"
+                   title="اطلاعات کاربری"
+                   :api="api"
+                   :entity-id-key="entityIdKey"
+                   :entity-param-key="entityParamKey"
+                   :show-route-name="showRouteName"
+                   :show-close-button="false"
+                   :show-edit-button="false"
+                   :show-expand-button="false"
+                   :show-save-button="false"
+                   :show-reload-button="false"
+                   :default-layout="false"
+                   :redirect-after-edit="false"
+                   :after-load-input-data="afterLoadInputData" />
+      <div class="action">
+        <div class="row q-mt-lg justify-end">
+          <div class="col-md-4 col-12">
+            <q-btn color="primary"
+                   class="full-width"
+                   :loading="entityLoading"
+                   @click="edit">
+              ثبت تغییرات
+            </q-btn>
+          </div>
+        </div>
+      </div>
+    </q-card>
+  </div>
+</template>
+
+<script>
+import { EntityEdit } from 'quasar-crud'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { User } from 'src/models/User'
+
+export default {
+  name: 'AdminUserShow',
+  components: {
+    EntityEdit
+  },
+  mixins: [mixinWidget],
+  data: () => {
+    return {
+      mounted: false,
+      entityLoading: true,
+      entityTitle: '',
+      api: APIGateway.user.APIAdresses.base,
+      entityIdKey: 'id',
+      entityParamKey: 'id',
+      showRouteName: 'AdminPanel.Package.Show',
+      indexRouteName: 'AdminPanel.Package.List',
+      inputs: [
+        {
+          type: 'file',
+          name: 'picture',
+          responseKey: 'picture',
+          label: 'تصویر پروفایل',
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        { type: 'separator', name: 'space', size: '0', col: 'col-md-12' },
+        {
+          type: 'input',
+          name: 'firstname',
+          responseKey: 'firstname',
+          label: 'نام',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'input',
+          name: 'lastname',
+          responseKey: 'lastname',
+          label: 'نام خانوادگی',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'select',
+          name: 'roles',
+          responseKey: 'roles',
+          options: (new User()).totalRoles,
+          multiple: true,
+          label: 'نقش ها',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'input',
+          name: 'mobile_number',
+          responseKey: 'mobile_number',
+          label: 'شماره موبایل',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'hidden',
+          name: 'id',
+          responseKey: 'id'
+        }
+      ]
+    }
+  },
+  created() {
+    this.api = this.api + '/' + this.$route.params.id
+  },
+  mounted() {
+    this.mounted = true
+  },
+  methods: {
+    onUpdateServices () {
+      this.$refs.entityEdit.getData()
+    },
+    afterLoadInputData (response) {
+      this.entityTitle = response.firstname + ' ' + response.lastname
+      this.entityLoading = false
+    },
+    edit() {
+      this.entityLoading = true
+      this.$refs.entityEdit.editEntity()
+        .then(() => {
+          this.$refs.entityEdit.getData()
+          this.entityLoading = false
+        })
+        .catch(() => {
+          this.$refs.entityEdit.getData()
+          this.entityLoading = false
+        })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.AdminUserShow {
+  .title {
+    font-style: normal;
+    font-weight: 700;
+    font-size: 24px;
+    line-height: 140%;
+    color: #424242;
+    margin-bottom: 27px;
+    display: flex;
+    flex-flow: row;
+    .static-title {
+
+    }
+    .dynamic-title {
+
+    }
+    .back-action {
+      position: absolute;
+      right: 0;
+      top: 0;
+    }
+  }
+  :deep(.form) {
+    padding: 24px;
+  }
+}
+</style>

+ 21 - 0
src/components/Widgets/User/AdminUserShow/OptionPanel.vue

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

+ 21 - 0
src/components/Widgets/User/Profile/OptionPanel.vue

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

+ 166 - 0
src/components/Widgets/User/Profile/Profile.vue

@@ -0,0 +1,166 @@
+<template>
+  <div class="Profile"
+       :style="localOptions.style">
+    <div class="title">
+      <div class="static-title">
+        ویرایش حساب
+      </div>
+      <div class="back-action">
+        <q-btn flat
+               :to="{name: 'UserPanel.Dashboard'}"
+               color="grey">
+          بازگشت
+          >
+        </q-btn>
+      </div>
+    </div>
+    <q-card class="form"
+            flat>
+      <entity-edit v-if="mounted"
+                   ref="entityEdit"
+                   v-model:value="inputs"
+                   :api="api"
+                   :entity-id-key="entityIdKey"
+                   :entity-param-key="entityParamKey"
+                   :show-route-name="showRouteName"
+                   :show-close-button="false"
+                   :show-edit-button="false"
+                   :show-expand-button="false"
+                   :show-save-button="false"
+                   :show-reload-button="false"
+                   :default-layout="false"
+                   :redirect-after-edit="false"
+                   :after-load-input-data="afterLoadInputData" />
+      <div class="action">
+        <div class="row q-mt-lg justify-end">
+          <div class="col-md-4 col-12">
+            <q-btn color="primary"
+                   class="full-width"
+                   :loading="entityLoading"
+                   @click="edit">
+              ثبت تغییرات
+            </q-btn>
+          </div>
+        </div>
+      </div>
+    </q-card>
+  </div>
+</template>
+
+<script>
+import { EntityEdit } from 'quasar-crud'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+
+export default {
+  name: 'Profile',
+  components: {
+    EntityEdit
+  },
+  mixins: [mixinWidget],
+  data: () => {
+    return {
+      mounted: false,
+      entityLoading: true,
+      entityTitle: '',
+      api: APIGateway.user.APIAdresses.current,
+      entityIdKey: 'id',
+      entityParamKey: 'id',
+      showRouteName: 'AdminPanel.Package.Show',
+      indexRouteName: 'UserPanel.Dashboard',
+      inputs: [
+        {
+          type: 'file',
+          name: 'picture',
+          responseKey: 'picture',
+          label: 'تصویر پروفایل',
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        { type: 'separator', name: 'space', size: '0', col: 'col-md-12' },
+        {
+          type: 'input',
+          name: 'firstname',
+          responseKey: 'firstname',
+          label: 'نام',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'input',
+          name: 'lastname',
+          responseKey: 'lastname',
+          label: 'نام خانوادگی',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'input',
+          name: 'mobile_number',
+          responseKey: 'mobile_number',
+          label: 'شماره موبایل',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'hidden',
+          name: 'id',
+          responseKey: 'id'
+        }
+      ]
+    }
+  },
+  mounted() {
+    this.mounted = true
+  },
+  methods: {
+    onUpdateServices () {
+      this.$refs.entityEdit.getData()
+    },
+    afterLoadInputData () {
+      this.entityLoading = false
+    },
+    edit() {
+      this.entityLoading = true
+      this.$refs.entityEdit.editEntity(false)
+        .then(() => {
+          this.$refs.entityEdit.getData()
+          this.entityLoading = false
+        })
+        .catch(() => {
+          this.$refs.entityEdit.getData()
+          this.entityLoading = false
+        })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.Profile {
+  .title {
+    font-style: normal;
+    font-weight: 700;
+    font-size: 24px;
+    line-height: 140%;
+    color: #424242;
+    margin-bottom: 27px;
+    display: flex;
+    flex-flow: row;
+    .static-title {
+
+    }
+    .dynamic-title {
+
+    }
+    .back-action {
+      position: absolute;
+      right: 0;
+      top: 0;
+    }
+  }
+  :deep(.form) {
+    padding: 24px;
+  }
+}
+</style>

+ 5 - 3
src/router/routes.js

@@ -86,7 +86,7 @@ const routes = [
         path: 'panel',
         name: 'UserPanel',
         layoutConfig: {
-          layoutView: 'hHh LpR fff',
+          layoutView: 'hHh LpR lfr',
           layoutHeaderType: 'user-panel',
           layoutHeaderBordered: true,
           layoutLeftDrawer: true,
@@ -94,6 +94,7 @@ const routes = [
           layoutLeftSideBarType: 'user-panel',
           layoutLeftDrawerBordered: true,
           layoutLeftDrawerWidth: 240,
+          layoutFooterType: 'panel',
           layoutFooter: true
         },
         meta: { middlewares: [Authenticated, canSeeUserProfilePanel] },
@@ -199,7 +200,7 @@ const routes = [
         path: 'admin',
         name: 'Admin',
         layoutConfig: {
-          layoutView: 'hHh LpR fff',
+          layoutView: 'hHh LpR lfr',
           layoutHeaderType: 'admin',
           layoutHeaderBordered: true,
           layoutLeftDrawer: true,
@@ -207,7 +208,8 @@ const routes = [
           layoutLeftSideBarType: 'admin',
           layoutLeftDrawerBordered: true,
           layoutLeftDrawerWidth: 240,
-          layoutFooter: false
+          layoutFooterType: 'panel',
+          layoutFooter: true
         },
         meta: { middlewares: [Authenticated, canSeeAdminPanel] },
         component: () => import('src/layouts/AdminLayout.vue'),