clean code

This commit is contained in:
HAM!DREZA
2024-07-05 19:39:39 +03:30
parent ad7a4b839d
commit 852b6962c6
58 changed files with 911 additions and 2847 deletions
+5
View File
@@ -10,6 +10,7 @@ declare module 'vue' {
AddFaq: typeof import('./src/components/AddFaq.vue')['default']
AddVideo: typeof import('./src/components/AddVideo.vue')['default']
Avatar: typeof import('primevue/avatar')['default']
Badge: typeof import('primevue/badge')['default']
Button: typeof import('primevue/button')['default']
Card: typeof import('primevue/card')['default']
CategoryForm: typeof import('./src/components/CategoryForm.vue')['default']
@@ -32,6 +33,7 @@ declare module 'vue' {
IconSupport: typeof import('./src/components/icons/IconSupport.vue')['default']
IconTooling: typeof import('./src/components/icons/IconTooling.vue')['default']
InputGroup: typeof import('primevue/inputgroup')['default']
InputNumber: typeof import('primevue/inputnumber')['default']
InputText: typeof import('primevue/inputtext')['default']
Listbox: typeof import('primevue/listbox')['default']
Menu: typeof import('primevue/menu')['default']
@@ -43,6 +45,7 @@ declare module 'vue' {
PanelMenu: typeof import('primevue/panelmenu')['default']
ProductDetails: typeof import('./src/components/ProductDetails.vue')['default']
ProductForm: typeof import('./src/components/ProductForm.vue')['default']
ProgressBar: typeof import('primevue/progressbar')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SecondStep: typeof import('./src/components/ProductFormSteps/SecondStep.vue')['default']
@@ -53,7 +56,9 @@ declare module 'vue' {
Textarea: typeof import('primevue/textarea')['default']
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
ThirdStep: typeof import('./src/components/ProductFormSteps/ThirdStep.vue')['default']
ToggleButton: typeof import('primevue/togglebutton')['default']
Toolbar: typeof import('primevue/toolbar')['default']
TreeTable: typeof import('primevue/treetable')['default']
UserDetails: typeof import('./src/components/UserDetails.vue')['default']
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
}
+1 -5
View File
@@ -43,12 +43,8 @@ import { inject, reactive, ref } from "vue";
import { useFaqStore } from "@/stores/faq";
const store = useFaqStore();
const saving = ref(false);
const dialog = inject("dialogRef");
const type = ref();
const produc = reactive(dialog.value.data);
const { toast, t } = inject("service");
const { toast } = inject("service");
const newFaq = reactive({
title: "",
description: "",
-52
View File
@@ -82,8 +82,6 @@
<script setup>
import { inject, reactive, ref } from "vue";
import { usePrimeVue } from "primevue/config";
import { useToast } from "primevue/usetoast";
import { useHeadlinesStore } from "@/stores/Headlines"
import { useUploaderStore } from "@/stores/uploader";
const loading = ref(false)
@@ -95,7 +93,6 @@ const dialog = inject("dialogRef");
const type = ref();
const produc = reactive(dialog.value.data);
const saving = ref(false)
const videoStatus = ref(null);
const setType = () => {
if (type.value === "غیرفعال") {
newEpisode.status = "DeActivate";
@@ -134,53 +131,4 @@ const save = async() =>{
saving.value = false;
dialog.value.close()
}
const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
removeFileCallback(index);
totalSize.value -= parseInt(formatSize(file.size));
totalSizePercent.value = totalSize.value / 10;
};
const onClearTemplatingUpload = (clear) => {
clear();
totalSize.value = 0;
totalSizePercent.value = 0;
};
const onSelectedFiles = (event) => {
files.value = event.files;
files.value.forEach((file) => {
totalSize.value += parseInt(formatSize(file.size));
});
};
const uploadEvent = (callback) => {
totalSizePercent.value = totalSize.value / 10;
callback();
};
const onTemplatedUpload = () => {
toast.add({
severity: "info",
summary: "Success",
detail: "File Uploaded",
life: 3000,
});
};
const formatSize = (bytes) => {
const k = 1024;
const dm = 3;
const sizes = $primevue.config.locale.fileSizeTypes;
if (bytes === 0) {
return `0 ${sizes[0]}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(k));
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
return `${formattedSize} ${sizes[i]}`;
};
</script>
+48 -102
View File
@@ -1,107 +1,53 @@
<template>
<section class="flex flex-col gap-7 pt-5 w-96">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText v-model="name" class="w-full" />
<label>{{ $t('name') }}</label>
</FloatLabel>
<small v-if="errors.name">{{ errors.name }}</small>
</div>
<div class="w-full flex gap-2 mt-4">
<Button
:label="$t('back')"
outlined
severity="secondary"
class="ml-auto"
@click="dialog.close()"
/>
<Button
:label="$t('save')"
icon="pi pi-save"
severity="success"
@click="save()"
:loading="saving"
/>
</div>
</section>
</template>
<section class="flex flex-col gap-7 pt-5 w-96">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText v-model="name" class="w-full" />
<label>{{ $t("name") }}</label>
</FloatLabel>
<small v-if="errors.name">{{ errors.name }}</small>
</div>
<div class="w-full flex gap-2 mt-4">
<Button
:label="$t('back')"
outlined
severity="secondary"
class="ml-auto"
@click="dialog.close()"
/>
<Button
:label="$t('save')"
icon="pi pi-save"
severity="success"
@click="save()"
:loading="saving"
/>
</div>
</section>
</template>
<script setup>
import { useCategoriesStore } from '@/stores/categoreis'
// import { useUploaderStore } from '@/stores/uploader';
import { computed, inject, reactive, ref, watch } from 'vue'
const name = ref('')
const dialog = inject('dialogRef')
const { toast, t } = inject('service')
const { category } = dialog.value.data
const form = reactive(Object.assign({}, category || {}))
const errors = reactive({})
const file = ref({})
const saving = ref(false)
const store = useCategoriesStore()
const save = async () => {
saving.value = true
const { status, data } = await store.create(name)
saving.value = false
if (status === 201 || status === 200)
toast.add({
life: 2000,
severity: 'success',
summary: t('successful'),
detail: t('دسته بندی جدید ایجاد شد')
})
dialog.value.close()
// form.headerImage = data.url
// else
// return toast.add({
// life: 2000, severity: 'error', summary: t('error'), detail: data.msg
// })
}
// //Create or Update
// const { status, data } =
// !!category ? await store.update(category._id, form) : await store.store(form)
// saving.value = false
// if (status === 200 || status === 201) {
// toast.add({
// life: 2000,
// severity: 'success',
// summary: t('successful'),
// detail: t(!!category ? 'updateSuccessfully' : 'storeSuccessfully')
// })
// dialog.value.close()
// } else if (status === 422)
// Object.assign(errors, data)
// else
// toast.add({
// severity: 'error', life: 2000, detail: data.msg, summary: t('error')
// })
// }
// watch(computed(() => Object.assign({}, form)), (value, old) => {
// Object.keys(form).forEach((key) => {
// if (value[key] != old[key]) delete errors[key]
// })
// })
// const parentCategoryID = computed({
// get: () => form.parentCategoryID ? ({ [form.parentCategoryID]: true }) : null,
// set: (v) => form.parentCategoryID = Object.keys(v)[0]
// })
</script>
import { useCategoriesStore } from "@/stores/categoreis";
import { inject, reactive, ref } from "vue";
const name = ref("");
const dialog = inject("dialogRef");
const { toast, t } = inject("service");
const errors = reactive({});
const saving = ref(false);
const store = useCategoriesStore();
const save = async () => {
saving.value = true;
const { status, data } = await store.create(name);
saving.value = false;
if (status === 201 || status === 200)
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("دسته بندی جدید ایجاد شد"),
});
dialog.value.close();
};
</script>
<style lang="scss" scoped></style>
+9 -18
View File
@@ -4,7 +4,6 @@
<li class="flex items-center justify-between">
<strong>{{ $t('کد تخفیف') }}</strong>
<div class="flex items-center gap-2 w-max">
<!-- <i v-if="user.confirmNumber" class="pi pi-verified text-green-600"></i> -->
<span>{{ coupon.coupon.code }}</span>
</div>
</li>
@@ -27,17 +26,16 @@
<strong>{{ $t('تاریخ انقضا') }}</strong>
<span>{{ jDate(coupon.coupon.endDate) }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('نوع تخفیف') }}</strong>
<span>{{ coupon.coupon.status === "Active" ? 'فعال' : 'غیرفعال' }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('ایجاد شده توسط') }}</strong>
<span>{{ coupon.coupon.admin.name }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('وضعیت تخفیف') }}</strong>
<span>{{ coupon.coupon.status === "Active" ? 'فعال' : 'غیرفعال' }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('تغییر وضعیت') }}</strong>
<!-- <ToggleButton @change="changeStatus" v-model="checked" onLabel="فعال" offLabel="غیرفعال" /> -->
<Tag v-if="coupon.coupon.status === 'DeActivate'" @click="changeToActive" class="cursor-pointer" severity="success" value="فعال سازی"></Tag>
<Tag v-if="coupon.coupon.status === 'Active'" @click="changeToDeActive" class="cursor-pointer" severity="danger" value="غیرفعال سازی"></Tag>
</li>
@@ -50,15 +48,12 @@
<script setup>
import { inject, reactive } from 'vue';
import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat';
import {useDiscountStore} from '../stores/discounts'
const store = useDiscountStore()
import { ref } from 'vue';
import { useRouter } from 'vue-router';
const checked = ref(false);
const { confirm, toast, t } = inject("service");
const router = useRouter()
const saving = ref(false)
const { toast, t } = inject("service");
const changeToDeActive = async() =>{
const change = await store.changeStatus({
id: coupon.coupon.id,
@@ -107,11 +102,7 @@ const changeToActive = async() => {
}
};
const tabs = ref([
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
]);
const dialog = inject('dialogRef')
const coupon = reactive(Object.assign({}, dialog.value.data))
+7 -51
View File
@@ -82,21 +82,17 @@
<script setup>
import { inject, reactive, ref, watchEffect } from "vue";
import { usePrimeVue } from "primevue/config";
import { useToast } from "primevue/usetoast";
import { useHeadlinesStore } from "@/stores/Headlines"
import { useUploaderStore } from "@/stores/uploader";
const loading = ref(false)
const uploadStore = useUploaderStore();
const saving = ref(false)
const store = useHeadlinesStore()
const dialog = inject("dialogRef");
const { toast, t } = inject("service");
const type = ref();
const produc = reactive(dialog.value.data);
const videoStatus = ref(null);
const setType = () => {
if (type.value === "غیرفعال") {
newEpisode.status = "DeActivate";
@@ -150,54 +146,14 @@ const save = async() =>{
console.log(send);
saving.value = false
dialog.value.close()
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("ویدیو آپدیت شد"),
});
}
const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
removeFileCallback(index);
totalSize.value -= parseInt(formatSize(file.size));
totalSizePercent.value = totalSize.value / 10;
};
const onClearTemplatingUpload = (clear) => {
clear();
totalSize.value = 0;
totalSizePercent.value = 0;
};
const onSelectedFiles = (event) => {
files.value = event.files;
files.value.forEach((file) => {
totalSize.value += parseInt(formatSize(file.size));
});
};
const uploadEvent = (callback) => {
totalSizePercent.value = totalSize.value / 10;
callback();
};
const onTemplatedUpload = () => {
toast.add({
severity: "info",
summary: "Success",
detail: "File Uploaded",
life: 3000,
});
};
const formatSize = (bytes) => {
const k = 1024;
const dm = 3;
const sizes = $primevue.config.locale.fileSizeTypes;
if (bytes === 0) {
return `0 ${sizes[0]}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(k));
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
return `${formattedSize} ${sizes[i]}`;
};
</script>
+4 -18
View File
@@ -6,17 +6,16 @@
<li class="flex items-center justify-between">
<strong>{{ $t('id') }}</strong>
<div class="flex items-center gap-2 w-max">
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
<span>{{ user.product.id }}</span>
<span>{{ faq.product.id }}</span>
</div>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('سوال') }}</strong>
<span>{{ user.product.title }}</span>
<span>{{ faq.product.title }}</span>
</li>
<li class="flex items-center justify-between gap-32">
<strong>{{ $t('پاسخ') }}</strong>
<span>{{ user.product.description }}</span>
<span>{{ faq.product.description }}</span>
</li>
</ul>
@@ -26,22 +25,9 @@
<script setup>
import { inject, reactive } from 'vue';
import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat';
import { ref } from 'vue';
const tabs = ref([
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
]);
const dialog = inject('dialogRef')
const user = reactive(Object.assign({}, dialog.value.data))
const confirmToSales = (value) => {
dialog.value.data.confirm(user, value)
}
const faq = reactive(Object.assign({}, dialog.value.data))
</script>
<style lang="scss" scoped></style>
+34 -83
View File
@@ -1,91 +1,42 @@
<template>
<section class="flex flex-col gap-6 w-96">
<ul class="flex flex-col gap-5">
<li class="flex items-center justify-between">
<video :src="videoUrl.data.url" autoplay></video>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('عنوان') }}</strong>
<div class="flex items-center gap-2 w-max">
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
<span>{{ head.product.headline }}</span>
</div>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('توضیحات') }}</strong>
<span>{{ head.product.description }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('تعداد نظرات') }}</strong>
<span>{{ head.product.Comment.length }}</span>
</li>
<!--
<li class="flex items-center justify-between">
<strong>{{ $t('createdAt') }}</strong>
<span>{{ jDate(user.product.createdAt) }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('updatedAt') }}</strong>
<span>{{ jDate(user.product.updatedAt) }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('زمان دوره') }}</strong>
<span>{{ user.product.duration }} ساعت</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('زبان') }}</strong>
<span>{{ user.product.language }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('price') }}</strong>
<span>{{ numberFormat(user.product.price) }} تومان</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('تعداد دانشجو ها') }}</strong>
<span>{{ user.product.studentsCount }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('مدرس دوره') }}</strong>
<span>{{ user.product.teacher.name }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('حجم دوره') }}</strong>
<span>{{ user.product.totalFileSize }} گیگابایت</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('امتیاز دانشجویان') }}</strong>
<span>{{ user.product.averageRating }}</span>
</li> -->
</ul>
</section>
<section class="flex flex-col gap-6 w-96">
<ul class="flex flex-col gap-5">
<li class="flex items-center justify-between">
<video :src="videoUrl.data.url" autoplay></video>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("عنوان") }}</strong>
<div class="flex items-center gap-2 w-max">
<span>{{ head.product.headline }}</span>
</div>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("توضیحات") }}</strong>
<span>{{ head.product.description }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("تعداد نظرات") }}</strong>
<span>{{ head.product.Comment.length }}</span>
</li>
</ul>
</section>
</template>
<script setup>
import { inject, reactive, watchEffect } from 'vue';
import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat';
import { useUploaderStore } from "@/stores/uploader"
import { inject, reactive, watchEffect } from "vue";
import { useUploaderStore } from "@/stores/uploader";
import { ref } from 'vue';
const store = useUploaderStore()
const tabs = ref([
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
]);
const dialog = inject('dialogRef')
const videoUrl = ref()
const head = reactive(Object.assign({}, dialog.value.data))
console.log('head', );
watchEffect(async() =>{
videoUrl.value = await store.getUrl(head.product.videoUrl)
console.log(videoUrl);
})
// const confirmToSales = (value) => {
// dialog.value.data.confirm(user, value)
// }
import { ref } from "vue";
const store = useUploaderStore();
const dialog = inject("dialogRef");
const videoUrl = ref();
const head = reactive(Object.assign({}, dialog.value.data));
watchEffect(async () => {
videoUrl.value = await store.getUrl(head.product.videoUrl);
console.log(videoUrl);
});
</script>
<style lang="scss" scoped></style>
-44
View File
@@ -1,44 +0,0 @@
<script setup>
defineProps({
msg: {
type: String,
required: true
}
})
</script>
<template>
<div class="greetings">
<h1 class="green">{{ msg }}</h1>
<h3>
Youve successfully created a project with
<a href="https://vitejs.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>.
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
+64 -79
View File
@@ -1,88 +1,73 @@
<template>
<section class="flex flex-col gap-6 w-96">
<div class="flex flex-col gap-2 items-center self-center">
<Avatar :image="user.product.image" class="w-full h-40" />
<h2 class="text-xl">{{ user.product.name }}</h2>
<!-- <Tag :value="$t(user.accountType.toLowerCase())" severity="warning" /> -->
<section class="flex flex-col gap-6 w-96">
<div class="flex flex-col gap-2 items-center self-center">
<Avatar :image="data.product.image" class="w-full h-40" />
<h2 class="text-xl">{{ data.product.name }}</h2>
</div>
<ul class="flex flex-col gap-5">
<li class="flex items-center justify-between">
<strong>{{ $t("category") }}</strong>
<div class="flex items-center gap-2 w-max">
<span>{{ data.product.categories[0].name }}</span>
</div>
<ul class="flex flex-col gap-5">
<li class="flex items-center justify-between">
<strong>{{ $t('category') }}</strong>
<div class="flex items-center gap-2 w-max">
<!-- <i v-if="user.confirmNumber" class="pi pi-verified text-green-600"></i> -->
<span>{{ user.product.categories[0].name }}</span>
</div>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('id') }}</strong>
<div class="flex items-center gap-2 w-max">
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
<span>{{ user.product._id }}</span>
</div>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('تعداد ویدیو ها') }}</strong>
<span>{{ user.product.countOfVideos }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('createdAt') }}</strong>
<span>{{ jDate(user.product.createdAt) }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('updatedAt') }}</strong>
<span>{{ jDate(user.product.updatedAt) }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('زمان دوره') }}</strong>
<span>{{ user.product.duration }} ساعت</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('زبان') }}</strong>
<span>{{ user.product.language }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('price') }}</strong>
<span>{{ numberFormat(user.product.price) }} تومان</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('تعداد دانشجو ها') }}</strong>
<span>{{ user.product.studentsCount }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('مدرس دوره') }}</strong>
<span>{{ user.product.teacher.name }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('حجم دوره') }}</strong>
<span>{{ user.product.totalFileSize }} گیگابایت</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('امتیاز دانشجویان') }}</strong>
<span>{{ user.product.averageRating }}</span>
</li>
</ul>
</section>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("id") }}</strong>
<div class="flex items-center gap-2 w-max">
<span>{{ data.product._id }}</span>
</div>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("تعداد ویدیو ها") }}</strong>
<span>{{ data.product.countOfVideos }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("createdAt") }}</strong>
<span>{{ jDate(data.product.createdAt) }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("updatedAt") }}</strong>
<span>{{ jDate(data.product.updatedAt) }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("زمان دوره") }}</strong>
<span>{{ data.product.duration }} ساعت</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("زبان") }}</strong>
<span>{{ data.product.language }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("price") }}</strong>
<span>{{ numberFormat(data.product.price) }} تومان</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("تعداد دانشجو ها") }}</strong>
<span>{{ data.product.studentsCount }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("مدرس دوره") }}</strong>
<span>{{ data.product.teacher.name }}</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("حجم دوره") }}</strong>
<span>{{ data.product.totalFileSize }} گیگابایت</span>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t("امتیاز دانشجویان") }}</strong>
<span>{{ data.product.averageRating }}</span>
</li>
</ul>
</section>
</template>
<script setup>
import { inject, reactive } from 'vue';
import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat';
import { ref } from 'vue';
const tabs = ref([
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
]);
const dialog = inject('dialogRef')
import { inject, reactive } from "vue";
import { jDate } from "@/utils/jDate";
import { numberFormat } from "@/utils/numberFormat";
const dialog = inject("dialogRef");
const data = reactive(Object.assign({}, dialog.value.data));
const user = reactive(Object.assign({}, dialog.value.data))
const confirmToSales = (value) => {
dialog.value.data.confirm(user, value)
}
</script>
<style lang="scss" scoped></style>
-104
View File
@@ -1,104 +0,0 @@
<template>
<section class="container w-full">
<Stepper>
<StepperPanel :header="$t('information')">
<template #content="{ nextCallback }">
<FirstStep @close="dialog.close()" @next="nextCallback" />
</template>
</StepperPanel>
<StepperPanel :header="$t('details')">
<template #content="{ nextCallback, prevCallback }">
<SecondStep @prev="prevCallback" @next="nextCallback" />
</template>
</StepperPanel>
<StepperPanel :header="$t('images')">
<template #content="{ prevCallback }">
<ThirdStep @prev="prevCallback" @save="save()" />
</template>
</StepperPanel>
</Stepper>
</section>
</template>
<script setup>
import { useUploaderStore } from '@/stores/uploader';
import FirstStep from './ProductFormSteps/FirstStep.vue';
import SecondStep from './ProductFormSteps/SecondStep.vue';
import ThirdStep from './ProductFormSteps/ThirdStep.vue';
import { inject, provide, reactive, ref } from 'vue';
import { useProductsStore } from '@/stores/products';
const dialog = inject('dialogRef')
const { toast, t } = inject('service')
const { product } = dialog.value.data
const form = reactive(Object.assign({}, product || {}))
const errors = reactive({})
const saving = ref(false)
provide('data', { form, errors, saving })
const store = useProductsStore()
const uploader = useUploaderStore()
const save = async () => {
saving.value = true
//Upload File
if (typeof form.coverImage === 'object') {
const { status, data } = await uploader.single(form.coverImage)
if (status === 201)
form.coverImage = data.url
else
return toast.add({
life: 2000, severity: 'error', summary: t('error'), detail: data.msg
})
}
//upload images
const files = form.images.filter((image) => typeof image === 'object')
if (files.length > 0) {
const { status, data } = await uploader.bulk(files)
if (status === 201)
form.images = form.images.map((image) => {
if (typeof image === 'object')
return data.urls.shift();
return image;
})
else
return toast.add({
life: 2000, severity: 'error', summary: t('error'), detail: data.msg
})
}
//Create or Update
const { status, data } =
!!product ? await store.update(product._id, form) : await store.store(form)
saving.value = false
if (status === 200 || status === 201) {
toast.add({
life: 2000,
severity: 'success',
summary: t('successful'),
detail: t(!!category ? 'updateSuccessfully' : 'storeSuccessfully')
})
dialog.value.close()
} else if (status === 422)
Object.assign(errors, data)
else
toast.add({
severity: 'error', life: 2000, detail: data.msg, summary: t('error')
})
}
</script>
<style lang="scss" scoped></style>
@@ -1,67 +0,0 @@
<template>
<section class="flex flex-col gap-4 ">
<div class="flex flex-col gap-1">
<FileUpload mode="advanced" accept="image/*" multiple :maxFileSize="10490000" @select="chooseImages"
@remove="chooseImages" :invalidFileSizeMessage="$t('invalidFileSizeMessage')">
<template #header="{ chooseCallback }">
<div class="flex justify-between w-full items-center">
<h2 class="font-bold text-xl ">{{ $t('imagesGallery') }}</h2>
<Button :label="$t('chooseImage')" severity="secondary" icon="pi pi-images" text
@click="chooseCallback()" />
</div>
</template>
<template #content="{ files, removeFileCallback }">
<div v-if="files.length > 0" class="flex gap-4 justify-between ">
<div class="flex gap-4 w-96 h-96 relative m-4 rounded-lg overflow-hidden bg-gray-50 shrink-0">
<img :src="form.coverImage?.objectURL || form.coverImage" class="object-cover">
<div class=" bg-black/60 absolute bottom-2 right-2 flex rounded-full px-4 py-2">
<span class="text-white text-sm m-auto"> {{ $t('mainImage') }}</span>
</div>
</div>
<Listbox v-model="form.coverImage" :options="files" @change="changeCoverImage(files)"
class="border-none" listStyle="max-height:26rem">
<template #option="{ option, index }">
<div class="flex flex-row-reverse gap-4 items-center w-96 rounded">
<img :src="option.objectURL || option"
class="w-24 h-20 object-cover object-center rounded-lg">
<div class="flex items-end justify-center h-full flex-col gap-4 ">
<h3 class="font-bold truncate w-40 text-left">{{ option.name }}</h3>
<span dir="ltr" class="text-sm">{{ formatSize(option.size) }}</span>
</div>
<Button icon="pi pi-times" size="small" text rounded severity="danger"
class="ml-auto p-0 w-6 h-6" @click="removeFileCallback(index)" />
</div>
</template>
</Listbox>
</div>
</template>
<template #empty>
<div class="flex items-center justify-center flex-col gap-8 py-12 h-96">
<i class="pi pi-images border-2 rounded-full p-5 text-8xl text-400 border-400" />
<p>{{ $t('Drag and drop images to here to upload.') }}</p>
</div>
</template>
</FileUpload>
<small v-if="errors.coverImage || errors.images" class="p-error">
{{ errors.coverImage || errors.images }}
</small>
</div>
<div class="flex pt-4 justify-content-end">
<Button :label="$t('back')" outlined severity="secondary" class="ml-auto" @click="$emit('close')" />
<Button :label="$t('next')" icon="pi pi-arrow-left" iconPos="right" severity="success"
@click="$emit('next')" />
</div>
</section>
</template>
<script setup>
import { inject } from 'vue';
const dialogRef = inject('dialogRef')
const { form, errors } = inject('data')
</script>
<style lang="scss" scoped></style>
@@ -1,68 +0,0 @@
<template>
<section class="flex flex-col gap-4 ">
مرحله دوم
<!-- <div class="flex gap-7 bg-gray-50/50 p-7 pt-7 border rounded-lg">
<div class="flex flex-col gap-7">
<div class="flex flex-col gap-1 w-96">
<FloatLabel>
<InputText v-model="form.title" class="w-full" />
<label>{{ $t('title') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText v-model="form.titleEN" class="w-full ltr" />
<label>{{ $t('titleEN') }}</label>
</FloatLabel>
<small v-if="errors.titleEN">{{ errors.titleEN }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText v-model="form.link" class="w-full ltr" />
<label>{{ $t('link') }}</label>
</FloatLabel>
<small v-if="errors.link">{{ errors.link }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText v-model="form.metaTitle" class="w-full" />
<label>{{ $t('metaTitle') }}</label>
</FloatLabel>
<small v-if="errors.metaTitle">{{ errors.metaTitle }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<Textarea v-model="form.metaDescription" rows="5" class="w-full" />
<label>{{ $t('metaDescription') }}</label>
</FloatLabel>
<small v-if="errors.metaDescription">{{ errors.metaDescription }}</small>
</div>
</div>
<div class="flex flex-col gap-1 gorw">
<Editor v-model="form.description" :placeholder="$t('description')" class="h-full"
editorStyle="max-height: calc(100% -46px)" />
<small v-if="errors.description">{{ errors.description }}</small>
</div>
</div>
-->
<div class="flex pt-4 justify-content-end">
<Button :label="$t('back')" outlined severity="secondary" class="ml-auto" @click="$emit('close')" />
<Button :label="$t('next')" icon="pi pi-arrow-left" iconPos="right" severity="success"
@click="$emit('next')" />
</div>
</section>
</template>
<script setup>
import { inject } from 'vue';
const dialogRef = inject('dialogRef')
const { form, errors } = inject('data')
</script>
<style lang="scss" scoped></style>
@@ -1,108 +0,0 @@
<template>
<section class="flex flex-col gap-4 ">
<Panel :header="$t('اطلاعات دوره')">
<div class="grid grid-cols-3 gap-7">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('title') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('مدت زمان دوره') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('دسته بندی') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('قیمت') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('مدرس') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('درصد تکمیل شده') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('سطح') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('زبان') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('تعداد ویدیو') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t('حجم کل دوره') }}</label>
</FloatLabel>
<small v-if="errors.title">{{ errors.title }}</small>
</div>
</div>
<div class="flex flex-col gap-1 mt-8">
<Editor :placeholder="$t('description')" class="h-full"
editorStyle="min-height: 200px" />
<small v-if="errors.description">{{ errors.description }}</small>
</div>
</Panel>
<div class="flex pt-4 justify-content-end">
<Button :label="$t('prev')" outlined severity="secondary" icon="pi pi-arrow-right" class="ml-auto"
@click="$emit('prev')" />
<Button :label="$t('save')" icon="pi pi-save" severity="success" :loading="saving" @click="$emit('save')" />
</div>
</section>
</template>
<script setup>
import { formatSize } from '@/utils/formatSize'
import { inject } from 'vue';
const { form, errors, saving } = inject('data')
const chooseImages = ({ files }) => {
if (!form.coverImage) form.coverImage = files?.[0] || null
changeCoverImage(files)
}
const changeCoverImage = (files) => {
form.images = files.filter((img) => img != form.coverImage)
}
</script>
<style lang="scss" scoped></style>
-88
View File
@@ -1,88 +0,0 @@
<script setup>
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
</script>
<template>
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
Vues
<a href="https://vuejs.org/" target="_blank" rel="noopener">official documentation</a>
provides you with all information you need to get started.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<ToolingIcon />
</template>
<template #heading>Tooling</template>
This project is served and bundled with
<a href="https://vitejs.dev/guide/features.html" target="_blank" rel="noopener">Vite</a>. The
recommended IDE setup is
<a href="https://code.visualstudio.com/" target="_blank" rel="noopener">VSCode</a> +
<a href="https://github.com/johnsoncodehk/volar" target="_blank" rel="noopener">Volar</a>. If
you need to test your components and web pages, check out
<a href="https://www.cypress.io/" target="_blank" rel="noopener">Cypress</a> and
<a href="https://on.cypress.io/component" target="_blank" rel="noopener"
>Cypress Component Testing</a
>.
<br />
More instructions are available in <code>README.md</code>.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<EcosystemIcon />
</template>
<template #heading>Ecosystem</template>
Get official tools and libraries for your project:
<a href="https://pinia.vuejs.org/" target="_blank" rel="noopener">Pinia</a>,
<a href="https://router.vuejs.org/" target="_blank" rel="noopener">Vue Router</a>,
<a href="https://test-utils.vuejs.org/" target="_blank" rel="noopener">Vue Test Utils</a>, and
<a href="https://github.com/vuejs/devtools" target="_blank" rel="noopener">Vue Dev Tools</a>. If
you need more resources, we suggest paying
<a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">Awesome Vue</a>
a visit.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<CommunityIcon />
</template>
<template #heading>Community</template>
Got stuck? Ask your question on
<a href="https://chat.vuejs.org" target="_blank" rel="noopener">Vue Land</a>, our official
Discord server, or
<a href="https://stackoverflow.com/questions/tagged/vue.js" target="_blank" rel="noopener"
>StackOverflow</a
>. You should also subscribe to
<a href="https://news.vuejs.org" target="_blank" rel="noopener">our mailing list</a> and follow
the official
<a href="https://twitter.com/vuejs" target="_blank" rel="noopener">@vuejs</a>
twitter account for latest news in the Vue world.
</WelcomeItem>
<WelcomeItem>
<template #icon>
<SupportIcon />
</template>
<template #heading>Support Vue</template>
As an independent project, Vue relies on community backing for its sustainability. You can help
us by
<a href="https://vuejs.org/sponsor/" target="_blank" rel="noopener">becoming a sponsor</a>.
</WelcomeItem>
</template>
+1 -8
View File
@@ -3,20 +3,17 @@
<div class="flex flex-col gap-2 items-center self-center">
<Avatar :image="user.avatarMedia" shape="circle" class="w-24 h-24" />
<h2 class="text-xl">{{ user.name }} {{ user.lastName }}</h2>
<!-- <Tag :value="$t(user.accountType.toLowerCase())" severity="warning" /> -->
</div>
<ul class="flex flex-col gap-5">
<li class="flex items-center justify-between">
<strong>{{ $t('phoneNumber') }}</strong>
<div class="flex items-center gap-2 w-max">
<!-- <i v-if="user.confirmNumber" class="pi pi-verified text-green-600"></i> -->
<span>{{ user.phone }}</span>
</div>
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('id') }}</strong>
<div class="flex items-center gap-2 w-max">
<!-- <i v-if="user.confirmEmail" class="pi pi-verified text-green-600"></i> -->
<span>{{ user.id }}</span>
</div>
</li>
@@ -26,7 +23,6 @@
</li>
<li class="flex items-center justify-between">
<strong>{{ $t('تغییر وضعیت') }}</strong>
<!-- <ToggleButton @change="changeStatus" v-model="checked" onLabel="فعال" offLabel="غیرفعال" /> -->
<Tag v-if="user.status === 'DeActivate'" @click="changeToActive" class="cursor-pointer" severity="success" value="فعال سازی"></Tag>
<Tag v-if="user.status === 'Active'" @click="changeToDeActive" class="cursor-pointer" severity="danger" value="غیرفعال سازی"></Tag>
</li>
@@ -35,15 +31,12 @@
</template>
<script setup>
import { jDate } from '@/utils/jDate';
import { inject, reactive } from 'vue';
const dialog = inject('dialogRef')
import { useUsersStore } from '@/stores/users';
const store = useUsersStore()
const user = reactive(Object.assign({}, dialog.value.data.user))
const { confirm, toast, t } = inject("service");
const { toast, t } = inject("service");
const confirmToSales = (value) => {
dialog.value.data.confirm(user, value)
}
-86
View File
@@ -1,86 +0,0 @@
<template>
<div class="item">
<i>
<slot name="icon"></slot>
</i>
<div class="details">
<h3>
<slot name="heading"></slot>
</h3>
<slot></slot>
</div>
</div>
</template>
<style scoped>
.item {
margin-top: 2rem;
display: flex;
position: relative;
}
.details {
flex: 1;
margin-left: 1rem;
}
i {
display: flex;
place-items: center;
place-content: center;
width: 32px;
height: 32px;
color: var(--color-text);
}
h3 {
font-size: 1.2rem;
font-weight: 500;
margin-bottom: 0.4rem;
color: var(--color-heading);
}
@media (min-width: 1024px) {
.item {
margin-top: 0;
padding: 0.4rem 0 1rem calc(var(--section-gap) / 2);
}
i {
top: calc(50% - 25px);
left: -26px;
position: absolute;
border: 1px solid var(--color-border);
background: var(--color-background);
border-radius: 8px;
width: 50px;
height: 50px;
}
.item:before {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
bottom: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:after {
content: ' ';
border-left: 1px solid var(--color-border);
position: absolute;
left: 0;
top: calc(50% + 25px);
height: calc(50% - 25px);
}
.item:first-of-type:before {
display: none;
}
.item:last-of-type:after {
display: none;
}
}
</style>
-7
View File
@@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M15 4a1 1 0 1 0 0 2V4zm0 11v-1a1 1 0 0 0-1 1h1zm0 4l-.707.707A1 1 0 0 0 16 19h-1zm-4-4l.707-.707A1 1 0 0 0 11 14v1zm-4.707-1.293a1 1 0 0 0-1.414 1.414l1.414-1.414zm-.707.707l-.707-.707.707.707zM9 11v-1a1 1 0 0 0-.707.293L9 11zm-4 0h1a1 1 0 0 0-1-1v1zm0 4H4a1 1 0 0 0 1.707.707L5 15zm10-9h2V4h-2v2zm2 0a1 1 0 0 1 1 1h2a3 3 0 0 0-3-3v2zm1 1v6h2V7h-2zm0 6a1 1 0 0 1-1 1v2a3 3 0 0 0 3-3h-2zm-1 1h-2v2h2v-2zm-3 1v4h2v-4h-2zm1.707 3.293l-4-4-1.414 1.414 4 4 1.414-1.414zM11 14H7v2h4v-2zm-4 0c-.276 0-.525-.111-.707-.293l-1.414 1.414C5.42 15.663 6.172 16 7 16v-2zm-.707 1.121l3.414-3.414-1.414-1.414-3.414 3.414 1.414 1.414zM9 12h4v-2H9v2zm4 0a3 3 0 0 0 3-3h-2a1 1 0 0 1-1 1v2zm3-3V3h-2v6h2zm0-6a3 3 0 0 0-3-3v2a1 1 0 0 1 1 1h2zm-3-3H3v2h10V0zM3 0a3 3 0 0 0-3 3h2a1 1 0 0 1 1-1V0zM0 3v6h2V3H0zm0 6a3 3 0 0 0 3 3v-2a1 1 0 0 1-1-1H0zm3 3h2v-2H3v2zm1-1v4h2v-4H4zm1.707 4.707l.586-.586-1.414-1.414-.586.586 1.414 1.414z"
/>
</svg>
</template>
@@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="17" fill="currentColor">
<path
d="M11 2.253a1 1 0 1 0-2 0h2zm-2 13a1 1 0 1 0 2 0H9zm.447-12.167a1 1 0 1 0 1.107-1.666L9.447 3.086zM1 2.253L.447 1.42A1 1 0 0 0 0 2.253h1zm0 13H0a1 1 0 0 0 1.553.833L1 15.253zm8.447.833a1 1 0 1 0 1.107-1.666l-1.107 1.666zm0-14.666a1 1 0 1 0 1.107 1.666L9.447 1.42zM19 2.253h1a1 1 0 0 0-.447-.833L19 2.253zm0 13l-.553.833A1 1 0 0 0 20 15.253h-1zm-9.553-.833a1 1 0 1 0 1.107 1.666L9.447 14.42zM9 2.253v13h2v-13H9zm1.553-.833C9.203.523 7.42 0 5.5 0v2c1.572 0 2.961.431 3.947 1.086l1.107-1.666zM5.5 0C3.58 0 1.797.523.447 1.42l1.107 1.666C2.539 2.431 3.928 2 5.5 2V0zM0 2.253v13h2v-13H0zm1.553 13.833C2.539 15.431 3.928 15 5.5 15v-2c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM5.5 15c1.572 0 2.961.431 3.947 1.086l1.107-1.666C9.203 13.523 7.42 13 5.5 13v2zm5.053-11.914C11.539 2.431 12.928 2 14.5 2V0c-1.92 0-3.703.523-5.053 1.42l1.107 1.666zM14.5 2c1.573 0 2.961.431 3.947 1.086l1.107-1.666C18.203.523 16.421 0 14.5 0v2zm3.5.253v13h2v-13h-2zm1.553 12.167C18.203 13.523 16.421 13 14.5 13v2c1.573 0 2.961.431 3.947 1.086l1.107-1.666zM14.5 13c-1.92 0-3.703.523-5.053 1.42l1.107 1.666C11.539 15.431 12.928 15 14.5 15v-2z"
/>
</svg>
</template>
-7
View File
@@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="currentColor">
<path
d="M11.447 8.894a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm0 1.789a1 1 0 1 0 .894-1.789l-.894 1.789zM7.447 7.106a1 1 0 1 0-.894 1.789l.894-1.789zM10 9a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0H8zm9.447-5.606a1 1 0 1 0-.894-1.789l.894 1.789zm-2.894-.789a1 1 0 1 0 .894 1.789l-.894-1.789zm2 .789a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zM18 5a1 1 0 1 0-2 0h2zm-2 2.5a1 1 0 1 0 2 0h-2zm-5.447-4.606a1 1 0 1 0 .894-1.789l-.894 1.789zM9 1l.447-.894a1 1 0 0 0-.894 0L9 1zm-2.447.106a1 1 0 1 0 .894 1.789l-.894-1.789zm-6 3a1 1 0 1 0 .894 1.789L.553 4.106zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zm-2-.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 2.789a1 1 0 1 0 .894-1.789l-.894 1.789zM2 5a1 1 0 1 0-2 0h2zM0 7.5a1 1 0 1 0 2 0H0zm8.553 12.394a1 1 0 1 0 .894-1.789l-.894 1.789zm-1.106-2.789a1 1 0 1 0-.894 1.789l.894-1.789zm1.106 1a1 1 0 1 0 .894 1.789l-.894-1.789zm2.894.789a1 1 0 1 0-.894-1.789l.894 1.789zM8 19a1 1 0 1 0 2 0H8zm2-2.5a1 1 0 1 0-2 0h2zm-7.447.394a1 1 0 1 0 .894-1.789l-.894 1.789zM1 15H0a1 1 0 0 0 .553.894L1 15zm1-2.5a1 1 0 1 0-2 0h2zm12.553 2.606a1 1 0 1 0 .894 1.789l-.894-1.789zM17 15l.447.894A1 1 0 0 0 18 15h-1zm1-2.5a1 1 0 1 0-2 0h2zm-7.447-5.394l-2 1 .894 1.789 2-1-.894-1.789zm-1.106 1l-2-1-.894 1.789 2 1 .894-1.789zM8 9v2.5h2V9H8zm8.553-4.894l-2 1 .894 1.789 2-1-.894-1.789zm.894 0l-2-1-.894 1.789 2 1 .894-1.789zM16 5v2.5h2V5h-2zm-4.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zm-2.894-1l-2 1 .894 1.789 2-1L8.553.106zM1.447 5.894l2-1-.894-1.789-2 1 .894 1.789zm-.894 0l2 1 .894-1.789-2-1-.894 1.789zM0 5v2.5h2V5H0zm9.447 13.106l-2-1-.894 1.789 2 1 .894-1.789zm0 1.789l2-1-.894-1.789-2 1 .894 1.789zM10 19v-2.5H8V19h2zm-6.553-3.894l-2-1-.894 1.789 2 1 .894-1.789zM2 15v-2.5H0V15h2zm13.447 1.894l2-1-.894-1.789-2 1 .894 1.789zM18 15v-2.5h-2V15h2z"
/>
</svg>
</template>
-7
View File
@@ -1,7 +0,0 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="currentColor">
<path
d="M10 3.22l-.61-.6a5.5 5.5 0 0 0-7.666.105 5.5 5.5 0 0 0-.114 7.665L10 18.78l8.39-8.4a5.5 5.5 0 0 0-.114-7.665 5.5 5.5 0 0 0-7.666-.105l-.61.61z"
/>
</svg>
</template>
-19
View File
@@ -1,19 +0,0 @@
<!-- This icon is from <https://github.com/Templarian/MaterialDesign>, distributed under Apache 2.0 (https://www.apache.org/licenses/LICENSE-2.0) license-->
<template>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
aria-hidden="true"
role="img"
class="iconify iconify--mdi"
width="24"
height="24"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 24 24"
>
<path
d="M20 18v-4h-3v1h-2v-1H9v1H7v-1H4v4h16M6.33 8l-1.74 4H7v-1h2v1h6v-1h2v1h2.41l-1.74-4H6.33M9 5v1h6V5H9m12.84 7.61c.1.22.16.48.16.8V18c0 .53-.21 1-.6 1.41c-.4.4-.85.59-1.4.59H4c-.55 0-1-.19-1.4-.59C2.21 19 2 18.53 2 18v-4.59c0-.32.06-.58.16-.8L4.5 7.22C4.84 6.41 5.45 6 6.33 6H7V5c0-.55.18-1 .57-1.41C7.96 3.2 8.44 3 9 3h6c.56 0 1.04.2 1.43.59c.39.41.57.86.57 1.41v1h.67c.88 0 1.49.41 1.83 1.22l2.34 5.39z"
fill="currentColor"
></path>
</svg>
</template>
+3 -61
View File
@@ -18,51 +18,13 @@
<script setup>
import { getActivePinia } from 'pinia';
import { inject, reactive, ref } from 'vue';
import { inject, ref } from 'vue';
import { useRouter } from 'vue-router';
const { t, confirm, toast } = inject('service')
const { t, confirm } = inject('service')
const router = useRouter()
const items = ref([
// {
// label: t('blog'),
// icon: 'pi pi-sitemap',
// items: [
// {
// label: t('posts'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/blog/posts')
// },
// {
// label: t('categories'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/blog/categories')
// },
// ]
// },
// {
// label: t('news'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/news')
// },
// {
// label: t('emails'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/emails')
// },
// {
// label: t('sms'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/sms')
// },
// {
// label: t('moeens'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/moeens')
// },
{
label: t('pages'),
icon: 'pi pi-sitemap',
@@ -70,7 +32,7 @@ const items = ref([
{
label: t('header'),
icon: 'pi pi-desktop',
command: () => router.push('/pages/header-menu')
command: () => router.push('/pages/main')
},
{
label: t('aboutUs'),
@@ -82,11 +44,6 @@ const items = ref([
icon: 'pi pi-question-circle',
command: () => router.push('/pages/Faq')
},
// {
// label: t('contactUs'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/pages/contact-us')
// }
]
},
{
@@ -158,21 +115,6 @@ const items = ref([
icon: 'pi pi-sitemap',
command: () => router.push('/comments')
},
// {
// label: t('orders'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/orders')
// },
// {
// label: t('products'),
// icon: 'pi pi-sitemap',
// command: () => router.push('/products')
// },
// {
// label: t('brands'),
// icon: 'pi pi-apple',
// command: () => router.push('/brands')
// },
]);
-16
View File
@@ -11,21 +11,5 @@ axios.interceptors.request.use((config) => {
return config
})
// axios.interceptors.response.use(
// async (res) => {
// if(!res.data?.url || !res.data?.urls){
// res.data = JSON.parse(JSON.stringify(res.data).replaceAll('/uploads', baseURL + '/uploads'))
// return res
// }
// },
// async (error) => {
// const status = error?.response?.status
// if (status === 401)
// return window.location.href = '/login?to=' + window.location.href;
// return error?.response
// }
// )
export default axios
+84 -106
View File
@@ -14,116 +14,16 @@ const router = createRouter({
},
{
path: '/',
// name: 'Main',
redirect: '/courses',
component: Main,
children: [
{
path: '/categories',
name: 'Categories',
component: () => import('@/views/Categories.vue'),
},
{
path: '/news',
name: 'News',
component: () => import('@/views/News.vue'),
},
{
path: '/coupons',
name: 'Coupons',
component: () => import('@/views/Coupons.vue'),
},
{
path: '/courses',
name: 'courses',
component: () => import('@/views/Courses.vue'),
},
{
path: '/create',
name: 'create',
component: () => import('@/views/Create.vue'),
},
// {
// path: '/products',
// name: 'Products',
// component: () => import('@/views/Products.vue'),
// },
// {
// path: '/orders',
// name: 'Orders',
// component: () => import('@/views/Orders.vue'),
// },
{
path: '/comments',
name: 'Comments',
component: () => import('@/views/Comments.vue'),
},
{
path: '/users',
name: 'Users',
component: () => import('@/views/Users.vue'),
},
{
path: '/admins',
name: 'admins',
component: () => import('@/views/Admins.vue'),
},
{
path: '/:id',
name: 'Edit',
component: () => import('@/views/Edit.vue'),
},
{
path: '/CreateCoupon',
name: 'CreateCoupon',
component: () => import('@/views/CreateCoupon.vue'),
},
{
path: '/coupon/:id',
name: 'EditCoupon',
component: () => import('@/views/EditCoupon.vue'),
},
{
path: '/comments/:id',
name: 'Answer',
component: () => import('@/views/AnswerComment.vue'),
},
{
path: '/tickets/:id',
name: 'ShowTickets',
component: () => import('@/views/ShowTickets.vue'),
},
{
path: '/tickets',
name: 'Tickets',
component: () => import('@/views/Tickets.vue'),
},
{
path: '/videos',
name: 'Videos',
component: () => import('@/views/Videos.vue'),
},
{
path: '/videos/:id',
name: 'AddVideos',
component: () => import('@/views/AddVideos.vue'),
},
{
path: '/couponsHistory',
name: 'couponsHistory',
component: () => import('@/views/couponsHistory.vue'),
},
// {
// path: '/videos/create',
// name: 'createVideo',
// component: () => import('@/views/CreateVideo.vue'),
// },
{
path: '/pages',
redirect: '/pages/main',
children: [
{
path: 'header-menu',
name: 'HeaderMenu',
path: 'main',
name: 'main',
component: () => import('@/views/HeaderMenu.vue')
},
{
@@ -139,9 +39,87 @@ const router = createRouter({
]
}
},
{
path: '/courses',
name: 'courses',
component: () => import('@/views/Courses.vue'),
},
{
path: '/create',
name: 'create',
component: () => import('@/views/Create.vue'),
},
{
path: '/videos',
name: 'Videos',
component: () => import('@/views/Videos.vue'),
},
{
path: '/videos/:id',
name: 'AddVideos',
component: () => import('@/views/AddVideos.vue'),
},
{
path: '/:id',
name: 'Edit',
component: () => import('@/views/Edit.vue'),
},
{
path: '/coupons',
name: 'Coupons',
component: () => import('@/views/Coupons.vue'),
},
{
path: '/CreateCoupon',
name: 'CreateCoupon',
component: () => import('@/views/CreateCoupon.vue'),
},
{
path: '/coupon/:id',
name: 'EditCoupon',
component: () => import('@/views/EditCoupon.vue'),
},
{
path: '/couponsHistory',
name: 'couponsHistory',
component: () => import('@/views/couponsHistory.vue'),
},
{
path: '/categories',
name: 'Categories',
component: () => import('@/views/Categories.vue'),
},
{
path: '/admins',
name: 'admins',
component: () => import('@/views/Admins.vue'),
},
{
path: '/users',
name: 'Users',
component: () => import('@/views/Users.vue'),
},
{
path: '/tickets',
name: 'Tickets',
component: () => import('@/views/Tickets.vue'),
},
{
path: '/tickets/:id',
name: 'ShowTickets',
component: () => import('@/views/ShowTickets.vue'),
},
{
path: '/comments',
name: 'Comments',
component: () => import('@/views/Comments.vue'),
},
{
path: '/comments/:id',
name: 'Answer',
component: () => import('@/views/AnswerComment.vue'),
},
],
}
]
+7 -8
View File
@@ -13,32 +13,31 @@ export const useAdminsStore = defineStore('admins', {
getters: {
isLoggedIn: (state) => !!state.token
},
actions:{
actions: {
async login(number) {
const { status, data, error } = await axios.post('/auth/login', {
phone: number.value,
type: 1
})
if (status == 200) {
return { status, data, error }
}
}, async sendOtp(number, code) {
}, async sendOtp(number, code) {
const { status, data, error } = await axios.post('/auth/login-step-two', {
phone: number.value,
code: code.value
})
console.log('data server', data);
if (status == 200) {
if (data.roles[0] === "Admin") {
this.token = data.token
}
else{
this.token = ''
else {
this.token = ''
}
return { status, data, error }
}
+17 -97
View File
@@ -8,109 +8,29 @@ export const useCategoriesStore = defineStore('categories', {
fetching: true,
total: 0
}),
getters: {
// treeSelect: (state) => (id = null) => {
// if (id) {
// const foreach = (items) => {
// return items.filter((item) => {
// if (item.key != id) {
// if (item.children)
// item.children = foreach(item.children)
// return true
// } else return false
// })
// }
// return foreach(state.items)
// }
// return state.items
// },
find: (state) => (id) => {
if (id) {
const foreach = (items) => {
const item = items.find(({ key }) => key == id)
if (item) return item
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.children) {
const temp = foreach(item.children)
if (temp) return temp
}
}
}
return foreach(state.items)
}
return '';
}
},
actions: {
async index() {
this.fetching = true
const {data} = await axios.get('/courseCategory')
const { data } = await axios.get('/courseCategory')
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
return data
},
async create(id) {
const result = await axios.post('/courseCategory', {
name: id.value
})
async create(id) {
const result = await axios.post('/courseCategory', {
name: id.value
})
return result;
},
async remove(id) {
const result = await axios.delete(`/courseCategory/${id}`)
return result;
},
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == id)
this.items.splice(index, 1)
}
async store(form) {
const result = await axios.post('/admin/categories', form)
if (result.status === 201)
this.items.unshift(result.data)
return result;
},
async update(id, form) {
const result = await axios.put(`/admin/categories/${id}`, form)
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == id)
this.items[index] = result.data
}
return result;
},
async remove(id) {
const result = await axios.delete(`/courseCategory/${id}`)
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == id)
this.items.splice(index, 1)
}
return result;
},
return result;
},
}
})
-12
View File
@@ -1,12 +0,0 @@
import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const doubleCount = computed(() => count.value * 2)
function increment() {
count.value++
}
return { count, doubleCount, increment }
})
+2 -109
View File
@@ -8,133 +8,26 @@ export const useCommentStore = defineStore('comments', {
fetching: true,
total: 0
}),
getters: {
// treeSelect: (state) => (id = null) => {
// if (id) {
// const foreach = (items) => {
// return items.filter((item) => {
// if (item.key != id) {
// if (item.children)
// item.children = foreach(item.children)
// return true
// } else return false
// })
// }
// return foreach(state.items)
// }
// return state.items
// },
find: (state) => (id) => {
if (id) {
const foreach = (items) => {
const item = items.find(({ key }) => key == id)
if (item) return item
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.children) {
const temp = foreach(item.children)
if (temp) return temp
}
}
}
return foreach(state.items)
}
return '';
}
},
actions: {
async reply(id, body) {
this.fetching = true
const data = await axios.post(`/course/${id}/comment`, body)
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
},
async ticket(id) {
this.fetching = true
const { status, data } = await axios.get(`/ticket/${id}`)
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
},
async close(id){
this.fetching = true
const { status, data } = await axios.patch(`/ticket/close/${id}`, id)
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
},
async create(id) {
-73
View File
@@ -22,20 +22,6 @@ export const useDiscountStore = defineStore('discounts', {
this.fetching = false
return data
},
async index() {
this.items = [];
this.fetching = true
@@ -63,65 +49,6 @@ export const useDiscountStore = defineStore('discounts', {
this.fetching = false
return data
}
// async create(newCourse) {
// const { status, data } = await axios.post('/course', newCourse)
// return data
// },
// async delete(id) {
// const { status, data } = await axios.delete(`/course/${id}`, id)
// return data
// },
// async edit(id) {
// const { status, data } = await axios.get(`/course/${id}`)
// return data
// },
// async store(form) {
// const result = await axios.post('/admin/products/add', form)
// if (result.status === 201)
// this.items.unshift(result.data)
// return result;
// },
// async update(id, form) {
// const result = await axios.put(`/admin/products/${id}`, form)
// if (result.status === 200) {
// const index = this.items.findIndex(({ _id }) => _id == id)
// this.items[index] = result.data
// }
// return result;
// },
// async confirm(id, form) {
// const result = await axios.post(`/admin/products/confirm/${id}`, form)
// if (result.status === 200) {
// const index = this.items.findIndex(({ _id }) => _id == id)
// this.items[index] = result.data
// }
// return result;
// },
// async remove(id) {
// const result = await axios.delete(`/admin/products/${id}`)
// if (result.status === 200) {
// const index = this.items.findIndex(({ _id }) => _id == id)
// this.items.splice(index, 1)
// }
// return result;
// },
}
})
-21
View File
@@ -24,26 +24,5 @@ export const useFaqStore = defineStore('faq', {
const data = await axios.post('/admins/faq/category', form)
return data
},
async confirm(item, value) {
item.confirming = true
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
item.confirming = false
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == item.id)
if (index >= 0)
this.items.splice(index, 1)
else
this.items.unshift(item)
}
return result;
},
}
})
+3 -42
View File
@@ -20,7 +20,9 @@ export const useProductsStore = defineStore('products', {
const { status, data } = await axios.get('/course', { params })
this.fetching = false
return this.items = data.courses
this.items = data
},
@@ -48,46 +50,5 @@ export const useProductsStore = defineStore('products', {
},
// async store(form) {
// const result = await axios.post('/admin/products/add', form)
// if (result.status === 201)
// this.items.unshift(result.data)
// return result;
// },
// async update(id, form) {
// const result = await axios.put(`/admin/products/${id}`, form)
// if (result.status === 200) {
// const index = this.items.findIndex(({ _id }) => _id == id)
// this.items[index] = result.data
// }
// return result;
// },
// async confirm(id, form) {
// const result = await axios.post(`/admin/products/confirm/${id}`, form)
// if (result.status === 200) {
// const index = this.items.findIndex(({ _id }) => _id == id)
// this.items[index] = result.data
// }
// return result;
// },
// async remove(id) {
// const result = await axios.delete(`/admin/products/${id}`)
// if (result.status === 200) {
// const index = this.items.findIndex(({ _id }) => _id == id)
// this.items.splice(index, 1)
// }
// return result;
// },
}
})
+1 -41
View File
@@ -23,47 +23,7 @@ export const useStaticsStore = defineStore('statics', {
const result = await axios.patch('/admin/settings/edit', data)
this.fetching = false
return result
// if (status === 200) {
// this.items = data
// // this.total = data.total
// }
},
async create(id) {
const result = await axios.post('/courseCategory', {
name: id.value
})
return result;
},
async store(form) {
const result = await axios.post('/admin/categories', form)
if (result.status === 201)
this.items.unshift(result.data)
return result;
},
async update(id, form) {
const result = await axios.put(`/admin/categories/${id}`, form)
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == id)
this.items[index] = result.data
}
return result;
},
async remove(id) {
const result = await axios.delete(`/courseCategory/${id}`)
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == id)
this.items.splice(index, 1)
}
return result;
},
}
})
+4 -170
View File
@@ -8,200 +8,34 @@ export const useTicketsStore = defineStore('tickets', {
fetching: true,
total: 0
}),
getters: {
// treeSelect: (state) => (id = null) => {
// if (id) {
// const foreach = (items) => {
// return items.filter((item) => {
// if (item.key != id) {
// if (item.children)
// item.children = foreach(item.children)
// return true
// } else return false
// })
// }
// return foreach(state.items)
// }
// return state.items
// },
find: (state) => (id) => {
if (id) {
const foreach = (items) => {
const item = items.find(({ key }) => key == id)
if (item) return item
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.children) {
const temp = foreach(item.children)
if (temp) return temp
}
}
}
return foreach(state.items)
}
return '';
}
},
actions: {
async index(page, rows) {
this.fetching = true
const params = { page, rows }
const { status, data } = await axios.get('/ticket', { params })
const { status, data } = await axios.get('/ticket')
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
},
async ticket(id) {
this.fetching = true
const { status, data } = await axios.get(`/ticket/${id}`)
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
},
async close(id){
this.fetching = true
const { status, data } = await axios.patch(`/ticket/close/${id}`, id)
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
},
async reply(form){
this.fetching = true
const { status, data } = await axios.post('/ticket/message', form)
this.fetching = false
return data
if (status === 200) {
const map = (items, parent = null) => {
return items.map((item) => {
const temp = {
key: item._id,
label: item.name,
data: item
}
if (parent)
temp.data.parentCategoryID = { [parent]: true }
if (item.sub) {
temp.children = map(item.sub, temp.key)
delete temp.data.sub
}
return temp;
});
}
this.items = map(data)
// this.items = data.items
// this.total = data.total
}
},
async create(id) {
const result = await axios.post('/courseCategory', {
name: id.value
})
return result;
},
async store(form) {
const result = await axios.post('/admin/categories', form)
if (result.status === 201)
this.items.unshift(result.data)
return result;
},
async update(id, form) {
const result = await axios.put(`/admin/categories/${id}`, form)
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == id)
this.items[index] = result.data
}
return result;
},
async remove(id) {
const result = await axios.delete(`/courseCategory/${id}`)
if (result.status === 200) {
const index = this.items.findIndex(({ _id }) => _id == id)
this.items.splice(index, 1)
}
return result;
},
}
})
-14
View File
@@ -1,14 +0,0 @@
export const formatSize = (bytes) => {
const k = 1024;
const dm = 1;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
if (bytes === 0) {
return `0 ${sizes[0]}`;
}
const i = Math.floor(Math.log(bytes) / Math.log(k));
const formattedSize = parseFloat((bytes / Math.pow(k, i)).toFixed(dm));
return `${formattedSize} ${sizes[i]}`;
};
-10
View File
@@ -1,10 +0,0 @@
export const treeTogglerRTL = (selector) => {
try {
document.querySelectorAll(selector).forEach(element => {
element.style['margin-right'] = element.style['margin-left']
element.style['margin-left'] = null
});
} catch (error) {
}
}
+77 -99
View File
@@ -1,109 +1,87 @@
<template>
<section class="flex flex-col gap-4">
<Panel :header="$t('اطلاعات صفحه ی درباره ما')">
<div class="grid grid-cols-2 gap-7">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="data.expertise" />
<label>{{ $t("تعداد حوزه های تخصصی") }}</label>
</FloatLabel>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="data.student" />
<label>{{ $t("تعداد دانشجو ها") }}</label>
</FloatLabel>
</div>
<section class="flex flex-col gap-4">
<Panel :header="$t('اطلاعات صفحه ی درباره ما')">
<div class="grid grid-cols-2 gap-7">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="data.expertise" />
<label>{{ $t("تعداد حوزه های تخصصی") }}</label>
</FloatLabel>
</div>
<div class="grid grid-cols-2 gap-7 mt-7">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText v-model="data.title" class="w-full" />
<label>{{ $t("تعداد عناوین آموزشی") }}</label>
</FloatLabel>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="data.hours" />
<label>{{ $t("تعداد ساعات آموزشی") }}</label>
</FloatLabel>
</div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="data.student" />
<label>{{ $t("تعداد دانشجو ها") }}</label>
</FloatLabel>
</div>
<div class="flex pt-4 justify-end">
<Button
:label="$t('edit')"
icon="pi pi-save"
severity="success"
:loading="saving"
@click="save(data)"
/>
</div>
<div class="grid grid-cols-2 gap-7 mt-7">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText v-model="data.title" class="w-full" />
<label>{{ $t("تعداد عناوین آموزشی") }}</label>
</FloatLabel>
</div>
</Panel>
</section>
</template>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="data.hours" />
<label>{{ $t("تعداد ساعات آموزشی") }}</label>
</FloatLabel>
</div>
</div>
<div class="flex pt-4 justify-end">
<Button
:label="$t('edit')"
icon="pi pi-save"
severity="success"
:loading="saving"
@click="save(data)"
/>
</div>
</Panel>
</section>
</template>
<script setup>
import { jDate } from "@/utils/jDate";
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
import {
inject,
defineAsyncComponent,
watchEffect,
ref,
watch,
nextTick,
reactive,
} from "vue";
import { useStaticsStore } from "@/stores/statics";
import { useRoute } from "vue-router";
const data = reactive({
expertise: 0,
student: 0,
title: 0,
hours: 0,
});
const saving = ref(false);
const save = async (data) => {
saving.value = true;
const result = await store.edit(data);
if (result.data === true){
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("اطلاعات با موفقیت ویرایش شد"),
import { inject, watchEffect, ref, reactive } from "vue";
import { useStaticsStore } from "@/stores/statics";
const { toast, t } = inject("service");
const store = useStaticsStore();
const data = reactive({
expertise: 0,
student: 0,
title: 0,
hours: 0,
});
const saving = ref(false);
const save = async (data) => {
saving.value = true;
const result = await store.edit(data);
}
else
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: data.msg,
if (result.data === true) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("اطلاعات با موفقیت ویرایش شد"),
});
} else
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: data.msg,
});
saving.value = false;
};
watchEffect(async () => {
await store.index();
data.expertise = store.items.expertise;
data.student = store.items.student;
data.title = store.items.title;
data.hours = store.items.hours;
});
saving.value = false;
};
const expandedKeys = ref({});
const CategoryForm = defineAsyncComponent(() =>
import("../components/CategoryForm.vue")
);
const { dialog, confirm, toast, t } = inject("service");
const store = useStaticsStore();
watchEffect(async () => {
await store.index();
data.expertise = store.items.expertise;
data.student = store.items.student;
data.title = store.items.title;
data.hours = store.items.hours;
});
</script>
</script>
+111 -156
View File
@@ -1,169 +1,124 @@
<template>
<Panel :header="$t('لیست ویدیو های دوره')">
<template #icons>
<Panel :header="$t('لیست ویدیو های دوره')">
<template #icons>
<Button
icon="pi pi-plus"
:label="$t('افزودن ویدیو')"
severity="success"
@click="create()"
/>
</template>
<DataTable :value="videos">
<Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" />
<Column field="description" :header="$t('description')" />
<Column :header="$t('options')" headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button
icon="pi pi-plus"
:label="$t('افزودن ویدیو')"
severity="success"
@click="create()"
icon="pi pi-eye"
rounded
text
severity="secondary"
@click="show(data)"
/>
</template>
<DataTable :value="videos">
<Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" />
<Column field="description" :header="$t('description')" />
<Column :header="$t('options')" headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button icon="pi pi-eye" rounded text severity="secondary"
@click="show(data)" />
<Button
icon="pi pi-pencil"
rounded
text
severity="secondary"
@click="edit(data)"
/>
<Button
@click="remove(data)"
icon="pi pi-trash"
rounded
text
severity="danger"
:loading="data.removing"
/>
</div>
</template>
</Column>
<!-- <Column :field="({ price }) => numberFormat(price)" :header="$t('price')" />
<Column :header="$t('category')" class="whitespace-nowrap">
<template #body="{ data: { category } }">
<span v-if="category.length > 0">
{{ category.slice(-1)[0].name }}
</span>
</template>
</Column>
<Column field="name" :header="$t('reviews')">
<template #body="{ data: { name } }">
<Rating :modelValue="name" readonly :cancel="false" class="[&_svg]:text-yellow-400" />
</template>
</Column>
<Column :header="$t('status')">
<template #body="{ data: { confirmToShow, _id } }">
<Tag v-if="!confirmToShow" value="غیر فعال" severity="danger" @click="accepter(_id,true)"/>
<Tag v-if="confirmToShow" value="فعال" severity="success" @click="accepter(_id,false)"/>
</template>
</Column>
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" class="w-28" />
<Column :field="({ updatedAt }) => jDate(updatedAt)" :header="$t('updatedAt')" class="w-32 text-center" />
<Column headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
<Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(data)" />
<Button icon="pi pi-trash" rounded text severity="danger" :loading="data.removing"
@click="remove(data)" />
</div>
</template>
</Column> -->
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
<Button
icon="pi pi-pencil"
rounded
text
severity="secondary"
@click="edit(data)"
/>
<Button
@click="remove(data)"
icon="pi pi-trash"
rounded
text
severity="danger"
:loading="data.removing"
/>
</div>
</template>
<!-- <template #footer>
<Pagination :total="store.total" />
</template> -->
</DataTable>
<!-- <Galleria
v-model:visible="galleryVes"
:value="gallery"
:numVisible="5"
containerClass="w-1/2"
circular
fullScreen
showItemNavigators
:showThumbnailNavigators="false"
> -->
</Panel>
</template>
</Column>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
</DataTable>
</Panel>
</template>
<script setup>
const { toast, dialog, confirm, t } = inject('service')
import {
ref,
watchEffect,
reactive,
defineAsyncComponent,
inject,
watch,
} from "vue";
import { useRoute } from "vue-router";
import { useHeadlinesStore } from "@/stores/Headlines";
const store = useHeadlinesStore();
const route = useRoute();
const videos = ref();
const { dialog, confirm, t } = inject("service");
const HeadlineDetail = defineAsyncComponent(() =>
import("../components/HeadlineDetail.vue")
);
const AddVideo = defineAsyncComponent(() =>
import("../components/AddVideo.vue")
);
const EditVideo = defineAsyncComponent(() =>
import("../components/EditVideo.vue")
);
import { ref, watchEffect, reactive, defineAsyncComponent, inject, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useHeadlinesStore } from "@/stores/Headlines"
const store = useHeadlinesStore()
const route = useRoute()
const videos = ref()
const HeadlineDetail = defineAsyncComponent(() => import('../components/HeadlineDetail.vue'));
const AddVideo = defineAsyncComponent(() => import('../components/AddVideo.vue'));
const EditVideo = defineAsyncComponent(() => import('../components/EditVideo.vue'));
watchEffect(async() => {
videos.value = await store.index(route.params.id)
})
watch(videos, async()=>{
videos.value = await store.index(route.params.id)
})
watchEffect(async () => {
videos.value = await store.index(route.params.id);
});
watch(videos, async () => {
videos.value = await store.index(route.params.id);
});
const show = (product) => {
dialog.open(HeadlineDetail, {
props: { modal: true, closable: true, header: t('پیش نمایش ویدیو') },
data: {
product
}
});
}
const router = useRouter()
const create = () =>{
// router.push('/videos/create')
dialog.open(AddVideo, {
props: { modal: true, closable: true, header: t('افزودن ویدیو') },
data: {
produc : route.params.id
}
});
}
dialog.open(HeadlineDetail, {
props: { modal: true, closable: true, header: t("پیش نمایش ویدیو") },
data: {
product,
},
});
};
const create = () => {
dialog.open(AddVideo, {
props: { modal: true, closable: true, header: t("افزودن ویدیو") },
data: {
produc: route.params.id,
},
});
};
const edit = (product) => {
dialog.open(EditVideo, {
props: { modal: true, closable: true, header: t('ویرایش ویدیو') },
data: {
product
}
});
}
const remove = async(data) => {
confirm.require({
message: t('Are you sure you want to proceed?'),
header: t('Danger Zone'),
icon: 'pi pi-info-circle',
acceptClass: 'p-button-danger p-button-sm',
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
rejectLabel: t('cancel'),
acceptLabel: t('beOmitted'),
defaultFocus: 'reject',
accept: async () => {
await store.delete(data.id)
await store.index(route.params.id)
// if (status === 200)
// toast.add({
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
// });
// else
// toast.add({
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
// });
},
});
}
dialog.open(EditVideo, {
props: { modal: true, closable: true, header: t("ویرایش ویدیو") },
data: {
product,
},
});
};
const remove = async (data) => {
confirm.require({
message: t("Are you sure you want to proceed?"),
header: t("Danger Zone"),
icon: "pi pi-info-circle",
acceptClass: "p-button-danger p-button-sm",
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
rejectLabel: t("cancel"),
acceptLabel: t("beOmitted"),
defaultFocus: "reject",
accept: async () => {
await store.delete(data.id);
await store.index(route.params.id);
},
});
};
</script>
+2 -5
View File
@@ -32,22 +32,20 @@
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
</DataTable>
</Panel>
</template>
<script setup>
import { useUsersStore } from "@/stores/users";
import { inject, reactive, defineAsyncComponent, ref, watchEffect, watch } from "vue";
import { inject, defineAsyncComponent, ref, watchEffect, watch } from "vue";
const store = useUsersStore();
const admins = ref()
const UserDetails = defineAsyncComponent(() =>
import("../components/UserDetails.vue")
);
const { dialog, t } = inject("service");
const show = (user) => {
dialog.open(UserDetails, {
props: { modal: true, closable: true, header: t("userDetails") },
@@ -60,7 +58,6 @@ watchEffect(async () => {
});
watch(admins, async()=>{
await store.index();
admins.value = store.items.user.filter(item => item.roles[1] === 'Admin' );
})
</script>
-22
View File
@@ -1,7 +1,6 @@
<template>
<Panel :header="$t('پاسخ به نظرات')">
<div v-for="item in data.comments" :key="item.id">
<div class="card mb-8">
<Panel toggleable>
<template #header>
@@ -31,10 +30,6 @@
<p v-for="answr in item.replies" :key="answr.id" class="border rounded-md ms-[60px] mt-2 p-2">{{answr.text}}</p>
</Panel>
</div>
</div>
<Textarea v-model="value" placeholder="پاسخ..." class="w-full" rows="5" />
@@ -49,10 +44,6 @@
</div>
</Panel>
</template>
@@ -72,10 +63,8 @@ const store = useProductsStore()
const commentStore = useCommentStore()
const route = useRoute()
const data = ref([])
const menu = ref(null);
const toast = useToast();
const router = useRouter();
const replyTarget = ref(null)
const value = ref(null)
const reply = (id) =>{
@@ -86,14 +75,11 @@ const sendReply = async() =>{
}
if (value.value === null || value.value === '') {
}
const rep = await commentStore.reply(route.params.id, {
text: value.value,
parent: replyTarget.value
})
}
const items = ref([
{
@@ -113,14 +99,6 @@ const items = ref([
}
]);
const toggle = (event) => {
menu.value.toggle(event);
};
const save = () => {
toast.add({ severity: 'success', summary: 'Success', detail: 'Data Saved', life: 3000 });
};
watchEffect(async() => {
data.value = await store.edit(route.params.id)
+1 -19
View File
@@ -19,8 +19,6 @@
<Column headerClass="w-32" :header="$t('options')">
<template #body="{ data }">
<div class="flex justify-start">
<!-- <Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(categories)" />
-->
<Button
icon="pi pi-trash"
rounded
@@ -37,26 +35,19 @@
<script setup>
import { jDate } from "@/utils/jDate";
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
import {
inject,
defineAsyncComponent,
watchEffect,
ref,
watch,
nextTick,
watch
} from "vue";
import { useCategoriesStore } from "@/stores/categoreis";
import { useRoute } from "vue-router";
const categories = ref();
// const expandedKeys = ref({});
const CategoryForm = defineAsyncComponent(() =>
import("../components/CategoryForm.vue")
);
const { dialog, confirm, toast, t } = inject("service");
const store = useCategoriesStore();
const create = async() => {
@@ -66,14 +57,6 @@ const create = async() => {
});
await store.index(1,100)
};
// const edit = (category) => {
// dialog.open(CategoryForm, {
// props: { modal: true, closable: false, header: t('editCategory') },
// data: { categories: store.items, category }
// });
// };
const remove = (data) => {
confirm.require({
message: t("Are you sure you want to proceed?"),
@@ -115,4 +98,3 @@ watch(categories ,async () => {
});
</script>
<style lang="scss" scoped></style>
+101 -142
View File
@@ -1,66 +1,36 @@
<template>
<Panel :header="$t('comments')">
<DataTable :value="store.items">
<Column class="w-16" :header="$t('image')">
<template #body="{ data: { image } }">
<Avatar
:image="image"
size="large"
shape="circle"
class="transition-transform hover:scale-150"
/>
</template>
</Column>
<Column field="name" :header="$t('name')" />
<Column
:field="({ categories }) => categories[0].name"
:header="$t('category')"
/>
<Column field="price" :header="$t('price')" />
<Column
:field="({ createdAt }) => jDate(createdAt)"
:header="$t('createdAt')"
class="w-28"
/>
<Column
:field="({ updatedAt }) => jDate(updatedAt)"
:header="$t('updatedAt')"
class="w-32 text-center"
/>
<Column field="averageRating" :header="$t('امتیاز')" />
<Column :header="$t('مشاهده نظرات')" headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
<Panel :header="$t('comments')">
<DataTable :value="store.items.courses">
<Column class="w-16" :header="$t('image')">
<template #body="{ data: { image } }">
<Avatar
:image="image"
size="large"
shape="circle"
class="transition-transform hover:scale-150"
/>
</template>
</Column>
<Column field="name" :header="$t('name')" />
<Column
:field="({ categories }) => categories[0].name"
:header="$t('category')"
/>
</div>
</template>
</Column>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
<!-- <template #footer>
<Pagination :total="store.total" />
</template> -->
</DataTable>
<Galleria
v-model:visible="galleryVes"
:value="gallery"
:numVisible="5"
containerClass="w-1/2"
circular
fullScreen
showItemNavigators
:showThumbnailNavigators="false"
>
<template #item="{ item: { itemImageSrc, alt } }">
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
</template>
</Galleria>
<Column headerClass="w-32">
<Column :field="({ price }) => numberFormat(price)" :header="$t('price')" />
<Column
:field="({ createdAt }) => jDate(createdAt)"
:header="$t('createdAt')"
class="w-28"
/>
<Column
:field="({ updatedAt }) => jDate(updatedAt)"
:header="$t('updatedAt')"
class="w-32 text-center"
/>
<Column field="averageRating" :header="$t('امتیاز')" />
<Column :header="$t('مشاهده نظرات')" headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button
@@ -73,89 +43,78 @@
</div>
</template>
</Column>
</Panel>
</template>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
<template #footer>
<Pagination :total="store.items.count" />
</template>
</DataTable>
<Column headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button
icon="pi pi-eye"
rounded
text
severity="secondary"
@click="show(data)"
/>
</div>
</template>
</Column>
</Panel>
</template>
<script setup>
import { useProductsStore } from '@/stores/products';
import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat';
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const ProductForm = defineAsyncComponent(() => import('../components/ProductForm.vue'));
const ProductDetails = defineAsyncComponent(() => import('../components/ProductDetails.vue'));
const { toast, dialog, confirm, t } = inject('service')
var gallery = []
const galleryVes = ref(false)
const openGallery = (images =>{
gallery = []
images.map(image =>{
gallery.push({itemImageSrc:image, alt:"product"})
})
galleryVes.value = true
})
const router = useRouter()
const show = (data) => {
router.push(`/comments/${data._id}`)
}
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
const store = useProductsStore()
const route = useRoute()
const edit = (data) =>{
router.push(data._id)
}
const remove = (data) => {
confirm.require({
message: t('Are you sure you want to proceed?'),
header: t('Danger Zone'),
icon: 'pi pi-info-circle',
acceptClass: 'p-button-danger p-button-sm',
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
rejectLabel: t('cancel'),
acceptLabel: t('beOmitted'),
defaultFocus: 'reject',
accept: async () => {
const deleted = await store.delete(data._id)
await store.index()
}
import { useProductsStore } from "@/stores/products";
import { jDate } from "@/utils/jDate";
import { numberFormat } from "@/utils/numberFormat";
import {
});
}
watchEffect(async() => {
const { page = 1, pageSize = 10 } = route.query
await store.index(page, pageSize)
inject,
watchEffect,
} from "vue";
import { useRoute, useRouter } from "vue-router";
const { confirm, t } = inject("service");
const router = useRouter();
const show = (data) => {
router.push(`/comments/${data._id}`);
};
const store = useProductsStore();
const route = useRoute();
const edit = (data) => {
router.push(data._id);
};
const remove = (data) => {
confirm.require({
message: t("Are you sure you want to proceed?"),
header: t("Danger Zone"),
icon: "pi pi-info-circle",
acceptClass: "p-button-danger p-button-sm",
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
rejectLabel: t("cancel"),
acceptLabel: t("beOmitted"),
defaultFocus: "reject",
accept: async () => {
const deleted = await store.delete(data._id);
await store.index();
},
});
};
watchEffect(async () => {
const { page = 1, pageSize = 10 } = route.query;
await store.index(page, pageSize);
console.log(store);
});
</script>
})
const accepter = async (id,confirmToShow)=>{
const { status, data } = await store.confirm(id, {confirmToShow});
if (status === 200)
toast.add({
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
});
else
toast.add({
severity: 'error', summary: t('error'), detail: data.msg, life: 3000
});
}
</script>
<style lang="scss" scoped></style>
+74 -64
View File
@@ -1,74 +1,84 @@
<template>
<Panel :header="$t('coupons')">
<DataTable :value="store.items.discount">
<Column field="code" :header="$t('کد تخفیف')" />
<Column field="id" :header="$t('id')" />
<Column field="admin.name" :header="$t('ایجاد شده توسط')" />
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" />
<Column :field="({ startDate }) => jDate(startDate)" :header="$t('تاریخ شروع')" />
<Column :field="({ endDate }) => jDate(endDate)" :header="$t('تاریخ انقضا')" />
<Column field="statusText" :header="$t('status')" />
<Column headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button
icon="pi pi-pencil"
rounded
text
severity="secondary"
@click="edit(data)"
/>
<Button
icon="pi pi-eye"
rounded
text
severity="secondary"
@click="show(data)"
/>
</div>
</template>
</Column>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
<!--
<Panel :header="$t('coupons')">
<DataTable :value="store.items.discount">
<Column field="code" :header="$t('کد تخفیف')" />
<Column field="id" :header="$t('id')" />
<Column field="admin.name" :header="$t('ایجاد شده توسط')" />
<Column
:field="({ createdAt }) => jDate(createdAt)"
:header="$t('createdAt')"
/>
<Column
:field="({ startDate }) => jDate(startDate)"
:header="$t('تاریخ شروع')"
/>
<Column
:field="({ endDate }) => jDate(endDate)"
:header="$t('تاریخ انقضا')"
/>
<Column field="statusText" :header="$t('status')" />
<Column headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button
icon="pi pi-pencil"
rounded
text
severity="secondary"
@click="edit(data)"
/>
<Button
icon="pi pi-eye"
rounded
text
severity="secondary"
@click="show(data)"
/>
</div>
</template>
</Column>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
<template #footer>
<Pagination :total="store.total" />
</template> -->
</DataTable>
</Panel>
<Pagination :total="store.items.count" />
</template>
</DataTable>
</Panel>
</template>
<script setup>
import {useDiscountStore} from "../stores/discounts"
import { useDiscountStore } from "../stores/discounts";
import { jDate } from "@/utils/jDate";
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import {
defineAsyncComponent,
inject,
watchEffect,
} from "vue";
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const { dialog, t } = inject("service");
const CouponDetails = defineAsyncComponent(() =>
import("../components/CouponDetails.vue")
);
const { toast, dialog, confirm, t } = inject('service')
const CouponDetails = defineAsyncComponent(() => import('../components/CouponDetails.vue'));
const store = useDiscountStore()
const store = useDiscountStore();
const show = (coupon) => {
dialog.open(CouponDetails, {
props: { modal: true, closable: true, header: t('جزئیات تخفیف') },
data: {
coupon
}
});
}
const router = useRouter()
const edit = (data) =>{
router.push(`/coupon/${data.id}`)
}
watchEffect( async() =>{
await store.index()
})
dialog.open(CouponDetails, {
props: { modal: true, closable: true, header: t("جزئیات تخفیف") },
data: {
coupon,
},
});
};
const router = useRouter();
const edit = (data) => {
router.push(`/coupon/${data.id}`);
};
watchEffect(async () => {
const { page = 1, pageSize = 10 } = route.query;
await store.index(page, pageSize);
});
</script>
+67 -144
View File
@@ -8,7 +8,7 @@
@click="create()"
/>
</template>
<DataTable :value="store.items">
<DataTable :value="store.items.courses">
<Column class="w-16" :header="$t('image')">
<template #body="{ data: { image } }">
<Avatar
@@ -25,7 +25,10 @@
:header="$t('category')"
/>
<Column field="price" :header="$t('price')" />
<Column
:field="({ price }) => numberFormat(price)"
:header="$t('price')"
/>
<Column
:field="({ createdAt }) => jDate(createdAt)"
:header="$t('createdAt')"
@@ -41,7 +44,13 @@
<Column :header="$t('options')" headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
<Button
icon="pi pi-eye"
rounded
text
severity="secondary"
@click="show(data)"
/>
<Button
icon="pi pi-pencil"
rounded
@@ -49,72 +58,27 @@
severity="secondary"
@click="edit(data)"
/>
<Button
@click="remove(data)"
@click="remove(data)"
icon="pi pi-trash"
rounded
text
severity="danger"
:loading="data.removing"
/>
</div>
</template>
</Column>
<!-- <Column :field="({ price }) => numberFormat(price)" :header="$t('price')" />
<Column :header="$t('category')" class="whitespace-nowrap">
<template #body="{ data: { category } }">
<span v-if="category.length > 0">
{{ category.slice(-1)[0].name }}
</span>
</template>
</Column>
<Column field="name" :header="$t('reviews')">
<template #body="{ data: { name } }">
<Rating :modelValue="name" readonly :cancel="false" class="[&_svg]:text-yellow-400" />
</template>
</Column>
<Column :header="$t('status')">
<template #body="{ data: { confirmToShow, _id } }">
<Tag v-if="!confirmToShow" value="غیر فعال" severity="danger" @click="accepter(_id,true)"/>
<Tag v-if="confirmToShow" value="فعال" severity="success" @click="accepter(_id,false)"/>
</template>
</Column>
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" class="w-28" />
<Column :field="({ updatedAt }) => jDate(updatedAt)" :header="$t('updatedAt')" class="w-32 text-center" />
<Column headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button icon="pi pi-eye" rounded text severity="secondary" @click="show(data)" />
<Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(data)" />
<Button icon="pi pi-trash" rounded text severity="danger" :loading="data.removing"
@click="remove(data)" />
</div>
</template>
</Column> -->
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
<!-- <template #footer>
<template #footer>
<Pagination :total="store.items.count" />
</template> -->
</DataTable>
<Galleria
v-model:visible="galleryVes"
:value="gallery"
:numVisible="5"
containerClass="w-1/2"
circular
fullScreen
showItemNavigators
:showThumbnailNavigators="false"
>
<template #item="{ item: { itemImageSrc, alt } }">
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
</template>
</Galleria>
</DataTable>
<Column headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
@@ -132,104 +96,63 @@
</template>
<script setup>
import { useProductsStore } from '@/stores/products';
import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat';
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useProductsStore } from "@/stores/products";
import { jDate } from "@/utils/jDate";
import { numberFormat } from "@/utils/numberFormat";
import {
defineAsyncComponent,
inject,
onBeforeMount,
ref,
watchEffect,
} from "vue";
import { useRoute, useRouter } from "vue-router";
const ProductForm = defineAsyncComponent(() => import('../components/ProductForm.vue'));
const ProductDetails = defineAsyncComponent(() => import('../components/ProductDetails.vue'));
const ProductDetails = defineAsyncComponent(() =>
import("../components/ProductDetails.vue")
);
const { toast, dialog, confirm, t } = inject('service')
const { toast, dialog, confirm, t } = inject("service");
var gallery = []
const galleryVes = ref(false)
const openGallery = (images =>{
gallery = []
images.map(image =>{
gallery.push({itemImageSrc:image, alt:"product"})
})
galleryVes.value = true
})
const show = (product) => {
dialog.open(ProductDetails, {
props: { modal: true, closable: true, header: t('productDetails') },
data: {
product
}
});
}
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
const store = useProductsStore()
const route = useRoute()
const router = useRouter()
const edit = (data) =>{
router.push(data._id)
}
dialog.open(ProductDetails, {
props: { modal: true, closable: true, header: t("productDetails") },
data: {
product,
},
});
};
const store = useProductsStore();
const route = useRoute();
const router = useRouter();
const edit = (data) => {
router.push(data._id);
};
const remove = (data) => {
confirm.require({
message: t("Are you sure you want to proceed?"),
header: t("Danger Zone"),
icon: "pi pi-info-circle",
acceptClass: "p-button-danger p-button-sm",
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
rejectLabel: t("cancel"),
acceptLabel: t("beOmitted"),
defaultFocus: "reject",
accept: async () => {
const deleted = await store.delete(data._id);
confirm.require({
message: t('Are you sure you want to proceed?'),
header: t('Danger Zone'),
icon: 'pi pi-info-circle',
acceptClass: 'p-button-danger p-button-sm',
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
rejectLabel: t('cancel'),
acceptLabel: t('beOmitted'),
defaultFocus: 'reject',
accept: async () => {
const deleted = await store.delete(data._id)
await store.index()
}
// product.removing = true
await store.index();
},
});
};
// const { status, data } = await store.remove(product._id);
// product.removing = false
// if (status === 200)
// toast.add({
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
// });
// else
// toast.add({
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
// });
// },
});
}
const create = () => router.push('/create')
watchEffect(async() => {
const { page = 1, pageSize = 10 } = route.query
await store.index(page, pageSize)
})
const accepter = async (id,confirmToShow)=>{
const { status, data } = await store.confirm(id, {confirmToShow});
if (status === 200)
toast.add({
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
});
else
toast.add({
severity: 'error', summary: t('error'), detail: data.msg, life: 3000
});
}
const create = () => router.push("/create");
watchEffect(async () => {
const { page = 1, pageSize = 10 } = route.query;
await store.index(page, pageSize);
console.log(store.items);
});
</script>
<style lang="scss" scoped></style>
+4 -121
View File
@@ -23,7 +23,6 @@
:options="['انگلیسی', 'فارسی']"
class="w-full"
/>
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
<label>{{ $t("زبان آموزش") }}</label>
</FloatLabel>
</div>
@@ -139,22 +138,7 @@
</div>
</div>
<!--
<div class="flex flex-col gap-1">
<InputGroup>
<FloatLabel>
<Dropdown
:options="cities"
:optionLabel="cities.name"
:optionValue="cities.name"
class="w-full"
>
</Dropdown>
<label>{{ $t("brand") }}</label>
</FloatLabel>
</InputGroup>
</div> -->
<div class="flex pt-4 justify-end">
<Button
@@ -179,16 +163,12 @@
<script setup>
import Listbox from "primevue/listbox";
import { jDate } from "@/utils/jDate";
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
import {
inject,
defineAsyncComponent,
watchEffect,
ref,
watch,
nextTick,
reactive,
} from "vue";
@@ -231,10 +211,10 @@ const selectLevel = () => {
const selectLanguage = () => {
newCourse.language = language.value;
};
const selectedCity = ref();
const cities = ref();
const teachers = ref();
const levels = reactive(["آسان", "متوسط", "سخت"]);
const newCourse = reactive({
name: "",
description: "",
@@ -332,13 +312,8 @@ const create = async (newCourse) => {
};
const expandedKeys = ref({});
const CategoryForm = defineAsyncComponent(() =>
import("../components/CategoryForm.vue")
);
const { dialog, confirm, toast, t } = inject("service");
const { toast, t } = inject("service");
const store = useStaticsStore();
@@ -347,95 +322,3 @@ watchEffect(async () => {
});
</script>
<!--
<script setup>
import { useUploaderStore } from "@/stores/uploader";
import FirstStep from "../components/ProductFormSteps/FirstStep.vue";
import SecondStep from "../components/ProductFormSteps/SecondStep.vue";
import ThirdStep from "../components/ProductFormSteps/ThirdStep.vue";
import { inject, provide, reactive, ref } from "vue";
import { useProductsStore } from "@/stores/products";
const dialog = inject("dialogRef");
const { toast, t } = inject("service");
// const { product } = dialog.data
//const form = reactive(Object.assign({}, product || {}));
//console.log(form);
const errors = reactive({});
const saving = ref(false);
provide("data", { errors, saving });
const store = useProductsStore();
const uploader = useUploaderStore();
const save = async () => {
saving.value = true;
//Upload File
if (typeof form.coverImage === "object") {
const { status, data } = await uploader.single(form.coverImage);
if (status === 201) form.coverImage = data.url;
else
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: data.msg,
});
}
//upload images
const files = form.images.filter((image) => typeof image === "object");
if (files.length > 0) {
const { status, data } = await uploader.bulk(files);
if (status === 201)
form.images = form.images.map((image) => {
if (typeof image === "object") return data.urls.shift();
return image;
});
else
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: data.msg,
});
}
//Create or Update
// const { status, data } = !!product
// ? await store.update(product._id, form)
// : await store.store(form);
// saving.value = false;
// if (status === 200 || status === 201) {
// toast.add({
// life: 2000,
// severity: "success",
// summary: t("successful"),
// detail: t(!!category ? "updateSuccessfully" : "storeSuccessfully"),
// });
// dialog.value.close();
// } else if (status === 422) Object.assign(errors, data);
// else
// toast.add({
// severity: "error",
// life: 2000,
// detail: data.msg,
// summary: t("error"),
// });
};
</script>
<style lang="scss" scoped></style> -->
+4 -16
View File
@@ -69,12 +69,7 @@
</FloatLabel>
</div>
<!-- <div class="flex flex-col gap-1 my-custom-container inline">
<FloatLabel>
<InputText class="w-full" />
<label>{{ $t("تخفیف برای همه کاربران") }}</label>
</FloatLabel>
</div> -->
</div>
<div class="flex pt-4 justify-end">
@@ -93,16 +88,11 @@
<script setup>
import { useProductsStore } from "@/stores/products";
import { useDiscountStore } from "@/stores/discounts";
import { ref, watchEffect, reactive, inject } from "vue";
import moment from "moment-jalaali";
import { ref, reactive, inject } from "vue";
import { useRouter } from "vue-router";
const { dialog, confirm, toast, t } = inject("service");
const date = ref(null);
const date2 = ref(null);
const noe = ref("مبلغ");
const store = useProductsStore();
const { toast, t } = inject("service");
const discount = useDiscountStore();
const data = ref(null);
const type = ref(null);
const router = useRouter()
const setType = (val) => {
@@ -113,8 +103,6 @@ const setType = (val) => {
}
};
//const { dialog, confirm, toast, t } = inject("service");
const coupon = reactive({
startDate: "",
endDate: "",
+2 -24
View File
@@ -23,7 +23,6 @@
:options="['انگلیسی', 'فارسی']"
class="w-full"
/>
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
<label>{{ $t("زبان آموزش") }}</label>
</FloatLabel>
</div>
@@ -140,22 +139,7 @@
</div>
</div>
<!--
<div class="flex flex-col gap-1">
<InputGroup>
<FloatLabel>
<Dropdown
:options="cities"
:optionLabel="cities.name"
:optionValue="cities.name"
class="w-full"
>
</Dropdown>
<label>{{ $t("brand") }}</label>
</FloatLabel>
</InputGroup>
</div> -->
<div class="flex pt-4 justify-end">
<Button
@@ -180,10 +164,7 @@
<script setup>
import { useProductsStore } from "@/stores/products";
import { ref, watchEffect, reactive, inject } from "vue";
import { useStaticsStore } from "@/stores/statics";
import { useUploaderStore } from "@/stores/uploader";
import { useCategoriesStore } from "@/stores/categoreis";
import { useUsersStore } from "../stores/users";
const categoriesStore = useCategoriesStore();
@@ -192,10 +173,8 @@ const uploadStore = useUploaderStore();
const userStore = useUsersStore();
import { useRouter, useRoute } from "vue-router";
const route = useRoute()
const level = ref(null);
const selectedCategory = ref();
const selectedteacher = ref();
const language = ref("");
const saving = ref(false);
const teachers = ref();
const creating = ref(false);
@@ -255,7 +234,6 @@ watchEffect(async () => {
course.totalFileSize = data.value.course.totalFileSize;
});
// const levels = reactive(["آسان", "متوسط", "سخت"]);
const chooseImage = async (val) => {
saving.value = true;
@@ -278,7 +256,7 @@ const chooseImage = async (val) => {
});
}
};
const { dialog, confirm, toast, t } = inject("service");
const { toast, t } = inject("service");
const router = useRouter()
const editCourse = async (data) => {
creating.value = true;
+1 -2
View File
@@ -86,7 +86,6 @@
<script setup>
import { useDiscountStore } from "@/stores/discounts";
import { ref, watchEffect, reactive, inject } from "vue";
import moment from "moment-jalaali";
import { useRoute, useRouter } from "vue-router";
const date = ref(null);
const date2 = ref(null);
@@ -112,7 +111,7 @@ const discount = reactive({
allUsers: true,
});
const { dialog, confirm, toast, t } = inject("service");
const { toast, t } = inject("service");
const route = useRoute();
const saving = ref(false);
+1 -1
View File
@@ -59,7 +59,7 @@
import { defineAsyncComponent, inject, ref, watchEffect } from "vue";
import { useFaqStore } from "@/stores/faq";
const store = useFaqStore();
const { toast, dialog, confirm, t } = inject('service');
const { dialog, t } = inject('service');
const FaqDetails = defineAsyncComponent(() => import('../components/FaqDetails.vue'));
const AddFaq = defineAsyncComponent(() => import('../components/AddFaq.vue'));
const removing = ref(false)
+1 -12
View File
@@ -66,19 +66,13 @@
</template>
<script setup>
import { jDate } from "@/utils/jDate";
import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
import {
inject,
defineAsyncComponent,
watchEffect,
ref,
watch,
nextTick,
reactive,
} from "vue";
import { useStaticsStore } from "@/stores/statics";
import { useRoute } from "vue-router";
const data = reactive({
header: "",
@@ -113,13 +107,8 @@ async function save(data) {
saving.value = false;
}
// const expandedKeys = ref({});
// const CategoryForm = defineAsyncComponent(() =>
// import("../components/CategoryForm.vue")
// );
const { dialog, confirm, toast, t } = inject("service");
const { toast, t } = inject("service");
const store = useStaticsStore();
+1 -13
View File
@@ -56,7 +56,7 @@
</Card>
</template>
<script setup>
import { inject, reactive, ref } from "vue";
import { inject, ref } from "vue";
import { useAdminsStore } from "@/stores/admins";
import { useRouter } from "vue-router";
const { toast, t } = inject("service");
@@ -109,17 +109,5 @@ const checkOtp = async () => {
}
};
// // setTimeout(() => router.push('/'), 300);
// console.log(data);
// } else if (status === 400)
// // console.log('400', data.message[0])
// errors.value = data.message[0]
// else
// {
// console.log(data);
// }
// toast.add({
// severity: 'error', life: 2000, detail: data.msg, summary: t('error')
// })
</script>
-5
View File
@@ -1,5 +0,0 @@
<template>
<div>
اخبار
</div>
</template>
-3
View File
@@ -1,3 +0,0 @@
<template>
<h1>محصولات</h1>
</template>
-1
View File
@@ -50,7 +50,6 @@ const messages = ref([]);
const route = useRoute();
const send = async (value) => {
const sended = await store.reply({
content: value,
ticketId: route.params.id,
+3 -14
View File
@@ -24,14 +24,7 @@
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
<template #footer>
<!--
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
-->
<Pagination :total="store.total" />
</template>
</DataTable>
</Panel>
</template>
@@ -39,25 +32,21 @@
<script setup>
import { ref, watchEffect } from "vue";
import {useTicketsStore} from "@/stores/tickets"
import { useRoute, useRouter } from "vue-router";
import { useRouter } from "vue-router";
import {jDate} from "@/utils/jDate"
const store = useTicketsStore()
const router = useRouter()
const data = ref([])
const route = useRoute()
const chat = (data) =>{
router.push(`/tickets/${data.id}`)
}
const closeTicket = async(data) =>{
// console.log(data.id);
const closess = await store.close(data.id)
await store.index()
}
watchEffect(async() =>{
const { page = 1, pageSize = 10 } = route.query;
data.value = await store.index(page, pageSize)
data.value = await store.index()
})
</script>
+1 -11
View File
@@ -34,10 +34,6 @@
</template>
<template #footer>
<!--
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
-->
<Pagination :total="store.items.count" />
</template>
</DataTable>
@@ -48,8 +44,6 @@
<script setup>
import Pagination from "@/components/Pagination.vue";
import { useUsersStore } from "@/stores/users";
import { jDate } from "@/utils/jDate";
import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue";
import { useRoute } from "vue-router";
@@ -61,12 +55,8 @@ const { dialog, t } = inject("service");
const store = useUsersStore();
const options = reactive([
{ label: t("customers"), value: false },
{ label: t("sellers"), value: true },
]);
const confirmToSales = ref(false);
const show = (user) => {
dialog.open(UserDetails, {
+165 -164
View File
@@ -1,76 +1,39 @@
<template>
<Panel :header="$t('مدیریت ویدیو ها')">
<DataTable :value="store.items">
<Column class="w-16" :header="$t('image')">
<template #body="{ data: { image } }">
<Avatar
:image="image"
size="large"
shape="circle"
class="transition-transform hover:scale-150"
/>
</template>
</Column>
<Column field="name" :header="$t('name')" />
<Column
:field="({ categories }) => categories[0].name"
:header="$t('category')"
/>
<Column field="price" :header="$t('price')" />
<Column
:field="({ createdAt }) => jDate(createdAt)"
:header="$t('createdAt')"
class="w-28"
/>
<Column
:field="({ updatedAt }) => jDate(updatedAt)"
:header="$t('updatedAt')"
class="w-32 text-center"
/>
<Column field="averageRating" :header="$t('امتیاز')" />
<Column :header="$t('options')" headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button icon="pi pi-eye" rounded text severity="secondary"
@click="show(data)" />
<Button
icon="pi pi-video"
rounded
text
severity="secondary"
@click="edit(data)"
/>
</div>
</template>
</Column>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
<Panel :header="$t('مدیریت ویدیو ها')">
<DataTable :value="store.items.courses">
<Column class="w-16" :header="$t('image')">
<template #body="{ data: { image } }">
<Avatar
:image="image"
size="large"
shape="circle"
class="transition-transform hover:scale-150"
/>
</template>
<template #footer>
<Pagination :total="store.total" />
</template>
</DataTable>
<Galleria
v-model:visible="galleryVes"
:value="gallery"
:numVisible="5"
containerClass="w-1/2"
circular
fullScreen
showItemNavigators
:showThumbnailNavigators="false"
>
<template #item="{ item: { itemImageSrc, alt } }">
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
</template>
</Galleria>
<Column headerClass="w-32">
</Column>
<Column field="name" :header="$t('name')" />
<Column
:field="({ categories }) => categories[0].name"
:header="$t('category')"
/>
<Column
:field="({ price }) => numberFormat(price)"
:header="$t('price')"
/>
<Column
:field="({ createdAt }) => jDate(createdAt)"
:header="$t('createdAt')"
class="w-28"
/>
<Column
:field="({ updatedAt }) => jDate(updatedAt)"
:header="$t('updatedAt')"
class="w-32 text-center"
/>
<Column field="averageRating" :header="$t('امتیاز')" />
<Column :header="$t('options')" headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button
@@ -80,111 +43,149 @@
severity="secondary"
@click="show(data)"
/>
<Button
icon="pi pi-video"
rounded
text
severity="secondary"
@click="edit(data)"
/>
</div>
</template>
</Column>
</Panel>
</template>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
<template #footer>
<Pagination :total="store.total" />
</template>
</DataTable>
<Galleria
v-model:visible="galleryVes"
:value="gallery"
:numVisible="5"
containerClass="w-1/2"
circular
fullScreen
showItemNavigators
:showThumbnailNavigators="false"
>
<template #item="{ item: { itemImageSrc, alt } }">
<img :src="itemImageSrc" :alt="alt" class="w-full block" />
</template>
</Galleria>
<Column headerClass="w-32">
<template #body="{ data }">
<div class="flex justify-end">
<Button
icon="pi pi-eye"
rounded
text
severity="secondary"
@click="show(data)"
/>
</div>
</template>
</Column>
</Panel>
</template>
<script setup>
import { useProductsStore } from '@/stores/products';
import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat';
import { defineAsyncComponent, inject, onBeforeMount, ref, watchEffect } from 'vue';
import { useRoute, useRouter } from 'vue-router';
const ProductForm = defineAsyncComponent(() => import('../components/ProductForm.vue'));
const ProductDetails = defineAsyncComponent(() => import('../components/ProductDetails.vue'));
const { toast, dialog, confirm, t } = inject('service')
var gallery = []
const galleryVes = ref(false)
const openGallery = (images =>{
gallery = []
images.map(image =>{
gallery.push({itemImageSrc:image, alt:"product"})
})
galleryVes.value = true
})
const show = (product) => {
dialog.open(ProductDetails, {
props: { modal: true, closable: true, header: t('productDetails') },
data: {
product
}
});
}
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
const store = useProductsStore()
const route = useRoute()
const router = useRouter()
import { useProductsStore } from "@/stores/products";
import { jDate } from "@/utils/jDate";
import { numberFormat } from "@/utils/numberFormat";
import {
defineAsyncComponent,
inject,
onBeforeMount,
ref,
watchEffect,
} from "vue";
import { useRoute, useRouter } from "vue-router";
const ProductDetails = defineAsyncComponent(() =>
import("../components/ProductDetails.vue")
);
const edit = (data) =>{
router.push(`/videos/${data._id}`)
}
const remove = (data) => {
const { toast, dialog, confirm, t } = inject("service");
confirm.require({
message: t('Are you sure you want to proceed?'),
header: t('Danger Zone'),
icon: 'pi pi-info-circle',
acceptClass: 'p-button-danger p-button-sm',
rejectClass: 'p-button-secondary p-button-outlined p-button-sm',
rejectLabel: t('cancel'),
acceptLabel: t('beOmitted'),
defaultFocus: 'reject',
accept: async () => {
const deleted = await store.delete(data._id)
const show = (product) => {
dialog.open(ProductDetails, {
props: { modal: true, closable: true, header: t("productDetails") },
data: {
product,
},
});
};
await store.index()
}
// product.removing = true
// const { status, data } = await store.remove(product._id);
// product.removing = false
// if (status === 200)
// toast.add({
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
// });
// else
// toast.add({
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
// });
// },
});
}
watchEffect(async() => {
const { page = 1, pageSize = 10 } = route.query
await store.index(page, pageSize)
const store = useProductsStore();
const route = useRoute();
const router = useRouter();
const edit = (data) => {
router.push(`/videos/${data._id}`);
};
const remove = (data) => {
confirm.require({
message: t("Are you sure you want to proceed?"),
header: t("Danger Zone"),
icon: "pi pi-info-circle",
acceptClass: "p-button-danger p-button-sm",
rejectClass: "p-button-secondary p-button-outlined p-button-sm",
rejectLabel: t("cancel"),
acceptLabel: t("beOmitted"),
defaultFocus: "reject",
accept: async () => {
const deleted = await store.delete(data._id);
await store.index();
},
// product.removing = true
// const { status, data } = await store.remove(product._id);
// product.removing = false
// if (status === 200)
// toast.add({
// severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
// });
// else
// toast.add({
// severity: 'error', summary: t('error'), detail: data.msg, life: 3000
// });
// },
});
};
watchEffect(async () => {
const { page = 1, pageSize = 10 } = route.query;
await store.index(page, pageSize);
console.log(store.items);
});
const accepter = async (id, confirmToShow) => {
const { status, data } = await store.confirm(id, { confirmToShow });
})
const accepter = async (id,confirmToShow)=>{
const { status, data } = await store.confirm(id, {confirmToShow});
if (status === 200)
toast.add({
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000
});
toast.add({
severity: "success",
summary: t("successful"),
detail: t("destroySuccessfully"),
life: 3000,
});
else
toast.add({
severity: 'error', summary: t('error'), detail: data.msg, life: 3000
});
}
</script>
toast.add({
severity: "error",
summary: t("error"),
detail: data.msg,
life: 3000,
});
};
</script>
<style lang="scss" scoped></style>
+1 -1
View File
@@ -27,6 +27,6 @@ const store = useDiscountStore();
import { jDate } from "@/utils/jDate";
watchEffect(async () => {
await store.history();
console.log(store.items.discount);
});
</script>