| @@ -36,7 +36,6 @@ export default { | |||
| event.preventDefault(); | |||
| }, | |||
| handleKeyDown(event) { | |||
| console.log('handleKeyDown called', event.key, event.ctrlKey); | |||
| // Check if Ctrl+K is pressed | |||
| if (event.ctrlKey && event.key === 'k') { | |||
| // Prevent the default action (e.g., browser search) | |||
| @@ -47,6 +47,7 @@ | |||
| <label class="form-label">تصویر دسته</label> | |||
| <input | |||
| ref="imageFileRef" | |||
| type="file" | |||
| accept="image/*" | |||
| @change="handleImageChange" | |||
| @@ -206,6 +207,7 @@ export default { | |||
| const title = ref(); | |||
| const errors = ref({}); | |||
| const loading = ref(false); | |||
| const imageFileRef = ref(null); | |||
| watch( | |||
| () => props.parents, | |||
| @@ -310,6 +312,7 @@ export default { | |||
| description.value = ""; | |||
| selectedPaernt.value = "" | |||
| selectedIcon.value = ""; | |||
| imageFileRef.value.value = null | |||
| }, 500); | |||
| }) | |||
| .catch((error) => { | |||
| @@ -340,6 +343,7 @@ export default { | |||
| setSelectedIcon, | |||
| iconData, | |||
| selectedIcon, | |||
| imageFileRef, | |||
| }; | |||
| }, | |||
| }; | |||
| @@ -46,6 +46,7 @@ | |||
| <label class="form-label">تصویر دسته</label> | |||
| <input | |||
| ref="imageFileRef" | |||
| type="file" | |||
| @input="clearError('localImage')" | |||
| accept="image/*" | |||
| @@ -232,6 +233,7 @@ export default { | |||
| const image = ref(null); | |||
| const localId = toRef(props.id); | |||
| const errors = ref({}); | |||
| const imageFileRef = ref(null); | |||
| const loading = ref(false); | |||
| @@ -382,7 +384,7 @@ export default { | |||
| localParent, | |||
| allLocalParents, | |||
| setSelectedIcon, | |||
| imageFileRef, | |||
| localIcon, | |||
| }; | |||
| }, | |||
| @@ -24,7 +24,7 @@ | |||
| <form @submit.prevent="addIdentity"> | |||
| <BRow class="g-3"> | |||
| <!-- Brand Title --> | |||
| <BCol lg="6"> | |||
| <BCol lg="12"> | |||
| <div class="form-group"> | |||
| <label class="form-label">عنوان مشخصه</label> | |||
| @@ -42,25 +42,25 @@ | |||
| </div> | |||
| </BCol> | |||
| <BCol lg="6"> | |||
| <div class="form-group"> | |||
| <label class="form-label">انتخاب دسته</label> | |||
| <div class="color-picker-wrapper"> | |||
| <VueSelect | |||
| style="--vs-min-height: 48px; --vs-border-radius: 8px" | |||
| :isLoading="categorySelectorLoader" | |||
| v-model="selectedCat" | |||
| :options="formattedCategories" | |||
| placeholder="دسته ای را انتخاب کنید" | |||
| @search="handleSearch" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.selectedCat" class="text-danger"> | |||
| {{ errors.selectedCat }} | |||
| </small> | |||
| </div> | |||
| </BCol> | |||
| <!-- <BCol lg="6">--> | |||
| <!-- <div class="form-group">--> | |||
| <!-- <label class="form-label">انتخاب دسته</label>--> | |||
| <!-- <div class="color-picker-wrapper">--> | |||
| <!-- <VueSelect--> | |||
| <!-- style="--vs-min-height: 48px; --vs-border-radius: 8px"--> | |||
| <!-- :isLoading="categorySelectorLoader"--> | |||
| <!-- v-model="selectedCat"--> | |||
| <!-- :options="formattedCategories"--> | |||
| <!-- placeholder="دسته ای را انتخاب کنید"--> | |||
| <!-- @search="handleSearch"--> | |||
| <!-- />--> | |||
| <!-- </div>--> | |||
| <!-- <small v-if="errors.selectedCat" class="text-danger">--> | |||
| <!-- {{ errors.selectedCat }}--> | |||
| <!-- </small>--> | |||
| <!-- </div>--> | |||
| <!-- </BCol>--> | |||
| </BRow> | |||
| <!-- Submit Buttons --> | |||
| @@ -95,16 +95,21 @@ | |||
| <script> | |||
| import { ref, computed } from "vue"; | |||
| import VueSelect from "vue3-select-component"; | |||
| import { toast } from "vue3-toastify"; | |||
| import "vue3-toastify/dist/index.css"; | |||
| import ApiServiece from "@/services/ApiService"; | |||
| export default { | |||
| components: { | |||
| VueSelect, | |||
| props: { | |||
| categoryId: { | |||
| type: Number, | |||
| required: true, | |||
| } | |||
| }, | |||
| setup(props, { emit }) { | |||
| const selectedCat = ref(); | |||
| const title = ref(); | |||
| const errors = ref({}); | |||
| @@ -159,8 +164,8 @@ export default { | |||
| loading.value = true; | |||
| const formData = new FormData(); | |||
| if (selectedCat.value) { | |||
| formData.append("category_id", selectedCat.value); | |||
| if (props.categoryId) { | |||
| formData.append("category_id", props.categoryId); | |||
| } | |||
| formData.append("title", title.value); | |||
| @@ -28,7 +28,6 @@ export default { | |||
| event.preventDefault(); | |||
| }, | |||
| handleKeyDown(event) { | |||
| console.log('handleKeyDown called', event.key, event.ctrlKey); | |||
| // Check if Ctrl+K is pressed | |||
| if (event.ctrlKey && event.key === 'k') { | |||
| // Prevent the default action (e.g., browser search) | |||
| @@ -31,7 +31,6 @@ export default { | |||
| event.preventDefault(); | |||
| }, | |||
| handleKeyDown(event) { | |||
| console.log("handleKeyDown called", event.key, event.ctrlKey); | |||
| // Check if Ctrl+K is pressed | |||
| if (event.ctrlKey && event.key === "k") { | |||
| // Prevent the default action (e.g., browser search) | |||
| @@ -17,7 +17,6 @@ export default { | |||
| const dir = localStorage.getItem("dir"); | |||
| const validDir = dir === "ltr" || dir === "rtl" ? dir : "rtl"; | |||
| document.body.setAttribute("data-pc-direction", validDir); | |||
| console.log(validDir); | |||
| }, | |||
| }; | |||
| </script> | |||
| @@ -1,7 +1,7 @@ | |||
| import { createWebHistory, createRouter } from "vue-router"; | |||
| import routes from "./routes"; | |||
| import appConfig from "../../app.config"; | |||
| import store from "../state/store"; | |||
| //import store from "../state/store"; | |||
| const router = createRouter({ | |||
| history: createWebHistory("/"), | |||
| @@ -11,11 +11,11 @@ const router = createRouter({ | |||
| router.beforeResolve(async (routeTo, routeFrom, next) => { | |||
| try { | |||
| if (routeTo.matched.some((record) => record.meta.requiresAuth)) { | |||
| await store.dispatch("user/verifyLogin"); | |||
| //await store.dispatch("user/verifyLogin"); | |||
| if (!store.getters["user/isAuthenticated"]) { | |||
| return next({ name: "otpLogin" }); | |||
| } | |||
| // if (!store.getters["user/isAuthenticated"]) { | |||
| // return next({ name: "otpLogin" }); | |||
| // } | |||
| } | |||
| // For each matched route... | |||
| @@ -14,16 +14,6 @@ export default [ | |||
| component: () => | |||
| import("../views/live-preview/pages/auth1/forgot-password.vue"), | |||
| }, | |||
| { | |||
| path: "/dashPage", | |||
| name: "dashPage", | |||
| meta: { | |||
| requiresAuth: true, | |||
| title: "داشبورد", | |||
| }, | |||
| component: () => | |||
| import("../views/live-preview/pages/dashboard/dashPage.vue"), | |||
| }, | |||
| { | |||
| path: "/users", | |||
| name: "users", | |||
| @@ -1,6 +1,8 @@ | |||
| import axios from "axios"; | |||
| import router from "@/router/index"; | |||
| const url = process.env.VUE_APP_ROOT_URL; | |||
| const ApiServiece = axios.create({ | |||
| baseURL: url, | |||
| headers: { | |||
| @@ -27,7 +29,9 @@ ApiServiece.interceptors.response.use( | |||
| return response; | |||
| }, | |||
| (error) => { | |||
| console.error("API Error:", error); | |||
| if (error?.status === 401) { | |||
| router.push({ name: "otpLogin" }); | |||
| } | |||
| return Promise.reject(error); | |||
| } | |||
| ); | |||
| @@ -619,7 +619,7 @@ export default { | |||
| } | |||
| formData.append("panel", pannel.value); | |||
| if (pannel.value == "wholesale") { | |||
| formData.append("page_type", null); | |||
| formData.append("page_type", "main_page"); | |||
| } | |||
| if (pannel.value !== "wholesale") { | |||
| @@ -198,6 +198,31 @@ export default { | |||
| }); | |||
| }; | |||
| const setPageType = (banner)=> { | |||
| switch (banner.page_type) { | |||
| case 'category': | |||
| return 'دسته ' + banner?.category_page?.title; | |||
| case 'brand': | |||
| return 'برند ' + banner?.brand_page?.title | |||
| case 'main_page': | |||
| return 'صفحه اصلی' | |||
| case 'special_page': | |||
| return 'شگفت انگیزها' | |||
| case 'blog_page': | |||
| return 'مقالات' | |||
| } | |||
| } | |||
| const setProductOrCategory = (banner) => { | |||
| if (banner?.category_id) { | |||
| return `دسته<br>${banner?.category?.title}`; | |||
| } | |||
| if (banner?.product_id) { | |||
| return `محصول<br>${banner?.product?.title}`; | |||
| } | |||
| return ""; | |||
| }; | |||
| onMounted(() => { | |||
| getBanners(); | |||
| }); | |||
| @@ -224,6 +249,8 @@ export default { | |||
| searchPage, | |||
| visiblePages, | |||
| selectedPageType, | |||
| setPageType, | |||
| setProductOrCategory, | |||
| }; | |||
| }, | |||
| }; | |||
| @@ -282,6 +309,9 @@ export default { | |||
| <th>موقعیت</th> | |||
| <th>نوع</th> | |||
| <th>تاریخ ایجاد</th> | |||
| <th>پنل</th> | |||
| <th>نمایش در</th> | |||
| <th>صفحه فرود</th> | |||
| <th>عملیات</th> | |||
| </tr> | |||
| </thead> | |||
| @@ -328,6 +358,11 @@ export default { | |||
| <td v-if="banner.type === 'slider'">اسلایدر</td> | |||
| <td v-if="banner.type === 'banner'">بنر</td> | |||
| <td>{{ convertToJalali(banner.created_at) }}</td> | |||
| <td>{{ banner?.panel === 'wholesale' ? 'عمده': 'وب و اپلیکیشن' }}</td> | |||
| <td>{{ setPageType(banner) }}</td> | |||
| <td> | |||
| <div v-html="setProductOrCategory(banner)"></div> | |||
| </td> | |||
| <td> | |||
| <router-link | |||
| :to="`/editBanner/${banner?.id}`" | |||
| @@ -69,13 +69,8 @@ | |||
| class="form-control" | |||
| type="number" | |||
| placeholder="میزان سفارش" | |||
| :class="{ 'is-invalid': errors.minOrder }" | |||
| @input="clearError('minOrder')" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.minOrder" class="text-danger"> | |||
| {{ errors.minOrder }} | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| @@ -86,13 +81,8 @@ | |||
| type="number" | |||
| class="form-control" | |||
| placeholder="حداکثر میزان استفاده" | |||
| :class="{ 'is-invalid': errors.maxUsage }" | |||
| @input="clearError('maxUsage')" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.maxUsage" class="text-danger"> | |||
| {{ errors.maxUsage }} | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| @@ -101,8 +91,6 @@ | |||
| <select | |||
| v-model="whichPart" | |||
| class="form-control" | |||
| :class="{ 'is-invalid': errors.whichPart }" | |||
| @change="clearError('whichPart')" | |||
| placeholder="انتخاب محل اعمال تخفبف" | |||
| > | |||
| <option value="cat">دسته</option> | |||
| @@ -110,9 +98,6 @@ | |||
| <option value="all">همه</option> | |||
| </select> | |||
| </div> | |||
| <small v-if="errors.whichPart" class="text-danger"> | |||
| {{ errors.whichPart }} | |||
| </small> | |||
| </BCol> | |||
| <BCol v-if="whichPart === 'cat'" md="6"> | |||
| @@ -161,31 +146,25 @@ | |||
| <BCol md="6"> | |||
| <div class="form-group"> | |||
| <label class="form-label"> تاریخ اعمال تخفیف </label> | |||
| <DatePicker | |||
| :format="'jYYYY/jMM/jDD HH:mm:ss'" | |||
| type="datetime" | |||
| v-model="startDate" | |||
| clearable | |||
| ></DatePicker> | |||
| </div> | |||
| <small v-if="errors.startDate" class="text-danger"> | |||
| {{ errors.startDate }} | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| <div class="form-group"> | |||
| <label class="form-label"> تاریخ انقضای تخفیف </label> | |||
| <DatePicker | |||
| :format="'jYYYY/jMM/jDD HH:mm:ss'" | |||
| type="datetime" | |||
| v-model="expire" | |||
| clearable | |||
| ></DatePicker> | |||
| </div> | |||
| <small v-if="errors.expire" class="text-danger"> | |||
| {{ errors.expire }} | |||
| </small> | |||
| </BCol> | |||
| </BRow> | |||
| </BCardBody> | |||
| @@ -235,12 +214,12 @@ export default { | |||
| const title = ref(); | |||
| const discountType = ref(); | |||
| const amount = ref(); | |||
| const minOrder = ref(); | |||
| const minOrder = ref(null); | |||
| const selectedCat = ref(); | |||
| const selectedProduct = ref(); | |||
| const startDate = ref(); | |||
| const expire = ref(); | |||
| const maxUsage = ref(); | |||
| const maxUsage = ref(null); | |||
| const whichPart = ref(); | |||
| const loading = ref(false); | |||
| const categories = ref([]); | |||
| @@ -304,14 +283,14 @@ export default { | |||
| if (!amount.value) | |||
| errors.value.amount = "وارد کردن مقدار تخفیف الزامی می باشد"; | |||
| if (!selectedCat.value && whichPart.value === "cat") | |||
| errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | |||
| if (!selectedProduct.value && whichPart.value === "product") | |||
| errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | |||
| if (!whichPart.value) | |||
| errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | |||
| // if (!selectedCat.value && whichPart.value === "cat") | |||
| // errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | |||
| // if (!selectedProduct.value && whichPart.value === "product") | |||
| // errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | |||
| // | |||
| // | |||
| // if (!whichPart.value) | |||
| // errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | |||
| return Object.keys(errors.value).length === 0; | |||
| }; | |||
| @@ -333,7 +312,8 @@ export default { | |||
| formData.append("title", title.value); | |||
| formData.append("type", discountType.value); | |||
| formData.append("amount", amount.value); | |||
| formData.append("min_order", minOrder.value); | |||
| if(minOrder.value) | |||
| formData.append("min_order", minOrder.value); | |||
| if (whichPart.value === "cat") { | |||
| formData.append("category_id", selectedCat.value); | |||
| @@ -359,16 +339,16 @@ export default { | |||
| formData.append("expires_at", georgianDate); | |||
| } | |||
| formData.append("max_usage", maxUsage.value); | |||
| if (maxUsage.value) | |||
| formData.append("max_usage", maxUsage.value); | |||
| ApiServiece.post(`/admin/discounts`, formData) | |||
| .then((resp) => { | |||
| .then(() => { | |||
| loading.value = false; | |||
| toast.success("!تخفیف با موفقیت اضافه شد", { | |||
| position: "top-right", | |||
| autoClose: 1000, | |||
| }); | |||
| console.log(resp); | |||
| // Clear form fields after successful submission | |||
| title.value = ""; | |||
| @@ -36,9 +36,11 @@ export default { | |||
| const discounts = ref(); | |||
| const convertToJalali = (date) => { | |||
| if (!date) return | |||
| return moment(date, "YYYY-MM-DD HH:mm:ss") | |||
| .locale("fa") | |||
| .format("YYYY/MM/DD"); | |||
| .locale("fa") | |||
| .format("YYYY/MM/DD"); | |||
| }; | |||
| const handleSearchChange = () => { | |||
| @@ -67,7 +69,6 @@ export default { | |||
| .then((resp) => { | |||
| filterLoading.value = false; | |||
| discounts.value = resp.data.data.data; | |||
| console.log(resp.data.data); | |||
| currentPage.value = resp.data.data.current_page; | |||
| totalPages.value = resp.data.data.last_page; | |||
| }) | |||
| @@ -70,13 +70,8 @@ | |||
| class="form-control" | |||
| type="number" | |||
| placeholder="میزان سفارش" | |||
| :class="{ 'is-invalid': errors.minOrder }" | |||
| @input="clearError('minOrder')" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.minOrder" class="text-danger"> | |||
| {{ errors.minOrder }} | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| @@ -87,13 +82,8 @@ | |||
| type="number" | |||
| class="form-control" | |||
| placeholder="حداکثر میزان استفاده" | |||
| :class="{ 'is-invalid': errors.maxUsage }" | |||
| @input="clearError('maxUsage')" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.maxUsage" class="text-danger"> | |||
| {{ errors.maxUsage }} | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| @@ -102,8 +92,6 @@ | |||
| <select | |||
| v-model="whichPart" | |||
| class="form-control" | |||
| :class="{ 'is-invalid': errors.whichPart }" | |||
| @change="clearError('whichPart')" | |||
| placeholder="انتخاب محل اعمل تخفیف" | |||
| > | |||
| <option value="cat">دسته</option> | |||
| @@ -111,9 +99,6 @@ | |||
| <option value="all">همه</option> | |||
| </select> | |||
| </div> | |||
| <small v-if="errors.discountType" class="text-danger"> | |||
| {{ errors.discountType }} | |||
| </small> | |||
| </BCol> | |||
| <BCol v-if="whichPart === 'cat'" md="6"> | |||
| @@ -125,14 +110,10 @@ | |||
| :options="formattedCategories" | |||
| label="label" | |||
| :reduce="(option) => option.value" | |||
| @change="clearError('selectedCat')" | |||
| placeholder="دسته ای را انتخاب کنید" | |||
| @search="handleSearch" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.selectedCat" class="text-danger"> | |||
| {{ errors.selectedCat }} | |||
| </small> | |||
| </BCol> | |||
| <BCol v-if="whichPart === 'product'" md="6"> | |||
| @@ -144,13 +125,9 @@ | |||
| v-model="selectedProduct" | |||
| :options="formattedProducts" | |||
| @search="handleProductSearch" | |||
| @change="clearError('selectedProduct')" | |||
| placeholder="محصولی را انتخاب کنید " | |||
| /> | |||
| </div> | |||
| <small v-if="errors.selectedProduct" class="text-danger"> | |||
| {{ errors.selectedProduct }} | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| @@ -160,13 +137,11 @@ | |||
| <DatePicker | |||
| :format="'jYYYY/jMM/jDD HH:mm:ss'" | |||
| type="datetime" | |||
| clearable | |||
| v-model="startDate" | |||
| @input="handleStartDateInput" | |||
| ></DatePicker> | |||
| </div> | |||
| <small v-if="errors.startDate" class="text-danger"> | |||
| {{ errors.startDate }} | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| @@ -176,13 +151,11 @@ | |||
| <DatePicker | |||
| :format="'jYYYY/jMM/jDD HH:mm:ss'" | |||
| type="datetime" | |||
| clearable | |||
| v-model="expire" | |||
| @input="handleExpireDateInput" | |||
| ></DatePicker> | |||
| </div> | |||
| <small v-if="errors.expire" class="text-danger"> | |||
| {{ errors.expire }} | |||
| </small> | |||
| </BCol> | |||
| </BRow> | |||
| </BCardBody> | |||
| @@ -373,15 +346,15 @@ export default { | |||
| if (!amount.value) | |||
| errors.value.amount = "وارد کردن مقدار تخفیف الزامی می باشد"; | |||
| if (!selectedCat.value && whichPart.value === "cat") | |||
| errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | |||
| if (!selectedProduct.value && whichPart.value === "product") | |||
| errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | |||
| if (!startDate.value) | |||
| errors.value.startDate = "انتخاب تاریخ اعمال تخفیف الزامی می باشد "; | |||
| if (!whichPart.value) | |||
| errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | |||
| // if (!selectedCat.value && whichPart.value === "cat") | |||
| // errors.value.selectedCat = "انتخاب دسته برای تخفیف الزامی می باشد"; | |||
| // if (!selectedProduct.value && whichPart.value === "product") | |||
| // errors.value.selectedProduct = "انتخاب محصول برای تخفیف الزامی می باشد"; | |||
| // if (!startDate.value) | |||
| // errors.value.startDate = "انتخاب تاریخ اعمال تخفیف الزامی می باشد "; | |||
| // | |||
| // if (!whichPart.value) | |||
| // errors.value.whichPart = "مشخص کنید تخفیف بر چه بخشی اعمال شود"; | |||
| return Object.keys(errors.value).length === 0; | |||
| }; | |||
| @@ -406,7 +379,9 @@ export default { | |||
| formData.append("title", title.value); | |||
| formData.append("type", discountType.value); | |||
| formData.append("amount", amount.value); | |||
| formData.append("min_order", minOrder.value); | |||
| if (minOrder.value) { | |||
| formData.append("min_order", minOrder.value); | |||
| } | |||
| if (whichPart.value === "cat") { | |||
| formData.append("category_id", selectedCat.value); | |||
| } | |||
| @@ -431,7 +406,8 @@ export default { | |||
| formData.append("expires_at", georgianDate); | |||
| } | |||
| formData.append("max_usage", maxUsage.value); | |||
| if (maxUsage.value) | |||
| formData.append("max_usage", maxUsage.value); | |||
| ApiServiece.post(`/admin/discounts`, formData) | |||
| .then((resp) => { | |||
| @@ -357,15 +357,13 @@ export default { | |||
| <thead class="table-light"> | |||
| <tr> | |||
| <th class="text-start">جزییات محصول</th> | |||
| <th>توضیحات</th> | |||
| <th>رنگ محصول</th> | |||
| <th>وضعیت</th> | |||
| <th>تاریخ ایجاد</th> | |||
| <th>قیمت عمده</th> | |||
| <th>قیمت تک</th> | |||
| <th>تعداد سفارش</th> | |||
| <th>تعداد ارسال شده</th> | |||
| <th>عنوان برند</th> | |||
| <th>تصویر برند</th> | |||
| <th>عملیات</th> | |||
| </tr> | |||
| </thead> | |||
| @@ -394,21 +392,9 @@ export default { | |||
| </div> | |||
| </td> | |||
| <!-- Description --> | |||
| <!-- Product color --> | |||
| <td> | |||
| <div | |||
| type="button" | |||
| data-bs-target="#showDescription" | |||
| data-bs-toggle="modal" | |||
| @click="descriptionModal(order?.description)" | |||
| class="subject-box" | |||
| > | |||
| <i class="fas fa-comments subject-icon"></i> | |||
| <span class="subject-text"> | |||
| {{ order?.description?.slice(0, 20) | |||
| }}{{ order?.description?.length > 20 ? "..." : "" }} | |||
| </span> | |||
| </div> | |||
| {{ order?.attribute_value_product?.attribute_value?.title }} | |||
| </td> | |||
| <!-- Status --> | |||
| @@ -452,19 +438,6 @@ export default { | |||
| <!-- Sent Count --> | |||
| <td>{{ order.send_count }}</td> | |||
| <!-- Brand Title --> | |||
| <td>{{ order.product?.brand?.title }}</td> | |||
| <!-- Brand Image --> | |||
| <td> | |||
| <img | |||
| :src="order.product?.brand?.image" | |||
| alt="brand" | |||
| class="rounded" | |||
| style="width: 40px; height: 40px; object-fit: cover" | |||
| /> | |||
| </td> | |||
| <!-- Operations --> | |||
| <td> | |||
| <router-link | |||
| @@ -490,33 +463,9 @@ export default { | |||
| <li> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'waiting')" | |||
| > | |||
| <span class="badge badge-waiting">در انتظار</span> | |||
| </a> | |||
| </li> | |||
| <li> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'paid')" | |||
| > | |||
| <span class="badge badge-paid">پرداختشده</span> | |||
| </a> | |||
| </li> | |||
| <li> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'un_paid')" | |||
| > | |||
| <span class="badge badge-un_paid">پرداختنشده</span> | |||
| </a> | |||
| </li> | |||
| <li> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'approved')" | |||
| @click="changeStatus(order?.id, 'done')" | |||
| > | |||
| <span class="badge badge-approved">تأییدشده</span> | |||
| <span class="badge badge-in-done">کامل شده</span> | |||
| </a> | |||
| </li> | |||
| <li> | |||
| @@ -529,32 +478,6 @@ export default { | |||
| > | |||
| </a> | |||
| </li> | |||
| <li> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'shipping')" | |||
| > | |||
| <span class="badge badge-shipping" | |||
| >در حال ارسال</span | |||
| > | |||
| </a> | |||
| </li> | |||
| <li> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'delivered')" | |||
| > | |||
| <span class="badge badge-delivered">تحویلشده</span> | |||
| </a> | |||
| </li> | |||
| <li> | |||
| <a | |||
| class="dropdown-item d-flex justify-content-center align-items-center" | |||
| @click="changeStatus(order?.id, 'canceled')" | |||
| > | |||
| <span class="badge badge-canceled">لغوشده</span> | |||
| </a> | |||
| </li> | |||
| </ul> | |||
| </td> | |||
| </tr> | |||
| @@ -24,9 +24,11 @@ export default { | |||
| const orders = ref(); | |||
| const convertToJalali = (date) => { | |||
| if (!date) return | |||
| return moment(date, "YYYY-MM-DD HH:mm:ss") | |||
| .locale("fa") | |||
| .format("YYYY/MM/DD"); | |||
| .format("HH:mm:ss YYYY/MM/DD "); | |||
| }; | |||
| const loadingId = ref(null); | |||
| @@ -34,8 +36,12 @@ export default { | |||
| const getExport = (id) => { | |||
| loadingId.value = id; | |||
| ApiServiece.post(`admin/orders/${id}/export`, { responseType: "blob" }) | |||
| .then((resp) => { | |||
| //ApiServiece.post(`admin/orders/${id}/export`, {}, { responseType: "blob" }) | |||
| ApiServiece({ | |||
| method: 'POST', | |||
| url: `admin/orders/${id}/export`, | |||
| config: { responseType: "blob" } | |||
| }).then((resp) => { | |||
| const excelBlob = resp.data; | |||
| const url = window.URL.createObjectURL(excelBlob); | |||
| @@ -70,7 +76,6 @@ export default { | |||
| .then((resp) => { | |||
| filterLoading.value = false; | |||
| orders.value = resp.data.data.data; | |||
| console.log(orders.value); | |||
| currentPage.value = resp.data.data.current_page; | |||
| totalPages.value = resp.data.data.last_page; | |||
| }) | |||
| @@ -104,7 +104,9 @@ export default { | |||
| }; | |||
| const formatPrice = (price) => { | |||
| return numeral(price).format("0,0"); | |||
| if (!price) return 0 | |||
| return numeral(price).format("0,0") | |||
| }; | |||
| onMounted(() => { | |||
| @@ -142,6 +144,11 @@ export default { | |||
| <strong class="me-2">نام مشتری:</strong> | |||
| <span>{{ order?.user?.name || "بدون نام" }}</span> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| <strong class="me-2">موبایل:</strong> | |||
| <span>{{ order?.user?.mobile }}</span> | |||
| </BCol> | |||
| </BRow> | |||
| <BRow class="mb-3"> | |||
| @@ -161,27 +168,37 @@ export default { | |||
| <span> | |||
| {{ | |||
| order?.shipping_price != null | |||
| ? formatPrice(order.shipping_price) + " تومان" | |||
| ? formatPrice(order?.shipping_price) + ' تومان' | |||
| : "بدون قیمت" | |||
| }} | |||
| </span> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| <strong class="me-2">تخفیف:</strong> | |||
| <span>{{ | |||
| order?.discount | |||
| ? formatPrice(order.discount) + " تومان" | |||
| : "بدون تخفیف" | |||
| }}</span> | |||
| <span>{{ formatPrice(order?.discount?.amount) + (order?.discount?.type === 'percentage' ? ' درصد' : 'تومان') }}</span> | |||
| </BCol> | |||
| </BRow> | |||
| <BRow> | |||
| <BRow class="mb-3"> | |||
| <BCol md="6"> | |||
| <strong class="me-2">کد تخفیف:</strong> | |||
| <span>{{ order?.discount?.title ?? '-' }}</span> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| <strong class="me-2">رنگ سفارش:</strong> | |||
| <span>{{ order.color || "---" }}</span> | |||
| <span>{{ order?.status || "-" }}</span> | |||
| </BCol> | |||
| </BRow> | |||
| <BRow class="mb-3"> | |||
| <BCol md="6"> | |||
| <strong class="me-2">قیمت کل:</strong> | |||
| <span>{{ order?.total_price ? order?.total_price + 'تومان' : '0' }}</span> | |||
| </BCol> | |||
| </BRow> | |||
| </div> | |||
| <!-- Order Details and Items Here --> | |||
| @@ -195,6 +212,7 @@ export default { | |||
| :items="order.order_items" | |||
| :fields="[ | |||
| { key: 'title', label: 'عنوان محصول' }, | |||
| { key: 'color', label: 'رنگ محصول' }, | |||
| { key: 'count', label: 'تعداد در خواستی ' }, | |||
| { key: 'price', label: 'قیمت' }, | |||
| { key: 'created_at', label: 'تاریخ ثبت' }, | |||
| @@ -222,6 +240,10 @@ export default { | |||
| {{ data.item.count }} | |||
| </template> | |||
| <template #cell(color)="data"> | |||
| {{ data?.item?.attribute_value_product?.attribute_value?.title }} | |||
| </template> | |||
| <!-- Editable shipped quantity with API call --> | |||
| <template #cell(edit_count)="data"> | |||
| <input | |||
| @@ -61,14 +61,16 @@ | |||
| <BCol md="6"> | |||
| <div class="form-group"> | |||
| <label class="form-label">توضیحات محصول</label> | |||
| <div | |||
| @input="clearError('editorContent')" | |||
| ref="editor" | |||
| class="quill-editor" | |||
| ></div> | |||
| <textarea | |||
| v-model="description" | |||
| class="form-control" | |||
| placeholder="توضیحات محصول" | |||
| :class="{ 'is-invalid': errors.description }" | |||
| @input="clearError('description')" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.editorContent" class="text-danger"> | |||
| {{ errors.editorContent }} | |||
| <small v-if="errors.description" class="text-danger"> | |||
| {{ errors.description }} | |||
| </small> | |||
| </BCol> | |||
| @@ -77,6 +79,7 @@ | |||
| <label class="form-label">تصویر محصول</label> | |||
| <input | |||
| ref="productImageRef" | |||
| type="file" | |||
| accept="image/*" | |||
| @change="handleImageUpload" | |||
| @@ -395,8 +398,18 @@ | |||
| </BCard> | |||
| <BCard> | |||
| <div class="card-header"> | |||
| <div class="card-header d-flex justify-content-between"> | |||
| <h5 class="mb-0">اضافه کردن مشخصه ها</h5> | |||
| <BButton | |||
| v-if="relatedAttrebutes?.length" | |||
| variant="success" | |||
| size="lg" | |||
| class="mt-1 px-4 py-2 shadow-lg rounded-pill" | |||
| @click="handleAddIdentityClick" | |||
| > | |||
| <i class="fas fa-plus me-2"></i> کلیک کنید | |||
| </BButton> | |||
| </div> | |||
| <BRow class="g-3 mt-2"> | |||
| @@ -409,8 +422,7 @@ | |||
| variant="success" | |||
| size="lg" | |||
| class="mt-1 px-4 py-2 shadow-lg rounded-pill" | |||
| data-bs-toggle="modal" | |||
| data-bs-target="#addIdentity" | |||
| @click="handleAddIdentityClick" | |||
| > | |||
| <i class="fas fa-plus me-2"></i> کلیک کنید | |||
| </BButton> | |||
| @@ -554,7 +566,7 @@ | |||
| </div> | |||
| </div> | |||
| </BCardFooter> | |||
| <addIdentity @identity-updated="handleIdentityUpdate" /> | |||
| <addIdentity @identity-updated="handleIdentityUpdate" :categoryId="selectedCat" /> | |||
| <addAttribute | |||
| :attributeValues="attributeValues" | |||
| @attribute-updated="handleAttributeUpdated()" | |||
| @@ -572,12 +584,12 @@ import moment from "jalali-moment"; | |||
| import { toast } from "vue3-toastify"; | |||
| import "vue3-toastify/dist/index.css"; | |||
| import ApiServiece from "@/services/ApiService"; | |||
| import { ref, onMounted, watch, computed, nextTick } from "vue"; | |||
| import { ref, onMounted, watch, computed } from "vue"; | |||
| import Layout from "@/layout/custom.vue"; | |||
| import DatePicker from "vue3-persian-datetime-picker"; | |||
| import addIdentity from "@/components/modals/identity/addIdentity.vue"; | |||
| import Quill from "quill"; | |||
| import "quill/dist/quill.snow.css"; | |||
| import { Modal } from "bootstrap" | |||
| export default { | |||
| name: "SAMPLE-PAGE", | |||
| @@ -625,6 +637,10 @@ export default { | |||
| const editor = ref(null); | |||
| const editorContent = ref(""); | |||
| // ALIREZA | |||
| const productImageRef = ref(null) | |||
| const description = ref(null) | |||
| const getAttributeValues = () => { | |||
| ApiServiece.get(`admin/attributes`).then((resp) => { | |||
| console.log(resp); | |||
| @@ -636,21 +652,22 @@ export default { | |||
| getAttrebuteValues(); | |||
| }; | |||
| watch(selectedCat, () => { | |||
| ApiServiece.get(`admin/attributes?category_id=${selectedCat.value}`) | |||
| .then((resp) => { | |||
| relatedAttrebutes.value.push(...resp.data.data); | |||
| }) | |||
| .catch((err) => { | |||
| console.log(err); | |||
| }); | |||
| watch(selectedCat, (value) => { | |||
| if (value){ | |||
| ApiServiece.get(`admin/attributes?category_id=${selectedCat.value}`) | |||
| .then((resp) => { | |||
| relatedAttrebutes.value = resp?.data?.data | |||
| }) | |||
| .catch((err) => { | |||
| console.log(err); | |||
| }); | |||
| } | |||
| }); | |||
| const handleIdentityUpdate = () => { | |||
| ApiServiece.get(`admin/attributes?category_id=${selectedCat.value}`) | |||
| .then((resp) => { | |||
| relatedAttrebutes.value = resp.data.data; | |||
| console.log("sadsadsaasdasddsad"); | |||
| }) | |||
| .catch((err) => { | |||
| console.log(err); | |||
| @@ -779,8 +796,8 @@ export default { | |||
| if (!image.value) | |||
| errors.value.image = "وارد کردن عکس محصول ضروری می باشد"; | |||
| if (!editorContent.value) | |||
| errors.value.editorContent = "وارد کردن توضیحات محصول ضروری می باشد"; | |||
| if (!description.value) | |||
| errors.value.description = "وارد کردن توضیحات محصول ضروری می باشد"; | |||
| if (!productType.value) | |||
| errors.value.productType = "انتخاب حالت محصول ضروری می باشد"; | |||
| @@ -844,37 +861,51 @@ export default { | |||
| errors.value[field] = ""; | |||
| }; | |||
| onMounted(() => { | |||
| const quill = new Quill(editor.value, { | |||
| theme: "snow", | |||
| modules: { | |||
| toolbar: [ | |||
| [{ header: "1" }, { header: "2" }, { font: [] }], | |||
| [{ list: "ordered" }, { list: "bullet" }], | |||
| [{ align: [] }], | |||
| ["bold", "italic", "underline"], | |||
| ["link", "image"], | |||
| [{ script: "sub" }, { script: "super" }], | |||
| [{ direction: "rtl" }], | |||
| ], | |||
| }, | |||
| }); | |||
| const handleAddIdentityClick = () => { | |||
| if (!selectedCat.value) { | |||
| toast.error("لطفا ابتدا یک دستهبندی انتخاب کنید", { | |||
| position: "top-right", | |||
| autoClose: 3000 | |||
| }); | |||
| return; | |||
| } | |||
| quill.root.setAttribute("dir", "rtl"); | |||
| quill.format("direction", "rtl"); | |||
| const modal = new Modal(document.getElementById('addIdentity')); | |||
| nextTick(() => { | |||
| const rtlButton = quill.container.querySelector( | |||
| ".ql-direction[data-value='rtl']" | |||
| ); | |||
| if (rtlButton) { | |||
| rtlButton.click(); | |||
| } | |||
| }); | |||
| modal?.show(); | |||
| }; | |||
| quill.on("text-change", () => { | |||
| editorContent.value = quill.root.innerHTML; | |||
| }); | |||
| onMounted(() => { | |||
| // const quill = new Quill(editor.value, { | |||
| // theme: "snow", | |||
| // modules: { | |||
| // toolbar: [ | |||
| // [{ header: "1" }, { header: "2" }, { font: [] }], | |||
| // [{ list: "ordered" }, { list: "bullet" }], | |||
| // [{ align: [] }], | |||
| // ["bold", "italic", "underline"], | |||
| // ["link", "image"], | |||
| // [{ script: "sub" }, { script: "super" }], | |||
| // [{ direction: "rtl" }], | |||
| // ], | |||
| // }, | |||
| // }); | |||
| // | |||
| // quill.root.setAttribute("dir", "rtl"); | |||
| // quill.format("direction", "rtl"); | |||
| // | |||
| // nextTick(() => { | |||
| // const rtlButton = quill.container.querySelector( | |||
| // ".ql-direction[data-value='rtl']" | |||
| // ); | |||
| // if (rtlButton) { | |||
| // rtlButton.click(); | |||
| // } | |||
| // }); | |||
| // | |||
| // quill.on("text-change", () => { | |||
| // editorContent.value = quill.root.innerHTML; | |||
| // }); | |||
| getAttrebuteValues(); | |||
| getAttributeValues(); | |||
| }); | |||
| @@ -894,7 +925,7 @@ export default { | |||
| formData.append("title", title.value); | |||
| formData.append("slug", slug.value); | |||
| formData.append("summary", summary.value); | |||
| formData.append("description", editorContent.value); | |||
| formData.append("description", description.value); | |||
| if (isChosen.value == 1) { | |||
| formData.append("is_chosen", isChosen.value); | |||
| @@ -994,7 +1025,6 @@ export default { | |||
| // 🔸 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); | |||
| @@ -1030,6 +1060,10 @@ export default { | |||
| countInCarton.value = ""; | |||
| image.value = null; | |||
| images.value = []; | |||
| description.value = null; | |||
| if (productImageRef.value) { | |||
| productImageRef.value.value = null | |||
| } | |||
| attrebutes.value.forEach((attribute) => { | |||
| attribute.isChecked = false; | |||
| attribute.value = ""; | |||
| @@ -1097,6 +1131,9 @@ export default { | |||
| handleSearch, | |||
| formattedCategories, | |||
| categorySelectorLoader, | |||
| productImageRef, | |||
| description, | |||
| handleAddIdentityClick | |||
| }; | |||
| }, | |||
| }; | |||
| @@ -61,20 +61,19 @@ | |||
| </small> | |||
| </BCol> | |||
| <BCol md="6"> | |||
| <div class="form-group"> | |||
| <label class="form-label">توضیحات محصول</label> | |||
| <div | |||
| @input="clearError('editorContent')" | |||
| ref="editor" | |||
| class="quill-editor" | |||
| ></div> | |||
| <textarea | |||
| v-model="description" | |||
| class="form-control" | |||
| placeholder="توضیحات محصول" | |||
| :class="{ 'is-invalid': errors.description }" | |||
| @input="clearError('description')" | |||
| /> | |||
| </div> | |||
| <small v-if="errors.editorContent" class="text-danger"> | |||
| {{ errors.editorContent }} | |||
| <small v-if="errors.description" class="text-danger"> | |||
| {{ errors.description }} | |||
| </small> | |||
| </BCol> | |||
| @@ -866,7 +865,6 @@ export default { | |||
| const spescialPrice = ref(); | |||
| const wholesalePrice = ref(); | |||
| const retailePrice = ref(); | |||
| const description = ref(); | |||
| const productAttributes = ref([]); | |||
| const images = ref([{ file: null, preview: null }]); | |||
| const localImages = ref([{ preview: null }]); | |||
| @@ -895,6 +893,11 @@ export default { | |||
| const editorContent = ref(""); | |||
| const categorySelectorLoader = ref(false); | |||
| const brandSelectorLoader = ref(false); | |||
| // ALIREZA | |||
| const productImageRef = ref(null) | |||
| const description = ref(null) | |||
| const formattedCategories = computed(() => | |||
| Array.isArray(categories.value) | |||
| @@ -1179,8 +1182,8 @@ export default { | |||
| if (!imagePreview.value) | |||
| errors.value.image = "وارد کردن عکس محصول ضروری می باشد"; | |||
| if (!editorContent.value) | |||
| errors.value.editorContent = "وارد کردن توضیحات محصول ضروری می باشد"; | |||
| if (!description.value) | |||
| errors.value.description = "وارد کردن توضیحات محصول ضروری می باشد"; | |||
| if (!productType.value) | |||
| errors.value.productType = "انتخاب حالت محصول ضروری می باشد"; | |||
| @@ -1270,6 +1273,7 @@ export default { | |||
| spescialPrice.value = product.value?.special_price; | |||
| selectedCat.value = resp?.data?.data?.category.id; | |||
| selectedBrand.value = resp?.data?.data?.brand.id; | |||
| description.value = product.value?.description; | |||
| if (product.value?.special_expires_at) { | |||
| expire.value = moment( | |||
| @@ -1278,30 +1282,30 @@ export default { | |||
| ).format("jYYYY/jMM/jDD HH:mm:ss"); | |||
| } | |||
| if (editor.value) { | |||
| quillInstance.value = new Quill(editor.value, { | |||
| theme: "snow", | |||
| modules: { | |||
| toolbar: [ | |||
| [{ header: "1" }, { header: "2" }, { font: [] }], | |||
| [{ list: "ordered" }, { list: "bullet" }], | |||
| [{ align: [] }], | |||
| ["bold", "italic", "underline"], | |||
| ["link", "image"], | |||
| [{ script: "sub" }, { script: "super" }], | |||
| [{ direction: "rtl" }], | |||
| ], | |||
| }, | |||
| }); | |||
| quillInstance.value.root.innerHTML = product.value.description; | |||
| editorContent.value = product.value.description; | |||
| quillInstance.value.on("text-change", () => { | |||
| editorContent.value = quillInstance.value.root.innerHTML; | |||
| }); | |||
| } | |||
| // if (editor.value) { | |||
| // quillInstance.value = new Quill(editor.value, { | |||
| // theme: "snow", | |||
| // modules: { | |||
| // toolbar: [ | |||
| // [{ header: "1" }, { header: "2" }, { font: [] }], | |||
| // [{ list: "ordered" }, { list: "bullet" }], | |||
| // [{ align: [] }], | |||
| // ["bold", "italic", "underline"], | |||
| // ["link", "image"], | |||
| // [{ script: "sub" }, { script: "super" }], | |||
| // [{ direction: "rtl" }], | |||
| // ], | |||
| // }, | |||
| // }); | |||
| // | |||
| // quillInstance.value.root.innerHTML = product.value.description; | |||
| // | |||
| // | |||
| // | |||
| // quillInstance.value.on("text-change", () => { | |||
| // description.value = quillInstance.value.root.innerHTML; | |||
| // }); | |||
| // } | |||
| selectedBrand.value = product.value?.brand_id; | |||
| selectedCat.value = product.value?.category_id; | |||
| @@ -1456,7 +1460,7 @@ export default { | |||
| formData.append("title", title.value); | |||
| formData.append("slug", slug.value); | |||
| formData.append("summary", summary.value); | |||
| formData.append("description", editorContent.value); | |||
| formData.append("description", description.value); | |||
| if (productType.value == 2 || productType.value == 3) { | |||
| formData.append("count_in_carton", countInCarton.value); | |||
| @@ -1682,6 +1686,7 @@ export default { | |||
| brandSelectorLoader, | |||
| handleSearch, | |||
| handleBrandSearch, | |||
| productImageRef | |||
| }; | |||
| }, | |||
| }; | |||
| @@ -363,6 +363,8 @@ export default { | |||
| <th>عکس</th> | |||
| <th>عنوان</th> | |||
| <th>مدل</th> | |||
| <th>دسته بندی</th> | |||
| <th>برند</th> | |||
| <th>تعداد فروش</th> | |||
| <th>برگزیده</th> | |||
| <th>تخفیف برگزیده</th> | |||
| @@ -389,6 +391,11 @@ export default { | |||
| <td v-if="product.type == 1">تکی</td> | |||
| <td v-if="product.type == 2">عمده</td> | |||
| <td v-if="product.type == 3">تکی و عمده</td> | |||
| <td>{{ product?.category?.title }}</td> | |||
| <td>{{ product?.brand?.title }}</td> | |||
| <td>{{ product.sold_count }}</td> | |||
| <td> | |||
| <span v-if="product.is_chosen == 0"> | |||