This commit is contained in:
2025-01-04 21:01:04 +03:30
parent aad48fca63
commit 57d328bbfe
33 changed files with 1752 additions and 261 deletions
@@ -0,0 +1,63 @@
<script setup>
import { Listbox, InputText } from 'primevue';
import { onBeforeMount, ref } from 'vue';
import { CategoryService } from '@/service/CategoryService';
import ImageUploader from '@/views/uikit/ImageUploader.vue';
import ImageGallery from '@/views/uikit/ImageGallery.vue';
const categories = ref([]);
const selectedCategory = ref();
const belongingName = ref('');
const description = ref('');
const imageNames = ref([]);
const documentNames = ref([]);
const mainImageName = ref([]);
const tags = ref('');
const signedImageUrls = ref([{ url: 'https://upload.hitromall.com/product-images/0ff87b0cc333539a361e007d20aa9685-co3.jpg' }]);
onBeforeMount(async () => {
categories.value = await CategoryService.getAllCategories();
});
</script>
<template>
<div className="card">
<div class="font-semibold text-xl mb-4">ایجاد اموال</div>
<p>از طریق این صفحه می توانید اموال جدید را به سوابق اضافه کنید.</p>
<div class="flex my-6">
<div class="w-3/4 ml-3">
<div class="">
<label> نام دارایی </label>
<InputText v-model="belongingName" class="w-full my-2" placeholder="مثلا: 100 عدد کولر گازی" />
</div>
<div class="my-6">
<label> توضیحات </label>
<Textarea v-model="description" class="w-full my-2" placeholder="مثلا: در تاریخ 1403/09/11 خریداری شد." rows="6" />
</div>
<div class="my-6" v-if="signedImageUrls.length">
<label> تصاویر </label>
<ImageGallery v-model="signedImageUrls" gallery-id="images" />
</div>
<div class="my-6" v-if="signedImageUrls.length">
<label> فایل ها و مدارک </label>
</div>
</div>
<div class="w-1/4">
<div>
<label> دسته بندی </label>
<Listbox v-model="selectedCategory" :options="categories" optionLabel="nativeName" optionValue="_id" checkmark :highlightOnSelect="false" class="w-full my-2" />
</div>
<div class="my-6">
<label> تصویر اصلی </label>
<ImageUploader v-model="mainImageName" :max-files="1" mode="private" delete-endpoint="/api/files/bucket/delete/belonging-images" upload-endpoint="/api/files/bucket/upload-multi-filepond/belonging-images" />
</div>
<div class="my-8">
<label> برچسب ها </label>
<Chips v-model="tags" separator="," class="w-full" placeholder="مثلا: زمین، ملک، کیانپارس..." />
</div>
</div>
</div>
<div class="flex justify-center">
<Button>ذخیره</Button>
</div>
</div>
</template>
@@ -0,0 +1,279 @@
<script setup>
import { ProductService } from '@/service/ProductService';
import { FilterMatchMode } from '@primevue/core/api';
import { useToast } from 'primevue/usetoast';
import { onMounted, ref } from 'vue';
import { BelongingService } from '@/service/BelongingService';
onMounted(() => {
BelongingService.getAllBelongings().then((data) => (belongings.value = data));
});
const toast = useToast();
const dt = ref();
const belongings = ref();
const productDialog = ref(false);
const deleteProductDialog = ref(false);
const deleteProductsDialog = ref(false);
const product = ref({});
const selectedBelongings = ref();
const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }
});
const submitted = ref(false);
const statuses = ref([
{ label: 'INSTOCK', value: 'instock' },
{ label: 'LOWSTOCK', value: 'lowstock' },
{ label: 'OUTOFSTOCK', value: 'outofstock' }
]);
function formatCurrency(value) {
if (value) return value.toLocaleString('en-US', { style: 'currency', currency: 'USD' });
return;
}
function openNew() {
product.value = {};
submitted.value = false;
productDialog.value = true;
}
function hideDialog() {
productDialog.value = false;
submitted.value = false;
}
function saveProduct() {
submitted.value = true;
if (product?.value.name?.trim()) {
if (product.value.id) {
product.value.inventoryStatus = product.value.inventoryStatus.value ? product.value.inventoryStatus.value : product.value.inventoryStatus;
products.value[findIndexById(product.value.id)] = product.value;
toast.add({ severity: 'success', summary: 'Successful', detail: 'Product Updated', life: 3000 });
} else {
product.value.id = createId();
product.value.code = createId();
product.value.image = 'product-placeholder.svg';
product.value.inventoryStatus = product.value.inventoryStatus ? product.value.inventoryStatus.value : 'INSTOCK';
products.value.push(product.value);
toast.add({ severity: 'success', summary: 'Successful', detail: 'Product Created', life: 3000 });
}
productDialog.value = false;
product.value = {};
}
}
function editProduct(prod) {
product.value = { ...prod };
productDialog.value = true;
}
function confirmDeleteProduct(prod) {
product.value = prod;
deleteProductDialog.value = true;
}
function deleteProduct() {
products.value = products.value.filter((val) => val.id !== product.value.id);
deleteProductDialog.value = false;
product.value = {};
toast.add({ severity: 'success', summary: 'Successful', detail: 'Product Deleted', life: 3000 });
}
function findIndexById(id) {
let index = -1;
for (let i = 0; i < products.value.length; i++) {
if (products.value[i].id === id) {
index = i;
break;
}
}
return index;
}
function createId() {
let id = '';
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (var i = 0; i < 5; i++) {
id += chars.charAt(Math.floor(Math.random() * chars.length));
}
return id;
}
function exportCSV() {
dt.value.exportCSV();
}
function confirmDeleteSelected() {
deleteProductsDialog.value = true;
}
function deleteSelectedBelongings() {
products.value = products.value.filter((val) => !selectedBelongings.value.includes(val));
deleteProductsDialog.value = false;
selectedBelongings.value = null;
toast.add({ severity: 'success', summary: 'Successful', detail: 'Products Deleted', life: 3000 });
}
function getStatusLabel(status) {
switch (status) {
case 'INSTOCK':
return 'success';
case 'LOWSTOCK':
return 'warn';
case 'OUTOFSTOCK':
return 'danger';
default:
return null;
}
}
</script>
<template>
<div>
<div class="card">
<Toolbar class="mb-6">
<template #start>
<Button label="ساخت دارایی جدید" icon="pi pi-plus" severity="secondary" class="mr-2 mx-3" @click="openNew" />
<Button label="حذف دارایی" icon="pi pi-trash" severity="secondary" @click="confirmDeleteSelected" :disabled="!selectedBelongings || !selectedBelongings.length" />
</template>
<template #end>
</template>
</Toolbar>
<DataTable
ref="dt"
v-model:selection="selectedBelongings"
:value="belongings"
dataKey="id"
:paginator="true"
:rows="20"
:filters="filters"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
:rowsPerPageOptions="[20, 40, 80]"
currentPageReportTemplate="نمایش {first} تا {last} از {totalRecords} دارایی"
>
<template #header>
<div class="flex flex-wrap gap-2 items-center justify-between">
<h4 class="m-0">مدیریت اموال</h4>
<IconField>
<InputIcon>
<i class="pi pi-search" />
</InputIcon>
<InputText v-model="filters['global'].value" placeholder="جستجو..." />
</IconField>
</div>
</template>
<Column selectionMode="multiple" style="width: 3rem" :exportable="false"></Column>
<Column header="تصویر">
<template #body="slotProps">
<img :src="`https://primefaces.org/cdn/primevue/images/product/${slotProps.data.image}`" :alt="slotProps.data.image" class="rounded" style="width: 64px" />
</template>
</Column>
<Column field="belongingCode" header="کد" style="min-width: 12rem"></Column>
<Column field="name" header="نام" sortable style="min-width: 16rem"></Column>
<Column field="category" header="دسته بندی" sortable style="min-width: 10rem">
<template #body="slotProps">
{{ slotProps.data.nativeName }}
</template>
</Column>
<Column :exportable="false" style="min-width: 12rem">
<template #body="slotProps">
<Button icon="pi pi-pencil" outlined rounded class="mr-2" @click="editProduct(slotProps.data)" />
<Button icon="pi pi-trash" outlined rounded severity="danger" @click="confirmDeleteProduct(slotProps.data)" />
</template>
</Column>
</DataTable>
</div>
<Dialog v-model:visible="productDialog" :style="{ width: '450px' }" header="Product Details" :modal="true">
<div class="flex flex-col gap-6">
<img v-if="product.image" :src="`https://primefaces.org/cdn/primevue/images/product/${product.image}`" :alt="product.image" class="block m-auto pb-4" />
<div>
<label for="name" class="block font-bold mb-3">Name</label>
<InputText id="name" v-model.trim="product.name" required="true" autofocus :invalid="submitted && !product.name" fluid />
<small v-if="submitted && !product.name" class="text-red-500">Name is required.</small>
</div>
<div>
<label for="description" class="block font-bold mb-3">Description</label>
<Textarea id="description" v-model="product.description" required="true" rows="3" cols="20" fluid />
</div>
<div>
<label for="inventoryStatus" class="block font-bold mb-3">Inventory Status</label>
<Select id="inventoryStatus" v-model="product.inventoryStatus" :options="statuses" optionLabel="label" placeholder="Select a Status" fluid></Select>
</div>
<div>
<span class="block font-bold mb-4">Category</span>
<div class="grid grid-cols-12 gap-4">
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category1" v-model="product.category" name="category" value="Accessories" />
<label for="category1">Accessories</label>
</div>
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category2" v-model="product.category" name="category" value="Clothing" />
<label for="category2">Clothing</label>
</div>
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category3" v-model="product.category" name="category" value="Electronics" />
<label for="category3">Electronics</label>
</div>
<div class="flex items-center gap-2 col-span-6">
<RadioButton id="category4" v-model="product.category" name="category" value="Fitness" />
<label for="category4">Fitness</label>
</div>
</div>
</div>
<div class="grid grid-cols-12 gap-4">
<div class="col-span-6">
<label for="price" class="block font-bold mb-3">Price</label>
<InputNumber id="price" v-model="product.price" mode="currency" currency="USD" locale="en-US" fluid />
</div>
<div class="col-span-6">
<label for="quantity" class="block font-bold mb-3">Quantity</label>
<InputNumber id="quantity" v-model="product.quantity" integeronly fluid />
</div>
</div>
</div>
<template #footer>
<Button label="Cancel" icon="pi pi-times" text @click="hideDialog" />
<Button label="Save" icon="pi pi-check" @click="saveProduct" />
</template>
</Dialog>
<Dialog v-model:visible="deleteProductDialog" :style="{ width: '450px' }" header="Confirm" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="product"
>Are you sure you want to delete <b>{{ product.name }}</b
>?</span
>
</div>
<template #footer>
<Button label="No" icon="pi pi-times" text @click="deleteProductDialog = false" />
<Button label="Yes" icon="pi pi-check" @click="deleteProduct" />
</template>
</Dialog>
<Dialog v-model:visible="deleteProductsDialog" :style="{ width: '450px' }" header="Confirm" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="product">Are you sure you want to delete the selected products?</span>
</div>
<template #footer>
<Button label="No" icon="pi pi-times" text @click="deleteProductsDialog = false" />
<Button label="Yes" icon="pi pi-check" text @click="deleteselectedBelongings" />
</template>
</Dialog>
</div>
</template>
+194
View File
@@ -0,0 +1,194 @@
<script setup>
import { FilterMatchMode } from '@primevue/core/api';
import { onMounted, ref } from 'vue';
import { CategoryService } from '@/service/CategoryService';
import { useToast } from 'primevue/usetoast';
const toast = useToast();
import axios from 'axios';
onMounted(() => {
CategoryService.getAllCategories().then((data) => (categories.value = data));
});
const dt = ref();
const categories = ref();
const categoryDialog = ref(false);
const deleteCategoryDialog = ref(false);
const deleteProductsDialog = ref(false);
const category = ref({});
const selectedCategories = ref();
const filters = ref({
global: { value: null, matchMode: FilterMatchMode.CONTAINS }
});
const submitted = ref(false);
function openNew() {
category.value = {};
submitted.value = false;
categoryDialog.value = true;
}
function hideDialog() {
categoryDialog.value = false;
submitted.value = false;
}
async function saveCategory() {
submitted.value = true;
let status = 'failed';
if (category?.value._id?.trim()) {
status = await CategoryService.updateCategory(category.value._id, category.value);
} else {
status = await CategoryService.createCategory(category.value);
}
if (status === 'success') {
toast.add({ severity: 'success', summary: 'موفق', detail: 'دسته بندی جدید ساخته شد.', life: 3000 });
} else {
toast.add({ severity: 'error', summary: 'ناموفق', detail: status, life: 3000 });
}
categoryDialog.value = false;
category.value = {};
CategoryService.getAllCategories().then((data) => (categories.value = data));
}
function editCategory(cat) {
category.value = { ...cat };
categoryDialog.value = true;
}
function confirmDeleteCategory(prod) {
category.value = prod;
deleteCategoryDialog.value = true;
}
async function deleteCategory() {
const config = {
method: 'delete',
url: '/api/categories/admin/delete/' + category.value._id,
data: category.value,
withCredentials: true
};
try {
const response = await axios(config);
toast.add({ severity: 'success', summary: 'موفق', detail: 'دسته بندی جدید حذف شد.', life: 3000 });
} catch (err) {
toast.add({ severity: 'error', summary: 'ناموفق', detail: err.response.data.message, life: 3000 });
}
CategoryService.getAllCategories().then((data) => (categories.value = data));
deleteCategoryDialog.value = false;
category.value = {};
}
function exportCSV() {
dt.value.exportCSV();
}
function confirmDeleteSelected() {
deleteProductsDialog.value = true;
}
function deleteSelectedCategories() {
categories.value = categories.value.filter((val) => !selectedCategories.value.includes(val));
deleteProductsDialog.value = false;
selectedCategories.value = null;
toast.add({ severity: 'success', summary: 'موفقیت آمیز', detail: 'دسته بندی مورد نظر حذف شد.', life: 3000 });
}
</script>
<template>
<div>
<Toast></Toast>
<div class="card">
<Toolbar class="mb-6">
<template #start>
<Button label="ساخت دسته بندی جدید" icon="pi pi-plus" severity="secondary" class="mr-2 mx-3" @click="openNew" />
<Button label="حذف دسته بندی" icon="pi pi-trash" severity="secondary" @click="confirmDeleteSelected" :disabled="!selectedCategories || !selectedCategories.length" />
</template>
<template #end> </template>
</Toolbar>
<DataTable
ref="dt"
v-model:selection="selectedCategories"
:value="categories"
dataKey="id"
:paginator="true"
:rows="20"
:filters="filters"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink CurrentPageReport RowsPerPageDropdown"
:rowsPerPageOptions="[20, 40, 80]"
currentPageReportTemplate="نمایش {first} تا {last} از {totalRecords} دارایی"
>
<template #header>
<div class="flex flex-wrap gap-2 items-center justify-between">
<h4 class="m-0">مدیریت اموال</h4>
<IconField>
<InputIcon>
<i class="pi pi-search" />
</InputIcon>
<InputText v-model="filters['global'].value" placeholder="جستجو..." />
</IconField>
</div>
</template>
<Column selectionMode="multiple" style="width: 3rem" :exportable="false"></Column>
<Column field="nativeName" header="نام فارسی" sortable style="min-width: 16rem"></Column>
<Column field="englishName" header="نام انگلیسی" sortable style="min-width: 16rem"></Column>
<Column :exportable="false" style="min-width: 12rem">
<template #body="slotProps">
<Button icon="pi pi-pencil" outlined rounded class="mr-2 mx-3" @click="editCategory(slotProps.data)" />
<Button icon="pi pi-trash" class="mx-3" outlined rounded severity="danger" @click="confirmDeleteCategory(slotProps.data)" />
</template>
</Column>
</DataTable>
</div>
<Dialog v-model:visible="categoryDialog" :style="{ width: '450px' }" header="جزئیات دسته بندی" :modal="true">
<div class="flex flex-col gap-6">
<div>
<label for="nativeName" class="block font-bold mb-3">نام فارسی</label>
<InputText id="nativeName" v-model.trim="category.nativeName" required="true" autofocus :invalid="submitted && !category.nativeName" fluid />
<small v-if="submitted && !category.nativeName" class="text-red-500">ورود نام فارسی الزامی است.</small>
</div>
<div>
<label for="englishName" class="block font-bold mb-3">نام انگلیسی</label>
<InputText id="englishName" v-model.trim="category.englishName" required="true" autofocus :invalid="submitted && !category.englishName" fluid />
<small v-if="submitted && !category.englishName" class="text-red-500">ورود نام انگلیسی الزامی است.</small>
</div>
</div>
<template #footer>
<Button label="لغو" icon="pi pi-times" text @click="hideDialog" />
<Button label="ذخیره" icon="pi pi-check" @click="saveCategory" />
</template>
</Dialog>
<Dialog v-model:visible="deleteCategoryDialog" :style="{ width: '450px' }" header="تایید نهایی" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="category"
>آیا از حذف دسته بندی <b>{{ category.nativeName }} </b> مطمئن هستید؟</span
>
</div>
<template #footer>
<Button label="خیر" icon="pi pi-times" text @click="deleteCategoryDialog = false" />
<Button label="بله" icon="pi pi-check" @click="deleteCategory" />
</template>
</Dialog>
<Dialog v-model:visible="deleteProductsDialog" :style="{ width: '450px' }" header="Confirm" :modal="true">
<div class="flex items-center gap-4">
<i class="pi pi-exclamation-triangle !text-3xl" />
<span v-if="product">Are you sure you want to delete the selected products?</span>
</div>
<template #footer>
<Button label="No" icon="pi pi-times" text @click="deleteProductsDialog = false" />
<Button label="Yes" icon="pi pi-check" text @click="deleteSelectedCategories" />
</template>
</Dialog>
</div>
</template>
+124
View File
@@ -0,0 +1,124 @@
<script setup>
import { onBeforeMount, onMounted, ref } from 'vue';
import { CategoryService } from '@/service/CategoryService';
import { Tabs, TabList, Tab } from 'primevue';
import { useToast } from 'primevue/usetoast';
import axios from 'axios';
import { FeatureService } from '@/service/FeatureService';
const toast = useToast();
const selectedTab = ref('');
const selectedFeature = ref();
const submitted = ref(false);
const feature = ref({});
const categories = ref([]);
const featureDialog = ref(false);
onMounted(async () => {
categories.value = await CategoryService.getAllCategories();
});
const featureType = [
{
value: 'string',
name: 'متن'
},
{
value: 'boolean',
name: 'بله یا خیر'
},
{
value: 'number',
name: 'عدد'
},
{
value: 'date',
name: 'تاریخ'
}
];
function hideDialog() {
featureDialog.value = false;
submitted.value = false;
}
function openNew(categoryId) {
feature.value = {};
feature.value.category = categoryId;
submitted.value = false;
featureDialog.value = true;
}
const editFeature = (event, categoryId) => {
feature.value = event.value;
submitted.value = false;
featureDialog.value = true;
};
const saveFeature = async () => {
submitted.value = true;
let status = 'failed';
if (feature?.value._id?.trim()) {
status = await FeatureService.updateFeature(feature.value._id, feature.value);
} else {
status = await FeatureService.createFeature(feature.value);
}
if (status === 'success') {
toast.add({ severity: 'success', summary: 'موفق', detail: 'ویژگی جدید ساخته شد.', life: 3000 });
} else {
toast.add({ severity: 'error', summary: 'ناموفق', detail: status, life: 3000 });
}
featureDialog.value = false;
feature.value = {};
CategoryService.getAllCategories().then((data) => (categories.value = data));
};
</script>
<template>
<div className="card">
<div class="font-semibold text-xl mb-4">ویژگی دسته بندی ها</div>
<Tabs :value="selectedTab" :pt="{}">
<TabList>
<Tab v-for="category in categories" :key="category._id" :value="category._id">
{{ category.nativeName }}
</Tab>
</TabList>
<TabPanels>
<TabPanel v-for="category in categories" :key="category._id + '_content'" :value="category._id">
<p>لیست ویژگی های {{ category.nativeName }}</p>
<div class="w-1/3 my-3">
<Listbox filter checkmark :options="category.features" option-value="_id" option-label="nativeName" @item-dblclick="editFeature($event, category._id)" emptyMessage="موردی برای نمایش وجود ندارد."></Listbox>
<Button class="my-3" :categoryId="category._id" @click="openNew(category._id)">افزودن</Button>
</div>
</TabPanel>
</TabPanels>
</Tabs>
<Dialog v-model:visible="featureDialog" :style="{ width: '450px' }" header="ساخت ویژگی" :modal="true">
<div class="flex flex-col gap-6">
<div>
<label for="nativeName" class="block font-bold mb-3">نام فارسی</label>
<InputText id="nativeName" v-model.trim="feature.nativeName" required="true" autofocus :invalid="submitted && !feature.nativeName" fluid />
<small v-if="submitted && !feature.nativeName" class="text-red-500">ورود نام فارسی الزامی است.</small>
</div>
<div>
<label for="englishName" class="block font-bold mb-3">نام انگلیسی</label>
<InputText id="englishName" v-model.trim="feature.englishName" required="true" autofocus :invalid="submitted && !feature.englishName" fluid />
<small v-if="submitted && !feature.englishName" class="text-red-500">ورود نام انگلیسی الزامی است.</small>
</div>
<div>
<label for="unit" class="block font-bold mb-3">واحد</label>
<InputText id="unit" v-model.trim="feature.unit" required="true" autofocus :invalid="submitted && !feature.unit" fluid />
</div>
<div class="mb-6">
<label for="featureType" class="block font-bold mb-3">نوع ویژگی</label>
<Select id="featureType" option-label="name" option-value="value" :options="featureType" v-model.trim="feature.featureType" autofocus :invalid="submitted && !feature.featureType" fluid />
<small v-if="submitted && !feature.featureType" class="text-red-500">ورود نوع ویژگی الزامی است.</small>
</div>
</div>
<template #footer>
<Button label="لغو" icon="pi pi-times" text @click="hideDialog" />
<Button label="ذخیره" icon="pi pi-check" @click="saveFeature" />
</template>
</Dialog>
</div>
</template>
+45 -14
View File
@@ -1,10 +1,45 @@
<script setup>
import FloatingConfigurator from '@/components/FloatingConfigurator.vue';
import { ref } from 'vue';
import { onBeforeMount, ref } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';
const router = useRouter()
const email = ref('');
const password = ref('');
const checked = ref(false);
const loginLoading = ref(false)
import Cookies from 'js-cookie';
const login = async () => {
try {
loginLoading.value = true
const response = await axios.post('/api/users/user/login-email', {
email: email.value,
password: password.value,
})
console.log(response)
Cookies.set('bm-token', response.data.data.token)
localStorage.setItem('bm-token', response.data.data.token)
await router.push('/')
} catch (error) {
console.log('err', error)
loginLoading.value = false
}
}
onBeforeMount(async () => {
const token = Cookies.get('bm-token')
try {
const response = await axios.get('/api/users/user/get-self', {
headers: {
Authorization: `Bearer ${token}`
}
})
console.log(response)
await router.push('/')
} catch (error) {
console.log(error)
}
})
</script>
<template>
@@ -31,25 +66,21 @@ const checked = ref(false);
/>
</g>
</svg>
<div class="text-surface-900 dark:text-surface-0 text-3xl font-medium mb-4">Welcome to PrimeLand!</div>
<span class="text-muted-color font-medium">Sign in to continue</span>
<div class="text-surface-900 dark:text-surface-0 text-3xl font-medium mb-4">نرم افزار مدیریت اموال نیلگون</div>
<span class="text-muted-color font-medium">برای ادامه وارد شوید</span>
</div>
<div>
<label for="email1" class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">Email</label>
<InputText id="email1" type="text" placeholder="Email address" class="w-full md:w-[30rem] mb-8" v-model="email" />
<label for="email1" class="block text-surface-900 dark:text-surface-0 text-xl font-medium mb-2">ایمیل</label>
<InputText id="email1" type="text" placeholder="آدرس ایمیل" class="w-full md:w-[30rem] mb-8" v-model="email" />
<label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">Password</label>
<Password id="password1" v-model="password" placeholder="Password" :toggleMask="true" class="mb-4" fluid :feedback="false"></Password>
<label for="password1" class="block text-surface-900 dark:text-surface-0 font-medium text-xl mb-2">رمز عبور</label>
<Password id="password1" v-model="password" placeholder="رمز عبور" :toggleMask="true" class="mb-4" fluid :feedback="false"></Password>
<div class="flex items-center justify-between mt-2 mb-8 gap-8">
<div class="flex items-center">
<Checkbox v-model="checked" id="rememberme1" binary class="mr-2"></Checkbox>
<label for="rememberme1">Remember me</label>
</div>
<span class="font-medium no-underline ml-2 text-right cursor-pointer text-primary">Forgot password?</span>
<span class="font-medium no-underline ml-2 text-right cursor-pointer text-primary">فراموشی رمز عبور</span>
</div>
<Button label="Sign In" class="w-full" as="router-link" to="/"></Button>
<Button :loading="loginLoading" label="ورود" class="w-full" @click="login"></Button>
</div>
</div>
</div>