Просмотр исходного кода

Merge branch 'master' of http://git.hghweb.ir/ali.s/palizvani-front-vite into production

 Conflicts:
	src/components/Template/Header/MainHeader.vue
	src/components/Template/SideBar/Main.vue
HgH 1 год назад
Родитель
Сommit
41bef3f197
100 измененных файлов с 11347 добавлено и 552 удалено
  1. 1 1
      package.json
  2. 4 0
      src/api/APIGateway.js
  3. 32 0
      src/api/models/Aggregator.js
  4. 100 0
      src/api/models/AppSetting.js
  5. 2 1
      src/api/models/Basket.js
  6. 34 1
      src/api/models/Product.js
  7. 2 2
      src/assets/ShamsiDate.js
  8. 6915 0
      src/assets/js/IranianCities.js
  9. 5 1
      src/components/FormBuilderCustumComponents/ContentCategorySelector.vue
  10. 15 14
      src/components/FormBuilderCustumComponents/ContentMedias/ContentMedias.vue
  11. 36 9
      src/components/FormBuilderCustumComponents/ProductSelector.vue
  12. 1 1
      src/components/InvoicePaymentCard/InvoicePaymentCard.vue
  13. 2 2
      src/components/Template/Header/MainHeader.vue
  14. 14 0
      src/components/Template/SideBar/AdminPanelSideBar.vue
  15. 2 2
      src/components/Template/SideBar/Main.vue
  16. 1 0
      src/components/Widgets/Content/AdminContentCreate/AdminContentCreate.vue
  17. 9 0
      src/components/Widgets/Content/AdminContentList/AdminContentList.vue
  18. 1 0
      src/components/Widgets/Content/AdminContentShow/AdminContentShow.vue
  19. 158 0
      src/components/Widgets/Content/ContentCarousel/ContentCarousel.vue
  20. 74 0
      src/components/Widgets/Content/ContentCarousel/OptionPanel.vue
  21. 128 0
      src/components/Widgets/Content/ContentCarousel/componetns/ContentItem.vue
  22. 1 1
      src/components/Widgets/Content/ContentShow/components/productItem.vue
  23. 2 2
      src/components/Widgets/Content/ContentTopicList/components/packageItem.vue
  24. 2 2
      src/components/Widgets/Content/ContentTopicList/components/productItem.vue
  25. 90 0
      src/components/Widgets/Content/LastContentTopics/LastContentTopics.vue
  26. 0 0
      src/components/Widgets/Content/LastContentTopics/OptionPanel.vue
  27. 84 0
      src/components/Widgets/Content/LastContentTopics/TopicItem.vue
  28. 0 61
      src/components/Widgets/Content/LastContents/LastContents.vue
  29. 0 98
      src/components/Widgets/Content/LastContents/PostItem.vue
  30. 152 0
      src/components/Widgets/Donation/DonationIntro/DonationIntro.vue
  31. 100 0
      src/components/Widgets/Donation/DonationIntro/OptionPanel.vue
  32. 128 0
      src/components/Widgets/Donation/DonationIntro/componetns/ContentItem.vue
  33. 86 22
      src/components/Widgets/Donation/DonationShow/DonationShow.vue
  34. 1 1
      src/components/Widgets/Event/LastEvents/LastEvents.vue
  35. 28 4
      src/components/Widgets/FeatureBox/OptionPanel.vue
  36. 1 5
      src/components/Widgets/Invoice/AdminInvoiceList/AdminInvoiceList.vue
  37. 179 5
      src/components/Widgets/Invoice/AdminInvoiceShow/AdminInvoiceShow.vue
  38. 4 2
      src/components/Widgets/Invoice/InvoiceShow/InvoiceShow.vue
  39. 1 1
      src/components/Widgets/Other/ClassroomCarousel/ClassroomCarousel.vue
  40. 197 4
      src/components/Widgets/Other/Shop/ShopCompleteInfo/ShopCompleteInfo.vue
  41. 192 0
      src/components/Widgets/Post/LastPosts/LastPosts.vue
  42. 65 0
      src/components/Widgets/Post/LastPosts/OptionPanel.vue
  43. 149 0
      src/components/Widgets/Post/LastPosts/components/PostItem.vue
  44. 6 0
      src/components/Widgets/Post/PostShow/PostShow.vue
  45. 1 1
      src/components/Widgets/ProjectAttendanceSheet/ProjectAttendanceSheetShow/ProjectAttendanceSheetShow.vue
  46. 1 1
      src/components/Widgets/Registration/RegistrationShow/RegistrationShow.vue
  47. 48 33
      src/components/Widgets/Statictic/PublicWebStatistic/PublicWebStatistic.vue
  48. 68 0
      src/components/Widgets/Statictic/PublicWebStatistic/components/Item.vue
  49. 53 10
      src/components/Widgets/Store/Basket/AdminStoreBasketList/AdminStoreBasketList.vue
  50. 135 0
      src/components/Widgets/Store/Basket/AdminStoreBasketPurchaseRegulations/AdminStoreBasketPurchaseRegulations.vue
  51. 21 0
      src/components/Widgets/Store/Basket/AdminStoreBasketPurchaseRegulations/OptionPanel.vue
  52. 56 11
      src/components/Widgets/Store/Basket/AdminStoreBasketShow/AdminStoreBasketShow.vue
  53. 113 0
      src/components/Widgets/Store/Basket/AdminStoreDeliveryCost/AdminStoreDeliveryCost.vue
  54. 21 0
      src/components/Widgets/Store/Basket/AdminStoreDeliveryCost/OptionPanel.vue
  55. 5 5
      src/components/Widgets/Store/Basket/UserStoreBasketList/UserStoreBasketList.vue
  56. 57 0
      src/components/Widgets/Store/Basket/UserStoreBasketShow/ProductOrPackageMedias.vue
  57. 56 14
      src/components/Widgets/Store/Basket/UserStoreBasketShow/UserStoreBasketShow.vue
  58. 2 2
      src/components/Widgets/Store/Cart/components/packageItem.vue
  59. 2 2
      src/components/Widgets/Store/Cart/components/productItem.vue
  60. 24 5
      src/components/Widgets/Store/Cart/components/step1/cartItem.vue
  61. 5 5
      src/components/Widgets/Store/Cart/components/step1/sidebar.vue
  62. 51 3
      src/components/Widgets/Store/Cart/components/step2/cartAddress.vue
  63. 28 4
      src/components/Widgets/Store/Cart/components/step2/deliveryInfo.vue
  64. 33 6
      src/components/Widgets/Store/Cart/components/step2/sidebar.vue
  65. 2 2
      src/components/Widgets/Store/Cart/components/step2/step2.vue
  66. 2 2
      src/components/Widgets/Store/Shop/Shop.vue
  67. 50 27
      src/components/Widgets/Store/Shop/components/packageItem.vue
  68. 48 27
      src/components/Widgets/Store/Shop/components/productItem.vue
  69. 3 3
      src/components/Widgets/Store/Shop/components/sidebar.vue
  70. 9 8
      src/components/Widgets/Store/StorePackage/AdminStorePackageCreate/AdminStorePackageCreate.vue
  71. 56 3
      src/components/Widgets/Store/StorePackage/AdminStorePackageList/AdminStorePackageList.vue
  72. 3 2
      src/components/Widgets/Store/StorePackage/AdminStorePackageShow/AdminStorePackageShow.vue
  73. 114 44
      src/components/Widgets/Store/StorePackage/StorePackageShow/StorePackageShow.vue
  74. 4 3
      src/components/Widgets/Store/StorePackage/StorePackageShow/components/productItem.vue
  75. 4 3
      src/components/Widgets/Store/StoreProduct/AdminStoreProductCreate/AdminStoreProductCreate.vue
  76. 55 2
      src/components/Widgets/Store/StoreProduct/AdminStoreProductList/AdminStoreProductList.vue
  77. 3 3
      src/components/Widgets/Store/StoreProduct/AdminStoreProductShow/AdminStoreProductShow.vue
  78. 1 1
      src/components/Widgets/Store/StoreProduct/ProductCarousel/ProductCarousel.vue
  79. 49 27
      src/components/Widgets/Store/StoreProduct/ProductCarousel/productItem.vue
  80. 88 16
      src/components/Widgets/Store/StoreProduct/StoreProductShow/StoreProductShow.vue
  81. 1 1
      src/components/Widgets/Store/StoreProduct/StoreProductShow/components/productItem.vue
  82. 21 0
      src/components/Widgets/Store/StoreProduct/StoreRelatedProducts/OptionPanel.vue
  83. 160 0
      src/components/Widgets/Store/StoreProduct/StoreRelatedProducts/StoreRelatedProducts.vue
  84. 248 0
      src/components/Widgets/Store/StoreProduct/StoreRelatedProducts/components/productItem.vue
  85. 56 3
      src/components/Widgets/Team/AdminTeamShow/AdminTeamShow.vue
  86. 30 5
      src/components/Widgets/TextWidget/OptionPanel.vue
  87. 1 1
      src/components/Widgets/User/Profile/Profile.vue
  88. 9 5
      src/components/cart/cartCountAction/cartCountAction.vue
  89. 1 0
      src/css/Theme/Controls/carousel.scss
  90. 1 1
      src/css/Theme/Widgets/widgets.scss
  91. 19 0
      src/models/Basket.js
  92. 21 1
      src/models/Content.js
  93. 5 0
      src/models/Invoice.js
  94. 29 1
      src/models/Package.js
  95. 1 0
      src/models/Post.js
  96. 30 2
      src/models/Product.js
  97. 23 0
      src/pages/Admin/StoreBasket/DeliveryCost.vue
  98. 23 0
      src/pages/Admin/StoreBasket/PurchaseRegulations.vue
  99. 193 10
      src/pages/Admin/User/show.vue
  100. 200 5
      src/pages/User/Profile/profile.vue

+ 1 - 1
package.json

@@ -56,7 +56,7 @@
     "vue-router": "^4.1.6",
     "vue-router": "^4.1.6",
     "vue-tiptap-katex-core": "^2.1.5",
     "vue-tiptap-katex-core": "^2.1.5",
     "vue3-otp-input": "^0.4.1",
     "vue3-otp-input": "^0.4.1",
-    "vue3-tiptap-katex": "^1.1.27",
+    "vue3-tiptap-katex": "^1.1.31",
     "vuex": "^4.1.0",
     "vuex": "^4.1.0",
     "vuex-persistedstate": "^4.1.0",
     "vuex-persistedstate": "^4.1.0",
     "workbox-build": "^7.0.0",
     "workbox-build": "^7.0.0",

+ 4 - 0
src/api/APIGateway.js

@@ -25,6 +25,8 @@ import DonationAPI from 'src/api/models/Donation.js'
 import QuestionApi from 'src/api/models/Question.js'
 import QuestionApi from 'src/api/models/Question.js'
 import DiscountAPI from 'src/api/models/Discount.js'
 import DiscountAPI from 'src/api/models/Discount.js'
 import ClassroomAPI from 'src/api/models/Classroom.js'
 import ClassroomAPI from 'src/api/models/Classroom.js'
+import AggregatorAPI from 'src/api/models/Aggregator.js'
+import AppSettingAPI from 'src/api/models/AppSetting.js'
 import AnswerBookApi from 'src/api/models/AnswerBook.js'
 import AnswerBookApi from 'src/api/models/AnswerBook.js'
 import InstalmentAPI from 'src/api/models/Instalment.js'
 import InstalmentAPI from 'src/api/models/Instalment.js'
 import BasketItemAPI from 'src/api/models/BasketItem.js'
 import BasketItemAPI from 'src/api/models/BasketItem.js'
@@ -84,6 +86,8 @@ export const APIGateway = {
   question: new QuestionApi(),
   question: new QuestionApi(),
   discount: new DiscountAPI(),
   discount: new DiscountAPI(),
   classroom: new ClassroomAPI(),
   classroom: new ClassroomAPI(),
+  aggregator: new AggregatorAPI(),
+  appSetting: new AppSettingAPI(),
   answerBook: new AnswerBookApi(),
   answerBook: new AnswerBookApi(),
   instalment: new InstalmentAPI(),
   instalment: new InstalmentAPI(),
   basketItem: new BasketItemAPI(),
   basketItem: new BasketItemAPI(),

+ 32 - 0
src/api/models/Aggregator.js

@@ -0,0 +1,32 @@
+import { appApi } from 'src/boot/axios.js'
+import APIRepository from '../classes/APIRepository.js'
+
+export default class AggregatorAPI extends APIRepository {
+  constructor() {
+    super('aggregator', appApi)
+    this.APIAdresses = {
+      statistics: '/rma/aggregator/statistics'
+    }
+    this.CacheList = {
+      statistics: this.name + this.APIAdresses.statistics
+    }
+  }
+
+  statistics() {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.statistics,
+      resolveCallback: (response) => {
+        return {
+          users_count: response.data.users_count, // کاربر فعال
+          training_classrooms_count: response.data.training_classrooms_count, // دوره
+          event_classrooms_count: response.data.event_classrooms_count // رویداد
+        }
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+}

+ 100 - 0
src/api/models/AppSetting.js

@@ -0,0 +1,100 @@
+import { appApi } from 'src/boot/axios.js'
+import APIRepository from '../classes/APIRepository.js'
+
+export default class PageSettingAPI extends APIRepository {
+  constructor() {
+    super('page-setting', appApi)
+    this.APIAdresses = {
+      base: '/cma/settings',
+      getWithKey: (key) => '/cma/settings/key/' + key,
+      updateWithKey: (key) => '/admin/setting/' + key
+    }
+    this.CacheList = {
+      base: this.name + this.APIAdresses.base,
+      getWithKey: (key) => this.name + this.APIAdresses.getWithKey(key)
+    }
+  }
+
+  index (data, cache = { TTL: 1000 }) {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.base,
+      cacheKey: this.CacheList.base,
+      ...(cache !== undefined && { cache }),
+      resolveCallback: (response) => {
+        return {
+          referralCodeList: response.data.data,
+          paginate: response.data.meta
+          // {
+          //   current_page: 1,
+          //   from: 1,
+          //   last_page: 1,
+          //   path: '...',
+          //   per_page: 15,
+          //   to: 10,
+          //   total: 10
+          // }
+        }
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+
+  create (data) {
+    const formData = new FormData()
+    formData.append('key', data.key)
+    formData.append('value', data.value)
+    return this.sendRequest({
+      apiMethod: 'post',
+      api: this.api,
+      request: this.APIAdresses.base,
+      data: formData,
+      resolveCallback: (response) => {
+        return response.data
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+
+  update (data) {
+    // const settingKey = encodeURI(data.key)
+    const settingKey = data.key
+    const settingValue = data.value
+    return this.sendRequest({
+      apiMethod: 'post',
+      api: this.api,
+      request: this.APIAdresses.base,
+      data: {
+        key: settingKey,
+        value: settingValue
+      },
+      resolveCallback: (response) => {
+        return response
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+
+  get (data, cache = { TTL: 1000 }) {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.getWithKey(encodeURI(data)),
+      cacheKey: this.CacheList.getWithKey(encodeURI(data)),
+      ...(cache !== undefined && { cache }),
+      resolveCallback: (response) => {
+        return response.data
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+}

+ 2 - 1
src/api/models/Basket.js

@@ -12,6 +12,7 @@ export default class BasketAPI extends APIRepository {
       listOrders: '/store/baskets/list_orders',
       listOrders: '/store/baskets/list_orders',
       byId: (id) => '/store/baskets/' + id,
       byId: (id) => '/store/baskets/' + id,
       send: (id) => '/store/baskets/' + id + '/send',
       send: (id) => '/store/baskets/' + id + '/send',
+      setAddress: (id) => '/store/baskets/' + id + '/set_address',
       createInvoice: (id) => '/store/baskets/' + id + '/create_invoice',
       createInvoice: (id) => '/store/baskets/' + id + '/create_invoice',
       submitDiscountCode: (id) => '/store/baskets/' + id + '/submit_discount_code',
       submitDiscountCode: (id) => '/store/baskets/' + id + '/submit_discount_code',
       removeDiscountCode: (id) => '/store/baskets/' + id + '/remove_discount_code'
       removeDiscountCode: (id) => '/store/baskets/' + id + '/remove_discount_code'
@@ -127,7 +128,7 @@ export default class BasketAPI extends APIRepository {
     return this.sendRequest({
     return this.sendRequest({
       apiMethod: 'put',
       apiMethod: 'put',
       api: this.api,
       api: this.api,
-      request: this.APIAdresses.byId(basketId),
+      request: this.APIAdresses.setAddress(basketId),
       data: this.getNormalizedSendData({
       data: this.getNormalizedSendData({
         province: null, // String
         province: null, // String
         city: null, // String
         city: null, // String

+ 34 - 1
src/api/models/Product.js

@@ -7,7 +7,8 @@ export default class ProductAPI extends APIRepository {
     super('product', appApi)
     super('product', appApi)
     this.APIAdresses = {
     this.APIAdresses = {
       base: '/store/products',
       base: '/store/products',
-      byId: (id) => '/store/products/' + id
+      byId: (id) => '/store/products/' + id,
+      relatedProducts: (id) => '/store/products/' + id + '/related_products'
     }
     }
     this.CacheList = {
     this.CacheList = {
       base: this.name + this.APIAdresses.base
       base: this.name + this.APIAdresses.base
@@ -69,6 +70,38 @@ export default class ProductAPI extends APIRepository {
     })
     })
   }
   }
 
 
+  relatedProducts(data) {
+    return this.sendRequest({
+      apiMethod: 'get',
+      api: this.api,
+      request: this.APIAdresses.relatedProducts(data.id),
+      data: this.getNormalizedSendData({
+        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 ProductList(results),
+          paginate
+          // {
+          //   "count": 1,
+          //   "num_pages": 1,
+          //   "per_page": 10,
+          //   "current": 1,
+          //   "next": null,
+          //   "previous": null,
+          // }
+        }
+      },
+      rejectCallback: (error) => {
+        return error
+      }
+    })
+  }
+
   add (data) {
   add (data) {
     return this.sendRequest({
     return this.sendRequest({
       apiMethod: 'post',
       apiMethod: 'post',

+ 2 - 2
src/assets/ShamsiDate.js

@@ -15,8 +15,8 @@ class ShamsiDate {
     return moment(dateTime, 'YYYY-M-D HH:mm:ss').format('HH:mm:ss jYYYY/jMM/jDD')
     return moment(dateTime, 'YYYY-M-D HH:mm:ss').format('HH:mm:ss jYYYY/jMM/jDD')
   }
   }
 
 
-  static getTitledDateTime (dateTime) {
-    return moment(dateTime, 'YYYY-M-D HH:mm:ss').format('jD jMMMM jYYYY')
+  static getTitledDateTime (dateTime, format = 'jD jMMMM jYYYY') {
+    return moment(dateTime, 'YYYY-M-D HH:mm:ss').format(format)
   }
   }
 }
 }
 
 

+ 6915 - 0
src/assets/js/IranianCities.js

@@ -0,0 +1,6915 @@
+// https://github.com/sajaddp/list-of-cities-in-Iran
+
+export const Cities = [
+  {
+    id: 1,
+    name: 'اسکو',
+    slug: 'اسکو',
+    province_id: 1
+  },
+  {
+    id: 2,
+    name: 'اهر',
+    slug: 'اهر',
+    province_id: 1
+  },
+  {
+    id: 3,
+    name: 'ایلخچی',
+    slug: 'ایلخچی',
+    province_id: 1
+  },
+  {
+    id: 4,
+    name: 'آبش احمد',
+    slug: 'آبش-احمد',
+    province_id: 1
+  },
+  {
+    id: 5,
+    name: 'آذرشهر',
+    slug: 'آذرشهر',
+    province_id: 1
+  },
+  {
+    id: 6,
+    name: 'آقکند',
+    slug: 'آقکند',
+    province_id: 1
+  },
+  {
+    id: 7,
+    name: 'باسمنج',
+    slug: 'باسمنج',
+    province_id: 1
+  },
+  {
+    id: 8,
+    name: 'بخشایش',
+    slug: 'بخشایش',
+    province_id: 1
+  },
+  {
+    id: 9,
+    name: 'بستان آباد',
+    slug: 'بستان-آباد',
+    province_id: 1
+  },
+  {
+    id: 10,
+    name: 'بناب',
+    slug: 'بناب',
+    province_id: 1
+  },
+  {
+    id: 11,
+    name: 'بناب جدید',
+    slug: 'بناب-جدید',
+    province_id: 1
+  },
+  {
+    id: 12,
+    name: 'تبریز',
+    slug: 'تبریز',
+    province_id: 1
+  },
+  {
+    id: 13,
+    name: 'ترک',
+    slug: 'ترک',
+    province_id: 1
+  },
+  {
+    id: 14,
+    name: 'ترکمانچای',
+    slug: 'ترکمانچای',
+    province_id: 1
+  },
+  {
+    id: 15,
+    name: 'تسوج',
+    slug: 'تسوج',
+    province_id: 1
+  },
+  {
+    id: 16,
+    name: 'تیکمه داش',
+    slug: 'تیکمه-داش',
+    province_id: 1
+  },
+  {
+    id: 17,
+    name: 'جلفا',
+    slug: 'جلفا',
+    province_id: 1
+  },
+  {
+    id: 18,
+    name: 'خاروانا',
+    slug: 'خاروانا',
+    province_id: 1
+  },
+  {
+    id: 19,
+    name: 'خامنه',
+    slug: 'خامنه',
+    province_id: 1
+  },
+  {
+    id: 20,
+    name: 'خراجو',
+    slug: 'خراجو',
+    province_id: 1
+  },
+  {
+    id: 21,
+    name: 'خسروشهر',
+    slug: 'خسروشهر',
+    province_id: 1
+  },
+  {
+    id: 22,
+    name: 'خضرلو',
+    slug: 'خضرلو',
+    province_id: 1
+  },
+  {
+    id: 23,
+    name: 'خمارلو',
+    slug: 'خمارلو',
+    province_id: 1
+  },
+  {
+    id: 24,
+    name: 'خواجه',
+    slug: 'خواجه',
+    province_id: 1
+  },
+  {
+    id: 25,
+    name: 'دوزدوزان',
+    slug: 'دوزدوزان',
+    province_id: 1
+  },
+  {
+    id: 26,
+    name: 'زرنق',
+    slug: 'زرنق',
+    province_id: 1
+  },
+  {
+    id: 27,
+    name: 'زنوز',
+    slug: 'زنوز',
+    province_id: 1
+  },
+  {
+    id: 28,
+    name: 'سراب',
+    slug: 'سراب',
+    province_id: 1
+  },
+  {
+    id: 29,
+    name: 'سردرود',
+    slug: 'سردرود',
+    province_id: 1
+  },
+  {
+    id: 30,
+    name: 'سهند',
+    slug: 'سهند',
+    province_id: 1
+  },
+  {
+    id: 31,
+    name: 'سیس',
+    slug: 'سیس',
+    province_id: 1
+  },
+  {
+    id: 32,
+    name: 'سیه رود',
+    slug: 'سیه-رود',
+    province_id: 1
+  },
+  {
+    id: 33,
+    name: 'شبستر',
+    slug: 'شبستر',
+    province_id: 1
+  },
+  {
+    id: 34,
+    name: 'شربیان',
+    slug: 'شربیان',
+    province_id: 1
+  },
+  {
+    id: 35,
+    name: 'شرفخانه',
+    slug: 'شرفخانه',
+    province_id: 1
+  },
+  {
+    id: 36,
+    name: 'شندآباد',
+    slug: 'شندآباد',
+    province_id: 1
+  },
+  {
+    id: 37,
+    name: 'صوفیان',
+    slug: 'صوفیان',
+    province_id: 1
+  },
+  {
+    id: 38,
+    name: 'عجب شیر',
+    slug: 'عجب-شیر',
+    province_id: 1
+  },
+  {
+    id: 39,
+    name: 'قره آغاج',
+    slug: 'قره-آغاج',
+    province_id: 1
+  },
+  {
+    id: 40,
+    name: 'کشکسرای',
+    slug: 'کشکسرای',
+    province_id: 1
+  },
+  {
+    id: 41,
+    name: 'کلوانق',
+    slug: 'کلوانق',
+    province_id: 1
+  },
+  {
+    id: 42,
+    name: 'کلیبر',
+    slug: 'کلیبر',
+    province_id: 1
+  },
+  {
+    id: 43,
+    name: 'کوزه کنان',
+    slug: 'کوزه-کنان',
+    province_id: 1
+  },
+  {
+    id: 44,
+    name: 'گوگان',
+    slug: 'گوگان',
+    province_id: 1
+  },
+  {
+    id: 45,
+    name: 'لیلان',
+    slug: 'لیلان',
+    province_id: 1
+  },
+  {
+    id: 46,
+    name: 'مراغه',
+    slug: 'مراغه',
+    province_id: 1
+  },
+  {
+    id: 47,
+    name: 'مرند',
+    slug: 'مرند',
+    province_id: 1
+  },
+  {
+    id: 48,
+    name: 'ملکان',
+    slug: 'ملکان',
+    province_id: 1
+  },
+  {
+    id: 49,
+    name: 'ملک کیان',
+    slug: 'ملک-کیان',
+    province_id: 1
+  },
+  {
+    id: 50,
+    name: 'ممقان',
+    slug: 'ممقان',
+    province_id: 1
+  },
+  {
+    id: 51,
+    name: 'مهربان',
+    slug: 'مهربان',
+    province_id: 1
+  },
+  {
+    id: 52,
+    name: 'میانه',
+    slug: 'میانه',
+    province_id: 1
+  },
+  {
+    id: 53,
+    name: 'نظرکهریزی',
+    slug: 'نظرکهریزی',
+    province_id: 1
+  },
+  {
+    id: 54,
+    name: 'هادی شهر',
+    slug: 'هادی-شهر',
+    province_id: 1
+  },
+  {
+    id: 55,
+    name: 'هرگلان',
+    slug: 'هرگلان',
+    province_id: 1
+  },
+  {
+    id: 56,
+    name: 'هریس',
+    slug: 'هریس',
+    province_id: 1
+  },
+  {
+    id: 57,
+    name: 'هشترود',
+    slug: 'هشترود',
+    province_id: 1
+  },
+  {
+    id: 58,
+    name: 'هوراند',
+    slug: 'هوراند',
+    province_id: 1
+  },
+  {
+    id: 59,
+    name: 'وایقان',
+    slug: 'وایقان',
+    province_id: 1
+  },
+  {
+    id: 60,
+    name: 'ورزقان',
+    slug: 'ورزقان',
+    province_id: 1
+  },
+  {
+    id: 61,
+    name: 'یامچی',
+    slug: 'یامچی',
+    province_id: 1
+  },
+  {
+    id: 62,
+    name: 'ارومیه',
+    slug: 'ارومیه',
+    province_id: 2
+  },
+  {
+    id: 63,
+    name: 'اشنویه',
+    slug: 'اشنویه',
+    province_id: 2
+  },
+  {
+    id: 64,
+    name: 'ایواوغلی',
+    slug: 'ایواوغلی',
+    province_id: 2
+  },
+  {
+    id: 65,
+    name: 'آواجیق',
+    slug: 'آواجیق',
+    province_id: 2
+  },
+  {
+    id: 66,
+    name: 'باروق',
+    slug: 'باروق',
+    province_id: 2
+  },
+  {
+    id: 67,
+    name: 'بازرگان',
+    slug: 'بازرگان',
+    province_id: 2
+  },
+  {
+    id: 68,
+    name: 'بوکان',
+    slug: 'بوکان',
+    province_id: 2
+  },
+  {
+    id: 69,
+    name: 'پلدشت',
+    slug: 'پلدشت',
+    province_id: 2
+  },
+  {
+    id: 70,
+    name: 'پیرانشهر',
+    slug: 'پیرانشهر',
+    province_id: 2
+  },
+  {
+    id: 71,
+    name: 'تازه شهر',
+    slug: 'تازه-شهر',
+    province_id: 2
+  },
+  {
+    id: 72,
+    name: 'تکاب',
+    slug: 'تکاب',
+    province_id: 2
+  },
+  {
+    id: 73,
+    name: 'چهاربرج',
+    slug: 'چهاربرج',
+    province_id: 2
+  },
+  {
+    id: 74,
+    name: 'خوی',
+    slug: 'خوی',
+    province_id: 2
+  },
+  {
+    id: 75,
+    name: 'دیزج دیز',
+    slug: 'دیزج-دیز',
+    province_id: 2
+  },
+  {
+    id: 76,
+    name: 'ربط',
+    slug: 'ربط',
+    province_id: 2
+  },
+  {
+    id: 77,
+    name: 'سردشت',
+    slug: 'آذربایجان-غربی-سردشت',
+    province_id: 2
+  },
+  {
+    id: 78,
+    name: 'سرو',
+    slug: 'سرو',
+    province_id: 2
+  },
+  {
+    id: 79,
+    name: 'سلماس',
+    slug: 'سلماس',
+    province_id: 2
+  },
+  {
+    id: 80,
+    name: 'سیلوانه',
+    slug: 'سیلوانه',
+    province_id: 2
+  },
+  {
+    id: 81,
+    name: 'سیمینه',
+    slug: 'سیمینه',
+    province_id: 2
+  },
+  {
+    id: 82,
+    name: 'سیه چشمه',
+    slug: 'سیه-چشمه',
+    province_id: 2
+  },
+  {
+    id: 83,
+    name: 'شاهین دژ',
+    slug: 'شاهین-دژ',
+    province_id: 2
+  },
+  {
+    id: 84,
+    name: 'شوط',
+    slug: 'شوط',
+    province_id: 2
+  },
+  {
+    id: 85,
+    name: 'فیرورق',
+    slug: 'فیرورق',
+    province_id: 2
+  },
+  {
+    id: 86,
+    name: 'قره ضیاءالدین',
+    slug: 'قره-ضیاءالدین',
+    province_id: 2
+  },
+  {
+    id: 87,
+    name: 'قطور',
+    slug: 'قطور',
+    province_id: 2
+  },
+  {
+    id: 88,
+    name: 'قوشچی',
+    slug: 'قوشچی',
+    province_id: 2
+  },
+  {
+    id: 89,
+    name: 'کشاورز',
+    slug: 'کشاورز',
+    province_id: 2
+  },
+  {
+    id: 90,
+    name: 'گردکشانه',
+    slug: 'گردکشانه',
+    province_id: 2
+  },
+  {
+    id: 91,
+    name: 'ماکو',
+    slug: 'ماکو',
+    province_id: 2
+  },
+  {
+    id: 92,
+    name: 'محمدیار',
+    slug: 'محمدیار',
+    province_id: 2
+  },
+  {
+    id: 93,
+    name: 'محمودآباد',
+    slug: 'آذربایجان-غربی-محمودآباد',
+    province_id: 2
+  },
+  {
+    id: 94,
+    name: 'مهاباد',
+    slug: 'آذربایجان-غربی-مهاباد',
+    province_id: 2
+  },
+  {
+    id: 95,
+    name: 'میاندوآب',
+    slug: 'میاندوآب',
+    province_id: 2
+  },
+  {
+    id: 96,
+    name: 'میرآباد',
+    slug: 'میرآباد',
+    province_id: 2
+  },
+  {
+    id: 97,
+    name: 'نالوس',
+    slug: 'نالوس',
+    province_id: 2
+  },
+  {
+    id: 98,
+    name: 'نقده',
+    slug: 'نقده',
+    province_id: 2
+  },
+  {
+    id: 99,
+    name: 'نوشین',
+    slug: 'نوشین',
+    province_id: 2
+  },
+  {
+    id: 100,
+    name: 'اردبیل',
+    slug: 'اردبیل',
+    province_id: 3
+  },
+  {
+    id: 101,
+    name: 'اصلاندوز',
+    slug: 'اصلاندوز',
+    province_id: 3
+  },
+  {
+    id: 102,
+    name: 'آبی بیگلو',
+    slug: 'آبی-بیگلو',
+    province_id: 3
+  },
+  {
+    id: 103,
+    name: 'بیله سوار',
+    slug: 'بیله-سوار',
+    province_id: 3
+  },
+  {
+    id: 104,
+    name: 'پارس آباد',
+    slug: 'پارس-آباد',
+    province_id: 3
+  },
+  {
+    id: 105,
+    name: 'تازه کند',
+    slug: 'تازه-کند',
+    province_id: 3
+  },
+  {
+    id: 106,
+    name: 'تازه کندانگوت',
+    slug: 'تازه-کندانگوت',
+    province_id: 3
+  },
+  {
+    id: 107,
+    name: 'جعفرآباد',
+    slug: 'جعفرآباد',
+    province_id: 3
+  },
+  {
+    id: 108,
+    name: 'خلخال',
+    slug: 'خلخال',
+    province_id: 3
+  },
+  {
+    id: 109,
+    name: 'رضی',
+    slug: 'رضی',
+    province_id: 3
+  },
+  {
+    id: 110,
+    name: 'سرعین',
+    slug: 'سرعین',
+    province_id: 3
+  },
+  {
+    id: 111,
+    name: 'عنبران',
+    slug: 'عنبران',
+    province_id: 3
+  },
+  {
+    id: 112,
+    name: 'فخرآباد',
+    slug: 'فخرآباد',
+    province_id: 3
+  },
+  {
+    id: 113,
+    name: 'کلور',
+    slug: 'کلور',
+    province_id: 3
+  },
+  {
+    id: 114,
+    name: 'کوراییم',
+    slug: 'کوراییم',
+    province_id: 3
+  },
+  {
+    id: 115,
+    name: 'گرمی',
+    slug: 'گرمی',
+    province_id: 3
+  },
+  {
+    id: 116,
+    name: 'گیوی',
+    slug: 'گیوی',
+    province_id: 3
+  },
+  {
+    id: 117,
+    name: 'لاهرود',
+    slug: 'لاهرود',
+    province_id: 3
+  },
+  {
+    id: 118,
+    name: 'مشگین شهر',
+    slug: 'مشگین-شهر',
+    province_id: 3
+  },
+  {
+    id: 119,
+    name: 'نمین',
+    slug: 'نمین',
+    province_id: 3
+  },
+  {
+    id: 120,
+    name: 'نیر',
+    slug: 'اردبیل-نیر',
+    province_id: 3
+  },
+  {
+    id: 121,
+    name: 'هشتجین',
+    slug: 'هشتجین',
+    province_id: 3
+  },
+  {
+    id: 122,
+    name: 'هیر',
+    slug: 'هیر',
+    province_id: 3
+  },
+  {
+    id: 123,
+    name: 'ابریشم',
+    slug: 'ابریشم',
+    province_id: 4
+  },
+  {
+    id: 124,
+    name: 'ابوزیدآباد',
+    slug: 'ابوزیدآباد',
+    province_id: 4
+  },
+  {
+    id: 125,
+    name: 'اردستان',
+    slug: 'اردستان',
+    province_id: 4
+  },
+  {
+    id: 126,
+    name: 'اژیه',
+    slug: 'اژیه',
+    province_id: 4
+  },
+  {
+    id: 127,
+    name: 'اصفهان',
+    slug: 'اصفهان',
+    province_id: 4
+  },
+  {
+    id: 128,
+    name: 'افوس',
+    slug: 'افوس',
+    province_id: 4
+  },
+  {
+    id: 129,
+    name: 'انارک',
+    slug: 'انارک',
+    province_id: 4
+  },
+  {
+    id: 130,
+    name: 'ایمانشهر',
+    slug: 'ایمانشهر',
+    province_id: 4
+  },
+  {
+    id: 131,
+    name: 'آران وبیدگل',
+    slug: 'آران-وبیدگل',
+    province_id: 4
+  },
+  {
+    id: 132,
+    name: 'بادرود',
+    slug: 'بادرود',
+    province_id: 4
+  },
+  {
+    id: 133,
+    name: 'باغ بهادران',
+    slug: 'باغ-بهادران',
+    province_id: 4
+  },
+  {
+    id: 134,
+    name: 'بافران',
+    slug: 'بافران',
+    province_id: 4
+  },
+  {
+    id: 135,
+    name: 'برزک',
+    slug: 'برزک',
+    province_id: 4
+  },
+  {
+    id: 136,
+    name: 'برف انبار',
+    slug: 'برف-انبار',
+    province_id: 4
+  },
+  {
+    id: 137,
+    name: 'بهاران شهر',
+    slug: 'بهاران-شهر',
+    province_id: 4
+  },
+  {
+    id: 138,
+    name: 'بهارستان',
+    slug: 'بهارستان',
+    province_id: 4
+  },
+  {
+    id: 139,
+    name: 'بوئین و میاندشت',
+    slug: 'بوئین-میاندشت',
+    province_id: 4
+  },
+  {
+    id: 140,
+    name: 'پیربکران',
+    slug: 'پیربکران',
+    province_id: 4
+  },
+  {
+    id: 141,
+    name: 'تودشک',
+    slug: 'تودشک',
+    province_id: 4
+  },
+  {
+    id: 142,
+    name: 'تیران',
+    slug: 'تیران',
+    province_id: 4
+  },
+  {
+    id: 143,
+    name: 'جندق',
+    slug: 'جندق',
+    province_id: 4
+  },
+  {
+    id: 144,
+    name: 'جوزدان',
+    slug: 'جوزدان',
+    province_id: 4
+  },
+  {
+    id: 145,
+    name: 'جوشقان و کامو',
+    slug: 'جوشقان-کامو',
+    province_id: 4
+  },
+  {
+    id: 146,
+    name: 'چادگان',
+    slug: 'چادگان',
+    province_id: 4
+  },
+  {
+    id: 147,
+    name: 'چرمهین',
+    slug: 'چرمهین',
+    province_id: 4
+  },
+  {
+    id: 148,
+    name: 'چمگردان',
+    slug: 'چمگردان',
+    province_id: 4
+  },
+  {
+    id: 149,
+    name: 'حبیب آباد',
+    slug: 'حبیب-آباد',
+    province_id: 4
+  },
+  {
+    id: 150,
+    name: 'حسن آباد',
+    slug: 'اصفهان-حسن-آباد',
+    province_id: 4
+  },
+  {
+    id: 151,
+    name: 'حنا',
+    slug: 'حنا',
+    province_id: 4
+  },
+  {
+    id: 152,
+    name: 'خالدآباد',
+    slug: 'خالدآباد',
+    province_id: 4
+  },
+  {
+    id: 153,
+    name: 'خمینی شهر',
+    slug: 'خمینی-شهر',
+    province_id: 4
+  },
+  {
+    id: 154,
+    name: 'خوانسار',
+    slug: 'خوانسار',
+    province_id: 4
+  },
+  {
+    id: 155,
+    name: 'خور',
+    slug: 'اصفهان-خور',
+    province_id: 4
+  },
+  {
+    id: 157,
+    name: 'خورزوق',
+    slug: 'خورزوق',
+    province_id: 4
+  },
+  {
+    id: 158,
+    name: 'داران',
+    slug: 'داران',
+    province_id: 4
+  },
+  {
+    id: 159,
+    name: 'دامنه',
+    slug: 'دامنه',
+    province_id: 4
+  },
+  {
+    id: 160,
+    name: 'درچه',
+    slug: 'درچه',
+    province_id: 4
+  },
+  {
+    id: 161,
+    name: 'دستگرد',
+    slug: 'دستگرد',
+    province_id: 4
+  },
+  {
+    id: 162,
+    name: 'دهاقان',
+    slug: 'دهاقان',
+    province_id: 4
+  },
+  {
+    id: 163,
+    name: 'دهق',
+    slug: 'دهق',
+    province_id: 4
+  },
+  {
+    id: 164,
+    name: 'دولت آباد',
+    slug: 'اصفهان-دولت-آباد',
+    province_id: 4
+  },
+  {
+    id: 165,
+    name: 'دیزیچه',
+    slug: 'دیزیچه',
+    province_id: 4
+  },
+  {
+    id: 166,
+    name: 'رزوه',
+    slug: 'رزوه',
+    province_id: 4
+  },
+  {
+    id: 167,
+    name: 'رضوانشهر',
+    slug: 'اصفهان-رضوانشهر',
+    province_id: 4
+  },
+  {
+    id: 168,
+    name: 'زاینده رود',
+    slug: 'زاینده-رود',
+    province_id: 4
+  },
+  {
+    id: 169,
+    name: 'زرین شهر',
+    slug: 'زرین-شهر',
+    province_id: 4
+  },
+  {
+    id: 170,
+    name: 'زواره',
+    slug: 'زواره',
+    province_id: 4
+  },
+  {
+    id: 171,
+    name: 'زیباشهر',
+    slug: 'زیباشهر',
+    province_id: 4
+  },
+  {
+    id: 172,
+    name: 'سده لنجان',
+    slug: 'سده-لنجان',
+    province_id: 4
+  },
+  {
+    id: 173,
+    name: 'سفیدشهر',
+    slug: 'سفیدشهر',
+    province_id: 4
+  },
+  {
+    id: 174,
+    name: 'سگزی',
+    slug: 'سگزی',
+    province_id: 4
+  },
+  {
+    id: 175,
+    name: 'سمیرم',
+    slug: 'سمیرم',
+    province_id: 4
+  },
+  {
+    id: 176,
+    name: 'شاهین شهر',
+    slug: 'شاهین-شهر',
+    province_id: 4
+  },
+  {
+    id: 177,
+    name: 'شهرضا',
+    slug: 'شهرضا',
+    province_id: 4
+  },
+  {
+    id: 178,
+    name: 'طالخونچه',
+    slug: 'طالخونچه',
+    province_id: 4
+  },
+  {
+    id: 179,
+    name: 'عسگران',
+    slug: 'عسگران',
+    province_id: 4
+  },
+  {
+    id: 180,
+    name: 'علویجه',
+    slug: 'علویجه',
+    province_id: 4
+  },
+  {
+    id: 181,
+    name: 'فرخی',
+    slug: 'فرخی',
+    province_id: 4
+  },
+  {
+    id: 182,
+    name: 'فریدونشهر',
+    slug: 'فریدونشهر',
+    province_id: 4
+  },
+  {
+    id: 183,
+    name: 'فلاورجان',
+    slug: 'فلاورجان',
+    province_id: 4
+  },
+  {
+    id: 184,
+    name: 'فولادشهر',
+    slug: 'فولادشهر',
+    province_id: 4
+  },
+  {
+    id: 185,
+    name: 'قمصر',
+    slug: 'قمصر',
+    province_id: 4
+  },
+  {
+    id: 186,
+    name: 'قهجاورستان',
+    slug: 'قهجاورستان',
+    province_id: 4
+  },
+  {
+    id: 187,
+    name: 'قهدریجان',
+    slug: 'قهدریجان',
+    province_id: 4
+  },
+  {
+    id: 188,
+    name: 'کاشان',
+    slug: 'کاشان',
+    province_id: 4
+  },
+  {
+    id: 189,
+    name: 'کرکوند',
+    slug: 'کرکوند',
+    province_id: 4
+  },
+  {
+    id: 190,
+    name: 'کلیشاد و سودرجان',
+    slug: 'کلیشاد-سودرجان',
+    province_id: 4
+  },
+  {
+    id: 191,
+    name: 'کمشچه',
+    slug: 'کمشچه',
+    province_id: 4
+  },
+  {
+    id: 192,
+    name: 'کمه',
+    slug: 'کمه',
+    province_id: 4
+  },
+  {
+    id: 193,
+    name: 'کهریزسنگ',
+    slug: 'کهریزسنگ',
+    province_id: 4
+  },
+  {
+    id: 194,
+    name: 'کوشک',
+    slug: 'کوشک',
+    province_id: 4
+  },
+  {
+    id: 195,
+    name: 'کوهپایه',
+    slug: 'کوهپایه',
+    province_id: 4
+  },
+  {
+    id: 196,
+    name: 'گرگاب',
+    slug: 'گرگاب',
+    province_id: 4
+  },
+  {
+    id: 197,
+    name: 'گزبرخوار',
+    slug: 'گزبرخوار',
+    province_id: 4
+  },
+  {
+    id: 198,
+    name: 'گلپایگان',
+    slug: 'گلپایگان',
+    province_id: 4
+  },
+  {
+    id: 199,
+    name: 'گلدشت',
+    slug: 'گلدشت',
+    province_id: 4
+  },
+  {
+    id: 200,
+    name: 'گلشهر',
+    slug: 'گلشهر',
+    province_id: 4
+  },
+  {
+    id: 201,
+    name: 'گوگد',
+    slug: 'گوگد',
+    province_id: 4
+  },
+  {
+    id: 202,
+    name: 'لای بید',
+    slug: 'لای-بید',
+    province_id: 4
+  },
+  {
+    id: 203,
+    name: 'مبارکه',
+    slug: 'مبارکه',
+    province_id: 4
+  },
+  {
+    id: 204,
+    name: 'مجلسی',
+    slug: 'مجلسی',
+    province_id: 4
+  },
+  {
+    id: 205,
+    name: 'محمدآباد',
+    slug: 'اصفهان-محمدآباد',
+    province_id: 4
+  },
+  {
+    id: 206,
+    name: 'مشکات',
+    slug: 'مشکات',
+    province_id: 4
+  },
+  {
+    id: 207,
+    name: 'منظریه',
+    slug: 'منظریه',
+    province_id: 4
+  },
+  {
+    id: 208,
+    name: 'مهاباد',
+    slug: 'اصفهان-مهاباد',
+    province_id: 4
+  },
+  {
+    id: 209,
+    name: 'میمه',
+    slug: 'اصفهان-میمه',
+    province_id: 4
+  },
+  {
+    id: 210,
+    name: 'نائین',
+    slug: 'نائین',
+    province_id: 4
+  },
+  {
+    id: 211,
+    name: 'نجف آباد',
+    slug: 'نجف-آباد',
+    province_id: 4
+  },
+  {
+    id: 212,
+    name: 'نصرآباد',
+    slug: 'اصفهان-نصرآباد',
+    province_id: 4
+  },
+  {
+    id: 213,
+    name: 'نطنز',
+    slug: 'نطنز',
+    province_id: 4
+  },
+  {
+    id: 214,
+    name: 'نوش آباد',
+    slug: 'نوش-آباد',
+    province_id: 4
+  },
+  {
+    id: 215,
+    name: 'نیاسر',
+    slug: 'نیاسر',
+    province_id: 4
+  },
+  {
+    id: 216,
+    name: 'نیک آباد',
+    slug: 'نیک-آباد',
+    province_id: 4
+  },
+  {
+    id: 217,
+    name: 'هرند',
+    slug: 'هرند',
+    province_id: 4
+  },
+  {
+    id: 218,
+    name: 'ورزنه',
+    slug: 'ورزنه',
+    province_id: 4
+  },
+  {
+    id: 219,
+    name: 'ورنامخواست',
+    slug: 'ورنامخواست',
+    province_id: 4
+  },
+  {
+    id: 220,
+    name: 'وزوان',
+    slug: 'وزوان',
+    province_id: 4
+  },
+  {
+    id: 221,
+    name: 'ونک',
+    slug: 'ونک',
+    province_id: 4
+  },
+  {
+    id: 222,
+    name: 'اسارا',
+    slug: 'اسارا',
+    province_id: 5
+  },
+  {
+    id: 223,
+    name: 'اشتهارد',
+    slug: 'اشتهارد',
+    province_id: 5
+  },
+  {
+    id: 224,
+    name: 'تنکمان',
+    slug: 'تنکمان',
+    province_id: 5
+  },
+  {
+    id: 225,
+    name: 'چهارباغ',
+    slug: 'چهارباغ',
+    province_id: 5
+  },
+  {
+    id: 226,
+    name: 'سعید آباد',
+    slug: 'سعید-آباد',
+    province_id: 5
+  },
+  {
+    id: 227,
+    name: 'شهر جدید هشتگرد',
+    slug: 'شهر-جدید-هشتگرد',
+    province_id: 5
+  },
+  {
+    id: 228,
+    name: 'طالقان',
+    slug: 'طالقان',
+    province_id: 5
+  },
+  {
+    id: 229,
+    name: 'کرج',
+    slug: 'کرج',
+    province_id: 5
+  },
+  {
+    id: 230,
+    name: 'کمال شهر',
+    slug: 'کمال-شهر',
+    province_id: 5
+  },
+  {
+    id: 231,
+    name: 'کوهسار',
+    slug: 'کوهسار',
+    province_id: 5
+  },
+  {
+    id: 232,
+    name: 'گرمدره',
+    slug: 'گرمدره',
+    province_id: 5
+  },
+  {
+    id: 233,
+    name: 'ماهدشت',
+    slug: 'ماهدشت',
+    province_id: 5
+  },
+  {
+    id: 234,
+    name: 'محمدشهر',
+    slug: 'البرز-محمدشهر',
+    province_id: 5
+  },
+  {
+    id: 235,
+    name: 'مشکین دشت',
+    slug: 'مشکین-دشت',
+    province_id: 5
+  },
+  {
+    id: 236,
+    name: 'نظرآباد',
+    slug: 'نظرآباد',
+    province_id: 5
+  },
+  {
+    id: 237,
+    name: 'هشتگرد',
+    slug: 'هشتگرد',
+    province_id: 5
+  },
+  {
+    id: 238,
+    name: 'ارکواز',
+    slug: 'ارکواز',
+    province_id: 6
+  },
+  {
+    id: 239,
+    name: 'ایلام',
+    slug: 'ایلام',
+    province_id: 6
+  },
+  {
+    id: 240,
+    name: 'ایوان',
+    slug: 'ایوان',
+    province_id: 6
+  },
+  {
+    id: 241,
+    name: 'آبدانان',
+    slug: 'آبدانان',
+    province_id: 6
+  },
+  {
+    id: 242,
+    name: 'آسمان آباد',
+    slug: 'آسمان-آباد',
+    province_id: 6
+  },
+  {
+    id: 243,
+    name: 'بدره',
+    slug: 'بدره',
+    province_id: 6
+  },
+  {
+    id: 244,
+    name: 'پهله',
+    slug: 'پهله',
+    province_id: 6
+  },
+  {
+    id: 245,
+    name: 'توحید',
+    slug: 'توحید',
+    province_id: 6
+  },
+  {
+    id: 246,
+    name: 'چوار',
+    slug: 'چوار',
+    province_id: 6
+  },
+  {
+    id: 247,
+    name: 'دره شهر',
+    slug: 'دره-شهر',
+    province_id: 6
+  },
+  {
+    id: 248,
+    name: 'دلگشا',
+    slug: 'دلگشا',
+    province_id: 6
+  },
+  {
+    id: 249,
+    name: 'دهلران',
+    slug: 'دهلران',
+    province_id: 6
+  },
+  {
+    id: 250,
+    name: 'زرنه',
+    slug: 'زرنه',
+    province_id: 6
+  },
+  {
+    id: 251,
+    name: 'سراب باغ',
+    slug: 'سراب-باغ',
+    province_id: 6
+  },
+  {
+    id: 252,
+    name: 'سرابله',
+    slug: 'سرابله',
+    province_id: 6
+  },
+  {
+    id: 253,
+    name: 'صالح آباد',
+    slug: 'ایلام-صالح-آباد',
+    province_id: 6
+  },
+  {
+    id: 254,
+    name: 'لومار',
+    slug: 'لومار',
+    province_id: 6
+  },
+  {
+    id: 255,
+    name: 'مهران',
+    slug: 'مهران',
+    province_id: 6
+  },
+  {
+    id: 256,
+    name: 'مورموری',
+    slug: 'مورموری',
+    province_id: 6
+  },
+  {
+    id: 257,
+    name: 'موسیان',
+    slug: 'موسیان',
+    province_id: 6
+  },
+  {
+    id: 258,
+    name: 'میمه',
+    slug: 'ایلام-میمه',
+    province_id: 6
+  },
+  {
+    id: 259,
+    name: 'امام حسن',
+    slug: 'امام-حسن',
+    province_id: 7
+  },
+  {
+    id: 260,
+    name: 'انارستان',
+    slug: 'انارستان',
+    province_id: 7
+  },
+  {
+    id: 261,
+    name: 'اهرم',
+    slug: 'اهرم',
+    province_id: 7
+  },
+  {
+    id: 262,
+    name: 'آب پخش',
+    slug: 'آب-پخش',
+    province_id: 7
+  },
+  {
+    id: 263,
+    name: 'آبدان',
+    slug: 'آبدان',
+    province_id: 7
+  },
+  {
+    id: 264,
+    name: 'برازجان',
+    slug: 'برازجان',
+    province_id: 7
+  },
+  {
+    id: 265,
+    name: 'بردخون',
+    slug: 'بردخون',
+    province_id: 7
+  },
+  {
+    id: 266,
+    name: 'بندردیر',
+    slug: 'بندردیر',
+    province_id: 7
+  },
+  {
+    id: 267,
+    name: 'بندردیلم',
+    slug: 'بندردیلم',
+    province_id: 7
+  },
+  {
+    id: 268,
+    name: 'بندرریگ',
+    slug: 'بندرریگ',
+    province_id: 7
+  },
+  {
+    id: 269,
+    name: 'بندرکنگان',
+    slug: 'بندرکنگان',
+    province_id: 7
+  },
+  {
+    id: 270,
+    name: 'بندرگناوه',
+    slug: 'بندرگناوه',
+    province_id: 7
+  },
+  {
+    id: 271,
+    name: 'بنک',
+    slug: 'بنک',
+    province_id: 7
+  },
+  {
+    id: 272,
+    name: 'بوشهر',
+    slug: 'بوشهر',
+    province_id: 7
+  },
+  {
+    id: 273,
+    name: 'تنگ ارم',
+    slug: 'تنگ-ارم',
+    province_id: 7
+  },
+  {
+    id: 274,
+    name: 'جم',
+    slug: 'جم',
+    province_id: 7
+  },
+  {
+    id: 275,
+    name: 'چغادک',
+    slug: 'چغادک',
+    province_id: 7
+  },
+  {
+    id: 276,
+    name: 'خارک',
+    slug: 'خارک',
+    province_id: 7
+  },
+  {
+    id: 277,
+    name: 'خورموج',
+    slug: 'خورموج',
+    province_id: 7
+  },
+  {
+    id: 278,
+    name: 'دالکی',
+    slug: 'دالکی',
+    province_id: 7
+  },
+  {
+    id: 279,
+    name: 'دلوار',
+    slug: 'دلوار',
+    province_id: 7
+  },
+  {
+    id: 280,
+    name: 'ریز',
+    slug: 'ریز',
+    province_id: 7
+  },
+  {
+    id: 281,
+    name: 'سعدآباد',
+    slug: 'سعدآباد',
+    province_id: 7
+  },
+  {
+    id: 282,
+    name: 'سیراف',
+    slug: 'سیراف',
+    province_id: 7
+  },
+  {
+    id: 283,
+    name: 'شبانکاره',
+    slug: 'شبانکاره',
+    province_id: 7
+  },
+  {
+    id: 284,
+    name: 'شنبه',
+    slug: 'شنبه',
+    province_id: 7
+  },
+  {
+    id: 285,
+    name: 'عسلویه',
+    slug: 'عسلویه',
+    province_id: 7
+  },
+  {
+    id: 286,
+    name: 'کاکی',
+    slug: 'کاکی',
+    province_id: 7
+  },
+  {
+    id: 287,
+    name: 'کلمه',
+    slug: 'کلمه',
+    province_id: 7
+  },
+  {
+    id: 288,
+    name: 'نخل تقی',
+    slug: 'نخل-تقی',
+    province_id: 7
+  },
+  {
+    id: 289,
+    name: 'وحدتیه',
+    slug: 'وحدتیه',
+    province_id: 7
+  },
+  {
+    id: 290,
+    name: 'ارجمند',
+    slug: 'ارجمند',
+    province_id: 8
+  },
+  {
+    id: 291,
+    name: 'اسلامشهر',
+    slug: 'اسلامشهر',
+    province_id: 8
+  },
+  {
+    id: 292,
+    name: 'اندیشه',
+    slug: 'اندیشه',
+    province_id: 8
+  },
+  {
+    id: 293,
+    name: 'آبسرد',
+    slug: 'آبسرد',
+    province_id: 8
+  },
+  {
+    id: 294,
+    name: 'آبعلی',
+    slug: 'آبعلی',
+    province_id: 8
+  },
+  {
+    id: 295,
+    name: 'باغستان',
+    slug: 'باغستان',
+    province_id: 8
+  },
+  {
+    id: 296,
+    name: 'باقرشهر',
+    slug: 'باقرشهر',
+    province_id: 8
+  },
+  {
+    id: 297,
+    name: 'بومهن',
+    slug: 'بومهن',
+    province_id: 8
+  },
+  {
+    id: 298,
+    name: 'پاکدشت',
+    slug: 'پاکدشت',
+    province_id: 8
+  },
+  {
+    id: 299,
+    name: 'پردیس',
+    slug: 'پردیس',
+    province_id: 8
+  },
+  {
+    id: 300,
+    name: 'پیشوا',
+    slug: 'پیشوا',
+    province_id: 8
+  },
+  {
+    id: 301,
+    name: 'تهران',
+    slug: 'تهران',
+    province_id: 8
+  },
+  {
+    id: 302,
+    name: 'جوادآباد',
+    slug: 'جوادآباد',
+    province_id: 8
+  },
+  {
+    id: 303,
+    name: 'چهاردانگه',
+    slug: 'چهاردانگه',
+    province_id: 8
+  },
+  {
+    id: 304,
+    name: 'حسن آباد',
+    slug: 'تهران-حسن-آباد',
+    province_id: 8
+  },
+  {
+    id: 305,
+    name: 'دماوند',
+    slug: 'دماوند',
+    province_id: 8
+  },
+  {
+    id: 306,
+    name: 'دیزین',
+    slug: 'دیزین',
+    province_id: 8
+  },
+  {
+    id: 307,
+    name: 'شهر ری',
+    slug: 'شهر-ری',
+    province_id: 8
+  },
+  {
+    id: 308,
+    name: 'رباط کریم',
+    slug: 'رباط-کریم',
+    province_id: 8
+  },
+  {
+    id: 309,
+    name: 'رودهن',
+    slug: 'رودهن',
+    province_id: 8
+  },
+  {
+    id: 310,
+    name: 'شاهدشهر',
+    slug: 'شاهدشهر',
+    province_id: 8
+  },
+  {
+    id: 311,
+    name: 'شریف آباد',
+    slug: 'شریف-آباد',
+    province_id: 8
+  },
+  {
+    id: 312,
+    name: 'شمشک',
+    slug: 'شمشک',
+    province_id: 8
+  },
+  {
+    id: 313,
+    name: 'شهریار',
+    slug: 'شهریار',
+    province_id: 8
+  },
+  {
+    id: 314,
+    name: 'صالح آباد',
+    slug: 'تهران-صالح-آباد',
+    province_id: 8
+  },
+  {
+    id: 315,
+    name: 'صباشهر',
+    slug: 'صباشهر',
+    province_id: 8
+  },
+  {
+    id: 316,
+    name: 'صفادشت',
+    slug: 'صفادشت',
+    province_id: 8
+  },
+  {
+    id: 317,
+    name: 'فردوسیه',
+    slug: 'فردوسیه',
+    province_id: 8
+  },
+  {
+    id: 318,
+    name: 'فشم',
+    slug: 'فشم',
+    province_id: 8
+  },
+  {
+    id: 319,
+    name: 'فیروزکوه',
+    slug: 'فیروزکوه',
+    province_id: 8
+  },
+  {
+    id: 320,
+    name: 'قدس',
+    slug: 'قدس',
+    province_id: 8
+  },
+  {
+    id: 321,
+    name: 'قرچک',
+    slug: 'قرچک',
+    province_id: 8
+  },
+  {
+    id: 322,
+    name: 'کهریزک',
+    slug: 'کهریزک',
+    province_id: 8
+  },
+  {
+    id: 323,
+    name: 'کیلان',
+    slug: 'کیلان',
+    province_id: 8
+  },
+  {
+    id: 324,
+    name: 'گلستان',
+    slug: 'شهر-گلستان',
+    province_id: 8
+  },
+  {
+    id: 325,
+    name: 'لواسان',
+    slug: 'لواسان',
+    province_id: 8
+  },
+  {
+    id: 326,
+    name: 'ملارد',
+    slug: 'ملارد',
+    province_id: 8
+  },
+  {
+    id: 327,
+    name: 'میگون',
+    slug: 'میگون',
+    province_id: 8
+  },
+  {
+    id: 328,
+    name: 'نسیم شهر',
+    slug: 'نسیم-شهر',
+    province_id: 8
+  },
+  {
+    id: 329,
+    name: 'نصیرآباد',
+    slug: 'نصیرآباد',
+    province_id: 8
+  },
+  {
+    id: 330,
+    name: 'وحیدیه',
+    slug: 'وحیدیه',
+    province_id: 8
+  },
+  {
+    id: 331,
+    name: 'ورامین',
+    slug: 'ورامین',
+    province_id: 8
+  },
+  {
+    id: 332,
+    name: 'اردل',
+    slug: 'اردل',
+    province_id: 9
+  },
+  {
+    id: 333,
+    name: 'آلونی',
+    slug: 'آلونی',
+    province_id: 9
+  },
+  {
+    id: 334,
+    name: 'باباحیدر',
+    slug: 'باباحیدر',
+    province_id: 9
+  },
+  {
+    id: 335,
+    name: 'بروجن',
+    slug: 'بروجن',
+    province_id: 9
+  },
+  {
+    id: 336,
+    name: 'بلداجی',
+    slug: 'بلداجی',
+    province_id: 9
+  },
+  {
+    id: 337,
+    name: 'بن',
+    slug: 'بن',
+    province_id: 9
+  },
+  {
+    id: 338,
+    name: 'جونقان',
+    slug: 'جونقان',
+    province_id: 9
+  },
+  {
+    id: 339,
+    name: 'چلگرد',
+    slug: 'چلگرد',
+    province_id: 9
+  },
+  {
+    id: 340,
+    name: 'سامان',
+    slug: 'سامان',
+    province_id: 9
+  },
+  {
+    id: 341,
+    name: 'سفیددشت',
+    slug: 'سفیددشت',
+    province_id: 9
+  },
+  {
+    id: 342,
+    name: 'سودجان',
+    slug: 'سودجان',
+    province_id: 9
+  },
+  {
+    id: 343,
+    name: 'سورشجان',
+    slug: 'سورشجان',
+    province_id: 9
+  },
+  {
+    id: 344,
+    name: 'شلمزار',
+    slug: 'شلمزار',
+    province_id: 9
+  },
+  {
+    id: 345,
+    name: 'شهرکرد',
+    slug: 'شهرکرد',
+    province_id: 9
+  },
+  {
+    id: 346,
+    name: 'طاقانک',
+    slug: 'طاقانک',
+    province_id: 9
+  },
+  {
+    id: 347,
+    name: 'فارسان',
+    slug: 'فارسان',
+    province_id: 9
+  },
+  {
+    id: 348,
+    name: 'فرادنبه',
+    slug: 'فرادبنه',
+    province_id: 9
+  },
+  {
+    id: 349,
+    name: 'فرخ شهر',
+    slug: 'فرخ-شهر',
+    province_id: 9
+  },
+  {
+    id: 350,
+    name: 'کیان',
+    slug: 'کیان',
+    province_id: 9
+  },
+  {
+    id: 351,
+    name: 'گندمان',
+    slug: 'گندمان',
+    province_id: 9
+  },
+  {
+    id: 352,
+    name: 'گهرو',
+    slug: 'گهرو',
+    province_id: 9
+  },
+  {
+    id: 353,
+    name: 'لردگان',
+    slug: 'لردگان',
+    province_id: 9
+  },
+  {
+    id: 354,
+    name: 'مال خلیفه',
+    slug: 'مال-خلیفه',
+    province_id: 9
+  },
+  {
+    id: 355,
+    name: 'ناغان',
+    slug: 'ناغان',
+    province_id: 9
+  },
+  {
+    id: 356,
+    name: 'نافچ',
+    slug: 'نافچ',
+    province_id: 9
+  },
+  {
+    id: 357,
+    name: 'نقنه',
+    slug: 'نقنه',
+    province_id: 9
+  },
+  {
+    id: 358,
+    name: 'هفشجان',
+    slug: 'هفشجان',
+    province_id: 9
+  },
+  {
+    id: 359,
+    name: 'ارسک',
+    slug: 'ارسک',
+    province_id: 10
+  },
+  {
+    id: 360,
+    name: 'اسدیه',
+    slug: 'اسدیه',
+    province_id: 10
+  },
+  {
+    id: 361,
+    name: 'اسفدن',
+    slug: 'اسفدن',
+    province_id: 10
+  },
+  {
+    id: 362,
+    name: 'اسلامیه',
+    slug: 'اسلامیه',
+    province_id: 10
+  },
+  {
+    id: 363,
+    name: 'آرین شهر',
+    slug: 'آرین-شهر',
+    province_id: 10
+  },
+  {
+    id: 364,
+    name: 'آیسک',
+    slug: 'آیسک',
+    province_id: 10
+  },
+  {
+    id: 365,
+    name: 'بشرویه',
+    slug: 'بشرویه',
+    province_id: 10
+  },
+  {
+    id: 366,
+    name: 'بیرجند',
+    slug: 'بیرجند',
+    province_id: 10
+  },
+  {
+    id: 367,
+    name: 'حاجی آباد',
+    slug: 'خراسان-جنوبی-حاجی-آباد',
+    province_id: 10
+  },
+  {
+    id: 368,
+    name: 'خضری دشت بیاض',
+    slug: 'خضری-دشت-بیاض',
+    province_id: 10
+  },
+  {
+    id: 369,
+    name: 'خوسف',
+    slug: 'خوسف',
+    province_id: 10
+  },
+  {
+    id: 370,
+    name: 'زهان',
+    slug: 'زهان',
+    province_id: 10
+  },
+  {
+    id: 371,
+    name: 'سرایان',
+    slug: 'سرایان',
+    province_id: 10
+  },
+  {
+    id: 372,
+    name: 'سربیشه',
+    slug: 'سربیشه',
+    province_id: 10
+  },
+  {
+    id: 373,
+    name: 'سه قلعه',
+    slug: 'سه-قلعه',
+    province_id: 10
+  },
+  {
+    id: 374,
+    name: 'شوسف',
+    slug: 'شوسف',
+    province_id: 10
+  },
+  {
+    id: 375,
+    name: 'طبس ',
+    slug: 'خراسان-جنوبی-طبس-',
+    province_id: 10
+  },
+  {
+    id: 376,
+    name: 'فردوس',
+    slug: 'فردوس',
+    province_id: 10
+  },
+  {
+    id: 377,
+    name: 'قاین',
+    slug: 'قاین',
+    province_id: 10
+  },
+  {
+    id: 378,
+    name: 'قهستان',
+    slug: 'قهستان',
+    province_id: 10
+  },
+  {
+    id: 379,
+    name: 'محمدشهر',
+    slug: 'خراسان-جنوبی-محمدشهر',
+    province_id: 10
+  },
+  {
+    id: 380,
+    name: 'مود',
+    slug: 'مود',
+    province_id: 10
+  },
+  {
+    id: 381,
+    name: 'نهبندان',
+    slug: 'نهبندان',
+    province_id: 10
+  },
+  {
+    id: 382,
+    name: 'نیمبلوک',
+    slug: 'نیمبلوک',
+    province_id: 10
+  },
+  {
+    id: 383,
+    name: 'احمدآباد صولت',
+    slug: 'احمدآباد-صولت',
+    province_id: 11
+  },
+  {
+    id: 384,
+    name: 'انابد',
+    slug: 'انابد',
+    province_id: 11
+  },
+  {
+    id: 385,
+    name: 'باجگیران',
+    slug: 'باجگیران',
+    province_id: 11
+  },
+  {
+    id: 386,
+    name: 'باخرز',
+    slug: 'باخرز',
+    province_id: 11
+  },
+  {
+    id: 387,
+    name: 'بار',
+    slug: 'بار',
+    province_id: 11
+  },
+  {
+    id: 388,
+    name: 'بایگ',
+    slug: 'بایگ',
+    province_id: 11
+  },
+  {
+    id: 389,
+    name: 'بجستان',
+    slug: 'بجستان',
+    province_id: 11
+  },
+  {
+    id: 390,
+    name: 'بردسکن',
+    slug: 'بردسکن',
+    province_id: 11
+  },
+  {
+    id: 391,
+    name: 'بیدخت',
+    slug: 'بیدخت',
+    province_id: 11
+  },
+  {
+    id: 392,
+    name: 'تایباد',
+    slug: 'تایباد',
+    province_id: 11
+  },
+  {
+    id: 393,
+    name: 'تربت جام',
+    slug: 'تربت-جام',
+    province_id: 11
+  },
+  {
+    id: 394,
+    name: 'تربت حیدریه',
+    slug: 'تربت-حیدریه',
+    province_id: 11
+  },
+  {
+    id: 395,
+    name: 'جغتای',
+    slug: 'جغتای',
+    province_id: 11
+  },
+  {
+    id: 396,
+    name: 'جنگل',
+    slug: 'جنگل',
+    province_id: 11
+  },
+  {
+    id: 397,
+    name: 'چاپشلو',
+    slug: 'چاپشلو',
+    province_id: 11
+  },
+  {
+    id: 398,
+    name: 'چکنه',
+    slug: 'چکنه',
+    province_id: 11
+  },
+  {
+    id: 399,
+    name: 'چناران',
+    slug: 'چناران',
+    province_id: 11
+  },
+  {
+    id: 400,
+    name: 'خرو',
+    slug: 'خرو',
+    province_id: 11
+  },
+  {
+    id: 401,
+    name: 'خلیل آباد',
+    slug: 'خلیل-آباد',
+    province_id: 11
+  },
+  {
+    id: 402,
+    name: 'خواف',
+    slug: 'خواف',
+    province_id: 11
+  },
+  {
+    id: 403,
+    name: 'داورزن',
+    slug: 'داورزن',
+    province_id: 11
+  },
+  {
+    id: 404,
+    name: 'درگز',
+    slug: 'درگز',
+    province_id: 11
+  },
+  {
+    id: 405,
+    name: 'در رود',
+    slug: 'در-رود',
+    province_id: 11
+  },
+  {
+    id: 406,
+    name: 'دولت آباد',
+    slug: 'خراسان-رضوی-دولت-آباد',
+    province_id: 11
+  },
+  {
+    id: 407,
+    name: 'رباط سنگ',
+    slug: 'رباط-سنگ',
+    province_id: 11
+  },
+  {
+    id: 408,
+    name: 'رشتخوار',
+    slug: 'رشتخوار',
+    province_id: 11
+  },
+  {
+    id: 409,
+    name: 'رضویه',
+    slug: 'رضویه',
+    province_id: 11
+  },
+  {
+    id: 410,
+    name: 'روداب',
+    slug: 'روداب',
+    province_id: 11
+  },
+  {
+    id: 411,
+    name: 'ریوش',
+    slug: 'ریوش',
+    province_id: 11
+  },
+  {
+    id: 412,
+    name: 'سبزوار',
+    slug: 'سبزوار',
+    province_id: 11
+  },
+  {
+    id: 413,
+    name: 'سرخس',
+    slug: 'سرخس',
+    province_id: 11
+  },
+  {
+    id: 414,
+    name: 'سفیدسنگ',
+    slug: 'سفیدسنگ',
+    province_id: 11
+  },
+  {
+    id: 415,
+    name: 'سلامی',
+    slug: 'سلامی',
+    province_id: 11
+  },
+  {
+    id: 416,
+    name: 'سلطان آباد',
+    slug: 'سلطان-آباد',
+    province_id: 11
+  },
+  {
+    id: 417,
+    name: 'سنگان',
+    slug: 'سنگان',
+    province_id: 11
+  },
+  {
+    id: 418,
+    name: 'شادمهر',
+    slug: 'شادمهر',
+    province_id: 11
+  },
+  {
+    id: 419,
+    name: 'شاندیز',
+    slug: 'شاندیز',
+    province_id: 11
+  },
+  {
+    id: 420,
+    name: 'ششتمد',
+    slug: 'ششتمد',
+    province_id: 11
+  },
+  {
+    id: 421,
+    name: 'شهرآباد',
+    slug: 'شهرآباد',
+    province_id: 11
+  },
+  {
+    id: 422,
+    name: 'شهرزو',
+    slug: 'شهرزو',
+    province_id: 11
+  },
+  {
+    id: 423,
+    name: 'صالح آباد',
+    slug: 'خراسان-رضوی-صالح-آباد',
+    province_id: 11
+  },
+  {
+    id: 424,
+    name: 'طرقبه',
+    slug: 'طرقبه',
+    province_id: 11
+  },
+  {
+    id: 425,
+    name: 'عشق آباد',
+    slug: 'خراسان-رضوی-عشق-آباد',
+    province_id: 11
+  },
+  {
+    id: 426,
+    name: 'فرهادگرد',
+    slug: 'فرهادگرد',
+    province_id: 11
+  },
+  {
+    id: 427,
+    name: 'فریمان',
+    slug: 'فریمان',
+    province_id: 11
+  },
+  {
+    id: 428,
+    name: 'فیروزه',
+    slug: 'فیروزه',
+    province_id: 11
+  },
+  {
+    id: 429,
+    name: 'فیض آباد',
+    slug: 'فیض-آباد',
+    province_id: 11
+  },
+  {
+    id: 430,
+    name: 'قاسم آباد',
+    slug: 'قاسم-آباد',
+    province_id: 11
+  },
+  {
+    id: 431,
+    name: 'قدمگاه',
+    slug: 'قدمگاه',
+    province_id: 11
+  },
+  {
+    id: 432,
+    name: 'قلندرآباد',
+    slug: 'قلندرآباد',
+    province_id: 11
+  },
+  {
+    id: 433,
+    name: 'قوچان',
+    slug: 'قوچان',
+    province_id: 11
+  },
+  {
+    id: 434,
+    name: 'کاخک',
+    slug: 'کاخک',
+    province_id: 11
+  },
+  {
+    id: 435,
+    name: 'کاریز',
+    slug: 'کاریز',
+    province_id: 11
+  },
+  {
+    id: 436,
+    name: 'کاشمر',
+    slug: 'کاشمر',
+    province_id: 11
+  },
+  {
+    id: 437,
+    name: 'کدکن',
+    slug: 'کدکن',
+    province_id: 11
+  },
+  {
+    id: 438,
+    name: 'کلات',
+    slug: 'کلات',
+    province_id: 11
+  },
+  {
+    id: 439,
+    name: 'کندر',
+    slug: 'کندر',
+    province_id: 11
+  },
+  {
+    id: 440,
+    name: 'گلمکان',
+    slug: 'گلمکان',
+    province_id: 11
+  },
+  {
+    id: 441,
+    name: 'گناباد',
+    slug: 'گناباد',
+    province_id: 11
+  },
+  {
+    id: 442,
+    name: 'لطف آباد',
+    slug: 'لطف-آباد',
+    province_id: 11
+  },
+  {
+    id: 443,
+    name: 'مزدآوند',
+    slug: 'مزدآوند',
+    province_id: 11
+  },
+  {
+    id: 444,
+    name: 'مشهد',
+    slug: 'مشهد',
+    province_id: 11
+  },
+  {
+    id: 445,
+    name: 'ملک آباد',
+    slug: 'ملک-آباد',
+    province_id: 11
+  },
+  {
+    id: 446,
+    name: 'نشتیفان',
+    slug: 'نشتیفان',
+    province_id: 11
+  },
+  {
+    id: 447,
+    name: 'نصرآباد',
+    slug: 'خراسان-رضوی-نصرآباد',
+    province_id: 11
+  },
+  {
+    id: 448,
+    name: 'نقاب',
+    slug: 'نقاب',
+    province_id: 11
+  },
+  {
+    id: 449,
+    name: 'نوخندان',
+    slug: 'نوخندان',
+    province_id: 11
+  },
+  {
+    id: 450,
+    name: 'نیشابور',
+    slug: 'نیشابور',
+    province_id: 11
+  },
+  {
+    id: 451,
+    name: 'نیل شهر',
+    slug: 'نیل-شهر',
+    province_id: 11
+  },
+  {
+    id: 452,
+    name: 'همت آباد',
+    slug: 'همت-آباد',
+    province_id: 11
+  },
+  {
+    id: 453,
+    name: 'یونسی',
+    slug: 'یونسی',
+    province_id: 11
+  },
+  {
+    id: 454,
+    name: 'اسفراین',
+    slug: 'اسفراین',
+    province_id: 12
+  },
+  {
+    id: 455,
+    name: 'ایور',
+    slug: 'ایور',
+    province_id: 12
+  },
+  {
+    id: 456,
+    name: 'آشخانه',
+    slug: 'آشخانه',
+    province_id: 12
+  },
+  {
+    id: 457,
+    name: 'بجنورد',
+    slug: 'بجنورد',
+    province_id: 12
+  },
+  {
+    id: 458,
+    name: 'پیش قلعه',
+    slug: 'پیش-قلعه',
+    province_id: 12
+  },
+  {
+    id: 459,
+    name: 'تیتکانلو',
+    slug: 'تیتکانلو',
+    province_id: 12
+  },
+  {
+    id: 460,
+    name: 'جاجرم',
+    slug: 'جاجرم',
+    province_id: 12
+  },
+  {
+    id: 461,
+    name: 'حصارگرمخان',
+    slug: 'حصارگرمخان',
+    province_id: 12
+  },
+  {
+    id: 462,
+    name: 'درق',
+    slug: 'درق',
+    province_id: 12
+  },
+  {
+    id: 463,
+    name: 'راز',
+    slug: 'راز',
+    province_id: 12
+  },
+  {
+    id: 464,
+    name: 'سنخواست',
+    slug: 'سنخواست',
+    province_id: 12
+  },
+  {
+    id: 465,
+    name: 'شوقان',
+    slug: 'شوقان',
+    province_id: 12
+  },
+  {
+    id: 466,
+    name: 'شیروان',
+    slug: 'شیروان',
+    province_id: 12
+  },
+  {
+    id: 467,
+    name: 'صفی آباد',
+    slug: 'خراسان-شمالی-صفی-آباد',
+    province_id: 12
+  },
+  {
+    id: 468,
+    name: 'فاروج',
+    slug: 'فاروج',
+    province_id: 12
+  },
+  {
+    id: 469,
+    name: 'قاضی',
+    slug: 'قاضی',
+    province_id: 12
+  },
+  {
+    id: 470,
+    name: 'گرمه',
+    slug: 'گرمه',
+    province_id: 12
+  },
+  {
+    id: 471,
+    name: 'لوجلی',
+    slug: 'لوجلی',
+    province_id: 12
+  },
+  {
+    id: 472,
+    name: 'اروندکنار',
+    slug: 'اروندکنار',
+    province_id: 13
+  },
+  {
+    id: 473,
+    name: 'الوان',
+    slug: 'الوان',
+    province_id: 13
+  },
+  {
+    id: 474,
+    name: 'امیدیه',
+    slug: 'امیدیه',
+    province_id: 13
+  },
+  {
+    id: 475,
+    name: 'اندیمشک',
+    slug: 'اندیمشک',
+    province_id: 13
+  },
+  {
+    id: 476,
+    name: 'اهواز',
+    slug: 'اهواز',
+    province_id: 13
+  },
+  {
+    id: 477,
+    name: 'ایذه',
+    slug: 'ایذه',
+    province_id: 13
+  },
+  {
+    id: 478,
+    name: 'آبادان',
+    slug: 'آبادان',
+    province_id: 13
+  },
+  {
+    id: 479,
+    name: 'آغاجاری',
+    slug: 'آغاجاری',
+    province_id: 13
+  },
+  {
+    id: 480,
+    name: 'باغ ملک',
+    slug: 'باغ-ملک',
+    province_id: 13
+  },
+  {
+    id: 481,
+    name: 'بستان',
+    slug: 'بستان',
+    province_id: 13
+  },
+  {
+    id: 482,
+    name: 'بندرامام خمینی',
+    slug: 'بندرامام-خمینی',
+    province_id: 13
+  },
+  {
+    id: 483,
+    name: 'بندرماهشهر',
+    slug: 'بندرماهشهر',
+    province_id: 13
+  },
+  {
+    id: 484,
+    name: 'بهبهان',
+    slug: 'بهبهان',
+    province_id: 13
+  },
+  {
+    id: 485,
+    name: 'ترکالکی',
+    slug: 'ترکالکی',
+    province_id: 13
+  },
+  {
+    id: 486,
+    name: 'جایزان',
+    slug: 'جایزان',
+    province_id: 13
+  },
+  {
+    id: 487,
+    name: 'چمران',
+    slug: 'چمران',
+    province_id: 13
+  },
+  {
+    id: 488,
+    name: 'چویبده',
+    slug: 'چویبده',
+    province_id: 13
+  },
+  {
+    id: 489,
+    name: 'حر',
+    slug: 'حر',
+    province_id: 13
+  },
+  {
+    id: 490,
+    name: 'حسینیه',
+    slug: 'حسینیه',
+    province_id: 13
+  },
+  {
+    id: 491,
+    name: 'حمزه',
+    slug: 'حمزه',
+    province_id: 13
+  },
+  {
+    id: 492,
+    name: 'حمیدیه',
+    slug: 'حمیدیه',
+    province_id: 13
+  },
+  {
+    id: 493,
+    name: 'خرمشهر',
+    slug: 'خرمشهر',
+    province_id: 13
+  },
+  {
+    id: 494,
+    name: 'دارخوین',
+    slug: 'دارخوین',
+    province_id: 13
+  },
+  {
+    id: 495,
+    name: 'دزآب',
+    slug: 'دزآب',
+    province_id: 13
+  },
+  {
+    id: 496,
+    name: 'دزفول',
+    slug: 'دزفول',
+    province_id: 13
+  },
+  {
+    id: 497,
+    name: 'دهدز',
+    slug: 'دهدز',
+    province_id: 13
+  },
+  {
+    id: 498,
+    name: 'رامشیر',
+    slug: 'رامشیر',
+    province_id: 13
+  },
+  {
+    id: 499,
+    name: 'رامهرمز',
+    slug: 'رامهرمز',
+    province_id: 13
+  },
+  {
+    id: 500,
+    name: 'رفیع',
+    slug: 'رفیع',
+    province_id: 13
+  },
+  {
+    id: 501,
+    name: 'زهره',
+    slug: 'زهره',
+    province_id: 13
+  },
+  {
+    id: 502,
+    name: 'سالند',
+    slug: 'سالند',
+    province_id: 13
+  },
+  {
+    id: 503,
+    name: 'سردشت',
+    slug: 'خوزستان-سردشت',
+    province_id: 13
+  },
+  {
+    id: 504,
+    name: 'سوسنگرد',
+    slug: 'سوسنگرد',
+    province_id: 13
+  },
+  {
+    id: 505,
+    name: 'شادگان',
+    slug: 'شادگان',
+    province_id: 13
+  },
+  {
+    id: 506,
+    name: 'شاوور',
+    slug: 'شاوور',
+    province_id: 13
+  },
+  {
+    id: 507,
+    name: 'شرافت',
+    slug: 'شرافت',
+    province_id: 13
+  },
+  {
+    id: 508,
+    name: 'شوش',
+    slug: 'شوش',
+    province_id: 13
+  },
+  {
+    id: 509,
+    name: 'شوشتر',
+    slug: 'شوشتر',
+    province_id: 13
+  },
+  {
+    id: 510,
+    name: 'شیبان',
+    slug: 'شیبان',
+    province_id: 13
+  },
+  {
+    id: 511,
+    name: 'صالح شهر',
+    slug: 'صالح-شهر',
+    province_id: 13
+  },
+  {
+    id: 512,
+    name: 'صفی آباد',
+    slug: 'خوزستان-صفی-آباد',
+    province_id: 13
+  },
+  {
+    id: 513,
+    name: 'صیدون',
+    slug: 'صیدون',
+    province_id: 13
+  },
+  {
+    id: 514,
+    name: 'قلعه تل',
+    slug: 'قلعه-تل',
+    province_id: 13
+  },
+  {
+    id: 515,
+    name: 'قلعه خواجه',
+    slug: 'قلعه-خواجه',
+    province_id: 13
+  },
+  {
+    id: 516,
+    name: 'گتوند',
+    slug: 'گتوند',
+    province_id: 13
+  },
+  {
+    id: 517,
+    name: 'لالی',
+    slug: 'لالی',
+    province_id: 13
+  },
+  {
+    id: 518,
+    name: 'مسجدسلیمان',
+    slug: 'مسجدسلیمان',
+    province_id: 13
+  },
+  {
+    id: 520,
+    name: 'ملاثانی',
+    slug: 'ملاثانی',
+    province_id: 13
+  },
+  {
+    id: 521,
+    name: 'میانرود',
+    slug: 'میانرود',
+    province_id: 13
+  },
+  {
+    id: 522,
+    name: 'مینوشهر',
+    slug: 'مینوشهر',
+    province_id: 13
+  },
+  {
+    id: 523,
+    name: 'هفتگل',
+    slug: 'هفتگل',
+    province_id: 13
+  },
+  {
+    id: 524,
+    name: 'هندیجان',
+    slug: 'هندیجان',
+    province_id: 13
+  },
+  {
+    id: 525,
+    name: 'هویزه',
+    slug: 'هویزه',
+    province_id: 13
+  },
+  {
+    id: 526,
+    name: 'ویس',
+    slug: 'ویس',
+    province_id: 13
+  },
+  {
+    id: 527,
+    name: 'ابهر',
+    slug: 'ابهر',
+    province_id: 14
+  },
+  {
+    id: 528,
+    name: 'ارمغان خانه',
+    slug: 'ارمغان-خانه',
+    province_id: 14
+  },
+  {
+    id: 529,
+    name: 'آب بر',
+    slug: 'آب-بر',
+    province_id: 14
+  },
+  {
+    id: 530,
+    name: 'چورزق',
+    slug: 'چورزق',
+    province_id: 14
+  },
+  {
+    id: 531,
+    name: 'حلب',
+    slug: 'حلب',
+    province_id: 14
+  },
+  {
+    id: 532,
+    name: 'خرمدره',
+    slug: 'خرمدره',
+    province_id: 14
+  },
+  {
+    id: 533,
+    name: 'دندی',
+    slug: 'دندی',
+    province_id: 14
+  },
+  {
+    id: 534,
+    name: 'زرین آباد',
+    slug: 'زرین-آباد',
+    province_id: 14
+  },
+  {
+    id: 535,
+    name: 'زرین رود',
+    slug: 'زرین-رود',
+    province_id: 14
+  },
+  {
+    id: 536,
+    name: 'زنجان',
+    slug: 'زنجان',
+    province_id: 14
+  },
+  {
+    id: 537,
+    name: 'سجاس',
+    slug: 'سجاس',
+    province_id: 14
+  },
+  {
+    id: 538,
+    name: 'سلطانیه',
+    slug: 'سلطانیه',
+    province_id: 14
+  },
+  {
+    id: 539,
+    name: 'سهرورد',
+    slug: 'سهرورد',
+    province_id: 14
+  },
+  {
+    id: 540,
+    name: 'صائین قلعه',
+    slug: 'صائین-قلعه',
+    province_id: 14
+  },
+  {
+    id: 541,
+    name: 'قیدار',
+    slug: 'قیدار',
+    province_id: 14
+  },
+  {
+    id: 542,
+    name: 'گرماب',
+    slug: 'گرماب',
+    province_id: 14
+  },
+  {
+    id: 543,
+    name: 'ماه نشان',
+    slug: 'ماه-نشان',
+    province_id: 14
+  },
+  {
+    id: 544,
+    name: 'هیدج',
+    slug: 'هیدج',
+    province_id: 14
+  },
+  {
+    id: 545,
+    name: 'امیریه',
+    slug: 'امیریه',
+    province_id: 15
+  },
+  {
+    id: 546,
+    name: 'ایوانکی',
+    slug: 'ایوانکی',
+    province_id: 15
+  },
+  {
+    id: 547,
+    name: 'آرادان',
+    slug: 'آرادان',
+    province_id: 15
+  },
+  {
+    id: 548,
+    name: 'بسطام',
+    slug: 'بسطام',
+    province_id: 15
+  },
+  {
+    id: 549,
+    name: 'بیارجمند',
+    slug: 'بیارجمند',
+    province_id: 15
+  },
+  {
+    id: 550,
+    name: 'دامغان',
+    slug: 'دامغان',
+    province_id: 15
+  },
+  {
+    id: 551,
+    name: 'درجزین',
+    slug: 'درجزین',
+    province_id: 15
+  },
+  {
+    id: 552,
+    name: 'دیباج',
+    slug: 'دیباج',
+    province_id: 15
+  },
+  {
+    id: 553,
+    name: 'سرخه',
+    slug: 'سرخه',
+    province_id: 15
+  },
+  {
+    id: 554,
+    name: 'سمنان',
+    slug: 'سمنان',
+    province_id: 15
+  },
+  {
+    id: 555,
+    name: 'شاهرود',
+    slug: 'شاهرود',
+    province_id: 15
+  },
+  {
+    id: 556,
+    name: 'شهمیرزاد',
+    slug: 'شهمیرزاد',
+    province_id: 15
+  },
+  {
+    id: 557,
+    name: 'کلاته خیج',
+    slug: 'کلاته-خیج',
+    province_id: 15
+  },
+  {
+    id: 558,
+    name: 'گرمسار',
+    slug: 'گرمسار',
+    province_id: 15
+  },
+  {
+    id: 559,
+    name: 'مجن',
+    slug: 'مجن',
+    province_id: 15
+  },
+  {
+    id: 560,
+    name: 'مهدی شهر',
+    slug: 'مهدی-شهر',
+    province_id: 15
+  },
+  {
+    id: 561,
+    name: 'میامی',
+    slug: 'میامی',
+    province_id: 15
+  },
+  {
+    id: 562,
+    name: 'ادیمی',
+    slug: 'ادیمی',
+    province_id: 16
+  },
+  {
+    id: 563,
+    name: 'اسپکه',
+    slug: 'اسپکه',
+    province_id: 16
+  },
+  {
+    id: 564,
+    name: 'ایرانشهر',
+    slug: 'ایرانشهر',
+    province_id: 16
+  },
+  {
+    id: 565,
+    name: 'بزمان',
+    slug: 'بزمان',
+    province_id: 16
+  },
+  {
+    id: 566,
+    name: 'بمپور',
+    slug: 'بمپور',
+    province_id: 16
+  },
+  {
+    id: 567,
+    name: 'بنت',
+    slug: 'بنت',
+    province_id: 16
+  },
+  {
+    id: 568,
+    name: 'بنجار',
+    slug: 'بنجار',
+    province_id: 16
+  },
+  {
+    id: 569,
+    name: 'پیشین',
+    slug: 'پیشین',
+    province_id: 16
+  },
+  {
+    id: 570,
+    name: 'جالق',
+    slug: 'جالق',
+    province_id: 16
+  },
+  {
+    id: 571,
+    name: 'چابهار',
+    slug: 'چابهار',
+    province_id: 16
+  },
+  {
+    id: 572,
+    name: 'خاش',
+    slug: 'خاش',
+    province_id: 16
+  },
+  {
+    id: 573,
+    name: 'دوست محمد',
+    slug: 'دوست-محمد',
+    province_id: 16
+  },
+  {
+    id: 574,
+    name: 'راسک',
+    slug: 'راسک',
+    province_id: 16
+  },
+  {
+    id: 575,
+    name: 'زابل',
+    slug: 'زابل',
+    province_id: 16
+  },
+  {
+    id: 576,
+    name: 'زابلی',
+    slug: 'زابلی',
+    province_id: 16
+  },
+  {
+    id: 577,
+    name: 'زاهدان',
+    slug: 'زاهدان',
+    province_id: 16
+  },
+  {
+    id: 578,
+    name: 'زهک',
+    slug: 'زهک',
+    province_id: 16
+  },
+  {
+    id: 579,
+    name: 'سراوان',
+    slug: 'سراوان',
+    province_id: 16
+  },
+  {
+    id: 580,
+    name: 'سرباز',
+    slug: 'سرباز',
+    province_id: 16
+  },
+  {
+    id: 581,
+    name: 'سوران',
+    slug: 'سوران',
+    province_id: 16
+  },
+  {
+    id: 582,
+    name: 'سیرکان',
+    slug: 'سیرکان',
+    province_id: 16
+  },
+  {
+    id: 583,
+    name: 'علی اکبر',
+    slug: 'علی-اکبر',
+    province_id: 16
+  },
+  {
+    id: 584,
+    name: 'فنوج',
+    slug: 'فنوج',
+    province_id: 16
+  },
+  {
+    id: 585,
+    name: 'قصرقند',
+    slug: 'قصرقند',
+    province_id: 16
+  },
+  {
+    id: 586,
+    name: 'کنارک',
+    slug: 'کنارک',
+    province_id: 16
+  },
+  {
+    id: 587,
+    name: 'گشت',
+    slug: 'گشت',
+    province_id: 16
+  },
+  {
+    id: 588,
+    name: 'گلمورتی',
+    slug: 'گلمورتی',
+    province_id: 16
+  },
+  {
+    id: 589,
+    name: 'محمدان',
+    slug: 'محمدان',
+    province_id: 16
+  },
+  {
+    id: 590,
+    name: 'محمدآباد',
+    slug: 'سیستان-و-بلوچستان-محمدآباد',
+    province_id: 16
+  },
+  {
+    id: 591,
+    name: 'محمدی',
+    slug: 'محمدی',
+    province_id: 16
+  },
+  {
+    id: 592,
+    name: 'میرجاوه',
+    slug: 'میرجاوه',
+    province_id: 16
+  },
+  {
+    id: 593,
+    name: 'نصرت آباد',
+    slug: 'نصرت-آباد',
+    province_id: 16
+  },
+  {
+    id: 594,
+    name: 'نگور',
+    slug: 'نگور',
+    province_id: 16
+  },
+  {
+    id: 595,
+    name: 'نوک آباد',
+    slug: 'نوک-آباد',
+    province_id: 16
+  },
+  {
+    id: 596,
+    name: 'نیک شهر',
+    slug: 'نیک-شهر',
+    province_id: 16
+  },
+  {
+    id: 597,
+    name: 'هیدوچ',
+    slug: 'هیدوچ',
+    province_id: 16
+  },
+  {
+    id: 598,
+    name: 'اردکان',
+    slug: 'فارس-اردکان',
+    province_id: 17
+  },
+  {
+    id: 599,
+    name: 'ارسنجان',
+    slug: 'ارسنجان',
+    province_id: 17
+  },
+  {
+    id: 600,
+    name: 'استهبان',
+    slug: 'استهبان',
+    province_id: 17
+  },
+  {
+    id: 601,
+    name: 'اشکنان',
+    slug: 'اشکنان',
+    province_id: 17
+  },
+  {
+    id: 602,
+    name: 'افزر',
+    slug: 'افزر',
+    province_id: 17
+  },
+  {
+    id: 603,
+    name: 'اقلید',
+    slug: 'اقلید',
+    province_id: 17
+  },
+  {
+    id: 604,
+    name: 'امام شهر',
+    slug: 'امام-شهر',
+    province_id: 17
+  },
+  {
+    id: 605,
+    name: 'اهل',
+    slug: 'اهل',
+    province_id: 17
+  },
+  {
+    id: 606,
+    name: 'اوز',
+    slug: 'اوز',
+    province_id: 17
+  },
+  {
+    id: 607,
+    name: 'ایج',
+    slug: 'ایج',
+    province_id: 17
+  },
+  {
+    id: 608,
+    name: 'ایزدخواست',
+    slug: 'ایزدخواست',
+    province_id: 17
+  },
+  {
+    id: 609,
+    name: 'آباده',
+    slug: 'آباده',
+    province_id: 17
+  },
+  {
+    id: 610,
+    name: 'آباده طشک',
+    slug: 'آباده-طشک',
+    province_id: 17
+  },
+  {
+    id: 611,
+    name: 'باب انار',
+    slug: 'باب-انار',
+    province_id: 17
+  },
+  {
+    id: 612,
+    name: 'بالاده',
+    slug: 'فارس-بالاده',
+    province_id: 17
+  },
+  {
+    id: 613,
+    name: 'بنارویه',
+    slug: 'بنارویه',
+    province_id: 17
+  },
+  {
+    id: 614,
+    name: 'بهمن',
+    slug: 'بهمن',
+    province_id: 17
+  },
+  {
+    id: 615,
+    name: 'بوانات',
+    slug: 'بوانات',
+    province_id: 17
+  },
+  {
+    id: 616,
+    name: 'بیرم',
+    slug: 'بیرم',
+    province_id: 17
+  },
+  {
+    id: 617,
+    name: 'بیضا',
+    slug: 'بیضا',
+    province_id: 17
+  },
+  {
+    id: 618,
+    name: 'جنت شهر',
+    slug: 'جنت-شهر',
+    province_id: 17
+  },
+  {
+    id: 619,
+    name: 'جهرم',
+    slug: 'جهرم',
+    province_id: 17
+  },
+  {
+    id: 620,
+    name: 'جویم',
+    slug: 'جویم',
+    province_id: 17
+  },
+  {
+    id: 621,
+    name: 'زرین دشت',
+    slug: 'زرین-دشت',
+    province_id: 17
+  },
+  {
+    id: 622,
+    name: 'حسن آباد',
+    slug: 'فارس-حسن-آباد',
+    province_id: 17
+  },
+  {
+    id: 623,
+    name: 'خان زنیان',
+    slug: 'خان-زنیان',
+    province_id: 17
+  },
+  {
+    id: 624,
+    name: 'خاوران',
+    slug: 'خاوران',
+    province_id: 17
+  },
+  {
+    id: 625,
+    name: 'خرامه',
+    slug: 'خرامه',
+    province_id: 17
+  },
+  {
+    id: 626,
+    name: 'خشت',
+    slug: 'خشت',
+    province_id: 17
+  },
+  {
+    id: 627,
+    name: 'خنج',
+    slug: 'خنج',
+    province_id: 17
+  },
+  {
+    id: 628,
+    name: 'خور',
+    slug: 'فارس-خور',
+    province_id: 17
+  },
+  {
+    id: 629,
+    name: 'داراب',
+    slug: 'داراب',
+    province_id: 17
+  },
+  {
+    id: 630,
+    name: 'داریان',
+    slug: 'داریان',
+    province_id: 17
+  },
+  {
+    id: 631,
+    name: 'دبیران',
+    slug: 'دبیران',
+    province_id: 17
+  },
+  {
+    id: 632,
+    name: 'دژکرد',
+    slug: 'دژکرد',
+    province_id: 17
+  },
+  {
+    id: 633,
+    name: 'دهرم',
+    slug: 'دهرم',
+    province_id: 17
+  },
+  {
+    id: 634,
+    name: 'دوبرجی',
+    slug: 'دوبرجی',
+    province_id: 17
+  },
+  {
+    id: 635,
+    name: 'رامجرد',
+    slug: 'رامجرد',
+    province_id: 17
+  },
+  {
+    id: 636,
+    name: 'رونیز',
+    slug: 'رونیز',
+    province_id: 17
+  },
+  {
+    id: 637,
+    name: 'زاهدشهر',
+    slug: 'زاهدشهر',
+    province_id: 17
+  },
+  {
+    id: 638,
+    name: 'زرقان',
+    slug: 'زرقان',
+    province_id: 17
+  },
+  {
+    id: 639,
+    name: 'سده',
+    slug: 'سده',
+    province_id: 17
+  },
+  {
+    id: 640,
+    name: 'سروستان',
+    slug: 'سروستان',
+    province_id: 17
+  },
+  {
+    id: 641,
+    name: 'سعادت شهر',
+    slug: 'سعادت-شهر',
+    province_id: 17
+  },
+  {
+    id: 642,
+    name: 'سورمق',
+    slug: 'سورمق',
+    province_id: 17
+  },
+  {
+    id: 643,
+    name: 'سیدان',
+    slug: 'سیدان',
+    province_id: 17
+  },
+  {
+    id: 644,
+    name: 'ششده',
+    slug: 'ششده',
+    province_id: 17
+  },
+  {
+    id: 645,
+    name: 'شهرپیر',
+    slug: 'شهرپیر',
+    province_id: 17
+  },
+  {
+    id: 646,
+    name: 'شهرصدرا',
+    slug: 'شهرصدرا',
+    province_id: 17
+  },
+  {
+    id: 647,
+    name: 'شیراز',
+    slug: 'شیراز',
+    province_id: 17
+  },
+  {
+    id: 648,
+    name: 'صغاد',
+    slug: 'صغاد',
+    province_id: 17
+  },
+  {
+    id: 649,
+    name: 'صفاشهر',
+    slug: 'صفاشهر',
+    province_id: 17
+  },
+  {
+    id: 650,
+    name: 'علامرودشت',
+    slug: 'علامرودشت',
+    province_id: 17
+  },
+  {
+    id: 651,
+    name: 'فدامی',
+    slug: 'فدامی',
+    province_id: 17
+  },
+  {
+    id: 652,
+    name: 'فراشبند',
+    slug: 'فراشبند',
+    province_id: 17
+  },
+  {
+    id: 653,
+    name: 'فسا',
+    slug: 'فسا',
+    province_id: 17
+  },
+  {
+    id: 654,
+    name: 'فیروزآباد',
+    slug: 'فارس-فیروزآباد',
+    province_id: 17
+  },
+  {
+    id: 655,
+    name: 'قائمیه',
+    slug: 'قائمیه',
+    province_id: 17
+  },
+  {
+    id: 656,
+    name: 'قادرآباد',
+    slug: 'قادرآباد',
+    province_id: 17
+  },
+  {
+    id: 657,
+    name: 'قطب آباد',
+    slug: 'قطب-آباد',
+    province_id: 17
+  },
+  {
+    id: 658,
+    name: 'قطرویه',
+    slug: 'قطرویه',
+    province_id: 17
+  },
+  {
+    id: 659,
+    name: 'قیر',
+    slug: 'قیر',
+    province_id: 17
+  },
+  {
+    id: 660,
+    name: 'کارزین (فتح آباد)',
+    slug: 'کارزین-فتح-آباد',
+    province_id: 17
+  },
+  {
+    id: 661,
+    name: 'کازرون',
+    slug: 'کازرون',
+    province_id: 17
+  },
+  {
+    id: 662,
+    name: 'کامفیروز',
+    slug: 'کامفیروز',
+    province_id: 17
+  },
+  {
+    id: 663,
+    name: 'کره ای',
+    slug: 'کره-ای',
+    province_id: 17
+  },
+  {
+    id: 664,
+    name: 'کنارتخته',
+    slug: 'کنارتخته',
+    province_id: 17
+  },
+  {
+    id: 665,
+    name: 'کوار',
+    slug: 'کوار',
+    province_id: 17
+  },
+  {
+    id: 666,
+    name: 'گراش',
+    slug: 'گراش',
+    province_id: 17
+  },
+  {
+    id: 667,
+    name: 'گله دار',
+    slug: 'گله-دار',
+    province_id: 17
+  },
+  {
+    id: 668,
+    name: 'لار',
+    slug: 'لار',
+    province_id: 17
+  },
+  {
+    id: 669,
+    name: 'لامرد',
+    slug: 'لامرد',
+    province_id: 17
+  },
+  {
+    id: 670,
+    name: 'لپویی',
+    slug: 'لپویی',
+    province_id: 17
+  },
+  {
+    id: 671,
+    name: 'لطیفی',
+    slug: 'لطیفی',
+    province_id: 17
+  },
+  {
+    id: 672,
+    name: 'مبارک آباددیز',
+    slug: 'مبارک-آباددیز',
+    province_id: 17
+  },
+  {
+    id: 673,
+    name: 'مرودشت',
+    slug: 'مرودشت',
+    province_id: 17
+  },
+  {
+    id: 674,
+    name: 'مشکان',
+    slug: 'مشکان',
+    province_id: 17
+  },
+  {
+    id: 675,
+    name: 'مصیری',
+    slug: 'مصیری',
+    province_id: 17
+  },
+  {
+    id: 676,
+    name: 'مهر',
+    slug: 'مهر',
+    province_id: 17
+  },
+  {
+    id: 677,
+    name: 'میمند',
+    slug: 'میمند',
+    province_id: 17
+  },
+  {
+    id: 678,
+    name: 'نوبندگان',
+    slug: 'نوبندگان',
+    province_id: 17
+  },
+  {
+    id: 679,
+    name: 'نوجین',
+    slug: 'نوجین',
+    province_id: 17
+  },
+  {
+    id: 680,
+    name: 'نودان',
+    slug: 'نودان',
+    province_id: 17
+  },
+  {
+    id: 681,
+    name: 'نورآباد',
+    slug: 'فارس-نورآباد',
+    province_id: 17
+  },
+  {
+    id: 682,
+    name: 'نی ریز',
+    slug: 'نی-ریز',
+    province_id: 17
+  },
+  {
+    id: 683,
+    name: 'وراوی',
+    slug: 'وراوی',
+    province_id: 17
+  },
+  {
+    id: 684,
+    name: 'ارداق',
+    slug: 'ارداق',
+    province_id: 18
+  },
+  {
+    id: 685,
+    name: 'اسفرورین',
+    slug: 'اسفرورین',
+    province_id: 18
+  },
+  {
+    id: 686,
+    name: 'اقبالیه',
+    slug: 'اقبالیه',
+    province_id: 18
+  },
+  {
+    id: 687,
+    name: 'الوند',
+    slug: 'الوند',
+    province_id: 18
+  },
+  {
+    id: 688,
+    name: 'آبگرم',
+    slug: 'آبگرم',
+    province_id: 18
+  },
+  {
+    id: 689,
+    name: 'آبیک',
+    slug: 'آبیک',
+    province_id: 18
+  },
+  {
+    id: 690,
+    name: 'آوج',
+    slug: 'آوج',
+    province_id: 18
+  },
+  {
+    id: 691,
+    name: 'بوئین زهرا',
+    slug: 'بوئین-زهرا',
+    province_id: 18
+  },
+  {
+    id: 692,
+    name: 'بیدستان',
+    slug: 'بیدستان',
+    province_id: 18
+  },
+  {
+    id: 693,
+    name: 'تاکستان',
+    slug: 'تاکستان',
+    province_id: 18
+  },
+  {
+    id: 694,
+    name: 'خاکعلی',
+    slug: 'خاکعلی',
+    province_id: 18
+  },
+  {
+    id: 695,
+    name: 'خرمدشت',
+    slug: 'خرمدشت',
+    province_id: 18
+  },
+  {
+    id: 696,
+    name: 'دانسفهان',
+    slug: 'دانسفهان',
+    province_id: 18
+  },
+  {
+    id: 697,
+    name: 'رازمیان',
+    slug: 'رازمیان',
+    province_id: 18
+  },
+  {
+    id: 698,
+    name: 'سگزآباد',
+    slug: 'سگزآباد',
+    province_id: 18
+  },
+  {
+    id: 699,
+    name: 'سیردان',
+    slug: 'سیردان',
+    province_id: 18
+  },
+  {
+    id: 700,
+    name: 'شال',
+    slug: 'شال',
+    province_id: 18
+  },
+  {
+    id: 701,
+    name: 'شریفیه',
+    slug: 'شریفیه',
+    province_id: 18
+  },
+  {
+    id: 702,
+    name: 'ضیاآباد',
+    slug: 'ضیاآباد',
+    province_id: 18
+  },
+  {
+    id: 703,
+    name: 'قزوین',
+    slug: 'قزوین',
+    province_id: 18
+  },
+  {
+    id: 704,
+    name: 'کوهین',
+    slug: 'کوهین',
+    province_id: 18
+  },
+  {
+    id: 705,
+    name: 'محمدیه',
+    slug: 'محمدیه',
+    province_id: 18
+  },
+  {
+    id: 706,
+    name: 'محمودآباد نمونه',
+    slug: 'محمودآباد-نمونه',
+    province_id: 18
+  },
+  {
+    id: 707,
+    name: 'معلم کلایه',
+    slug: 'معلم-کلایه',
+    province_id: 18
+  },
+  {
+    id: 708,
+    name: 'نرجه',
+    slug: 'نرجه',
+    province_id: 18
+  },
+  {
+    id: 709,
+    name: 'جعفریه',
+    slug: 'جعفریه',
+    province_id: 19
+  },
+  {
+    id: 710,
+    name: 'دستجرد',
+    slug: 'دستجرد',
+    province_id: 19
+  },
+  {
+    id: 711,
+    name: 'سلفچگان',
+    slug: 'سلفچگان',
+    province_id: 19
+  },
+  {
+    id: 712,
+    name: 'قم',
+    slug: 'قم',
+    province_id: 19
+  },
+  {
+    id: 713,
+    name: 'قنوات',
+    slug: 'قنوات',
+    province_id: 19
+  },
+  {
+    id: 714,
+    name: 'کهک',
+    slug: 'کهک',
+    province_id: 19
+  },
+  {
+    id: 715,
+    name: 'آرمرده',
+    slug: 'آرمرده',
+    province_id: 20
+  },
+  {
+    id: 716,
+    name: 'بابارشانی',
+    slug: 'بابارشانی',
+    province_id: 20
+  },
+  {
+    id: 717,
+    name: 'بانه',
+    slug: 'بانه',
+    province_id: 20
+  },
+  {
+    id: 718,
+    name: 'بلبان آباد',
+    slug: 'بلبان-آباد',
+    province_id: 20
+  },
+  {
+    id: 719,
+    name: 'بوئین سفلی',
+    slug: 'بوئین-سفلی',
+    province_id: 20
+  },
+  {
+    id: 720,
+    name: 'بیجار',
+    slug: 'بیجار',
+    province_id: 20
+  },
+  {
+    id: 721,
+    name: 'چناره',
+    slug: 'چناره',
+    province_id: 20
+  },
+  {
+    id: 722,
+    name: 'دزج',
+    slug: 'دزج',
+    province_id: 20
+  },
+  {
+    id: 723,
+    name: 'دلبران',
+    slug: 'دلبران',
+    province_id: 20
+  },
+  {
+    id: 724,
+    name: 'دهگلان',
+    slug: 'دهگلان',
+    province_id: 20
+  },
+  {
+    id: 725,
+    name: 'دیواندره',
+    slug: 'دیواندره',
+    province_id: 20
+  },
+  {
+    id: 726,
+    name: 'زرینه',
+    slug: 'زرینه',
+    province_id: 20
+  },
+  {
+    id: 727,
+    name: 'سروآباد',
+    slug: 'سروآباد',
+    province_id: 20
+  },
+  {
+    id: 728,
+    name: 'سریش آباد',
+    slug: 'سریش-آباد',
+    province_id: 20
+  },
+  {
+    id: 729,
+    name: 'سقز',
+    slug: 'سقز',
+    province_id: 20
+  },
+  {
+    id: 730,
+    name: 'سنندج',
+    slug: 'سنندج',
+    province_id: 20
+  },
+  {
+    id: 731,
+    name: 'شویشه',
+    slug: 'شویشه',
+    province_id: 20
+  },
+  {
+    id: 732,
+    name: 'صاحب',
+    slug: 'صاحب',
+    province_id: 20
+  },
+  {
+    id: 733,
+    name: 'قروه',
+    slug: 'قروه',
+    province_id: 20
+  },
+  {
+    id: 734,
+    name: 'کامیاران',
+    slug: 'کامیاران',
+    province_id: 20
+  },
+  {
+    id: 735,
+    name: 'کانی دینار',
+    slug: 'کانی-دینار',
+    province_id: 20
+  },
+  {
+    id: 736,
+    name: 'کانی سور',
+    slug: 'کانی-سور',
+    province_id: 20
+  },
+  {
+    id: 737,
+    name: 'مریوان',
+    slug: 'مریوان',
+    province_id: 20
+  },
+  {
+    id: 738,
+    name: 'موچش',
+    slug: 'موچش',
+    province_id: 20
+  },
+  {
+    id: 739,
+    name: 'یاسوکند',
+    slug: 'یاسوکند',
+    province_id: 20
+  },
+  {
+    id: 740,
+    name: 'اختیارآباد',
+    slug: 'اختیارآباد',
+    province_id: 21
+  },
+  {
+    id: 741,
+    name: 'ارزوئیه',
+    slug: 'ارزوئیه',
+    province_id: 21
+  },
+  {
+    id: 742,
+    name: 'امین شهر',
+    slug: 'امین-شهر',
+    province_id: 21
+  },
+  {
+    id: 743,
+    name: 'انار',
+    slug: 'انار',
+    province_id: 21
+  },
+  {
+    id: 744,
+    name: 'اندوهجرد',
+    slug: 'اندوهجرد',
+    province_id: 21
+  },
+  {
+    id: 745,
+    name: 'باغین',
+    slug: 'باغین',
+    province_id: 21
+  },
+  {
+    id: 746,
+    name: 'بافت',
+    slug: 'بافت',
+    province_id: 21
+  },
+  {
+    id: 747,
+    name: 'بردسیر',
+    slug: 'بردسیر',
+    province_id: 21
+  },
+  {
+    id: 748,
+    name: 'بروات',
+    slug: 'بروات',
+    province_id: 21
+  },
+  {
+    id: 749,
+    name: 'بزنجان',
+    slug: 'بزنجان',
+    province_id: 21
+  },
+  {
+    id: 750,
+    name: 'بم',
+    slug: 'بم',
+    province_id: 21
+  },
+  {
+    id: 751,
+    name: 'بهرمان',
+    slug: 'بهرمان',
+    province_id: 21
+  },
+  {
+    id: 752,
+    name: 'پاریز',
+    slug: 'پاریز',
+    province_id: 21
+  },
+  {
+    id: 753,
+    name: 'جبالبارز',
+    slug: 'جبالبارز',
+    province_id: 21
+  },
+  {
+    id: 754,
+    name: 'جوپار',
+    slug: 'جوپار',
+    province_id: 21
+  },
+  {
+    id: 755,
+    name: 'جوزم',
+    slug: 'جوزم',
+    province_id: 21
+  },
+  {
+    id: 756,
+    name: 'جیرفت',
+    slug: 'جیرفت',
+    province_id: 21
+  },
+  {
+    id: 757,
+    name: 'چترود',
+    slug: 'چترود',
+    province_id: 21
+  },
+  {
+    id: 758,
+    name: 'خاتون آباد',
+    slug: 'خاتون-آباد',
+    province_id: 21
+  },
+  {
+    id: 759,
+    name: 'خانوک',
+    slug: 'خانوک',
+    province_id: 21
+  },
+  {
+    id: 760,
+    name: 'خورسند',
+    slug: 'خورسند',
+    province_id: 21
+  },
+  {
+    id: 761,
+    name: 'درب بهشت',
+    slug: 'درب-بهشت',
+    province_id: 21
+  },
+  {
+    id: 762,
+    name: 'دهج',
+    slug: 'دهج',
+    province_id: 21
+  },
+  {
+    id: 763,
+    name: 'رابر',
+    slug: 'رابر',
+    province_id: 21
+  },
+  {
+    id: 764,
+    name: 'راور',
+    slug: 'راور',
+    province_id: 21
+  },
+  {
+    id: 765,
+    name: 'راین',
+    slug: 'راین',
+    province_id: 21
+  },
+  {
+    id: 766,
+    name: 'رفسنجان',
+    slug: 'رفسنجان',
+    province_id: 21
+  },
+  {
+    id: 767,
+    name: 'رودبار',
+    slug: 'کرمان-رودبار',
+    province_id: 21
+  },
+  {
+    id: 768,
+    name: 'ریحان شهر',
+    slug: 'ریحان-شهر',
+    province_id: 21
+  },
+  {
+    id: 769,
+    name: 'زرند',
+    slug: 'زرند',
+    province_id: 21
+  },
+  {
+    id: 770,
+    name: 'زنگی آباد',
+    slug: 'زنگی-آباد',
+    province_id: 21
+  },
+  {
+    id: 771,
+    name: 'زیدآباد',
+    slug: 'زیدآباد',
+    province_id: 21
+  },
+  {
+    id: 772,
+    name: 'سیرجان',
+    slug: 'سیرجان',
+    province_id: 21
+  },
+  {
+    id: 773,
+    name: 'شهداد',
+    slug: 'شهداد',
+    province_id: 21
+  },
+  {
+    id: 774,
+    name: 'شهربابک',
+    slug: 'شهربابک',
+    province_id: 21
+  },
+  {
+    id: 775,
+    name: 'صفائیه',
+    slug: 'صفائیه',
+    province_id: 21
+  },
+  {
+    id: 776,
+    name: 'عنبرآباد',
+    slug: 'عنبرآباد',
+    province_id: 21
+  },
+  {
+    id: 777,
+    name: 'فاریاب',
+    slug: 'فاریاب',
+    province_id: 21
+  },
+  {
+    id: 778,
+    name: 'فهرج',
+    slug: 'فهرج',
+    province_id: 21
+  },
+  {
+    id: 779,
+    name: 'قلعه گنج',
+    slug: 'قلعه-گنج',
+    province_id: 21
+  },
+  {
+    id: 780,
+    name: 'کاظم آباد',
+    slug: 'کاظم-آباد',
+    province_id: 21
+  },
+  {
+    id: 781,
+    name: 'کرمان',
+    slug: 'کرمان',
+    province_id: 21
+  },
+  {
+    id: 782,
+    name: 'کشکوئیه',
+    slug: 'کشکوئیه',
+    province_id: 21
+  },
+  {
+    id: 783,
+    name: 'کهنوج',
+    slug: 'کهنوج',
+    province_id: 21
+  },
+  {
+    id: 784,
+    name: 'کوهبنان',
+    slug: 'کوهبنان',
+    province_id: 21
+  },
+  {
+    id: 785,
+    name: 'کیانشهر',
+    slug: 'کیانشهر',
+    province_id: 21
+  },
+  {
+    id: 786,
+    name: 'گلباف',
+    slug: 'گلباف',
+    province_id: 21
+  },
+  {
+    id: 787,
+    name: 'گلزار',
+    slug: 'گلزار',
+    province_id: 21
+  },
+  {
+    id: 788,
+    name: 'لاله زار',
+    slug: 'لاله-زار',
+    province_id: 21
+  },
+  {
+    id: 789,
+    name: 'ماهان',
+    slug: 'ماهان',
+    province_id: 21
+  },
+  {
+    id: 790,
+    name: 'محمدآباد',
+    slug: 'کرمان-محمدآباد',
+    province_id: 21
+  },
+  {
+    id: 791,
+    name: 'محی آباد',
+    slug: 'محی-آباد',
+    province_id: 21
+  },
+  {
+    id: 792,
+    name: 'مردهک',
+    slug: 'مردهک',
+    province_id: 21
+  },
+  {
+    id: 793,
+    name: 'مس سرچشمه',
+    slug: 'مس-سرچشمه',
+    province_id: 21
+  },
+  {
+    id: 794,
+    name: 'منوجان',
+    slug: 'منوجان',
+    province_id: 21
+  },
+  {
+    id: 795,
+    name: 'نجف شهر',
+    slug: 'نجف-شهر',
+    province_id: 21
+  },
+  {
+    id: 796,
+    name: 'نرماشیر',
+    slug: 'نرماشیر',
+    province_id: 21
+  },
+  {
+    id: 797,
+    name: 'نظام شهر',
+    slug: 'نظام-شهر',
+    province_id: 21
+  },
+  {
+    id: 798,
+    name: 'نگار',
+    slug: 'نگار',
+    province_id: 21
+  },
+  {
+    id: 799,
+    name: 'نودژ',
+    slug: 'نودژ',
+    province_id: 21
+  },
+  {
+    id: 800,
+    name: 'هجدک',
+    slug: 'هجدک',
+    province_id: 21
+  },
+  {
+    id: 801,
+    name: 'یزدان شهر',
+    slug: 'یزدان-شهر',
+    province_id: 21
+  },
+  {
+    id: 802,
+    name: 'ازگله',
+    slug: 'ازگله',
+    province_id: 22
+  },
+  {
+    id: 803,
+    name: 'اسلام آباد غرب',
+    slug: 'اسلام-آباد-غرب',
+    province_id: 22
+  },
+  {
+    id: 804,
+    name: 'باینگان',
+    slug: 'باینگان',
+    province_id: 22
+  },
+  {
+    id: 805,
+    name: 'بیستون',
+    slug: 'بیستون',
+    province_id: 22
+  },
+  {
+    id: 806,
+    name: 'پاوه',
+    slug: 'پاوه',
+    province_id: 22
+  },
+  {
+    id: 807,
+    name: 'تازه آباد',
+    slug: 'تازه-آباد',
+    province_id: 22
+  },
+  {
+    id: 808,
+    name: 'جوان رود',
+    slug: 'جوان-رود',
+    province_id: 22
+  },
+  {
+    id: 809,
+    name: 'حمیل',
+    slug: 'حمیل',
+    province_id: 22
+  },
+  {
+    id: 810,
+    name: 'ماهیدشت',
+    slug: 'ماهیدشت',
+    province_id: 22
+  },
+  {
+    id: 811,
+    name: 'روانسر',
+    slug: 'روانسر',
+    province_id: 22
+  },
+  {
+    id: 812,
+    name: 'سرپل ذهاب',
+    slug: 'سرپل-ذهاب',
+    province_id: 22
+  },
+  {
+    id: 813,
+    name: 'سرمست',
+    slug: 'سرمست',
+    province_id: 22
+  },
+  {
+    id: 814,
+    name: 'سطر',
+    slug: 'سطر',
+    province_id: 22
+  },
+  {
+    id: 815,
+    name: 'سنقر',
+    slug: 'سنقر',
+    province_id: 22
+  },
+  {
+    id: 816,
+    name: 'سومار',
+    slug: 'سومار',
+    province_id: 22
+  },
+  {
+    id: 817,
+    name: 'شاهو',
+    slug: 'شاهو',
+    province_id: 22
+  },
+  {
+    id: 818,
+    name: 'صحنه',
+    slug: 'صحنه',
+    province_id: 22
+  },
+  {
+    id: 819,
+    name: 'قصرشیرین',
+    slug: 'قصرشیرین',
+    province_id: 22
+  },
+  {
+    id: 820,
+    name: 'کرمانشاه',
+    slug: 'کرمانشاه',
+    province_id: 22
+  },
+  {
+    id: 821,
+    name: 'کرندغرب',
+    slug: 'کرندغرب',
+    province_id: 22
+  },
+  {
+    id: 822,
+    name: 'کنگاور',
+    slug: 'کنگاور',
+    province_id: 22
+  },
+  {
+    id: 823,
+    name: 'کوزران',
+    slug: 'کوزران',
+    province_id: 22
+  },
+  {
+    id: 824,
+    name: 'گهواره',
+    slug: 'گهواره',
+    province_id: 22
+  },
+  {
+    id: 825,
+    name: 'گیلانغرب',
+    slug: 'گیلانغرب',
+    province_id: 22
+  },
+  {
+    id: 826,
+    name: 'میان راهان',
+    slug: 'میان-راهان',
+    province_id: 22
+  },
+  {
+    id: 827,
+    name: 'نودشه',
+    slug: 'نودشه',
+    province_id: 22
+  },
+  {
+    id: 828,
+    name: 'نوسود',
+    slug: 'نوسود',
+    province_id: 22
+  },
+  {
+    id: 829,
+    name: 'هرسین',
+    slug: 'هرسین',
+    province_id: 22
+  },
+  {
+    id: 830,
+    name: 'هلشی',
+    slug: 'هلشی',
+    province_id: 22
+  },
+  {
+    id: 831,
+    name: 'باشت',
+    slug: 'باشت',
+    province_id: 23
+  },
+  {
+    id: 832,
+    name: 'پاتاوه',
+    slug: 'پاتاوه',
+    province_id: 23
+  },
+  {
+    id: 833,
+    name: 'چرام',
+    slug: 'چرام',
+    province_id: 23
+  },
+  {
+    id: 834,
+    name: 'چیتاب',
+    slug: 'چیتاب',
+    province_id: 23
+  },
+  {
+    id: 835,
+    name: 'دهدشت',
+    slug: 'دهدشت',
+    province_id: 23
+  },
+  {
+    id: 836,
+    name: 'دوگنبدان',
+    slug: 'دوگنبدان',
+    province_id: 23
+  },
+  {
+    id: 837,
+    name: 'دیشموک',
+    slug: 'دیشموک',
+    province_id: 23
+  },
+  {
+    id: 838,
+    name: 'سوق',
+    slug: 'سوق',
+    province_id: 23
+  },
+  {
+    id: 839,
+    name: 'سی سخت',
+    slug: 'سی-سخت',
+    province_id: 23
+  },
+  {
+    id: 840,
+    name: 'قلعه رئیسی',
+    slug: 'قلعه-رئیسی',
+    province_id: 23
+  },
+  {
+    id: 841,
+    name: 'گراب سفلی',
+    slug: 'گراب-سفلی',
+    province_id: 23
+  },
+  {
+    id: 842,
+    name: 'لنده',
+    slug: 'لنده',
+    province_id: 23
+  },
+  {
+    id: 843,
+    name: 'لیکک',
+    slug: 'لیکک',
+    province_id: 23
+  },
+  {
+    id: 844,
+    name: 'مادوان',
+    slug: 'مادوان',
+    province_id: 23
+  },
+  {
+    id: 845,
+    name: 'مارگون',
+    slug: 'مارگون',
+    province_id: 23
+  },
+  {
+    id: 846,
+    name: 'یاسوج',
+    slug: 'یاسوج',
+    province_id: 23
+  },
+  {
+    id: 847,
+    name: 'انبارآلوم',
+    slug: 'انبارآلوم',
+    province_id: 24
+  },
+  {
+    id: 848,
+    name: 'اینچه برون',
+    slug: 'اینچه-برون',
+    province_id: 24
+  },
+  {
+    id: 849,
+    name: 'آزادشهر',
+    slug: 'آزادشهر',
+    province_id: 24
+  },
+  {
+    id: 850,
+    name: 'آق قلا',
+    slug: 'آق-قلا',
+    province_id: 24
+  },
+  {
+    id: 851,
+    name: 'بندرترکمن',
+    slug: 'بندرترکمن',
+    province_id: 24
+  },
+  {
+    id: 852,
+    name: 'بندرگز',
+    slug: 'بندرگز',
+    province_id: 24
+  },
+  {
+    id: 853,
+    name: 'جلین',
+    slug: 'جلین',
+    province_id: 24
+  },
+  {
+    id: 854,
+    name: 'خان ببین',
+    slug: 'خان-ببین',
+    province_id: 24
+  },
+  {
+    id: 855,
+    name: 'دلند',
+    slug: 'دلند',
+    province_id: 24
+  },
+  {
+    id: 856,
+    name: 'رامیان',
+    slug: 'رامیان',
+    province_id: 24
+  },
+  {
+    id: 857,
+    name: 'سرخنکلاته',
+    slug: 'سرخنکلاته',
+    province_id: 24
+  },
+  {
+    id: 858,
+    name: 'سیمین شهر',
+    slug: 'سیمین-شهر',
+    province_id: 24
+  },
+  {
+    id: 859,
+    name: 'علی آباد کتول',
+    slug: 'علی-آباد-کتول',
+    province_id: 24
+  },
+  {
+    id: 860,
+    name: 'فاضل آباد',
+    slug: 'فاضل-آباد',
+    province_id: 24
+  },
+  {
+    id: 861,
+    name: 'کردکوی',
+    slug: 'کردکوی',
+    province_id: 24
+  },
+  {
+    id: 862,
+    name: 'کلاله',
+    slug: 'کلاله',
+    province_id: 24
+  },
+  {
+    id: 863,
+    name: 'گالیکش',
+    slug: 'گالیکش',
+    province_id: 24
+  },
+  {
+    id: 864,
+    name: 'گرگان',
+    slug: 'گرگان',
+    province_id: 24
+  },
+  {
+    id: 865,
+    name: 'گمیش تپه',
+    slug: 'گمیش-تپه',
+    province_id: 24
+  },
+  {
+    id: 866,
+    name: 'گنبدکاووس',
+    slug: 'گنبدکاووس',
+    province_id: 24
+  },
+  {
+    id: 867,
+    name: 'مراوه',
+    slug: 'مراوه',
+    province_id: 24
+  },
+  {
+    id: 868,
+    name: 'مینودشت',
+    slug: 'مینودشت',
+    province_id: 24
+  },
+  {
+    id: 869,
+    name: 'نگین شهر',
+    slug: 'نگین-شهر',
+    province_id: 24
+  },
+  {
+    id: 870,
+    name: 'نوده خاندوز',
+    slug: 'نوده-خاندوز',
+    province_id: 24
+  },
+  {
+    id: 871,
+    name: 'نوکنده',
+    slug: 'نوکنده',
+    province_id: 24
+  },
+  {
+    id: 872,
+    name: 'ازنا',
+    slug: 'ازنا',
+    province_id: 25
+  },
+  {
+    id: 873,
+    name: 'اشترینان',
+    slug: 'اشترینان',
+    province_id: 25
+  },
+  {
+    id: 874,
+    name: 'الشتر',
+    slug: 'الشتر',
+    province_id: 25
+  },
+  {
+    id: 875,
+    name: 'الیگودرز',
+    slug: 'الیگودرز',
+    province_id: 25
+  },
+  {
+    id: 876,
+    name: 'بروجرد',
+    slug: 'بروجرد',
+    province_id: 25
+  },
+  {
+    id: 877,
+    name: 'پلدختر',
+    slug: 'پلدختر',
+    province_id: 25
+  },
+  {
+    id: 878,
+    name: 'چالانچولان',
+    slug: 'چالانچولان',
+    province_id: 25
+  },
+  {
+    id: 879,
+    name: 'چغلوندی',
+    slug: 'چغلوندی',
+    province_id: 25
+  },
+  {
+    id: 880,
+    name: 'چقابل',
+    slug: 'چقابل',
+    province_id: 25
+  },
+  {
+    id: 881,
+    name: 'خرم آباد',
+    slug: 'لرستان-خرم-آباد',
+    province_id: 25
+  },
+  {
+    id: 882,
+    name: 'درب گنبد',
+    slug: 'درب-گنبد',
+    province_id: 25
+  },
+  {
+    id: 883,
+    name: 'دورود',
+    slug: 'دورود',
+    province_id: 25
+  },
+  {
+    id: 884,
+    name: 'زاغه',
+    slug: 'زاغه',
+    province_id: 25
+  },
+  {
+    id: 885,
+    name: 'سپیددشت',
+    slug: 'سپیددشت',
+    province_id: 25
+  },
+  {
+    id: 886,
+    name: 'سراب دوره',
+    slug: 'سراب-دوره',
+    province_id: 25
+  },
+  {
+    id: 887,
+    name: 'فیروزآباد',
+    slug: 'لرستان-فیروزآباد',
+    province_id: 25
+  },
+  {
+    id: 888,
+    name: 'کونانی',
+    slug: 'کونانی',
+    province_id: 25
+  },
+  {
+    id: 889,
+    name: 'کوهدشت',
+    slug: 'کوهدشت',
+    province_id: 25
+  },
+  {
+    id: 890,
+    name: 'گراب',
+    slug: 'گراب',
+    province_id: 25
+  },
+  {
+    id: 891,
+    name: 'معمولان',
+    slug: 'معمولان',
+    province_id: 25
+  },
+  {
+    id: 892,
+    name: 'مومن آباد',
+    slug: 'مومن-آباد',
+    province_id: 25
+  },
+  {
+    id: 893,
+    name: 'نورآباد',
+    slug: 'لرستان-نورآباد',
+    province_id: 25
+  },
+  {
+    id: 894,
+    name: 'ویسیان',
+    slug: 'ویسیان',
+    province_id: 25
+  },
+  {
+    id: 895,
+    name: 'احمدسرگوراب',
+    slug: 'احمدسرگوراب',
+    province_id: 26
+  },
+  {
+    id: 896,
+    name: 'اسالم',
+    slug: 'اسالم',
+    province_id: 26
+  },
+  {
+    id: 897,
+    name: 'اطاقور',
+    slug: 'اطاقور',
+    province_id: 26
+  },
+  {
+    id: 898,
+    name: 'املش',
+    slug: 'املش',
+    province_id: 26
+  },
+  {
+    id: 899,
+    name: 'آستارا',
+    slug: 'آستارا',
+    province_id: 26
+  },
+  {
+    id: 900,
+    name: 'آستانه اشرفیه',
+    slug: 'آستانه-اشرفیه',
+    province_id: 26
+  },
+  {
+    id: 901,
+    name: 'بازار جمعه',
+    slug: 'بازار-جمعه',
+    province_id: 26
+  },
+  {
+    id: 902,
+    name: 'بره سر',
+    slug: 'بره-سر',
+    province_id: 26
+  },
+  {
+    id: 903,
+    name: 'بندرانزلی',
+    slug: 'بندرانزلی',
+    province_id: 26
+  },
+  {
+    id: 906,
+    name: 'پره سر',
+    slug: 'پره-سر',
+    province_id: 26
+  },
+  {
+    id: 907,
+    name: 'تالش',
+    slug: 'تالش',
+    province_id: 26
+  },
+  {
+    id: 908,
+    name: 'توتکابن',
+    slug: 'توتکابن',
+    province_id: 26
+  },
+  {
+    id: 909,
+    name: 'جیرنده',
+    slug: 'جیرنده',
+    province_id: 26
+  },
+  {
+    id: 910,
+    name: 'چابکسر',
+    slug: 'چابکسر',
+    province_id: 26
+  },
+  {
+    id: 911,
+    name: 'چاف و چمخاله',
+    slug: 'چاف-و-چمخاله',
+    province_id: 26
+  },
+  {
+    id: 912,
+    name: 'چوبر',
+    slug: 'چوبر',
+    province_id: 26
+  },
+  {
+    id: 913,
+    name: 'حویق',
+    slug: 'حویق',
+    province_id: 26
+  },
+  {
+    id: 914,
+    name: 'خشکبیجار',
+    slug: 'خشکبیجار',
+    province_id: 26
+  },
+  {
+    id: 915,
+    name: 'خمام',
+    slug: 'خمام',
+    province_id: 26
+  },
+  {
+    id: 916,
+    name: 'دیلمان',
+    slug: 'دیلمان',
+    province_id: 26
+  },
+  {
+    id: 917,
+    name: 'رانکوه',
+    slug: 'رانکوه',
+    province_id: 26
+  },
+  {
+    id: 918,
+    name: 'رحیم آباد',
+    slug: 'رحیم-آباد',
+    province_id: 26
+  },
+  {
+    id: 919,
+    name: 'رستم آباد',
+    slug: 'رستم-آباد',
+    province_id: 26
+  },
+  {
+    id: 920,
+    name: 'رشت',
+    slug: 'رشت',
+    province_id: 26
+  },
+  {
+    id: 921,
+    name: 'رضوانشهر',
+    slug: 'گیلان-رضوانشهر',
+    province_id: 26
+  },
+  {
+    id: 922,
+    name: 'رودبار',
+    slug: 'گیلان-رودبار',
+    province_id: 26
+  },
+  {
+    id: 923,
+    name: 'رودبنه',
+    slug: 'رودبنه',
+    province_id: 26
+  },
+  {
+    id: 924,
+    name: 'رودسر',
+    slug: 'رودسر',
+    province_id: 26
+  },
+  {
+    id: 925,
+    name: 'سنگر',
+    slug: 'سنگر',
+    province_id: 26
+  },
+  {
+    id: 926,
+    name: 'سیاهکل',
+    slug: 'سیاهکل',
+    province_id: 26
+  },
+  {
+    id: 927,
+    name: 'شفت',
+    slug: 'شفت',
+    province_id: 26
+  },
+  {
+    id: 928,
+    name: 'شلمان',
+    slug: 'شلمان',
+    province_id: 26
+  },
+  {
+    id: 929,
+    name: 'صومعه سرا',
+    slug: 'صومعه-سرا',
+    province_id: 26
+  },
+  {
+    id: 930,
+    name: 'فومن',
+    slug: 'فومن',
+    province_id: 26
+  },
+  {
+    id: 931,
+    name: 'کلاچای',
+    slug: 'کلاچای',
+    province_id: 26
+  },
+  {
+    id: 932,
+    name: 'کوچصفهان',
+    slug: 'کوچصفهان',
+    province_id: 26
+  },
+  {
+    id: 933,
+    name: 'کومله',
+    slug: 'کومله',
+    province_id: 26
+  },
+  {
+    id: 934,
+    name: 'کیاشهر',
+    slug: 'کیاشهر',
+    province_id: 26
+  },
+  {
+    id: 935,
+    name: 'گوراب زرمیخ',
+    slug: 'گوراب-زرمیخ',
+    province_id: 26
+  },
+  {
+    id: 936,
+    name: 'لاهیجان',
+    slug: 'لاهیجان',
+    province_id: 26
+  },
+  {
+    id: 937,
+    name: 'لشت نشا',
+    slug: 'لشت-نشا',
+    province_id: 26
+  },
+  {
+    id: 938,
+    name: 'لنگرود',
+    slug: 'لنگرود',
+    province_id: 26
+  },
+  {
+    id: 939,
+    name: 'لوشان',
+    slug: 'لوشان',
+    province_id: 26
+  },
+  {
+    id: 940,
+    name: 'لولمان',
+    slug: 'لولمان',
+    province_id: 26
+  },
+  {
+    id: 941,
+    name: 'لوندویل',
+    slug: 'لوندویل',
+    province_id: 26
+  },
+  {
+    id: 942,
+    name: 'لیسار',
+    slug: 'لیسار',
+    province_id: 26
+  },
+  {
+    id: 943,
+    name: 'ماسال',
+    slug: 'ماسال',
+    province_id: 26
+  },
+  {
+    id: 944,
+    name: 'ماسوله',
+    slug: 'ماسوله',
+    province_id: 26
+  },
+  {
+    id: 945,
+    name: 'مرجقل',
+    slug: 'مرجقل',
+    province_id: 26
+  },
+  {
+    id: 946,
+    name: 'منجیل',
+    slug: 'منجیل',
+    province_id: 26
+  },
+  {
+    id: 947,
+    name: 'واجارگاه',
+    slug: 'واجارگاه',
+    province_id: 26
+  },
+  {
+    id: 948,
+    name: 'امیرکلا',
+    slug: 'امیرکلا',
+    province_id: 27
+  },
+  {
+    id: 949,
+    name: 'ایزدشهر',
+    slug: 'ایزدشهر',
+    province_id: 27
+  },
+  {
+    id: 950,
+    name: 'آلاشت',
+    slug: 'آلاشت',
+    province_id: 27
+  },
+  {
+    id: 951,
+    name: 'آمل',
+    slug: 'آمل',
+    province_id: 27
+  },
+  {
+    id: 952,
+    name: 'بابل',
+    slug: 'بابل',
+    province_id: 27
+  },
+  {
+    id: 953,
+    name: 'بابلسر',
+    slug: 'بابلسر',
+    province_id: 27
+  },
+  {
+    id: 954,
+    name: 'بلده',
+    slug: 'مازندران-بلده',
+    province_id: 27
+  },
+  {
+    id: 955,
+    name: 'بهشهر',
+    slug: 'بهشهر',
+    province_id: 27
+  },
+  {
+    id: 956,
+    name: 'بهنمیر',
+    slug: 'بهنمیر',
+    province_id: 27
+  },
+  {
+    id: 957,
+    name: 'پل سفید',
+    slug: 'پل-سفید',
+    province_id: 27
+  },
+  {
+    id: 958,
+    name: 'تنکابن',
+    slug: 'تنکابن',
+    province_id: 27
+  },
+  {
+    id: 959,
+    name: 'جویبار',
+    slug: 'جویبار',
+    province_id: 27
+  },
+  {
+    id: 960,
+    name: 'چالوس',
+    slug: 'چالوس',
+    province_id: 27
+  },
+  {
+    id: 961,
+    name: 'چمستان',
+    slug: 'چمستان',
+    province_id: 27
+  },
+  {
+    id: 962,
+    name: 'خرم آباد',
+    slug: 'مازندران-خرم-آباد',
+    province_id: 27
+  },
+  {
+    id: 963,
+    name: 'خلیل شهر',
+    slug: 'خلیل-شهر',
+    province_id: 27
+  },
+  {
+    id: 964,
+    name: 'خوش رودپی',
+    slug: 'خوش-رودپی',
+    province_id: 27
+  },
+  {
+    id: 965,
+    name: 'دابودشت',
+    slug: 'دابودشت',
+    province_id: 27
+  },
+  {
+    id: 966,
+    name: 'رامسر',
+    slug: 'رامسر',
+    province_id: 27
+  },
+  {
+    id: 967,
+    name: 'رستمکلا',
+    slug: 'رستمکلا',
+    province_id: 27
+  },
+  {
+    id: 968,
+    name: 'رویان',
+    slug: 'رویان',
+    province_id: 27
+  },
+  {
+    id: 969,
+    name: 'رینه',
+    slug: 'رینه',
+    province_id: 27
+  },
+  {
+    id: 970,
+    name: 'زرگرمحله',
+    slug: 'زرگرمحله',
+    province_id: 27
+  },
+  {
+    id: 971,
+    name: 'زیرآب',
+    slug: 'زیرآب',
+    province_id: 27
+  },
+  {
+    id: 972,
+    name: 'ساری',
+    slug: 'ساری',
+    province_id: 27
+  },
+  {
+    id: 973,
+    name: 'سرخرود',
+    slug: 'سرخرود',
+    province_id: 27
+  },
+  {
+    id: 974,
+    name: 'سلمان شهر',
+    slug: 'سلمان-شهر',
+    province_id: 27
+  },
+  {
+    id: 975,
+    name: 'سورک',
+    slug: 'سورک',
+    province_id: 27
+  },
+  {
+    id: 976,
+    name: 'شیرگاه',
+    slug: 'شیرگاه',
+    province_id: 27
+  },
+  {
+    id: 977,
+    name: 'شیرود',
+    slug: 'شیرود',
+    province_id: 27
+  },
+  {
+    id: 978,
+    name: 'عباس آباد',
+    slug: 'عباس-آباد',
+    province_id: 27
+  },
+  {
+    id: 979,
+    name: 'فریدونکنار',
+    slug: 'فریدونکنار',
+    province_id: 27
+  },
+  {
+    id: 980,
+    name: 'فریم',
+    slug: 'فریم',
+    province_id: 27
+  },
+  {
+    id: 981,
+    name: 'قائم شهر',
+    slug: 'قائم-شهر',
+    province_id: 27
+  },
+  {
+    id: 982,
+    name: 'کتالم',
+    slug: 'کتالم',
+    province_id: 27
+  },
+  {
+    id: 983,
+    name: 'کلارآباد',
+    slug: 'کلارآباد',
+    province_id: 27
+  },
+  {
+    id: 984,
+    name: 'کلاردشت',
+    slug: 'کلاردشت',
+    province_id: 27
+  },
+  {
+    id: 985,
+    name: 'کله بست',
+    slug: 'کله-بست',
+    province_id: 27
+  },
+  {
+    id: 986,
+    name: 'کوهی خیل',
+    slug: 'کوهی-خیل',
+    province_id: 27
+  },
+  {
+    id: 987,
+    name: 'کیاسر',
+    slug: 'کیاسر',
+    province_id: 27
+  },
+  {
+    id: 988,
+    name: 'کیاکلا',
+    slug: 'کیاکلا',
+    province_id: 27
+  },
+  {
+    id: 989,
+    name: 'گتاب',
+    slug: 'گتاب',
+    province_id: 27
+  },
+  {
+    id: 990,
+    name: 'گزنک',
+    slug: 'گزنک',
+    province_id: 27
+  },
+  {
+    id: 991,
+    name: 'گلوگاه',
+    slug: 'گلوگاه',
+    province_id: 27
+  },
+  {
+    id: 992,
+    name: 'محمودآباد',
+    slug: 'مازندران-محمودآباد',
+    province_id: 27
+  },
+  {
+    id: 993,
+    name: 'مرزن آباد',
+    slug: 'مرزن-آباد',
+    province_id: 27
+  },
+  {
+    id: 994,
+    name: 'مرزیکلا',
+    slug: 'مرزیکلا',
+    province_id: 27
+  },
+  {
+    id: 995,
+    name: 'نشتارود',
+    slug: 'نشتارود',
+    province_id: 27
+  },
+  {
+    id: 996,
+    name: 'نکا',
+    slug: 'نکا',
+    province_id: 27
+  },
+  {
+    id: 997,
+    name: 'نور',
+    slug: 'نور',
+    province_id: 27
+  },
+  {
+    id: 998,
+    name: 'نوشهر',
+    slug: 'نوشهر',
+    province_id: 27
+  },
+  {
+    id: 999,
+    name: 'اراک',
+    slug: 'اراک',
+    province_id: 28
+  },
+  {
+    id: 1000,
+    name: 'آستانه',
+    slug: 'آستانه',
+    province_id: 28
+  },
+  {
+    id: 1001,
+    name: 'آشتیان',
+    slug: 'آشتیان',
+    province_id: 28
+  },
+  {
+    id: 1002,
+    name: 'پرندک',
+    slug: 'پرندک',
+    province_id: 28
+  },
+  {
+    id: 1003,
+    name: 'تفرش',
+    slug: 'تفرش',
+    province_id: 28
+  },
+  {
+    id: 1004,
+    name: 'توره',
+    slug: 'توره',
+    province_id: 28
+  },
+  {
+    id: 1005,
+    name: 'جاورسیان',
+    slug: 'جاورسیان',
+    province_id: 28
+  },
+  {
+    id: 1006,
+    name: 'خشکرود',
+    slug: 'خشکرود',
+    province_id: 28
+  },
+  {
+    id: 1007,
+    name: 'خمین',
+    slug: 'خمین',
+    province_id: 28
+  },
+  {
+    id: 1008,
+    name: 'خنداب',
+    slug: 'خنداب',
+    province_id: 28
+  },
+  {
+    id: 1009,
+    name: 'داودآباد',
+    slug: 'داودآباد',
+    province_id: 28
+  },
+  {
+    id: 1010,
+    name: 'دلیجان',
+    slug: 'دلیجان',
+    province_id: 28
+  },
+  {
+    id: 1011,
+    name: 'رازقان',
+    slug: 'رازقان',
+    province_id: 28
+  },
+  {
+    id: 1012,
+    name: 'زاویه',
+    slug: 'زاویه',
+    province_id: 28
+  },
+  {
+    id: 1013,
+    name: 'ساروق',
+    slug: 'ساروق',
+    province_id: 28
+  },
+  {
+    id: 1014,
+    name: 'ساوه',
+    slug: 'ساوه',
+    province_id: 28
+  },
+  {
+    id: 1015,
+    name: 'سنجان',
+    slug: 'سنجان',
+    province_id: 28
+  },
+  {
+    id: 1016,
+    name: 'شازند',
+    slug: 'شازند',
+    province_id: 28
+  },
+  {
+    id: 1017,
+    name: 'غرق آباد',
+    slug: 'غرق-آباد',
+    province_id: 28
+  },
+  {
+    id: 1018,
+    name: 'فرمهین',
+    slug: 'فرمهین',
+    province_id: 28
+  },
+  {
+    id: 1019,
+    name: 'قورچی باشی',
+    slug: 'قورچی-باشی',
+    province_id: 28
+  },
+  {
+    id: 1020,
+    name: 'کرهرود',
+    slug: 'کرهرود',
+    province_id: 28
+  },
+  {
+    id: 1021,
+    name: 'کمیجان',
+    slug: 'کمیجان',
+    province_id: 28
+  },
+  {
+    id: 1022,
+    name: 'مامونیه',
+    slug: 'مامونیه',
+    province_id: 28
+  },
+  {
+    id: 1023,
+    name: 'محلات',
+    slug: 'محلات',
+    province_id: 28
+  },
+  {
+    id: 1024,
+    name: 'مهاجران',
+    slug: 'مهاجران',
+    province_id: 28
+  },
+  {
+    id: 1025,
+    name: 'میلاجرد',
+    slug: 'میلاجرد',
+    province_id: 28
+  },
+  {
+    id: 1026,
+    name: 'نراق',
+    slug: 'نراق',
+    province_id: 28
+  },
+  {
+    id: 1027,
+    name: 'نوبران',
+    slug: 'نوبران',
+    province_id: 28
+  },
+  {
+    id: 1028,
+    name: 'نیمور',
+    slug: 'نیمور',
+    province_id: 28
+  },
+  {
+    id: 1029,
+    name: 'هندودر',
+    slug: 'هندودر',
+    province_id: 28
+  },
+  {
+    id: 1030,
+    name: 'ابوموسی',
+    slug: 'ابوموسی',
+    province_id: 29
+  },
+  {
+    id: 1031,
+    name: 'بستک',
+    slug: 'بستک',
+    province_id: 29
+  },
+  {
+    id: 1032,
+    name: 'بندرجاسک',
+    slug: 'بندرجاسک',
+    province_id: 29
+  },
+  {
+    id: 1033,
+    name: 'بندرچارک',
+    slug: 'بندرچارک',
+    province_id: 29
+  },
+  {
+    id: 1034,
+    name: 'بندرخمیر',
+    slug: 'بندرخمیر',
+    province_id: 29
+  },
+  {
+    id: 1035,
+    name: 'بندرعباس',
+    slug: 'بندرعباس',
+    province_id: 29
+  },
+  {
+    id: 1036,
+    name: 'بندرلنگه',
+    slug: 'بندرلنگه',
+    province_id: 29
+  },
+  {
+    id: 1037,
+    name: 'بیکا',
+    slug: 'بیکا',
+    province_id: 29
+  },
+  {
+    id: 1038,
+    name: 'پارسیان',
+    slug: 'پارسیان',
+    province_id: 29
+  },
+  {
+    id: 1039,
+    name: 'تخت',
+    slug: 'تخت',
+    province_id: 29
+  },
+  {
+    id: 1040,
+    name: 'جناح',
+    slug: 'جناح',
+    province_id: 29
+  },
+  {
+    id: 1041,
+    name: 'حاجی آباد',
+    slug: 'هرمزگان-حاجی-آباد',
+    province_id: 29
+  },
+  {
+    id: 1042,
+    name: 'درگهان',
+    slug: 'درگهان',
+    province_id: 29
+  },
+  {
+    id: 1043,
+    name: 'دهبارز',
+    slug: 'دهبارز',
+    province_id: 29
+  },
+  {
+    id: 1044,
+    name: 'رویدر',
+    slug: 'رویدر',
+    province_id: 29
+  },
+  {
+    id: 1045,
+    name: 'زیارتعلی',
+    slug: 'زیارتعلی',
+    province_id: 29
+  },
+  {
+    id: 1046,
+    name: 'سردشت',
+    slug: 'هرمزگان-سردشت',
+    province_id: 29
+  },
+  {
+    id: 1047,
+    name: 'سندرک',
+    slug: 'سندرک',
+    province_id: 29
+  },
+  {
+    id: 1048,
+    name: 'سوزا',
+    slug: 'سوزا',
+    province_id: 29
+  },
+  {
+    id: 1049,
+    name: 'سیریک',
+    slug: 'سیریک',
+    province_id: 29
+  },
+  {
+    id: 1050,
+    name: 'فارغان',
+    slug: 'فارغان',
+    province_id: 29
+  },
+  {
+    id: 1051,
+    name: 'فین',
+    slug: 'فین',
+    province_id: 29
+  },
+  {
+    id: 1052,
+    name: 'قشم',
+    slug: 'قشم',
+    province_id: 29
+  },
+  {
+    id: 1053,
+    name: 'قلعه قاضی',
+    slug: 'قلعه-قاضی',
+    province_id: 29
+  },
+  {
+    id: 1054,
+    name: 'کنگ',
+    slug: 'کنگ',
+    province_id: 29
+  },
+  {
+    id: 1055,
+    name: 'کوشکنار',
+    slug: 'کوشکنار',
+    province_id: 29
+  },
+  {
+    id: 1056,
+    name: 'کیش',
+    slug: 'کیش',
+    province_id: 29
+  },
+  {
+    id: 1057,
+    name: 'گوهران',
+    slug: 'گوهران',
+    province_id: 29
+  },
+  {
+    id: 1058,
+    name: 'میناب',
+    slug: 'میناب',
+    province_id: 29
+  },
+  {
+    id: 1059,
+    name: 'هرمز',
+    slug: 'هرمز',
+    province_id: 29
+  },
+  {
+    id: 1060,
+    name: 'هشتبندی',
+    slug: 'هشتبندی',
+    province_id: 29
+  },
+  {
+    id: 1061,
+    name: 'ازندریان',
+    slug: 'ازندریان',
+    province_id: 30
+  },
+  {
+    id: 1062,
+    name: 'اسدآباد',
+    slug: 'اسدآباد',
+    province_id: 30
+  },
+  {
+    id: 1063,
+    name: 'برزول',
+    slug: 'برزول',
+    province_id: 30
+  },
+  {
+    id: 1064,
+    name: 'بهار',
+    slug: 'بهار',
+    province_id: 30
+  },
+  {
+    id: 1065,
+    name: 'تویسرکان',
+    slug: 'تویسرکان',
+    province_id: 30
+  },
+  {
+    id: 1066,
+    name: 'جورقان',
+    slug: 'جورقان',
+    province_id: 30
+  },
+  {
+    id: 1067,
+    name: 'جوکار',
+    slug: 'جوکار',
+    province_id: 30
+  },
+  {
+    id: 1068,
+    name: 'دمق',
+    slug: 'دمق',
+    province_id: 30
+  },
+  {
+    id: 1069,
+    name: 'رزن',
+    slug: 'رزن',
+    province_id: 30
+  },
+  {
+    id: 1070,
+    name: 'زنگنه',
+    slug: 'زنگنه',
+    province_id: 30
+  },
+  {
+    id: 1071,
+    name: 'سامن',
+    slug: 'سامن',
+    province_id: 30
+  },
+  {
+    id: 1072,
+    name: 'سرکان',
+    slug: 'سرکان',
+    province_id: 30
+  },
+  {
+    id: 1073,
+    name: 'شیرین سو',
+    slug: 'شیرین-سو',
+    province_id: 30
+  },
+  {
+    id: 1074,
+    name: 'صالح آباد',
+    slug: 'همدان-صالح-آباد',
+    province_id: 30
+  },
+  {
+    id: 1075,
+    name: 'فامنین',
+    slug: 'فامنین',
+    province_id: 30
+  },
+  {
+    id: 1076,
+    name: 'فرسفج',
+    slug: 'فرسفج',
+    province_id: 30
+  },
+  {
+    id: 1077,
+    name: 'فیروزان',
+    slug: 'فیروزان',
+    province_id: 30
+  },
+  {
+    id: 1078,
+    name: 'قروه درجزین',
+    slug: 'قروه-درجزین',
+    province_id: 30
+  },
+  {
+    id: 1079,
+    name: 'قهاوند',
+    slug: 'قهاوند',
+    province_id: 30
+  },
+  {
+    id: 1080,
+    name: 'کبودر آهنگ',
+    slug: 'کبودر-آهنگ',
+    province_id: 30
+  },
+  {
+    id: 1081,
+    name: 'گل تپه',
+    slug: 'گل-تپه',
+    province_id: 30
+  },
+  {
+    id: 1082,
+    name: 'گیان',
+    slug: 'گیان',
+    province_id: 30
+  },
+  {
+    id: 1083,
+    name: 'لالجین',
+    slug: 'لالجین',
+    province_id: 30
+  },
+  {
+    id: 1084,
+    name: 'مریانج',
+    slug: 'مریانج',
+    province_id: 30
+  },
+  {
+    id: 1085,
+    name: 'ملایر',
+    slug: 'ملایر',
+    province_id: 30
+  },
+  {
+    id: 1086,
+    name: 'نهاوند',
+    slug: 'نهاوند',
+    province_id: 30
+  },
+  {
+    id: 1087,
+    name: 'همدان',
+    slug: 'همدان',
+    province_id: 30
+  },
+  {
+    id: 1088,
+    name: 'ابرکوه',
+    slug: 'ابرکوه',
+    province_id: 31
+  },
+  {
+    id: 1089,
+    name: 'احمدآباد',
+    slug: 'احمدآباد',
+    province_id: 31
+  },
+  {
+    id: 1090,
+    name: 'اردکان',
+    slug: 'یزد-اردکان',
+    province_id: 31
+  },
+  {
+    id: 1091,
+    name: 'اشکذر',
+    slug: 'اشکذر',
+    province_id: 31
+  },
+  {
+    id: 1092,
+    name: 'بافق',
+    slug: 'بافق',
+    province_id: 31
+  },
+  {
+    id: 1093,
+    name: 'بفروئیه',
+    slug: 'بفروئیه',
+    province_id: 31
+  },
+  {
+    id: 1094,
+    name: 'بهاباد',
+    slug: 'بهاباد',
+    province_id: 31
+  },
+  {
+    id: 1095,
+    name: 'تفت',
+    slug: 'تفت',
+    province_id: 31
+  },
+  {
+    id: 1096,
+    name: 'حمیدیا',
+    slug: 'حمیدیا',
+    province_id: 31
+  },
+  {
+    id: 1097,
+    name: 'خضرآباد',
+    slug: 'خضرآباد',
+    province_id: 31
+  },
+  {
+    id: 1098,
+    name: 'دیهوک',
+    slug: 'دیهوک',
+    province_id: 31
+  },
+  {
+    id: 1099,
+    name: 'زارچ',
+    slug: 'زارچ',
+    province_id: 31
+  },
+  {
+    id: 1100,
+    name: 'شاهدیه',
+    slug: 'شاهدیه',
+    province_id: 31
+  },
+  {
+    id: 1101,
+    name: 'طبس',
+    slug: 'یزد-طبس',
+    province_id: 31
+  },
+  {
+    id: 1103,
+    name: 'عقدا',
+    slug: 'عقدا',
+    province_id: 31
+  },
+  {
+    id: 1104,
+    name: 'مروست',
+    slug: 'مروست',
+    province_id: 31
+  },
+  {
+    id: 1105,
+    name: 'مهردشت',
+    slug: 'مهردشت',
+    province_id: 31
+  },
+  {
+    id: 1106,
+    name: 'مهریز',
+    slug: 'مهریز',
+    province_id: 31
+  },
+  {
+    id: 1107,
+    name: 'میبد',
+    slug: 'میبد',
+    province_id: 31
+  },
+  {
+    id: 1108,
+    name: 'ندوشن',
+    slug: 'ندوشن',
+    province_id: 31
+  },
+  {
+    id: 1109,
+    name: 'نیر',
+    slug: 'یزد-نیر',
+    province_id: 31
+  },
+  {
+    id: 1110,
+    name: 'هرات',
+    slug: 'هرات',
+    province_id: 31
+  },
+  {
+    id: 1111,
+    name: 'یزد',
+    slug: 'یزد',
+    province_id: 31
+  },
+  {
+    id: 1116,
+    name: 'پرند',
+    slug: 'پرند',
+    province_id: 8
+  },
+  {
+    id: 1117,
+    name: 'فردیس',
+    slug: 'فردیس',
+    province_id: 5
+  },
+  {
+    id: 1118,
+    name: 'مارلیک',
+    slug: 'مارلیک',
+    province_id: 5
+  },
+  {
+    id: 1119,
+    name: 'سادات شهر',
+    slug: 'سادات-شهر',
+    province_id: 27
+  },
+  {
+    id: 1121,
+    name: 'زیباکنار',
+    slug: 'زیباکنار',
+    province_id: 26
+  },
+  {
+    id: 1135,
+    name: 'کردان',
+    slug: 'کردان',
+    province_id: 5
+  },
+  {
+    id: 1137,
+    name: 'ساوجبلاغ',
+    slug: 'ساوجبلاغ',
+    province_id: 5
+  },
+  {
+    id: 1138,
+    name: 'تهران دشت',
+    slug: 'تهران-دشت',
+    province_id: 5
+  },
+  {
+    id: 1150,
+    name: 'گلبهار',
+    slug: 'گلبهار',
+    province_id: 11
+  },
+  {
+    id: 1153,
+    name: 'قیامدشت',
+    slug: 'قیامدشت',
+    province_id: 8
+  },
+  {
+    id: 1155,
+    name: 'بینالود',
+    slug: 'بینالود',
+    province_id: 11
+  },
+  {
+    id: 1159,
+    name: 'پیربازار',
+    slug: 'پیربازار',
+    province_id: 26
+  },
+  {
+    id: 1160,
+    name: 'رضوانشهر',
+    slug: 'رضوانشهر',
+    province_id: 31
+  }
+]
+
+export const Provinces = [
+  {
+    id: 1,
+    name: 'آذربایجان شرقی',
+    slug: 'آذربایجان-شرقی',
+    tel_prefix: '041'
+  },
+  {
+    id: 2,
+    name: 'آذربایجان غربی',
+    slug: 'آذربایجان-غربی',
+    tel_prefix: '044'
+  },
+  {
+    id: 3,
+    name: 'اردبیل',
+    slug: 'اردبیل',
+    tel_prefix: '045'
+  },
+  {
+    id: 4,
+    name: 'اصفهان',
+    slug: 'اصفهان',
+    tel_prefix: '031'
+  },
+  {
+    id: 5,
+    name: 'البرز',
+    slug: 'البرز',
+    tel_prefix: '026'
+  },
+  {
+    id: 6,
+    name: 'ایلام',
+    slug: 'ایلام',
+    tel_prefix: '084'
+  },
+  {
+    id: 7,
+    name: 'بوشهر',
+    slug: 'بوشهر',
+    tel_prefix: '077'
+  },
+  {
+    id: 8,
+    name: 'تهران',
+    slug: 'تهران',
+    tel_prefix: '021'
+  },
+  {
+    id: 9,
+    name: 'چهارمحال و بختیاری',
+    slug: 'چهارمحال-بختیاری',
+    tel_prefix: '038'
+  },
+  {
+    id: 10,
+    name: 'خراسان جنوبی',
+    slug: 'خراسان-جنوبی',
+    tel_prefix: '056'
+  },
+  {
+    id: 11,
+    name: 'خراسان رضوی',
+    slug: 'خراسان-رضوی',
+    tel_prefix: '051'
+  },
+  {
+    id: 12,
+    name: 'خراسان شمالی',
+    slug: 'خراسان-شمالی',
+    tel_prefix: '058'
+  },
+  {
+    id: 13,
+    name: 'خوزستان',
+    slug: 'خوزستان',
+    tel_prefix: '061'
+  },
+  {
+    id: 14,
+    name: 'زنجان',
+    slug: 'زنجان',
+    tel_prefix: '024'
+  },
+  {
+    id: 15,
+    name: 'سمنان',
+    slug: 'سمنان',
+    tel_prefix: '023'
+  },
+  {
+    id: 16,
+    name: 'سیستان و بلوچستان',
+    slug: 'سیستان-بلوچستان',
+    tel_prefix: '054'
+  },
+  {
+    id: 17,
+    name: 'فارس',
+    slug: 'فارس',
+    tel_prefix: '071'
+  },
+  {
+    id: 18,
+    name: 'قزوین',
+    slug: 'قزوین',
+    tel_prefix: '028'
+  },
+  {
+    id: 19,
+    name: 'قم',
+    slug: 'قم',
+    tel_prefix: '025'
+  },
+  {
+    id: 20,
+    name: 'کردستان',
+    slug: 'کردستان',
+    tel_prefix: '087'
+  },
+  {
+    id: 21,
+    name: 'کرمان',
+    slug: 'کرمان',
+    tel_prefix: '034'
+  },
+  {
+    id: 22,
+    name: 'کرمانشاه',
+    slug: 'کرمانشاه',
+    tel_prefix: '083'
+  },
+  {
+    id: 23,
+    name: 'کهگیلویه و بویراحمد',
+    slug: 'کهگیلویه-بویراحمد',
+    tel_prefix: '074'
+  },
+  {
+    id: 24,
+    name: 'گلستان',
+    slug: 'گلستان',
+    tel_prefix: '017'
+  },
+  {
+    id: 25,
+    name: 'لرستان',
+    slug: 'لرستان',
+    tel_prefix: '066'
+  },
+  {
+    id: 26,
+    name: 'گیلان',
+    slug: 'گیلان',
+    tel_prefix: '013'
+  },
+  {
+    id: 27,
+    name: 'مازندران',
+    slug: 'مازندران',
+    tel_prefix: '011'
+  },
+  {
+    id: 28,
+    name: 'مرکزی',
+    slug: 'مرکزی',
+    tel_prefix: '086'
+  },
+  {
+    id: 29,
+    name: 'هرمزگان',
+    slug: 'هرمزگان',
+    tel_prefix: '076'
+  },
+  {
+    id: 30,
+    name: 'همدان',
+    slug: 'همدان',
+    tel_prefix: '081'
+  },
+  {
+    id: 31,
+    name: 'یزد',
+    slug: 'یزد',
+    tel_prefix: '035'
+  }
+]
+
+export function getCitiesOfProvince (provinceName) {
+  const targetProvince = Provinces.find(province => province.name === provinceName)
+  if (!targetProvince) {
+    return targetProvince
+  }
+  return Cities.filter(city => city.province_id === targetProvince.id)
+}

+ 5 - 1
src/components/FormBuilderCustumComponents/ContentCategorySelector.vue

@@ -88,9 +88,13 @@ export default {
     }
     }
   },
   },
   watch: {
   watch: {
-    value () {
+    value (newValue, oldValue) {
+      if (newValue && oldValue && newValue.toString() === oldValue.toString()) {
+        return
+      }
       if (this.value?.id) {
       if (this.value?.id) {
         this.getCategories()
         this.getCategories()
+        // this.$emit('update:value', this.value.id)
       } else if (this.value) {
       } else if (this.value) {
         this.inputData = this.value
         this.inputData = this.value
       }
       }

+ 15 - 14
src/components/FormBuilderCustumComponents/ContentMedias/ContentMedias.vue

@@ -171,21 +171,22 @@ export default {
         if (!source) {
         if (!source) {
           return
           return
         }
         }
+        window.open(source, '_blank')
 
 
-        const fileName = (source.match(/(?<=\/)[^/?#]+(?=[^/]*$)/gm) || [media.title])[0]
-        this.fetchLoading = true
-        fetch(source)
-          .then(response => response.blob())
-          .then(blob => {
-            const link = document.createElement('a')
-            link.href = URL.createObjectURL(blob)
-            link.download = fileName
-            link.click()
-            this.fetchLoading = false
-          })
-          .catch(() => {
-            this.fetchLoading = false
-          })
+        // const fileName = (source.match(/(?<=\/)[^/?#]+(?=[^/]*$)/gm) || [media.title])[0]
+        // this.fetchLoading = true
+        // fetch(source)
+        //   .then(response => response.blob())
+        //   .then(blob => {
+        //     const link = document.createElement('a')
+        //     link.href = URL.createObjectURL(blob)
+        //     link.download = fileName
+        //     link.click()
+        //     this.fetchLoading = false
+        //   })
+        //   .catch(() => {
+        //     this.fetchLoading = false
+        //   })
       }
       }
     },
     },
     onDelete (media) {
     onDelete (media) {

+ 36 - 9
src/components/FormBuilderCustumComponents/ProductSelector.vue

@@ -82,11 +82,18 @@ export default {
             field: row => (new Product(row)).is_physical_info.label
             field: row => (new Product(row)).is_physical_info.label
           },
           },
           {
           {
-            name: 'content_category',
+            name: 'store_category',
             required: true,
             required: true,
             label: 'دسته فروشگاه',
             label: 'دسته فروشگاه',
             align: 'left',
             align: 'left',
-            field: row => row.store_category_info?.parent?.parent?.title + '، ' + row.store_category_info?.parent?.title + '، ' + row.store_category_info?.title
+            field: row => (row.store_category_info?.parent?.parent?.title || '-') + '، ' + (row.store_category_info?.parent?.title || '-') + '، ' + (row.store_category_info?.title || '-')
+          },
+          {
+            name: 'store_category1',
+            required: true,
+            label: 'دسته محتوا',
+            align: 'left',
+            field: row => (row.content_category_info?.parent?.parent?.title || '-') + '، ' + (row.content_category_info?.parent?.title || '-') + '، ' + (row.content_category_info?.title || '-')
           },
           },
           {
           {
             name: 'unit_price',
             name: 'unit_price',
@@ -105,15 +112,31 @@ export default {
         ]
         ]
       },
       },
       itemIdentifyKey: 'id',
       itemIdentifyKey: 'id',
-      itemIndicatorKey: (product) => product ? product.title + '(' + product.id + ')' : '-'
+      itemIndicatorKey: (product) => {
+        return product ? product.title + '(' + product.id + ')' : '-'
+      }
     }
     }
   },
   },
   watch: {
   watch: {
     value: {
     value: {
-      handler (newValue) {
-        if (newValue) {
-          this.selected = newValue
+      handler (newValue, oldValue) {
+        if (!newValue || (newValue && oldValue && newValue.toString() === oldValue.toString())) {
+          return
         }
         }
+        if (this.selectionMode === 'multiple') {
+          if (isNaN(newValue[0])) {
+            this.selected = newValue
+          } else {
+            this.inputData = newValue
+          }
+        } else {
+          if (isNaN(newValue)) {
+            this.selected = newValue
+          } else {
+            this.inputData = newValue
+          }
+        }
+        this.updateFormValue()
       },
       },
       immediate: true
       immediate: true
     }
     }
@@ -132,11 +155,15 @@ export default {
         }
         }
       })
       })
       this.selected = selected
       this.selected = selected
-      this.inputData = selected
+      this.updateFormValue()
+    },
+    updateFormValue () {
       if (this.selectionMode === 'multiple') {
       if (this.selectionMode === 'multiple') {
-        this.$emit('update:value', selected.map(product => product.id))
+        this.inputData = this.selected.map(product => product.id)
+        this.$emit('update:value', this.selected.map(product => product.id))
       } else {
       } else {
-        this.$emit('update:value', selected.id)
+        this.inputData = this.selected.id
+        this.$emit('update:value', this.selected.id)
       }
       }
     }
     }
   }
   }

+ 1 - 1
src/components/InvoicePaymentCard/InvoicePaymentCard.vue

@@ -98,7 +98,7 @@
       <div>
       <div>
         <instalments :wallet="wallet"
         <instalments :wallet="wallet"
                      :invoice="invoice"
                      :invoice="invoice"
-                     :can-action="canAction"
+                     :can-action="true"
                      @onInstalmentPayed="onInstalmentPayed" />
                      @onInstalmentPayed="onInstalmentPayed" />
       </div>
       </div>
     </div>
     </div>

+ 2 - 2
src/components/Template/Header/MainHeader.vue

@@ -56,7 +56,7 @@
                     <q-item-section>کتب سطح یک</q-item-section>
                     <q-item-section>کتب سطح یک</q-item-section>
                   </q-item>
                   </q-item>
                   <q-item v-close-popup
                   <q-item v-close-popup
-                          href="https://formafzar.com/form/zmvhh"
+                          href="https://formafzar.com/form/c73vr"
                           target="_blank"
                           target="_blank"
                           clickable>
                           clickable>
                     <q-item-section>کتب سطح دو</q-item-section>
                     <q-item-section>کتب سطح دو</q-item-section>
@@ -86,7 +86,7 @@
                     <q-item-section>کتب جریان‌شناسی سطح یک</q-item-section>
                     <q-item-section>کتب جریان‌شناسی سطح یک</q-item-section>
                   </q-item>
                   </q-item>
                   <q-item v-close-popup
                   <q-item v-close-popup
-                          href="https://formafzar.com/form/xskmo"
+                          href="https://formafzar.com/form/w1m0i"
                           target="_blank"
                           target="_blank"
                           clickable>
                           clickable>
                     <q-item-section>کتب جریان‌شناسی سطح دو</q-item-section>
                     <q-item-section>کتب جریان‌شناسی سطح دو</q-item-section>

+ 14 - 0
src/components/Template/SideBar/AdminPanelSideBar.vue

@@ -252,6 +252,20 @@ export default {
               routeName: 'Admin.Store.Discount.List',
               routeName: 'Admin.Store.Discount.List',
               show: true,
               show: true,
               active: false
               active: false
+            },
+            {
+              title: 'هزینه ارسال',
+              roles: ['superuser'],
+              routeName: 'Admin.Store.Basket.DeliveryCost',
+              show: true,
+              active: false
+            },
+            {
+              title: 'آیین نامه خرید',
+              roles: ['superuser'],
+              routeName: 'Admin.Store.Basket.PurchaseRegulations',
+              show: true,
+              active: false
             }
             }
             // {
             // {
             //   title: 'تنظیمات',
             //   title: 'تنظیمات',

+ 2 - 2
src/components/Template/SideBar/Main.vue

@@ -85,7 +85,7 @@ export default {
             },
             },
             {
             {
               title: 'کتب سطح دو',
               title: 'کتب سطح دو',
-              externalLink: 'https://formafzar.com/form/zmvhh',
+              externalLink: 'https://formafzar.com/form/c73vr',
               show: true
               show: true
             },
             },
             {
             {
@@ -110,7 +110,7 @@ export default {
             },
             },
             {
             {
               title: 'کتب جریان‌شناسی سطح دو',
               title: 'کتب جریان‌شناسی سطح دو',
-              externalLink: 'https://formafzar.com/form/xskmo',
+              externalLink: 'https://formafzar.com/form/w1m0i',
               show: true
               show: true
             },
             },
             {
             {

+ 1 - 0
src/components/Widgets/Content/AdminContentCreate/AdminContentCreate.vue

@@ -53,6 +53,7 @@ export default {
       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' },
         { type: 'file', name: 'thumbnail', responseKey: 'thumbnail', label: 'عکس', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'file', name: 'thumbnail', responseKey: 'thumbnail', label: 'عکس', placeholder: ' ', col: 'col-md-6 col-12' },
+        { type: 'checkbox', name: 'is_hidden', responseKey: 'is_hidden', label: 'پنهان باشد؟', placeholder: ' ', value: true, col: 'col-12' },
         { type: ContentCategorySelectorComp, name: 'category', responseKey: 'category', col: 'col-12' },
         { type: ContentCategorySelectorComp, name: 'category', responseKey: 'category', col: 'col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', col: 'col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', col: 'col-12' },

+ 9 - 0
src/components/Widgets/Content/AdminContentList/AdminContentList.vue

@@ -48,6 +48,7 @@
 <script>
 <script>
 import { shallowRef } from 'vue'
 import { shallowRef } from 'vue'
 import { EntityIndex } from 'quasar-crud'
 import { EntityIndex } from 'quasar-crud'
+import { Content } from 'src/models/Content.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import BtnControl from 'src/components/Control/btn.vue'
 import BtnControl from 'src/components/Control/btn.vue'
@@ -75,6 +76,7 @@ export default {
       inputs: [
       inputs: [
         { type: ContentCategorySelectorComp, name: 'category', responseKey: 'category', col: 'col-12' },
         { type: ContentCategorySelectorComp, name: 'category', responseKey: 'category', col: 'col-12' },
         { type: 'input', name: 'search', value: null, col: 'col-md-3 col-12', label: 'جست و جو', placeholder: ' ' },
         { type: 'input', name: 'search', value: null, col: 'col-md-3 col-12', label: 'جست و جو', placeholder: ' ' },
+        { type: 'select', name: 'is_hidden', label: 'وضعیت نمایش', options: (new Content()).is_hiddenEnums, placeholder: ' ', col: 'col-md-3 col-12' },
         { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: this.search, col: 'col-md-2 col-12' }
         { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: this.search, col: 'col-md-2 col-12' }
       ],
       ],
       table: {
       table: {
@@ -107,6 +109,13 @@ export default {
             align: 'left',
             align: 'left',
             field: row => row.category_info?.title
             field: row => row.category_info?.title
           },
           },
+          {
+            name: 'is_hidden',
+            required: true,
+            label: 'نمایش',
+            align: 'left',
+            field: row => (new Content(row)).is_hidden_info.label
+          },
           {
           {
             name: 'action',
             name: 'action',
             required: true,
             required: true,

+ 1 - 0
src/components/Widgets/Content/AdminContentShow/AdminContentShow.vue

@@ -87,6 +87,7 @@ export default {
       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' },
         { type: 'file', name: 'thumbnail', responseKey: 'thumbnail', label: 'عکس', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'file', name: 'thumbnail', responseKey: 'thumbnail', label: 'عکس', placeholder: ' ', col: 'col-md-6 col-12' },
+        { type: 'checkbox', name: 'is_hidden', responseKey: 'is_hidden', label: 'پنهان باشد؟', placeholder: ' ', col: 'col-12' },
         { type: ContentCategorySelectorComp, name: 'category', responseKey: 'category_info', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'category', responseKey: 'category_info', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', col: 'col-md-12 col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', col: 'col-md-12 col-12' },

+ 158 - 0
src/components/Widgets/Content/ContentCarousel/ContentCarousel.vue

@@ -0,0 +1,158 @@
+<template>
+  <div class="contentCarousel">
+    <div class="contentCarousel-title">
+      <div>
+        <q-banner class="banner">
+          {{ getCategoryTitle }}
+        </q-banner>
+      </div>
+      <div>
+        <q-btn flat
+               :to="{name: 'Public.ContentCategoryParentParent.List'}">
+          مشاهده همه
+          <svg xmlns="http://www.w3.org/2000/svg"
+               width="18.387"
+               height="11.502"
+               viewBox="0 0 18.387 11.502"
+               class="q-ml-sm">
+            <path id="Combined_Shape"
+                  data-name="Combined Shape"
+                  d="M-9.338,11.408a.748.748,0,0,0,.388-.656V6.5h8.2A.751.751,0,0,0,0,5.75.75.75,0,0,0-.75,5h-8.2V.75A.751.751,0,0,0-9.338.093.769.769,0,0,0-9.7,0a.735.735,0,0,0-.4.115l-7.938,5a.747.747,0,0,0-.35.635.742.742,0,0,0,.35.634l7.938,5a.751.751,0,0,0,.4.116A.746.746,0,0,0-9.338,11.408ZM-10.45,9.392l-5.78-3.641,5.78-3.642Z"
+                  transform="translate(18.387)"
+                  fill="#eac38a" />
+          </svg>
+        </q-btn>
+      </div>
+    </div>
+    <div class="carousel-section">
+      <div v-if="!contents.loading && contents.list.length > 0"
+           ref="slider"
+           class="splide"
+           role="group"
+           aria-label="Splide Basic HTML Example">
+        <div class="splide__track">
+          <ul class="splide__list">
+            <li v-for="(content, contentIndex) in contents.list"
+                :key="contentIndex"
+                class="splide__slide">
+              <content-item :content="content" />
+            </li>
+          </ul>
+        </div>
+      </div>
+      <div v-else-if="!contents.loading && contents.list.length === 0">
+        رویدادی یافت نشد.
+      </div>
+      <div v-else-if="contents.loading">
+        کمی صبر کنید ...
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Splide from '@splidejs/splide'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { ContentList } from 'src/models/Content.js'
+import '@splidejs/splide/dist/css/splide-core.min.css'
+import ContentItem from './componetns/ContentItem.vue'
+import { mixinPrefetchServerData, mixinWidget } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'EventCarousel',
+  components: {
+    ContentItem
+  },
+  mixins: [mixinPrefetchServerData, mixinWidget],
+  data () {
+    return {
+      loading: false,
+      slider: null,
+      contents: new ContentList(),
+      breakpoints: {
+        1920: {
+          perPage: 3
+        },
+        900: {
+          perPage: 2
+        },
+        600: {
+          perPage: 1
+        }
+      },
+      maximizedToggle: true
+    }
+  },
+  computed: {
+    getCategoryTitle () {
+      return this.localOptions.categoryTitle
+    }
+  },
+  mounted () {
+    this.loadSlider()
+  },
+  methods: {
+    loadSlider () {
+      this.$nextTick(() => {
+        if (!this.$refs.slider) {
+          return
+        }
+        new Splide(this.$refs.slider, {
+          direction: 'rtl',
+          paginationDirection: 'rtl',
+          // type: 'loop',
+          focus: 0,
+          gap: 24,
+          snap: true,
+          // focus: 'center',
+          breakpoints: this.breakpoints
+        }).mount()
+      })
+    },
+    prefetchServerDataPromise () {
+      return this.getContents()
+    },
+    prefetchServerDataPromiseThen (contentList) {
+      this.contents = new ContentList(contentList.list)
+      this.contents.loading = false
+      this.loadSlider()
+    },
+    prefetchServerDataPromiseCatch () {
+      this.contents.loading = false
+    },
+
+    getContents () {
+      const params = {}
+
+      if (this.localOptions.categoryCategoryCategoryId) {
+        params.category__parent__parent = this.localOptions.categoryCategoryCategoryId
+      }
+
+      if (this.localOptions.categoryCategoryId) {
+        params.category__parent = this.localOptions.categoryCategoryId
+      }
+
+      if (this.localOptions.categoryId) {
+        params.category = this.localOptions.categoryId
+      }
+
+      this.contents.loading = true
+      return APIGateway.content.index(params)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.contentCarousel {
+  padding: 60px 0;
+  .contentCarousel-title {
+    display: flex;
+    flex-flow: row;
+    justify-content: space-between;
+    padding-bottom: 33px;
+  }
+  .carousel-section {
+  }
+}
+</style>

+ 74 - 0
src/components/Widgets/Content/ContentCarousel/OptionPanel.vue

@@ -0,0 +1,74 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <div class="q-mt-md">
+        عنوان
+      </div>
+      <div class="row">
+        <div class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryTitle" />
+        </div>
+      </div>
+      <div class="q-mt-md">
+        کد دسته پدربزرگ
+      </div>
+      <div class="row">
+        <div class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryCategoryCategoryId" />
+        </div>
+      </div>
+      <div>
+        کد دسته پدر
+      </div>
+      <div class="row">
+        <div class="col-md-4 col-12">
+          <q-checkbox v-model="localOptions.categoryCategoryFromQueryParams"
+                      label="استفاده از کوری پارام صفحه" />
+        </div>
+        <div v-if="!localOptions.categoryCategoryFromQueryParams"
+             class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryCategoryId" />
+        </div>
+      </div>
+      <div>
+        کد دسته فرزند
+      </div>
+      <div class="row">
+        <div class="col-md-4 col-12">
+          <q-checkbox v-model="localOptions.categoryFromQueryParams"
+                      label="استفاده از کوری پارام صفحه" />
+        </div>
+        <div v-if="!localOptions.categoryFromQueryParams"
+             class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryId" />
+        </div>
+      </div>
+    </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],
+  data () {
+    return {
+      defaultOptions: {
+        categoryTitle: 'عنوان دلخواه برای دسته محتوای مشخص شده',
+        categoryId: null,
+        categoryCategoryId: null,
+        categoryCategoryCategoryId: null,
+        categoryFromQueryParams: false,
+        categoryCategoryFromQueryParams: false
+      }
+    }
+  }
+})
+</script>

+ 128 - 0
src/components/Widgets/Content/ContentCarousel/componetns/ContentItem.vue

@@ -0,0 +1,128 @@
+<template>
+  <q-card flat
+          class="content-item">
+    <div class="thumbnail">
+      <img class="thumbnail-img"
+           :src="content.thumbnail"
+           @click="goToPage">
+    </div>
+    <div class="infos">
+      <div class="title ellipsis-2-lines">
+        <router-link :to="{name: 'Public.Content.Show', params: {id: content.id}}">
+          {{ content.title }}
+        </router-link>
+      </div>
+      <div class="date">
+        <q-icon name="calendar_month"
+                size="20px" />
+        {{ miladiToShamsi() }}
+      </div>
+    </div>
+    <q-card-actions class="action-section">
+      <q-btn color="primary"
+             class="btn-show-content"
+             :to="{name: 'Public.Content.Show', params: {id: content.id}}">
+        مشاهده جزییات
+        <svg xmlns="http://www.w3.org/2000/svg"
+             width="18.387"
+             height="11.502"
+             viewBox="0 0 18.387 11.502">
+          <path id="Combined_Shape"
+                data-name="Combined Shape"
+                d="M-9.338,11.408a.748.748,0,0,0,.388-.656V6.5h8.2A.751.751,0,0,0,0,5.75.75.75,0,0,0-.75,5h-8.2V.75A.751.751,0,0,0-9.338.093.769.769,0,0,0-9.7,0a.735.735,0,0,0-.4.115l-7.938,5a.747.747,0,0,0-.35.635.742.742,0,0,0,.35.634l7.938,5a.751.751,0,0,0,.4.116A.746.746,0,0,0-9.338,11.408ZM-10.45,9.392l-5.78-3.641,5.78-3.642Z"
+                transform="translate(18.387)"
+                fill="#fff" />
+        </svg>
+      </q-btn>
+    </q-card-actions>
+  </q-card>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import Assist from 'src/assets/js/Assist.js'
+import { Content } from 'src/models/Content.js'
+
+export default defineComponent({
+  name: 'ContentItem',
+  props: {
+    content: {
+      type: Content,
+      default: new Content()
+    }
+  },
+  data() {
+    return {
+    }
+  },
+  methods: {
+    goToPage () {
+      this.$router.push({ name: 'Public.Content.Show', params: { id: this.content.id } })
+    },
+    miladiToShamsi () {
+      return Assist.miladiToShamsi(this.content.creation_time, 'jD jMMMM jYYYY ساعت HH:mm')
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.content-item {
+  width: 100%;
+  background-color: #F6F6F6;
+  border-radius: 8px;
+  position: relative;
+  z-index: 1;
+
+  .thumbnail {
+    padding: 0;
+    cursor: pointer;
+    .thumbnail-img {
+      width: 100%;
+      height: auto;
+    }
+  }
+  .infos {
+    position: absolute;
+    left: 0;
+    padding-top: 12px;
+    bottom: 60px;
+    width: 100%;
+    z-index: 2;
+    -webkit-box-shadow: inset 0px -200px 55px -90px rgba(0,0,0,0.32);
+    -moz-box-shadow: inset 0px -200px 55px -90px rgba(0,0,0,0.32);
+    box-shadow: inset 0px -200px 55px -90px rgba(0,0,0,0.32);
+    .title {
+      padding: 0 15px;
+      text-align: left;
+      margin-bottom: 49px;
+      a {
+        color: white;
+        font-size: 16px;
+        font-weight: bold;
+      }
+    }
+    .date {
+      padding: 0 15px;
+      text-align: left;
+      margin-bottom: 24px;
+      color: white;
+      font-size: 16px;
+      font-weight: normal;
+      .q-icon {
+        color: #EAC38A;
+        margin-right: 12.5px;
+      }
+    }
+  }
+  .action-section {
+    padding: 0;
+    .btn-show-content {
+      width: 100%;
+      height: 60px;
+      border-top-right-radius: 0;
+      border-top-left-radius: 0;
+    }
+  }
+}
+</style>

+ 1 - 1
src/components/Widgets/Content/ContentShow/components/productItem.vue

@@ -30,7 +30,7 @@
         </div>
         </div>
         <div class="product-item__price-info-value">
         <div class="product-item__price-info-value">
           {{ product.getFinalPrice() }}
           {{ product.getFinalPrice() }}
-          تومان
+          ریال
         </div>
         </div>
       </div>
       </div>
       <q-btn color="primary"
       <q-btn color="primary"

+ 2 - 2
src/components/Widgets/Content/ContentTopicList/components/packageItem.vue

@@ -19,11 +19,11 @@
     <div class="package-item__price">
     <div class="package-item__price">
       <div class="package-item__price-base">
       <div class="package-item__price-base">
         {{ parseInt(packageItem.unit_price).toLocaleString('fa') }}
         {{ parseInt(packageItem.unit_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
       <div class="package-item__price-final">
       <div class="package-item__price-final">
         {{ packageItem.getFinalPrice() }}
         {{ packageItem.getFinalPrice() }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="package-item__actions">
     <div class="package-item__actions">

+ 2 - 2
src/components/Widgets/Content/ContentTopicList/components/productItem.vue

@@ -27,11 +27,11 @@
     <div class="product-item__price">
     <div class="product-item__price">
       <div class="product-item__price-base">
       <div class="product-item__price-base">
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
       <div class="product-item__price-final">
       <div class="product-item__price-final">
         {{ product.getFinalPrice() }}
         {{ product.getFinalPrice() }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="product-item__actions">
     <div class="product-item__actions">

+ 90 - 0
src/components/Widgets/Content/LastContentTopics/LastContentTopics.vue

@@ -0,0 +1,90 @@
+<template>
+  <div class="ContentTopic"
+       :style="localOptions.style">
+    <div class="ContentTopic-title">
+      <div>
+        <q-banner class="banner">
+          دسته‌بندی موضوعی
+        </q-banner>
+      </div>
+      <div>
+        <q-btn flat
+               :to="{name: 'Public.PostTopic.List'}">
+          مشاهده همه
+          <svg xmlns="http://www.w3.org/2000/svg"
+               width="18.387"
+               height="11.502"
+               viewBox="0 0 18.387 11.502"
+               class="q-ml-sm">
+            <path id="Combined_Shape"
+                  data-name="Combined Shape"
+                  d="M-9.338,11.408a.748.748,0,0,0,.388-.656V6.5h8.2A.751.751,0,0,0,0,5.75.75.75,0,0,0-.75,5h-8.2V.75A.751.751,0,0,0-9.338.093.769.769,0,0,0-9.7,0a.735.735,0,0,0-.4.115l-7.938,5a.747.747,0,0,0-.35.635.742.742,0,0,0,.35.634l7.938,5a.751.751,0,0,0,.4.116A.746.746,0,0,0-9.338,11.408ZM-10.45,9.392l-5.78-3.641,5.78-3.642Z"
+                  transform="translate(18.387)"
+                  fill="#eac38a" />
+          </svg>
+        </q-btn>
+      </div>
+    </div>
+    <div class="ContentTopic-list">
+      <div class="row q-col-gutter-md">
+        <div v-for="post in contentTopicList.list"
+             :key="post.id"
+             class="col-md-6 col-12">
+          <topic-item :content-topic="post" />
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import TopicItem from './TopicItem.vue'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { ContentTopicList } from 'src/models/ContentTopic.js'
+import { mixinPrefetchServerData, mixinWidget } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'LastPosts',
+  components: {
+    TopicItem
+  },
+  mixins: [mixinWidget, mixinPrefetchServerData],
+  data () {
+    return {
+      mounted: false,
+      contentTopicList: new ContentTopicList()
+    }
+  },
+  mounted() {
+    this.mounted = true
+  },
+  methods: {
+    prefetchServerDataPromise () {
+      return this.getApiRequest()
+    },
+    prefetchServerDataPromiseThen ({ list }) {
+      this.contentTopicList = new ContentTopicList(list)
+      this.contentTopicList.loading = false
+    },
+    prefetchServerDataPromiseCatch () {
+      this.contentTopicList.loading = false
+    },
+
+    getApiRequest() {
+      this.contentTopicList.loading = true
+      return APIGateway.contentTopic.index()
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.ContentTopic {
+  .ContentTopic-title {
+    display: flex;
+    flex-flow: row;
+    justify-content: space-between;
+    padding-bottom: 33px;
+  }
+}
+</style>

+ 0 - 0
src/components/Widgets/Content/LastContents/OptionPanel.vue → src/components/Widgets/Content/LastContentTopics/OptionPanel.vue


+ 84 - 0
src/components/Widgets/Content/LastContentTopics/TopicItem.vue

@@ -0,0 +1,84 @@
+<template>
+  <q-card class="ContentTopicItem"
+          flat>
+    <div class="thumbnail">
+      <router-link :to="{name: 'Public.Content.Show', params: {id: contentTopic.content}}">
+        <q-img :src="contentTopic.thumbnail" />
+      </router-link>
+    </div>
+    <div class="texts">
+      <div class="title ellipsis-2-lines">
+        <router-link :to="{name: 'Public.Content.Show', params: {id: contentTopic.content}}">
+          {{ contentTopic.title }}
+        </router-link>
+      </div>
+      <div v-if="contentTopic.content_info"
+           class="content"
+           v-html="contentTopic.content_info.description" />
+    </div>
+  </q-card>
+</template>
+
+<script>
+import Assist from 'src/assets/js/Assist.js'
+import { ContentTopic } from 'src/models/ContentTopic.js'
+
+export default {
+  name: 'ContentTopicItem',
+  props: {
+    contentTopic: {
+      type: ContentTopic,
+      default: new ContentTopic()
+    }
+  },
+  methods: {
+    miladiToShamsi (date) {
+      return Assist.miladiToShamsi(date, 'jDD jMMM jYYYY')
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.ContentTopicItem {
+  display: flex;
+  flex-flow: row;
+  gap: 24px;
+  justify-content: flex-start;
+  border-radius: 20px;
+  padding: 32px 28px;
+  background: #FBF4EA;
+  $thumbnail-width: 250px;
+  .thumbnail {
+    width: $thumbnail-width;
+    border-radius: 10px;
+    .q-img {
+      width: 100%;
+      border-radius: 10px;
+    }
+  }
+  .texts {
+    width: calc(100% - #{$thumbnail-width});
+    padding-top: 24px;
+    .title {
+      color: #212121;
+      font-size: 16px;
+      font-weight: bold;
+      margin-bottom: 28px;
+    }
+    .content {
+      color: #212121;
+      font-size: 16px;
+    }
+  }
+  @media screen and (max-width: 600px) {
+    flex-flow: column;
+    .thumbnail {
+      width: 100%;
+    }
+    .texts {
+      width: 100%;
+    }
+  }
+}
+</style>

+ 0 - 61
src/components/Widgets/Content/LastContents/LastContents.vue

@@ -1,61 +0,0 @@
-<template>
-  <div class="LastBlogs row q-col-gutter-md"
-       :style="localOptions.style">
-    <div v-for="post in postList.list"
-         :key="post.id"
-         class="col-md-6 col-12">
-      <post-item :post="post" />
-    </div>
-  </div>
-</template>
-
-<script>
-import PostItem from './PostItem.vue'
-import { PostList } from 'src/models/Post.js'
-import { APIGateway } from 'src/api/APIGateway.js'
-import { mixinPrefetchServerData, mixinWidget } from 'src/mixin/Mixins.js'
-
-export default {
-  name: 'LastPosts',
-  components: {
-    PostItem
-  },
-  mixins: [mixinWidget, mixinPrefetchServerData],
-  data () {
-    return {
-      mounted: false,
-      defaultOptions: {
-        categoryId: null,
-        categoryParentId: null,
-        categoryParentParentId: 1
-      },
-      postList: new PostList()
-    }
-  },
-  mounted() {
-    this.mounted = true
-  },
-  methods: {
-    prefetchServerDataPromise () {
-      return this.getApiRequest()
-    },
-    prefetchServerDataPromiseThen ({ list }) {
-      this.postList = new PostList(list)
-      this.postList.loading = false
-    },
-    prefetchServerDataPromiseCatch () {
-      this.postList.loading = false
-    },
-
-    getApiRequest() {
-      this.postList.loading = true
-      return APIGateway.post.index({ category__parent__parent: this.localOptions.categoryParentParentId, category__parent: this.localOptions.categoryParentId, category: this.localOptions.categoryId })
-    }
-  }
-}
-</script>
-
-<style scoped lang="scss">
-.LastBlogs {
-}
-</style>

+ 0 - 98
src/components/Widgets/Content/LastContents/PostItem.vue

@@ -1,98 +0,0 @@
-<template>
-  <q-card class="PostItem"
-          flat>
-    <div class="thumbnail">
-      <router-link :to="{name: 'Public.Post.Show', params: {id: post.id}}">
-        <q-img :src="post.thumbnail" />
-      </router-link>
-    </div>
-    <div class="texts">
-      <div class="category-title">
-        <router-link :to="{name: 'Public.Post.Show', params: {id: post.id}}">
-          {{ post.category_info.title }}
-        </router-link>
-      </div>
-      <div class="title ellipsis-2-lines">
-        <router-link :to="{name: 'Public.Post.Show', params: {id: post.id}}">
-          {{ post.title }}
-        </router-link>
-      </div>
-      <div class="published-date">
-        تاریخ انتشار
-        {{ miladiToShamsi(post.creation_time) }}
-      </div>
-    </div>
-  </q-card>
-</template>
-
-<script>
-import { Post } from 'src/models/Post.js'
-import Assist from 'src/assets/js/Assist.js'
-
-export default {
-  name: 'PostItem',
-  props: {
-    post: {
-      type: Post,
-      default: new Post()
-    }
-  },
-  methods: {
-    miladiToShamsi (date) {
-      return Assist.miladiToShamsi(date, 'jDD jMMM jYYYY')
-    }
-  }
-}
-</script>
-
-<style lang="scss" scoped>
-.PostItem {
-  display: flex;
-  flex-flow: row;
-  justify-content: flex-start;
-  border: 2px solid transparent;
-  padding: 16px;
-  &:hover {
-    border-radius: 14px;
-    border-color: #0B6AB1;
-  }
-  .thumbnail {
-    width: 148px;
-    .q-img {
-      width: 100%;
-    }
-  }
-  .texts {
-    width: calc(100% - 148px);
-    position: relative;
-    padding-left: 14px;
-    .category-title {
-      width: max-content;
-      border-radius: 3px;
-      background: #E3E7F4;
-      padding: 4px 8px;
-      color: #666;
-      font-size: 14px;
-      font-weight: 500;
-      line-height: 100%;
-      margin-bottom: 8px;
-    }
-    .title {
-      width: 100%;
-      color: #000;
-      text-align: left;
-      font-size: 18px;
-      font-weight: 700;
-    }
-    .published-date {
-      color: #000;
-      text-align: right;
-      font-size: 14px;
-      font-weight: 200;
-      position: absolute;
-      left: 14px;
-      bottom: 0;
-    }
-  }
-}
-</style>

+ 152 - 0
src/components/Widgets/Donation/DonationIntro/DonationIntro.vue

@@ -0,0 +1,152 @@
+<template>
+  <div class="DonationIntro">
+    <div class="DonationIntro-title">
+      <q-banner class="banner">
+        صدقات و خیریه
+      </q-banner>
+    </div>
+    <div class="content">
+      <div class="text-and-action">
+        <div class="text"
+             v-html="localOptions.text" />
+        <q-btn color="primary"
+               :to="{ name: 'Public.DonationCategory.List' }">
+          پرداخت صدقات و خیریه
+        </q-btn>
+      </div>
+      <div class="carousel-section">
+        <div v-if="localOptions.slides.length > 0"
+             ref="slider"
+             class="splide"
+             role="group"
+             aria-label="Splide Basic HTML Example">
+          <div class="splide__track">
+            <ul class="splide__list">
+              <li v-for="(slide, slideIndex) in localOptions.slides"
+                  :key="slideIndex"
+                  class="splide__slide">
+                <q-img :src="slide" />
+              </li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Splide from '@splidejs/splide'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import '@splidejs/splide/dist/css/splide-core.min.css'
+
+export default {
+  name: 'EventCarousel',
+  mixins: [mixinWidget],
+  data () {
+    return {
+      loading: false,
+      slider: null,
+      breakpoints: {
+        1920: {
+          perPage: 1
+        },
+        900: {
+          perPage: 1
+        },
+        600: {
+          perPage: 1
+        }
+      },
+      maximizedToggle: true,
+      defaultOptions: {
+        text: null,
+        slides: []
+      }
+    }
+  },
+  mounted () {
+    this.loadSlider()
+  },
+  methods: {
+    loadSlider () {
+      this.$nextTick(() => {
+        if (!this.$refs.slider) {
+          return
+        }
+        new Splide(this.$refs.slider, {
+          direction: 'rtl',
+          paginationDirection: 'rtl',
+          // type: 'loop',
+          focus: 0,
+          gap: 24,
+          snap: true,
+          // focus: 'center',
+          breakpoints: this.breakpoints
+        }).mount()
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.DonationIntro {
+  padding: 60px 0;
+  .DonationIntro-title {
+    display: flex;
+    flex-flow: row;
+    justify-content: space-between;
+    padding-bottom: 33px;
+  }
+  .content {
+    display: flex;
+    flex-flow: row;
+    gap: 48px;
+    justify-content: space-between;
+    align-items: stretch;
+    .text-and-action {
+      width: 50%;
+      display: flex;
+      flex-flow: column;
+      justify-content: space-between;
+      align-items: flex-end;
+      .text {
+        width: 100%;
+      }
+    }
+    .carousel-section {
+      width: 50%;
+    }
+    :deep(.carousel-section) {
+      .splide__arrows {
+        top: auto;
+        bottom: 0;
+        .splide__arrow {
+          &.splide__arrow--prev {
+            left: 50%;
+            transform: translateX(-120%);
+          }
+          &.splide__arrow--next {
+            right: 50%;
+            transform: translateX(120%);
+          }
+        }
+      }
+    }
+
+    @media screen and (max-width: 1200px) {
+      & {
+        flex-flow: column;
+        gap: 16px;
+        .text-and-action {
+          width: 100%;
+        }
+        .carousel-section {
+          width: 100%;
+        }
+      }
+    }
+  }
+}
+</style>

+ 100 - 0
src/components/Widgets/Donation/DonationIntro/OptionPanel.vue

@@ -0,0 +1,100 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <div>
+        <div>
+          متن:
+        </div>
+        <form-builder-tiptap-editor v-model:value="localOptions.text"
+                                    :options="editorOptions" />
+      </div>
+      <q-separator class="q-my-lg" />
+      <div class="row">
+        <div class="col-12">
+          <q-btn color="primary"
+                 class="full-width"
+                 @click="addSlide">
+            افزودن تصویر آپلود شده
+          </q-btn>
+        </div>
+        <div class="col-12">
+          <div class="input-container col-12 q-mt-md">
+            <div class="outsideLabel">آدرس عکس</div>
+            <image-uploader v-model:file="newSlide" />
+          </div>
+        </div>
+      </div>
+      <div class="slides">
+        <div v-for="(slide, slideIndex) in localOptions.slides"
+             :key="slideIndex"
+             class="slide-item">
+          <q-btn color="red"
+                 @click="removeSlide(slideIndex)">
+            <q-icon name="close" />
+          </q-btn>
+          <q-img :src="slide" />
+        </div>
+      </div>
+    </template>
+  </option-panel-tabs>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
+import ImageUploader from 'src/components/ImageUploader.vue'
+import PostMixin from 'src/components/Widgets/Post/PostMixin.js'
+import FormBuilderTiptapEditor from 'src/components/FormBuilderCustumComponents/FormBuilderTiptapEditor.vue'
+import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
+
+export default defineComponent({
+  name: 'OptionPanel',
+  components: {
+    FormBuilderTiptapEditor,
+    OptionPanelTabs,
+    ImageUploader
+  },
+  mixins: [mixinOptionPanel, PostMixin],
+  data () {
+    const authorization = 'Bearer ' + this.$store.getters['Auth/accessToken']
+    const uploadVideo = this.uploadVideo
+    const uploadAudio = this.uploadAudio
+    return {
+      newSlide: null,
+      editorOptions: {
+        bubbleMenu: false,
+        floatingMenu: false,
+        poem: false,
+        reading: false,
+        loadBareHtml: true,
+        persianKeyboard: true,
+        mathliveOptions: { smartFence: false },
+        uploadServer: {
+          url: '/api' + APIGateway.media.APIAdresses.base,
+          instantUpload: true,
+          responseKey: 'file',
+          headers: { Authorization: authorization }
+        },
+        uploadVideo,
+        uploadAudio
+      },
+      defaultOptions: {
+        text: null,
+        slides: []
+      }
+    }
+  },
+  methods: {
+    addSlide () {
+      if (!this.newSlide) {
+        return
+      }
+      this.localOptions.slides.push(JSON.parse(JSON.stringify(this.newSlide)))
+    },
+    removeSlide(slideIndex) {
+      this.localOptions.slides.splice(slideIndex, 1)
+    }
+  }
+})
+</script>

+ 128 - 0
src/components/Widgets/Donation/DonationIntro/componetns/ContentItem.vue

@@ -0,0 +1,128 @@
+<template>
+  <q-card flat
+          class="content-item">
+    <div class="thumbnail">
+      <img class="thumbnail-img"
+           :src="content.thumbnail"
+           @click="goToPage">
+    </div>
+    <div class="infos">
+      <div class="title ellipsis-2-lines">
+        <router-link :to="{name: 'Public.Content.Show', params: {id: content.id}}">
+          {{ content.title }}
+        </router-link>
+      </div>
+      <div class="date">
+        <q-icon name="calendar_month"
+                size="20px" />
+        {{ miladiToShamsi() }}
+      </div>
+    </div>
+    <q-card-actions class="action-section">
+      <q-btn color="primary"
+             class="btn-show-content"
+             :to="{name: 'Public.Content.Show', params: {id: content.id}}">
+        مشاهده و ثبت نام
+        <svg xmlns="http://www.w3.org/2000/svg"
+             width="18.387"
+             height="11.502"
+             viewBox="0 0 18.387 11.502">
+          <path id="Combined_Shape"
+                data-name="Combined Shape"
+                d="M-9.338,11.408a.748.748,0,0,0,.388-.656V6.5h8.2A.751.751,0,0,0,0,5.75.75.75,0,0,0-.75,5h-8.2V.75A.751.751,0,0,0-9.338.093.769.769,0,0,0-9.7,0a.735.735,0,0,0-.4.115l-7.938,5a.747.747,0,0,0-.35.635.742.742,0,0,0,.35.634l7.938,5a.751.751,0,0,0,.4.116A.746.746,0,0,0-9.338,11.408ZM-10.45,9.392l-5.78-3.641,5.78-3.642Z"
+                transform="translate(18.387)"
+                fill="#fff" />
+        </svg>
+      </q-btn>
+    </q-card-actions>
+  </q-card>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import Assist from 'src/assets/js/Assist.js'
+import { Content } from 'src/models/Content.js'
+
+export default defineComponent({
+  name: 'ContentItem',
+  props: {
+    content: {
+      type: Content,
+      default: new Content()
+    }
+  },
+  data() {
+    return {
+    }
+  },
+  methods: {
+    goToPage () {
+      this.$router.push({ name: 'Public.Content.Show', params: { id: this.content.id } })
+    },
+    miladiToShamsi () {
+      return Assist.miladiToShamsi(this.content.creation_time, 'jD jMMMM jYYYY ساعت HH:mm')
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.content-item {
+  width: 100%;
+  background-color: #F6F6F6;
+  border-radius: 8px;
+  position: relative;
+  z-index: 1;
+
+  .thumbnail {
+    padding: 0;
+    cursor: pointer;
+    .thumbnail-img {
+      width: 100%;
+      height: auto;
+    }
+  }
+  .infos {
+    position: absolute;
+    left: 0;
+    padding-top: 12px;
+    bottom: 60px;
+    width: 100%;
+    z-index: 2;
+    -webkit-box-shadow: inset 0px -200px 55px -90px rgba(0,0,0,0.32);
+    -moz-box-shadow: inset 0px -200px 55px -90px rgba(0,0,0,0.32);
+    box-shadow: inset 0px -200px 55px -90px rgba(0,0,0,0.32);
+    .title {
+      padding: 0 15px;
+      text-align: left;
+      margin-bottom: 49px;
+      a {
+        color: white;
+        font-size: 16px;
+        font-weight: bold;
+      }
+    }
+    .date {
+      padding: 0 15px;
+      text-align: left;
+      margin-bottom: 24px;
+      color: white;
+      font-size: 16px;
+      font-weight: normal;
+      .q-icon {
+        color: #EAC38A;
+        margin-right: 12.5px;
+      }
+    }
+  }
+  .action-section {
+    padding: 0;
+    .btn-show-content {
+      width: 100%;
+      height: 60px;
+      border-top-right-radius: 0;
+      border-top-left-radius: 0;
+    }
+  }
+}
+</style>

+ 86 - 22
src/components/Widgets/Donation/DonationShow/DonationShow.vue

@@ -18,24 +18,43 @@
           <q-img :src="donationCategory.thumbnail" />
           <q-img :src="donationCategory.thumbnail" />
         </div>
         </div>
       </div>
       </div>
-      <div v-if="donation.id"
+      <div class="show-donation-selector">
+        <q-banner class="donation-category-title">
+          پرداخت
+          صدقه
+          آنلاین
+        </q-banner>
+        <div class="donation-selector-form">
+          <entity-create v-if="mounted"
+                         ref="entityCreate"
+                         v-model:value="donationSelectorInputs"
+                         :api="donationSelectorApi"
+                         :entity-id-key="donationSelectorEntityIdKey"
+                         :entity-param-key="donationSelectorEntityParamKey"
+                         :show-route-name="donationSelectorShowRouteName"
+                         :default-layout="false"
+                         :show-close-button="false"
+                         :show-edit-button="false"
+                         :show-expand-button="false"
+                         :show-save-button="false"
+                         :show-reload-button="false" />
+        </div>
+      </div>
+      <div v-if="selectedDonation?.id"
            class="show-donation-section">
            class="show-donation-section">
         <div class="show-donation-payment-section">
         <div class="show-donation-payment-section">
           <q-banner class="donation-category-title">
           <q-banner class="donation-category-title">
-            پرداخت
-            {{donation.title}}
+            شرح مورد استفاده
+            <!--            {{selectedDonation.title}}-->
           </q-banner>
           </q-banner>
         </div>
         </div>
         <div class="show-donation-info-section">
         <div class="show-donation-info-section">
           <div class="donation-content">
           <div class="donation-content">
-            <q-banner class="donation-title">
-              {{donation.title}}
-            </q-banner>
             <div class="donation-description"
             <div class="donation-description"
-                 v-html="donation.description" />
+                 v-html="selectedDonation.description" />
           </div>
           </div>
           <div class="donation-thumbnail">
           <div class="donation-thumbnail">
-            <q-img :src="donation.thumbnail" />
+            <q-img :src="selectedDonation.thumbnail" />
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
@@ -44,15 +63,21 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import { Donation } from 'src/models/Donation.js'
+import { shallowRef } from 'vue'
+import { EntityCreate } from 'quasar-crud'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
+import { DonationList } from 'src/models/Donation.js'
+import BtnControl from 'src/components/Control/btn.vue'
 import { DonationCategory } from 'src/models/DonationCategory.js'
 import { DonationCategory } from 'src/models/DonationCategory.js'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import { FormBuilderAssist } from 'quasar-form-builder'
+
+const BtnControlComp = shallowRef(BtnControl)
 
 
 export default {
 export default {
   name: 'DonationList',
   name: 'DonationList',
-  components: { Breadcrumbs },
+  components: { Breadcrumbs, EntityCreate },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
   data () {
   data () {
     return {
     return {
@@ -68,21 +93,47 @@ export default {
       table: {
       table: {
         columns: []
         columns: []
       },
       },
-      donation: new Donation(),
+      donations: new DonationList(),
       donationCategory: new DonationCategory(),
       donationCategory: new DonationCategory(),
       mounted: false,
       mounted: false,
-      createRouteName: ''
+      createRouteName: '',
+
+      donationSelectorApi: 'id',
+      donationSelectorInputs: [
+        { type: 'select', name: 'donation', responseKey: 'donation', label: 'مورد استفاده', placeholder: ' ', optionValue: 'id', optionLabel: 'title', col: 'col-md-8 col-12' },
+        { type: 'input', name: 'amount', responseKey: 'amount', label: 'مبلغ', placeholder: ' ', col: 'col-md-4 col-12' },
+        { type: 'input', name: 'fullName', responseKey: 'fullName', label: 'نام و نام خانوادگی', placeholder: ' ', col: 'col-md-4 col-12' },
+        { type: 'input', name: 'mobile', responseKey: 'mobile', label: 'موبایل(جهت اصلاع رسانی)', placeholder: ' ', col: 'col-md-4 col-12' },
+        { type: 'checkbox', name: 'private', responseKey: 'private', label: 'ناشناس بمانم', placeholder: ' ', value: false, class: 'flex justify-end items-end full-height', col: 'col-md-2 col-12' },
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'پرداخت', placeholder: ' ', atClick: () => {}, class: 'flex justify-end items-end full-height', col: 'col-md-2 col-12' }
+      ],
+      donationSelectorEntityIdKey: 'id',
+      donationSelectorEntityParamKey: 'id',
+      donationSelectorShowRouteName: 'Admin.Donation.Show'
+    }
+  },
+  computed: {
+    selectedDonation () {
+      const donationId = FormBuilderAssist.getInputsByName(this.donationSelectorInputs, 'donation')?.value
+      if (!donationId) {
+        return null
+      }
+
+      return this.donations.list.find(item => item.id === donationId)
     }
     }
   },
   },
   mounted() {
   mounted() {
-    this.getDonation()
+    this.getDonations()
   },
   },
   methods: {
   methods: {
-    getDonation () {
-      this.donation.loading = true
-      APIGateway.donation.get(this.$route.params.donation_category_id)
-        .then((donation) => {
-          this.donation = new Donation(donation)
+    getDonations () {
+      this.donations.loading = true
+      APIGateway.donation.index({
+        category: this.$route.params.donation_category_id
+      })
+        .then(({ list }) => {
+          this.donations = new DonationList(list)
+          FormBuilderAssist.setAttributeByName(this.donationSelectorInputs, 'donation', 'options', this.donations.list)
           this.getDonationCategory()
           this.getDonationCategory()
         })
         })
     },
     },
@@ -110,10 +161,6 @@ export default {
           {
           {
             label: donationCategory.title,
             label: donationCategory.title,
             to: { name: 'Public.DonationCategory.Show', params: { donation_category_id: donationCategory.id } }
             to: { name: 'Public.DonationCategory.Show', params: { donation_category_id: donationCategory.id } }
-          },
-          {
-            label: donation.title,
-            to: { name: 'Public.DonationCategory.Donation.Show', params: { donation_category_id: donationCategory.id, donation_id: donation.id } }
           }
           }
         ]
         ]
       })
       })
@@ -147,6 +194,23 @@ export default {
         .donation-category-description {}
         .donation-category-description {}
       }
       }
     }
     }
+    .show-donation-selector {
+      .donation-category-title {
+        margin-bottom: 34px;
+      }
+      .donation-selector-form {
+        background: #F5F5F5;
+        padding: 44px 24px 24px 24px;
+        margin-bottom: 60px;
+        :deep(.entity-crud-formBuilder) {
+          .form-builder-checkbox {
+            .q-checkbox {
+              height: 48px !important;
+            }
+          }
+        }
+      }
+    }
     .show-donation-section {
     .show-donation-section {
       .show-donation-payment-section {
       .show-donation-payment-section {
         margin-bottom: 16px;
         margin-bottom: 16px;

+ 1 - 1
src/components/Widgets/Event/LastEvents/LastEvents.vue

@@ -82,7 +82,7 @@ export default {
         900: {
         900: {
           perPage: 2
           perPage: 2
         },
         },
-        500: {
+        600: {
           perPage: 1
           perPage: 1
         }
         }
       },
       },

+ 28 - 4
src/components/Widgets/FeatureBox/OptionPanel.vue

@@ -19,7 +19,8 @@
         </div>
         </div>
         <div class="input-container q-py-md">
         <div class="input-container q-py-md">
           <div class="outsideLabel">description</div>
           <div class="outsideLabel">description</div>
-          <editor v-model:value="localOptions.description" />
+          <form-builder-tiptap-editor v-model:value="localOptions.description"
+                                      :options="editorOptions" />
         </div>
         </div>
       </div>
       </div>
     </template>
     </template>
@@ -27,15 +28,17 @@
 </template>
 </template>
 <script>
 <script>
 import { defineComponent } from 'vue'
 import { defineComponent } from 'vue'
-import Editor from 'src/components/Utils/Editor.vue'
+import { APIGateway } from 'src/api/APIGateway.js'
 import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
 import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
 import ImageUploader from 'src/components/ImageUploader.vue'
 import ImageUploader from 'src/components/ImageUploader.vue'
+import PostMixin from 'src/components/Widgets/Post/PostMixin.js'
+import FormBuilderTiptapEditor from 'src/components/FormBuilderCustumComponents/FormBuilderTiptapEditor.vue'
 import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
 import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
 
 
 export default defineComponent({
 export default defineComponent({
   name: 'OptionPanel',
   name: 'OptionPanel',
-  components: { OptionPanelTabs, ImageUploader, Editor },
-  mixins: [mixinOptionPanel],
+  components: { OptionPanelTabs, ImageUploader, FormBuilderTiptapEditor },
+  mixins: [mixinOptionPanel, PostMixin],
   props: {
   props: {
     options: {
     options: {
       type: Object,
       type: Object,
@@ -45,6 +48,10 @@ export default defineComponent({
     }
     }
   },
   },
   data() {
   data() {
+    const authorization = 'Bearer ' + this.$store.getters['Auth/accessToken']
+    const uploadVideo = this.uploadVideo
+    const uploadAudio = this.uploadAudio
+
     return {
     return {
       defaultOptions: {
       defaultOptions: {
         title: null,
         title: null,
@@ -69,6 +76,23 @@ export default defineComponent({
             translateX: 0,
             translateX: 0,
             translateY: 0
             translateY: 0
           }
           }
+        },
+        editorOptions: {
+          bubbleMenu: false,
+          floatingMenu: false,
+          poem: false,
+          reading: false,
+          loadBareHtml: true,
+          persianKeyboard: true,
+          mathliveOptions: { smartFence: false },
+          uploadServer: {
+            url: '/api' + APIGateway.media.APIAdresses.base,
+            instantUpload: true,
+            responseKey: 'file',
+            headers: { Authorization: authorization }
+          },
+          uploadVideo,
+          uploadAudio
         }
         }
       }
       }
     }
     }

+ 1 - 5
src/components/Widgets/Invoice/AdminInvoiceList/AdminInvoiceList.vue

@@ -175,11 +175,7 @@ export default {
   },
   },
   methods: {
   methods: {
     getInvoiceTitle (invoice) {
     getInvoiceTitle (invoice) {
-      if (invoice.item_info.item_type === 'CLASSROOM') {
-        return invoice.item_info.title
-      } else {
-        return invoice.item_info.title || '-'
-      }
+      return invoice.title || '-'
     },
     },
     search () {
     search () {
       this.$refs.entityIndex.search()
       this.$refs.entityIndex.search()

+ 179 - 5
src/components/Widgets/Invoice/AdminInvoiceShow/AdminInvoiceShow.vue

@@ -83,17 +83,29 @@
       <q-separator />
       <q-separator />
       <q-card-section>
       <q-card-section>
         <div class="row">
         <div class="row">
-          <div class="col-md-4 col-12 flex items-center justify-start">
+          <div v-if="invoice.item_type==='CLASSROOM'"
+               class="col-md-4 col-12 flex items-center justify-start">
             <q-img :src="invoice.item_info.thumbnail"
             <q-img :src="invoice.item_info.thumbnail"
                    width="100px" />
                    width="100px" />
             {{ invoice.item_info.title }}
             {{ invoice.item_info.title }}
           </div>
           </div>
-          <div class="col-md-4 col-12">
+          <div v-else
+               class="col-md-4 col-12 flex items-center justify-start">
+            {{ invoice.title }}
+          </div>
+          <div v-if="invoice.item_type==='CLASSROOM'"
+               class="col-md-4 col-12">
             <q-icon name="isax:user" />
             <q-icon name="isax:user" />
             استاد:
             استاد:
             {{ invoice.item_info.professor_info.firstname }}
             {{ invoice.item_info.professor_info.firstname }}
             {{ invoice.item_info.professor_info.lastname }}
             {{ invoice.item_info.professor_info.lastname }}
           </div>
           </div>
+          <div v-else-if="invoice.item_type==='STORE_BASKET'"
+               class="col-md-4 col-12">
+            <q-icon name="isax:user" />
+            کد سفارش:
+            {{ invoice.item_info.id }}
+          </div>
           <div class="col-md-4 col-12">
           <div class="col-md-4 col-12">
             <q-icon name="isax:card" />
             <q-icon name="isax:card" />
             قیمت:
             قیمت:
@@ -110,7 +122,7 @@
         </q-card-section>
         </q-card-section>
         <q-separator />
         <q-separator />
         <q-card-section>
         <q-card-section>
-          <invoice-payment-card v-if="!invoice.loading && invoice.status!=='PAID_FULL' && invoice.status!=='PAID_IN_INSTALMENT'"
+          <invoice-payment-card v-if="!invoice.loading && invoice.status!=='PAID_FULL'"
                                 :wallet="wallet"
                                 :wallet="wallet"
                                 :invoice="invoice"
                                 :invoice="invoice"
                                 :show-invoice-link="false"
                                 :show-invoice-link="false"
@@ -126,21 +138,80 @@
       </q-card>
       </q-card>
 
 
     </div>
     </div>
+    <div v-if="invoice.item_type==='STORE_BASKET'"
+         class="q-mt-md">
+      <entity-index v-model:value="tableInputs"
+                    title="لیست محصولات سفارش"
+                    :table="table"
+                    :loaded-data="invoice.item_info"
+                    :table-keys="tableKeys"
+                    :show-search-button="false"
+                    :show-expand-button="false"
+                    :show-reload-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 === 'thumbnail'">
+            <q-img :src="inputData.col.value"
+                   width="100px" />
+          </template>
+          <template v-else-if="inputData.col.name === 'download'">
+            <q-btn v-if="!inputData.col.value"
+                   color="primary"
+                   @click="openDownloadDialog(inputData.props.row)">
+              <q-icon name="download"
+                      class="q-mr-sm" />
+              دانلود
+            </q-btn>
+            <template v-else>
+              {{}}
+            </template>
+          </template>
+          <template v-else-if="inputData.col.name === 'action'">
+            <q-btn color="primary"
+                   outline
+                   :to="{name: inputData.props.row.product ? 'Admin.Store.Product.Show' : 'Admin.Store.Package.Show', params: {id: inputData.props.row.product || inputData.props.row.package}}">
+              <q-icon name="visibility"
+                      class="q-mr-sm" />
+              مشاهده
+            </q-btn>
+          </template>
+          <template v-else>
+            {{ inputData.col.value }}
+          </template>
+        </template>
+      </entity-index>
+      <q-dialog v-model="downloadDialog">
+        <q-card style="width: 800px; max-width: 95%;">
+          <q-card-section>
+            محتوای قابل دانلود
+          </q-card-section>
+          <q-separator />
+          <q-card-section>
+            <product-or-package-medias :basket-item="selectedBasketItem" />
+          </q-card-section>
+        </q-card>
+      </q-dialog>
+    </div>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
 import Assist from 'assets/js/Assist.js'
 import Assist from 'assets/js/Assist.js'
 import { User } from 'src/models/User.js'
 import { User } from 'src/models/User.js'
+import { EntityIndex } from 'quasar-crud'
 import { Wallet } from 'src/models/Wallet.js'
 import { Wallet } from 'src/models/Wallet.js'
+import { Product } from 'src/models/Product.js'
 import { Invoice } from 'src/models/Invoice.js'
 import { Invoice } from 'src/models/Invoice.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import InvoicePaymentCard from 'src/components/InvoicePaymentCard/InvoicePaymentCard.vue'
 import InvoicePaymentCard from 'src/components/InvoicePaymentCard/InvoicePaymentCard.vue'
+import ProductOrPackageMedias from 'src/components/Widgets/Store/Basket/UserStoreBasketShow/ProductOrPackageMedias.vue'
 
 
 export default {
 export default {
   name: 'AdminInvoiceShow',
   name: 'AdminInvoiceShow',
-  components: { InvoicePaymentCard },
+  components: { InvoicePaymentCard, EntityIndex, ProductOrPackageMedias },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
   props: {
   props: {
     invoiceId: {
     invoiceId: {
@@ -148,13 +219,112 @@ export default {
       default: null
       default: null
     }
     }
   },
   },
-  data: () => {
+  data () {
     return {
     return {
       user: new User(),
       user: new User(),
       wallet: new Wallet(),
       wallet: new Wallet(),
       invoice: new Invoice(),
       invoice: new Invoice(),
       defaultOptions: {
       defaultOptions: {
         showBackBtn: true
         showBackBtn: true
+      },
+
+      downloadDialog: false,
+      selectedBasketItem: null,
+      tableKeys: {
+        data: 'items_info.list',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+      tableInputs: [],
+      table: {
+        columns: [
+          {
+            name: 'number',
+            required: true,
+            label: 'شماره',
+            align: 'left',
+            field: () => ''
+          },
+          {
+            name: 'id',
+            required: true,
+            label: 'شناسه',
+            align: 'left',
+            field: row => row.product_info?.id ? row.product_info.id : row.package_info.id
+          },
+          {
+            name: 'title',
+            required: true,
+            label: 'نام محصول',
+            align: 'left',
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).title
+          },
+          {
+            name: 'is_package_or_product',
+            required: true,
+            label: 'نوع محصول',
+            align: 'left',
+            field: row => row.package ? 'بسته' : 'تکی'
+          },
+          {
+            name: 'store_category',
+            required: true,
+            label: 'دسته فروشگاه',
+            align: 'left',
+            field: row => ((row.product_info?.id ? row.product_info : row.package_info).store_category_info?.parent?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).store_category_info?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).store_category_info?.title || '-')
+          },
+          {
+            name: 'store_category1',
+            required: true,
+            label: 'دسته محتوا',
+            align: 'left',
+            field: row => ((row.product_info?.id ? row.product_info : row.package_info).content_category_info?.parent?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).content_category_info?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).content_category_info?.title || '-')
+          },
+          {
+            name: 'count',
+            required: true,
+            label: 'تعداد',
+            align: 'left',
+            field: row => row.count
+          },
+          {
+            name: 'is_physical_info',
+            required: true,
+            label: 'نوع جنس',
+            align: 'left',
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).is_physical_info.label
+          },
+          {
+            name: 'physical_type_info',
+            required: true,
+            label: 'نوع محصول',
+            align: 'left',
+            field: row => row.product_info ? (new Product(row.product_info)).physical_type_info.label : '-'
+          },
+          {
+            name: 'unit_price',
+            required: true,
+            label: 'قیمت',
+            align: 'left',
+            field: row => parseInt((row.product_info?.id ? row.product_info : row.package_info).unit_price?.toString()).toLocaleString('fa')
+          },
+          {
+            name: 'download',
+            required: true,
+            label: 'دانلود',
+            align: 'left',
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).is_physical
+          },
+          {
+            name: 'action',
+            required: true,
+            label: 'عملیات',
+            align: 'left',
+            field: row => ''
+          }
+        ]
       }
       }
     }
     }
   },
   },
@@ -164,6 +334,10 @@ export default {
     this.getInvoice()
     this.getInvoice()
   },
   },
   methods: {
   methods: {
+    openDownloadDialog (basketItem) {
+      this.selectedBasketItem = basketItem
+      this.downloadDialog = true
+    },
     loadAuthData() { // prevent Hydration node mismatch
     loadAuthData() { // prevent Hydration node mismatch
       this.user = this.$store.getters['Auth/user']
       this.user = this.$store.getters['Auth/user']
     },
     },

+ 4 - 2
src/components/Widgets/Invoice/InvoiceShow/InvoiceShow.vue

@@ -89,13 +89,13 @@
       </q-card-section>
       </q-card-section>
     </q-card>
     </q-card>
     <div class="action q-mt-lg">
     <div class="action q-mt-lg">
-      <q-card v-if="!invoice.loading && invoice.status!=='PAYING' && invoice.status!=='PAID_FULL' && invoice.status!=='PAID_IN_INSTALMENT'"
+      <q-card v-if="!invoice.loading && !(invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT')"
               class="q-mb-md">
               class="q-mb-md">
         <q-card-section>
         <q-card-section>
           تنها در صورتی که وضعیت سفارش در حال پرداخت باشد امکان پرداخت با کیف پول وجود دارد
           تنها در صورتی که وضعیت سفارش در حال پرداخت باشد امکان پرداخت با کیف پول وجود دارد
         </q-card-section>
         </q-card-section>
       </q-card>
       </q-card>
-      <q-card v-if="!invoice.loading && (invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT')">
+      <q-card v-if="!invoice.loading && (invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT' || invoice.status==='PAID_IN_INSTALMENT')">
         <q-card-section>
         <q-card-section>
           پرداخت
           پرداخت
         </q-card-section>
         </q-card-section>
@@ -105,6 +105,8 @@
                                 :invoice="invoice"
                                 :invoice="invoice"
                                 class="payment-card"
                                 class="payment-card"
                                 :can-action="localOptions.canAction"
                                 :can-action="localOptions.canAction"
+                                :show-message="invoice.status==='PAYING'"
+                                :show-wallet="invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT'"
                                 @onCancel="onCancel"
                                 @onCancel="onCancel"
                                 @onAccept="onAccept"
                                 @onAccept="onAccept"
                                 @onInstalmentPayed="onInstalmentPayed" />
                                 @onInstalmentPayed="onInstalmentPayed" />

+ 1 - 1
src/components/Widgets/Other/ClassroomCarousel/ClassroomCarousel.vue

@@ -82,7 +82,7 @@ export default {
         900: {
         900: {
           perPage: 2
           perPage: 2
         },
         },
-        500: {
+        600: {
           perPage: 1
           perPage: 1
         }
         }
       },
       },

+ 197 - 4
src/components/Widgets/Other/Shop/ShopCompleteInfo/ShopCompleteInfo.vue

@@ -150,6 +150,7 @@ import { Classroom } from 'src/models/Classroom.js'
 import { AnswerBook } from 'src/models/AnswerBook.js'
 import { AnswerBook } from 'src/models/AnswerBook.js'
 import { FormBuilderAssist } from 'quasar-form-builder'
 import { FormBuilderAssist } from 'quasar-form-builder'
 import ShowEventTest from './components/ShowEventTest.vue'
 import ShowEventTest from './components/ShowEventTest.vue'
+import { Provinces, Cities, getCitiesOfProvince } from 'src/assets/js/IranianCities.js'
 
 
 export default {
 export default {
   name: 'ShopCompleteInfo',
   name: 'ShopCompleteInfo',
@@ -157,6 +158,7 @@ export default {
   mixins: [mixinAuth],
   mixins: [mixinAuth],
   data: () => ({
   data: () => ({
     mounted: false,
     mounted: false,
+    inputDataLoaded: false,
     panel: 'userInfo',
     panel: 'userInfo',
     step: false,
     step: false,
     rulesAccept: false,
     rulesAccept: false,
@@ -233,18 +235,76 @@ export default {
       },
       },
       { type: 'checkbox', name: 'is_abroad_birth_address', placeholder: ' ', label: 'محل تولد خارج از کشور است؟', responseKey: 'is_abroad_birth_address', className: 'require', col: 'col-12' },
       { type: 'checkbox', name: 'is_abroad_birth_address', placeholder: ' ', label: 'محل تولد خارج از کشور است؟', responseKey: 'is_abroad_birth_address', className: 'require', col: 'col-12' },
       { type: 'input', name: 'birth_country', label: 'کشور محل تولد', responseKey: 'birth_country', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
       { type: 'input', name: 'birth_country', label: 'کشور محل تولد', responseKey: 'birth_country', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
-      { type: 'input', name: 'birth_province', label: 'استان محل تولد', responseKey: 'birth_province', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
-      { type: 'input', name: 'birth_city', label: 'شهر محل تولد', responseKey: 'birth_city', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
+      {
+        type: 'select',
+        name: 'birth_province',
+        options: Provinces,
+        optionLabel: 'name',
+        optionValue: 'name',
+        createNewValue: true,
+        newValueMode: 'add-unique',
+        label: 'استان محل تولد',
+        responseKey: 'birth_province',
+        placeholder: ' ',
+        className: 'require',
+        col: 'col-md-4 col-12'
+      },
+      {
+        type: 'select',
+        name: 'birth_city',
+        options: Cities,
+        optionLabel: 'name',
+        optionValue: 'name',
+        createNewValue: true,
+        newValueMode: 'add-unique',
+        label: 'شهر محل تولد',
+        responseKey: 'birth_city',
+        placeholder: ' ',
+        className: 'require',
+        col: 'col-md-4 col-12'
+      },
       { type: 'checkbox', name: 'is_abroad_living_address', placeholder: ' ', label: 'محل زندگی فعلی خارج از کشور است؟', responseKey: 'is_abroad_living_address', className: 'require', col: 'col-12' },
       { type: 'checkbox', name: 'is_abroad_living_address', placeholder: ' ', label: 'محل زندگی فعلی خارج از کشور است؟', responseKey: 'is_abroad_living_address', className: 'require', col: 'col-12' },
       { type: 'input', name: 'living_country', label: 'کشور محل زندگی فعلی', responseKey: 'living_country', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
       { type: 'input', name: 'living_country', label: 'کشور محل زندگی فعلی', responseKey: 'living_country', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
-      { type: 'input', name: 'living_province', label: 'استان محل زندگی فعلی', responseKey: 'living_province', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
-      { type: 'input', name: 'living_city', label: 'شهر محل زندگی فعلی', responseKey: 'living_city', placeholder: ' ', className: 'require', col: 'col-md-4 col-12' },
+      {
+        type: 'select',
+        name: 'living_province',
+        options: Provinces,
+        optionLabel: 'name',
+        optionValue: 'name',
+        createNewValue: true,
+        newValueMode: 'add-unique',
+        label: 'استان محل زندگی فعلی',
+        responseKey: 'living_province',
+        placeholder: ' ',
+        className: 'require',
+        col: 'col-md-4 col-12'
+      },
+      {
+        type: 'select',
+        name: 'living_city',
+        options: Cities,
+        optionLabel: 'name',
+        optionValue: 'name',
+        createNewValue: true,
+        newValueMode: 'add-unique',
+        label: 'شهر محل زندگی فعلی',
+        responseKey: 'living_city',
+        placeholder: ' ',
+        className: 'require',
+        col: 'col-md-4 col-12'
+      },
       { type: 'input', name: 'living_postal_code', label: 'کد پستی محل زندگی', responseKey: 'living_postal_code', placeholder: ' ', className: 'require', col: ' col-md-3 col-12' },
       { type: 'input', name: 'living_postal_code', label: 'کد پستی محل زندگی', responseKey: 'living_postal_code', placeholder: ' ', className: 'require', col: ' col-md-3 col-12' },
       { type: 'input', name: 'living_address', label: 'آدرس پستی محل زندگی', responseKey: 'living_address', placeholder: ' ', className: 'require', col: 'col-md-9 col-12' },
       { type: 'input', name: 'living_address', label: 'آدرس پستی محل زندگی', responseKey: 'living_address', placeholder: ' ', className: 'require', col: 'col-md-9 col-12' },
       { type: 'hidden', name: 'id', responseKey: 'id' }
       { type: 'hidden', name: 'id', responseKey: 'id' }
     ]
     ]
   }),
   }),
   computed: {
   computed: {
+    isAbroadBirthAddress () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'is_abroad_birth_address').value
+    },
+    isAbroadLivingAddress () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'is_abroad_living_address').value
+    },
     mainTitle () {
     mainTitle () {
       if (this.$route.query.source === 'event') {
       if (this.$route.query.source === 'event') {
         return 'ثبت نام در رویداد'
         return 'ثبت نام در رویداد'
@@ -258,6 +318,136 @@ export default {
       } else {
       } else {
         return 'مشاهده جزییات و قوانین دوره'
         return 'مشاهده جزییات و قوانین دوره'
       }
       }
+    },
+    selectedBirthProvince () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'birth_province').value
+    },
+    selectedLivingProvince () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'living_province').value
+    }
+  },
+  watch: {
+    isAbroadBirthAddress: {
+      immediate: true,
+      handler (newValue) {
+        this.$nextTick(() => {
+          if (!newValue) {
+            FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'value', 'ایران')
+            FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'readonly', true)
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'type', 'select')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'options', Provinces)
+
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'select')
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', true)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'options', getCitiesOfProvince(this.selectedBirthProvince))
+            }
+          } else {
+            // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'value', null)
+            FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'readonly', false)
+
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'disable', false)
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'value', null)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+            }
+          }
+        })
+      }
+    },
+    isAbroadLivingAddress: {
+      immediate: true,
+      handler (newValue) {
+        this.$nextTick(() => {
+          if (!newValue) {
+            FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'value', 'ایران')
+            FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'readonly', true)
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'type', 'select')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'options', Provinces)
+
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'select')
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', true)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'options', getCitiesOfProvince(this.selectedLivingProvince))
+            }
+          } else {
+            // FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'value', null)
+            FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'readonly', false)
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'disable', false)
+
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'value', null)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+            }
+          }
+        })
+      }
+    },
+    selectedBirthProvince (newValue) {
+      if (this.isAbroadBirthAddress) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'input')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+        return
+      }
+
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', true)
+        if (this.inputDataLoaded) {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+        }
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedBirthProvince)
+
+      FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+      }
+    },
+    selectedLivingProvince (newValue) {
+      if (this.isAbroadBirthAddress) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'input')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+        return
+      }
+
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', true)
+        if (this.inputDataLoaded) {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+        }
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedLivingProvince)
+
+      FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+      }
     }
     }
   },
   },
   mounted () {
   mounted () {
@@ -385,6 +575,9 @@ export default {
     },
     },
     afterLoadInputData () {
     afterLoadInputData () {
       this.checkNationalCode()
       this.checkNationalCode()
+      this.$nextTick(() => {
+        this.inputDataLoaded = true
+      })
     },
     },
     checkNationalCode () {
     checkNationalCode () {
       if (FormBuilderAssist.getInputsByName(this.inputs, 'national_code')?.value) {
       if (FormBuilderAssist.getInputsByName(this.inputs, 'national_code')?.value) {

+ 192 - 0
src/components/Widgets/Post/LastPosts/LastPosts.vue

@@ -0,0 +1,192 @@
+<template>
+  <div class="LastPostsCarousel">
+    <div class="LastPostsCarousel-title">
+      <div>
+        <q-banner class="banner">
+          آخرین
+          اخبار
+        </q-banner>
+      </div>
+      <div>
+        <q-btn flat
+               :to="{name: 'Public.Post.List.News'}">
+          مشاهده همه
+          <svg xmlns="http://www.w3.org/2000/svg"
+               width="18.387"
+               height="11.502"
+               viewBox="0 0 18.387 11.502"
+               class="q-ml-sm">
+            <path id="Combined_Shape"
+                  data-name="Combined Shape"
+                  d="M-9.338,11.408a.748.748,0,0,0,.388-.656V6.5h8.2A.751.751,0,0,0,0,5.75.75.75,0,0,0-.75,5h-8.2V.75A.751.751,0,0,0-9.338.093.769.769,0,0,0-9.7,0a.735.735,0,0,0-.4.115l-7.938,5a.747.747,0,0,0-.35.635.742.742,0,0,0,.35.634l7.938,5a.751.751,0,0,0,.4.116A.746.746,0,0,0-9.338,11.408ZM-10.45,9.392l-5.78-3.641,5.78-3.642Z"
+                  transform="translate(18.387)"
+                  fill="#eac38a" />
+          </svg>
+        </q-btn>
+      </div>
+    </div>
+    <div class="carousel-section">
+      <div v-if="!posts.loading && posts.list.length > 0"
+           ref="slider"
+           class="splide"
+           role="group"
+           aria-label="Splide Basic HTML Example">
+        <div class="splide__track">
+          <ul class="splide__list">
+            <li v-for="(post, postIndex) in posts.list"
+                :key="postIndex"
+                class="splide__slide">
+              <post-item :post="post" />
+            </li>
+          </ul>
+        </div>
+      </div>
+      <div v-else-if="!posts.loading && posts.list.length === 0">
+        رویدادی یافت نشد.
+      </div>
+      <div v-else-if="posts.loading">
+        کمی صبر کنید ...
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Splide from '@splidejs/splide'
+import { PostList } from 'src/models/Post.js'
+import PostItem from './components/PostItem.vue'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import '@splidejs/splide/dist/css/splide-core.min.css'
+import { mixinPrefetchServerData, mixinWidget } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'LastPosts',
+  components: {
+    PostItem
+  },
+  mixins: [mixinPrefetchServerData, mixinWidget],
+  data () {
+    return {
+      loading: false,
+      slider: null,
+      posts: new PostList(),
+      defaultOptions: {
+        categoryId: null,
+        categoryCategoryId: null,
+        categoryCategoryCategoryId: null,
+        categoryFromQueryParams: false,
+        categoryCategoryFromQueryParams: false,
+        categoryCategoryCategoryFromQueryParams: false
+      },
+      breakpoints: {
+        1920: {
+          perPage: 1
+        },
+        900: {
+          perPage: 1
+        },
+        600: {
+          perPage: 1
+        }
+      },
+      maximizedToggle: true
+    }
+  },
+  computed: {
+    postCategoryId () {
+      if (this.localOptions.categoryId) {
+        return this.localOptions.categoryId
+      }
+
+      return null
+    }
+  },
+  mounted () {
+    this.loadSlider()
+  },
+  methods: {
+    loadSlider () {
+      this.$nextTick(() => {
+        if (!this.$refs.slider) {
+          return
+        }
+        new Splide(this.$refs.slider, {
+          direction: 'rtl',
+          paginationDirection: 'rtl',
+          // type: 'loop',
+          focus: 0,
+          gap: 24,
+          snap: true,
+          // focus: 'center',
+          breakpoints: this.breakpoints
+        }).mount()
+      })
+    },
+    prefetchServerDataPromise () {
+      return this.getLastPosts()
+    },
+    prefetchServerDataPromiseThen (postList) {
+      this.posts = new PostList(postList.list)
+      this.posts.loading = false
+      this.loadSlider()
+    },
+    prefetchServerDataPromiseCatch () {
+      this.posts.loading = false
+    },
+
+    getTerm (event) {
+      if (!event.beginning_registration_period) {
+        return '-'
+      }
+      return ShamsiDate.getTerm(event.beginning_registration_period)
+    },
+    getLastPosts () {
+      const params = {}
+
+      if (this.localOptions.categoryCategoryCategoryId) {
+        params.category__parent__parent = this.localOptions.categoryCategoryCategoryId
+      }
+
+      if (this.localOptions.categoryCategoryId) {
+        params.category__parent = this.localOptions.categoryCategoryId
+      }
+
+      if (this.localOptions.categoryId) {
+        params.category = this.localOptions.categoryId
+      }
+
+      this.posts.loading = true
+      return APIGateway.post.index(params)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.LastPostsCarousel {
+  padding: 60px 0;
+  .LastPostsCarousel-title {
+    display: flex;
+    flex-flow: row;
+    justify-content: space-between;
+    padding-bottom: 33px;
+  }
+  :deep(.carousel-section) {
+    .splide__arrows {
+      top: auto;
+      bottom: 0;
+      .splide__arrow {
+        &.splide__arrow--prev {
+          left: 50%;
+          transform: translateX(-120%);
+        }
+        &.splide__arrow--next {
+          right: 50%;
+          transform: translateX(120%);
+        }
+      }
+    }
+  }
+}
+</style>

+ 65 - 0
src/components/Widgets/Post/LastPosts/OptionPanel.vue

@@ -0,0 +1,65 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <div class="q-mt-md">
+        کد دسته پدربزرگ
+      </div>
+      <div class="row">
+        <div class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryCategoryCategoryId" />
+        </div>
+      </div>
+      <div>
+        کد دسته پدر
+      </div>
+      <div class="row">
+        <div class="col-md-4 col-12">
+          <q-checkbox v-model="localOptions.categoryCategoryFromQueryParams"
+                      label="استفاده از کوری پارام صفحه" />
+        </div>
+        <div v-if="!localOptions.categoryCategoryFromQueryParams"
+             class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryCategoryId" />
+        </div>
+      </div>
+      <div>
+        کد دسته فرزند
+      </div>
+      <div class="row">
+        <div class="col-md-4 col-12">
+          <q-checkbox v-model="localOptions.categoryFromQueryParams"
+                      label="استفاده از کوری پارام صفحه" />
+        </div>
+        <div v-if="!localOptions.categoryFromQueryParams"
+             class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryId" />
+        </div>
+      </div>
+    </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],
+  data () {
+    return {
+      defaultOptions: {
+        categoryId: null,
+        categoryCategoryId: null,
+        categoryCategoryCategoryId: null,
+        categoryFromQueryParams: false,
+        categoryCategoryFromQueryParams: false
+      }
+    }
+  }
+})
+</script>

+ 149 - 0
src/components/Widgets/Post/LastPosts/components/PostItem.vue

@@ -0,0 +1,149 @@
+<template>
+  <div class="col-md-4 col-sm-6 col-12">
+    <q-card flat
+            class="PostItem">
+      <div class="content">
+        <div class="title-and-body">
+          <div class="title ellipsis-2-lines">
+            <router-link :to="{name: 'Public.Post.Show', params: {id: post.id}}">
+              {{post.title}}
+            </router-link>
+          </div>
+          <div class="body"
+               v-html="post.text" />
+        </div>
+        <div class="meta-and-action">
+          <div class="date-time">
+            <q-icon name="calendar_month"
+                    size="20px" />
+            <span class="date-time-value">
+              {{ getDateTime(post.creation_time) }}
+            </span>
+          </div>
+          <q-btn color="primary"
+                 outline
+                 class="btn-show-post"
+                 :to="{name: 'Public.Post.Show', params: {id: post.id}}">
+            مشاهده جزئیات
+          </q-btn>
+        </div>
+      </div>
+      <div class="thumbnail">
+        <router-link :to="{name: 'Public.Post.Show', params: {id: post.id}}">
+          <q-img :src="post.thumbnail" />
+        </router-link>
+      </div>
+    </q-card>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { Post } from 'src/models/Post.js'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+
+export default defineComponent({
+  name: 'PostItem',
+  props: {
+    post: {
+      type: Post,
+      default: new Post()
+    }
+  },
+  methods: {
+    getDateTime (dateTime) {
+      return ShamsiDate.getTitledDateTime(dateTime, 'a hh:mm - jYYYY/jMM/jDD')
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.PostItem {
+  display: flex;
+  flex-flow: row;
+  justify-content: space-between;
+  background-color: white;
+  border-radius: 8px;
+  padding: 0;
+  margin: 0;
+  $thumbnail-width: 400px;
+  .thumbnail {
+    //width: 45%;
+    width: $thumbnail-width;
+    .q-img {
+      border-radius: 0 8px 8px 0;
+    }
+  }
+  .content {
+    //width: 55%;
+    width: calc( 100% - #{$thumbnail-width} );
+    padding: 52px 80px 52px 40px;
+    display: flex;
+    flex-flow: column;
+    justify-content: space-between;
+    .title-and-body {
+      margin-bottom: 28px;
+      .title {
+        font-size: 18px;
+        letter-spacing: 0;
+        margin-bottom: 26px;
+        a {
+          color: #212121;
+        }
+      }
+      .body {
+        font-size: 16px;
+        letter-spacing: 0;
+        color: #212121;
+        height: 220px;
+        overflow: hidden;
+      }
+    }
+    .meta-and-action {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      .date-time {
+        color: #929292;
+        .date-time-value {
+          margin-left: 8px;
+        }
+      }
+    }
+  }
+  @media screen and (max-width: 1024px) {
+    & {
+      flex-flow: column;
+      .thumbnail {
+        order: 1;
+        width: 100%;
+        margin-bottom: 8px;
+        .q-img {
+          border-radius: 8px;
+        }
+      }
+      .content {
+        order: 2;
+        width: 100%;
+        padding: 24px !important;
+      }
+    }
+  }
+  @media screen and (max-width: 600px) {
+    & {
+      .content {
+        .meta-and-action {
+          flex-flow: column;
+          justify-content: space-between;
+          align-items: flex-start;
+          gap: 8px;
+          .q-btn {
+            width: 100%;
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 6 - 0
src/components/Widgets/Post/PostShow/PostShow.vue

@@ -292,10 +292,16 @@ export default {
     .summary-body {
     .summary-body {
       font-size: 20px;
       font-size: 20px;
       color: #707070;
       color: #707070;
+      img {
+        max-width: 100% !important;
+      }
     }
     }
   }
   }
   :deep(.post-body) {
   :deep(.post-body) {
     margin-top: 32px;
     margin-top: 32px;
+    img {
+      max-width: 100% !important;
+    }
     .barehtml {
     .barehtml {
       width: 600px;
       width: 600px;
       max-width: 100%;
       max-width: 100%;

+ 1 - 1
src/components/Widgets/ProjectAttendanceSheet/ProjectAttendanceSheetShow/ProjectAttendanceSheetShow.vue

@@ -88,7 +88,7 @@
           تنها در صورتی که وضعیت سفارش در حال پرداخت باشد امکان پرداخت با کیف پول وجود دارد
           تنها در صورتی که وضعیت سفارش در حال پرداخت باشد امکان پرداخت با کیف پول وجود دارد
         </q-card-section>
         </q-card-section>
       </q-card>
       </q-card>
-      <q-card v-if="!invoice.loading && (invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT')">
+      <q-card v-if="!invoice.loading && (invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT' || invoice.status==='PAID_IN_INSTALMENT')">
         <q-card-section>
         <q-card-section>
           پرداخت
           پرداخت
         </q-card-section>
         </q-card-section>

+ 1 - 1
src/components/Widgets/Registration/RegistrationShow/RegistrationShow.vue

@@ -88,7 +88,7 @@
           تنها در صورتی که وضعیت سفارش در حال پرداخت باشد امکان پرداخت با کیف پول وجود دارد
           تنها در صورتی که وضعیت سفارش در حال پرداخت باشد امکان پرداخت با کیف پول وجود دارد
         </q-card-section>
         </q-card-section>
       </q-card>
       </q-card>
-      <q-card v-if="!invoice.loading && (invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT')">
+      <q-card v-if="!invoice.loading && (invoice.status==='PAYING' || invoice.status==='PAYING_IN_INSTALMENT' || invoice.status==='PAID_IN_INSTALMENT')">
         <q-card-section>
         <q-card-section>
           پرداخت
           پرداخت
         </q-card-section>
         </q-card-section>

+ 48 - 33
src/components/Widgets/Statictic/PublicWebStatistic/PublicWebStatistic.vue

@@ -7,48 +7,63 @@
         </q-banner>
         </q-banner>
       </div>
       </div>
     </div>
     </div>
-    <div class="PublicWebStatistic-boxes row">
-      <div class="PublicWebStatistic-boxes row" />
+    <div class="PublicWebStatistic-boxes">
+      <q-linear-progress v-if="loading"
+                         indeterminate />
+      <div v-else
+           class="row q-col-gutter-md">
+        <div class="col-md-4 col-12">
+          <statistic-item :value="statistics.users_count"
+                          title="کاربر فعال"
+                          icon="groups" />
+        </div>
+        <div class="col-md-4 col-12">
+          <statistic-item :value="statistics.training_classrooms_count"
+                          title="دوره"
+                          icon="co_present" />
+        </div>
+        <div class="col-md-4 col-12">
+          <statistic-item :value="statistics.event_classrooms_count"
+                          title="رویداد"
+                          icon="event_note" />
+        </div>
+      </div>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
-import { User } from 'src/models/User.js'
-// import ShamsiDate from 'src/assets/ShamsiDate.js'
-// import { APIGateway } from 'src/api/APIGateway.js'
-import { ClassroomList } from 'src/models/Classroom.js'
-import { ClassroomRegistrationList } from 'src/models/ClassroomRegistration.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import StatisticItem from './components/Item.vue'
 
 
 export default {
 export default {
   name: 'PublicWebStatistic',
   name: 'PublicWebStatistic',
-  data: () => ({
-    loading: false,
-    user: new User(),
-    classroomsKey: Date.now(),
-    userRegistrations: new ClassroomRegistrationList(),
-    slide: 0,
-    classrooms: new ClassroomList(),
-    breakpoints: {
-      // 1024 and up
-      1024: {
-        itemsToShow: 3,
-        snapAlign: 'start'
-      },
-      // 700px and up
-      700: {
-        itemsToShow: 3.5,
-        snapAlign: 'center'
-      },
-      // 300px and up
-      300: {
-        itemsToShow: 1.5,
-        snapAlign: 'center'
+  components: { StatisticItem },
+  data () {
+    return {
+      loading: false,
+      statistics: {
+        users_count: 0,
+        training_classrooms_count: 0,
+        event_classrooms_count: 0
       }
       }
-    },
-    maximizedToggle: true,
-    dialog: false
-  })
+    }
+  },
+  mounted() {
+    this.getStatistics()
+  },
+  methods: {
+    getStatistics () {
+      this.loading = true
+      APIGateway.aggregator.statistics()
+        .then((statistics) => {
+          this.statistics = statistics
+        })
+        .finally(() => {
+          this.loading = false
+        })
+    }
+  }
 }
 }
 </script>
 </script>
 
 

+ 68 - 0
src/components/Widgets/Statictic/PublicWebStatistic/components/Item.vue

@@ -0,0 +1,68 @@
+<template>
+  <div class="item">
+    <div class="item-icon">
+      <q-icon :name="icon" />
+    </div>
+    <div class="item-content">
+      <div class="item-title">
+        {{ title }}
+      </div>
+      <div class="item-value">
+        {{ value }}
+        +
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'StatisticItem',
+  props: {
+    title: {
+      type: String,
+      default: undefined
+    },
+    value: {
+      type: String,
+      default: undefined
+    },
+    icon: {
+      type: String,
+      default: undefined
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.item {
+  border-radius: 20px;
+  background: #FBF4EA;
+  padding: 64px 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 20px;
+  .item-icon {
+    color: #EAC38A;
+    width: 70px;
+    .q-icon {
+      font-size: 70px;
+    }
+  }
+  .item-content {
+    width: fit-content;
+    .item-title {
+      font-size: 18px;
+      color: #212121;
+      margin-bottom: 8px;
+    }
+    .item-value {
+      font-size: 18px;
+      font-weight: bold;
+      color: #212121;
+    }
+  }
+}
+</style>

+ 53 - 10
src/components/Widgets/Store/Basket/AdminStoreBasketList/AdminStoreBasketList.vue

@@ -3,12 +3,13 @@
        :style="localOptions.style">
        :style="localOptions.style">
     <div v-if="showBackBtn"
     <div v-if="showBackBtn"
          class="more-action">
          class="more-action">
-      <q-btn color="primary">
-        <q-icon name="file_download" />
-        دریافت
-      </q-btn>
+         <!--      <q-btn color="primary">-->
+         <!--        <q-icon name="file_download" />-->
+         <!--        دریافت-->
+         <!--      </q-btn>-->
     </div>
     </div>
     <entity-index v-if="mounted"
     <entity-index v-if="mounted"
+                  ref="entityIndex"
                   v-model:value="inputs"
                   v-model:value="inputs"
                   title="لیست سفارشات"
                   title="لیست سفارشات"
                   :api="api"
                   :api="api"
@@ -41,11 +42,15 @@
 </template>
 </template>
 
 
 <script>
 <script>
+import { shallowRef } from 'vue'
+import Assist from 'assets/js/Assist.js'
 import { EntityIndex } from 'quasar-crud'
 import { EntityIndex } from 'quasar-crud'
 import { Basket } from 'src/models/Basket.js'
 import { Basket } from 'src/models/Basket.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
-import Assist from 'assets/js/Assist'
+import BtnControl from 'src/components/Control/btn.vue'
+
+const BtnControlComp = shallowRef(BtnControl)
 
 
 export default {
 export default {
   name: 'AdminBasketList',
   name: 'AdminBasketList',
@@ -67,7 +72,40 @@ export default {
         perPage: 'per_page',
         perPage: 'per_page',
         pageKey: 'page'
         pageKey: 'page'
       },
       },
-      inputs: [],
+      inputs: [
+        {
+          type: 'input',
+          name: 'search',
+          label: 'جست جو',
+          placeholder: ' ',
+          col: 'col-md-4 col-12'
+        },
+        {
+          type: 'select',
+          name: 'order_type',
+          label: 'نوع سفارش',
+          options: (new Basket()).order_typeEnums,
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        {
+          type: 'select',
+          name: 'status',
+          label: 'وضعیت',
+          options: [
+            {
+              label: 'پرداخت شده',
+              value: 'PAID'
+            },
+            {
+              label: 'ارسال شده',
+              value: 'SENT'
+            }],
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: this.search, col: 'col-md-2 col-12' }
+      ],
       table: {
       table: {
         columns: [
         columns: [
           {
           {
@@ -103,12 +141,12 @@ export default {
             required: true,
             required: true,
             label: 'نوع سفارش',
             label: 'نوع سفارش',
             align: 'left',
             align: 'left',
-            field: () => '-'
+            field: (row) => row.order_type ? new Basket(row).order_type_info.label : '-'
           },
           },
           {
           {
             name: 'overall_order_price',
             name: 'overall_order_price',
             required: true,
             required: true,
-            label: 'قیمت کل(تومان)',
+            label: 'قیمت کل(ریال)',
             align: 'left',
             align: 'left',
             field: row => parseInt(row.overall_order_price).toLocaleString('fa')
             field: row => parseInt(row.overall_order_price).toLocaleString('fa')
           },
           },
@@ -120,11 +158,11 @@ export default {
             field: row => (new Basket(row)).status_info.label
             field: row => (new Basket(row)).status_info.label
           },
           },
           {
           {
-            name: 'creation_time',
+            name: 'order_time',
             required: true,
             required: true,
             label: 'تاریخ ثبت',
             label: 'تاریخ ثبت',
             align: 'left',
             align: 'left',
-            field: row => Assist.miladiToShamsi(row.creation_time)
+            field: row => Assist.miladiToShamsi(row.order_time)
           },
           },
           {
           {
             name: 'action',
             name: 'action',
@@ -141,6 +179,11 @@ export default {
   },
   },
   mounted() {
   mounted() {
     this.mounted = true
     this.mounted = true
+  },
+  methods: {
+    search () {
+      this.$refs.entityIndex.search()
+    }
   }
   }
 }
 }
 </script>
 </script>

+ 135 - 0
src/components/Widgets/Store/Basket/AdminStoreBasketPurchaseRegulations/AdminStoreBasketPurchaseRegulations.vue

@@ -0,0 +1,135 @@
+<template>
+  <div class="AdminPostCreate"
+       :style="localOptions.style">
+    <div class="flex justify-end">
+      <q-btn flat
+             color="grey"
+             @click="$router.go(-1)">
+        بازگشت
+        >
+      </q-btn>
+    </div>
+    <entity-create v-if="mounted"
+                   ref="entityCreate"
+                   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" />
+  </div>
+</template>
+
+<script>
+import { shallowRef } from 'vue'
+import { EntityCreate } from 'quasar-crud'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import BtnControl from 'src/components/Control/btn.vue'
+import PostMixin from 'src/components/Widgets/Post/PostMixin.js'
+import FormBuilderTiptapEditor from 'src/components/FormBuilderCustumComponents/FormBuilderTiptapEditor.vue'
+import { FormBuilderAssist } from 'quasar-form-builder'
+
+const BtnControlComp = shallowRef(BtnControl)
+const FormBuilderTiptapEditorComp = shallowRef(FormBuilderTiptapEditor)
+
+export default {
+  name: 'AdminPostCreate',
+  components: { EntityCreate },
+  mixins: [mixinWidget, PostMixin],
+  data () {
+    const authorization = 'Bearer ' + this.$store.getters['Auth/accessToken']
+    const uploadVideo = this.uploadVideo
+    const uploadAudio = this.uploadAudio
+    return {
+      mounted: false,
+      entityLoading: false,
+      api: APIGateway.post.APIAdresses.base,
+      entityIdKey: 'id',
+      entityParamKey: 'id',
+      showRouteName: 'Admin.Post.Show',
+      inputs: [
+        {
+          type: FormBuilderTiptapEditorComp,
+          name: 'text',
+          responseKey: 'text',
+          label: 'متن آیین نامه خرید',
+          options: {
+            bubbleMenu: false,
+            floatingMenu: false,
+            poem: false,
+            reading: false,
+            loadBareHtml: true,
+            persianKeyboard: true,
+            mathliveOptions: { smartFence: false },
+            uploadServer: {
+              url: '/api' + APIGateway.media.APIAdresses.base,
+              instantUpload: true,
+              responseKey: 'file',
+              headers: { Authorization: authorization }
+            },
+            uploadVideo,
+            uploadAudio
+          },
+          col: 'col-md-12 col-12'
+        },
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'ثبت', placeholder: ' ', atClick: this.onSubmit, col: 'col-md-6' }
+      ]
+    }
+  },
+  mounted () {
+    this.getPurchaseRegulations()
+  },
+  methods: {
+    getPurchaseRegulations () {
+      this.purchaseRegulationsLoading = true
+      APIGateway.appSetting.get('PurchaseRegulations')
+        .then((purchaseRegulations) => {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'text', 'value', purchaseRegulations.value)
+          this.mounted = true
+        })
+        .finally(() => {
+          this.purchaseRegulationsLoading = false
+        })
+    },
+    onSubmit () {
+      const formData = FormBuilderAssist.getFormData(this.inputs)
+      this.purchaseRegulationsLoading = true
+      APIGateway.appSetting.update({ key: 'PurchaseRegulations', value: formData.text })
+        .then((purchaseRegulations) => {
+          this.purchaseRegulations = purchaseRegulations
+        })
+        .finally(() => {
+          this.purchaseRegulationsLoading = false
+        })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.AdminPostCreate {
+  .title {
+    font-style: normal;
+    font-weight: 700;
+    font-size: 24px;
+    line-height: 140%;
+    color: #424242;
+    margin-bottom: 27px;
+    position: relative;
+    .back-action {
+      position: absolute;
+      right: 0;
+      top: 0;
+    }
+  }
+  :deep(.form) {
+    padding: 24px;
+  }
+}
+</style>

+ 21 - 0
src/components/Widgets/Store/Basket/AdminStoreBasketPurchaseRegulations/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>

+ 56 - 11
src/components/Widgets/Store/Basket/AdminStoreBasketShow/AdminStoreBasketShow.vue

@@ -61,6 +61,18 @@
             <q-img :src="inputData.col.value"
             <q-img :src="inputData.col.value"
                    width="100px" />
                    width="100px" />
           </template>
           </template>
+          <template v-else-if="inputData.col.name === 'download'">
+            <q-btn v-if="!inputData.col.value"
+                   color="primary"
+                   @click="openDownloadDialog(inputData.props.row)">
+              <q-icon name="download"
+                      class="q-mr-sm" />
+              دانلود
+            </q-btn>
+            <template v-else>
+              {{}}
+            </template>
+          </template>
           <template v-else-if="inputData.col.name === 'action'">
           <template v-else-if="inputData.col.name === 'action'">
             <q-btn color="primary"
             <q-btn color="primary"
                    outline
                    outline
@@ -75,6 +87,17 @@
           </template>
           </template>
         </template>
         </template>
       </entity-index>
       </entity-index>
+      <q-dialog v-model="downloadDialog">
+        <q-card style="width: 800px; max-width: 95%;">
+          <q-card-section>
+            محتوای قابل دانلود
+          </q-card-section>
+          <q-separator />
+          <q-card-section>
+            <product-or-package-medias :basket-item="selectedBasketItem" />
+          </q-card-section>
+        </q-card>
+      </q-dialog>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
@@ -83,13 +106,14 @@
 import { shallowRef } from 'vue'
 import { shallowRef } from 'vue'
 import { Basket } from 'src/models/Basket.js'
 import { Basket } from 'src/models/Basket.js'
 import { Product } from 'src/models/Product.js'
 import { Product } from 'src/models/Product.js'
-import { Package } from 'src/models/Package.js'
+// import { Package } from 'src/models/Package.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import BtnControl from 'components/Control/btn.vue'
 import BtnControl from 'components/Control/btn.vue'
 import { EntityShow, EntityIndex } from 'quasar-crud'
 import { EntityShow, EntityIndex } from 'quasar-crud'
 import { FormBuilderAssist } from 'quasar-form-builder'
 import { FormBuilderAssist } from 'quasar-form-builder'
 import FormBuilderDateTime from 'src/components/FormBuilderCustumComponents/FormBuilderDateTime.vue'
 import FormBuilderDateTime from 'src/components/FormBuilderCustumComponents/FormBuilderDateTime.vue'
+import ProductOrPackageMedias from 'src/components/Widgets/Store/Basket/UserStoreBasketShow/ProductOrPackageMedias.vue'
 
 
 const BtnControlComp = shallowRef(BtnControl)
 const BtnControlComp = shallowRef(BtnControl)
 const FormBuilderDateTimeComp = shallowRef(FormBuilderDateTime)
 const FormBuilderDateTimeComp = shallowRef(FormBuilderDateTime)
@@ -98,13 +122,16 @@ export default {
   name: 'AdminStoreBasketShow',
   name: 'AdminStoreBasketShow',
   components: {
   components: {
     EntityShow,
     EntityShow,
-    EntityIndex
+    EntityIndex,
+    ProductOrPackageMedias
   },
   },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
   data () {
   data () {
     return {
     return {
       mounted: false,
       mounted: false,
       entityLoading: false,
       entityLoading: false,
+      downloadDialog: false,
+      selectedBasketItem: null,
       api: APIGateway.basket.APIAdresses.byId(this.$route.params.id),
       api: APIGateway.basket.APIAdresses.byId(this.$route.params.id),
       entityIdKey: 'id',
       entityIdKey: 'id',
       entityParamKey: 'id',
       entityParamKey: 'id',
@@ -133,14 +160,14 @@ export default {
             required: true,
             required: true,
             label: 'شناسه',
             label: 'شناسه',
             align: 'left',
             align: 'left',
-            field: row => row.id
+            field: row => row.product_info?.id ? row.product_info.id : row.package_info.id
           },
           },
           {
           {
             name: 'title',
             name: 'title',
             required: true,
             required: true,
             label: 'نام محصول',
             label: 'نام محصول',
             align: 'left',
             align: 'left',
-            field: row => (row.product_info || row.package_info).title
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).title
           },
           },
           {
           {
             name: 'is_package_or_product',
             name: 'is_package_or_product',
@@ -150,11 +177,18 @@ export default {
             field: row => row.package ? 'بسته' : 'تکی'
             field: row => row.package ? 'بسته' : 'تکی'
           },
           },
           {
           {
-            name: 'content_category',
+            name: 'store_category',
+            required: true,
+            label: 'دسته فروشگاه',
+            align: 'left',
+            field: row => ((row.product_info?.id ? row.product_info : row.package_info).store_category_info?.parent?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).store_category_info?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).store_category_info?.title || '-')
+          },
+          {
+            name: 'store_category1',
             required: true,
             required: true,
-            label: 'دسته بندی اصلی',
+            label: 'دسته محتوا',
             align: 'left',
             align: 'left',
-            field: row => row.content_category
+            field: row => ((row.product_info?.id ? row.product_info : row.package_info).content_category_info?.parent?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).content_category_info?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).content_category_info?.title || '-')
           },
           },
           {
           {
             name: 'count',
             name: 'count',
@@ -168,7 +202,7 @@ export default {
             required: true,
             required: true,
             label: 'نوع جنس',
             label: 'نوع جنس',
             align: 'left',
             align: 'left',
-            field: row => row.product_info ? (new Product(row.product_info)).is_physical_info.label : (new Package(row.product_info)).is_physical_info.label
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).is_physical_info.label
           },
           },
           {
           {
             name: 'physical_type_info',
             name: 'physical_type_info',
@@ -182,7 +216,14 @@ export default {
             required: true,
             required: true,
             label: 'قیمت',
             label: 'قیمت',
             align: 'left',
             align: 'left',
-            field: row => parseInt((row.product_info || row.package_info).unit_price.toString()).toLocaleString('fa')
+            field: row => parseInt((row.product_info?.id ? row.product_info : row.package_info).unit_price?.toString()).toLocaleString('fa')
+          },
+          {
+            name: 'download',
+            required: true,
+            label: 'دانلود',
+            align: 'left',
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).is_physical
           },
           },
           {
           {
             name: 'action',
             name: 'action',
@@ -233,8 +274,8 @@ export default {
       { type: 'separator', name: 'space1', label: 'مشخصات سفارش', className: 'custom-separator', col: 'col-12' },
       { type: 'separator', name: 'space1', label: 'مشخصات سفارش', className: 'custom-separator', col: 'col-12' },
       { type: 'input', name: 'id', responseKey: 'id', label: 'شناسه', placeholder: ' ', col: 'col-md-2 col-12' },
       { type: 'input', name: 'id', responseKey: 'id', label: 'شناسه', placeholder: ' ', col: 'col-md-2 col-12' },
       { type: 'input', name: 'items_info.length', responseKey: 'items_info.length', label: 'تعداد اقلام', placeholder: ' ', col: 'col-md-2 col-12' },
       { type: 'input', name: 'items_info.length', responseKey: 'items_info.length', label: 'تعداد اقلام', placeholder: ' ', col: 'col-md-2 col-12' },
-      { type: FormBuilderDateTimeComp, name: 'creation_time', responseKey: 'creation_time', outsideLabel: 'تاریخ ثبت', placeholder: ' ', disable: true, col: 'col-md-3 col-12' },
-      { type: 'input', name: 'overall_order_price', responseKey: 'overall_order_price', label: 'قیمت کل(تومان)', placeholder: ' ', col: 'col-md-3 col-12' },
+      { type: FormBuilderDateTimeComp, name: 'creation_time', responseKey: 'creation_time', outsideLabel: 'تاریخ ثبت', placeholder: ' ', disable: true, col: 'col-md-6 col-12' },
+      { type: 'input', name: 'overall_order_price', responseKey: 'overall_order_price', label: 'قیمت کل(ریال)', placeholder: ' ', col: 'col-md-2 col-12' },
       { type: BtnControlComp, name: 'btnSent', responseKey: 'btnSent', label: 'ارسال', placeholder: ' ', outline: true, ignoreValue: true, atClick: this.sendBasket, loading: this.entityLoading, col: 'col-md-2 col-12' }
       { type: BtnControlComp, name: 'btnSent', responseKey: 'btnSent', label: 'ارسال', placeholder: ' ', outline: true, ignoreValue: true, atClick: this.sendBasket, loading: this.entityLoading, col: 'col-md-2 col-12' }
     ]
     ]
   },
   },
@@ -242,6 +283,10 @@ export default {
     this.mounted = true
     this.mounted = true
   },
   },
   methods: {
   methods: {
+    openDownloadDialog (basketItem) {
+      this.selectedBasketItem = basketItem
+      this.downloadDialog = true
+    },
     showUserPage () {
     showUserPage () {
       const routeData = this.$router.resolve({ name: 'Admin.User.Show', params: { id: this.basket.owner } })
       const routeData = this.$router.resolve({ name: 'Admin.User.Show', params: { id: this.basket.owner } })
       window.open(routeData.href, '_blank')
       window.open(routeData.href, '_blank')

+ 113 - 0
src/components/Widgets/Store/Basket/AdminStoreDeliveryCost/AdminStoreDeliveryCost.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="AdminPostCreate"
+       :style="localOptions.style">
+    <div class="flex justify-end">
+      <q-btn flat
+             color="grey"
+             @click="$router.go(-1)">
+        بازگشت
+        >
+      </q-btn>
+    </div>
+    <entity-create v-if="mounted"
+                   ref="entityCreate"
+                   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" />
+  </div>
+</template>
+
+<script>
+import { shallowRef } from 'vue'
+import { EntityCreate } from 'quasar-crud'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import BtnControl from 'src/components/Control/btn.vue'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import PostMixin from 'src/components/Widgets/Post/PostMixin.js'
+
+const BtnControlComp = shallowRef(BtnControl)
+
+export default {
+  name: 'AdminPostCreate',
+  components: { EntityCreate },
+  mixins: [mixinWidget, PostMixin],
+  data () {
+    return {
+      mounted: false,
+      entityLoading: false,
+      api: null,
+      entityIdKey: 'id',
+      entityParamKey: 'id',
+      showRouteName: null,
+      inputs: [
+        {
+          type: 'input',
+          name: 'text',
+          responseKey: 'text',
+          label: 'هزینه ارسال با پست پیشتاز',
+          col: 'col-md-12 col-12'
+        },
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'ثبت', placeholder: ' ', atClick: this.onSubmit, col: 'col-md-6' }
+      ]
+    }
+  },
+  mounted () {
+    this.getPurchaseRegulations()
+  },
+  methods: {
+    getPurchaseRegulations () {
+      this.purchaseRegulationsLoading = true
+      APIGateway.appSetting.get('DeliveryCost-post-pishtaz')
+        .then((purchaseRegulations) => {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'text', 'value', purchaseRegulations.value)
+          this.mounted = true
+        })
+        .finally(() => {
+          this.purchaseRegulationsLoading = false
+        })
+    },
+    onSubmit () {
+      const formData = FormBuilderAssist.getFormData(this.inputs)
+      this.purchaseRegulationsLoading = true
+      APIGateway.appSetting.update({ key: 'DeliveryCost-post-pishtaz', value: formData.text })
+        .then((purchaseRegulations) => {
+          this.purchaseRegulations = purchaseRegulations
+        })
+        .finally(() => {
+          this.purchaseRegulationsLoading = false
+        })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.AdminPostCreate {
+  .title {
+    font-style: normal;
+    font-weight: 700;
+    font-size: 24px;
+    line-height: 140%;
+    color: #424242;
+    margin-bottom: 27px;
+    position: relative;
+    .back-action {
+      position: absolute;
+      right: 0;
+      top: 0;
+    }
+  }
+  :deep(.form) {
+    padding: 24px;
+  }
+}
+</style>

+ 21 - 0
src/components/Widgets/Store/Basket/AdminStoreDeliveryCost/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>

+ 5 - 5
src/components/Widgets/Store/Basket/UserStoreBasketList/UserStoreBasketList.vue

@@ -34,11 +34,11 @@
 </template>
 </template>
 
 
 <script>
 <script>
+import Assist from 'assets/js/Assist.js'
 import { EntityIndex } from 'quasar-crud'
 import { EntityIndex } from 'quasar-crud'
 import { Basket } from 'src/models/Basket.js'
 import { Basket } from 'src/models/Basket.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
-import Assist from 'assets/js/Assist'
 
 
 export default {
 export default {
   name: 'UserStoreBasketList',
   name: 'UserStoreBasketList',
@@ -96,12 +96,12 @@ export default {
             required: true,
             required: true,
             label: 'نوع سفارش',
             label: 'نوع سفارش',
             align: 'left',
             align: 'left',
-            field: () => '-'
+            field: (row) => row.order_type ? new Basket(row).order_type_info.label : '-'
           },
           },
           {
           {
             name: 'overall_order_price',
             name: 'overall_order_price',
             required: true,
             required: true,
-            label: 'قیمت کل(تومان)',
+            label: 'قیمت کل(ریال)',
             align: 'left',
             align: 'left',
             field: row => parseInt(row.overall_order_price).toLocaleString('fa')
             field: row => parseInt(row.overall_order_price).toLocaleString('fa')
           },
           },
@@ -113,11 +113,11 @@ export default {
             field: row => (new Basket(row)).status_info.label
             field: row => (new Basket(row)).status_info.label
           },
           },
           {
           {
-            name: 'creation_time',
+            name: 'order_time',
             required: true,
             required: true,
             label: 'تاریخ ثبت',
             label: 'تاریخ ثبت',
             align: 'left',
             align: 'left',
-            field: row => Assist.miladiToShamsi(row.creation_time)
+            field: row => Assist.miladiToShamsi(row.order_time)
           },
           },
           {
           {
             name: 'action',
             name: 'action',

+ 57 - 0
src/components/Widgets/Store/Basket/UserStoreBasketShow/ProductOrPackageMedias.vue

@@ -0,0 +1,57 @@
+<template>
+  <q-card v-for="(product, productIndex) in products.list"
+          :key="productIndex"
+          class="q-mb-md">
+    <q-card-section>{{ product.title }}</q-card-section>
+    <q-separator />
+    <q-card-section>
+      <content-medias v-if="product.medias_info.list.length > 0"
+                      :can-delete="false"
+                      :can-add-media="false"
+                      :show-download-link="true"
+                      :show-page-link="false"
+                      :readonly="true"
+                      :value="product.medias_info.list" />
+      <q-badge v-else>
+        محتوای قابل دانلودی برای این محصول وجود ندارد
+      </q-badge>
+    </q-card-section>
+  </q-card>
+</template>
+
+<script>
+import { BasketItem } from 'src/models/BasketItem.js'
+import { ProductList } from 'src/models/Product.js'
+import ContentMedias from 'src/components/FormBuilderCustumComponents/ContentMedias/ContentMedias.vue'
+
+export default {
+  name: 'ProductOrPackageMedias',
+  components: { ContentMedias },
+  props: {
+    basketItem: BasketItem,
+    default: new BasketItem()
+  },
+  data () {
+    return {
+      products: new ProductList(),
+      medias: null
+    }
+  },
+  mounted() {
+    this.loadProducts()
+  },
+  methods: {
+    loadProducts () {
+      if (this.basketItem.product_info.id) {
+        this.products = new ProductList([this.basketItem.product_info])
+      } else if (this.basketItem.package_info.id) {
+        this.products = new ProductList(this.basketItem.package_info.products_info)
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+
+</style>

+ 56 - 14
src/components/Widgets/Store/Basket/UserStoreBasketShow/UserStoreBasketShow.vue

@@ -42,7 +42,6 @@
         </div>
         </div>
       </template>
       </template>
     </entity-show>
     </entity-show>
-
     <div class="q-mt-md">
     <div class="q-mt-md">
       <entity-index v-if="basket.id"
       <entity-index v-if="basket.id"
                     v-model:value="tableInputs"
                     v-model:value="tableInputs"
@@ -61,6 +60,16 @@
             <q-img :src="inputData.col.value"
             <q-img :src="inputData.col.value"
                    width="100px" />
                    width="100px" />
           </template>
           </template>
+          <template v-else-if="inputData.col.name === 'download'">
+            <q-btn v-if="!inputData.col.value"
+                   color="primary"
+                   @click="openDownloadDialog(inputData.props.row)">
+              <q-icon name="download"
+                      class="q-mr-sm" />
+              دانلود
+            </q-btn>
+            <span v-else />
+          </template>
           <template v-else-if="inputData.col.name === 'action'">
           <template v-else-if="inputData.col.name === 'action'">
             <q-btn color="primary"
             <q-btn color="primary"
                    outline
                    outline
@@ -75,19 +84,31 @@
           </template>
           </template>
         </template>
         </template>
       </entity-index>
       </entity-index>
+      <q-dialog v-model="downloadDialog">
+        <q-card style="width: 800px; max-width: 95%;">
+          <q-card-section>
+            محتوای قابل دانلود
+          </q-card-section>
+          <q-separator />
+          <q-card-section>
+            <product-or-package-medias :basket-item="selectedBasketItem" />
+          </q-card-section>
+        </q-card>
+      </q-dialog>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
 import { shallowRef } from 'vue'
 import { shallowRef } from 'vue'
-import { Package } from 'src/models/Package'
+// import { Package } from 'src/models/Package'
 import { Basket } from 'src/models/Basket.js'
 import { Basket } from 'src/models/Basket.js'
 import { Product } from 'src/models/Product.js'
 import { Product } from 'src/models/Product.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { EntityShow, EntityIndex } from 'quasar-crud'
 import { EntityShow, EntityIndex } from 'quasar-crud'
 import { FormBuilderAssist } from 'quasar-form-builder'
 import { FormBuilderAssist } from 'quasar-form-builder'
+import ProductOrPackageMedias from './ProductOrPackageMedias.vue'
 import FormBuilderDateTime from 'src/components/FormBuilderCustumComponents/FormBuilderDateTime.vue'
 import FormBuilderDateTime from 'src/components/FormBuilderCustumComponents/FormBuilderDateTime.vue'
 
 
 const FormBuilderDateTimeComp = shallowRef(FormBuilderDateTime)
 const FormBuilderDateTimeComp = shallowRef(FormBuilderDateTime)
@@ -96,12 +117,15 @@ export default {
   name: 'AdminStoreBasketShow',
   name: 'AdminStoreBasketShow',
   components: {
   components: {
     EntityShow,
     EntityShow,
-    EntityIndex
+    EntityIndex,
+    ProductOrPackageMedias
   },
   },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
   data () {
   data () {
     return {
     return {
       mounted: false,
       mounted: false,
+      downloadDialog: false,
+      selectedBasketItem: null,
       entityLoading: false,
       entityLoading: false,
       api: APIGateway.basket.APIAdresses.byId(this.$route.params.id),
       api: APIGateway.basket.APIAdresses.byId(this.$route.params.id),
       entityIdKey: 'id',
       entityIdKey: 'id',
@@ -131,14 +155,14 @@ export default {
             required: true,
             required: true,
             label: 'شناسه',
             label: 'شناسه',
             align: 'left',
             align: 'left',
-            field: row => row.id
+            field: row => row.product_info?.id ? row.product_info.id : row.package_info.id
           },
           },
           {
           {
             name: 'title',
             name: 'title',
             required: true,
             required: true,
             label: 'نام محصول',
             label: 'نام محصول',
             align: 'left',
             align: 'left',
-            field: row => (row.product_info || row.package_info).title
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).title
           },
           },
           {
           {
             name: 'is_package_or_product',
             name: 'is_package_or_product',
@@ -148,11 +172,18 @@ export default {
             field: row => row.package ? 'بسته' : 'تکی'
             field: row => row.package ? 'بسته' : 'تکی'
           },
           },
           {
           {
-            name: 'content_category',
+            name: 'store_category',
+            required: true,
+            label: 'دسته فروشگاه',
+            align: 'left',
+            field: row => ((row.product_info?.id ? row.product_info : row.package_info).store_category_info?.parent?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).store_category_info?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).store_category_info?.title || '-')
+          },
+          {
+            name: 'store_category1',
             required: true,
             required: true,
-            label: 'دسته بندی اصلی',
+            label: 'دسته محتوا',
             align: 'left',
             align: 'left',
-            field: row => row.content_category
+            field: row => ((row.product_info?.id ? row.product_info : row.package_info).content_category_info?.parent?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).content_category_info?.parent?.title || '-') + '، ' + ((row.product_info || row.package_info).content_category_info?.title || '-')
           },
           },
           {
           {
             name: 'count',
             name: 'count',
@@ -166,7 +197,7 @@ export default {
             required: true,
             required: true,
             label: 'نوع جنس',
             label: 'نوع جنس',
             align: 'left',
             align: 'left',
-            field: row => row.product_info ? (new Product(row.product_info)).is_physical_info.label : (new Package(row.product_info)).is_physical_info.label
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).is_physical_info.label
           },
           },
           {
           {
             name: 'physical_type_info',
             name: 'physical_type_info',
@@ -180,12 +211,19 @@ export default {
             required: true,
             required: true,
             label: 'قیمت',
             label: 'قیمت',
             align: 'left',
             align: 'left',
-            field: row => parseInt((row.product_info || row.package_info).unit_price?.toString()).toLocaleString('fa')
+            field: row => parseInt((row.product_info?.id ? row.product_info : row.package_info).unit_price?.toString()).toLocaleString('fa')
+          },
+          {
+            name: 'download',
+            required: true,
+            label: 'دانلود',
+            align: 'left',
+            field: row => (row.product_info?.id ? row.product_info : row.package_info).is_physical
           },
           },
           {
           {
             name: 'action',
             name: 'action',
             required: true,
             required: true,
-            label: 'عملیات',
+            label: 'مشاهده',
             align: 'left',
             align: 'left',
             field: row => ''
             field: row => ''
           }
           }
@@ -219,15 +257,19 @@ export default {
     this.inputs = [
     this.inputs = [
       { type: 'separator', name: 'space1', label: 'مشخصات سفارش', className: 'custom-separator', col: 'col-12' },
       { type: 'separator', name: 'space1', label: 'مشخصات سفارش', className: 'custom-separator', col: 'col-12' },
       { type: 'input', name: 'id', responseKey: 'id', label: 'شناسه', placeholder: ' ', disable: true, col: 'col-md-2 col-12' },
       { type: 'input', name: 'id', responseKey: 'id', label: 'شناسه', placeholder: ' ', disable: true, col: 'col-md-2 col-12' },
-      { type: 'input', name: 'items_info.length', responseKey: 'items_info.length', label: 'تعداد اقلام', placeholder: ' ', disable: true, col: 'col-md-2 col-12' },
-      { type: FormBuilderDateTimeComp, name: 'creation_time', responseKey: 'creation_time', outsideLabel: 'تاریخ ثبت', placeholder: ' ', disable: true, col: 'col-md-3 col-12' },
-      { type: 'input', name: 'overall_order_price', responseKey: 'overall_order_price', label: 'قیمت کل(تومان)', placeholder: ' ', disable: true, col: 'col-md-3 col-12' }
+      { type: 'input', name: 'items_info.length', responseKey: 'items_info.length', label: 'تعداد اقلام', placeholder: ' ', disable: true, col: 'col-md-1 col-12' },
+      { type: FormBuilderDateTimeComp, name: 'creation_time', responseKey: 'creation_time', outsideLabel: 'تاریخ ثبت', placeholder: ' ', disable: true, col: 'col-md-6 col-12' },
+      { type: 'input', name: 'overall_order_price', responseKey: 'overall_order_price', label: 'قیمت کل(ریال)', placeholder: ' ', disable: true, col: 'col-md-3 col-12' }
     ]
     ]
   },
   },
   mounted() {
   mounted() {
     this.mounted = true
     this.mounted = true
   },
   },
   methods: {
   methods: {
+    openDownloadDialog (basketItem) {
+      this.selectedBasketItem = basketItem
+      this.downloadDialog = true
+    },
     afterLoadInputData (data) {
     afterLoadInputData (data) {
       this.entityLoading = false
       this.entityLoading = false
       this.basket = new Basket(data)
       this.basket = new Basket(data)

+ 2 - 2
src/components/Widgets/Store/Cart/components/packageItem.vue

@@ -19,11 +19,11 @@
     <div class="package-item__price">
     <div class="package-item__price">
       <div class="package-item__price-base">
       <div class="package-item__price-base">
         {{ parseInt(packageItem.unit_price).toLocaleString('fa') }}
         {{ parseInt(packageItem.unit_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
       <div class="package-item__price-final">
       <div class="package-item__price-final">
         {{ packageItem.getFinalPrice() }}
         {{ packageItem.getFinalPrice() }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="package-item__actions">
     <div class="package-item__actions">

+ 2 - 2
src/components/Widgets/Store/Cart/components/productItem.vue

@@ -27,11 +27,11 @@
     <div class="product-item__price">
     <div class="product-item__price">
       <div class="product-item__price-base">
       <div class="product-item__price-base">
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
       <div class="product-item__price-final">
       <div class="product-item__price-final">
         {{ product.getFinalPrice() }}
         {{ product.getFinalPrice() }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="product-item__actions">
     <div class="product-item__actions">

+ 24 - 5
src/components/Widgets/Store/Cart/components/step1/cartItem.vue

@@ -50,11 +50,11 @@
         <div v-if="packageOrProductObject.unit_price != packageOrProductObject.sellable_price"
         <div v-if="packageOrProductObject.unit_price != packageOrProductObject.sellable_price"
              class="cart-item__price-base">
              class="cart-item__price-base">
           {{ packageOrProductObject.unit_price.toLocaleString('fa') }}
           {{ packageOrProductObject.unit_price.toLocaleString('fa') }}
-          تومان
+          ریال
         </div>
         </div>
         <div class="cart-item__price-final">
         <div class="cart-item__price-final">
           {{ packageOrProductObject.sellable_price.toLocaleString('fa') }}
           {{ packageOrProductObject.sellable_price.toLocaleString('fa') }}
-          تومان
+          ریال
         </div>
         </div>
       </div>
       </div>
       <div v-if="basketItem.package"
       <div v-if="basketItem.package"
@@ -170,10 +170,17 @@ export default {
       width: 32px;
       width: 32px;
       height: 32px;
       height: 32px;
     }
     }
+    @media screen and (max-width: 600px) {
+      & {
+        top: 4px;
+        left: auto;
+        right: 4px;
+      }
+    }
   }
   }
   .cart-item__thumbnail {
   .cart-item__thumbnail {
     width: $cart-item__thumbnail-width;
     width: $cart-item__thumbnail-width;
-    @media screen and (max-width: 500px) {
+    @media screen and (max-width: 600px) {
       & {
       & {
         width: 100%;
         width: 100%;
       }
       }
@@ -214,6 +221,13 @@ export default {
           font-size: 16px;
           font-size: 16px;
         }
         }
       }
       }
+      @media screen and (max-width: 1200px) {
+        & {
+          flex-flow: column;
+          gap: 8px;
+          margin-bottom: 8px;
+        }
+      }
     }
     }
     .cart-item__type-info {
     .cart-item__type-info {
       position: absolute;
       position: absolute;
@@ -236,7 +250,7 @@ export default {
       right: 20px;
       right: 20px;
       top: 65px;
       top: 65px;
       width: 120px;
       width: 120px;
-      @media screen and (max-width: 500px) {
+      @media screen and (max-width: 1200px) {
         & {
         & {
           position: relative;
           position: relative;
           top: 0;
           top: 0;
@@ -263,7 +277,7 @@ export default {
         color: $primary;
         color: $primary;
         font-size: 24px;
         font-size: 24px;
       }
       }
-      @media screen and (max-width: 500px) {
+      @media screen and (max-width: 1200px) {
         & {
         & {
           position: relative;
           position: relative;
           top: 0;
           top: 0;
@@ -297,5 +311,10 @@ export default {
       }
       }
     }
     }
   }
   }
+  @media screen and (max-width: 600px) {
+    & {
+      padding: 56px 22px 24px 24px;
+    }
+  }
 }
 }
 </style>
 </style>

+ 5 - 5
src/components/Widgets/Store/Cart/components/step1/sidebar.vue

@@ -6,7 +6,7 @@
       </div>
       </div>
       <div class="step1__sidebar-total-price-value">
       <div class="step1__sidebar-total-price-value">
         {{ basket.price.toLocaleString('fa') }}
         {{ basket.price.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div v-if="basket.margin_amount"
     <div v-if="basket.margin_amount"
@@ -16,7 +16,7 @@
       </div>
       </div>
       <div class="step1__sidebar-total-discount-value">
       <div class="step1__sidebar-total-discount-value">
         {{ basket.margin_amount.toLocaleString('fa') }}
         {{ basket.margin_amount.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div v-if="basket.margin_amount"
     <div v-if="basket.margin_amount"
@@ -28,7 +28,7 @@
       </div>
       </div>
       <div class="step1__sidebar-total-price-value">
       <div class="step1__sidebar-total-price-value">
         {{ basket.sellable_price.toLocaleString('fa') }}
         {{ basket.sellable_price.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div v-if="basket.discount"
     <div v-if="basket.discount"
@@ -38,7 +38,7 @@
       </div>
       </div>
       <div class="step1__sidebar-discount-code-value">
       <div class="step1__sidebar-discount-code-value">
         {{ basket.discount_amount.toLocaleString('fa') }}
         {{ basket.discount_amount.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="step1__sidebar-separator" />
     <div class="step1__sidebar-separator" />
@@ -48,7 +48,7 @@
       </div>
       </div>
       <div class="step1__sidebar-final-price-value">
       <div class="step1__sidebar-final-price-value">
         {{ basket.order_price.toLocaleString('fa') }}
         {{ basket.order_price.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="step1__sidebar-continue-action">
     <div class="step1__sidebar-continue-action">

+ 51 - 3
src/components/Widgets/Store/Cart/components/step2/cartAddress.vue

@@ -54,6 +54,7 @@
 import { Basket } from 'src/models/Basket.js'
 import { Basket } from 'src/models/Basket.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { FormBuilder, FormBuilderAssist } from 'quasar-form-builder'
 import { FormBuilder, FormBuilderAssist } from 'quasar-form-builder'
+import { Provinces, Cities, getCitiesOfProvince } from 'src/assets/js/IranianCities.js'
 
 
 export default {
 export default {
   name: 'CartAddress',
   name: 'CartAddress',
@@ -68,13 +69,38 @@ export default {
   data () {
   data () {
     return {
     return {
       addressRadio: 1,
       addressRadio: 1,
+      inputDataLoaded: false,
       mounted: false,
       mounted: false,
       dialog: false,
       dialog: false,
       setAddressLoading: false,
       setAddressLoading: false,
       addressInputs: [
       addressInputs: [
-        { type: 'input', name: 'province', responseKey: 'province', label: 'استان', placeholder: ' ', col: 'col-md-4 col-12' },
-        { type: 'input', name: 'city', responseKey: 'city', label: 'شهر', placeholder: ' ', col: 'col-md-4 col-12' },
-        { type: 'input', name: 'postal_code', responseKey: 'postal_code', label: 'کد پستی', placeholder: ' ', col: 'col-md-4 col-12' },
+        {
+          type: 'select',
+          name: 'province',
+          options: Provinces,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          responseKey: 'province',
+          label: 'استان',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'select',
+          name: 'city',
+          options: Cities,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          responseKey: 'city',
+          label: 'شهر',
+          placeholder: ' ',
+          col: 'col-md-6 col-12'
+        },
+        { type: 'input', name: 'postal_code', responseKey: 'postal_code', label: 'کد پستی', placeholder: ' ', col: 'col-12' },
         { type: 'input', name: 'address', responseKey: 'address', label: 'آدرس پستی', placeholder: ' ', col: 'col-12' }
         { type: 'input', name: 'address', responseKey: 'address', label: 'آدرس پستی', placeholder: ' ', col: 'col-12' }
       ]
       ]
     }
     }
@@ -82,6 +108,25 @@ export default {
   computed: {
   computed: {
     addressString () {
     addressString () {
       return 'file_download'
       return 'file_download'
+    },
+    selectedProvince () {
+      return FormBuilderAssist.getInputsByName(this.addressInputs, 'province').value
+    }
+  },
+  watch: {
+    selectedProvince (newValue) {
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.addressInputs, 'city', 'disable', true)
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedProvince)
+
+      FormBuilderAssist.setAttributeByName(this.addressInputs, 'city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.addressInputs, 'city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.addressInputs, 'city', 'value', null)
+      }
     }
     }
   },
   },
   mounted() {
   mounted() {
@@ -109,6 +154,9 @@ export default {
     },
     },
     setFormData () {
     setFormData () {
       FormBuilderAssist.setInputValues(this.basket, this.addressInputs)
       FormBuilderAssist.setInputValues(this.basket, this.addressInputs)
+      this.$nextTick(() => {
+        this.inputDataLoaded = true
+      })
     }
     }
   }
   }
 }
 }

+ 28 - 4
src/components/Widgets/Store/Cart/components/step2/deliveryInfo.vue

@@ -19,7 +19,7 @@
         </div>
         </div>
         <div class="delivery-info__selected-type-meta">
         <div class="delivery-info__selected-type-meta">
           <q-icon name="shopping_bag" />
           <q-icon name="shopping_bag" />
-          هزینه به آدرس و وزن مرصوله متغیر میباشد
+          هزینه به آدرس و وزن مرسوله متغیر میباشد
         </div>
         </div>
       </div>
       </div>
       <div class="delivery-info__price-info">
       <div class="delivery-info__price-info">
@@ -28,8 +28,13 @@
           هزینه ارسال
           هزینه ارسال
         </div>
         </div>
         <div class="delivery-info__price-value">
         <div class="delivery-info__price-value">
-          {{ basket.delivery_cost.toLocaleString('fa') }}
-          تومان
+          <q-linear-progress v-if="deliveryCostLoading"
+                             indeterminate />
+          <template v-else>
+            <!--            {{ basket.delivery_cost.toLocaleString('fa') }}-->
+            {{ deliveryCost.toLocaleString('fa') }}
+            ریال
+          </template>
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
@@ -38,6 +43,7 @@
 
 
 <script>
 <script>
 import { Basket } from 'src/models/Basket.js'
 import { Basket } from 'src/models/Basket.js'
+import { APIGateway } from 'src/api/APIGateway.js'
 
 
 export default {
 export default {
   name: 'DeliveryInfo',
   name: 'DeliveryInfo',
@@ -49,7 +55,25 @@ export default {
   },
   },
   data () {
   data () {
     return {
     return {
-      radioValue: 1
+      radioValue: 1,
+      deliveryCost: 0,
+      deliveryCostLoading: false
+    }
+  },
+  mounted() {
+    this.getDeliveryCost()
+  },
+  methods: {
+    getDeliveryCost () {
+      this.deliveryCostLoading = true
+      APIGateway.appSetting.get('DeliveryCost-post-pishtaz')
+        .then((deliveryCost) => {
+          this.deliveryCost = parseInt(deliveryCost.value || 0)
+          this.mounted = true
+        })
+        .finally(() => {
+          this.deliveryCostLoading = false
+        })
     }
     }
   }
   }
 }
 }

+ 33 - 6
src/components/Widgets/Store/Cart/components/step2/sidebar.vue

@@ -6,7 +6,7 @@
       </div>
       </div>
       <div class="step2__sidebar-total-price-value">
       <div class="step2__sidebar-total-price-value">
         {{ basket.price.toLocaleString('fa') }}
         {{ basket.price.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="step2__sidebar-overall-discount-amount">
     <div class="step2__sidebar-overall-discount-amount">
@@ -15,7 +15,7 @@
       </div>
       </div>
       <div class="step2__sidebar-overall-discount-amount-value">
       <div class="step2__sidebar-overall-discount-amount-value">
         {{ basket.overall_discount_amount.toLocaleString('fa') }}
         {{ basket.overall_discount_amount.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="step2__sidebar-delivery-cost">
     <div class="step2__sidebar-delivery-cost">
@@ -23,8 +23,18 @@
         هزینه ارسال
         هزینه ارسال
       </div>
       </div>
       <div class="step2__sidebar-delivery-cost-value">
       <div class="step2__sidebar-delivery-cost-value">
-        {{ basket.delivery_cost.toLocaleString('fa') }}
-        تومان
+        <q-linear-progress v-if="deliveryCostLoading"
+                           indeterminate />
+        <template v-else>
+          <!--          {{ basket.delivery_cost.toLocaleString('fa') }}-->
+          <span v-if="basket.order_type!=='VIRTUAL'">
+            {{ deliveryCost.toLocaleString('fa') }}
+          </span>
+          <span v-else>
+            0
+          </span>
+          ریال
+        </template>
       </div>
       </div>
     </div>
     </div>
     <div class="step2__sidebar-separator" />
     <div class="step2__sidebar-separator" />
@@ -34,7 +44,7 @@
       </div>
       </div>
       <div class="step2__sidebar-final-price-value">
       <div class="step2__sidebar-final-price-value">
         {{ basket.overall_order_price.toLocaleString('fa') }}
         {{ basket.overall_order_price.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="step2__sidebar-continue-action">
     <div class="step2__sidebar-continue-action">
@@ -49,6 +59,7 @@
 
 
 <script>
 <script>
 import { Basket } from 'src/models/Basket.js'
 import { Basket } from 'src/models/Basket.js'
+import { APIGateway } from 'src/api/APIGateway.js'
 
 
 export default {
 export default {
   name: 'cartItem',
   name: 'cartItem',
@@ -61,12 +72,28 @@ export default {
   emits: ['complete'],
   emits: ['complete'],
   data () {
   data () {
     return {
     return {
-      step: 1
+      step: 1,
+      deliveryCost: 0,
+      deliveryCostLoading: false
     }
     }
   },
   },
+  mounted() {
+    this.getDeliveryCost()
+  },
   methods: {
   methods: {
     onComplete () {
     onComplete () {
       this.$emit('complete')
       this.$emit('complete')
+    },
+    getDeliveryCost () {
+      this.deliveryCostLoading = true
+      APIGateway.appSetting.get('DeliveryCost-post-pishtaz')
+        .then((deliveryCost) => {
+          this.deliveryCost = parseInt(deliveryCost.value || 0)
+          this.mounted = true
+        })
+        .finally(() => {
+          this.deliveryCostLoading = false
+        })
     }
     }
   }
   }
 }
 }

Разница между файлами не показана из-за своего большого размера
+ 2 - 2
src/components/Widgets/Store/Cart/components/step2/step2.vue


+ 2 - 2
src/components/Widgets/Store/Shop/Shop.vue

@@ -150,7 +150,7 @@ export default {
         { type: 'hidden', name: 'unit_price__lt', value: null },
         { type: 'hidden', name: 'unit_price__lt', value: null },
         { type: 'hidden', name: 'unit_price__gte', value: null },
         { type: 'hidden', name: 'unit_price__gte', value: null },
         { type: 'hidden', name: 'inventory__gte', value: null },
         { type: 'hidden', name: 'inventory__gte', value: null },
-        { type: 'hidden', name: 'category__in', value: null },
+        { type: 'hidden', name: 'store_category__in', value: null },
         { type: 'hidden', name: 'physical_type__in', value: null },
         { type: 'hidden', name: 'physical_type__in', value: null },
         { type: 'hidden', name: 'medias__type__in', value: null }
         { type: 'hidden', name: 'medias__type__in', value: null }
       ],
       ],
@@ -193,7 +193,7 @@ export default {
       FormBuilderAssist.setAttributeByName(this.productInputs, 'unit_price__lt', 'value', this.sidebarFilters.unit_price__lt)
       FormBuilderAssist.setAttributeByName(this.productInputs, 'unit_price__lt', 'value', this.sidebarFilters.unit_price__lt)
       FormBuilderAssist.setAttributeByName(this.productInputs, 'unit_price__gte', 'value', this.sidebarFilters.unit_price__gte)
       FormBuilderAssist.setAttributeByName(this.productInputs, 'unit_price__gte', 'value', this.sidebarFilters.unit_price__gte)
       FormBuilderAssist.setAttributeByName(this.productInputs, 'inventory__gte', 'value', this.sidebarFilters.inventory__gte)
       FormBuilderAssist.setAttributeByName(this.productInputs, 'inventory__gte', 'value', this.sidebarFilters.inventory__gte)
-      FormBuilderAssist.setAttributeByName(this.productInputs, 'category__in', 'value', this.sidebarFilters.category__in?.join())
+      FormBuilderAssist.setAttributeByName(this.productInputs, 'store_category__in', 'value', this.sidebarFilters.store_category__in?.join())
       FormBuilderAssist.setAttributeByName(this.productInputs, 'physical_type__in', 'value', this.sidebarFilters.physical_type__in?.join())
       FormBuilderAssist.setAttributeByName(this.productInputs, 'physical_type__in', 'value', this.sidebarFilters.physical_type__in?.join())
       FormBuilderAssist.setAttributeByName(this.productInputs, 'medias__type__in', 'value', this.sidebarFilters.medias__type__in?.join())
       FormBuilderAssist.setAttributeByName(this.productInputs, 'medias__type__in', 'value', this.sidebarFilters.medias__type__in?.join())
     },
     },

+ 50 - 27
src/components/Widgets/Store/Shop/components/packageItem.vue

@@ -1,6 +1,15 @@
 <template>
 <template>
   <div class="package-item">
   <div class="package-item">
     <div class="package-item__badges">
     <div class="package-item__badges">
+      <div v-if="packageItem.is_purchased"
+           class="package-item__badge">
+        <div class="package-item__badge-title">
+          خریداری شده است
+        </div>
+        <div class="package-item__badge-icon">
+          <q-icon name="done_all" />
+        </div>
+      </div>
       <div class="package-item__badge">
       <div class="package-item__badge">
         <div class="package-item__badge-title">
         <div class="package-item__badge-title">
           بسته
           بسته
@@ -20,11 +29,11 @@
       <div v-if="packageItem.unit_price != packageItem.sellable_price"
       <div v-if="packageItem.unit_price != packageItem.sellable_price"
            class="package-item__price-base">
            class="package-item__price-base">
         {{ parseInt(packageItem.unit_price).toLocaleString('fa') }}
         {{ parseInt(packageItem.unit_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
       <div class="package-item__price-final">
       <div class="package-item__price-final">
         {{ packageItem.sellable_price.toLocaleString('fa') }}
         {{ packageItem.sellable_price.toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="package-item__actions">
     <div class="package-item__actions">
@@ -34,18 +43,27 @@
              outline>
              outline>
         مشاهده جزییات
         مشاهده جزییات
       </q-btn>
       </q-btn>
-      <q-btn v-if="!basketItem"
-             class="package-item__actions-add-to-cart"
+      <template v-if="!packageItem.is_add_to_basket_disabled">
+        <q-btn v-if="!basketItem"
+               class="package-item__actions-add-to-cart"
+               :loading="addToCartLoading"
+               color="primary"
+               @click="addToCart">
+          افزودن به سبد
+        </q-btn>
+        <cart-count-action v-else
+                           :basket-item="basketItem"
+                           @increase="onIncrease"
+                           @decrease="onDecrease"
+                           @remove="onRemove" />
+      </template>
+      <q-btn v-if="packageItem.inventory === 0"
+             class="product-btn-add-to-cart"
              :loading="addToCartLoading"
              :loading="addToCartLoading"
-             color="primary"
-             @click="addToCart">
-        افزودن به سبد
+             disable
+             color="primary">
+        ناموجود
       </q-btn>
       </q-btn>
-      <cart-count-action v-else
-                         :basket-item="basketItem"
-                         @increase="onIncrease"
-                         @decrease="onDecrease"
-                         @remove="onRemove" />
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
@@ -181,14 +199,16 @@ export default {
     padding: 0 4px;
     padding: 0 4px;
     text-align: center;
     text-align: center;
     font-size: 18px;
     font-size: 18px;
-    margin-bottom: 52px;
+    //margin-bottom: 52px;
+    margin-bottom: 8px;
   }
   }
   .package-item__price {
   .package-item__price {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: space-between;
     justify-content: space-between;
     padding: 0 20px;
     padding: 0 20px;
-    margin-bottom: 43px;
+    //margin-bottom: 43px;
+    margin-bottom: 16px;
     .package-item__price-final {
     .package-item__price-final {
       font-size: 20px;
       font-size: 20px;
       color: #475F4A;
       color: #475F4A;
@@ -200,7 +220,9 @@ export default {
     }
     }
   }
   }
   .package-item__actions {
   .package-item__actions {
-    display: none;
+    //display: none;
+    display: flex;
+    flex-flow: column;
     margin: 0 25px 25px 25px;
     margin: 0 25px 25px 25px;
     .package-item__actions-view {
     .package-item__actions-view {
       background: white;
       background: white;
@@ -213,24 +235,25 @@ export default {
   }
   }
   &:hover {
   &:hover {
     .package-item__badges {
     .package-item__badges {
+      z-index: 1;
       .package-item__badge {
       .package-item__badge {
         .package-item__badge-title {
         .package-item__badge-title {
           display: block;
           display: block;
         }
         }
       }
       }
     }
     }
-    :deep(.package-item__thumbnail) {
-      margin: 10px 105px 4px 105px;
-      .q-img {
-        width: 100%;
-      }
-    }
-    .package-item__title {
-      margin-bottom: 44px;
-    }
-    .package-item__price {
-      margin-bottom: 27px;
-    }
+    //:deep(.package-item__thumbnail) {
+    //  margin: 10px 105px 4px 105px;
+    //  .q-img {
+    //    width: 100%;
+    //  }
+    //}
+    //.package-item__title {
+    //  margin-bottom: 44px;
+    //}
+    //.package-item__price {
+    //  margin-bottom: 27px;
+    //}
     .package-item__actions {
     .package-item__actions {
       display: flex;
       display: flex;
       flex-flow: column;
       flex-flow: column;

+ 48 - 27
src/components/Widgets/Store/Shop/components/productItem.vue

@@ -1,6 +1,15 @@
 <template>
 <template>
   <div class="product-item">
   <div class="product-item">
     <div class="product-item__badges">
     <div class="product-item__badges">
+      <div v-if="product.is_purchased"
+           class="product-item__badge">
+        <div class="product-item__badge-title">
+          خریداری شده است
+        </div>
+        <div class="product-item__badge-icon">
+          <q-icon name="done_all" />
+        </div>
+      </div>
       <div class="product-item__badge">
       <div class="product-item__badge">
         <div class="product-item__badge-title">
         <div class="product-item__badge-title">
           {{ product.is_physical_info.label }}
           {{ product.is_physical_info.label }}
@@ -29,11 +38,11 @@
       <div v-if="product.unit_price !== product.sellable_price"
       <div v-if="product.unit_price !== product.sellable_price"
            class="product-item__price-base">
            class="product-item__price-base">
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
       <div class="product-item__price-final">
       <div class="product-item__price-final">
         {{ parseInt(product.sellable_price).toLocaleString('fa') }}
         {{ parseInt(product.sellable_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="product-item__actions">
     <div class="product-item__actions">
@@ -43,18 +52,27 @@
              outline>
              outline>
         مشاهده جزییات
         مشاهده جزییات
       </q-btn>
       </q-btn>
-      <q-btn v-if="!basketItem"
-             class="product-item__actions-add-to-cart"
+      <template v-if="!product.is_add_to_basket_disabled">
+        <q-btn v-if="!basketItem && product.inventory > 0"
+               class="product-item__actions-add-to-cart"
+               :loading="addToCartLoading"
+               color="primary"
+               @click="addToCart">
+          افزودن به سبد
+        </q-btn>
+        <cart-count-action v-else
+                           :basket-item="basketItem"
+                           @increase="onIncrease"
+                           @decrease="onDecrease"
+                           @remove="onRemove" />
+      </template>
+      <q-btn v-if="product.inventory === 0"
+             class="product-btn-add-to-cart"
              :loading="addToCartLoading"
              :loading="addToCartLoading"
-             color="primary"
-             @click="addToCart">
-        افزودن به سبد
+             disable
+             color="primary">
+        ناموجود
       </q-btn>
       </q-btn>
-      <cart-count-action v-else
-                         :basket-item="basketItem"
-                         @increase="onIncrease"
-                         @decrease="onDecrease"
-                         @remove="onRemove" />
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
@@ -193,14 +211,15 @@ export default {
     padding: 0 4px;
     padding: 0 4px;
     text-align: center;
     text-align: center;
     font-size: 18px;
     font-size: 18px;
-    margin-bottom: 52px;
+    //margin-bottom: 52px;
+    margin-bottom: 8px;
   }
   }
   .product-item__price {
   .product-item__price {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: space-between;
     justify-content: space-between;
     padding: 0 20px;
     padding: 0 20px;
-    margin-bottom: 43px;
+    margin-bottom: 16px;
     .product-item__price-final {
     .product-item__price-final {
       font-size: 18px;
       font-size: 18px;
       color: #475F4A;
       color: #475F4A;
@@ -212,7 +231,9 @@ export default {
     }
     }
   }
   }
   .product-item__actions {
   .product-item__actions {
-    display: none;
+    //display: none;
+    display: flex;
+    flex-flow: column;
     margin: 0 25px 25px 25px;
     margin: 0 25px 25px 25px;
     .product-item__actions-view {
     .product-item__actions-view {
       background: white;
       background: white;
@@ -232,18 +253,18 @@ export default {
         }
         }
       }
       }
     }
     }
-    :deep(.product-item__thumbnail) {
-      margin: 10px auto 4px auto;
-      .q-img {
-        width: 90px;
-      }
-    }
-    .product-item__title {
-      margin-bottom: 44px;
-    }
-    .product-item__price {
-      margin-bottom: 27px;
-    }
+    //:deep(.product-item__thumbnail) {
+    //  margin: 10px auto 4px auto;
+    //  .q-img {
+    //    width: 90px;
+    //  }
+    //}
+    //.product-item__title {
+    //  margin-bottom: 44px;
+    //}
+    //.product-item__price {
+    //  margin-bottom: 27px;
+    //}
     .product-item__actions {
     .product-item__actions {
       display: flex;
       display: flex;
       flex-flow: column;
       flex-flow: column;

+ 3 - 3
src/components/Widgets/Store/Shop/components/sidebar.vue

@@ -35,12 +35,12 @@
         <div class="sidebar__price-label-min">
         <div class="sidebar__price-label-min">
           از
           از
           {{ parseInt(filters.price.min.toString()).toLocaleString('fa') }}
           {{ parseInt(filters.price.min.toString()).toLocaleString('fa') }}
-          تومان
+          ریال
         </div>
         </div>
         <div class="sidebar__price-label-max">
         <div class="sidebar__price-label-max">
           تا
           تا
           {{ parseInt(filters.price.max.toString()).toLocaleString('fa') }}
           {{ parseInt(filters.price.max.toString()).toLocaleString('fa') }}
-          تومان
+          ریال
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
@@ -195,7 +195,7 @@ export default {
         unit_price__lt: this.filters.price.max,
         unit_price__lt: this.filters.price.max,
         unit_price__gte: this.filters.price.min,
         unit_price__gte: this.filters.price.min,
         inventory__gte: this.filters.exist ? 1 : null,
         inventory__gte: this.filters.exist ? 1 : null,
-        category__in: this.filters.categories,
+        store_category__in: this.filters.categories,
         physical_type__in: this.filters.types.filter(tp => this.physicalTypes.includes(tp)),
         physical_type__in: this.filters.types.filter(tp => this.physicalTypes.includes(tp)),
         medias__type__in: this.filters.types.filter(tp => this.virtualTypes.includes(tp))
         medias__type__in: this.filters.types.filter(tp => this.virtualTypes.includes(tp))
       }
       }

+ 9 - 8
src/components/Widgets/Store/StorePackage/AdminStorePackageCreate/AdminStorePackageCreate.vue

@@ -30,9 +30,9 @@ import { shallowRef } from 'vue'
 import { EntityCreate } from 'quasar-crud'
 import { EntityCreate } from 'quasar-crud'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
-import BtnControl from 'components/Control/btn.vue'
-import ProductSelector from 'components/FormBuilderCustumComponents/ProductSelector.vue'
-import ContentCategorySelector from 'components/FormBuilderCustumComponents/ContentCategorySelector.vue'
+import BtnControl from 'src/components/Control/btn.vue'
+import ProductSelector from 'src/components/FormBuilderCustumComponents/ProductSelector.vue'
+import ContentCategorySelector from 'src/components/FormBuilderCustumComponents/ContentCategorySelector.vue'
 
 
 const BtnControlComp = shallowRef(BtnControl)
 const BtnControlComp = shallowRef(BtnControl)
 const ProductSelectorComp = shallowRef(ProductSelector)
 const ProductSelectorComp = shallowRef(ProductSelector)
@@ -42,22 +42,23 @@ export default {
   name: 'AdminContentCreate',
   name: 'AdminContentCreate',
   components: { EntityCreate },
   components: { EntityCreate },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
-  data: () => {
+  data () {
     return {
     return {
       mounted: false,
       mounted: false,
       entityLoading: false,
       entityLoading: false,
-      api: APIGateway.content.APIAdresses.base,
+      api: APIGateway.package.APIAdresses.base,
       entityIdKey: 'id',
       entityIdKey: 'id',
       entityParamKey: 'id',
       entityParamKey: 'id',
-      showRouteName: 'Admin.Content.Show',
+      showRouteName: 'Admin.Store.Package.Show',
       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' },
         { type: 'file', name: 'thumbnail', responseKey: 'thumbnail', label: 'عکس', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'file', name: 'thumbnail', responseKey: 'thumbnail', label: 'عکس', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
-        { type: 'inputEditor', name: 'specification', responseKey: 'specification', label: 'specification', col: 'col-md-12 col-12' },
-        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد', placeholder: ' ', col: 'col-md-3 col-12' },
+        { type: 'checkbox', name: 'is_physical', responseKey: 'is_physical', label: 'فیزیکی', placeholder: ' ', col: 'col-md-3 col-12' },
+        { type: 'checkbox', name: 'is_hidden', responseKey: 'is_hidden', label: 'پنهان باشد؟', placeholder: ' ', value: true, col: 'col-md-9 col-12' },
+        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد(ریال)', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-2 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-2 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-2 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-2 col-12' },

+ 56 - 3
src/components/Widgets/Store/StorePackage/AdminStorePackageList/AdminStorePackageList.vue

@@ -39,6 +39,7 @@
           <q-tab-panel name="packages"
           <q-tab-panel name="packages"
                        class="q-pa-none">
                        class="q-pa-none">
             <entity-index v-if="mounted"
             <entity-index v-if="mounted"
+                          ref="entityIndex"
                           v-model:value="inputs"
                           v-model:value="inputs"
                           title="لیست بسته ها"
                           title="لیست بسته ها"
                           :api="api"
                           :api="api"
@@ -75,11 +76,17 @@
 </template>
 </template>
 
 
 <script>
 <script>
+import { shallowRef } from 'vue'
 import { EntityIndex } from 'quasar-crud'
 import { EntityIndex } from 'quasar-crud'
 import { Package } from 'src/models/Package.js'
 import { Package } from 'src/models/Package.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
-import AdminStoreProductList from 'components/Widgets/Store/StoreProduct/AdminStoreProductList/AdminStoreProductList.vue'
+import BtnControl from 'src/components/Control/btn.vue'
+import ContentCategorySelector from 'src/components/FormBuilderCustumComponents/ContentCategorySelector.vue'
+import AdminStoreProductList from 'src/components/Widgets/Store/StoreProduct/AdminStoreProductList/AdminStoreProductList.vue'
+
+const BtnControlComp = shallowRef(BtnControl)
+const ContentCategorySelectorComp = shallowRef(ContentCategorySelector)
 
 
 export default {
 export default {
   name: 'AdminStorePackageList',
   name: 'AdminStorePackageList',
@@ -96,7 +103,34 @@ export default {
         perPage: 'per_page',
         perPage: 'per_page',
         pageKey: 'page'
         pageKey: 'page'
       },
       },
-      inputs: [],
+      inputs: [
+        { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
+        { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
+        {
+          type: 'input',
+          name: 'search',
+          label: 'جست جو',
+          placeholder: ' ',
+          col: 'col-md-4 col-12'
+        },
+        {
+          type: 'select',
+          name: 'is_hidden',
+          label: 'وضعیت نمایش',
+          options: (new Package()).is_hiddenEnums,
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        {
+          type: 'select',
+          name: 'is_physical',
+          label: 'نوع بسته',
+          options: (new Package()).is_physicalEnums,
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: this.search, col: 'col-md-2 col-12' }
+      ],
       table: {
       table: {
         columns: [
         columns: [
           {
           {
@@ -132,7 +166,14 @@ export default {
             required: true,
             required: true,
             label: 'دسته فروشگاه',
             label: 'دسته فروشگاه',
             align: 'left',
             align: 'left',
-            field: row => row.store_category_info?.parent?.parent?.title + '، ' + row.store_category_info?.parent?.title + '، ' + row.store_category_info?.title
+            field: row => (row.store_category_info?.parent?.parent?.title || '-') + '، ' + (row.store_category_info?.parent?.title || '-') + '، ' + (row.store_category_info?.title || '-')
+          },
+          {
+            name: 'store_category1',
+            required: true,
+            label: 'دسته محتوا',
+            align: 'left',
+            field: row => (row.content_category_info?.parent?.parent?.title || '-') + '، ' + (row.content_category_info?.parent?.title || '-') + '، ' + (row.content_category_info?.title || '-')
           },
           },
           {
           {
             name: 'category_info',
             name: 'category_info',
@@ -141,6 +182,13 @@ export default {
             align: 'left',
             align: 'left',
             field: row => parseInt(row.unit_price.toString()).toLocaleString('fa')
             field: row => parseInt(row.unit_price.toString()).toLocaleString('fa')
           },
           },
+          {
+            name: 'is_hidden',
+            required: true,
+            label: 'نمایش',
+            align: 'left',
+            field: row => (new Package(row)).is_hidden_info.label
+          },
           {
           {
             name: 'action',
             name: 'action',
             required: true,
             required: true,
@@ -156,6 +204,11 @@ export default {
   },
   },
   mounted() {
   mounted() {
     this.mounted = true
     this.mounted = true
+  },
+  methods: {
+    search () {
+      this.$refs.entityIndex.search()
+    }
   }
   }
 }
 }
 </script>
 </script>

+ 3 - 2
src/components/Widgets/Store/StorePackage/AdminStorePackageShow/AdminStorePackageShow.vue

@@ -62,8 +62,9 @@ export default {
         { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
-        { type: 'inputEditor', name: 'specification', responseKey: 'specification', label: 'specification', col: 'col-md-12 col-12' },
-        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد', placeholder: ' ', col: 'col-md-3 col-12' },
+        { type: 'checkbox', name: 'is_physical', responseKey: 'is_physical', label: 'فیزیکی', placeholder: ' ', col: 'col-md-3 col-12' },
+        { type: 'checkbox', name: 'is_hidden', responseKey: 'is_hidden', label: 'پنهان باشد؟', placeholder: ' ', col: 'col-md-9 col-12' },
+        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد(ریال)', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-2 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-2 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-2 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-2 col-12' },

+ 114 - 44
src/components/Widgets/Store/StorePackage/StorePackageShow/StorePackageShow.vue

@@ -11,29 +11,22 @@
           <div class="package-show__title">
           <div class="package-show__title">
             {{ storePackage.title }}
             {{ storePackage.title }}
           </div>
           </div>
-          <div class="package-show__attrs">
-            <div class="package-show__attrs-title">
-              ویژگی ها:
-            </div>
-            <div class="package-show__attrs-items">
-              <div class="package-show__attrs-item">
-                <div class="package-show__attrs-item-label">
-                  وزن:
-                </div>
-                <div class="package-show__attrs-item-value">
-                  ۲۰۰ گرم
-                </div>
-              </div>
-              <div class="package-show__attrs-item">
-                <div class="package-show__attrs-item-label">
-                  وزن:
-                </div>
-                <div class="package-show__attrs-item-value">
-                  ۲۰۰ گرم
-                </div>
-              </div>
-            </div>
-          </div>
+          <!--          <div class="package-show__attrs">-->
+        <!--            <div class="package-show__attrs-title">-->
+        <!--              ویژگی ها:-->
+        <!--            </div>-->
+        <!--            <div class="package-show__attrs-items">-->
+        <!--              <div class="package-show__attrs-item">-->
+        <!--                <div class="package-show__attrs-item-label">-->
+        <!--                  وزن:-->
+        <!--                </div>-->
+        <!--                <div class="package-show__attrs-item-value">-->
+        <!--                  {{ storePackage.weight }}-->
+        <!--                  گرم-->
+        <!--                </div>-->
+        <!--              </div>-->
+        <!--            </div>-->
+        <!--          </div>-->
         </div>
         </div>
       </div>
       </div>
       <div class="package-show__summery-price">
       <div class="package-show__summery-price">
@@ -45,22 +38,54 @@
           <q-icon name="shopping_cart" />
           <q-icon name="shopping_cart" />
           قیمت محصول
           قیمت محصول
         </div>
         </div>
-        <div class="package-show__summery-price-base">
-          {{ storePackage.unit_price }}
-          تومان
+        <div v-if="storePackage.unit_price !== storePackage.sellable_price"
+             class="package-show__summery-price-base">
+          {{ parseInt(storePackage.unit_price).toLocaleString('fa') }}
+          ریال
         </div>
         </div>
         <div class="package-show__summery-price-final">
         <div class="package-show__summery-price-final">
-          {{ storePackage.getFinalPrice() }}
-          تومان
+          {{ parseInt(storePackage.sellable_price).toLocaleString('fa') }}
+          ریال
         </div>
         </div>
-        <q-btn label="افزودن به سبد"
-               outline
-               color="primary"
-               :loading="addToCartLoading"
-               class="package-show__summery-price-action"
-               @click="addToCart" />
+        <div class="package-show__summery-price-action">
+          <template v-if="!storePackage.is_add_to_basket_disabled">
+            <q-btn v-if="!basketItem && storePackage.inventory > 0"
+                   class="package-btn-add-to-cart"
+                   :loading="addToCartLoading"
+                   color="primary"
+                   @click="addToCart">
+              افزودن به سبد
+            </q-btn>
+            <cart-count-action v-else
+                               :basket-item="basketItem"
+                               @increase="onIncrease"
+                               @decrease="onDecrease"
+                               @remove="onRemove" />
+          </template>
+          <q-btn v-if="storePackage.inventory === 0"
+                 class="package-btn-add-to-cart"
+                 :loading="addToCartLoading"
+                 disable
+                 color="primary">
+            ناموجود
+          </q-btn>
+        </div>
+        <!--        <q-btn label="افزودن به سبد"-->
+        <!--               outline-->
+        <!--               color="primary"-->
+        <!--               :loading="addToCartLoading"-->
+        <!--               class="package-show__summery-price-action"-->
+        <!--               @click="addToCart" />-->
       </div>
       </div>
     </div>
     </div>
+    <div v-if="storePackage.description"
+         class="package-show__descriptions">
+      <q-banner class="package-show__descriptions-title">
+        توضیحات:
+      </q-banner>
+      <div class="package-show__descriptions-data"
+           v-html="storePackage.description" />
+    </div>
     <div class="package-show__products">
     <div class="package-show__products">
       <q-expansion-item expand-separator
       <q-expansion-item expand-separator
                         default-opened
                         default-opened
@@ -73,14 +98,7 @@
       </q-expansion-item>
       </q-expansion-item>
 
 
     </div>
     </div>
-    <div v-if="storePackage.description"
-         class="package-show__descriptions">
-      <q-banner class="package-show__descriptions-title">
-        توضیحات:
-      </q-banner>
-      <div class="package-show__descriptions-data"
-           v-html="storePackage.description" />
-    </div>
+
   </div>
   </div>
 </template>
 </template>
 
 
@@ -92,10 +110,11 @@ import { APIGateway } from 'src/api/APIGateway.js'
 import ProductItem from './components/productItem.vue'
 import ProductItem from './components/productItem.vue'
 import DiscountBadge from './components/discountBadge.vue'
 import DiscountBadge from './components/discountBadge.vue'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import CartCountAction from 'src/components/cart/cartCountAction/cartCountAction.vue'
 
 
 export default {
 export default {
   name: 'ContentShow',
   name: 'ContentShow',
-  components: { Breadcrumbs, ProductItem, DiscountBadge },
+  components: { Breadcrumbs, ProductItem, DiscountBadge, CartCountAction },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
   data () {
   data () {
     return {
     return {
@@ -106,6 +125,17 @@ export default {
   computed: {
   computed: {
     packageId () {
     packageId () {
       return this.$route.params.id
       return this.$route.params.id
+    },
+    basket () {
+      return this.$store.getters['Shop/basket']
+    },
+    basketItem () {
+      const target = this.basket.items_info.list.find(basketItem => basketItem.package === this.storePackage.id)
+      if (!target) {
+        return null
+      }
+
+      return target
     }
     }
   },
   },
   mounted() {
   mounted() {
@@ -115,12 +145,36 @@ export default {
   methods: {
   methods: {
     addToCart () {
     addToCart () {
       this.addToCartLoading = true
       this.addToCartLoading = true
-      APIGateway.basketItem.addProduct(this.packageId, 1)
+      APIGateway.basketItem.addPackage(this.packageId, 1)
         .finally(() => {
         .finally(() => {
           this.addToCartLoading = false
           this.addToCartLoading = false
           this.checkoutReview()
           this.checkoutReview()
         })
         })
     },
     },
+    onIncrease () {
+      this.addToCartLoading = true
+      APIGateway.basketItem.incrementPackage(this.basketItem.package)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
+    onDecrease () {
+      this.addToCartLoading = true
+      APIGateway.basketItem.decrementPackage(this.basketItem.package)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
+    onRemove () {
+      this.addToCartLoading = true
+      APIGateway.basketItem.remove(this.basketItem.id)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
     checkoutReview () {
     checkoutReview () {
       this.basket.loading = true
       this.basket.loading = true
       APIGateway.basket.checkoutReview()
       APIGateway.basket.checkoutReview()
@@ -257,6 +311,7 @@ export default {
       }
       }
     }
     }
     .package-show__summery-price {
     .package-show__summery-price {
+      position: relative;
       width: $price-section-with;
       width: $price-section-with;
       border-radius: 8px;
       border-radius: 8px;
       background: #F6F6F6;
       background: #F6F6F6;
@@ -287,6 +342,9 @@ export default {
       .package-show__summery-price-action {
       .package-show__summery-price-action {
         width: 100%;
         width: 100%;
         background: white !important;
         background: white !important;
+        .package-btn-add-to-cart {
+          width: 100%;
+        }
       }
       }
       @media screen and (max-width: 900px) {
       @media screen and (max-width: 900px) {
         & {
         & {
@@ -296,6 +354,17 @@ export default {
         }
         }
       }
       }
     }
     }
+    @media screen and (max-width: 1024px) {
+      & {
+        flex-flow: column;
+      }
+      .package-show__summery-info {
+        width: 100%;
+      }
+      .package-show__summery-price {
+        width: 100%;
+      }
+    }
   }
   }
   .package-show__products {
   .package-show__products {
     margin-bottom: 47px;
     margin-bottom: 47px;
@@ -337,6 +406,7 @@ export default {
     }
     }
   }
   }
   .package-show__descriptions {
   .package-show__descriptions {
+    margin-bottom: 47px;
     padding: 32px 32px 70px 32px;
     padding: 32px 32px 70px 32px;
     border: 1px solid #DFE1EC;
     border: 1px solid #DFE1EC;
     border-radius: 8px;
     border-radius: 8px;

+ 4 - 3
src/components/Widgets/Store/StorePackage/StorePackageShow/components/productItem.vue

@@ -29,11 +29,12 @@
           قیمت محصول:
           قیمت محصول:
         </div>
         </div>
         <div class="product-item__price-info-value">
         <div class="product-item__price-info-value">
-          {{ product.getFinalPrice() }}
-          تومان
+          {{ parseInt(product.unit_price).toLocaleString('fa') }}
+          ریال
         </div>
         </div>
       </div>
       </div>
-      <q-btn color="primary"
+      <q-btn v-if="!product.is_hidden"
+             color="primary"
              :to="{ name: 'Public.Product', params: { id: product.id }}"
              :to="{ name: 'Public.Product', params: { id: product.id }}"
              class="product-item__price-action">
              class="product-item__price-action">
         مشاهده
         مشاهده

+ 4 - 3
src/components/Widgets/Store/StoreProduct/AdminStoreProductCreate/AdminStoreProductCreate.vue

@@ -58,14 +58,15 @@ export default {
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', col: 'col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', col: 'col-12' },
-        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد', placeholder: ' ', col: 'col-md-6 col-12' },
+        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد(ریال)', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-6 col-12' },
-        { type: 'checkbox', name: 'is_physical', responseKey: 'is_physical', label: 'فیزیکی', placeholder: ' ', value: false, col: 'col-12' },
+        { type: 'checkbox', name: 'is_physical', responseKey: 'is_physical', label: 'فیزیکی', placeholder: ' ', value: false, col: 'col-md-3 col-12' },
+        { type: 'checkbox', name: 'is_hidden', responseKey: 'is_hidden', label: 'پنهان باشد؟', placeholder: ' ', value: true, col: 'col-md-9 col-12' },
         { type: 'select', name: 'physical_type', responseKey: 'physical_type', label: 'نوع محصول فیزیکی', options: (new Product()).physical_typeEnums, placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'select', name: 'physical_type', responseKey: 'physical_type', label: 'نوع محصول فیزیکی', options: (new Product()).physical_typeEnums, placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'weight', responseKey: 'weight', label: 'وزن', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'weight', responseKey: 'weight', label: 'وزن', placeholder: ' ', col: 'col-md-3 col-12' },
-        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'ویرایش محصول', placeholder: ' ', ignoreValue: true, atClick: () => {}, col: 'col-md-6' }
+        { type: BtnControlComp, name: 'btn', responseKey: 'btn', label: 'ایجاد محصول', placeholder: ' ', ignoreValue: true, atClick: () => {}, col: 'col-md-6' }
       ]
       ]
     }
     }
   },
   },

+ 55 - 2
src/components/Widgets/Store/StoreProduct/AdminStoreProductList/AdminStoreProductList.vue

@@ -8,6 +8,7 @@
              :to="{name: 'Admin.Store.Product.Create'}" />
              :to="{name: 'Admin.Store.Product.Create'}" />
     </div>
     </div>
     <entity-index v-if="mounted"
     <entity-index v-if="mounted"
+                  ref="entityIndex"
                   v-model:value="inputs"
                   v-model:value="inputs"
                   title="لیست محصولات"
                   title="لیست محصولات"
                   :api="api"
                   :api="api"
@@ -40,10 +41,16 @@
 </template>
 </template>
 
 
 <script>
 <script>
+import { shallowRef } from 'vue'
 import { EntityIndex } from 'quasar-crud'
 import { EntityIndex } from 'quasar-crud'
 import { Product } from 'src/models/Product.js'
 import { Product } from 'src/models/Product.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
+import BtnControl from 'src/components/Control/btn.vue'
+import ContentCategorySelector from 'src/components/FormBuilderCustumComponents/ContentCategorySelector.vue'
+
+const BtnControlComp = shallowRef(BtnControl)
+const ContentCategorySelectorComp = shallowRef(ContentCategorySelector)
 
 
 export default {
 export default {
   name: 'AdminContentList',
   name: 'AdminContentList',
@@ -65,7 +72,34 @@ export default {
         perPage: 'per_page',
         perPage: 'per_page',
         pageKey: 'page'
         pageKey: 'page'
       },
       },
-      inputs: [],
+      inputs: [
+        { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
+        { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
+        {
+          type: 'input',
+          name: 'search',
+          label: 'جست جو',
+          placeholder: ' ',
+          col: 'col-md-4 col-12'
+        },
+        {
+          type: 'select',
+          name: 'is_hidden',
+          label: 'وضعیت نمایش',
+          options: (new Product()).is_hiddenEnums,
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        {
+          type: 'select',
+          name: 'is_physical',
+          label: 'نوع محصول',
+          options: (new Product()).is_physicalEnums,
+          placeholder: ' ',
+          col: 'col-md-3 col-12'
+        },
+        { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: this.search, col: 'col-md-2 col-12' }
+      ],
       table: {
       table: {
         columns: [
         columns: [
           {
           {
@@ -101,7 +135,14 @@ export default {
             required: true,
             required: true,
             label: 'دسته فروشگاه',
             label: 'دسته فروشگاه',
             align: 'left',
             align: 'left',
-            field: row => row.store_category_info?.parent?.parent?.title + '، ' + row.store_category_info?.parent?.title + '، ' + row.store_category_info?.title
+            field: row => (row.store_category_info?.parent?.parent?.title || '-') + '، ' + (row.store_category_info?.parent?.title || '-') + '، ' + (row.store_category_info?.title || '-')
+          },
+          {
+            name: 'store_category1',
+            required: true,
+            label: 'دسته محتوا',
+            align: 'left',
+            field: row => (row.content_category_info?.parent?.parent?.title || '-') + '، ' + (row.content_category_info?.parent?.title || '-') + '، ' + (row.content_category_info?.title || '-')
           },
           },
           {
           {
             name: 'unit_price',
             name: 'unit_price',
@@ -110,6 +151,13 @@ export default {
             align: 'left',
             align: 'left',
             field: row => parseInt(row.unit_price.toString()).toLocaleString('fa')
             field: row => parseInt(row.unit_price.toString()).toLocaleString('fa')
           },
           },
+          {
+            name: 'is_hidden',
+            required: true,
+            label: 'نمایش',
+            align: 'left',
+            field: row => (new Product(row)).is_hidden_info.label
+          },
           {
           {
             name: 'action',
             name: 'action',
             required: true,
             required: true,
@@ -125,6 +173,11 @@ export default {
   },
   },
   mounted() {
   mounted() {
     this.mounted = true
     this.mounted = true
+  },
+  methods: {
+    search () {
+      this.$refs.entityIndex.search()
+    }
   }
   }
 }
 }
 </script>
 </script>

+ 3 - 3
src/components/Widgets/Store/StoreProduct/AdminStoreProductShow/AdminStoreProductShow.vue

@@ -62,11 +62,11 @@ export default {
         { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'content_category', responseKey: 'content_category_info', categoryType: 'content', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: ContentCategorySelectorComp, name: 'store_category', responseKey: 'store_category_info', categoryType: 'store', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
         { type: 'inputEditor', name: 'description', responseKey: 'description', label: 'توضیحات', col: 'col-md-12 col-12' },
-        { type: 'inputEditor', name: 'specification', responseKey: 'specification', label: 'specification', col: 'col-md-12 col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', sourceType: 'PRODUCT', col: 'col-md-12 col-12' },
         { type: ContentMediasComp, name: 'medias', responseKey: 'medias_info', sourceType: 'PRODUCT', col: 'col-md-12 col-12' },
-        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد', placeholder: ' ', col: 'col-md-6 col-12' },
+        { type: 'input', name: 'unit_price', responseKey: 'unit_price', label: 'قیمت واحد(ریال)', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-6 col-12' },
         { type: 'input', name: 'discount_percent', responseKey: 'discount_percent', label: 'درصد تخفیف', placeholder: ' ', col: 'col-md-6 col-12' },
-        { type: 'checkbox', name: 'is_physical', responseKey: 'is_physical', label: 'فیزیکی', placeholder: ' ', col: 'col-12' },
+        { type: 'checkbox', name: 'is_physical', responseKey: 'is_physical', label: 'فیزیکی', placeholder: ' ', col: 'col-md-3 col-12' },
+        { type: 'checkbox', name: 'is_hidden', responseKey: 'is_hidden', label: 'پنهان باشد؟', placeholder: ' ', col: 'col-md-9 col-12' },
         { type: 'select', name: 'physical_type', responseKey: 'physical_type', label: 'نوع محصول فیزیکی', options: (new Product()).physical_typeEnums, placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'select', name: 'physical_type', responseKey: 'physical_type', label: 'نوع محصول فیزیکی', options: (new Product()).physical_typeEnums, placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'inventory', responseKey: 'inventory', label: 'موجودی', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-3 col-12' },
         { type: 'input', name: 'maximum_order_count', responseKey: 'maximum_order_count', label: 'سقف سفارش مجاز', placeholder: ' ', col: 'col-md-3 col-12' },

+ 1 - 1
src/components/Widgets/Store/StoreProduct/ProductCarousel/ProductCarousel.vue

@@ -76,7 +76,7 @@ export default {
         900: {
         900: {
           perPage: 2
           perPage: 2
         },
         },
-        500: {
+        600: {
           perPage: 1
           perPage: 1
         }
         }
       },
       },

+ 49 - 27
src/components/Widgets/Store/StoreProduct/ProductCarousel/productItem.vue

@@ -1,6 +1,15 @@
 <template>
 <template>
   <div class="product-item">
   <div class="product-item">
     <div class="product-item__badges">
     <div class="product-item__badges">
+      <div v-if="product.is_purchased"
+           class="product-item__badge">
+        <div class="product-item__badge-title">
+          خریداری شده است
+        </div>
+        <div class="product-item__badge-icon">
+          <q-icon name="done_all" />
+        </div>
+      </div>
       <div class="product-item__badge">
       <div class="product-item__badge">
         <div class="product-item__badge-title">
         <div class="product-item__badge-title">
           {{ product.is_physical_info.label }}
           {{ product.is_physical_info.label }}
@@ -29,11 +38,11 @@
       <div v-if="product.unit_price !== product.sellable_price"
       <div v-if="product.unit_price !== product.sellable_price"
            class="product-item__price-base">
            class="product-item__price-base">
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
         {{ parseInt(product.unit_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
       <div class="product-item__price-final">
       <div class="product-item__price-final">
         {{ parseInt(product.sellable_price).toLocaleString('fa') }}
         {{ parseInt(product.sellable_price).toLocaleString('fa') }}
-        تومان
+        ریال
       </div>
       </div>
     </div>
     </div>
     <div class="product-item__actions">
     <div class="product-item__actions">
@@ -43,18 +52,27 @@
              outline>
              outline>
         مشاهده جزییات
         مشاهده جزییات
       </q-btn>
       </q-btn>
-      <q-btn v-if="!basketItem"
-             class="product-item__actions-add-to-cart"
+      <template v-if="!product.is_add_to_basket_disabled">
+        <q-btn v-if="!basketItem && product.inventory > 0"
+               class="product-item__actions-add-to-cart"
+               :loading="addToCartLoading"
+               color="primary"
+               @click="addToCart">
+          افزودن به سبد
+        </q-btn>
+        <cart-count-action v-else
+                           :basket-item="basketItem"
+                           @increase="onIncrease"
+                           @decrease="onDecrease"
+                           @remove="onRemove" />
+      </template>
+      <q-btn v-if="product.inventory === 0"
+             class="product-btn-add-to-cart"
              :loading="addToCartLoading"
              :loading="addToCartLoading"
-             color="primary"
-             @click="addToCart">
-        افزودن به سبد
+             disable
+             color="primary">
+        ناموجود
       </q-btn>
       </q-btn>
-      <cart-count-action v-else
-                         :basket-item="basketItem"
-                         @increase="onIncrease"
-                         @decrease="onDecrease"
-                         @remove="onRemove" />
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>
@@ -193,14 +211,16 @@ export default {
     padding: 0 4px;
     padding: 0 4px;
     text-align: center;
     text-align: center;
     font-size: 18px;
     font-size: 18px;
-    margin-bottom: 52px;
+    //margin-bottom: 52px;
+    margin-bottom: 8px;
   }
   }
   .product-item__price {
   .product-item__price {
     display: flex;
     display: flex;
     align-items: center;
     align-items: center;
     justify-content: space-between;
     justify-content: space-between;
     padding: 0 20px;
     padding: 0 20px;
-    margin-bottom: 43px;
+    //margin-bottom: 43px;
+    margin-bottom: 8px;
     .product-item__price-final {
     .product-item__price-final {
       font-size: 18px;
       font-size: 18px;
       color: #475F4A;
       color: #475F4A;
@@ -212,7 +232,9 @@ export default {
     }
     }
   }
   }
   .product-item__actions {
   .product-item__actions {
-    display: none;
+    //display: none;
+    display: flex;
+    flex-flow: column;
     margin: 0 25px 25px 25px;
     margin: 0 25px 25px 25px;
     .product-item__actions-view {
     .product-item__actions-view {
       background: white;
       background: white;
@@ -231,18 +253,18 @@ export default {
         }
         }
       }
       }
     }
     }
-    :deep(.product-item__thumbnail) {
-      margin: 10px auto 4px auto;
-      img {
-        width: 90px;
-      }
-    }
-    .product-item__title {
-      margin-bottom: 44px;
-    }
-    .product-item__price {
-      margin-bottom: 27px;
-    }
+    //:deep(.product-item__thumbnail) {
+    //  margin: 10px auto 4px auto;
+    //  img {
+    //    width: 90px;
+    //  }
+    //}
+    //.product-item__title {
+    //  margin-bottom: 44px;
+    //}
+    //.product-item__price {
+    //  margin-bottom: 27px;
+    //}
     .product-item__actions {
     .product-item__actions {
       display: flex;
       display: flex;
       flex-flow: column;
       flex-flow: column;

+ 88 - 16
src/components/Widgets/Store/StoreProduct/StoreProductShow/StoreProductShow.vue

@@ -11,6 +11,11 @@
           <div class="product-show__title">
           <div class="product-show__title">
             {{ product.title }}
             {{ product.title }}
           </div>
           </div>
+          <q-badge v-if="product.is_purchased">
+            <q-icon name="done_all"
+                    class="q-mr-sm" />
+            این محصول را خریده اید
+          </q-badge>
           <div class="product-show__attrs">
           <div class="product-show__attrs">
             <div class="product-show__attrs-title">
             <div class="product-show__attrs-title">
               ویژگی ها:
               ویژگی ها:
@@ -60,18 +65,35 @@
         <div v-if="product.unit_price !== product.sellable_price"
         <div v-if="product.unit_price !== product.sellable_price"
              class="product-show__summery-price-base">
              class="product-show__summery-price-base">
           {{ parseInt(product.unit_price).toLocaleString('fa') }}
           {{ parseInt(product.unit_price).toLocaleString('fa') }}
-          تومان
+          ریال
         </div>
         </div>
         <div class="product-show__summery-price-final">
         <div class="product-show__summery-price-final">
           {{ parseInt(product.sellable_price).toLocaleString('fa') }}
           {{ parseInt(product.sellable_price).toLocaleString('fa') }}
-          تومان
+          ریال
+        </div>
+        <div class="product-show__summery-price-action">
+          <template v-if="!product.is_add_to_basket_disabled">
+            <q-btn v-if="!basketItem && product.inventory > 0"
+                   class="product-btn-add-to-cart"
+                   :loading="addToCartLoading"
+                   color="primary"
+                   @click="addToCart">
+              افزودن به سبد
+            </q-btn>
+            <cart-count-action v-else
+                               :basket-item="basketItem"
+                               @increase="onIncrease"
+                               @decrease="onDecrease"
+                               @remove="onRemove" />
+          </template>
+          <q-btn v-if="product.inventory === 0"
+                 class="product-btn-add-to-cart"
+                 :loading="addToCartLoading"
+                 disable
+                 color="primary">
+            ناموجود
+          </q-btn>
         </div>
         </div>
-        <q-btn label="افزودن به سبد"
-               outline
-               color="primary"
-               :loading="addToCartLoading"
-               class="product-show__summery-price-action"
-               @click="addToCart" />
       </div>
       </div>
     </div>
     </div>
     <div v-if="product.description"
     <div v-if="product.description"
@@ -82,7 +104,7 @@
       <div class="product-show__descriptions-data"
       <div class="product-show__descriptions-data"
            v-html="product.description" />
            v-html="product.description" />
     </div>
     </div>
-    <div v-if="product.medias_info"
+    <div v-if="product.is_purchased && product.medias_info.list.length > 0"
          class="product-show__medias">
          class="product-show__medias">
       <medias-component :medias="product.medias_info" />
       <medias-component :medias="product.medias_info" />
     </div>
     </div>
@@ -97,10 +119,12 @@ import { APIGateway } from 'src/api/APIGateway.js'
 import MediasComponent from './components/Medias.vue'
 import MediasComponent from './components/Medias.vue'
 import DiscountBadge from './components/discountBadge.vue'
 import DiscountBadge from './components/discountBadge.vue'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import CartCountAction from 'src/components/cart/cartCountAction/cartCountAction.vue'
+// import CartCountAction from './components/cartCountAction.vue'
 
 
 export default {
 export default {
-  name: 'ContentShow',
-  components: { Breadcrumbs, MediasComponent, DiscountBadge },
+  name: 'StoreProductShow',
+  components: { Breadcrumbs, MediasComponent, DiscountBadge, CartCountAction },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
   data () {
   data () {
     return {
     return {
@@ -111,11 +135,22 @@ export default {
   computed: {
   computed: {
     productId () {
     productId () {
       return this.$route.params.id
       return this.$route.params.id
+    },
+    basket () {
+      return this.$store.getters['Shop/basket']
+    },
+    basketItem () {
+      const target = this.basket.items_info.list.find(basketItem => basketItem.product === this.product.id)
+      if (!target) {
+        return null
+      }
+
+      return target
     }
     }
   },
   },
   mounted() {
   mounted() {
     this.setBreadcrumbsLoading()
     this.setBreadcrumbsLoading()
-    this.getPackage()
+    this.getProduct()
   },
   },
   methods: {
   methods: {
     addToCart () {
     addToCart () {
@@ -126,17 +161,41 @@ export default {
           this.checkoutReview()
           this.checkoutReview()
         })
         })
     },
     },
+    onIncrease () {
+      this.addToCartLoading = true
+      APIGateway.basketItem.incrementProduct(this.basketItem.product)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
+    onDecrease () {
+      this.addToCartLoading = true
+      APIGateway.basketItem.decrementProduct(this.basketItem.product)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
+    onRemove () {
+      this.addToCartLoading = true
+      APIGateway.basketItem.remove(this.basketItem.id)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
     checkoutReview () {
     checkoutReview () {
-      this.basket.loading = true
+      this.addToCartLoading = true
       APIGateway.basket.checkoutReview()
       APIGateway.basket.checkoutReview()
         .then((basket) => {
         .then((basket) => {
           this.$store.commit('Shop/updateBasket', new Basket(basket))
           this.$store.commit('Shop/updateBasket', new Basket(basket))
         })
         })
         .finally(() => {
         .finally(() => {
-          this.basket.loading = false
+          this.addToCartLoading = false
         })
         })
     },
     },
-    getPackage () {
+    getProduct () {
       this.product.loading = true
       this.product.loading = true
       APIGateway.product.get(this.productId)
       APIGateway.product.get(this.productId)
         .then((product) => {
         .then((product) => {
@@ -295,7 +354,9 @@ export default {
       }
       }
       .product-show__summery-price-action {
       .product-show__summery-price-action {
         width: 100%;
         width: 100%;
-        background: white !important;
+        .product-btn-add-to-cart {
+          width: 100%;
+        }
       }
       }
       @media screen and (max-width: 900px) {
       @media screen and (max-width: 900px) {
         & {
         & {
@@ -305,6 +366,17 @@ export default {
         }
         }
       }
       }
     }
     }
+    @media screen and (max-width: 1024px) {
+      & {
+        flex-flow: column;
+      }
+      .product-show__summery-info {
+        width: 100%;
+      }
+      .product-show__summery-price {
+        width: 100%;
+      }
+    }
   }
   }
   .product-show__products {
   .product-show__products {
     margin-bottom: 47px;
     margin-bottom: 47px;

+ 1 - 1
src/components/Widgets/Store/StoreProduct/StoreProductShow/components/productItem.vue

@@ -30,7 +30,7 @@
         </div>
         </div>
         <div class="product-item__price-info-value">
         <div class="product-item__price-info-value">
           {{ product.getFinalPrice() }}
           {{ product.getFinalPrice() }}
-          تومان
+          ریال
         </div>
         </div>
       </div>
       </div>
       <q-btn color="primary"
       <q-btn color="primary"

+ 21 - 0
src/components/Widgets/Store/StoreProduct/StoreRelatedProducts/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>

+ 160 - 0
src/components/Widgets/Store/StoreProduct/StoreRelatedProducts/StoreRelatedProducts.vue

@@ -0,0 +1,160 @@
+<template>
+  <div class="StoreRelatedProducts"
+       :style="localOptions.style">
+    <q-card v-if="relatedProducts.list.length > 0"
+            class="StoreRelatedProducts__card">
+      <q-card-section>
+        محصولات مرتبط
+      </q-card-section>
+      <q-separator />
+      <div class="products">
+        <product-item v-for="(product, productIndex) in relatedProducts.list"
+                      :key="productIndex"
+                      :product="product"
+                      :add-to-cart-loading="addToCartLoading"
+                      class="product-item"
+                      @increase="onIncrease"
+                      @decrease="onDecrease"
+                      @remove="onRemove"
+                      @add-to-cart="addToCart" />
+      </div>
+    </q-card>
+  </div>
+</template>
+
+<script>
+import { Basket } from 'src/models/Basket.js'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import ProductItem from './components/productItem.vue'
+import { Product, ProductList } from 'src/models/Product.js'
+
+export default {
+  name: 'StoreRelatedProducts',
+  components: { ProductItem },
+  mixins: [mixinWidget],
+  data () {
+    return {
+      addToCartLoading: false,
+      relatedProducts: new ProductList()
+    }
+  },
+  computed: {
+    productId () {
+      return this.$route.params.id
+    }
+  },
+  mounted () {
+    this.getRelatedProducts()
+  },
+  methods: {
+    getRelatedProducts () {
+      this.relatedProducts.loading = true
+      APIGateway.product.relatedProducts({ id: this.productId })
+        .then((products) => {
+          this.relatedProducts = new ProductList(products.list)
+        })
+        .finally(() => {
+          this.relatedProducts.loading = false
+        })
+    },
+    addToCart (productId) {
+      this.addToCartLoading = true
+      APIGateway.basketItem.addProduct(productId, 1)
+        .finally(() => {
+          this.addToCartLoading = false
+          this.checkoutReview()
+        })
+    },
+    onIncrease (productId) {
+      this.addToCartLoading = true
+      APIGateway.basketItem.incrementProduct(productId)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
+    onDecrease (productId) {
+      this.addToCartLoading = true
+      APIGateway.basketItem.decrementProduct(productId)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
+    onRemove (basketItemId) {
+      this.addToCartLoading = true
+      APIGateway.basketItem.remove(basketItemId)
+        .finally(() => {
+          this.checkoutReview()
+          this.addToCartLoading = false
+        })
+    },
+    checkoutReview () {
+      this.addToCartLoading = true
+      APIGateway.basket.checkoutReview()
+        .then((basket) => {
+          this.$store.commit('Shop/updateBasket', new Basket(basket))
+        })
+        .finally(() => {
+          this.addToCartLoading = false
+        })
+    },
+    getProduct () {
+      this.product.loading = true
+      APIGateway.product.get(this.productId)
+        .then((product) => {
+          this.product = new Product(product)
+          this.updateBreadcrumbs()
+        })
+        .finally(() => {
+          this.product.loading = false
+        })
+    },
+    setBreadcrumbsLoading () {
+      this.$store.commit('AppLayout/updateBreadcrumbs', {
+        visible: false,
+        loading: true
+      })
+    },
+    updateBreadcrumbs () {
+      this.$store.commit('AppLayout/updateBreadcrumbs', {
+        visible: true,
+        loading: false,
+        path: [
+          {
+            label: 'خانه',
+            to: { name: 'Public.Home' }
+          },
+          {
+            label: 'فروشگاه',
+            to: { name: 'Public.Shop' }
+          },
+          {
+            label: this.product.title,
+            to: { name: 'Public.Package', params: { id: this.product } }
+          }
+        ]
+      })
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.StoreRelatedProducts {
+  .StoreRelatedProducts__card {
+    box-shadow: none;
+    border: 1px solid #DFE1EC;
+    border-radius: 8px;
+    .products {
+      .product-item {
+        border-bottom: solid 1px #DFE1EC;
+        &:last-child {
+          border-bottom: none;
+        }
+      }
+    }
+  }
+}
+</style>

+ 248 - 0
src/components/Widgets/Store/StoreProduct/StoreRelatedProducts/components/productItem.vue

@@ -0,0 +1,248 @@
+<template>
+  <div class="product-item">
+    <div class="product-item__meta">
+      <div class="product-item__meta-thumbnail"
+           @click="goToProductPage">
+        <q-img :src="product.thumbnail" />
+      </div>
+      <div class="product-item__meta-info">
+        <div class="product-item__product-title">
+          <router-link :to="{ name: 'Public.Product', params: { id: product.id }}">
+            {{ product.title }}
+          </router-link>
+        </div>
+        <div class="product-item__product-atrs">
+          <q-badge v-if="product.is_purchased">
+            <q-icon name="done_all"
+                    class="q-mr-sm" />
+            این محصول را خریده اید
+          </q-badge>
+          <div class="product-item__product-atrs-title">
+            ویژگی ها
+          </div>
+          <div class="product-item__product-atrs-list">
+            <div class="product-item__product-atr">
+              <div class="product-item__product-atr-label">
+                {{ product.title }}
+              </div>
+              <div class="product-item__product-atr-value">
+                {{ product.title }}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div class="product-item__price">
+      <div class="product-item__price-info">
+        <div class="product-item__price-info-label">
+          <q-icon name="shopping_cart" />
+          قیمت محصول
+        </div>
+        <div class="product-item__price-info-value">
+          <div v-if="product.unit_price !== product.sellable_price"
+               class="product-item__price-base">
+            {{ parseInt(product.unit_price).toLocaleString('fa') }}
+            ریال
+          </div>
+          <div class="product-item__price-final">
+            {{ parseInt(product.sellable_price).toLocaleString('fa') }}
+            ریال
+          </div>
+        </div>
+      </div>
+      <div class="product-item__price-action">
+        <template v-if="!product.is_add_to_basket_disabled">
+          <q-btn v-if="!basketItem && product.inventory > 0"
+                 class="product-btn-add-to-cart"
+                 :loading="addToCartLoading"
+                 color="primary"
+                 @click="addToCart">
+            افزودن به سبد
+          </q-btn>
+          <cart-count-action v-else
+                             :basket-item="basketItem"
+                             :loading="addToCartLoading"
+                             @increase="onIncrease"
+                             @decrease="onDecrease"
+                             @remove="onRemove" />
+        </template>
+        <q-btn v-if="product.inventory === 0"
+               class="product-btn-add-to-cart"
+               :loading="addToCartLoading"
+               disable
+               color="primary">
+          ناموجود
+        </q-btn>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Product } from 'src/models/Product.js'
+import CartCountAction from 'src/components/cart/cartCountAction/cartCountAction.vue'
+
+export default {
+  name: 'productItem',
+  components: { CartCountAction },
+  props: {
+    product: {
+      type: Product,
+      default: new Product()
+    },
+    addToCartLoading: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['add-to-cart', 'increase', 'decrease', 'remove'],
+  computed: {
+    basket () {
+      return this.$store.getters['Shop/basket']
+    },
+    basketItem () {
+      const target = this.basket.items_info.list.find(basketItem => basketItem.product === this.product.id)
+      if (!target) {
+        return null
+      }
+
+      return target
+    }
+  },
+  methods: {
+    goToProductPage () {
+      this.$router.push({ name: 'Public.Product', params: { id: this.product.id } })
+    },
+    addToCart () {
+      this.$emit('add-to-cart', this.product.id)
+    },
+    onIncrease () {
+      this.$emit('increase', this.product.id)
+    },
+    onDecrease () {
+      this.$emit('decrease', this.product.id)
+    },
+    onRemove () {
+      this.$emit('remove', this.basketItem.id)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.product-item {
+  padding: 24px;
+  display: flex;
+  justify-content: space-between;
+  background: #F9F9FB;
+  .product-item__meta {
+    display: flex;
+    gap: 32px;
+    justify-content: flex-start;
+    .product-item__meta-thumbnail {
+      cursor: pointer;
+      width: 123px;
+    }
+    .product-item__meta-info {
+      .product-item__product-title {
+        color: #212121;
+        font-size: 18px;
+        margin-bottom: 24px;
+      }
+      .product-item__product-atrs {
+        .product-item__product-atrs-title {
+          font-size: 16px;
+          letter-spacing: 0;
+          color: #AAA095;
+          margin-bottom: 19px;
+        }
+        .product-item__product-atrs-list {
+          .product-item__product-atr {
+            font-size: 16px;
+            display: flex;
+            gap: 4px;
+            align-items: center;
+            margin-bottom: 31px;
+            justify-content: flex-start;
+            &:before {
+              content: ' ';
+              width: 8px;
+              height: 8px;
+              border-radius: 100%;
+              background: #475F4A;
+              margin-right: 6px;
+            }
+            .product-item__product-atr-label {
+              font-size: 16px;
+              color: #797979;
+            }
+            .product-item__product-atr-value {
+              font-size: 16px;
+              color: #212121;
+            }
+          }
+        }
+      }
+    }
+    @media screen and (max-width: 600px) {
+      & {
+        flex-flow: column;
+      }
+    }
+  }
+  .product-item__price {
+    display: flex;
+    flex-direction: column;
+    gap: 29px;
+    justify-content: center;
+    align-items: flex-end;
+    .product-item__price-info {
+      display: flex;
+      flex-flow: column;
+      justify-content: center;
+      align-items: flex-end;
+      .product-item__price-info-label {
+        font-size: 16px;
+        color: #616161;
+        margin-right: 53px;
+        margin-bottom: 20px;
+        .q-icon {
+          font-size: 20px;
+          color: #AAA095;
+          margin-right: 10px;
+        }
+      }
+      .product-item__price-info-value {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        padding: 0 20px;
+        //margin-bottom: 43px;
+        margin-bottom: 8px;
+        .product-item__price-final {
+          font-size: 24px;
+          color: #475F4A;
+        }
+        .product-item__price-base {
+          font-size: 16px;
+          color: #FF3D3D;
+          text-decoration: line-through;
+          margin-right: 18px;
+        }
+      }
+    }
+    .product-item__price-action {
+      width: 252px;
+      .product-btn-add-to-cart {
+        width: 100%;
+      }
+    }
+  }
+  @media screen and (max-width: 1024px) {
+    & {
+      flex-flow: column;
+    }
+  }
+}
+</style>

+ 56 - 3
src/components/Widgets/Team/AdminTeamShow/AdminTeamShow.vue

@@ -62,6 +62,7 @@
 <script>
 <script>
 import { shallowRef } from 'vue'
 import { shallowRef } from 'vue'
 import { Team } from 'src/models/Team.js'
 import { Team } from 'src/models/Team.js'
+import { User } from 'src/models/User.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { mixinWidget } from 'src/mixin/Mixins.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { EntityEdit, EntityIndex } from 'quasar-crud'
 import { EntityEdit, EntityIndex } from 'quasar-crud'
@@ -69,8 +70,8 @@ import BtnControl from 'src/components/Control/btn.vue'
 import DeleteBtn from 'src/components/Control/DeleteBtn.vue'
 import DeleteBtn from 'src/components/Control/DeleteBtn.vue'
 import { FormBuilder, inputMixin, FormBuilderAssist } from 'quasar-form-builder'
 import { FormBuilder, inputMixin, FormBuilderAssist } from 'quasar-form-builder'
 import EntityInput from 'quasar-crud/src/components/Entity/Attachment/EntityInput.vue'
 import EntityInput from 'quasar-crud/src/components/Entity/Attachment/EntityInput.vue'
+import { Provinces, Cities, getCitiesOfProvince } from 'src/assets/js/IranianCities.js'
 import FormBuilderInputEditor from 'src/components/FormBuilderCustumComponents/FormBuilderInputEditor.vue'
 import FormBuilderInputEditor from 'src/components/FormBuilderCustumComponents/FormBuilderInputEditor.vue'
-import { User } from 'src/models/User'
 
 
 const BtnControlComp = shallowRef(BtnControl)
 const BtnControlComp = shallowRef(BtnControl)
 const EntityInputComp = shallowRef(EntityInput)
 const EntityInputComp = shallowRef(EntityInput)
@@ -90,6 +91,7 @@ export default {
     const classroomId = this.$route.params.classroom_id
     const classroomId = this.$route.params.classroom_id
     return {
     return {
       mounted: false,
       mounted: false,
+      inputDataLoaded: false,
       team: new Team(),
       team: new Team(),
       entityLoading: true,
       entityLoading: true,
       api: APIGateway.team.APIAdresses.byId(teamId),
       api: APIGateway.team.APIAdresses.byId(teamId),
@@ -290,8 +292,32 @@ export default {
       teamRegistrationListInputs: [
       teamRegistrationListInputs: [
         { type: 'hidden', name: 'team', value: teamId },
         { type: 'hidden', name: 'team', value: teamId },
         { type: 'select', name: 'owner__gender', value: null, options: (new User()).genderEnums, col: 'col-md-2 col-12', label: 'جنسیت', placeholder: ' ' },
         { type: 'select', name: 'owner__gender', value: null, options: (new User()).genderEnums, col: 'col-md-2 col-12', label: 'جنسیت', placeholder: ' ' },
-        { type: 'input', name: 'owner__living_province', value: null, col: 'col-md-2 col-12', label: 'استان', placeholder: ' ' },
-        { type: 'input', name: 'owner__living_city', value: null, col: 'col-md-2 col-12', label: 'شهر', placeholder: ' ' },
+        {
+          type: 'select',
+          name: 'owner__living_province',
+          options: Provinces,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          value: null,
+          col: 'col-md-2 col-12',
+          label: 'استان',
+          placeholder: ' '
+        },
+        {
+          type: 'select',
+          name: 'owner__living_city',
+          options: Cities,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          value: null,
+          col: 'col-md-2 col-12',
+          label: 'شهر',
+          placeholder: ' '
+        },
         { type: 'input', name: 'search', value: null, col: 'col-md-2 col-12', label: 'جست و جو', placeholder: ' ' },
         { type: 'input', name: 'search', value: null, col: 'col-md-2 col-12', label: 'جست و جو', placeholder: ' ' },
         { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: () => {}, col: 'col-md-2 col-12' }
         { type: BtnControlComp, name: 'btn', label: 'جستجو', placeholder: ' ', atClick: () => {}, col: 'col-md-2 col-12' }
       ],
       ],
@@ -372,6 +398,30 @@ export default {
       }
       }
     }
     }
   },
   },
+  computed: {
+    selectedProvince () {
+      return FormBuilderAssist.getInputsByName(this.teamRegistrationListInputs, 'owner__living_province').value
+    }
+  },
+  watch: {
+    selectedProvince (newValue) {
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.teamRegistrationListInputs, 'owner__living_city', 'disable', true)
+        if (this.inputDataLoaded) {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+        }
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedProvince)
+
+      FormBuilderAssist.setAttributeByName(this.teamRegistrationListInputs, 'owner__living_city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.teamRegistrationListInputs, 'owner__living_city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+      }
+    }
+  },
   mounted () {
   mounted () {
     this.setActionBtn()
     this.setActionBtn()
     this.mounted = true
     this.mounted = true
@@ -407,6 +457,9 @@ export default {
       this.entityLoading = false
       this.entityLoading = false
       FormBuilderAssist.setAttributeByName(this.inputs, 'leader', 'value', this.team.leader)
       FormBuilderAssist.setAttributeByName(this.inputs, 'leader', 'value', this.team.leader)
       FormBuilderAssist.setAttributeByName(this.inputs, 'leader', 'selected', this.team.leader_info)
       FormBuilderAssist.setAttributeByName(this.inputs, 'leader', 'selected', this.team.leader_info)
+      this.$nextTick(() => {
+        this.inputDataLoaded = true
+      })
     },
     },
     edit() {
     edit() {
       this.entityLoading = true
       this.entityLoading = true

+ 30 - 5
src/components/Widgets/TextWidget/OptionPanel.vue

@@ -5,7 +5,8 @@
       <div class="option-panel-container">
       <div class="option-panel-container">
         <div class="row q-mt-sm">
         <div class="row q-mt-sm">
           <div class="col-md-12">
           <div class="col-md-12">
-            <editor v-model:value="localOptions.text" />
+            <form-builder-tiptap-editor v-model:value="localOptions.text"
+                                        :options="editorOptions" />
           </div>
           </div>
           <editor-options v-model:options="localOptions" />
           <editor-options v-model:options="localOptions" />
         </div>
         </div>
@@ -15,15 +16,18 @@
 </template>
 </template>
 <script>
 <script>
 import { defineComponent } from 'vue'
 import { defineComponent } from 'vue'
+import { APIGateway } from 'src/api/APIGateway.js'
+import EditorOptions from 'components/EditorOptions.vue'
 import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
 import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
+import PostMixin from 'src/components/Widgets/Post/PostMixin.js'
+import FormBuilderTiptapEditor from 'src/components/FormBuilderCustumComponents/FormBuilderTiptapEditor.vue'
 import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
 import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
-import Editor from 'components/Utils/Editor2.vue'
-import EditorOptions from 'components/EditorOptions.vue'
+// import Editor from 'components/Utils/Editor2.vue'
 
 
 export default defineComponent({
 export default defineComponent({
   name: 'OptionPanel',
   name: 'OptionPanel',
-  components: { Editor, OptionPanelTabs, EditorOptions },
-  mixins: [mixinOptionPanel],
+  components: { OptionPanelTabs, EditorOptions, FormBuilderTiptapEditor },
+  mixins: [mixinOptionPanel, PostMixin],
   props: {
   props: {
     options: {
     options: {
       type: Object,
       type: Object,
@@ -33,6 +37,10 @@ export default defineComponent({
     }
     }
   },
   },
   data() {
   data() {
+    const authorization = 'Bearer ' + this.$store.getters['Auth/accessToken']
+    const uploadVideo = this.uploadVideo
+    const uploadAudio = this.uploadAudio
+
     return {
     return {
       fontStyle: ['inherit', 'normal', 'italic'],
       fontStyle: ['inherit', 'normal', 'italic'],
       foreColor: '#000000',
       foreColor: '#000000',
@@ -75,6 +83,23 @@ export default defineComponent({
           fontStyle: null,
           fontStyle: null,
           lineHeight: null
           lineHeight: null
         }
         }
+      },
+      editorOptions: {
+        bubbleMenu: false,
+        floatingMenu: false,
+        poem: false,
+        reading: false,
+        loadBareHtml: true,
+        persianKeyboard: true,
+        mathliveOptions: { smartFence: false },
+        uploadServer: {
+          url: '/api' + APIGateway.media.APIAdresses.base,
+          instantUpload: true,
+          responseKey: 'file',
+          headers: { Authorization: authorization }
+        },
+        uploadVideo,
+        uploadAudio
       }
       }
     }
     }
   },
   },

+ 1 - 1
src/components/Widgets/User/Profile/Profile.vue

@@ -58,7 +58,7 @@ export default {
     EntityEdit
     EntityEdit
   },
   },
   mixins: [mixinWidget],
   mixins: [mixinWidget],
-  data: () => {
+  data () {
     return {
     return {
       mounted: false,
       mounted: false,
       entityLoading: true,
       entityLoading: true,

+ 9 - 5
src/components/cart/cartCountAction/cartCountAction.vue

@@ -4,24 +4,24 @@
       <q-btn outline
       <q-btn outline
              color="primary"
              color="primary"
              icon="add"
              icon="add"
-             :loading="basketItem.loading"
+             :loading="basketItem?.loading || loading"
              @click="onIncrease" />
              @click="onIncrease" />
     </div>
     </div>
     <div class="cart-count-action-count">
     <div class="cart-count-action-count">
-      {{ basketItem.count }}
+      {{ basketItem?.count }}
     </div>
     </div>
     <div class="cart-count-action-decrease">
     <div class="cart-count-action-decrease">
-      <q-btn v-if="basketItem.count > 1"
+      <q-btn v-if="basketItem?.count > 1"
              outline
              outline
              color="red"
              color="red"
              icon="remove"
              icon="remove"
-             :loading="basketItem.loading"
+             :loading="basketItem?.loading || loading"
              @click="onDecrease" />
              @click="onDecrease" />
       <q-btn v-else
       <q-btn v-else
              flat
              flat
              color="red"
              color="red"
              icon="delete_outline"
              icon="delete_outline"
-             :loading="basketItem.loading"
+             :loading="basketItem?.loading || loading"
              @click="onRemove" />
              @click="onRemove" />
     </div>
     </div>
   </div>
   </div>
@@ -36,6 +36,10 @@ export default {
     basketItem: {
     basketItem: {
       type: BasketItem,
       type: BasketItem,
       default: new BasketItem()
       default: new BasketItem()
+    },
+    loading: {
+      type: Boolean,
+      default: false
     }
     }
   },
   },
   emits: ['increase', 'decrease', 'remove'],
   emits: ['increase', 'decrease', 'remove'],

+ 1 - 0
src/css/Theme/Controls/carousel.scss

@@ -89,6 +89,7 @@
   .splide__pagination {
   .splide__pagination {
     margin-top: 24px;
     margin-top: 24px;
     gap: 4px;
     gap: 4px;
+    padding-inline-start: 0;
     li {
     li {
       .splide__pagination__page {
       .splide__pagination__page {
         transition: all 0.3s;
         transition: all 0.3s;

+ 1 - 1
src/css/Theme/Widgets/widgets.scss

@@ -1,5 +1,5 @@
 //@import 'src/css/Theme/Widgets/card';
 //@import 'src/css/Theme/Widgets/card';
-//@import 'src/css/Theme/Widgets/scrollBar.scss';
+@import './scrollBar.scss';
 //@import './chip';
 //@import './chip';
 @import './banner.scss';
 @import './banner.scss';
 
 

+ 19 - 0
src/models/Basket.js

@@ -20,6 +20,7 @@ class Basket extends Model {
       },
       },
       { key: 'description' },
       { key: 'description' },
       { key: 'status' },
       { key: 'status' },
+      { key: 'order_type' },
       {
       {
         key: 'price',
         key: 'price',
         default: 0
         default: 0
@@ -101,6 +102,24 @@ class Basket extends Model {
             value: 'SENT'
             value: 'SENT'
           }
           }
         ]
         ]
+      },
+      order_type: {
+        infoKey: 'order_type_info',
+        enumListKey: 'order_typeEnums',
+        enums: [
+          {
+            label: 'فیزیکی',
+            value: 'PHYSICAL'
+          },
+          {
+            label: 'مجازی',
+            value: 'VIRTUAL'
+          },
+          {
+            label: 'فیزیکی و مجازی',
+            value: 'PHYSICAL_VIRTUAL'
+          }
+        ]
       }
       }
     })
     })
   }
   }

+ 21 - 1
src/models/Content.js

@@ -12,10 +12,15 @@ class Content extends Model {
       { key: 'description' },
       { key: 'description' },
       { key: 'summary' },
       { key: 'summary' },
       { key: 'thumbnail' },
       { key: 'thumbnail' },
+      { key: 'creation_time' },
       {
       {
         key: 'is_free',
         key: 'is_free',
         defalut: false
         defalut: false
       },
       },
+      {
+        key: 'is_hidden',
+        defalut: true
+      },
       {
       {
         key: 'medias',
         key: 'medias',
         defalut: []
         defalut: []
@@ -38,7 +43,22 @@ class Content extends Model {
         key: 'hashtags_info',
         key: 'hashtags_info',
         relatedModel: ContentHashtagList
         relatedModel: ContentHashtagList
       }
       }
-    ])
+    ], {
+      is_hidden: {
+        infoKey: 'is_hidden_info',
+        enumListKey: 'is_hiddenEnums',
+        enums: [
+          {
+            label: 'پنهان',
+            value: true
+          },
+          {
+            label: 'نمایان',
+            value: false
+          }
+        ]
+      }
+    })
 
 
     if (!this.thumbnail) {
     if (!this.thumbnail) {
       this.thumbnail = '/img/sample-post-thumbnail.jpg'
       this.thumbnail = '/img/sample-post-thumbnail.jpg'

+ 5 - 0
src/models/Invoice.js

@@ -1,4 +1,5 @@
 import { User } from 'src/models/User.js'
 import { User } from 'src/models/User.js'
+import { Basket } from 'src/models/Basket.js'
 import { Classroom } from 'src/models/Classroom.js'
 import { Classroom } from 'src/models/Classroom.js'
 import { Model, Collection } from 'js-abstract-model'
 import { Model, Collection } from 'js-abstract-model'
 
 
@@ -96,6 +97,10 @@ class Invoice extends Model {
         ]
         ]
       }
       }
     })
     })
+
+    if (this.item_type === 'STORE_BASKET') {
+      this.item_info = new Basket(data.item_info)
+    }
   }
   }
 }
 }
 
 

+ 29 - 1
src/models/Package.js

@@ -9,14 +9,28 @@ class Package extends Model {
       { key: 'title' },
       { key: 'title' },
       { key: 'inventory' },
       { key: 'inventory' },
       { key: 'store_category' },
       { key: 'store_category' },
+      { key: 'store_category_info' },
       { key: 'content_category' },
       { key: 'content_category' },
-      { key: 'unit_price' },
+      { key: 'content_category_info' },
+      {
+        key: 'unit_price',
+        default: 0
+      },
       { key: 'sellable_price' },
       { key: 'sellable_price' },
       { key: 'discount_percent' },
       { key: 'discount_percent' },
       { key: 'description' },
       { key: 'description' },
       { key: 'is_physical' },
       { key: 'is_physical' },
+      { key: 'is_hidden' },
       { key: 'maximum_order_count' },
       { key: 'maximum_order_count' },
       { key: 'weight' },
       { key: 'weight' },
+      {
+        key: 'is_add_to_basket_disabled',
+        default: false
+      },
+      {
+        key: 'is_purchased',
+        default: false
+      },
       { key: 'specification' },
       { key: 'specification' },
       { key: 'products' },
       { key: 'products' },
       {
       {
@@ -37,6 +51,20 @@ class Package extends Model {
             value: false
             value: false
           }
           }
         ]
         ]
+      },
+      is_hidden: {
+        infoKey: 'is_hidden_info',
+        enumListKey: 'is_hiddenEnums',
+        enums: [
+          {
+            label: 'پنهان',
+            value: true
+          },
+          {
+            label: 'نمایان',
+            value: false
+          }
+        ]
       }
       }
     })
     })
 
 

+ 1 - 0
src/models/Post.js

@@ -12,6 +12,7 @@ class Post extends Model {
       { key: 'summary' },
       { key: 'summary' },
       { key: 'thumbnail' },
       { key: 'thumbnail' },
       { key: 'category' },
       { key: 'category' },
+      { key: 'creation_time' },
       {
       {
         key: 'category_info',
         key: 'category_info',
         relatedModel: PostCategory
         relatedModel: PostCategory

+ 30 - 2
src/models/Product.js

@@ -9,8 +9,13 @@ class Product extends Model {
       { key: 'title' },
       { key: 'title' },
       { key: 'inventory' },
       { key: 'inventory' },
       { key: 'store_category' },
       { key: 'store_category' },
+      { key: 'store_category_info' },
       { key: 'content_category' },
       { key: 'content_category' },
-      { key: 'unit_price' },
+      { key: 'content_category_info' },
+      {
+        key: 'unit_price',
+        default: 0
+      },
       { key: 'sellable_price' },
       { key: 'sellable_price' },
       {
       {
         key: 'discount_percent',
         key: 'discount_percent',
@@ -19,12 +24,21 @@ class Product extends Model {
       { key: 'description' },
       { key: 'description' },
       { key: 'maximum_order_count' },
       { key: 'maximum_order_count' },
       { key: 'is_physical' },
       { key: 'is_physical' },
+      { key: 'is_hidden' },
       { key: 'weight' },
       { key: 'weight' },
       { key: 'physical_type' },
       { key: 'physical_type' },
       { key: 'specification' },
       { key: 'specification' },
+      {
+        key: 'is_add_to_basket_disabled',
+        default: false
+      },
+      {
+        key: 'is_purchased',
+        default: false
+      },
       {
       {
         key: 'medias',
         key: 'medias',
-        defalut: []
+        default: []
       },
       },
       {
       {
         key: 'medias_info',
         key: 'medias_info',
@@ -45,6 +59,20 @@ class Product extends Model {
           }
           }
         ]
         ]
       },
       },
+      is_hidden: {
+        infoKey: 'is_hidden_info',
+        enumListKey: 'is_hiddenEnums',
+        enums: [
+          {
+            label: 'پنهان',
+            value: true
+          },
+          {
+            label: 'نمایان',
+            value: false
+          }
+        ]
+      },
       physical_type: {
       physical_type: {
         infoKey: 'physical_type_info',
         infoKey: 'physical_type_info',
         enumListKey: 'physical_typeEnums',
         enumListKey: 'physical_typeEnums',

+ 23 - 0
src/pages/Admin/StoreBasket/DeliveryCost.vue

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

+ 23 - 0
src/pages/Admin/StoreBasket/PurchaseRegulations.vue

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

+ 193 - 10
src/pages/Admin/User/show.vue

@@ -243,13 +243,15 @@
 <script>
 <script>
 import Assist from 'assets/js/Assist.js'
 import Assist from 'assets/js/Assist.js'
 import Enums from 'src/assets/Enums/Enums.js'
 import Enums from 'src/assets/Enums/Enums.js'
+import { Invoice } from 'src/models/Invoice.js'
+import { Payment } from 'src/models/Payment.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
+import { Classroom } from 'src/models/Classroom.js'
 import { EntityEdit, EntityIndex } from 'quasar-crud'
 import { EntityEdit, EntityIndex } from 'quasar-crud'
-import { Classroom } from 'src/models/Classroom'
-import { Registration } from 'src/models/Registration'
-import { Invoice } from 'src/models/Invoice'
-import { Payment } from 'src/models/Payment'
-import { Transaction } from 'src/models/Transaction'
+import { Transaction } from 'src/models/Transaction.js'
+import { Registration } from 'src/models/Registration.js'
+import { Provinces, Cities, getCitiesOfProvince } from 'src/assets/js/IranianCities.js'
+import { FormBuilderAssist } from 'quasar-form-builder'
 
 
 export default {
 export default {
   name: 'Admin.User.Show',
   name: 'Admin.User.Show',
@@ -259,6 +261,7 @@ export default {
     return {
     return {
       tab: 'userInfo',
       tab: 'userInfo',
       financialTab: 'invoiceList',
       financialTab: 'invoiceList',
+      inputDataLoaded: false,
       api: APIGateway.user.APIAdresses.byId(userId),
       api: APIGateway.user.APIAdresses.byId(userId),
       mounted: false,
       mounted: false,
       entityIdKey: 'id',
       entityIdKey: 'id',
@@ -331,15 +334,63 @@ export default {
           col: 'col-md-3 col-12'
           col: 'col-md-3 col-12'
         },
         },
 
 
-        { type: 'checkbox', name: 'is_abroad_birth_address', placeholder: ' ', label: 'محل تولد خارج از کشور است؟', responseKey: 'is_abroad_birth_address', className: 'require', col: 'col-12' },
+        { type: 'checkbox', name: 'is_abroad_birth_address', placeholder: ' ', label: 'محل تولد خارج از کشور است؟۳۳۳۳', responseKey: 'is_abroad_birth_address', className: 'require', col: 'col-12' },
         { type: 'input', name: 'birth_country', placeholder: ' ', label: 'کشور محل تولد', responseKey: 'birth_country', col: 'col-md-4 col-12' },
         { type: 'input', name: 'birth_country', placeholder: ' ', label: 'کشور محل تولد', responseKey: 'birth_country', col: 'col-md-4 col-12' },
-        { type: 'input', name: 'birth_province', placeholder: ' ', label: 'استان محل تولد', responseKey: 'birth_province', col: 'col-md-4 col-12' },
-        { type: 'input', name: 'birth_city', placeholder: ' ', label: 'شهر محل تولد', responseKey: 'birth_city', col: 'col-md-4 col-12' },
+        {
+          type: 'select',
+          name: 'birth_province',
+          options: Provinces,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'استان محل تولد',
+          responseKey: 'birth_province',
+          col: 'col-md-4 col-12'
+        },
+        {
+          type: 'select',
+          name: 'birth_city',
+          options: Cities,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'شهر محل تولد',
+          responseKey: 'birth_city',
+          col: 'col-md-4 col-12'
+        },
 
 
         { type: 'checkbox', name: 'is_abroad_living_address', placeholder: ' ', label: 'محل زندگی فعلی خارج از کشور است؟', responseKey: 'is_abroad_living_address', className: 'require', col: 'col-12' },
         { type: 'checkbox', name: 'is_abroad_living_address', placeholder: ' ', label: 'محل زندگی فعلی خارج از کشور است؟', responseKey: 'is_abroad_living_address', className: 'require', col: 'col-12' },
         { type: 'input', name: 'living_country', placeholder: ' ', label: 'کشور محل زندگی فعلی', responseKey: 'living_country', col: 'col-md-4 col-12' },
         { type: 'input', name: 'living_country', placeholder: ' ', label: 'کشور محل زندگی فعلی', responseKey: 'living_country', col: 'col-md-4 col-12' },
-        { type: 'input', name: 'living_province', placeholder: ' ', label: 'استان محل زندگی فعلی', responseKey: 'living_province', col: 'col-md-4 col-12' },
-        { type: 'input', name: 'living_city', placeholder: ' ', label: 'شهر محل زندگی فعلی', responseKey: 'living_city', col: 'col-md-4 col-12' },
+        {
+          type: 'select',
+          name: 'living_province',
+          options: Provinces,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'استان محل زندگی فعلی',
+          responseKey: 'living_province',
+          col: 'col-md-4 col-12'
+        },
+        {
+          type: 'select',
+          name: 'living_city',
+          options: Cities,
+          optionLabel: 'name',
+          optionValue: 'name',
+          createNewValue: true,
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'شهر محل زندگی فعلی',
+          responseKey: 'living_city',
+          col: 'col-md-4 col-12'
+        },
         { type: 'input', name: 'living_postal_code', placeholder: ' ', label: 'کد پستی محل زندگی', responseKey: 'living_postal_code', col: 'col-md-3 col-12' },
         { type: 'input', name: 'living_postal_code', placeholder: ' ', label: 'کد پستی محل زندگی', responseKey: 'living_postal_code', col: 'col-md-3 col-12' },
         { type: 'input', name: 'living_address', placeholder: ' ', label: 'آدرس پستی محل زندگی', responseKey: 'living_address', col: 'col-md-6 col-12' },
         { type: 'input', name: 'living_address', placeholder: ' ', label: 'آدرس پستی محل زندگی', responseKey: 'living_address', col: 'col-md-6 col-12' },
 
 
@@ -855,6 +906,135 @@ export default {
       }
       }
     }
     }
   },
   },
+  computed: {
+    isAbroadBirthAddress () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'is_abroad_birth_address').value
+    },
+    isAbroadLivingAddress () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'is_abroad_living_address').value
+    },
+    selectedBirthProvince () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'birth_province').value
+    },
+    selectedLivingProvince () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'living_province').value
+    }
+  },
+  watch: {
+    isAbroadBirthAddress (newValue) {
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'value', 'ایران')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'disable', true)
+        if (this.inputDataLoaded) {
+          // province
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'type', 'select')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'disable', false)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'value', null)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'options', Provinces)
+
+          // city
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'select')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', true)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'options', [])
+        }
+      } else {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'value', null)
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'disable', false)
+
+        if (this.inputDataLoaded) {
+          // province
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'type', 'input')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'disable', false)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'value', null)
+
+          // city
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'input')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+        }
+      }
+    },
+    isAbroadLivingAddress (newValue) {
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'value', 'ایران')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'disable', true)
+        if (this.inputDataLoaded) {
+          // province
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'type', 'select')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'disable', false)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'value', null)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'options', Provinces)
+
+          // city
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'select')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', true)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'options', [])
+        }
+      } else {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'value', null)
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'disable', false)
+        if (this.inputDataLoaded) {
+          // province
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'type', 'input')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'disable', false)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'value', null)
+
+          // city
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'input')
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+        }
+      }
+    },
+    selectedBirthProvince (newValue) {
+      if (this.isAbroadBirthAddress) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'input')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+        return
+      }
+
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', true)
+        if (this.inputDataLoaded) {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+        }
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedBirthProvince)
+
+      FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+      }
+    },
+    selectedLivingProvince (newValue) {
+      if (this.isAbroadBirthAddress) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'input')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+        return
+      }
+
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', true)
+        if (this.inputDataLoaded) {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+        }
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedLivingProvince)
+
+      FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+      }
+    }
+  },
   mounted () {
   mounted () {
     this.mounted = true
     this.mounted = true
   },
   },
@@ -869,6 +1049,9 @@ export default {
       // console.log('roles', roles)
       // console.log('roles', roles)
       // this.setInputValue('roles', roles)
       // this.setInputValue('roles', roles)
       // setNewInputData(this.inputs)
       // setNewInputData(this.inputs)
+      this.$nextTick(() => {
+        this.inputDataLoaded = true
+      })
     },
     },
     setInputValue (name, value) {
     setInputValue (name, value) {
       const inputIndex = this.inputs.findIndex(input => input.name === name)
       const inputIndex = this.inputs.findIndex(input => input.name === name)

+ 200 - 5
src/pages/User/Profile/profile.vue

@@ -33,11 +33,12 @@ import { User } from 'src/models/User.js'
 import Enums from 'src/assets/Enums/Enums.js'
 import Enums from 'src/assets/Enums/Enums.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import { APIGateway } from 'src/api/APIGateway.js'
 import Warning from 'src/components/other/Warning.vue'
 import Warning from 'src/components/other/Warning.vue'
+import { FormBuilderAssist } from 'quasar-form-builder'
 import VerifyEmail from 'src/components/other/VerifyEmail.vue'
 import VerifyEmail from 'src/components/other/VerifyEmail.vue'
 import NationalCode from 'src/components/other/NationalCode.vue'
 import NationalCode from 'src/components/other/NationalCode.vue'
 import VerifyMobileNumber from 'src/components/other/VerifyMobileNumber.vue'
 import VerifyMobileNumber from 'src/components/other/VerifyMobileNumber.vue'
+import { Provinces, Cities, getCitiesOfProvince } from 'src/assets/js/IranianCities.js'
 import FormBuilderBarCode from 'src/components/FormBuilderCustumComponents/FormBuilderBarCode.vue'
 import FormBuilderBarCode from 'src/components/FormBuilderCustumComponents/FormBuilderBarCode.vue'
-import { FormBuilderAssist } from 'quasar-form-builder'
 
 
 const WarningComp = shallowRef(Warning)
 const WarningComp = shallowRef(Warning)
 const VerifyEmailComp = shallowRef(VerifyEmail)
 const VerifyEmailComp = shallowRef(VerifyEmail)
@@ -51,6 +52,7 @@ export default {
   data () {
   data () {
     return {
     return {
       mounted: false,
       mounted: false,
+      inputDataLoaded: false,
       tab: 'userInfo',
       tab: 'userInfo',
       api: APIGateway.user.APIAdresses.current,
       api: APIGateway.user.APIAdresses.current,
       entityIdKey: 'id',
       entityIdKey: 'id',
@@ -122,18 +124,208 @@ export default {
         },
         },
         { type: 'checkbox', name: 'is_abroad_birth_address', placeholder: ' ', label: 'محل تولد خارج از کشور است؟', responseKey: 'is_abroad_birth_address', className: 'require', col: 'col-12' },
         { type: 'checkbox', name: 'is_abroad_birth_address', placeholder: ' ', label: 'محل تولد خارج از کشور است؟', responseKey: 'is_abroad_birth_address', className: 'require', col: 'col-12' },
         { type: 'input', name: 'birth_country', placeholder: ' ', label: 'کشور محل تولد', responseKey: 'birth_country', className: 'require', col: 'col-md-6 col-12' },
         { type: 'input', name: 'birth_country', placeholder: ' ', label: 'کشور محل تولد', responseKey: 'birth_country', className: 'require', col: 'col-md-6 col-12' },
-        { type: 'input', name: 'birth_province', placeholder: ' ', label: 'استان محل تولد', responseKey: 'birth_province', className: 'require', col: 'col-md-6 col-12' },
-        { type: 'input', name: 'birth_city', placeholder: ' ', label: 'شهر محل تولد', responseKey: 'birth_city', className: 'require', col: 'col-md-6 col-12' },
+        {
+          type: 'select',
+          name: 'birth_province',
+          options: Provinces,
+          createNewValue: true,
+          optionLabel: 'name',
+          optionValue: 'name',
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'استان محل تولد',
+          responseKey: 'birth_province',
+          className: 'require',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'select',
+          name: 'birth_city',
+          options: Cities,
+          createNewValue: true,
+          optionLabel: 'name',
+          optionValue: 'name',
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'شهر محل تولد',
+          responseKey: 'birth_city',
+          className: 'require',
+          col: 'col-md-6 col-12'
+        },
         { type: 'checkbox', name: 'is_abroad_living_address', placeholder: ' ', label: 'محل زندگی فعلی خارج از کشور است؟', responseKey: 'is_abroad_living_address', className: 'require', col: 'col-12' },
         { type: 'checkbox', name: 'is_abroad_living_address', placeholder: ' ', label: 'محل زندگی فعلی خارج از کشور است؟', responseKey: 'is_abroad_living_address', className: 'require', col: 'col-12' },
         { type: 'input', name: 'living_country', placeholder: ' ', label: 'کشور محل زندگی فعلی', responseKey: 'living_country', className: 'require', col: 'col-md-6 col-12' },
         { type: 'input', name: 'living_country', placeholder: ' ', label: 'کشور محل زندگی فعلی', responseKey: 'living_country', className: 'require', col: 'col-md-6 col-12' },
-        { type: 'input', name: 'living_province', placeholder: ' ', label: 'استان محل زندگی فعلی', responseKey: 'living_province', className: 'require', col: 'col-md-6 col-12' },
-        { type: 'input', name: 'living_city', placeholder: ' ', label: 'شهر محل زندگی فعلی', responseKey: 'living_city', className: 'require', col: 'col-md-6 col-12' },
+        {
+          type: 'select',
+          name: 'living_province',
+          options: Provinces,
+          createNewValue: true,
+          optionLabel: 'name',
+          optionValue: 'name',
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'استان محل زندگی فعلی',
+          responseKey: 'living_province',
+          className: 'require',
+          col: 'col-md-6 col-12'
+        },
+        {
+          type: 'select',
+          name: 'living_city',
+          options: Cities,
+          createNewValue: true,
+          optionLabel: 'name',
+          optionValue: 'name',
+          newValueMode: 'add-unique',
+          placeholder: ' ',
+          label: 'شهر محل زندگی فعلی',
+          responseKey: 'living_city',
+          className: 'require',
+          col: 'col-md-6 col-12'
+        },
         { type: 'input', name: 'living_postal_code', placeholder: ' ', label: 'کد پستی محل زندگی', responseKey: 'living_postal_code', className: 'require', col: 'col-md-6 col-12' },
         { type: 'input', name: 'living_postal_code', placeholder: ' ', label: 'کد پستی محل زندگی', responseKey: 'living_postal_code', className: 'require', col: 'col-md-6 col-12' },
         { type: 'input', name: 'living_address', placeholder: ' ', label: 'آدرس پستی محل زندگی', responseKey: 'living_address', className: 'require', col: 'col-md-6 col-12' },
         { type: 'input', name: 'living_address', placeholder: ' ', label: 'آدرس پستی محل زندگی', responseKey: 'living_address', className: 'require', col: 'col-md-6 col-12' },
         { type: 'hidden', name: 'id', responseKey: 'id', label: 'id', col: 'col-12' }
         { type: 'hidden', name: 'id', responseKey: 'id', label: 'id', col: 'col-12' }
       ]
       ]
     }
     }
   },
   },
+  computed: {
+    isAbroadBirthAddress () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'is_abroad_birth_address').value
+    },
+    isAbroadLivingAddress () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'is_abroad_living_address').value
+    },
+    selectedBirthProvince () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'birth_province').value
+    },
+    selectedLivingProvince () {
+      return FormBuilderAssist.getInputsByName(this.inputs, 'living_province').value
+    }
+  },
+  watch: {
+    isAbroadBirthAddress: {
+      immediate: true,
+      handler (newValue) {
+        this.$nextTick(() => {
+          if (!newValue) {
+            FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'value', 'ایران')
+            FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'readonly', true)
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'type', 'select')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'options', Provinces)
+
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'select')
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', true)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'options', getCitiesOfProvince(this.selectedBirthProvince))
+            }
+          } else {
+            // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'value', null)
+            FormBuilderAssist.setAttributeByName(this.inputs, 'birth_country', 'readonly', false)
+
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'disable', false)
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_province', 'value', null)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+            }
+          }
+        })
+      }
+    },
+    isAbroadLivingAddress: {
+      immediate: true,
+      handler (newValue) {
+        this.$nextTick(() => {
+          if (!newValue) {
+            FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'value', 'ایران')
+            FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'readonly', true)
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'type', 'select')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'options', Provinces)
+
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'select')
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', true)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'options', getCitiesOfProvince(this.selectedLivingProvince))
+            }
+          } else {
+            // FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'value', null)
+            FormBuilderAssist.setAttributeByName(this.inputs, 'living_country', 'readonly', false)
+            if (this.inputDataLoaded) {
+              // province
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'disable', false)
+
+              // city
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'input')
+              FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_province', 'value', null)
+              // FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+            }
+          }
+        })
+      }
+    },
+    selectedBirthProvince (newValue) {
+      if (this.isAbroadBirthAddress) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'type', 'input')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+        return
+      }
+
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', true)
+        if (this.inputDataLoaded) {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+        }
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedBirthProvince)
+
+      FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'birth_city', 'value', null)
+      }
+    },
+    selectedLivingProvince (newValue) {
+      if (this.isAbroadBirthAddress) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'type', 'input')
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+        return
+      }
+
+      if (!newValue) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', true)
+        if (this.inputDataLoaded) {
+          FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+        }
+        return
+      }
+
+      const filteredCities = getCitiesOfProvince(this.selectedLivingProvince)
+
+      FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'options', filteredCities)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'disable', false)
+      if (this.inputDataLoaded) {
+        FormBuilderAssist.setAttributeByName(this.inputs, 'living_city', 'value', null)
+      }
+    }
+  },
   mounted() {
   mounted() {
     this.mounted = true
     this.mounted = true
   },
   },
@@ -146,6 +338,9 @@ export default {
 
 
       this.updateGender(responseData, setNewInputData)
       this.updateGender(responseData, setNewInputData)
       this.updateMaritalStatus(responseData, setNewInputData)
       this.updateMaritalStatus(responseData, setNewInputData)
+      this.$nextTick(() => {
+        this.inputDataLoaded = true
+      })
     },
     },
     updateGender (responseData, setNewInputData) {
     updateGender (responseData, setNewInputData) {
       // const gender = this.getInputValue('gender')
       // const gender = this.getInputValue('gender')

Некоторые файлы не были показаны из-за большого количества измененных файлов