deploy v-1

This commit is contained in:
HAM!DREZA
2024-07-03 21:25:34 +03:30
parent 2b399d3bb1
commit ad7a4b839d
49 changed files with 1636 additions and 953 deletions
+1
View File
@@ -1 +1,2 @@
node_modules/ node_modules/
dist/
+6 -8
View File
@@ -7,20 +7,19 @@ export {}
/* prettier-ignore */ /* prettier-ignore */
declare module 'vue' { declare module 'vue' {
export interface GlobalComponents { export interface GlobalComponents {
AddFaq: typeof import('./src/components/AddFaq.vue')['default']
AddVideo: typeof import('./src/components/AddVideo.vue')['default'] AddVideo: typeof import('./src/components/AddVideo.vue')['default']
Avatar: typeof import('primevue/avatar')['default'] Avatar: typeof import('primevue/avatar')['default']
Badge: typeof import('primevue/badge')['default']
Button: typeof import('primevue/button')['default'] Button: typeof import('primevue/button')['default']
Card: typeof import('primevue/card')['default'] Card: typeof import('primevue/card')['default']
CategoryForm: typeof import('./src/components/CategoryForm.vue')['default'] CategoryForm: typeof import('./src/components/CategoryForm.vue')['default']
Checkbox: typeof import('primevue/checkbox')['default']
Column: typeof import('primevue/column')['default'] Column: typeof import('primevue/column')['default']
copy: typeof import('./src/components/ProductDetails copy.vue')['default']
CouponDetails: typeof import('./src/components/CouponDetails.vue')['default'] CouponDetails: typeof import('./src/components/CouponDetails.vue')['default']
DataTable: typeof import('primevue/datatable')['default'] DataTable: typeof import('primevue/datatable')['default']
Dropdown: typeof import('primevue/dropdown')['default'] Dropdown: typeof import('primevue/dropdown')['default']
Editor: typeof import('primevue/editor')['default'] Editor: typeof import('primevue/editor')['default']
Fieldset: typeof import('primevue/fieldset')['default'] EditVideo: typeof import('./src/components/EditVideo.vue')['default']
FaqDetails: typeof import('./src/components/FaqDetails.vue')['default']
FileUpload: typeof import('primevue/fileupload')['default'] FileUpload: typeof import('primevue/fileupload')['default']
FirstStep: typeof import('./src/components/ProductFormSteps/FirstStep.vue')['default'] FirstStep: typeof import('./src/components/ProductFormSteps/FirstStep.vue')['default']
FloatLabel: typeof import('primevue/floatlabel')['default'] FloatLabel: typeof import('primevue/floatlabel')['default']
@@ -36,26 +35,25 @@ declare module 'vue' {
InputText: typeof import('primevue/inputtext')['default'] InputText: typeof import('primevue/inputtext')['default']
Listbox: typeof import('primevue/listbox')['default'] Listbox: typeof import('primevue/listbox')['default']
Menu: typeof import('primevue/menu')['default'] Menu: typeof import('primevue/menu')['default']
Message: typeof import('primevue/message')['default']
OverlayPanel: typeof import('primevue/overlaypanel')['default'] OverlayPanel: typeof import('primevue/overlaypanel')['default']
Pagination: typeof import('./src/components/Pagination.vue')['default'] Pagination: typeof import('./src/components/Pagination.vue')['default']
Paginator: typeof import('primevue/paginator')['default'] Paginator: typeof import('primevue/paginator')['default']
Panel: typeof import('primevue/panel')['default'] Panel: typeof import('primevue/panel')['default']
PanelMenu: typeof import('primevue/panelmenu')['default'] PanelMenu: typeof import('primevue/panelmenu')['default']
Password: typeof import('primevue/password')['default']
ProductDetails: typeof import('./src/components/ProductDetails.vue')['default'] ProductDetails: typeof import('./src/components/ProductDetails.vue')['default']
ProductForm: typeof import('./src/components/ProductForm.vue')['default'] ProductForm: typeof import('./src/components/ProductForm.vue')['default']
ProgressBar: typeof import('primevue/progressbar')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
SecondStep: typeof import('./src/components/ProductFormSteps/SecondStep.vue')['default'] SecondStep: typeof import('./src/components/ProductFormSteps/SecondStep.vue')['default']
Sidebar: typeof import('primevue/sidebar')['default'] Sidebar: typeof import('primevue/sidebar')['default']
Stepper: typeof import('primevue/stepper')['default']
StepperPanel: typeof import('primevue/stepperpanel')['default']
Tag: typeof import('primevue/tag')['default'] Tag: typeof import('primevue/tag')['default']
Textarea: typeof import('primevue/textarea')['default'] Textarea: typeof import('primevue/textarea')['default']
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default'] TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
ThirdStep: typeof import('./src/components/ProductFormSteps/ThirdStep.vue')['default'] ThirdStep: typeof import('./src/components/ProductFormSteps/ThirdStep.vue')['default']
ToggleButton: typeof import('primevue/togglebutton')['default']
Toolbar: typeof import('primevue/toolbar')['default'] Toolbar: typeof import('primevue/toolbar')['default']
TreeTable: typeof import('primevue/treetable')['default']
UserDetails: typeof import('./src/components/UserDetails.vue')['default'] UserDetails: typeof import('./src/components/UserDetails.vue')['default']
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default'] WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
} }
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

+73
View File
@@ -0,0 +1,73 @@
<template>
<section class="flex flex-col gap-4 pt-7">
<div class="flex gap-7">
<div class="flex flex-col gap-7 items-center">
<div class="flex flex-col gap-1 w-96">
<FloatLabel>
<InputText class="w-full" v-model="newFaq.title" />
<label>{{ $t("سوال") }}</label>
</FloatLabel>
</div>
<div class="flex flex-col gap-1 w-96">
<FloatLabel>
<Textarea v-model="newFaq.description" rows="3" class="w-full" />
<label>{{ $t("پاسخ") }}</label>
</FloatLabel>
</div>
</div>
</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(newFaq)"
:loading="saving"
/>
</div>
</section>
</template>
<script setup>
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 newFaq = reactive({
title: "",
description: "",
});
const save = async (newFaq) => {
saving.value = true;
const send = await store.create(newFaq);
if (send.status === 200 || send.status === 201) {
toast.add({
severity: "success",
summary: "موفق",
detail: "آیتم جدید ایجاد شد",
life: 3000,
});
dialog.value.close()
}
saving.value = false;
store.index()
};
</script>
+53 -181
View File
@@ -5,15 +5,15 @@
<div class="flex flex-col gap-7 items-center"> <div class="flex flex-col gap-7 items-center">
<div class="flex flex-col gap-1 w-96"> <div class="flex flex-col gap-1 w-96">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="headline"/> <InputText class="w-full" v-model="newEpisode.headline"/>
<label>{{ $t("عنوان ویدیو") }}</label> <label>{{ $t("عنوان ویدیو") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1 w-96"> <div class="flex flex-col gap-1 w-96">
<FloatLabel> <FloatLabel>
<Textarea v-model="description" rows="3" class="w-full" /> <Textarea v-model="newEpisode.description" rows="3" class="w-full" />
<label>{{ $t("توضیحات متا") }}</label> <label>{{ $t("توضیحات") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -33,166 +33,33 @@
</div> </div>
<div class="flex flex-col gap-7 items-center"> <div class="flex flex-col gap-7 items-center">
<div> <FileUpload mode="advanced" multiple @select="chooseImages"
<div class="card w-[400px]"> @remove="chooseImages" >
<Toast /> <template #header="{ chooseCallback }">
<FileUpload <div class="flex justify-between w-full items-center">
name="demo[]" <h2 class="font-bold text-xl ">{{ $t('بارگذاری ویدیو') }}</h2>
url="/api/upload" <Button :label="$t('انتخاب ویدیو')" severity="secondary" icon="pi pi-video" text
@upload="onTemplatedUpload($event)" @click="chooseCallback()" />
:multiple="true"
accept="image/*"
:maxFileSize="1000000"
@select="onSelectedFiles"
>
<template
#header="{
chooseCallback,
uploadCallback,
clearCallback,
files,
}"
>
<div
class="flex flex-wrap justify-between items-center flex-1 gap-4"
>
<div class="flex gap-2">
<Button
@click="chooseCallback()"
icon="pi pi-images"
rounded
outlined
severity="secondary"
></Button>
<Button
@click="uploadEvent(uploadCallback)"
icon="pi pi-cloud-upload"
rounded
outlined
severity="success"
:disabled="!files || files.length === 0"
></Button>
<Button
@click="clearCallback()"
icon="pi pi-times"
rounded
outlined
severity="danger"
:disabled="!files || files.length === 0"
></Button>
</div>
<ProgressBar
:value="totalSizePercent"
:showValue="false"
class="md:w-20rem h-1 w-full md:ml-auto"
>
<span class="whitespace-nowrap"
>{{ totalSize }}B / 1Mb</span
>
</ProgressBar>
</div> </div>
</template> </template>
<template <template #content="{ files }">
#content="{ <div v-if="files.length > 0" class="flex gap-4 justify-between ">
files, <div v-if="loading" class="h-[300px] w-[500px] flex justify-center items-center">درحال بارگذاری ویدیو</div>
uploadedFiles, <div v-else class="h-[300px] w-[500px] flex justify-center items-center">ویدیو آپلود شد</div>
removeUploadedFileCallback,
removeFileCallback,
}"
>
<div class="flex flex-col gap-8 pt-4">
<div v-if="files.length > 0">
<h5>Pending</h5>
<div class="flex flex-wrap gap-4">
<div
v-for="(file, index) of files"
:key="file.name + file.type + file.size"
class="p-8 rounded-border flex flex-col border border-surface items-center gap-4"
>
<div>
<img
role="presentation"
:alt="file.name"
:src="file.objectURL"
width="100"
height="50"
/>
</div>
<span
class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden"
>{{ file.name }}</span
>
<div>{{ formatSize(file.size) }}</div>
<Badge value="Pending" severity="warn" />
<Button
icon="pi pi-times"
@click="
onRemoveTemplatingFile(
file,
removeFileCallback,
index
)
"
outlined
rounded
severity="danger"
/>
</div>
</div>
</div> </div>
</template>
<div v-if="uploadedFiles.length > 0">
<h5>Completed</h5>
<div class="flex flex-wrap gap-4">
<div
v-for="(file, index) of uploadedFiles"
:key="file.name + file.type + file.size"
class="p-8 rounded-border flex flex-col border border-surface items-center gap-4"
>
<div>
<img
role="presentation"
:alt="file.name"
:src="file.objectURL"
width="100"
height="50"
/>
</div>
<span
class="font-semibold text-ellipsis max-w-60 whitespace-nowrap overflow-hidden"
>{{ file.name }}</span
>
<div>{{ formatSize(file.size) }}</div>
<Badge
value="Completed"
class="mt-4"
severity="success"
/>
<Button
icon="pi pi-times"
@click="removeUploadedFileCallback(index)"
outlined
rounded
severity="danger"
/>
</div>
</div>
</div>
</div>
</template>
<template #empty> <template #empty>
<div class="flex items-center justify-center flex-col"> <div class="flex items-center justify-center flex-col gap-8 py-12 w-[51rem] h-96">
<i <i class="pi pi-video border-2 rounded-full p-5 text-8xl text-400 border-400" />
class="pi pi-cloud-upload border-2 rounded-full p-8 text-4xl text-muted-color" <p>{{ $t('جهت آپلود ویدیو را اینجا رها کنید') }}</p>
/>
<p class="mt-6 mb-0">ویدیو را اینجا رها کنید</p>
</div> </div>
</template> </template>
</FileUpload> </FileUpload>
</div> </div>
</div> </div>
</div>
</div>
<div class="w-full flex gap-2 mt-4"> <div class="w-full flex gap-2 mt-4">
<Button <Button
@@ -218,56 +85,61 @@ import { inject, reactive, ref } from "vue";
import { usePrimeVue } from "primevue/config"; import { usePrimeVue } from "primevue/config";
import { useToast } from "primevue/usetoast"; import { useToast } from "primevue/usetoast";
import { useHeadlinesStore } from "@/stores/Headlines" import { useHeadlinesStore } from "@/stores/Headlines"
import { useUploaderStore } from "@/stores/uploader";
const loading = ref(false)
const uploadStore = useUploaderStore();
const store = useHeadlinesStore() const store = useHeadlinesStore()
const dialog = inject("dialogRef"); const dialog = inject("dialogRef");
const type = ref(); const type = ref();
const produc = reactive(Object.assign({}, dialog.value.data)); const produc = reactive(dialog.value.data);
const sabt = () => { const saving = ref(false)
console.log(produc);
};
const videoStatus = ref(null); const videoStatus = ref(null);
const setType = () => { const setType = () => {
if (type.value === "غیرفعال") { if (type.value === "غیرفعال") {
videoStatus.value = "DeActivate"; newEpisode.status = "DeActivate";
} }
if (type.value === "تکمیل شده") { if (type.value === "تکمیل شده") {
videoStatus.value = "Completed"; newEpisode.status = "Completed";
} }
if (type.value === "درحال ضبط") { if (type.value === "درحال ضبط") {
videoStatus.value = "Recording"; newEpisode.status = "Recording";
} }
if (type.value === "به زودی...") { if (type.value === "به زودی...") {
videoStatus.value = "Soon"; newEpisode.status = "Soon";
}
};
const newEpisode = reactive({
headline: "",
description: "",
course: produc.produc,
videoUrl: "",
status: ""
})
const chooseImages = async(files) => {
loading.value = true
const uploader = await uploadStore.uploader(files.files[0]);
newEpisode.videoUrl = uploader.data.key
loading.value = false
} }
console.log("type:", videoStatus.value, produc.produc);
console.log(type.value);
};
const headline = ref('');
const description = ref('');
const videoUrl = ref('');
const $primevue = usePrimeVue();
const toast = useToast();
const totalSize = ref(0);
const totalSizePercent = ref(0);
const files = ref([]);
const course = ref(produc.produc)
const save = async() =>{ const save = async() =>{
const send = await store.create({ saving.value = true;
headline: headline.value, const send = await store.create(newEpisode)
description: description.value, saving.value = false;
course: produc.produc, dialog.value.close()
videoUrl: 'https://dlearn.storage.iran.liara.space/test/1cb985c8-26a0-4120-824c-f496349e45bfmatt_devir--one_minute_drive_preview.webm',
status: videoStatus.value
})
console.log('send', send);
} }
const onRemoveTemplatingFile = (file, removeFileCallback, index) => { const onRemoveTemplatingFile = (file, removeFileCallback, index) => {
removeFileCallback(index); removeFileCallback(index);
totalSize.value -= parseInt(formatSize(file.size)); totalSize.value -= parseInt(formatSize(file.size));
totalSizePercent.value = totalSize.value / 10; totalSizePercent.value = totalSize.value / 10;
}; };
const onClearTemplatingUpload = (clear) => { const onClearTemplatingUpload = (clear) => {
+2 -2
View File
@@ -50,8 +50,8 @@
const save = async () => { const save = async () => {
saving.value = true saving.value = true
const { status, data } = await store.create(name) const { status, data } = await store.create(name)
console.log('status',status);
save.value = false saving.value = false
if (status === 201 || status === 200) if (status === 201 || status === 200)
toast.add({ toast.add({
+51 -4
View File
@@ -37,8 +37,11 @@
</li> </li>
<li class="flex items-center justify-between"> <li class="flex items-center justify-between">
<strong>{{ $t('تغییر وضعیت') }}</strong> <strong>{{ $t('تغییر وضعیت') }}</strong>
<ToggleButton @change="changeStatus" v-model="checked" onLabel="فعال" offLabel="غیرفعال" /> <!-- <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> </li>
</ul> </ul>
</section> </section>
@@ -51,13 +54,57 @@ import { numberFormat } from '@/utils/numberFormat';
import {useDiscountStore} from '../stores/discounts' import {useDiscountStore} from '../stores/discounts'
const store = useDiscountStore() const store = useDiscountStore()
import { ref } from 'vue'; import { ref } from 'vue';
import { useRouter } from 'vue-router';
const checked = ref(false); const checked = ref(false);
const changeStatus = async() => { const { confirm, toast, t } = inject("service");
const router = useRouter()
const saving = ref(false)
const changeToDeActive = async() =>{
const change = await store.changeStatus({
id: coupon.coupon.id,
status: 'DeActivate'
})
if (change === true) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("وضعیت ویرایش شد"),
});
store.index()
dialog.value.close()
}else{
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("خطا، محدد تلاش کنید"),
});
}
}
const changeToActive = async() => {
const change = await store.changeStatus({ const change = await store.changeStatus({
id: coupon.coupon.id, id: coupon.coupon.id,
status: 'Active' status: 'Active'
}) })
console.log(change); if (change === true) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("وضعیت ویرایش شد"),
});
store.index()
dialog.value.close()
}else{
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("خطا، محدد تلاش کنید"),
});
}
}; };
const tabs = ref([ const tabs = ref([
@@ -68,7 +115,7 @@ const changeStatus = async() => {
const dialog = inject('dialogRef') const dialog = inject('dialogRef')
const coupon = reactive(Object.assign({}, dialog.value.data)) const coupon = reactive(Object.assign({}, dialog.value.data))
console.log('der', coupon.coupon);
const confirmToSales = (value) => { const confirmToSales = (value) => {
dialog.value.data.confirm(user, value) dialog.value.data.confirm(user, value)
} }
+203
View File
@@ -0,0 +1,203 @@
<template>
<section class="flex flex-col gap-4 pt-7">
<div class="flex gap-7">
<div class="flex flex-col gap-7 items-center">
<div class="flex flex-col gap-1 w-96">
<FloatLabel>
<InputText class="w-full" v-model="newEpisode.headline"/>
<label>{{ $t("عنوان ویدیو") }}</label>
</FloatLabel>
</div>
<div class="flex flex-col gap-1 w-96">
<FloatLabel>
<Textarea v-model="newEpisode.description" rows="3" class="w-full" />
<label>{{ $t("توضیحات") }}</label>
</FloatLabel>
</div>
<div class="flex flex-col gap-1 w-96">
<FloatLabel>
<Dropdown
v-model="type"
@change="setType"
:options="['تکمیل شده', 'غیرفعال', 'درحال ضبط', 'به زودی...']"
:optionLabel="levels"
:optionValue="levels"
class="w-full"
/>
<label>{{ $t("وضعیت ویدیو") }}</label>
</FloatLabel>
</div>
</div>
<div class="flex flex-col gap-7 items-center">
<FileUpload mode="advanced" multiple @select="chooseImages"
@remove="chooseImages" >
<template #header="{ chooseCallback }">
<div class="flex justify-between w-full items-center">
<h2 class="font-bold text-xl ">{{ $t('بارگذاری ویدیو') }}</h2>
<Button :label="$t('انتخاب ویدیو')" severity="secondary" icon="pi pi-video" text
@click="chooseCallback()" />
</div>
</template>
<template #content="{ files }">
<div v-if="files.length > 0" class="flex gap-4 justify-between ">
<div v-if="loading" class="h-[300px] w-[500px] flex justify-center items-center">درحال بارگذاری ویدیو</div>
<div v-else class="h-[300px] w-[500px] flex justify-center items-center">ویدیو آپلود شد</div>
</div>
</template>
<template #empty>
<div class="flex items-center justify-center flex-col gap-8 py-12 w-[51rem] h-96">
<i class="pi pi-video border-2 rounded-full p-5 text-8xl text-400 border-400" />
<p>{{ $t('جهت تغییر ویدیو، ویدیو جدید را اینجا رها کنید') }}</p>
</div>
</template>
</FileUpload>
</div>
</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 { 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 type = ref();
const produc = reactive(dialog.value.data);
const videoStatus = ref(null);
const setType = () => {
if (type.value === "غیرفعال") {
newEpisode.status = "DeActivate";
}
if (type.value === "تکمیل شده") {
newEpisode.status = "Completed";
}
if (type.value === "درحال ضبط") {
newEpisode.status = "Recording";
}
if (type.value === "به زودی...") {
newEpisode.status = "Soon";
}
};
const newEpisode = reactive({
headline: produc.product.headline,
description: produc.product.description,
course: produc.product.course,
videoUrl: produc.product.videoUrl,
status: produc.product.status
})
watchEffect(() =>{
if (produc.product.status === "Completed") {
type.value = 'تکمیل شده'
}
if (produc.product.status === "DeActivate") {
type.value = 'غیر فعال'
}
if (produc.product.status === "Recording") {
type.value = "درحال ضبط"
}
if (produc.product.status === "Soon") {
type.value = "به زودی..."
}
})
const chooseImages = async(files) => {
loading.value = true
const uploader = await uploadStore.uploader(files.files[0]);
newEpisode.videoUrl = uploader.data.key
loading.value = false
}
const save = async() =>{
saving.value = true
const send = await store.edit(produc.product.id, newEpisode)
console.log(send);
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>
+47
View File
@@ -0,0 +1,47 @@
<template>
<section class="flex flex-col gap-6 w-[700px]">
<ul class="flex flex-col gap-5 space-y-6">
<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.title }}</span>
</li>
<li class="flex items-center justify-between gap-32">
<strong>{{ $t('پاسخ') }}</strong>
<span>{{ user.product.description }}</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')
const user = reactive(Object.assign({}, dialog.value.data))
const confirmToSales = (value) => {
dialog.value.data.confirm(user, value)
}
</script>
<style lang="scss" scoped></style>
+11 -5
View File
@@ -3,7 +3,7 @@
<ul class="flex flex-col gap-5"> <ul class="flex flex-col gap-5">
<li class="flex items-center justify-between"> <li class="flex items-center justify-between">
<video :src="head.product.videoUrl" autoplay></video> <video :src="videoUrl.data.url" autoplay></video>
</li> </li>
<li class="flex items-center justify-between"> <li class="flex items-center justify-between">
<strong>{{ $t('عنوان') }}</strong> <strong>{{ $t('عنوان') }}</strong>
@@ -63,20 +63,26 @@
</template> </template>
<script setup> <script setup>
import { inject, reactive } from 'vue'; import { inject, reactive, watchEffect } from 'vue';
import { jDate } from '@/utils/jDate'; import { jDate } from '@/utils/jDate';
import { numberFormat } from '@/utils/numberFormat'; import { numberFormat } from '@/utils/numberFormat';
import { ref } from 'vue'; import { useUploaderStore } from "@/stores/uploader"
import { ref } from 'vue';
const store = useUploaderStore()
const tabs = ref([ const tabs = ref([
{ title: 'Tab 1', content: 'Tab 1 Content', value: '0' }, { title: 'Tab 1', content: 'Tab 1 Content', value: '0' },
{ title: 'Tab 2', content: 'Tab 2 Content', value: '1' }, { title: 'Tab 2', content: 'Tab 2 Content', value: '1' },
{ title: 'Tab 3', content: 'Tab 3 Content', value: '2' } { title: 'Tab 3', content: 'Tab 3 Content', value: '2' }
]); ]);
const dialog = inject('dialogRef') const dialog = inject('dialogRef')
const videoUrl = ref()
const head = reactive(Object.assign({}, dialog.value.data)) const head = reactive(Object.assign({}, dialog.value.data))
console.log('der', head); console.log('head', );
watchEffect(async() =>{
videoUrl.value = await store.getUrl(head.product.videoUrl)
console.log(videoUrl);
})
// const confirmToSales = (value) => { // const confirmToSales = (value) => {
// dialog.value.data.confirm(user, value) // dialog.value.data.confirm(user, value)
// } // }
+1 -1
View File
@@ -79,7 +79,7 @@ import { ref } from 'vue';
const dialog = inject('dialogRef') const dialog = inject('dialogRef')
const user = reactive(Object.assign({}, dialog.value.data)) const user = reactive(Object.assign({}, dialog.value.data))
console.log('der', user.product);
const confirmToSales = (value) => { const confirmToSales = (value) => {
dialog.value.data.confirm(user, value) dialog.value.data.confirm(user, value)
} }
+1 -2
View File
@@ -34,7 +34,7 @@ const { toast, t } = inject('service')
const { product } = dialog.value.data const { product } = dialog.value.data
const form = reactive(Object.assign({}, product || {})) const form = reactive(Object.assign({}, product || {}))
console.log(form)
const errors = reactive({}) const errors = reactive({})
const saving = ref(false) const saving = ref(false)
@@ -48,7 +48,6 @@ const uploader = useUploaderStore()
const save = async () => { const save = async () => {
saving.value = true saving.value = true
console.log(form);
//Upload File //Upload File
if (typeof form.coverImage === 'object') { if (typeof form.coverImage === 'object') {
@@ -60,7 +60,7 @@ import { inject } from 'vue';
const dialogRef = inject('dialogRef') const dialogRef = inject('dialogRef')
const { form, errors } = inject('data') const { form, errors } = inject('data')
console.log('ax',form);
</script> </script>
+60 -3
View File
@@ -24,7 +24,12 @@
<strong>{{ $t('status') }}</strong> <strong>{{ $t('status') }}</strong>
<span>{{ user.statusText }}</span> <span>{{ user.statusText }}</span>
</li> </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>
</ul> </ul>
</section> </section>
</template> </template>
@@ -35,12 +40,64 @@ import { jDate } from '@/utils/jDate';
import { inject, reactive } from 'vue'; import { inject, reactive } from 'vue';
const dialog = inject('dialogRef') const dialog = inject('dialogRef')
import { useUsersStore } from '@/stores/users';
const store = useUsersStore()
const user = reactive(Object.assign({}, dialog.value.data.user)) const user = reactive(Object.assign({}, dialog.value.data.user))
const { confirm, toast, t } = inject("service");
const confirmToSales = (value) => { const confirmToSales = (value) => {
dialog.value.data.confirm(user, value) dialog.value.data.confirm(user, value)
} }
const changeToDeActive = async() =>{
const change = await store.changeStatus({
userId: user.id,
status: 'DeActivate'
})
if (change === true) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("وضعیت ویرایش شد"),
});
store.index()
dialog.value.close()
}else{
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("خطا، محدد تلاش کنید"),
});
}
}
const changeToActive = async() => {
const change = await store.changeStatus({
userId: user.id,
status: 'Active'
})
if (change === true) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("وضعیت ویرایش شد"),
});
store.index()
dialog.value.close()
}else{
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("خطا، محدد تلاش کنید"),
});
}
};
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>
+1 -1
View File
@@ -128,7 +128,7 @@ const items = ref([
{ {
label: t('تاریخچه ی تخفیف ها'), label: t('تاریخچه ی تخفیف ها'),
icon: 'pi pi-history', icon: 'pi pi-history',
command: () => router.push('/Coupons') command: () => router.push('/couponsHistory')
}, },
], ],
+1 -1
View File
@@ -1,7 +1,7 @@
import { useAdminsStore } from '@/stores/admins' import { useAdminsStore } from '@/stores/admins'
import axioss from 'axios' import axioss from 'axios'
const baseURL = 'https://dlearn.iran.liara.run' const baseURL = 'https://dlearn.liara.run'
const axios = axioss.create({ baseURL }) const axios = axioss.create({ baseURL })
+7 -2
View File
@@ -14,8 +14,8 @@ const router = createRouter({
}, },
{ {
path: '/', path: '/',
name: 'Main', // name: 'Main',
//redirect: '/products', redirect: '/courses',
component: Main, component: Main,
children: [ children: [
{ {
@@ -108,6 +108,11 @@ const router = createRouter({
name: 'AddVideos', name: 'AddVideos',
component: () => import('@/views/AddVideos.vue'), component: () => import('@/views/AddVideos.vue'),
}, },
{
path: '/couponsHistory',
name: 'couponsHistory',
component: () => import('@/views/couponsHistory.vue'),
},
// { // {
// path: '/videos/create', // path: '/videos/create',
// name: 'createVideo', // name: 'createVideo',
+2 -3
View File
@@ -30,10 +30,9 @@ export const useHeadlinesStore = defineStore('headlines', {
// }, // },
async edit(id) { async edit(id, body) {
const { status, data } = await axios.get(`/course/${id}`) const { status, data } = await axios.put(`/courseHeadline/${id}`, body)
return data return data
}, },
async create(form){ async create(form){
const {status, data} = await axios.post('/courseHeadline', form) const {status, data} = await axios.post('/courseHeadline', form)
+8 -9
View File
@@ -19,11 +19,8 @@ export const useAdminsStore = defineStore('admins', {
phone: number.value, phone: number.value,
type: 1 type: 1
}) })
console.log('state',data);
if (status == 200) { if (status == 200) {
// this.token = data.accessToken
// this.permissions = data.permissions
// console.log(data);
return { status, data, error } return { status, data, error }
} }
@@ -33,13 +30,15 @@ export const useAdminsStore = defineStore('admins', {
phone: number.value, phone: number.value,
code: code.value code: code.value
}) })
console.log('data server', data);
console.log(data);
if (status == 200) { if (status == 200) {
console.log('first', this.token); if (data.roles[0] === "Admin") {
this.token = data.token this.token = data.token
// this.permissions = data.permissions }
console.log('two', this.token); else{
this.token = ''
}
return { status, data, error } return { status, data, error }
} }
+3 -4
View File
@@ -46,10 +46,9 @@ export const useCategoriesStore = defineStore('categories', {
} }
}, },
actions: { actions: {
async index(page, rows) { async index() {
this.fetching = true this.fetching = true
const params = { page, rows } const {data} = await axios.get('/courseCategory')
const { status, data } = await axios.get('/courseCategory', { params })
this.fetching = false this.fetching = false
return data return data
if (status === 200) { if (status === 200) {
@@ -81,7 +80,7 @@ export const useCategoriesStore = defineStore('categories', {
name: id.value name: id.value
}) })
//console.log('result', result);
return result; return result;
}, },
+2 -2
View File
@@ -50,7 +50,7 @@ export const useCommentStore = defineStore('comments', {
this.fetching = true this.fetching = true
const data = await axios.post(`/course/${id}/comment`, body) const data = await axios.post(`/course/${id}/comment`, body)
this.fetching = false this.fetching = false
console.log(id, 'id');
return data return data
@@ -142,7 +142,7 @@ export const useCommentStore = defineStore('comments', {
name: id.value name: id.value
}) })
//console.log('result', result);
return result; return result;
}, },
+27 -6
View File
@@ -10,6 +10,32 @@ export const useDiscountStore = defineStore('discounts', {
total: 0 total: 0
}), }),
actions: { actions: {
async add(form) {
this.fetching = true
const { status, data } = await axios.post('/admins/discount', form)
this.fetching = false
return data
},
async history() {
this.fetching = true
const { status, data } = await axios.get('/admins/discountHistory')
this.fetching = false
return data
},
async index() { async index() {
this.items = []; this.items = [];
this.fetching = true this.fetching = true
@@ -18,12 +44,7 @@ export const useDiscountStore = defineStore('discounts', {
return this.items = data return this.items = data
}, },
async add(form) {
this.fetching = true
const { status, data } = await axios.post('/admins/discount', form)
this.fetching = false
return data
},
async getDiscount(id) { async getDiscount(id) {
this.items = []; this.items = [];
this.fetching = true this.fetching = true
+16 -6
View File
@@ -8,18 +8,28 @@ export const useFaqStore = defineStore('faq', {
total: 0 total: 0
}), }),
actions: { actions: {
async index(page, pageSize) { async index() {
this.items = []; this.items = [];
this.fetching = true this.fetching = true
const { status, data } = await axios.get('/admins/faq/category')
const params = { page, pageSize }
const { status, data } = await axios.get('/admin/user/list', { params })
this.fetching = false this.fetching = false
this.total = 30
this.items = data; this.items = data;
}, },
async remove(id) {
const data = await axios.delete(`/admins/faq/category/${id}`)
return data
},
async create(form) {
const data = await axios.post('/admins/faq/category', form)
return data
},
async confirm(item, value) { async confirm(item, value) {
item.confirming = true item.confirming = true
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value }) const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
+5
View File
@@ -29,6 +29,11 @@ export const useProductsStore = defineStore('products', {
return data return data
},
async update(course) {
const { status, data } = await axios.put('/course', course)
return data
}, },
async delete(id) { async delete(id) {
const { status, data } = await axios.delete(`/course/${id}`, id) const { status, data } = await axios.delete(`/course/${id}`, id)
+1 -38
View File
@@ -8,43 +8,6 @@ export const useStaticsStore = defineStore('statics', {
fetching: true, fetching: true,
total: 0 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: { actions: {
async index() { async index() {
this.fetching = true this.fetching = true
@@ -70,7 +33,7 @@ export const useStaticsStore = defineStore('statics', {
name: id.value name: id.value
}) })
//console.log('result', result);
return result; return result;
}, },
+1 -1
View File
@@ -171,7 +171,7 @@ export const useTicketsStore = defineStore('tickets', {
name: id.value name: id.value
}) })
//console.log('result', result);
return result; return result;
}, },
+7 -40
View File
@@ -14,9 +14,7 @@ export const useUploaderStore = defineStore('uploader', {
form.append('directoryName', 'directoryName') form.append('directoryName', 'directoryName')
form.append('file', file) form.append('file', file)
// for (var pair of form.entries()) {
// console.log('pairs :',pair[0]+ ', ' + pair[1]);
// }
let config = { let config = {
headers: { headers: {
"Content-Type": "multipart/form-data", "Content-Type": "multipart/form-data",
@@ -28,49 +26,18 @@ export const useUploaderStore = defineStore('uploader', {
form, form,
config config
); );
console.log('result', result);
return result; return result;
} catch (error) { } catch (error) {
console.log('err', error);
return error; return error;
} }
// const result = await axios.post('/uploader', form,
// {
// headers: { "Content-Type": "multipart/form-data" }
// }
// )
// console.log(result);
// return result
// const result = await axios({
// method: "post",
// url: "/uploader",
// data: form,
// headers: { "Content-Type": "multipart/form-data" },
// })
// .then(function (response) {
// console.log(response);
// })
// .catch(function (response) {
// console.log(response);
// });
// console.log('result', result);
// return result;
}, },
async getUrl(fileName){
const data = await axios.get(`/uploader/file?fileName=${fileName}`)
return data
},
async bulk(files) { async bulk(files) {
+4
View File
@@ -20,6 +20,10 @@ export const useUsersStore = defineStore('users', {
this.items = data; this.items = data;
}, },
async changeStatus(form) {
const { status, data } = await axios.patch('/admin/user/status', form)
return data
},
async confirm(item, value) { async confirm(item, value) {
item.confirming = true item.confirming = true
const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value }) const result = await axios.put(`/admin/users/seller/${item.id}`, { confirmToSales: value })
+1 -1
View File
@@ -5,6 +5,6 @@ export const treeTogglerRTL = (selector) => {
element.style['margin-left'] = null element.style['margin-left'] = null
}); });
} catch (error) { } catch (error) {
console.log(error);
} }
} }
+27 -4
View File
@@ -4,13 +4,13 @@
<div class="grid grid-cols-2 gap-7"> <div class="grid grid-cols-2 gap-7">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="data.title" /> <InputText class="w-full" v-model="data.expertise" />
<label>{{ $t("حوزه های تخصصی") }}</label> <label>{{ $t("تعداد حوزه های تخصصی") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="data.title" /> <InputText class="w-full" v-model="data.student" />
<label>{{ $t("تعداد دانشجو ها") }}</label> <label>{{ $t("تعداد دانشجو ها") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -18,7 +18,7 @@
<div class="grid grid-cols-2 gap-7 mt-7"> <div class="grid grid-cols-2 gap-7 mt-7">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText v-model="data.student" class="w-full" /> <InputText v-model="data.title" class="w-full" />
<label>{{ $t("تعداد عناوین آموزشی") }}</label> <label>{{ $t("تعداد عناوین آموزشی") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -62,8 +62,31 @@
title: 0, title: 0,
hours: 0, hours: 0,
}); });
const saving = ref(false);
const save = async (data) => { const save = async (data) => {
saving.value = true;
const result = await store.edit(data); const result = await store.edit(data);
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;
}; };
const expandedKeys = ref({}); const expandedKeys = ref({});
+23 -8
View File
@@ -9,7 +9,7 @@
@click="create()" @click="create()"
/> />
</template> </template>
<DataTable :value="store.items"> <DataTable :value="videos">
<Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" /> <Column class="w-64" field="headline" :header="$t('عنوان ویدیو')" />
<Column field="description" :header="$t('description')" /> <Column field="description" :header="$t('description')" />
@@ -73,9 +73,9 @@
<template #empty> <template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p> <p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template> </template>
<template #footer> <!-- <template #footer>
<Pagination :total="store.total" /> <Pagination :total="store.total" />
</template> </template> -->
</DataTable> </DataTable>
<!-- <Galleria <!-- <Galleria
v-model:visible="galleryVes" v-model:visible="galleryVes"
@@ -95,18 +95,22 @@
<script setup> <script setup>
const { toast, dialog, confirm, t } = inject('service') const { toast, dialog, confirm, t } = inject('service')
import { ref, watchEffect, reactive, defineAsyncComponent, inject } from "vue"; import { ref, watchEffect, reactive, defineAsyncComponent, inject, watch } from "vue";
import { useRoute, useRouter } from "vue-router"; import { useRoute, useRouter } from "vue-router";
import { useHeadlinesStore } from "@/stores/Headlines" import { useHeadlinesStore } from "@/stores/Headlines"
const store = useHeadlinesStore() const store = useHeadlinesStore()
const route = useRoute() const route = useRoute()
const videos = reactive({}) const videos = ref()
const HeadlineDetail = defineAsyncComponent(() => import('../components/HeadlineDetail.vue')); const HeadlineDetail = defineAsyncComponent(() => import('../components/HeadlineDetail.vue'));
const AddVideo = defineAsyncComponent(() => import('../components/AddVideo.vue')); const AddVideo = defineAsyncComponent(() => import('../components/AddVideo.vue'));
watchEffect(async() => { const EditVideo = defineAsyncComponent(() => import('../components/EditVideo.vue'));
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) => { const show = (product) => {
dialog.open(HeadlineDetail, { dialog.open(HeadlineDetail, {
props: { modal: true, closable: true, header: t('پیش نمایش ویدیو') }, props: { modal: true, closable: true, header: t('پیش نمایش ویدیو') },
@@ -125,6 +129,17 @@ const create = () =>{
} }
}); });
} }
const edit = (product) => {
dialog.open(EditVideo, {
props: { modal: true, closable: true, header: t('ویرایش ویدیو') },
data: {
product
}
});
}
const remove = async(data) => { const remove = async(data) => {
confirm.require({ confirm.require({
message: t('Are you sure you want to proceed?'), message: t('Are you sure you want to proceed?'),
+8 -11
View File
@@ -33,19 +33,13 @@
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p> <p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template> </template>
<template #footer>
<!--
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
-->
<Pagination :total="store.total" />
</template>
</DataTable> </DataTable>
</Panel> </Panel>
</template> </template>
<script setup> <script setup>
import { useUsersStore } from "@/stores/users"; import { useUsersStore } from "@/stores/users";
import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue"; import { inject, reactive, defineAsyncComponent, ref, watchEffect, watch } from "vue";
const store = useUsersStore(); const store = useUsersStore();
const admins = ref() const admins = ref()
@@ -61,9 +55,12 @@ const show = (user) => {
}); });
}; };
watchEffect(async () => { watchEffect(async () => {
await store.index(1, 100); await store.index();
console.log("user", store.items); admins.value = store.items.user.filter(item => item.roles[1] === 'Admin' );
admins.value = store.items.filter(item => item.roles[1] === 'Admin' );
console.log('filter',admins);
}); });
watch(admins, async()=>{
await store.index();
admins.value = store.items.user.filter(item => item.roles[1] === 'Admin' );
})
</script> </script>
+10 -7
View File
@@ -1,12 +1,15 @@
<template> <template>
<Panel :header="$t('پاسخ به نظرات')"> <Panel :header="$t('پاسخ به نظرات')">
<div v-for="item in data.comments" :key="item.id"> <div v-for="item in data.comments" :key="item.id">
<div class="card mb-8"> <div class="card mb-8">
<Panel toggleable> <Panel toggleable>
<template #header> <template #header>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Avatar :image="item.createdBy.avatarMedia" size="large" shape="circle" /> <Avatar v-if="item.createdBy.avatarMedia" :image="item.createdBy.avatarMedia" size="large" shape="circle" />
<span class="font-bold">{{item.createdBy.name}}</span> <Avatar v-else image="/src/assets/alt.png" size="large" shape="circle" />
<span v-if="item.createdBy.name" class="font-bold">{{item.createdBy.name}}</span>
<span v-else class="font-bold">کاربر بدون نام</span>
</div> </div>
</template> </template>
<template #footer> <template #footer>
@@ -80,17 +83,17 @@ const reply = (id) =>{
} }
const sendReply = async() =>{ const sendReply = async() =>{
if (replyTarget.value === null || replyTarget.value === '') { if (replyTarget.value === null || replyTarget.value === '') {
return console.log('کامنت مورد نظر را انتخاب کنید');
} }
if (value.value === null || value.value === '') { if (value.value === null || value.value === '') {
return console.log('متن پیام را وارد کنید');
} }
// console.log(value.value);
const rep = await commentStore.reply(route.params.id, { const rep = await commentStore.reply(route.params.id, {
text: value.value, text: value.value,
parent: replyTarget.value parent: replyTarget.value
}) })
console.log('rep', rep);
} }
const items = ref([ const items = ref([
{ {
@@ -120,6 +123,6 @@ const save = () => {
watchEffect(async() => { watchEffect(async() => {
data.value = await store.edit(route.params.id) data.value = await store.edit(route.params.id)
console.log('store', data.value.comments);
}) })
</script> </script>
+80 -79
View File
@@ -1,117 +1,118 @@
<template> <template>
<Panel :header="$t('categories')"> <Panel :header="$t('categories')">
<template #icons> <template #icons>
<Button icon="pi pi-plus" :label="$t('add')" severity="success" @click="create()" /> <Button
icon="pi pi-plus"
:label="$t('add')"
severity="success"
@click="create()"
/>
</template> </template>
<TreeTable :value="store.items" v-model:expandedKeys="expandedKeys"> <DataTable :value="categories">
<Column field="name" :header="$t('name')" /> <Column field="name" :header="$t('name')" />
<!-- <Column :header="$t('image')">
<template #body="{ node: { data: { headerImage } } }">
<Image :src="headerImage" imageClass="rounded h-10" class="align-middle" preview
:pt="{ toolbar: 'flex-row-reverse' }" />
</template>
</Column> -->
<Column field="id" :header="$t('id')" /> <Column field="id" :header="$t('id')" />
<Column
:field="({ createdAt }) => jDate(createdAt)"
:header="$t('createdAt')"
/>
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" />
<Column headerClass="w-32" :header="$t('options')"> <Column headerClass="w-32" :header="$t('options')">
<template #body="{ node: { data } }"> <template #body="{ data }">
<div class="flex justify-start"> <div class="flex justify-start">
<!-- <Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(data)" /> --> <!-- <Button icon="pi pi-pencil" rounded text severity="secondary" @click="edit(categories)" />
<Button icon="pi pi-trash" rounded text severity="danger" @click="remove(data)" -->
:loading="data.removing" /> <Button
icon="pi pi-trash"
rounded
text
severity="danger"
@click="remove(data)"
/>
</div> </div>
</template> </template>
</Column> </Column>
</DataTable>
<template #empty>
<p>{{ store.fetching ? $t('loading') : $t('emptyTable') }}</p>
</template>
<template #footer>
<Pagination :total="store.total" />
</template>
</TreeTable>
</Panel> </Panel>
</template> </template>
<script setup> <script setup>
import { jDate } from '@/utils/jDate'; import { jDate } from "@/utils/jDate";
import { treeTogglerRTL } from '@/utils/tree-toggler-rtl'; import { treeTogglerRTL } from "@/utils/tree-toggler-rtl";
import { inject, defineAsyncComponent, watchEffect, ref, watch, nextTick } from 'vue'; import {
import { useCategoriesStore } from '@/stores/categoreis'; inject,
import { useRoute } from 'vue-router'; defineAsyncComponent,
watchEffect,
ref,
watch,
nextTick,
} from "vue";
import { useCategoriesStore } from "@/stores/categoreis";
import { useRoute } from "vue-router";
const categories = ref();
// const expandedKeys = ref({});
const expandedKeys = ref({}); const CategoryForm = defineAsyncComponent(() =>
import("../components/CategoryForm.vue")
);
const CategoryForm = defineAsyncComponent(() => import('../components/CategoryForm.vue')); const { dialog, confirm, toast, t } = inject("service");
const { dialog, confirm, toast, t } = inject('service') const store = useCategoriesStore();
const store = useCategoriesStore() const create = async() => {
const create = () => {
dialog.open(CategoryForm, { dialog.open(CategoryForm, {
props: { modal: true, closable: false, header: t('createNewCategory') }, props: { modal: true, closable: false, header: t("createNewCategory") },
data: { categories: 'store.items' } data: { categories: "store.items" },
}); });
await store.index(1,100)
}; };
const edit = (category) => { // const edit = (category) => {
dialog.open(CategoryForm, { // dialog.open(CategoryForm, {
props: { modal: true, closable: false, header: t('editCategory') }, // props: { modal: true, closable: false, header: t('editCategory') },
data: { categories: store.items, category } // data: { categories: store.items, category }
}); // });
}; // };
const remove = (category) => { const remove = (data) => {
// console.log(category.id);
confirm.require({ confirm.require({
message: t('Are you sure you want to proceed?'), message: t("Are you sure you want to proceed?"),
header: t('Danger Zone'), header: t("Danger Zone"),
icon: 'pi pi-info-circle', icon: "pi pi-info-circle",
acceptClass: 'p-button-danger p-button-sm', acceptClass: "p-button-danger p-button-sm",
rejectClass: 'p-button-secondary p-button-outlined p-button-sm', rejectClass: "p-button-secondary p-button-outlined p-button-sm",
rejectLabel: t('cancel'), rejectLabel: t("cancel"),
acceptLabel: t('beOmitted'), acceptLabel: t("beOmitted"),
defaultFocus: 'reject', defaultFocus: "reject",
accept: async () => { accept: async () => {
category.removing = true const deleted = await store.remove(data.id);
if (deleted.status === 200 || deleted.status === 201) {
const { status, data } = await store.remove(category.id);
category.removing = false
if (status === 200)
toast.add({ toast.add({
severity: 'success', summary: t('successful'), detail: t('destroySuccessfully'), life: 3000 severity: "success",
summary: t("successful"),
detail: t("دسته بندی حذف شد"),
life: 3000,
}); });
else
} else
toast.add({ toast.add({
severity: 'error', summary: t('error'), detail: data.msg, life: 3000 severity: "error",
summary: t("error"),
detail: t("خطا، مجدد تلاش کنید"),
life: 3000,
}); });
}, },
}); });
}; };
const route = useRoute() watchEffect(async () => {
categories.value = await store.index(1, 100);
watchEffect(() => { });
const { page = 1, rows = 10 } = route.query watch(categories ,async () => {
store.index(page, rows) categories.value = await store.index(1, 100);
}) });
watch(store , () =>{
store.index()
})
watch(expandedKeys, () => nextTick(() => treeTogglerRTL('button.p-treetable-toggler')))
</script> </script>
<style lang="scss" scoped></style> <style lang="scss" scoped></style>
+5 -56
View File
@@ -38,44 +38,13 @@
</div> </div>
</template> </template>
</Column> </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> <template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p> <p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template> </template>
<template #footer> <!-- <template #footer>
<Pagination :total="store.total" /> <Pagination :total="store.total" />
</template> </template> -->
</DataTable> </DataTable>
<Galleria <Galleria
v-model:visible="galleryVes" v-model:visible="galleryVes"
@@ -131,7 +100,6 @@
}) })
const router = useRouter() const router = useRouter()
const show = (data) => { const show = (data) => {
console.log(data);
router.push(`/comments/${data._id}`) router.push(`/comments/${data._id}`)
} }
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue')); const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
@@ -144,7 +112,7 @@ console.log(data);
const remove = (data) => { const remove = (data) => {
console.log('done', data._id)
confirm.require({ confirm.require({
message: t('Are you sure you want to proceed?'), message: t('Are you sure you want to proceed?'),
header: t('Danger Zone'), header: t('Danger Zone'),
@@ -156,25 +124,10 @@ console.log(data);
defaultFocus: 'reject', defaultFocus: 'reject',
accept: async () => { accept: async () => {
const deleted = await store.delete(data._id) const deleted = await store.delete(data._id)
console.log(deleted);
await store.index() 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
// });
// },
}); });
} }
@@ -185,11 +138,7 @@ console.log(data);
watchEffect(async() => { watchEffect(async() => {
const { page = 1, pageSize = 10 } = route.query const { page = 1, pageSize = 10 } = route.query
await store.index(page, pageSize) await store.index(page, pageSize)
console.log(store.items[2].image);
console.log(String.fromCharCode(store.items[2].image)).join('');
// store.items.forEach((product) => {
// product.status = { value: t('active'), severity: 'success' };
// })
}) })
+5 -9
View File
@@ -1,6 +1,6 @@
<template> <template>
<Panel :header="$t('coupons')"> <Panel :header="$t('coupons')">
<DataTable :value="store.items"> <DataTable :value="store.items.discount">
<Column field="code" :header="$t('کد تخفیف')" /> <Column field="code" :header="$t('کد تخفیف')" />
<Column field="id" :header="$t('id')" /> <Column field="id" :header="$t('id')" />
@@ -8,7 +8,7 @@
<Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" /> <Column :field="({ createdAt }) => jDate(createdAt)" :header="$t('createdAt')" />
<Column :field="({ startDate }) => jDate(startDate)" :header="$t('تاریخ شروع')" /> <Column :field="({ startDate }) => jDate(startDate)" :header="$t('تاریخ شروع')" />
<Column :field="({ endDate }) => jDate(endDate)" :header="$t('تاریخ انقضا')" /> <Column :field="({ endDate }) => jDate(endDate)" :header="$t('تاریخ انقضا')" />
<Column field="status" :header="$t('status')" /> <Column field="statusText" :header="$t('status')" />
<Column headerClass="w-32"> <Column headerClass="w-32">
@@ -34,14 +34,11 @@
<template #empty> <template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p> <p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template> </template>
<template #footer>
<!-- <!--
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator> <template #footer>
-->
<Pagination :total="store.total" /> <Pagination :total="store.total" />
</template> </template> -->
</DataTable> </DataTable>
</Panel> </Panel>
</template> </template>
@@ -68,11 +65,10 @@ const show = (coupon) => {
} }
const router = useRouter() const router = useRouter()
const edit = (data) =>{ const edit = (data) =>{
console.log(data.id);
router.push(`/coupon/${data.id}`) router.push(`/coupon/${data.id}`)
} }
watchEffect( async() =>{ watchEffect( async() =>{
await store.index() await store.index()
console.log('data', data);
}) })
</script> </script>
+8 -12
View File
@@ -3,7 +3,7 @@
<template #icons> <template #icons>
<Button <Button
icon="pi pi-plus" icon="pi pi-plus"
:label="$t('add')" :label="$t('ایجاد دوره ی جدید')"
severity="success" severity="success"
@click="create()" @click="create()"
/> />
@@ -97,9 +97,9 @@
<template #empty> <template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p> <p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template> </template>
<template #footer> <!-- <template #footer>
<Pagination :total="store.total" /> <Pagination :total="store.items.count" />
</template> </template> -->
</DataTable> </DataTable>
<Galleria <Galleria
v-model:visible="galleryVes" v-model:visible="galleryVes"
@@ -161,6 +161,7 @@ const show = (product) => {
} }
}); });
} }
const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue')); const UserDetails = defineAsyncComponent(() => import('../components/UserDetails.vue'));
const store = useProductsStore() const store = useProductsStore()
@@ -172,7 +173,7 @@ const edit = (data) =>{
const remove = (data) => { const remove = (data) => {
console.log('done', data._id)
confirm.require({ confirm.require({
message: t('Are you sure you want to proceed?'), message: t('Are you sure you want to proceed?'),
header: t('Danger Zone'), header: t('Danger Zone'),
@@ -184,7 +185,7 @@ const remove = (data) => {
defaultFocus: 'reject', defaultFocus: 'reject',
accept: async () => { accept: async () => {
const deleted = await store.delete(data._id) const deleted = await store.delete(data._id)
console.log(deleted);
await store.index() await store.index()
} }
// product.removing = true // product.removing = true
@@ -206,18 +207,13 @@ const remove = (data) => {
}); });
} }
const create = () => router.push('/create')
watchEffect(async() => { watchEffect(async() => {
const { page = 1, pageSize = 10 } = route.query const { page = 1, pageSize = 10 } = route.query
await store.index(page, pageSize) await store.index(page, pageSize)
console.log(store.items[2].image);
console.log(String.fromCharCode(store.items[2].image)).join('');
// store.items.forEach((product) => {
// product.status = { value: t('active'), severity: 'success' };
// })
}) })
+161 -84
View File
@@ -1,23 +1,39 @@
<template> <template>
<section class="flex flex-col gap-4"> <section class="flex flex-col gap-4">
<Panel :header="$t('createCourse')"> <Panel :header="$t('createCourse')">
<div class="grid grid-cols-3 gap-7"> <div class="flex">
<div class="basis-4/6">
<div class="grid grid-cols-2 gap-7">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="newCourse.name" /> <InputText
@change="setSlug"
class="w-full"
v-model="newCourse.name"
/>
<label>{{ $t("title") }}</label> <label>{{ $t("title") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="newCourse.language" /> <Dropdown
v-model="language"
@change="selectLanguage"
:options="['انگلیسی', 'فارسی']"
class="w-full"
/>
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
<label>{{ $t("زبان آموزش") }}</label> <label>{{ $t("زبان آموزش") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText v-model="newCourse.duration" class="w-full" /> <InputText
v-model="newCourse.duration"
class="w-full"
prefix="ساعت"
/>
<label>{{ $t("مدت زمان دوره") }}</label> <label>{{ $t("مدت زمان دوره") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -26,27 +42,13 @@
<FloatLabel> <FloatLabel>
<Dropdown <Dropdown
v-model="level" v-model="level"
@change="selectLevel"
:options="['مبتدی', 'متوسط', 'پیشرفته']" :options="['مبتدی', 'متوسط', 'پیشرفته']"
class="w-full" class="w-full"
/> />
<label>{{ $t("سطح دوره") }}</label> <label>{{ $t("سطح دوره") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="newCourse.teacher" />
<label>{{ $t("مدرس دوره") }}</label>
</FloatLabel>
</div>
<!-- <div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="newCourse.categories" />
<label>{{ $t("دسته بندی") }}</label>
</FloatLabel>
</div> -->
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
@@ -60,7 +62,7 @@
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="newCourse.videosCount" /> <InputText class="w-full" v-model="newCourse.countOfVideos" />
<label>{{ $t("تعداد ویدیو ها") }}</label> <label>{{ $t("تعداد ویدیو ها") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -76,49 +78,64 @@
<FloatLabel> <FloatLabel>
<InputGroup> <InputGroup>
<InputText class="pointer-events-none truncate" dir="ltr" /> <InputText class="pointer-events-none truncate" dir="ltr" />
<Button severity="secondary"> <Button severity="secondary" :loading="saving">
<i class="pi pi-image ml-2"></i> <i class="pi pi-image ml-2"></i>
<span>{{ $t("chooseImage") }}</span> <span>{{ $t("chooseImage") }}</span>
<input <input
type="file" type="file"
class="opacity-0 absolute inset-0 cursor-pointer" class="opacity-0 absolute inset-0 cursor-pointer"
@input="chooseImage" @input="chooseImage"
/> />
</Button> </Button>
</InputGroup> </InputGroup>
<label>{{ $t("image") }}</label> <label>{{ $t("image") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
</div> </div>
<div class="w-full mt-7">
<div class="flex w-full mt-7">
<div class="basis-4/6">
<FloatLabel> <FloatLabel>
<Editor <Editor
v-model="newCourse.description" v-model="newCourse.description"
placeholder="توضیحات" placeholder="توضیحات"
editorStyle="height: 320px" editorStyle="height: 315px"
/> />
</FloatLabel> </FloatLabel>
</div> </div>
</div>
<div class="basis-2/6 flex flex-col gap-7 ms-7">
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="newCourse.price" />
<label>{{ $t("قیمت دوره") }}</label>
</FloatLabel>
</div>
<div class="basis-2/6 ms-8">
<div class="card flex flex-col gap-2 justify-center">
<Listbox <Listbox
listStyle="height:240px" listStyle="height:240px"
emptyMessage="دسته بندی وجود ندارد" emptyMessage="دسته بندی وجود ندارد"
emptyFilterMessage="دسته بندی وجود ندارد" emptyFilterMessage="دسته بندی وجود ندارد"
v-model="selectedCity" v-model="selectedCategory"
@change="selectCategory"
filterPlaceholder=" دسته بندی" filterPlaceholder=" دسته بندی"
:options="cities" :options="cities"
filter filter
optionLabel="name" optionLabel="name"
class="w-full" class="w-full"
/> />
</div>
<div></div> <Listbox
listStyle="height:240px"
emptyMessage="مدرس مورد نظر یافت نشد"
emptyFilterMessage="مدرس وجود ندارد"
v-model="selectedteacher"
@change="selectTeacher"
filterPlaceholder=" مدرس دوره"
:options="teachers"
filter
optionLabel="name"
class="w-full"
/>
</div> </div>
</div> </div>
@@ -144,11 +161,19 @@
:label="$t('create')" :label="$t('create')"
icon="pi pi-save" icon="pi pi-save"
severity="success" severity="success"
:loading="saving" :loading="creating"
@click="create(newCourse)" @click="create(newCourse)"
/> />
</div> </div>
</Panel> </Panel>
<Message
v-if="saving"
class="absolute right-12 bottom-8"
icon="pi pi-spinner-dotted"
severity="info"
>درحال آپلود تصویر...</Message
>
</section> </section>
</template> </template>
@@ -171,88 +196,142 @@ import { useStaticsStore } from "@/stores/statics";
import { useUploaderStore } from "@/stores/uploader"; import { useUploaderStore } from "@/stores/uploader";
import { useProductsStore } from "@/stores/products"; import { useProductsStore } from "@/stores/products";
import { useCategoriesStore } from "@/stores/categoreis"; import { useCategoriesStore } from "@/stores/categoreis";
import { useUsersStore } from "../stores/users";
const categoriesStore = useCategoriesStore(); const categoriesStore = useCategoriesStore();
const courseStore = useProductsStore(); const courseStore = useProductsStore();
const uploadStore = useUploaderStore(); const uploadStore = useUploaderStore();
import { useRoute } from "vue-router"; const userStore = useUsersStore();
import { useRoute, useRouter } from "vue-router";
const route = useRoute(); const route = useRoute();
const level = ref(null) const level = ref(null);
const selectedCategory = ref();
const selectedteacher = ref();
const language = ref("");
const saving = ref(false);
const creating = ref(false);
const selectCategory = () => {
newCourse.categories = selectedCategory.value.id;
};
const selectTeacher = () => {
newCourse.teacher = selectedteacher.value.id;
};
watchEffect(async () => { watchEffect(async () => {
const { page = 1, rows = 10 } = route.query; const { page = 1, rows = 10 } = route.query;
const categoriess = await categoriesStore.index(page, rows); const categoriess = await categoriesStore.index(page, rows);
console.log("cat", categoriess);
cities.value = categoriess; cities.value = categoriess;
}); });
watchEffect(async () => {
await userStore.index();
teachers.value = userStore.items.user;
});
const selectLevel = () => {
newCourse.level = level.value;
};
const selectLanguage = () => {
newCourse.language = language.value;
};
const selectedCity = ref(); const selectedCity = ref();
const cities = ref(); const cities = ref();
const teachers = ref();
const levels = reactive(["آسان", "متوسط", "سخت"]); const levels = reactive(["آسان", "متوسط", "سخت"]);
const newCourse = reactive({ const newCourse = reactive({
name: "", name: "",
description: "", description: "",
slug: "", slug: "تست",
duration: "", duration: "",
image: "", image: "",
categories: "", categories: "",
price: 0, price: null,
teacher: "", teacher: "",
completionPercentage: 0, completionPercentage: null,
level: "", level: "",
language: "", language: "",
countOfVideos: 0, countOfVideos: null,
totalFileSize: "", totalFileSize: "",
}); });
const chooseImage = async (val) => { const setSlug = () => {
console.log(val.target.files[0]); newCourse.slug = newCourse.name;
const uploader = await uploadStore.uploader(val.target.files[0]);
console.log('uploader', uploader.data.url);
newCourse.image = uploader.data.url;
console.log("تصویر آپلود شد");
console.log("upldr", uploader);
}; };
const create = async (data) => { const chooseImage = async (val) => {
if (newCourse.image === "") { saving.value = true;
return console.log("تصویر دوره وارد نشده است"); const uploader = await uploadStore.uploader(val.target.files[0]);
newCourse.image = uploader.data.url;
saving.value = false;
if (uploader.status === 200 || uploader.status === 201) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("تصویر آپلود شد"),
});
} else {
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("خطا در بارگذاری تصویر، مجدد تلاش کنید"),
});
} }
};
const router = useRouter();
const create = async (newCourse) => {
creating.value = true;
if ( if (
newCourse.name === "" || newCourse.name === "" ||
newCourse.description === "" || newCourse.description === "" ||
// newCourse.slug === "" ||
newCourse.duration === "" || newCourse.duration === "" ||
// newCourse.categories === "" || newCourse.categories === "" ||
// newCourse.teacher === "" || newCourse.price === null ||
newCourse.completionPercentage === 0 || newCourse.teacher === "" ||
// newCourse.level === "" || newCourse.completionPercentage === null ||
// newCourse.language === "" || newCourse.level === "" ||
newCourse.countOfVideos === "" || newCourse.language === "" ||
newCourse.countOfVideos === null ||
newCourse.totalFileSize === "" newCourse.totalFileSize === ""
) { ) {
return console.log("تمام مقادیر را وارد کنید"); creating.value = false;
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("تمام فیلد ها را وارد کنید"),
});
}
if (newCourse.image === "" || newCourse.image === null) {
creating.value = false;
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("تصویر دوره را وارد کنید"),
});
} }
newCourse.name = data.name;
newCourse.description = data.description;
newCourse.slug = data.name;
newCourse.duration = data.duration;
// newCourse.categories = data.categories;
newCourse.categories = "66587ae698d0a0db14693a5e";
// newCourse.teacher = data.teacher;
newCourse.teacher = "66530b0480ef484e2bd19220";
// newCourse.completionPercentage = data.completionPercentage;
newCourse.completionPercentage = 20;
// newCourse.level = data.level;
newCourse.level = "متوسط";
// newCourse.language = data.language;
newCourse.language = "فارسی";
newCourse.countOfVideos = data.countOfVideos;
newCourse.totalFileSize = data.totalFileSize;
// console.log("newCourse: ", newCourse);
const creat = await courseStore.create(newCourse); const creat = await courseStore.create(newCourse);
console.log("created", creat);
if (creat.message === "دوره با موفقیت ثبت شد") {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("دوره جدید با موفقیت ایجاد شد"),
});
router.push('/courses')
creating.value = false;
} else {
creating.value = false;
toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("مشکل در ایجاد دوره، مجدد تلاش کنید"),
});
}
}; };
// console.log("form", data);
// console.log("json", JSON.stringify(data));
// const { result } = await store.edit(data);
// console.log("res", result);
// };
const expandedKeys = ref({}); const expandedKeys = ref({});
const CategoryForm = defineAsyncComponent(() => const CategoryForm = defineAsyncComponent(() =>
@@ -265,9 +344,7 @@ const store = useStaticsStore();
watchEffect(async () => { watchEffect(async () => {
await store.index(); await store.index();
// console.log(store.items);
//data.videosCount = store.items.hours;
}); });
</script> </script>
@@ -300,7 +377,7 @@ const uploader = useUploaderStore();
const save = async () => { const save = async () => {
saving.value = true; saving.value = true;
console.log(form);
//Upload File //Upload File
if (typeof form.coverImage === "object") { if (typeof form.coverImage === "object") {
+41 -14
View File
@@ -55,14 +55,14 @@
<label>{{ $t("نوع تخفیف") }}</label> <label>{{ $t("نوع تخفیف") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div v-if="noe === 'مبلغ'" class="flex flex-col gap-1"> <div v-if="type === 'مبلغ'" class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="coupon.price" /> <InputText class="w-full" v-model="coupon.price" />
<label>{{ $t("مبلغ تخفیف") }}</label> <label>{{ $t("مبلغ تخفیف") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div v-if="noe === 'درصد'" class="flex flex-col gap-1"> <div v-if="type === 'درصد'" class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="coupon.percent" /> <InputText class="w-full" v-model="coupon.percent" />
<label>{{ $t("درصد تخفیف") }}</label> <label>{{ $t("درصد تخفیف") }}</label>
@@ -92,23 +92,29 @@
<script setup> <script setup>
import { useProductsStore } from "@/stores/products"; import { useProductsStore } from "@/stores/products";
import { useDiscountStore } from "@/stores/discounts" import { useDiscountStore } from "@/stores/discounts";
import { ref, watchEffect, reactive } from "vue"; import { ref, watchEffect, reactive, inject } from "vue";
import moment from 'moment-jalaali' import moment from "moment-jalaali";
const date= ref(null) import { useRouter } from "vue-router";
const date2= ref(null) const { dialog, confirm, toast, t } = inject("service");
const noe = ref('مبلغ') const date = ref(null);
const date2 = ref(null);
const noe = ref("مبلغ");
const store = useProductsStore(); const store = useProductsStore();
const discount = useDiscountStore(); const discount = useDiscountStore();
const data = ref(null); const data = ref(null);
const type = ref(null); const type = ref(null);
const router = useRouter()
const setType = (val) => { const setType = (val) => {
if (val.value === 'درصد') { if (val.value === "درصد") {
coupon.discountType = 'percent' coupon.discountType = "percent";
} else { } else {
coupon.discountType = 'price' coupon.discountType = "price";
}
} }
};
//const { dialog, confirm, toast, t } = inject("service");
const coupon = reactive({ const coupon = reactive({
startDate: "", startDate: "",
endDate: "", endDate: "",
@@ -116,11 +122,32 @@ const coupon = reactive({
discountType: type.value, discountType: type.value,
price: "", price: "",
percent: null, percent: null,
allUsers: true allUsers: true,
}); });
const saving = ref(false);
const create = async (coupon) => { const create = async (coupon) => {
const datas = await discount.add(coupon) saving.value = true
const datas = await discount.add(coupon);
saving.value = false;
if (datas === true) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("کد تخفیف ایجاد شد"),
});
router.push('/Coupons')
} else{
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: "خطا رخ داد، مجدد تلاش کنید",
});
} }
};
</script> </script>
<style scoped> <style scoped>
+189 -48
View File
@@ -1,23 +1,39 @@
<template> <template>
<section class="flex flex-col gap-4"> <section class="flex flex-col gap-4">
<Panel :header="$t('ویرایش دوره')"> <Panel :header="$t('ویرایش اطلاعات دوره')">
<div class="grid grid-cols-3 gap-7"> <div class="flex">
<div class="basis-4/6">
<div class="grid grid-cols-2 gap-7">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="course.name" /> <InputText
@change="setSlug"
class="w-full"
v-model="course.name"
/>
<label>{{ $t("title") }}</label> <label>{{ $t("title") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="course.language" /> <Dropdown
v-model="course.language"
@change="selectLanguage"
:options="['انگلیسی', 'فارسی']"
class="w-full"
/>
<!-- <InputText class="w-full" v-model="newCourse.language" /> -->
<label>{{ $t("زبان آموزش") }}</label> <label>{{ $t("زبان آموزش") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="course.duration" /> <InputText
v-model="course.duration"
class="w-full"
prefix="ساعت"
/>
<label>{{ $t("مدت زمان دوره") }}</label> <label>{{ $t("مدت زمان دوره") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -25,32 +41,28 @@
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<Dropdown <Dropdown
:options="levels"
:optionLabel="levels"
:optionValue="levels"
v-model="course.level" v-model="course.level"
@change="selectLevel"
:options="['مبتدی', 'متوسط', 'پیشرفته']"
class="w-full" class="w-full"
/> />
<label>{{ $t("سطح دوره") }}</label> <label>{{ $t("سطح دوره") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="course.teacher.name" />
<label>{{ $t("مدرس دوره") }}</label>
</FloatLabel>
</div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="course.completionPercentage" /> <InputText
class="w-full"
v-model="course.completionPercentage"
/>
<label>{{ $t("درصد تکمیل دوره") }}</label> <label>{{ $t("درصد تکمیل دوره") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="course.videosCount" /> <InputText class="w-full" v-model="course.countOfVideos" />
<label>{{ $t("تعداد ویدیو ها") }}</label> <label>{{ $t("تعداد ویدیو ها") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -62,20 +74,18 @@
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputGroup> <InputGroup>
<InputText class="pointer-events-none truncate" dir="ltr" /> <InputText class="pointer-events-none truncate" dir="ltr" />
<Button severity="secondary"> <Button severity="secondary" :loading="saving">
<i class="pi pi-image ml-2"></i> <i class="pi pi-image ml-2"></i>
<span>{{ $t("chooseImage") }}</span> <span>{{ $t("chooseImage") }}</span>
<input <input
:value="course.image"
type="file" type="file"
:value="course.image"
class="opacity-0 absolute inset-0 cursor-pointer" class="opacity-0 absolute inset-0 cursor-pointer"
accept="image/*" @input="chooseImage"
/> />
</Button> </Button>
</InputGroup> </InputGroup>
@@ -83,77 +93,153 @@
</FloatLabel> </FloatLabel>
</div> </div>
</div> </div>
<div class="w-full mt-7">
<div class="flex w-full mt-7">
<div class="basis-4/6">
<FloatLabel> <FloatLabel>
<Editor <Editor
v-model="course.description" v-model="course.description"
placeholder="توضیحات" placeholder="توضیحات"
editorStyle="height: 320px" editorStyle="height: 315px"
/> />
</FloatLabel> </FloatLabel>
</div> </div>
</div>
<div class="basis-2/6 ms-8"> <div class="basis-2/6 flex flex-col gap-7 ms-7">
<div class="card flex flex-col gap-2 justify-center "> <div class="flex flex-col gap-1">
<FloatLabel>
<InputText class="w-full" v-model="course.price" />
<label>{{ $t("قیمت دوره") }}</label>
</FloatLabel>
</div>
<span class="hidden">{{ course.price }}</span>
<Listbox <Listbox
listStyle="height:240px" listStyle="height:240px"
emptyMessage="دسته بندی وجود ندارد" emptyMessage="دسته بندی وجود ندارد"
emptyFilterMessage="دسته بندی وجود ندارد" emptyFilterMessage="دسته بندی وجود ندارد"
v-model="selectedCategory"
@change="selectCategory"
filterPlaceholder=" دسته بندی" filterPlaceholder=" دسته بندی"
:options="cities" :options="cities"
filter filter
optionLabel="name" optionLabel="name"
class="w-full" class="w-full"
/> />
</div>
<div></div> <Listbox
listStyle="height:240px"
emptyMessage="مدرس مورد نظر یافت نشد"
emptyFilterMessage="مدرس وجود ندارد"
v-model="selectedteacher"
@change="selectTeacher"
filterPlaceholder=" مدرس دوره"
:options="teachers"
filter
optionLabel="name"
class="w-full"
/>
</div> </div>
</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"> <div class="flex pt-4 justify-end">
<Button <Button
:label="$t('edit')" :label="$t('edit')"
icon="pi pi-save" icon="pi pi-save"
severity="success" severity="success"
:loading="saving" :loading="creating"
@click="create(course)" @click="editCourse"
/> />
</div> </div>
</Panel> </Panel>
<Message
v-if="saving"
class="absolute right-12 bottom-8"
icon="pi pi-spinner-dotted"
severity="info"
>درحال آپلود تصویر...</Message
>
</section> </section>
</template> </template>
<script setup> <script setup>
import { useProductsStore } from "@/stores/products"; import { useProductsStore } from "@/stores/products";
import { ref, watchEffect, reactive } from "vue"; 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();
const courseStore = useProductsStore();
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);
const store = useProductsStore(); const store = useProductsStore();
const data = ref(null); const data = ref(null);
const cities = ref();
const selectCategory = () => {
course.categories = selectedCategory.value.id;
};
const selectTeacher = () => {
course.teacher = selectedteacher.value.id;
};
const course = reactive({ const course = reactive({
id: "", id: route.params.id,
name: "", name: "",
description: "", description: "",
slug: "", slug: "",
duration: "", duration: "",
image: "", image: "",
categories: "", categories: "",
price: 0, price: "",
teacher: "", teacher: "",
completionPercentage: 0, completionPercentage: null,
level: "", level: "",
language: "", language: "",
countOfVideos: 0, countOfVideos: null,
totalFileSize: "", totalFileSize: "",
}); });
import { useRoute } from "vue-router";
const route = useRoute();
watchEffect(async () => {
const { page = 1, rows = 10 } = route.query;
const categoriess = await categoriesStore.index(page, rows);
cities.value = categoriess;
});
watchEffect(async () => {
await userStore.index();
teachers.value = userStore.items.user;
});
watchEffect(async () => { watchEffect(async () => {
data.value = await store.edit(route.params.id); data.value = await store.edit(route.params.id);
course.id = data.value.course.id;
course.name = data.value.course.name; course.name = data.value.course.name;
course.description = data.value.course.description; course.description = data.value.course.description;
course.slug = data.value.course.slug; course.slug = data.value.course.slug;
@@ -171,13 +257,68 @@ watchEffect(async () => {
// const levels = reactive(["آسان", "متوسط", "سخت"]); // const levels = reactive(["آسان", "متوسط", "سخت"]);
const chooseImage = async (el) => { const chooseImage = async (val) => {
// const uploader = await uploadStore.uploader(el.target.value); saving.value = true;
// //console.log('uploader', uploader.data.url); const uploader = await uploadStore.uploader(val.target.files[0]);
// newCourse.image = uploader.data.url; course.image = uploader.data.url;
// console.log("تصویر آپلود شد"); saving.value = false;
// console.log('upldr',uploader); if (uploader.status === 200 || uploader.status === 201) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("تصویر آپلود شد"),
});
} else {
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("خطا در بارگذاری تصویر، مجدد تلاش کنید"),
});
}
};
const { dialog, confirm, toast, t } = inject("service");
const router = useRouter()
const editCourse = async (data) => {
creating.value = true;
if (
course.name === "" ||
course.description === "" ||
course.duration === "" ||
course.categories === "" ||
course.price === null ||
course.teacher === "" ||
course.completionPercentage === null ||
course.level === "" ||
course.language === "" ||
course.countOfVideos === null ||
course.totalFileSize === ""
) {
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("تمام فیلد ها را وارد کنید"),
});
}
if (course.image === "" || course.image === null) {
creating.value = false;
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("تصویر دوره را وارد کنید"),
});
}
const update = await courseStore.update(course);
creating.value = false
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("دوره با موفقیت ویرایش شد"),
});
router.push('/courses')
}; };
const create = async (data) => {};
</script> </script>
+26 -3
View File
@@ -85,14 +85,15 @@
<script setup> <script setup>
import { useDiscountStore } from "@/stores/discounts"; import { useDiscountStore } from "@/stores/discounts";
import { ref, watchEffect, reactive } from "vue"; import { ref, watchEffect, reactive, inject } from "vue";
import moment from "moment-jalaali"; import moment from "moment-jalaali";
import { useRoute } from "vue-router"; import { useRoute, useRouter } from "vue-router";
const date = ref(null); const date = ref(null);
const date2 = ref(null); const date2 = ref(null);
const noe = ref("مبلغ"); const noe = ref("مبلغ");
const store = useDiscountStore(); const store = useDiscountStore();
const data = ref(); const data = ref();
const router = useRouter()
const setType = (val) => { const setType = (val) => {
if (val.value === 'درصد') { if (val.value === 'درصد') {
discount.discountType = 'percent' discount.discountType = 'percent'
@@ -110,13 +111,35 @@ const discount = reactive({
percent: 0, percent: 0,
allUsers: true, allUsers: true,
}); });
const { dialog, confirm, toast, t } = inject("service");
const route = useRoute(); const route = useRoute();
const saving = ref(false);
const create = async(val) => { const create = async(val) => {
saving.value = true;
const posts = await store.update(val) const posts = await store.update(val)
saving.value = false;
if (posts === true) {
toast.add({
life: 2000,
severity: "success",
summary: t("successful"),
detail: t("تخفیف ویرایش شد"),
});
router.push('/Coupons')
}else{
return toast.add({
life: 2000,
severity: "error",
summary: t("error"),
detail: t("خطا، محدد تلاش کنید"),
});
}
}; };
watchEffect(async () => { watchEffect(async () => {
data.value = await store.getDiscount(route.params.id); data.value = await store.getDiscount(route.params.id);
console.log("this", data.value);
discount.id = data.value.id; discount.id = data.value.id;
(discount.startDate = data.value.startDate), (discount.startDate = data.value.startDate),
(discount.endDate = data.value.endDate), (discount.endDate = data.value.endDate),
+83 -2
View File
@@ -1,8 +1,89 @@
<template> <template>
<h1>س</h1> <Panel :header="$t('سوالات متداول')">
<template #icons>
<Button
icon="pi pi-plus"
:label="$t('ایجاد آیتم جدید')"
severity="success"
@click="create()"
/>
</template>
<DataTable :value="store.items">
<Column field="title" :header="$t('title')" />
<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
@click="remove(data)"
icon="pi pi-trash"
rounded
text
severity="danger"
:loading="removing"
/>
</div>
</template>
</Column>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</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> </template>
<script setup>
<script setup>
import { defineAsyncComponent, inject, ref, watchEffect } from "vue";
import { useFaqStore } from "@/stores/faq";
const store = useFaqStore();
const { toast, dialog, confirm, t } = inject('service');
const FaqDetails = defineAsyncComponent(() => import('../components/FaqDetails.vue'));
const AddFaq = defineAsyncComponent(() => import('../components/AddFaq.vue'));
const removing = ref(false)
const show = (product) => {
dialog.open(FaqDetails, {
props: { modal: true, closable: true, header: t('جزئیات آیتم') },
data: {
product
}
});
}
const create = () =>{
dialog.open(AddFaq, {
props: { modal: true, closable: true, header: t('ایجاد آیتم جدید') },
});
}
const remove = async (item) => {
removing.value = true
const {data} = await store.remove(item.id)
removing.value = false
await store.index()
};
watchEffect(async() =>{
await store.index()
})
</script> </script>
+41 -19
View File
@@ -4,13 +4,13 @@
<div class="grid grid-cols-1 gap-7"> <div class="grid grid-cols-1 gap-7">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="data.title" /> <InputText class="w-full" v-model="data.header" />
<label>{{ $t("متن هدر") }}</label> <label>{{ $t("متن هدر") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<Textarea v-model="data.description" rows="5" class="w-full" /> <Textarea v-model="data.titleText" rows="5" class="w-full" />
<label>{{ $t("توضیحات هدر") }}</label> <label>{{ $t("توضیحات هدر") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -30,7 +30,7 @@
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText class="w-full" v-model="data.rates" /> <InputText class="w-full" v-model="data.rate" />
<label>{{ $t("امتیاز دانشجو ها") }}</label> <label>{{ $t("امتیاز دانشجو ها") }}</label>
</FloatLabel> </FloatLabel>
</div> </div>
@@ -79,24 +79,45 @@ import {
} from "vue"; } from "vue";
import { useStaticsStore } from "@/stores/statics"; import { useStaticsStore } from "@/stores/statics";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
const data = reactive({ const data = reactive({
student: 0, header: "",
title: 0, titleText: "",
expertise: 0, student: null,
about: "string", title: null,
rate: null,
expertise: null,
about: "",
}); });
const save = async (data) => { const saving = ref(false);
// console.log('form', data); async function save(data) {
// console.log('json', JSON.stringify(data)); saving.value = true;
const result = await store.edit(data); const result = await store.edit(data);
// console.log("res", result);
};
const expandedKeys = ref({});
const CategoryForm = defineAsyncComponent(() => if (result.data === true) {
import("../components/CategoryForm.vue") toast.add({
); life: 2000,
severity: "success",
summary: t("successful"),
detail: t("اطلاعات با موفقیت ویرایش شد"),
});
} else {
toast.add({
severity: "error",
life: 2000,
detail: t("خطا، مجدد تلاش کنید"),
summary: t("خطا، مجدد تلاش کنید"),
});
}
saving.value = false;
}
// const expandedKeys = ref({});
// const CategoryForm = defineAsyncComponent(() =>
// import("../components/CategoryForm.vue")
// );
const { dialog, confirm, toast, t } = inject("service"); const { dialog, confirm, toast, t } = inject("service");
@@ -104,11 +125,12 @@ const store = useStaticsStore();
watchEffect(async () => { watchEffect(async () => {
await store.index(); await store.index();
// console.log(store.items); data.header = store.items.header;
data.student = store.items.student; data.student = store.items.student;
data.title = store.items.title; data.title = store.items.title;
data.expertise = store.items.expertise; data.expertise = store.items.expertise;
data.about = store.items.about; data.about = store.items.about;
//data.videosCount = store.items.hours; data.titleText = store.items.titleText;
data.rate = store.items.rate;
}); });
</script> </script>
+66 -37
View File
@@ -2,83 +2,112 @@
<Card <Card
v-if="!changeForm" v-if="!changeForm"
class="m-auto w-96" class="m-auto w-96"
:pt="{ title: 'text-center pt-4', content: 'flex flex-col gap-9 pt-8 h-30' }" :pt="{
title: 'text-center pt-4',
content: 'flex flex-col gap-9 pt-8 h-30',
}"
> >
<template #title> {{ $t('login') }} </template> <template #title> {{ $t("login") }} </template>
<template #content> <template #content>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText v-model="number" class="w-full ltr" /> <InputText v-model="number" class="w-full ltr" />
<label>{{ $t('phoneNumber') }}</label> <label>{{ $t("phoneNumber") }}</label>
</FloatLabel> </FloatLabel>
<small v-if="errors.length > 0">{{ errors }}</small> <small v-if="errors.length > 0">{{ errors }}</small>
</div> </div>
</template> </template>
<template #footer> <template #footer>
<Button :label="$t('sendOtp')" class="w-full" :loading="loading" @click="login()" /> <Button
:label="$t('sendOtp')"
class="w-full"
:loading="loading"
@click="login()"
/>
</template> </template>
</Card> </Card>
<Card <Card
v-else v-else
class="m-auto w-96" class="m-auto w-96"
:pt="{ title: 'text-center pt-4', content: 'flex flex-col gap-9 pt-8 h-30' }" :pt="{
title: 'text-center pt-4',
content: 'flex flex-col gap-9 pt-8 h-30',
}"
> >
<template #title> {{ $t('otp') }} </template> <template #title> {{ $t("otp") }} </template>
<template #content> <template #content>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<FloatLabel> <FloatLabel>
<InputText v-model="code" class="w-full ltr" /> <InputText v-model="code" class="w-full ltr" />
<label>{{ $t('otp') }}</label> <label>{{ $t("otp") }}</label>
</FloatLabel> </FloatLabel>
<small v-if="errors.length > 0">{{ errors }}</small> <small v-if="errors.length > 0">{{ errors }}</small>
</div> </div>
</template> </template>
<template #footer> <template #footer>
<Button :label="$t('login')" class="w-full" :loading="loading" @click="checkOtp()" /> <Button
:label="$t('login')"
class="w-full"
:loading="loading"
@click="checkOtp()"
/>
</template> </template>
</Card> </Card>
</template> </template>
<script setup> <script setup>
import { inject, reactive, ref } from 'vue' import { inject, reactive, ref } from "vue";
import { useAdminsStore } from '@/stores/admins' import { useAdminsStore } from "@/stores/admins";
import { useRouter } from 'vue-router' import { useRouter } from "vue-router";
const { toast, t } = inject('service') const { toast, t } = inject("service");
const loading = ref(false) const loading = ref(false);
const changeForm = ref(false) const changeForm = ref(false);
const store = useAdminsStore() const store = useAdminsStore();
const errors = ref({}) const errors = ref({});
const number = ref(null) const number = ref(null);
const code = ref(1234) const code = ref(1234);
const router = useRouter() const router = useRouter();
const login = async () => { const login = async () => {
loading.value = true loading.value = true;
const { status, data } = await store.login(number) const { status, data } = await store.login(number);
loading.value = false loading.value = false;
if (status === 200) { if (status === 200) {
changeForm.value = true changeForm.value = true;
toast.add({ toast.add({
severity: 'success', severity: "success",
life: 2000, life: 2000,
detail: t('sendOtpSuccessfully'), detail: t("sendOtpSuccessfully"),
summary: t('successful'), summary: t("successful"),
closable: false closable: false,
}) });
}
} }
};
const checkOtp = async () => { const checkOtp = async () => {
loading.value = true loading.value = true;
const { status, data } = await store.sendOtp(number, code) const { status, data } = await store.sendOtp(number, code);
console.log('com data', data) console.log("login", data);
console.log('com status', status)
loading.value = false loading.value = false;
if (status === 200) { if (status === 200) {
if (data.roles[0] === "Admin") {
toast.add({ toast.add({
severity: 'success', life: 2000, detail: t('loginSuccessfully'), summary: t('successful'), closable: false severity: "success",
}) life: 2000,
setTimeout(() => router.push('/'), 300); detail: t("loginSuccessfully"),
summary: t("successful"),
closable: false,
});
setTimeout(() => router.push("/"), 300);
} else {
return toast.add({
severity: "error",
life: 2000,
detail: t("شما مجوز ورود ندارید"),
summary: t("error"),
});
} }
} }
};
// // setTimeout(() => router.push('/'), 300); // // setTimeout(() => router.push('/'), 300);
// console.log(data); // console.log(data);
+8 -6
View File
@@ -5,8 +5,10 @@
<Panel toggleable> <Panel toggleable>
<template #header > <template #header >
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<Avatar :image="item.createdBy.avatarMedia" size="large" shape="circle" /> <Avatar v-if="item.createdBy.avatarMedia" :image="item.createdBy.avatarMedia" size="large" shape="circle" />
<span class="font-bold">{{item.createdBy.name}}</span> <Avatar v-else image="/src/assets/alt.png" size="large" shape="circle" />
<span v-if="item.createdBy.name" class="font-bold">{{item.createdBy.name}}</span>
<span v-else class="font-bold">کاربر بدون نام</span>
<Tag v-if="item.createdBy.roles[0] === 'Admin'" class="gap-1" icon="pi pi-user" severity="info" value="ادمین"></Tag> <Tag v-if="item.createdBy.roles[0] === 'Admin'" class="gap-1" icon="pi pi-user" severity="info" value="ادمین"></Tag>
<Tag v-else class="gap-1" icon="pi pi-user" severity="success" value="کاربر"></Tag> <Tag v-else class="gap-1" icon="pi pi-user" severity="success" value="کاربر"></Tag>
</div> </div>
@@ -46,19 +48,19 @@ import { jDate } from "@/utils/jDate";
const store = useTicketsStore(); const store = useTicketsStore();
const messages = ref([]); const messages = ref([]);
const route = useRoute(); const route = useRoute();
console.log(route.params.id);
const send = async (value) => { const send = async (value) => {
console.log(value);
const sended = await store.reply({ const sended = await store.reply({
content: value, content: value,
ticketId: route.params.id, ticketId: route.params.id,
}); });
console.log('sended', sended);
}; };
watchEffect(async () => { watchEffect(async () => {
const data = await store.ticket(route.params.id); const data = await store.ticket(route.params.id);
messages.value = data.ticketMessages.reverse() messages.value = data.ticketMessages.reverse()
console.log("mess", data);
}); });
</script> </script>
+3 -3
View File
@@ -47,17 +47,17 @@ const data = ref([])
const route = useRoute() const route = useRoute()
const chat = (data) =>{ const chat = (data) =>{
router.push(`/tickets/${data.id}`) router.push(`/tickets/${data.id}`)
console.log(data.id);
} }
const closeTicket = async(data) =>{ const closeTicket = async(data) =>{
// console.log(data.id); // console.log(data.id);
const closess = await store.close(data.id) const closess = await store.close(data.id)
console.log('close',closess);
await store.index() await store.index()
} }
watchEffect(async() =>{ watchEffect(async() =>{
const { page = 1, pageSize = 10 } = route.query; const { page = 1, pageSize = 10 } = route.query;
data.value = await store.index(page, pageSize) data.value = await store.index(page, pageSize)
console.log(data.value[1]);
}) })
</script> </script>
+4 -4
View File
@@ -1,6 +1,6 @@
<template> <template>
<Panel :header="$t('users')"> <Panel :header="$t('users')">
<DataTable :value="store.items"> <DataTable :value="store.items.user">
<Column class="w-16"> <Column class="w-16">
<template #body="{ data: { avatarMedia } }"> <template #body="{ data: { avatarMedia } }">
<Avatar <Avatar
@@ -38,7 +38,7 @@
<Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator> <Paginator :rows="store.total" :totalRecords="store.total" :rowsPerPageOptions="[5, 20, 30]"></Paginator>
--> -->
<Pagination :total="store.total" /> <Pagination :total="store.items.count" />
</template> </template>
</DataTable> </DataTable>
</Panel> </Panel>
@@ -49,7 +49,7 @@
import Pagination from "@/components/Pagination.vue"; import Pagination from "@/components/Pagination.vue";
import { useUsersStore } from "@/stores/users"; import { useUsersStore } from "@/stores/users";
import { jDate } from "@/utils/jDate"; import { jDate } from "@/utils/jDate";
console.log("users", useUsersStore);
import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue"; import { inject, reactive, defineAsyncComponent, ref, watchEffect } from "vue";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
@@ -82,5 +82,5 @@ watchEffect(async () => {
await store.index(page, pageSize); await store.index(page, pageSize);
const pages = await store.page(); const pages = await store.page();
}); });
console.log(store);
</script> </script>
+2 -8
View File
@@ -128,7 +128,7 @@
const remove = (data) => { const remove = (data) => {
console.log('done', data._id)
confirm.require({ confirm.require({
message: t('Are you sure you want to proceed?'), message: t('Are you sure you want to proceed?'),
header: t('Danger Zone'), header: t('Danger Zone'),
@@ -140,7 +140,7 @@
defaultFocus: 'reject', defaultFocus: 'reject',
accept: async () => { accept: async () => {
const deleted = await store.delete(data._id) const deleted = await store.delete(data._id)
console.log(deleted);
await store.index() await store.index()
} }
// product.removing = true // product.removing = true
@@ -165,15 +165,9 @@
watchEffect(async() => { watchEffect(async() => {
const { page = 1, pageSize = 10 } = route.query const { page = 1, pageSize = 10 } = route.query
await store.index(page, pageSize) await store.index(page, pageSize)
console.log(store.items[2].image);
console.log(String.fromCharCode(store.items[2].image)).join('');
// store.items.forEach((product) => {
// product.status = { value: t('active'), severity: 'success' };
// })
}) })
+32
View File
@@ -0,0 +1,32 @@
<template>
<Panel :header="$t('تاریخچه ی تخفیف ها')">
<DataTable :value="store.items.discount">
<Column field="code" :header="$t('کد تخفیف')" />
<Column
:field="({ admin }) => admin.name"
:header="$t('سازنده')"
/>
<Column :field="({startDate}) => jDate(startDate)" :header="$t('تاریخ شروع')" />
<Column :field="({endDate}) => jDate(endDate)" :header="$t('تاریخ پایان')" />
<Column
field="statusText"
:header="$t('status')"
/>
<template #empty>
<p>{{ store.fetching ? $t("loading") : $t("emptyTable") }}</p>
</template>
</DataTable>
</Panel>
</template>
<script setup>
import { watchEffect } from "vue";
import { useDiscountStore } from "../stores/discounts";
const store = useDiscountStore();
import { jDate } from "@/utils/jDate";
watchEffect(async () => {
await store.history();
console.log(store.items.discount);
});
</script>