| @@ -1,11 +1,11 @@ | |||||
| { | { | ||||
| "name": "LightAble", | |||||
| "name": "NovinPlast", | |||||
| "version": "0.1.0", | "version": "0.1.0", | ||||
| "lockfileVersion": 3, | "lockfileVersion": 3, | ||||
| "requires": true, | "requires": true, | ||||
| "packages": { | "packages": { | ||||
| "": { | "": { | ||||
| "name": "LightAble", | |||||
| "name": "NovinPlast", | |||||
| "version": "0.1.0", | "version": "0.1.0", | ||||
| "dependencies": { | "dependencies": { | ||||
| "@amcharts/amcharts4": "^4.10.38", | "@amcharts/amcharts4": "^4.10.38", | ||||
| @@ -1,5 +1,5 @@ | |||||
| { | { | ||||
| "name": "LightAble", | |||||
| "name": "NovinPlast", | |||||
| "version": "0.1.0", | "version": "0.1.0", | ||||
| "private": true, | "private": true, | ||||
| "scripts": { | "scripts": { | ||||
| @@ -21,7 +21,7 @@ export default { | |||||
| const store = useStore(); | const store = useStore(); | ||||
| const mobile = ref(""); | const mobile = ref(""); | ||||
| const otpSent = ref(false); | const otpSent = ref(false); | ||||
| const timer = ref(120); | |||||
| const timer = ref(60); | |||||
| const otpCode = ref(""); | const otpCode = ref(""); | ||||
| const resendAvailable = ref(false); | const resendAvailable = ref(false); | ||||
| let timerInterval = null; | let timerInterval = null; | ||||
| @@ -45,24 +45,12 @@ export default { | |||||
| }) | }) | ||||
| .catch((err) => { | .catch((err) => { | ||||
| sendOtpLoading.value = false; | sendOtpLoading.value = false; | ||||
| console.log(err); | |||||
| const status = err.status; | |||||
| if (status == 429) { | |||||
| Swal.fire({ | |||||
| icon: "error", | |||||
| title: "تعداد تلاشها زیاد شد", | |||||
| text: "لطفاً بعداً دوباره امتحان کنید", | |||||
| confirmButtonText: "باشه", | |||||
| }); | |||||
| } | |||||
| if (status == 400) { | |||||
| Swal.fire({ | |||||
| icon: "warning", | |||||
| title: "کد تایید قبلاً ارسال شده", | |||||
| text: "برای این شماره، کد ورود قبلاً ارسال شده است. لطفاً کمی صبر کنید تا کد قبلی منقضی شود", | |||||
| confirmButtonText: "باشه", | |||||
| }); | |||||
| } | |||||
| Swal.fire({ | |||||
| icon: "error", | |||||
| title: "خطا در ارسال کد", | |||||
| text: err.response.data.message, | |||||
| confirmButtonText: "متوجه شدم", | |||||
| }); | |||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -78,10 +66,11 @@ export default { | |||||
| }; | }; | ||||
| const resendOtp = () => { | const resendOtp = () => { | ||||
| otpSent.value = false; | |||||
| otpCode.value = ""; | otpCode.value = ""; | ||||
| timer.value = 120; | |||||
| timer.value = 60; | |||||
| resendAvailable.value = false; | resendAvailable.value = false; | ||||
| sendOtp(); // Resend OTP | |||||
| startTimer(); // Restart the countdown | |||||
| }; | }; | ||||
| const verifyOtp = async () => { | const verifyOtp = async () => { | ||||
| @@ -134,7 +123,6 @@ export default { | |||||
| try { | try { | ||||
| const response = await ApiServiece.get("settings/logo_fav"); | const response = await ApiServiece.get("settings/logo_fav"); | ||||
| settings.value = response.data.data; | settings.value = response.data.data; | ||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Error fetching settings:", error); | console.error("Error fetching settings:", error); | ||||
| } | } | ||||
| @@ -230,14 +218,14 @@ export default { | |||||
| role="status" | role="status" | ||||
| aria-hidden="true" | aria-hidden="true" | ||||
| ></span> | ></span> | ||||
| <span v-else> ورود</span> | |||||
| <span v-else>ورود</span> | |||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="d-grid mt-3"> | <div class="d-grid mt-3"> | ||||
| <button | <button | ||||
| v-if="!otpSent && !resendAvailable" | |||||
| v-if="!otpSent" | |||||
| @click="sendOtp" | @click="sendOtp" | ||||
| type="button" | type="button" | ||||
| class="btn btn-primary" | class="btn btn-primary" | ||||
| @@ -252,15 +240,14 @@ export default { | |||||
| <span v-else> ارسال کد ورود </span> | <span v-else> ارسال کد ورود </span> | ||||
| </button> | </button> | ||||
| <div class="d-grid mt-3" v-if="resendAvailable && otpSent"> | |||||
| <button | |||||
| @click="resendOtp" | |||||
| type="button" | |||||
| class="btn btn-primary" | |||||
| > | |||||
| ارسال مجدد کد | |||||
| </button> | |||||
| </div> | |||||
| <button | |||||
| v-if="resendAvailable && otpSent" | |||||
| @click="resendOtp" | |||||
| type="button" | |||||
| class="btn btn-primary" | |||||
| > | |||||
| ارسال مجدد کد | |||||
| </button> | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -1,12 +1,15 @@ | |||||
| <script> | <script> | ||||
| import { useStore } from "vuex"; | import { useStore } from "vuex"; | ||||
| import { ref } from "vue"; | |||||
| import ApiServiece from "@/services/ApiService"; | |||||
| import { ref, onMounted } from "vue"; | |||||
| import { useRouter } from "vue-router"; | import { useRouter } from "vue-router"; | ||||
| import Swal from "sweetalert2"; | import Swal from "sweetalert2"; | ||||
| export default { | export default { | ||||
| name: "RESET-PASSWORD", | name: "RESET-PASSWORD", | ||||
| components: {}, | components: {}, | ||||
| setup() { | setup() { | ||||
| const logo = ref(); | |||||
| const settings = ref() | |||||
| const router = useRouter(); | const router = useRouter(); | ||||
| const loading = ref(false); | const loading = ref(false); | ||||
| const errors = ref({}); | const errors = ref({}); | ||||
| @@ -58,6 +61,20 @@ export default { | |||||
| errors.value[field] = ""; | errors.value[field] = ""; | ||||
| }; | }; | ||||
| const getSettings = async () => { | |||||
| logo.value = localStorage.getItem("logo"); | |||||
| try { | |||||
| const response = await ApiServiece.get("settings/logo_fav"); | |||||
| settings.value = response.data.data; | |||||
| } catch (error) { | |||||
| console.error("Error fetching settings:", error); | |||||
| } | |||||
| }; | |||||
| onMounted(() => { | |||||
| getSettings(); | |||||
| }); | |||||
| return { | return { | ||||
| newPassword, | newPassword, | ||||
| confirmPassword, | confirmPassword, | ||||
| @@ -65,6 +82,7 @@ export default { | |||||
| clearError, | clearError, | ||||
| errors, | errors, | ||||
| loading, | loading, | ||||
| logo, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -74,10 +92,12 @@ export default { | |||||
| <div class="auth-main v2"> | <div class="auth-main v2"> | ||||
| <div class="bg-overlay bg-dark"></div> | <div class="bg-overlay bg-dark"></div> | ||||
| <div class="auth-wrapper"> | <div class="auth-wrapper"> | ||||
| <div class="auth-sidecontent"></div> | |||||
| <div class="auth-form"> | <div class="auth-form"> | ||||
| <div class="card my-5 mx-3"> | <div class="card my-5 mx-3"> | ||||
| <div style="direction: rtl" class="card-body"> | <div style="direction: rtl" class="card-body"> | ||||
| <div class="text-center mb-4"> | |||||
| <img :src="logo" alt="Logo" class="styled-logo" /> | |||||
| </div> | |||||
| <h4 class="f-w-500 mb-1">بازنشانی رمز عبور</h4> | <h4 class="f-w-500 mb-1">بازنشانی رمز عبور</h4> | ||||
| <p class="mb-3"> | <p class="mb-3"> | ||||
| باز گشت به صفحه | باز گشت به صفحه | ||||
| @@ -134,3 +154,45 @@ export default { | |||||
| </div> | </div> | ||||
| <Rightbar /> | <Rightbar /> | ||||
| </template> | </template> | ||||
| <style scoped> | |||||
| .styled-logo { | |||||
| display: block; | |||||
| max-width: 120px; /* Adjust the logo size here */ | |||||
| width: 100%; | |||||
| height: auto; | |||||
| border-radius: 10px; | |||||
| box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.2); | |||||
| margin: 0 auto 30px; | |||||
| margin-bottom: 90px; | |||||
| } | |||||
| .card { | |||||
| border: none; | |||||
| border-radius: 10px; | |||||
| box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); | |||||
| } | |||||
| .card-body { | |||||
| padding: 40px; | |||||
| } | |||||
| h4 { | |||||
| font-size: 1.5rem; | |||||
| font-weight: 500; | |||||
| } | |||||
| .text-center { | |||||
| text-align: center; | |||||
| } | |||||
| .mb-3 { | |||||
| margin-bottom: 1.5rem; | |||||
| } | |||||
| .form-control { | |||||
| border-radius: 5px; | |||||
| box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); | |||||
| padding: 0.8rem 1rem; | |||||
| } | |||||
| </style> | |||||
| @@ -309,12 +309,14 @@ export default { | |||||
| Authorization: `Bearer ${localStorage.getItem("token")}`, | Authorization: `Bearer ${localStorage.getItem("token")}`, | ||||
| }, | }, | ||||
| }) | }) | ||||
| .then((resp) => { | |||||
| console.log(resp); | |||||
| .then(() => { | |||||
| toast.success("!بلاگ با موفقیت اضافه شد", { | toast.success("!بلاگ با موفقیت اضافه شد", { | ||||
| position: "top-right", | position: "top-right", | ||||
| autoClose: 1000, | autoClose: 1000, | ||||
| }); | }); | ||||
| setTimeout(() => { | |||||
| window.location.reload(); | |||||
| }, 1500); | |||||
| }) | }) | ||||
| .catch((error) => { | .catch((error) => { | ||||
| @@ -17,6 +17,8 @@ export default { | |||||
| showDescription, | showDescription, | ||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const productSeletorLoader = ref(false); | |||||
| const blogSelectorLoader = ref(false); | |||||
| const products = ref([]); | const products = ref([]); | ||||
| const selectedProduct = ref(); | const selectedProduct = ref(); | ||||
| const blogs = ref([]); | const blogs = ref([]); | ||||
| @@ -65,13 +67,15 @@ export default { | |||||
| const handleBlogSearch = async (searchTerm) => { | const handleBlogSearch = async (searchTerm) => { | ||||
| if (searchTerm.length < 3) return; | if (searchTerm.length < 3) return; | ||||
| blogSelectorLoader.value = true; | |||||
| try { | try { | ||||
| const response = await ApiServiece.get( | const response = await ApiServiece.get( | ||||
| `admin/blogs?title=${searchTerm}` | `admin/blogs?title=${searchTerm}` | ||||
| ); | ); | ||||
| blogs.value = response.data.data; | blogs.value = response.data.data; | ||||
| blogSelectorLoader.value = false; | |||||
| } catch (error) { | } catch (error) { | ||||
| blogSelectorLoader.value = false; | |||||
| blogs.value = []; | blogs.value = []; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -87,14 +91,16 @@ export default { | |||||
| const handleProductsSearch = async (searchTerm) => { | const handleProductsSearch = async (searchTerm) => { | ||||
| if (searchTerm.length < 3) return; | if (searchTerm.length < 3) return; | ||||
| productSeletorLoader.value = true; | |||||
| try { | try { | ||||
| const response = await ApiServiece.get( | const response = await ApiServiece.get( | ||||
| `admin/products?title=${searchTerm}` | `admin/products?title=${searchTerm}` | ||||
| ); | ); | ||||
| products.value = response.data.data; | products.value = response.data.data; | ||||
| productSeletorLoader.value = false; | |||||
| } catch (error) { | } catch (error) { | ||||
| products.value = []; | products.value = []; | ||||
| productSeletorLoader.value = false; | |||||
| } | } | ||||
| }; | }; | ||||
| @@ -282,6 +288,8 @@ export default { | |||||
| formattedProducts, | formattedProducts, | ||||
| handleProductsSearch, | handleProductsSearch, | ||||
| selectedProduct, | selectedProduct, | ||||
| productSeletorLoader, | |||||
| blogSelectorLoader | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -328,6 +336,7 @@ export default { | |||||
| " | " | ||||
| v-model="selectedBlog" | v-model="selectedBlog" | ||||
| :options="formattedBlog" | :options="formattedBlog" | ||||
| :isLoading="blogSelectorLoader" | |||||
| placeholder="بلاگی را انتخاب کنید" | placeholder="بلاگی را انتخاب کنید" | ||||
| @search="handleBlogSearch" | @search="handleBlogSearch" | ||||
| /> | /> | ||||
| @@ -341,6 +350,7 @@ export default { | |||||
| " | " | ||||
| v-model="selectedProduct" | v-model="selectedProduct" | ||||
| :options="formattedProducts" | :options="formattedProducts" | ||||
| :isLoading="productSeletorLoader" | |||||
| placeholder="محصولی را انتخاب کنید" | placeholder="محصولی را انتخاب کنید" | ||||
| @search="handleProductsSearch" | @search="handleProductsSearch" | ||||
| /> | /> | ||||
| @@ -107,28 +107,26 @@ | |||||
| > | > | ||||
| <option value="cat">دسته</option> | <option value="cat">دسته</option> | ||||
| <option value="product">محصول</option> | <option value="product">محصول</option> | ||||
| <option value="both">هردو</option> | |||||
| <option value="all">همه</option> | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <small v-if="errors.discountType" class="text-danger"> | |||||
| {{ errors.discountType }} | |||||
| <small v-if="errors.whichPart" class="text-danger"> | |||||
| {{ errors.whichPart }} | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol v-if="whichPart === 'cat' || whichPart === 'both'" md="6"> | |||||
| <BCol v-if="whichPart === 'cat'" md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">دسته</label> | <label class="form-label">دسته</label> | ||||
| <select | |||||
| :class="{ 'is-invalid': errors.selectedCat }" | |||||
| <VueSelect | |||||
| style="--vs-min-height: 48px; --vs-border-radius: 8px" | |||||
| :isLoading="categorySelectorLoader" | |||||
| v-model="selectedCat" | v-model="selectedCat" | ||||
| class="form-control" | |||||
| @select="clearError('selectedCat')" | |||||
| placeholder="انتخاب دسته" | |||||
| > | |||||
| <option v-for="cat in cats" :key="cat.id" :value="cat.id"> | |||||
| {{ cat.title }} | |||||
| </option> | |||||
| </select> | |||||
| :options="formattedCategories" | |||||
| placeholder="دسته ای را انتخاب کنید" | |||||
| @search="handleSearch" | |||||
| /> | |||||
| </div> | </div> | ||||
| <small v-if="errors.blogCat" class="text-danger"> | <small v-if="errors.blogCat" class="text-danger"> | ||||
| {{ errors.blogCat }} | {{ errors.blogCat }} | ||||
| @@ -136,7 +134,7 @@ | |||||
| </BCol> | </BCol> | ||||
| <BCol | <BCol | ||||
| v-if="whichPart === 'product' || whichPart === 'both'" | |||||
| v-if="whichPart === 'product'" | |||||
| sm="6" | sm="6" | ||||
| class="mt-3" | class="mt-3" | ||||
| style="margin-top: 30px" | style="margin-top: 30px" | ||||
| @@ -147,8 +145,10 @@ | |||||
| <VueSelect | <VueSelect | ||||
| style="--vs-min-height: 48px; --vs-border-radius: 8px" | style="--vs-min-height: 48px; --vs-border-radius: 8px" | ||||
| :isLoading="categorySelectorLoader" | |||||
| v-model="selectedProduct" | v-model="selectedProduct" | ||||
| :options="formattedProducts" | :options="formattedProducts" | ||||
| @search="handleProductSearch" | |||||
| placeholder="محصولی را انتخاب کنید" | placeholder="محصولی را انتخاب کنید" | ||||
| /> | /> | ||||
| <small v-if="errors.selectedProduct" class="text-danger"> | <small v-if="errors.selectedProduct" class="text-danger"> | ||||
| @@ -212,11 +212,11 @@ | |||||
| <script> | <script> | ||||
| import VueSelect from "vue3-select-component"; | import VueSelect from "vue3-select-component"; | ||||
| import moment from "moment-jalaali"; | |||||
| import moment from "jalali-moment"; | |||||
| import { toast } from "vue3-toastify"; | import { toast } from "vue3-toastify"; | ||||
| import "vue3-toastify/dist/index.css"; | import "vue3-toastify/dist/index.css"; | ||||
| import ApiServiece from "@/services/ApiService"; | import ApiServiece from "@/services/ApiService"; | ||||
| import { ref, onMounted, computed } from "vue"; | |||||
| import { ref, computed } from "vue"; | |||||
| import Layout from "@/layout/custom.vue"; | import Layout from "@/layout/custom.vue"; | ||||
| import DatePicker from "vue3-persian-datetime-picker"; | import DatePicker from "vue3-persian-datetime-picker"; | ||||
| @@ -229,6 +229,7 @@ export default { | |||||
| VueSelect, | VueSelect, | ||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const productSelectorLoader = ref(false); | |||||
| const title = ref(); | const title = ref(); | ||||
| const discountType = ref(); | const discountType = ref(); | ||||
| const amount = ref(); | const amount = ref(); | ||||
| @@ -240,36 +241,58 @@ export default { | |||||
| const maxUsage = ref(); | const maxUsage = ref(); | ||||
| const whichPart = ref(); | const whichPart = ref(); | ||||
| const loading = ref(false); | const loading = ref(false); | ||||
| const cats = ref([]); | |||||
| const categories = ref([]); | |||||
| const errors = ref({}); | const errors = ref({}); | ||||
| const products = ref(); | const products = ref(); | ||||
| const getCats = () => { | |||||
| ApiServiece.get(`admin/categories`) | |||||
| .then((resp) => { | |||||
| cats.value = resp.data.data; | |||||
| }) | |||||
| .catch((err) => { | |||||
| console.log(err); | |||||
| }); | |||||
| const categorySelectorLoader = ref(false); | |||||
| const handleSearch = async (searchTerm) => { | |||||
| if (searchTerm.length < 3) return; | |||||
| categorySelectorLoader.value = true; | |||||
| try { | |||||
| const response = await ApiServiece.get( | |||||
| `admin/categories?title=${searchTerm}` | |||||
| ); | |||||
| categories.value = response.data.data; | |||||
| categorySelectorLoader.value = false; | |||||
| } catch (error) { | |||||
| categorySelectorLoader.value = false; | |||||
| categories.value = []; | |||||
| } | |||||
| }; | }; | ||||
| const getProduct = () => { | |||||
| ApiServiece.get(`admin/products`) | |||||
| .then((resp) => { | |||||
| products.value = resp.data.data; | |||||
| }) | |||||
| .catch((err) => { | |||||
| console.log(err); | |||||
| }); | |||||
| const formattedCategories = computed(() => | |||||
| Array.isArray(categories.value) | |||||
| ? categories.value.map((category) => ({ | |||||
| value: category.id, | |||||
| label: category.title, | |||||
| })) | |||||
| : [] | |||||
| ); | |||||
| const handleProductSearch = async (searchTerm) => { | |||||
| if (searchTerm.length < 3) return; | |||||
| productSelectorLoader.value = true; | |||||
| try { | |||||
| const response = await ApiServiece.get( | |||||
| `admin/products?title=${searchTerm}` | |||||
| ); | |||||
| products.value = response.data.data; | |||||
| productSelectorLoader.value = false; | |||||
| } catch (error) { | |||||
| productSelectorLoader.value = false; | |||||
| products.value = []; | |||||
| } | |||||
| }; | }; | ||||
| const formattedProducts = computed(() => { | |||||
| return products.value.map((product) => ({ | |||||
| value: product.id, | |||||
| label: product.title, | |||||
| })); | |||||
| }); | |||||
| const formattedProducts = computed(() => | |||||
| Array.isArray(products.value) | |||||
| ? products.value.map((product) => ({ | |||||
| value: product.id, | |||||
| label: product.title, | |||||
| })) | |||||
| : [] | |||||
| ); | |||||
| const validateForm = () => { | const validateForm = () => { | ||||
| errors.value = {}; | errors.value = {}; | ||||
| @@ -278,24 +301,14 @@ export default { | |||||
| errors.value.discountType = "وارد کردن حالت تخفیف الزامی است"; | errors.value.discountType = "وارد کردن حالت تخفیف الزامی است"; | ||||
| if (!amount.value) | if (!amount.value) | ||||
| errors.value.amount = "وارد کردن مقدار تخفیف الزامی می باشد"; | errors.value.amount = "وارد کردن مقدار تخفیف الزامی می باشد"; | ||||
| if (!minOrder.value) | |||||
| errors.value.minOrder = "وارد کردن حداقل میزان تخفیف الزامی می باشد"; | |||||
| if ( | |||||
| !selectedCat.value && | |||||
| (whichPart.value === "cat" || whichPart.value === "both") | |||||
| ) | |||||
| if (!selectedCat.value && whichPart.value === "cat") | |||||
| errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | ||||
| if ( | |||||
| !selectedProduct.value && | |||||
| (whichPart.value === "product" || whichPart.value === "both") | |||||
| ) | |||||
| if (!selectedProduct.value && whichPart.value === "product") | |||||
| errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | ||||
| if (!startDate.value) | if (!startDate.value) | ||||
| errors.value.startDate = "انتخاب تاریخ اعمال تخفیف الزامی می باشد "; | errors.value.startDate = "انتخاب تاریخ اعمال تخفیف الزامی می باشد "; | ||||
| if (!expire.value) | |||||
| errors.value.expire = "انتخاب تاریخ انقضای تخفیف الزامی می باشد "; | |||||
| if (!maxUsage.value) | |||||
| errors.value.maxUsage = "مشخص کنید تخفیف چند بار مصرف است "; | |||||
| if (!whichPart.value) | if (!whichPart.value) | ||||
| errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | ||||
| return Object.keys(errors.value).length === 0; | return Object.keys(errors.value).length === 0; | ||||
| @@ -305,21 +318,7 @@ export default { | |||||
| errors.value[field] = ""; | errors.value[field] = ""; | ||||
| }; | }; | ||||
| onMounted(() => { | |||||
| getCats(); | |||||
| getProduct(); | |||||
| }); | |||||
| function convertJalaliToGregorian(jalaliDate) { | |||||
| return moment(jalaliDate, "jYYYY/jMM/jDD HH:mm:ss") | |||||
| .locale("fa") // Ensure Persian locale | |||||
| .format("YYYY-MM-DD HH:mm:ss"); // Convert to Gregorian format | |||||
| } | |||||
| const submitForm = () => { | const submitForm = () => { | ||||
| startDate.value = convertJalaliToGregorian(startDate.value); | |||||
| expire.value = convertJalaliToGregorian(expire.value); | |||||
| if (!validateForm()) { | if (!validateForm()) { | ||||
| toast.error("لطفا فیلد های لازم را وارد نمایید", { | toast.error("لطفا فیلد های لازم را وارد نمایید", { | ||||
| position: "top-right", | position: "top-right", | ||||
| @@ -327,22 +326,38 @@ export default { | |||||
| }); | }); | ||||
| return; | return; | ||||
| } | } | ||||
| loading.value = true; | loading.value = true; | ||||
| const formData = new FormData(); | const formData = new FormData(); | ||||
| formData.append("title", title.value); | formData.append("title", title.value); | ||||
| formData.append("type", discountType.value); | formData.append("type", discountType.value); | ||||
| formData.append("amount", amount.value); | formData.append("amount", amount.value); | ||||
| formData.append("min_order", minOrder.value); | formData.append("min_order", minOrder.value); | ||||
| if (whichPart.value === "cat" || whichPart.value === "both") { | |||||
| if (whichPart.value === "cat") { | |||||
| formData.append("category_id", selectedCat.value); | formData.append("category_id", selectedCat.value); | ||||
| } | } | ||||
| if (whichPart.value === "product" || whichPart.value === "both") { | |||||
| if (whichPart.value === "product") { | |||||
| formData.append("product_id", selectedProduct.value); | formData.append("product_id", selectedProduct.value); | ||||
| } | } | ||||
| formData.append("starts_at", startDate.value); | |||||
| formData.append("expires_at", expire.value); | |||||
| if (startDate.value) { | |||||
| const georgianDate = moment( | |||||
| startDate.value, | |||||
| "jYYYY/jMM/jDD HH:mm:ss" | |||||
| ).format("YYYY/MM/DD HH:mm:ss"); | |||||
| formData.append("starts_at", georgianDate); | |||||
| } | |||||
| if (expire.value) { | |||||
| const georgianDate = moment( | |||||
| expire.value, | |||||
| "jYYYY/jMM/jDD HH:mm:ss" | |||||
| ).format("YYYY/MM/DD HH:mm:ss"); | |||||
| formData.append("expires_at", georgianDate); | |||||
| } | |||||
| formData.append("max_usage", maxUsage.value); | formData.append("max_usage", maxUsage.value); | ||||
| ApiServiece.post(`/admin/discounts`, formData) | ApiServiece.post(`/admin/discounts`, formData) | ||||
| @@ -353,6 +368,18 @@ export default { | |||||
| autoClose: 1000, | autoClose: 1000, | ||||
| }); | }); | ||||
| console.log(resp); | console.log(resp); | ||||
| // Clear form fields after successful submission | |||||
| title.value = ""; | |||||
| discountType.value = ""; | |||||
| amount.value = ""; | |||||
| minOrder.value = ""; | |||||
| whichPart.value = ""; | |||||
| selectedCat.value = null; | |||||
| selectedProduct.value = null; | |||||
| startDate.value = ""; | |||||
| expire.value = ""; | |||||
| maxUsage.value = ""; | |||||
| }) | }) | ||||
| .catch((error) => { | .catch((error) => { | ||||
| loading.value = false; | loading.value = false; | ||||
| @@ -365,7 +392,7 @@ export default { | |||||
| }; | }; | ||||
| return { | return { | ||||
| cats, | |||||
| categories, | |||||
| errors, | errors, | ||||
| title, | title, | ||||
| products, | products, | ||||
| @@ -381,6 +408,12 @@ export default { | |||||
| whichPart, | whichPart, | ||||
| clearError, | clearError, | ||||
| loading, | loading, | ||||
| handleSearch, | |||||
| categorySelectorLoader, | |||||
| formattedCategories, | |||||
| handleProductSearch, | |||||
| productSelectorLoader, | |||||
| formattedProducts, | formattedProducts, | ||||
| }; | }; | ||||
| }, | }, | ||||
| @@ -60,9 +60,9 @@ export default { | |||||
| ApiServiece.get( | ApiServiece.get( | ||||
| `admin/discounts?title=${searchQuery.value || ""}&product_id=${ | `admin/discounts?title=${searchQuery.value || ""}&product_id=${ | ||||
| selectedProduct.value || "" | selectedProduct.value || "" | ||||
| }&category_id=${selectedcategory.value || ""}&type=${selectedDiscountFormat.value || ""}&paginate=${ | |||||
| paginate.value || 10 | |||||
| }&page=${page.value || 1}` | |||||
| }&category_id=${selectedcategory.value || ""}&type=${ | |||||
| selectedDiscountFormat.value || "" | |||||
| }&paginate=${paginate.value || 10}&page=${page.value || 1}` | |||||
| ) | ) | ||||
| .then((resp) => { | .then((resp) => { | ||||
| filterLoading.value = false; | filterLoading.value = false; | ||||
| @@ -192,8 +192,7 @@ export default { | |||||
| }); | }); | ||||
| watch(selectedDiscountType, () => { | watch(selectedDiscountType, () => { | ||||
| selectedProduct.value = "", | |||||
| selectedcategory.value = "" | |||||
| (selectedProduct.value = ""), (selectedcategory.value = ""); | |||||
| if (selectedDiscountType.value === "") { | if (selectedDiscountType.value === "") { | ||||
| filterLoading.value = true; | filterLoading.value = true; | ||||
| ApiServiece.get( | ApiServiece.get( | ||||
| @@ -297,7 +296,7 @@ export default { | |||||
| <select | <select | ||||
| class="form-select form-select-sm" | class="form-select form-select-sm" | ||||
| v-model="selectedDiscountType" | v-model="selectedDiscountType" | ||||
| style="width: 120px; border-radius: 15px ; margin-right: 7px;" | |||||
| style="width: 120px; border-radius: 15px; margin-right: 7px" | |||||
| > | > | ||||
| <option value="" disabled selected>اعمال بر</option> | <option value="" disabled selected>اعمال بر</option> | ||||
| <option value="">همه</option> | <option value="">همه</option> | ||||
| @@ -339,7 +338,8 @@ export default { | |||||
| <th>مدل</th> | <th>مدل</th> | ||||
| <th>مقدار</th> | <th>مقدار</th> | ||||
| <th>حداقل سفارش</th> | <th>حداقل سفارش</th> | ||||
| <th>تاریخ ایجاد</th> | |||||
| <th>حداکثر میزان استفاده</th> | |||||
| <th>تاریخ شروع</th> | |||||
| <th>تاریخ انقضا</th> | <th>تاریخ انقضا</th> | ||||
| <th>عملیات</th> | <th>عملیات</th> | ||||
| </tr> | </tr> | ||||
| @@ -351,6 +351,7 @@ export default { | |||||
| <td v-if="discount.type === 'percentage'">درصدی</td> | <td v-if="discount.type === 'percentage'">درصدی</td> | ||||
| <td>{{ discount.amount }}</td> | <td>{{ discount.amount }}</td> | ||||
| <td>{{ discount.min_order }}</td> | <td>{{ discount.min_order }}</td> | ||||
| <td>{{ discount.max_usage || "" }}</td> | |||||
| <td>{{ convertToJalali(discount?.starts_at) }}</td> | <td>{{ convertToJalali(discount?.starts_at) }}</td> | ||||
| <td>{{ convertToJalali(discount?.expires_at) }}</td> | <td>{{ convertToJalali(discount?.expires_at) }}</td> | ||||
| <td> | <td> | ||||
| @@ -108,7 +108,7 @@ | |||||
| > | > | ||||
| <option value="cat">دسته</option> | <option value="cat">دسته</option> | ||||
| <option value="product">محصول</option> | <option value="product">محصول</option> | ||||
| <option value="both">هردو</option> | |||||
| <option value="all">همه</option> | |||||
| </select> | </select> | ||||
| </div> | </div> | ||||
| <small v-if="errors.discountType" class="text-danger"> | <small v-if="errors.discountType" class="text-danger"> | ||||
| @@ -116,47 +116,33 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol v-if="whichPart === 'cat' || whichPart === 'both'" md="6"> | |||||
| <BCol v-if="whichPart === 'cat'" md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">دسته</label> | <label class="form-label">دسته</label> | ||||
| <select | |||||
| :class="{ 'is-invalid': errors.selectedCat }" | |||||
| <VueSelect | |||||
| style="--vs-min-height: 48px; --vs-border-radius: 8px" | |||||
| :isLoading="categorySelectorLoader" | |||||
| v-model="selectedCat" | v-model="selectedCat" | ||||
| class="form-control" | |||||
| @select="clearError('selectedCat')" | |||||
| placeholder="انتخاب دسته" | |||||
| > | |||||
| <option v-for="cat in cats" :key="cat.id" :value="cat.id"> | |||||
| {{ cat.title }} | |||||
| </option> | |||||
| </select> | |||||
| :options="formattedCategories" | |||||
| placeholder="دسته ای را انتخاب کنید" | |||||
| /> | |||||
| </div> | </div> | ||||
| <small v-if="errors.selectedCat" class="text-danger"> | <small v-if="errors.selectedCat" class="text-danger"> | ||||
| {{ errors.selectedCat }} | {{ errors.selectedCat }} | ||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol | |||||
| v-if="whichPart === 'product' || whichPart === 'both'" | |||||
| md="6" | |||||
| > | |||||
| <BCol v-if="whichPart === 'product'" md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">محصول</label> | <label class="form-label">محصول</label> | ||||
| <select | |||||
| :class="{ 'is-invalid': errors.selectedProduct }" | |||||
| <VueSelect | |||||
| style="--vs-min-height: 48px; --vs-border-radius: 8px" | |||||
| :isLoading="productSelectorLoader" | |||||
| v-model="selectedProduct" | v-model="selectedProduct" | ||||
| class="form-control" | |||||
| @select="clearError('selectedProduct')" | |||||
| placeholder="انتخاب محصول" | |||||
| > | |||||
| <option | |||||
| v-for="product in products" | |||||
| :key="product.id" | |||||
| :value="product.id" | |||||
| > | |||||
| {{ product.title }} | |||||
| </option> | |||||
| </select> | |||||
| :options="formattedProducts" | |||||
| placeholder="محصولی را انتخاب کنید " | |||||
| @search="handleProductSearch" | |||||
| /> | |||||
| </div> | </div> | ||||
| <small v-if="errors.selectedProduct" class="text-danger"> | <small v-if="errors.selectedProduct" class="text-danger"> | ||||
| {{ errors.selectedProduct }} | {{ errors.selectedProduct }} | ||||
| @@ -220,12 +206,13 @@ | |||||
| </template> | </template> | ||||
| <script> | <script> | ||||
| import moment from "moment"; | |||||
| import VueSelect from "vue3-select-component"; | |||||
| import moment from "jalali-moment"; | |||||
| import { useRoute } from "vue-router"; | import { useRoute } from "vue-router"; | ||||
| import { toast } from "vue3-toastify"; | import { toast } from "vue3-toastify"; | ||||
| import "vue3-toastify/dist/index.css"; | import "vue3-toastify/dist/index.css"; | ||||
| import ApiServiece from "@/services/ApiService"; | import ApiServiece from "@/services/ApiService"; | ||||
| import { ref, onMounted } from "vue"; | |||||
| import { ref, onMounted, computed } from "vue"; | |||||
| import Layout from "@/layout/custom.vue"; | import Layout from "@/layout/custom.vue"; | ||||
| import DatePicker from "vue3-persian-datetime-picker"; | import DatePicker from "vue3-persian-datetime-picker"; | ||||
| @@ -234,39 +221,78 @@ export default { | |||||
| components: { | components: { | ||||
| Layout, | Layout, | ||||
| DatePicker, | DatePicker, | ||||
| VueSelect, | |||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const route = useRoute(); | const route = useRoute(); | ||||
| const productSelectorLoader = ref(false); | |||||
| const title = ref(); | const title = ref(); | ||||
| const discountType = ref(); | const discountType = ref(); | ||||
| const amount = ref(); | const amount = ref(); | ||||
| const minOrder = ref(); | const minOrder = ref(); | ||||
| const selectedCat = ref(); | const selectedCat = ref(); | ||||
| const selectedProduct = ref(); | |||||
| const selectedProduct = ref({ | |||||
| value: 'product-id-123', // Example selected product ID | |||||
| label: 'Amazing Product', // Example selected product label | |||||
| }); | |||||
| const startDate = ref(); | const startDate = ref(); | ||||
| const expire = ref(); | const expire = ref(); | ||||
| const maxUsage = ref(); | const maxUsage = ref(); | ||||
| const whichPart = ref(); | const whichPart = ref(); | ||||
| const loading = ref(false); | const loading = ref(false); | ||||
| const cats = ref([]); | |||||
| const categories = ref([]); | |||||
| const errors = ref({}); | const errors = ref({}); | ||||
| const products = ref(); | const products = ref(); | ||||
| const discount = ref(); | const discount = ref(); | ||||
| const getCats = () => { | |||||
| ApiServiece.get(`admin/categories`) | |||||
| .then((resp) => { | |||||
| cats.value = resp.data.data; | |||||
| }) | |||||
| .catch((err) => { | |||||
| console.log(err); | |||||
| }); | |||||
| const formattedCategories = computed(() => | |||||
| Array.isArray(categories.value) | |||||
| ? categories.value.map((category) => ({ | |||||
| value: category.id, | |||||
| label: category.title, | |||||
| })) | |||||
| : [] | |||||
| ); | |||||
| const handleProductSearch = async (searchTerm) => { | |||||
| if (searchTerm.length < 3) return; | |||||
| productSelectorLoader.value = true; | |||||
| try { | |||||
| const response = await ApiServiece.get( | |||||
| `admin/products?title=${searchTerm}` | |||||
| ); | |||||
| products.value = response.data.data; | |||||
| productSelectorLoader.value = false; | |||||
| } catch (error) { | |||||
| productSelectorLoader.value = false; | |||||
| products.value = []; | |||||
| } | |||||
| }; | }; | ||||
| const getProduct = () => { | |||||
| ApiServiece.get(`admin/products`) | |||||
| const formattedProducts = computed(() => { | |||||
| // If products are available, map them to options | |||||
| if (Array.isArray(products.value) && products.value.length > 0) { | |||||
| return products.value.map((product) => ({ | |||||
| value: product.id, | |||||
| label: product.title, | |||||
| })); | |||||
| } | |||||
| // If no products are available, show a placeholder option | |||||
| return [ | |||||
| { | |||||
| value: "", | |||||
| label: "برای جستجو شروع به تایپ کنید...", | |||||
| disabled: true, | |||||
| }, | |||||
| ]; | |||||
| }); | |||||
| const getCats = () => { | |||||
| ApiServiece.get(`admin/categories`) | |||||
| .then((resp) => { | .then((resp) => { | ||||
| products.value = resp.data.data; | |||||
| categories.value = resp.data.data; | |||||
| }) | }) | ||||
| .catch((err) => { | .catch((err) => { | ||||
| console.log(err); | console.log(err); | ||||
| @@ -285,14 +311,30 @@ export default { | |||||
| if (discount.value.category_id) { | if (discount.value.category_id) { | ||||
| whichPart.value = "cat"; | whichPart.value = "cat"; | ||||
| } | } | ||||
| console.log(discount.value.product_id); | |||||
| selectedProduct.value = discount.value.product_id; | |||||
| if (!discount.value.category_id && !discount.value.product_id) { | |||||
| whichPart.value = "all"; | |||||
| } | |||||
| if (discount.value.product_id) { | if (discount.value.product_id) { | ||||
| whichPart.value = "product"; | whichPart.value = "product"; | ||||
| } | } | ||||
| startDate.value = discount.value.starts_at; | |||||
| expire.value = discount.value.expires_at; | |||||
| if (discount?.value.starts_at) { | |||||
| startDate.value = moment( | |||||
| discount?.value.starts_at, | |||||
| "YYYY-MM-DD HH:mm:ss" | |||||
| ).format("jYYYY/jMM/jDD HH:mm:ss"); | |||||
| } | |||||
| if (discount?.value.expires_at) { | |||||
| expire.value = moment( | |||||
| discount?.value.expires_at, | |||||
| "YYYY-MM-DD HH:mm:ss" | |||||
| ).format("jYYYY/jMM/jDD HH:mm:ss"); | |||||
| } | |||||
| maxUsage.value = discount.value.max_usage; | maxUsage.value = discount.value.max_usage; | ||||
| }) | }) | ||||
| .catch((err) => { | .catch((err) => { | ||||
| @@ -328,24 +370,14 @@ export default { | |||||
| errors.value.discountType = "وارد کردن حالت تخفیف الزامی است"; | errors.value.discountType = "وارد کردن حالت تخفیف الزامی است"; | ||||
| if (!amount.value) | if (!amount.value) | ||||
| errors.value.amount = "وارد کردن مقدار تخفیف الزامی می باشد"; | errors.value.amount = "وارد کردن مقدار تخفیف الزامی می باشد"; | ||||
| if (!minOrder.value) | |||||
| errors.value.minOrder = "وارد کردن حداقل میزان تخفیف الزامی می باشد"; | |||||
| if ( | |||||
| !selectedCat.value && | |||||
| (whichPart.value === "cat" || whichPart.value === "both") | |||||
| ) | |||||
| if (!selectedCat.value && whichPart.value === "cat") | |||||
| errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | ||||
| if ( | |||||
| !selectedProduct.value && | |||||
| (whichPart.value === "product" || whichPart.value === "both") | |||||
| ) | |||||
| if (!selectedProduct.value && whichPart.value === "product") | |||||
| errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | ||||
| if (!startDate.value) | if (!startDate.value) | ||||
| errors.value.startDate = "انتخاب تاریخ اعمال تخفیف الزامی می باشد "; | errors.value.startDate = "انتخاب تاریخ اعمال تخفیف الزامی می باشد "; | ||||
| if (!expire.value) | |||||
| errors.value.expire = "انتخاب تاریخ انقضای تخفیف الزامی می باشد "; | |||||
| if (!maxUsage.value) | |||||
| errors.value.maxUsage = "مشخص کنید تخفیف چند بار مصرف است "; | |||||
| if (!whichPart.value) | if (!whichPart.value) | ||||
| errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | ||||
| return Object.keys(errors.value).length === 0; | return Object.keys(errors.value).length === 0; | ||||
| @@ -357,7 +389,7 @@ export default { | |||||
| onMounted(() => { | onMounted(() => { | ||||
| getCats(); | getCats(); | ||||
| getProduct(); | |||||
| getDiscount(); | getDiscount(); | ||||
| }); | }); | ||||
| @@ -375,16 +407,30 @@ export default { | |||||
| formData.append("type", discountType.value); | formData.append("type", discountType.value); | ||||
| formData.append("amount", amount.value); | formData.append("amount", amount.value); | ||||
| formData.append("min_order", minOrder.value); | formData.append("min_order", minOrder.value); | ||||
| if (whichPart.value === "cat" || whichPart.value === "both") { | |||||
| if (whichPart.value === "cat") { | |||||
| formData.append("category_id", selectedCat.value); | formData.append("category_id", selectedCat.value); | ||||
| } | } | ||||
| if (whichPart.value === "product" || whichPart.value === "both") { | |||||
| if (whichPart.value === "product") { | |||||
| formData.append("product_id", selectedProduct.value); | formData.append("product_id", selectedProduct.value); | ||||
| } | } | ||||
| formData.append("starts_at", startDate.value); | |||||
| formData.append("expires_at", expire.value); | |||||
| if (startDate.value) { | |||||
| const georgianDate = moment( | |||||
| startDate.value, | |||||
| "jYYYY/jMM/jDD HH:mm:ss" | |||||
| ).format("YYYY/MM/DD HH:mm:ss"); | |||||
| formData.append("starts_at", georgianDate); | |||||
| } | |||||
| if (expire.value) { | |||||
| const georgianDate = moment( | |||||
| expire.value, | |||||
| "jYYYY/jMM/jDD HH:mm:ss" | |||||
| ).format("YYYY/MM/DD HH:mm:ss"); | |||||
| formData.append("expires_at", georgianDate); | |||||
| } | |||||
| formData.append("max_usage", maxUsage.value); | formData.append("max_usage", maxUsage.value); | ||||
| ApiServiece.post(`/admin/discounts`, formData) | ApiServiece.post(`/admin/discounts`, formData) | ||||
| @@ -407,7 +453,7 @@ export default { | |||||
| }; | }; | ||||
| return { | return { | ||||
| cats, | |||||
| categories, | |||||
| errors, | errors, | ||||
| title, | title, | ||||
| products, | products, | ||||
| @@ -425,6 +471,10 @@ export default { | |||||
| whichPart, | whichPart, | ||||
| clearError, | clearError, | ||||
| loading, | loading, | ||||
| formattedCategories, | |||||
| formattedProducts, | |||||
| handleProductSearch, | |||||
| productSelectorLoader, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -34,18 +34,19 @@ export default { | |||||
| const attributeTitle = ref(); | const attributeTitle = ref(); | ||||
| const attributeId = ref(); | const attributeId = ref(); | ||||
| const attrebuteCat = ref(); | const attrebuteCat = ref(); | ||||
| const selectorLoader = ref(false); | |||||
| const handleSearch = async (searchTerm) => { | const handleSearch = async (searchTerm) => { | ||||
| if (searchTerm.length < 3) return; | if (searchTerm.length < 3) return; | ||||
| selectorLoader.value = true; | |||||
| try { | try { | ||||
| const response = await ApiServiece.get( | const response = await ApiServiece.get( | ||||
| `admin/categories?title=${searchTerm}` | `admin/categories?title=${searchTerm}` | ||||
| ); | ); | ||||
| categories.value = response.data.data; | categories.value = response.data.data; | ||||
| console.log(categories.value, "products"); | |||||
| selectorLoader.value = false; | |||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Error fetching products:", error); | |||||
| selectorLoader.value = false; | |||||
| categories.value = []; | categories.value = []; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -231,6 +232,7 @@ export default { | |||||
| formattedCategories, | formattedCategories, | ||||
| handleSearch, | handleSearch, | ||||
| selectedcategory, | selectedcategory, | ||||
| selectorLoader, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -255,10 +257,17 @@ export default { | |||||
| <VueSelect | <VueSelect | ||||
| style="--vs-border-radius: 16px" | style="--vs-border-radius: 16px" | ||||
| v-model="selectedcategory" | v-model="selectedcategory" | ||||
| :isLoading="selectorLoader" | |||||
| :options="formattedCategories" | :options="formattedCategories" | ||||
| placeholder="دسته ای را انتخاب کنید" | placeholder="دسته ای را انتخاب کنید" | ||||
| @search="handleSearch" | @search="handleSearch" | ||||
| /> | |||||
| > | |||||
| <template #menu-header> | |||||
| <div class="menu-header"> | |||||
| <h3>دسته ها</h3> | |||||
| </div> | |||||
| </template> | |||||
| </VueSelect> | |||||
| </div> | </div> | ||||
| <button | <button | ||||
| data-bs-toggle="modal" | data-bs-toggle="modal" | ||||
| @@ -500,4 +509,15 @@ export default { | |||||
| cursor: pointer; | cursor: pointer; | ||||
| user-select: none; | user-select: none; | ||||
| } | } | ||||
| .menu-header { | |||||
| position: sticky; | |||||
| top: 0; | |||||
| padding: 0.5rem 1rem; | |||||
| background-color: #f4f4f5; | |||||
| } | |||||
| .menu-header h3 { | |||||
| margin: 0; | |||||
| color: var(--vs-option-text-color); | |||||
| } | |||||
| </style> | </style> | ||||
| @@ -13,6 +13,7 @@ export default { | |||||
| DatePicker, | DatePicker, | ||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const selectorLoader = ref(false); | |||||
| const isLoading = ref(false); | const isLoading = ref(false); | ||||
| const date = ref([]); | const date = ref([]); | ||||
| const brands = ref([]); | const brands = ref([]); | ||||
| @@ -25,6 +26,18 @@ export default { | |||||
| const selectedBrand = ref(); | const selectedBrand = ref(); | ||||
| const allProducts = ref([]); | const allProducts = ref([]); | ||||
| const getAllProducts = () => { | const getAllProducts = () => { | ||||
| if (date.value[0]) { | |||||
| date.value[0] = moment(date.value[0], "YYYY-MM-DD HH:mm:ss").format( | |||||
| "YYYY/MM/DD HH:mm:ss" | |||||
| ); | |||||
| } | |||||
| if (date.value[1]) { | |||||
| date.value[1] = moment(date.value[1], "YYYY-MM-DD HH:mm:ss").format( | |||||
| "YYYY/MM/DD HH:mm:ss" | |||||
| ); | |||||
| } | |||||
| filterLoading.value = true; | filterLoading.value = true; | ||||
| ApiServiece.get( | ApiServiece.get( | ||||
| `admin/orders/order-items/approved?brand_id=${ | `admin/orders/order-items/approved?brand_id=${ | ||||
| @@ -46,10 +59,10 @@ export default { | |||||
| }; | }; | ||||
| const convertToJalali = (date) => { | const convertToJalali = (date) => { | ||||
| return moment(date, "YYYY-MM-DD HH:mm:ss") | |||||
| .locale("fa") | |||||
| .format("YYYY/MM/DD"); | |||||
| }; | |||||
| return moment(date, "YYYY-MM-DD HH:mm:ss") | |||||
| .locale("fa") | |||||
| .format("jYYYY/jMM/jDD HH:mm:ss"); | |||||
| }; | |||||
| const getFile = () => { | const getFile = () => { | ||||
| isLoading.value = true; | isLoading.value = true; | ||||
| @@ -149,14 +162,15 @@ export default { | |||||
| const handleBrandSearch = async (searchTerm) => { | const handleBrandSearch = async (searchTerm) => { | ||||
| if (searchTerm.length < 3) return; | if (searchTerm.length < 3) return; | ||||
| selectorLoader.value = true; | |||||
| try { | try { | ||||
| const response = await ApiServiece.get( | const response = await ApiServiece.get( | ||||
| `admin/brands?title=${searchTerm}` | `admin/brands?title=${searchTerm}` | ||||
| ); | ); | ||||
| brands.value = response.data.data; | brands.value = response.data.data; | ||||
| selectorLoader.value = false; | |||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Error fetching products:", error); | |||||
| selectorLoader.value = false; | |||||
| brands.value = []; | brands.value = []; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -193,6 +207,7 @@ export default { | |||||
| formatWithCommas, | formatWithCommas, | ||||
| handleBrandSearch, | handleBrandSearch, | ||||
| filterLoading, | filterLoading, | ||||
| selectorLoader, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -214,17 +229,17 @@ export default { | |||||
| style="--vs-border-radius: 8px; min-width: 180px" | style="--vs-border-radius: 8px; min-width: 180px" | ||||
| v-model="selectedBrand" | v-model="selectedBrand" | ||||
| :options="formattedBrands" | :options="formattedBrands" | ||||
| :isLoading="selectorLoader" | |||||
| placeholder="برندی را انتخاب کنید" | placeholder="برندی را انتخاب کنید" | ||||
| @search="handleBrandSearch" | @search="handleBrandSearch" | ||||
| /> | /> | ||||
| <!-- Date Picker with Proper Width --> | <!-- Date Picker with Proper Width --> | ||||
| <DatePicker | <DatePicker | ||||
| format="YYYY/MM/DD HH:mm:ss" | |||||
| type="date" | |||||
| :format="'jYYYY/jMM/jDD HH:mm:ss'" | |||||
| type="datetime" | |||||
| :range="true" | :range="true" | ||||
| v-model="date" | v-model="date" | ||||
| @input="handleInput" | |||||
| class="custom-datepicker" | class="custom-datepicker" | ||||
| /> | /> | ||||
| </div> | </div> | ||||
| @@ -82,6 +82,7 @@ export default { | |||||
| shipping: "badge-shipping", | shipping: "badge-shipping", | ||||
| delivered: "badge-delivered", | delivered: "badge-delivered", | ||||
| canceled: "badge-canceled", | canceled: "badge-canceled", | ||||
| in_cart: "badge-in-cart", | |||||
| }; | }; | ||||
| return statusClasses[status] || "badge-secondary"; | return statusClasses[status] || "badge-secondary"; | ||||
| }; | }; | ||||
| @@ -96,6 +97,7 @@ export default { | |||||
| shipping: "در حال ارسال", | shipping: "در حال ارسال", | ||||
| delivered: "تحویلشده", | delivered: "تحویلشده", | ||||
| canceled: "لغوشده", | canceled: "لغوشده", | ||||
| in_cart: "در سبد خرید", | |||||
| }; | }; | ||||
| return statusLabels[status] || "نامشخص"; | return statusLabels[status] || "نامشخص"; | ||||
| }; | }; | ||||
| @@ -270,23 +272,29 @@ export default { | |||||
| <table class="table table-hover table-bordered m-0" dir="rtl"> | <table class="table table-hover table-bordered m-0" dir="rtl"> | ||||
| <thead class="table-light"> | <thead class="table-light"> | ||||
| <tr> | <tr> | ||||
| <th>شناسه</th> | |||||
| <th>وضعیت</th> | <th>وضعیت</th> | ||||
| <th>قیمت</th> | |||||
| <th>کاربر</th> | |||||
| <th>کد پیگیری</th> | |||||
| <th>تاریخ ایجاد</th> | <th>تاریخ ایجاد</th> | ||||
| <th>عملیات</th> | <th>عملیات</th> | ||||
| </tr> | </tr> | ||||
| </thead> | </thead> | ||||
| <tbody> | <tbody> | ||||
| <tr v-for="order in orders" :key="order.id"> | <tr v-for="order in orders" :key="order.id"> | ||||
| <td>{{ order?.id }}</td> | |||||
| <td> | <td> | ||||
| <span class="badge" :class="getStatusClass(order.status)"> | <span class="badge" :class="getStatusClass(order.status)"> | ||||
| {{ getStatusLabel(order.status) }} | {{ getStatusLabel(order.status) }} | ||||
| </span> | </span> | ||||
| </td> | </td> | ||||
| <td>{{ order?.total_price || "" }}</td> | |||||
| <td>{{ order?.user?.mobile || "مهمان" }}</td> | |||||
| <td>{{ order?.tracking_code || "" }}</td> | |||||
| <td>{{ convertToJalali(order?.created_at) }}</td> | <td>{{ convertToJalali(order?.created_at) }}</td> | ||||
| <td> | <td> | ||||
| @@ -597,6 +605,9 @@ export default { | |||||
| .badge-canceled { | .badge-canceled { | ||||
| background-color: #6c757d; | background-color: #6c757d; | ||||
| } | } | ||||
| .badge-in-cart { | |||||
| background-color: #0620c6; /* You can choose any color you prefer */ | |||||
| } | |||||
| .dropdown-item { | .dropdown-item { | ||||
| cursor: pointer; | cursor: pointer; | ||||
| } | } | ||||
| @@ -235,7 +235,7 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol v-if="spescial" md="6"> | |||||
| <BCol v-if="spescial == 1" md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label"> تخفیف ویژه </label> | <label class="form-label"> تخفیف ویژه </label> | ||||
| <input | <input | ||||
| @@ -252,7 +252,7 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol v-if="spescial" md="6"> | |||||
| <BCol v-if="spescial == 1" md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label"> | <label class="form-label"> | ||||
| تاریخ انقضای تخفیف محصول ویژه | تاریخ انقضای تخفیف محصول ویژه | ||||
| @@ -262,7 +262,6 @@ | |||||
| :format="'jYYYY/jMM/jDD HH:mm:ss'" | :format="'jYYYY/jMM/jDD HH:mm:ss'" | ||||
| type="datetime" | type="datetime" | ||||
| v-model="expire" | v-model="expire" | ||||
| @input="handleInput" | |||||
| ></DatePicker> | ></DatePicker> | ||||
| </div> | </div> | ||||
| <small v-if="errors.expire" class="text-danger"> | <small v-if="errors.expire" class="text-danger"> | ||||
| @@ -270,7 +269,7 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol md="6"> | |||||
| <!-- <BCol md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">دسته</label> | <label class="form-label">دسته</label> | ||||
| <select | <select | ||||
| @@ -288,26 +287,34 @@ | |||||
| <small v-if="errors.selectedCat" class="text-danger"> | <small v-if="errors.selectedCat" class="text-danger"> | ||||
| {{ errors.selectedCat }} | {{ errors.selectedCat }} | ||||
| </small> | </small> | ||||
| </BCol> --> | |||||
| <BCol md="6"> | |||||
| <div class="form-group"> | |||||
| <label class="form-label">دسته</label> | |||||
| <VueSelect | |||||
| :isLoading="categorySelectorLoader" | |||||
| v-model="selectedCat" | |||||
| :options="formattedCategories" | |||||
| placeholder="دسته ای را انتخاب کنید" | |||||
| @search="handleSearch" | |||||
| /> | |||||
| </div> | |||||
| <small v-if="errors.selectedCat" class="text-danger"> | |||||
| {{ errors.selectedCat }} | |||||
| </small> | |||||
| </BCol> | </BCol> | ||||
| <BCol md="6"> | <BCol md="6"> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">برند</label> | <label class="form-label">برند</label> | ||||
| <select | |||||
| :class="{ 'is-invalid': errors.selectedBrand }" | |||||
| <VueSelect | |||||
| v-model="selectedBrand" | v-model="selectedBrand" | ||||
| class="form-control" | |||||
| @change="clearError('selectedBrand')" | |||||
| placeholder="انتخاب برند محصول" | |||||
| > | |||||
| <option | |||||
| v-for="brand in brands" | |||||
| :key="brand.id" | |||||
| :value="brand.id" | |||||
| > | |||||
| {{ brand.title }} | |||||
| </option> | |||||
| </select> | |||||
| :isLoading="brandSelectorLoader" | |||||
| :options="formattedBrands" | |||||
| placeholder="برندی را انتخاب کنید" | |||||
| @search="handleBrandSearch" | |||||
| /> | |||||
| </div> | </div> | ||||
| <small v-if="errors.selectedBrand" class="text-danger"> | <small v-if="errors.selectedBrand" class="text-danger"> | ||||
| {{ errors.selectedBrand }} | {{ errors.selectedBrand }} | ||||
| @@ -549,7 +556,10 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| </BCardFooter> | </BCardFooter> | ||||
| <addIdentity :cats="cats" @identity-updated="handleIdentityUpdate" /> | |||||
| <addIdentity | |||||
| :cats="categories" | |||||
| @identity-updated="handleIdentityUpdate" | |||||
| /> | |||||
| <addAttribute | <addAttribute | ||||
| :attributeValues="attributeValues" | :attributeValues="attributeValues" | ||||
| @attribute-updated="handleAttributeUpdated()" | @attribute-updated="handleAttributeUpdated()" | ||||
| @@ -561,15 +571,17 @@ | |||||
| </template> | </template> | ||||
| <script> | <script> | ||||
| import VueSelect from "vue3-select-component"; | |||||
| import addAttribute from "@/components/modals/attribute/addAttribute.vue"; | import addAttribute from "@/components/modals/attribute/addAttribute.vue"; | ||||
| import moment from "moment"; | |||||
| import moment from "jalali-moment"; | |||||
| import { toast } from "vue3-toastify"; | import { toast } from "vue3-toastify"; | ||||
| import "vue3-toastify/dist/index.css"; | import "vue3-toastify/dist/index.css"; | ||||
| import ApiServiece from "@/services/ApiService"; | import ApiServiece from "@/services/ApiService"; | ||||
| import { ref, onMounted, watch } from "vue"; | |||||
| import { ref, onMounted, watch, computed } from "vue"; | |||||
| import Layout from "@/layout/custom.vue"; | import Layout from "@/layout/custom.vue"; | ||||
| import DatePicker from "vue3-persian-datetime-picker"; | import DatePicker from "vue3-persian-datetime-picker"; | ||||
| import addIdentity from "@/components/modals/identity/addIdentity.vue"; | import addIdentity from "@/components/modals/identity/addIdentity.vue"; | ||||
| export default { | export default { | ||||
| name: "SAMPLE-PAGE", | name: "SAMPLE-PAGE", | ||||
| components: { | components: { | ||||
| @@ -577,8 +589,12 @@ export default { | |||||
| DatePicker, | DatePicker, | ||||
| addIdentity, | addIdentity, | ||||
| addAttribute, | addAttribute, | ||||
| VueSelect, | |||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const categorySelectorLoader = ref(false); | |||||
| const categories = ref([]); | |||||
| const brandSelectorLoader = ref(false); | |||||
| const attributeValues = ref(); | const attributeValues = ref(); | ||||
| const relatedAttrebutes = ref([]); | const relatedAttrebutes = ref([]); | ||||
| const countInCarton = ref(); | const countInCarton = ref(); | ||||
| @@ -610,18 +626,8 @@ export default { | |||||
| const blogCat = ref(); | const blogCat = ref(); | ||||
| const author = ref(""); | const author = ref(""); | ||||
| const editor = ref(null); | const editor = ref(null); | ||||
| const cats = ref([]); | |||||
| const editorContent = ref(""); | |||||
| const getCats = () => { | |||||
| ApiServiece.get(`admin/categories`) | |||||
| .then((resp) => { | |||||
| cats.value = resp.data.data; | |||||
| }) | |||||
| .catch((err) => { | |||||
| console.log(err); | |||||
| }); | |||||
| }; | |||||
| const editorContent = ref(""); | |||||
| const getAttributeValues = () => { | const getAttributeValues = () => { | ||||
| ApiServiece.get(`admin/attributes`).then((resp) => { | ApiServiece.get(`admin/attributes`).then((resp) => { | ||||
| @@ -656,28 +662,54 @@ export default { | |||||
| }); | }); | ||||
| }; | }; | ||||
| const handleInput = () => { | |||||
| if (expire.value) { | |||||
| // Convert from Jalali to Georgian (Gregorian) | |||||
| expire.value = moment(expire.value, "jYYYY/jMM/jDD HH:mm:ss").format( | |||||
| "YYYY-MM-DD HH:mm:ss" | |||||
| const handleBrandSearch = async (searchTerm) => { | |||||
| if (searchTerm.length < 3) return; | |||||
| brandSelectorLoader.value = true; | |||||
| try { | |||||
| const response = await ApiServiece.get( | |||||
| `admin/brands?title=${searchTerm}` | |||||
| ); | ); | ||||
| } else { | |||||
| expire.value = null; | |||||
| clearError("expire"); | |||||
| brands.value = response.data.data; | |||||
| brandSelectorLoader.value = false; | |||||
| } catch (error) { | |||||
| brandSelectorLoader.value = false; | |||||
| brands.value = []; | |||||
| } | } | ||||
| }; | }; | ||||
| const getBrands = () => { | |||||
| ApiServiece.get(`admin/brands`) | |||||
| .then((resp) => { | |||||
| brands.value = resp.data.data; | |||||
| }) | |||||
| .catch((err) => { | |||||
| console.log(err); | |||||
| }); | |||||
| const formattedBrands = computed(() => | |||||
| Array.isArray(brands.value) | |||||
| ? brands.value.map((brand) => ({ | |||||
| value: brand.id, | |||||
| label: brand.title, | |||||
| })) | |||||
| : [] | |||||
| ); | |||||
| const handleSearch = async (searchTerm) => { | |||||
| if (searchTerm.length < 3) return; | |||||
| categorySelectorLoader.value = true; | |||||
| try { | |||||
| const response = await ApiServiece.get( | |||||
| `admin/categories?title=${searchTerm}` | |||||
| ); | |||||
| categories.value = response.data.data; | |||||
| categorySelectorLoader.value = false; | |||||
| } catch (error) { | |||||
| categorySelectorLoader.value = false; | |||||
| categories.value = []; | |||||
| } | |||||
| }; | }; | ||||
| const formattedCategories = computed(() => | |||||
| Array.isArray(categories.value) | |||||
| ? categories.value.map((category) => ({ | |||||
| value: category.id, | |||||
| label: category.title, | |||||
| })) | |||||
| : [] | |||||
| ); | |||||
| const getAttrebuteValues = () => { | const getAttrebuteValues = () => { | ||||
| ApiServiece.get(`admin/attribute-values?attribute_id=1`) | ApiServiece.get(`admin/attribute-values?attribute_id=1`) | ||||
| .then((resp) => { | .then((resp) => { | ||||
| @@ -815,18 +847,12 @@ export default { | |||||
| errors.value[field] = ""; | errors.value[field] = ""; | ||||
| }; | }; | ||||
| onMounted(() => { | onMounted(() => { | ||||
| getCats(); | |||||
| getBrands(); | |||||
| getAttrebuteValues(); | getAttrebuteValues(); | ||||
| getAttributeValues(); | getAttributeValues(); | ||||
| }); | }); | ||||
| let id = ""; | |||||
| const submitForm = () => { | const submitForm = () => { | ||||
| console.log(expire.value , "test") | |||||
| if (!validateForm()) { | if (!validateForm()) { | ||||
| toast.error("لطفا فیلد های لازم را وارد نمایید", { | toast.error("لطفا فیلد های لازم را وارد نمایید", { | ||||
| position: "top-right", | position: "top-right", | ||||
| @@ -842,6 +868,8 @@ export default { | |||||
| formData.append("slug", slug.value); | formData.append("slug", slug.value); | ||||
| formData.append("summary", summary.value); | formData.append("summary", summary.value); | ||||
| formData.append("description", description.value); | formData.append("description", description.value); | ||||
| formData.append("is_special", spescial.value); | |||||
| formData.append("is_chosen", isChosen.value); | |||||
| if (productType.value == 2) { | if (productType.value == 2) { | ||||
| formData.append("wholesale_price", wholesalePrice.value); | formData.append("wholesale_price", wholesalePrice.value); | ||||
| } | } | ||||
| @@ -858,31 +886,24 @@ export default { | |||||
| formData.append("chosen_price", chosenPrice.value); | formData.append("chosen_price", chosenPrice.value); | ||||
| } | } | ||||
| if (isChosen.value == 1 && spescial.value == 0) { | |||||
| formData.append("is_chosen", isChosen.value); | |||||
| } | |||||
| if (isChosen.value == 0 && spescial.value == 1) { | |||||
| formData.append("is_chosen", isChosen.value); | |||||
| } | |||||
| if (spescial.value == 1 && isChosen.value == 0) { | if (spescial.value == 1 && isChosen.value == 0) { | ||||
| formData.append("special_price", parseInt(spescialPrice.value, 10)); | formData.append("special_price", parseInt(spescialPrice.value, 10)); | ||||
| formData.append("special_expires_at", expire.value); | |||||
| } | |||||
| if (spescial.value == 1 && isChosen.value == 0) { | |||||
| formData.append("is_special", spescial.value); | |||||
| } | |||||
| if (spescial.value == 0 && isChosen.value == 1) { | |||||
| formData.append("is_special", spescial.value); | |||||
| // Convert Jalali to Gregorian before sending | |||||
| const georgianDate = moment( | |||||
| expire.value, | |||||
| "jYYYY/jMM/jDD HH:mm:ss" | |||||
| ).format("YYYY/MM/DD HH:mm:ss"); | |||||
| formData.append("special_expires_at", georgianDate); | |||||
| } | } | ||||
| formData.append("brand_id", selectedBrand.value); | formData.append("brand_id", selectedBrand.value); | ||||
| formData.append("category_id", selectedCat.value); | formData.append("category_id", selectedCat.value); | ||||
| formData.append("count_in_carton", countInCarton.value); | |||||
| if (productType.value === "2" || productType.value === "3") { | |||||
| formData.append("count_in_carton", countInCarton.value); | |||||
| } | |||||
| formData.append("image", image.value); | formData.append("image", image.value); | ||||
| ApiServiece.post(`admin/products`, formData, { | ApiServiece.post(`admin/products`, formData, { | ||||
| @@ -892,76 +913,98 @@ export default { | |||||
| }, | }, | ||||
| }) | }) | ||||
| .then((resp) => { | .then((resp) => { | ||||
| id = resp.data.data.id; | |||||
| console.log(id); | |||||
| const id = resp.data.data.id; | |||||
| // 🔸 Call attributes API if needed | |||||
| selectedAttributes.value = attrebutes.value | selectedAttributes.value = attrebutes.value | ||||
| .filter((attribute) => attribute.isChecked) | |||||
| .filter((attribute) => attribute.isChecked && attribute.value) | |||||
| .map((attribute) => ({ | .map((attribute) => ({ | ||||
| attribute_value_id: attribute.id, | attribute_value_id: attribute.id, | ||||
| inventory: attribute.value, | inventory: attribute.value, | ||||
| type: productType.value, | type: productType.value, | ||||
| })); | })); | ||||
| const finalPayload = { | |||||
| productAttributes: selectedAttributes.value, | |||||
| }; | |||||
| const jsonString = JSON.stringify(finalPayload, null, 2); | |||||
| console.log(jsonString); | |||||
| ApiServiece.post(`admin/products/${id}/attributes`, jsonString) | |||||
| .then(() => { | |||||
| selectedIdentities.value = relatedAttrebutes.value | |||||
| .filter((identity) => identity.isChecked) | |||||
| .map((identity) => ({ | |||||
| attribute_id: identity.id, | |||||
| attribute_value_title: identity.value, | |||||
| })); | |||||
| const finalPayload = { | |||||
| productSolidAttributes: selectedIdentities.value, | |||||
| }; | |||||
| const jsonString = JSON.stringify(finalPayload, null, 2); | |||||
| ApiServiece.post( | |||||
| `admin/products/${id}/solid-attributes`, | |||||
| jsonString | |||||
| ).then((resp) => { | |||||
| console.log(resp); | |||||
| }); | |||||
| }) | |||||
| .then((resp) => { | |||||
| console.log(resp); | |||||
| images.value.map((image) => { | |||||
| console.log(image.file); | |||||
| const formData = new FormData(); | |||||
| formData.append("image", image.file); | |||||
| ApiServiece.post(`admin/products/${id}/images`, formData, { | |||||
| headers: { | |||||
| "content-type": "multipart", | |||||
| Authorization: `Bearer ${localStorage.getItem("token")}`, | |||||
| }, | |||||
| }); | |||||
| if (selectedAttributes.value.length > 0) { | |||||
| const json = JSON.stringify( | |||||
| { productAttributes: selectedAttributes.value }, | |||||
| null, | |||||
| 2 | |||||
| ); | |||||
| ApiServiece.post(`admin/products/${id}/attributes`, json); | |||||
| } | |||||
| // 🔸 Call solid-attributes API if needed | |||||
| selectedIdentities.value = relatedAttrebutes.value | |||||
| .filter((identity) => identity.isChecked) | |||||
| .map((identity) => ({ | |||||
| attribute_id: identity.id, | |||||
| attribute_value_title: identity.value, | |||||
| })); | |||||
| if (selectedIdentities.value.length > 0) { | |||||
| const json = JSON.stringify( | |||||
| { productSolidAttributes: selectedIdentities.value }, | |||||
| null, | |||||
| 2 | |||||
| ); | |||||
| ApiServiece.post(`admin/products/${id}/solid-attributes`, json); | |||||
| } | |||||
| // 🔸 Upload images if any | |||||
| const validImages = images.value.filter((image) => image.file); | |||||
| if (validImages.length > 0) { | |||||
| console.log(validImages, "valid images"); | |||||
| validImages.forEach((image) => { | |||||
| const formData = new FormData(); | |||||
| formData.append("image", image.file); | |||||
| ApiServiece.post(`admin/products/${id}/images`, formData, { | |||||
| headers: { | |||||
| "content-type": "multipart", | |||||
| Authorization: `Bearer ${localStorage.getItem("token")}`, | |||||
| }, | |||||
| }); | }); | ||||
| }); | }); | ||||
| }) | |||||
| } | |||||
| .then(() => { | |||||
| loading.value = false; | |||||
| toast.success("!محصول با موفقیت اضافه شد", { | toast.success("!محصول با موفقیت اضافه شد", { | ||||
| position: "top-right", | position: "top-right", | ||||
| autoClose: 1000, | autoClose: 1000, | ||||
| }); | }); | ||||
| }) | }) | ||||
| .catch((error) => { | .catch((error) => { | ||||
| console.error(error); | console.error(error); | ||||
| loading.value = false; | |||||
| toast.error(`${error?.response?.data?.message}`, { | toast.error(`${error?.response?.data?.message}`, { | ||||
| position: "top-right", | position: "top-right", | ||||
| autoClose: 1000, | autoClose: 1000, | ||||
| }); | }); | ||||
| }) | |||||
| .finally(() => { | |||||
| loading.value = false; | |||||
| title.value = ""; | |||||
| imagePreview.value = null; | |||||
| slug.value = ""; | |||||
| summary.value = ""; | |||||
| description.value = ""; | |||||
| spescial.value = 0; | |||||
| isChosen.value = 0; | |||||
| wholesalePrice.value = ""; | |||||
| retailePrice.value = ""; | |||||
| productType.value = ""; | |||||
| chosenPrice.value = ""; | |||||
| spescialPrice.value = ""; | |||||
| expire.value = ""; | |||||
| selectedBrand.value = null; | |||||
| selectedCat.value = null; | |||||
| countInCarton.value = ""; | |||||
| image.value = null; | |||||
| images.value = []; | |||||
| attrebutes.value.forEach((attribute) => { | |||||
| attribute.isChecked = false; | |||||
| attribute.value = ""; | |||||
| }); | |||||
| relatedAttrebutes.value.forEach((identity) => { | |||||
| identity.isChecked = false; | |||||
| }); | |||||
| }); | }); | ||||
| }; | }; | ||||
| @@ -970,7 +1013,6 @@ export default { | |||||
| slug, | slug, | ||||
| summary, | summary, | ||||
| editor, | editor, | ||||
| cats, | |||||
| errors, | errors, | ||||
| image, | image, | ||||
| imagePreview, | imagePreview, | ||||
| @@ -1001,13 +1043,18 @@ export default { | |||||
| spescialPrice, | spescialPrice, | ||||
| chosenPrice, | chosenPrice, | ||||
| expire, | expire, | ||||
| handleInput, | |||||
| countInCarton, | countInCarton, | ||||
| relatedAttrebutes, | relatedAttrebutes, | ||||
| selectedIdentities, | selectedIdentities, | ||||
| attributeValues, | attributeValues, | ||||
| handleAttributeUpdated, | handleAttributeUpdated, | ||||
| handleIdentityUpdate, | handleIdentityUpdate, | ||||
| handleBrandSearch, | |||||
| brandSelectorLoader, | |||||
| formattedBrands, | |||||
| handleSearch, | |||||
| formattedCategories, | |||||
| categorySelectorLoader, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -140,7 +140,7 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol md="6"> | |||||
| <BCol md="6" v-if="productType != 1"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">تعداد در کارتن</label> | <label class="form-label">تعداد در کارتن</label> | ||||
| <input | <input | ||||
| @@ -229,7 +229,7 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol v-if="spescial" md="6"> | |||||
| <BCol v-if="spescial == 1" md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label"> تخفیف ویژه </label> | <label class="form-label"> تخفیف ویژه </label> | ||||
| <input | <input | ||||
| @@ -246,7 +246,7 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol v-if="spescial" md="6"> | |||||
| <BCol v-if="spescial == 1" md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label"> | <label class="form-label"> | ||||
| تاریخ انقضای تخفیف محصول ویژه | تاریخ انقضای تخفیف محصول ویژه | ||||
| @@ -263,7 +263,7 @@ | |||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCol md="6"> | |||||
| <!-- <BCol md="6"> | |||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">دسته</label> | <label class="form-label">دسته</label> | ||||
| <select | <select | ||||
| @@ -281,43 +281,40 @@ | |||||
| <small v-if="errors.blogCat" class="text-danger"> | <small v-if="errors.blogCat" class="text-danger"> | ||||
| {{ errors.blogCat }} | {{ errors.blogCat }} | ||||
| </small> | </small> | ||||
| </BCol> --> | |||||
| <BCol md="6"> | |||||
| <div class="form-group"> | |||||
| <label class="form-label">دسته</label> | |||||
| <VueSelect | |||||
| :isLoading="categorySelectorLoader" | |||||
| v-model="selectedCat" | |||||
| :options="formattedCategories" | |||||
| placeholder="دسته ای را انتخاب کنید" | |||||
| /> | |||||
| </div> | |||||
| <small v-if="errors.selectedCat" class="text-danger"> | |||||
| {{ errors.selectedCat }} | |||||
| </small> | |||||
| </BCol> | </BCol> | ||||
| <BCol md="6"> | <BCol md="6"> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="form-label">برند</label> | <label class="form-label">برند</label> | ||||
| <select | |||||
| :class="{ 'is-invalid': errors.selectedBrand }" | |||||
| <VueSelect | |||||
| v-model="selectedBrand" | v-model="selectedBrand" | ||||
| class="form-control" | |||||
| @select="clearError('selectedBrand')" | |||||
| placeholder="انتخاب برند محصول" | |||||
| > | |||||
| <option | |||||
| v-for="brand in brands" | |||||
| :key="brand.id" | |||||
| :value="brand.id" | |||||
| > | |||||
| {{ brand.title }} | |||||
| </option> | |||||
| </select> | |||||
| :isLoading="brandSelectorLoader" | |||||
| :options="formattedBrands" | |||||
| placeholder="برندی را انتخاب کنید" | |||||
| @search="handleBrandSearch" | |||||
| /> | |||||
| </div> | </div> | ||||
| <small v-if="errors.selectedBrand" class="text-danger"> | <small v-if="errors.selectedBrand" class="text-danger"> | ||||
| {{ errors.selectedBrand }} | {{ errors.selectedBrand }} | ||||
| </small> | </small> | ||||
| </BCol> | </BCol> | ||||
| <BCard> | |||||
| <div class="card-header"> | |||||
| <h5 class="mb-0">ویرایش ویژگی ها</h5> | |||||
| </div> | |||||
| <template v-if="locals.length === 0"> | |||||
| <BCol> | |||||
| <div class="alert alert-info text-center"> | |||||
| هیچ ویژگی برای این محصول انتخاب نکرده اید ... | |||||
| </div> | |||||
| </BCol> | |||||
| </template> | |||||
| <BCard v-if="locals.length !== 0"> | |||||
| <BRow class="g-3 mt-2"> | <BRow class="g-3 mt-2"> | ||||
| <!-- Loop through attributes --> | <!-- Loop through attributes --> | ||||
| <BCol | <BCol | ||||
| @@ -410,18 +407,11 @@ | |||||
| </BRow> | </BRow> | ||||
| </BCard> | </BCard> | ||||
| <BCard> | |||||
| <BCard v-if="localIdentities.length !== 0"> | |||||
| <div class="card-header"> | <div class="card-header"> | ||||
| <h5 class="mb-0">ویرایش مشخصه ها</h5> | <h5 class="mb-0">ویرایش مشخصه ها</h5> | ||||
| </div> | </div> | ||||
| <template v-if="localIdentities.length === 0"> | |||||
| <BCol> | |||||
| <div class="alert alert-info text-center"> | |||||
| برای شما مشخصهای ثبت نشده است. برای ساختن یک مشخصه کلیک | |||||
| کنید. | |||||
| </div> | |||||
| </BCol> | |||||
| </template> | |||||
| <BRow class="g-3 mt-2"> | <BRow class="g-3 mt-2"> | ||||
| <!-- Loop through attributes --> | <!-- Loop through attributes --> | ||||
| <BCol | <BCol | ||||
| @@ -802,7 +792,7 @@ | |||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <addIdentity | <addIdentity | ||||
| :cats="cats" | |||||
| :cats="categories" | |||||
| @attribute-updated="handleAttributeUpdated()" | @attribute-updated="handleAttributeUpdated()" | ||||
| /> | /> | ||||
| <addAttribute @attribute-updated="handleAttributeUpdated()" /> | <addAttribute @attribute-updated="handleAttributeUpdated()" /> | ||||
| @@ -814,13 +804,14 @@ | |||||
| </template> | </template> | ||||
| <script> | <script> | ||||
| import VueSelect from "vue3-select-component"; | |||||
| import Swal from "sweetalert2"; | import Swal from "sweetalert2"; | ||||
| import { useRoute } from "vue-router"; | import { useRoute } from "vue-router"; | ||||
| import { toast } from "vue3-toastify"; | import { toast } from "vue3-toastify"; | ||||
| import "vue3-toastify/dist/index.css"; | import "vue3-toastify/dist/index.css"; | ||||
| import moment from "moment"; | |||||
| import moment from "jalali-moment"; | |||||
| import ApiServiece from "@/services/ApiService"; | import ApiServiece from "@/services/ApiService"; | ||||
| import { ref, onMounted, watch } from "vue"; | |||||
| import { ref, onMounted, watch, computed } from "vue"; | |||||
| import Layout from "@/layout/custom.vue"; | import Layout from "@/layout/custom.vue"; | ||||
| import addIdentity from "@/components/modals/identity/addIdentity.vue"; | import addIdentity from "@/components/modals/identity/addIdentity.vue"; | ||||
| import addAttribute from "@/components/modals/attribute/addAttribute.vue"; | import addAttribute from "@/components/modals/attribute/addAttribute.vue"; | ||||
| @@ -833,6 +824,7 @@ export default { | |||||
| DatePicker, | DatePicker, | ||||
| addIdentity, | addIdentity, | ||||
| addAttribute, | addAttribute, | ||||
| VueSelect, | |||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const locals = ref([ | const locals = ref([ | ||||
| @@ -885,19 +877,45 @@ export default { | |||||
| const blogCat = ref(); | const blogCat = ref(); | ||||
| const author = ref(""); | const author = ref(""); | ||||
| const editor = ref(null); | const editor = ref(null); | ||||
| const cats = ref([]); | |||||
| const categories = ref([]); | |||||
| const editorContent = ref(""); | const editorContent = ref(""); | ||||
| const categorySelectorLoader = ref(false); | |||||
| const brandSelectorLoader = ref(false); | |||||
| const getCats = () => { | const getCats = () => { | ||||
| ApiServiece.get(`admin/categories`) | |||||
| .then((resp) => { | |||||
| cats.value = resp.data.data; | |||||
| }) | |||||
| .catch((err) => { | |||||
| console.log(err); | |||||
| }); | |||||
| ApiServiece.get("admin/categories").then((resp) => { | |||||
| categories.value = resp.data.data; | |||||
| }); | |||||
| console.log(categories.value, "cats"); | |||||
| }; | }; | ||||
| const getBrands = () => { | |||||
| ApiServiece.get("admin/brands").then((resp) => { | |||||
| brands.value = resp.data.data; | |||||
| }); | |||||
| console.log(brands.value, "brands"); | |||||
| }; | |||||
| const formattedCategories = computed(() => | |||||
| Array.isArray(categories.value) | |||||
| ? categories.value.map((category) => ({ | |||||
| value: category.id, | |||||
| label: category.title, | |||||
| })) | |||||
| : [] | |||||
| ); | |||||
| const formattedBrands = computed(() => | |||||
| Array.isArray(brands.value) | |||||
| ? brands.value.map((brand) => ({ | |||||
| value: brand.id, | |||||
| label: brand.title, | |||||
| })) | |||||
| : [] | |||||
| ); | |||||
| watch(selectedCat, () => { | watch(selectedCat, () => { | ||||
| ApiServiece.get(`admin/attributes?category_id=${selectedCat.value}`) | ApiServiece.get(`admin/attributes?category_id=${selectedCat.value}`) | ||||
| .then((resp) => { | .then((resp) => { | ||||
| @@ -937,43 +955,17 @@ export default { | |||||
| }); | }); | ||||
| }; | }; | ||||
| const convertToGeorgian = (date) => { | |||||
| return moment(date, "jYYYY/jMM/jDD HH:mm:ss").format( | |||||
| "YYYY-MM-DD HH:mm:ss" | |||||
| ); | |||||
| }; | |||||
| watch(expire, (newValue) => { | |||||
| if (newValue) { | |||||
| // Convert from Jalali to Georgian (Gregorian) format when `expire` changes | |||||
| expire.value = convertToGeorgian(newValue); | |||||
| console.log(expire.value); // Logs the Georgian format for debugging | |||||
| } | |||||
| }); | |||||
| const getBrands = () => { | |||||
| ApiServiece.get(`admin/brands`) | |||||
| .then((resp) => { | |||||
| brands.value = resp.data.data; | |||||
| }) | |||||
| .catch((err) => { | |||||
| console.log(err); | |||||
| }); | |||||
| }; | |||||
| const getAttrebuteValues = () => { | const getAttrebuteValues = () => { | ||||
| ApiServiece.get(`admin/attribute-values?attribute_id=1`) | ApiServiece.get(`admin/attribute-values?attribute_id=1`) | ||||
| .then((resp) => { | .then((resp) => { | ||||
| console.log(resp); | |||||
| console.log(resp, "attrebute values"); | |||||
| attrebutes.value = resp.data.data; | attrebutes.value = resp.data.data; | ||||
| console.log("Attributes before filtering:", attrebutes.value); | console.log("Attributes before filtering:", attrebutes.value); | ||||
| }) | }) | ||||
| .then(() => { | .then(() => { | ||||
| getProduct(); | getProduct(); | ||||
| }) | }) | ||||
| .then(() => { | |||||
| console.log("Locals after processing:", locals.value); | |||||
| }) | |||||
| .catch((err) => { | .catch((err) => { | ||||
| console.log(err); | console.log(err); | ||||
| }); | }); | ||||
| @@ -1180,12 +1172,12 @@ export default { | |||||
| if (!selectedBrand.value) | if (!selectedBrand.value) | ||||
| errors.value.selectedBrand = "انتخاب برند برای محصول ضروری می باشد"; | errors.value.selectedBrand = "انتخاب برند برای محصول ضروری می باشد"; | ||||
| if (!countInCarton.value) | |||||
| if (productType.value != 1 && !countInCarton.value) | |||||
| errors.value.countInCarton = | errors.value.countInCarton = | ||||
| "انتخاب تعداد محصول در هر کارتن ضروری می باشد"; | "انتخاب تعداد محصول در هر کارتن ضروری می باشد"; | ||||
| if (images.value.length <= 0) | |||||
| errors.value.images = "انتخاب عکس برای محصول ضروری می باشد"; | |||||
| // if (images.value.length <= 0) | |||||
| // errors.value.images = "انتخاب عکس برای محصول ضروری می باشد"; | |||||
| const missingInventory = attrebutes.value.filter( | const missingInventory = attrebutes.value.filter( | ||||
| (attribute) => | (attribute) => | ||||
| @@ -1241,9 +1233,15 @@ export default { | |||||
| chosenPrice.value = product.value?.chosen_price; | chosenPrice.value = product.value?.chosen_price; | ||||
| spescial.value = product.value?.is_special; | spescial.value = product.value?.is_special; | ||||
| spescialPrice.value = product.value?.special_price; | spescialPrice.value = product.value?.special_price; | ||||
| expire.value = product.value?.special_expires_at; | |||||
| if (product.value?.special_expires_at) { | |||||
| expire.value = moment( | |||||
| product.value.special_expires_at, | |||||
| "YYYY-MM-DD HH:mm:ss" | |||||
| ).format("jYYYY/jMM/jDD HH:mm:ss"); | |||||
| } | |||||
| selectedBrand.value = product.value?.brand_id; | selectedBrand.value = product.value?.brand_id; | ||||
| selectedCat.value = product.value?.category_id; | selectedCat.value = product.value?.category_id; | ||||
| countInCarton.value = product.value?.count_in_carton; | countInCarton.value = product.value?.count_in_carton; | ||||
| // Update images | // Update images | ||||
| @@ -1255,8 +1253,9 @@ export default { | |||||
| preview: imageUrl, | preview: imageUrl, | ||||
| })); | })); | ||||
| locals.value = product.value.product_attributes.map( | |||||
| locals.value = product.value.attribute_value_products.map( | |||||
| (productAttribute) => { | (productAttribute) => { | ||||
| console.log(locals.value, "locals"); | |||||
| return { | return { | ||||
| id: productAttribute.id, | id: productAttribute.id, | ||||
| title: productAttribute.attribute_value.title, | title: productAttribute.attribute_value.title, | ||||
| @@ -1283,21 +1282,18 @@ export default { | |||||
| repeatedAttrebute.value = hasRepeatedAttribute; | repeatedAttrebute.value = hasRepeatedAttribute; | ||||
| console.log(attrebutes.value); | |||||
| console.log(attrebutes.value); | |||||
| localIdentities.value = product.value.product_solid_attributes.map( | |||||
| (productIdentities) => { | |||||
| return { | |||||
| id: productIdentities.id, | |||||
| title: productIdentities.attribute_value.attribute.title, | |||||
| isChecked: true, | |||||
| value: productIdentities.attribute_value.title, | |||||
| attribute_value_id: productIdentities.attribute_value_id, | |||||
| }; | |||||
| } | |||||
| ); | |||||
| localIdentities.value = | |||||
| product.value.solid_attribute_value_products.map( | |||||
| (productIdentities) => { | |||||
| return { | |||||
| id: productIdentities.id, | |||||
| title: productIdentities.attribute_value.attribute.title, | |||||
| isChecked: true, | |||||
| value: productIdentities.attribute_value.title, | |||||
| attribute_value_id: productIdentities.attribute_value_id, | |||||
| }; | |||||
| } | |||||
| ); | |||||
| }) | }) | ||||
| .then(() => { | .then(() => { | ||||
| getIdentities(); | getIdentities(); | ||||
| @@ -1376,14 +1372,12 @@ export default { | |||||
| }; | }; | ||||
| onMounted(() => { | onMounted(() => { | ||||
| getAttrebuteValues(); | |||||
| getCats(); | getCats(); | ||||
| getBrands(); | getBrands(); | ||||
| getAttrebuteValues(); | |||||
| }); | }); | ||||
| let id = ""; | let id = ""; | ||||
| const submitForm = () => { | const submitForm = () => { | ||||
| if (!validateForm()) { | if (!validateForm()) { | ||||
| toast.error("لطفا فیلد های لازم را وارد نمایید", { | toast.error("لطفا فیلد های لازم را وارد نمایید", { | ||||
| position: "top-right", | position: "top-right", | ||||
| @@ -1400,7 +1394,9 @@ export default { | |||||
| formData.append("slug", slug.value); | formData.append("slug", slug.value); | ||||
| formData.append("summary", summary.value); | formData.append("summary", summary.value); | ||||
| formData.append("description", description.value); | formData.append("description", description.value); | ||||
| formData.append("count_in_carton", countInCarton.value); | |||||
| if (productType.value === "2" || productType.value === "3") { | |||||
| formData.append("count_in_carton", countInCarton.value); | |||||
| } | |||||
| if (productType.value == 2) { | if (productType.value == 2) { | ||||
| formData.append("wholesale_price", wholesalePrice.value); | formData.append("wholesale_price", wholesalePrice.value); | ||||
| } | } | ||||
| @@ -1426,8 +1422,18 @@ export default { | |||||
| } | } | ||||
| if (spescial.value == 1 && isChosen.value == 0) { | if (spescial.value == 1 && isChosen.value == 0) { | ||||
| formData.append("special_price", parseInt(spescialPrice.value, 10)); | |||||
| formData.append("special_expires_at", expire.value); | |||||
| if (spescialPrice.value) { | |||||
| formData.append("special_price", parseInt(spescialPrice.value, 10)); | |||||
| } | |||||
| if (expire.value) { | |||||
| const georgianDate = moment( | |||||
| expire.value, | |||||
| "jYYYY/jMM/jDD HH:mm:ss" | |||||
| ).format("YYYY/MM/DD HH:mm:ss"); | |||||
| formData.append("special_expires_at", georgianDate); | |||||
| } | |||||
| } | } | ||||
| if (spescial.value == 1 && isChosen.value == 0) { | if (spescial.value == 1 && isChosen.value == 0) { | ||||
| @@ -1562,7 +1568,7 @@ export default { | |||||
| slug, | slug, | ||||
| summary, | summary, | ||||
| editor, | editor, | ||||
| cats, | |||||
| categories, | |||||
| errors, | errors, | ||||
| image, | image, | ||||
| imagePreview, | imagePreview, | ||||
| @@ -1607,6 +1613,10 @@ export default { | |||||
| editIdentity, | editIdentity, | ||||
| repeatedIdentity, | repeatedIdentity, | ||||
| repeatedAttrebute, | repeatedAttrebute, | ||||
| categorySelectorLoader, | |||||
| formattedCategories, | |||||
| formattedBrands, | |||||
| brandSelectorLoader, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -15,6 +15,8 @@ export default { | |||||
| VueSelect, | VueSelect, | ||||
| }, | }, | ||||
| setup() { | setup() { | ||||
| const brandSelectorLoader = ref(false); | |||||
| const categorySelectorLoader = ref(false); | |||||
| let searchTimeout = null; | let searchTimeout = null; | ||||
| const selectedProductType = ref(""); | const selectedProductType = ref(""); | ||||
| const categories = ref([]); | const categories = ref([]); | ||||
| @@ -208,15 +210,15 @@ export default { | |||||
| const handleSearch = async (searchTerm) => { | const handleSearch = async (searchTerm) => { | ||||
| if (searchTerm.length < 3) return; | if (searchTerm.length < 3) return; | ||||
| categorySelectorLoader.value = true; | |||||
| try { | try { | ||||
| const response = await ApiServiece.get( | const response = await ApiServiece.get( | ||||
| `admin/categories?title=${searchTerm}` | `admin/categories?title=${searchTerm}` | ||||
| ); | ); | ||||
| categories.value = response.data.data; | categories.value = response.data.data; | ||||
| console.log(categories.value, "products"); | |||||
| categorySelectorLoader.value = false; | |||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Error fetching products:", error); | |||||
| categorySelectorLoader.value = false; | |||||
| categories.value = []; | categories.value = []; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -232,14 +234,15 @@ export default { | |||||
| const handleBrandSearch = async (searchTerm) => { | const handleBrandSearch = async (searchTerm) => { | ||||
| if (searchTerm.length < 3) return; | if (searchTerm.length < 3) return; | ||||
| brandSelectorLoader.value = true; | |||||
| try { | try { | ||||
| const response = await ApiServiece.get( | const response = await ApiServiece.get( | ||||
| `admin/brands?title=${searchTerm}` | `admin/brands?title=${searchTerm}` | ||||
| ); | ); | ||||
| brands.value = response.data.data; | brands.value = response.data.data; | ||||
| brandSelectorLoader.value = false; | |||||
| } catch (error) { | } catch (error) { | ||||
| console.error("Error fetching products:", error); | |||||
| brandSelectorLoader.value = false; | |||||
| brands.value = []; | brands.value = []; | ||||
| } | } | ||||
| }; | }; | ||||
| @@ -279,6 +282,8 @@ export default { | |||||
| formattedBrands, | formattedBrands, | ||||
| handleBrandSearch, | handleBrandSearch, | ||||
| selectedBrand, | selectedBrand, | ||||
| categorySelectorLoader, | |||||
| brandSelectorLoader, | |||||
| }; | }; | ||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -317,6 +322,7 @@ export default { | |||||
| --vs-min-height: 18px; | --vs-min-height: 18px; | ||||
| margin-right: 7px; | margin-right: 7px; | ||||
| " | " | ||||
| :isLoading="categorySelectorLoader" | |||||
| v-model="selectedcategory" | v-model="selectedcategory" | ||||
| :options="formattedCategories" | :options="formattedCategories" | ||||
| placeholder="دسته ای را انتخاب کنید" | placeholder="دسته ای را انتخاب کنید" | ||||
| @@ -330,6 +336,7 @@ export default { | |||||
| margin-right: 7px; | margin-right: 7px; | ||||
| " | " | ||||
| v-model="selectedBrand" | v-model="selectedBrand" | ||||
| :isLoading="brandSelectorLoader" | |||||
| :options="formattedBrands" | :options="formattedBrands" | ||||
| placeholder="برندی را انتخاب کنید" | placeholder="برندی را انتخاب کنید" | ||||
| @search="handleBrandSearch" | @search="handleBrandSearch" | ||||
| @@ -638,8 +645,6 @@ export default { | |||||
| font-weight: bold; | font-weight: bold; | ||||
| } | } | ||||
| .Product-Image { | .Product-Image { | ||||
| width: 50px; | width: 50px; | ||||
| height: 50px; | height: 50px; | ||||
| @@ -681,8 +686,6 @@ export default { | |||||
| color: #fff; | color: #fff; | ||||
| } | } | ||||
| td, | td, | ||||
| table[dir="rtl"] td, | table[dir="rtl"] td, | ||||
| table[dir="rtl"] th { | table[dir="rtl"] th { | ||||