feat: add settings data upload and single-name user forms
Allow SuperAdmins to upload import JSON from settings, and update user UI for merged name, gender, and extended profile fields.
This commit is contained in:
+9
-1
@@ -3,5 +3,13 @@ import axiosInstance from './axiosInstance';
|
||||
|
||||
export const seedApi = {
|
||||
getStatus: () => axiosInstance.get('/seed/status'),
|
||||
runSeed: () => axiosInstance.post('/seed', {}, { timeout: 60000 })
|
||||
runSeed: () => axiosInstance.post('/seed', {}, { timeout: 60000 }),
|
||||
importData: (file) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return axiosInstance.post('/data-import', formData, {
|
||||
timeout: 180000,
|
||||
headers: { 'Content-Type': 'multipart/form-data' }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
+9
-1
@@ -50,13 +50,21 @@
|
||||
"addUser": "افزودن کاربر جدید",
|
||||
"editUser": "ویرایش کاربر",
|
||||
"name": "نام",
|
||||
"surname": "نام خانوادگی",
|
||||
"gender": "جنسیت",
|
||||
"nationalId": "کد ملی",
|
||||
"phone": "شماره همراه",
|
||||
"email": "ایمیل",
|
||||
"preferredMessenger": "پیامرسان ترجیحی",
|
||||
"role": "نقش کاربر",
|
||||
"status": "وضعیت حساب",
|
||||
"fatherName": "نام پدر",
|
||||
"birthCertificateNumber": "شماره شناسنامه",
|
||||
"birthDate": "تاریخ تولد",
|
||||
"placeOfIssue": "محل صدور",
|
||||
"postalCode": "کد پستی",
|
||||
"education": "میزان تحصیلات و رشته",
|
||||
"parentPhoneNumber": "تلفن همراه والد",
|
||||
"address": "آدرس",
|
||||
"tabInfo": "اطلاعات شخصی",
|
||||
"tabCourses": "دورههای ثبتنامی",
|
||||
"tabSessions": "حضور و غیاب",
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
<Column field="name" header="نام و نام خانوادگی دانشجو">
|
||||
<template #body="{ data }">
|
||||
<span class="font-bold text-color">{{ data.name }} {{ data.surname }}</span>
|
||||
<span class="font-bold text-color">{{ data.name }}</span>
|
||||
<span class="text-xs text-muted block">کد ملی: {{ toPersianDigits(data.nationalIdCode || '—') }}</span>
|
||||
</template>
|
||||
</Column>
|
||||
@@ -121,7 +121,8 @@ const fetchSessionAttendance = async () => {
|
||||
return {
|
||||
userId: user._id,
|
||||
name: user.name || '',
|
||||
surname: user.surname || '',
|
||||
name: user.name || '',
|
||||
gender: user.gender || null,
|
||||
nationalIdCode: user.nationalIdCode || '',
|
||||
status: existing?.status || 'present',
|
||||
note: existing?.note || ''
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
<template #body="{ index }">{{ toPersianDigits(index + 1) }}</template>
|
||||
</Column>
|
||||
<Column header="نام">
|
||||
<template #body="{ data }">{{ data.name }} {{ data.surname }}</template>
|
||||
<template #body="{ data }">{{ data.name }}</template>
|
||||
</Column>
|
||||
<Column header="موبایل">
|
||||
<template #body="{ data }"><span dir="ltr">{{ data.phoneNumber || '—' }}</span></template>
|
||||
|
||||
@@ -97,7 +97,7 @@
|
||||
<template #body="{ index }">{{ toPersianDigits(index + 1) }}</template>
|
||||
</Column>
|
||||
<Column header="نام">
|
||||
<template #body="{ data }">{{ data.name }} {{ data.surname }}</template>
|
||||
<template #body="{ data }">{{ data.name }}</template>
|
||||
</Column>
|
||||
<Column header="موبایل">
|
||||
<template #body="{ data }">
|
||||
@@ -176,7 +176,7 @@ const availableUsers = computed(() => {
|
||||
.filter((u) => !enrolled.has(String(u._id)))
|
||||
.map((u) => ({
|
||||
...u,
|
||||
label: `${u.name || ''} ${u.surname || ''} — ${u.phoneNumber || ''}`.trim()
|
||||
label: `${u.name || ''} — ${u.phoneNumber || ''}`.trim()
|
||||
}));
|
||||
});
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<div class="flex flex-column gap-3">
|
||||
<div>
|
||||
<span class="text-muted text-xs block mb-1">نام کاربر / دانشجو</span>
|
||||
<span class="font-bold text-color text-base">{{ payment.user?.name || payment.userName || 'کاربر' }} {{ payment.user?.surname || '' }}</span>
|
||||
<span class="font-bold text-color text-base">{{ payment.user?.name || payment.userName || 'کاربر' }}</span>
|
||||
</div>
|
||||
<div v-if="payment.classes && payment.classes.length">
|
||||
<span class="text-muted text-xs block mb-1">کلاسهای مربوطه</span>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<Column field="user" header="نام دانشجو / کاربر">
|
||||
<template #body="{ data }">
|
||||
<router-link :to="`/payments/view/${data._id || data.id}`" class="font-bold text-color hover:text-primary">
|
||||
{{ data.user?.name || data.userName || 'کاربر' }} {{ data.user?.surname || '' }}
|
||||
{{ data.user?.name || data.userName || 'کاربر' }}
|
||||
</router-link>
|
||||
</template>
|
||||
</Column>
|
||||
@@ -196,7 +196,7 @@ const fetchDropdownData = async () => {
|
||||
]);
|
||||
const uData = uRes.data || uRes;
|
||||
const rawUsers = uData.items || uData.users || uData || [];
|
||||
usersList.value = rawUsers.map(u => ({ ...u, fullName: `${u.name} ${u.surname}` }));
|
||||
usersList.value = rawUsers.map(u => ({ ...u, fullName: u.name || '' }));
|
||||
|
||||
const cData = cRes.data || cRes;
|
||||
classesList.value = cData.items || cData.classes || cData || [];
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
<div class="settings-view w-full max-w-3xl mx-auto">
|
||||
<PageHeader
|
||||
title="تنظیمات سیستم"
|
||||
subtitle="عملیات راهاندازی و پیکربندی اولیه پایگاه داده"
|
||||
subtitle="عملیات راهاندازی و وارد کردن دادههای دورهها و کاربران"
|
||||
/>
|
||||
|
||||
<div class="surface-card p-4 sm:p-5 border-round-xl border-1 border-color shadow-sm">
|
||||
<div class="surface-card p-4 sm:p-5 border-round-xl border-1 border-color shadow-sm mb-4">
|
||||
<div class="flex align-items-start gap-3 mb-4">
|
||||
<div class="w-3rem h-3rem border-round-xl flex align-items-center justify-content-center flex-shrink-0 bg-primary-light text-primary">
|
||||
<i class="pi pi-database text-xl"></i>
|
||||
@@ -45,6 +45,67 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="surface-card p-4 sm:p-5 border-round-xl border-1 border-color shadow-sm">
|
||||
<div class="flex align-items-start gap-3 mb-4">
|
||||
<div class="w-3rem h-3rem border-round-xl flex align-items-center justify-content-center flex-shrink-0 bg-primary-light text-primary">
|
||||
<i class="pi pi-upload text-xl"></i>
|
||||
</div>
|
||||
<div class="flex-grow-1">
|
||||
<h2 class="text-lg font-bold text-color m-0 mb-1">وارد کردن دادهها</h2>
|
||||
<p class="text-sm text-muted m-0 line-height-3">
|
||||
فایل JSON ساختاریافته دورهها، کلاسها و کاربران را بارگذاری کنید. دادههای قبلی حذف نمیشوند؛ موارد تکراری بهروزرسانی یا به کلاس اضافه میشوند.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-column gap-3">
|
||||
<input
|
||||
ref="fileInputRef"
|
||||
type="file"
|
||||
accept="application/json,.json"
|
||||
class="hidden"
|
||||
@change="onFileSelected"
|
||||
/>
|
||||
|
||||
<div class="flex flex-column sm:flex-row align-items-stretch sm:align-items-center gap-3">
|
||||
<Button
|
||||
:label="selectedFileName || 'انتخاب فایل JSON'"
|
||||
:icon="selectedFileName ? 'pi pi-file' : 'pi pi-folder-open'"
|
||||
severity="secondary"
|
||||
outlined
|
||||
class="font-semibold"
|
||||
@click="fileInputRef?.click()"
|
||||
/>
|
||||
<Button
|
||||
label="بارگذاری و افزودن به پایگاه داده"
|
||||
icon="pi pi-cloud-upload"
|
||||
class="font-bold"
|
||||
:loading="isImporting"
|
||||
:disabled="!selectedFile"
|
||||
@click="confirmImport"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p class="text-xs text-muted m-0 line-height-3">
|
||||
نمونه فایل آماده: <code dir="ltr">raw-data/import-data.json</code>
|
||||
</p>
|
||||
|
||||
<div
|
||||
v-if="importResult"
|
||||
class="p-3 border-round surface-ground border-1 border-color text-sm"
|
||||
>
|
||||
<div class="font-semibold mb-2">نتیجه آخرین بارگذاری</div>
|
||||
<ul class="m-0 pr-3 line-height-3">
|
||||
<li>دوره جدید: {{ importResult.stats?.coursesCreated ?? 0 }} — بازاستفاده: {{ importResult.stats?.coursesReused ?? 0 }}</li>
|
||||
<li>کلاس جدید: {{ importResult.stats?.classesCreated ?? 0 }} — بازاستفاده: {{ importResult.stats?.classesReused ?? 0 }}</li>
|
||||
<li>کاربر جدید: {{ importResult.stats?.studentsCreated ?? 0 }} — بهروزرسانی: {{ importResult.stats?.studentsUpdated ?? 0 }}</li>
|
||||
<li>ثبتنام در کلاس: {{ importResult.stats?.enrollmentsAdded ?? 0 }} — رد شده: {{ importResult.stats?.studentsSkipped ?? 0 }}</li>
|
||||
<li v-if="importResult.warnings?.length">هشدارها: {{ importResult.warnings.length }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -64,6 +125,12 @@ const isSeeding = ref(false);
|
||||
const isLocked = ref(false);
|
||||
const lockedAt = ref(null);
|
||||
|
||||
const fileInputRef = ref(null);
|
||||
const selectedFile = ref(null);
|
||||
const selectedFileName = ref('');
|
||||
const isImporting = ref(false);
|
||||
const importResult = ref(null);
|
||||
|
||||
const lockedAtLabel = computed(() => {
|
||||
if (!lockedAt.value) return '';
|
||||
const date = new Date(lockedAt.value);
|
||||
@@ -118,5 +185,48 @@ const confirmSeed = () => {
|
||||
});
|
||||
};
|
||||
|
||||
const onFileSelected = (event) => {
|
||||
const file = event.target.files?.[0] || null;
|
||||
selectedFile.value = file;
|
||||
selectedFileName.value = file?.name || '';
|
||||
importResult.value = null;
|
||||
};
|
||||
|
||||
const runImport = async () => {
|
||||
if (!selectedFile.value) return;
|
||||
isImporting.value = true;
|
||||
try {
|
||||
const response = await seedApi.importData(selectedFile.value);
|
||||
const data = response?.data || response;
|
||||
importResult.value = data;
|
||||
showSuccess('دادهها با موفقیت به پایگاه داده اضافه شدند.');
|
||||
} catch (err) {
|
||||
showError(err);
|
||||
} finally {
|
||||
isImporting.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const confirmImport = () => {
|
||||
if (!selectedFile.value) return;
|
||||
confirm.require({
|
||||
header: 'وارد کردن دادهها',
|
||||
message: 'دورهها، کلاسها و کاربران از فایل JSON به پایگاه داده افزوده میشوند. دادههای موجود حذف نمیشوند. ادامه میدهید؟',
|
||||
icon: 'pi pi-upload',
|
||||
acceptLabel: 'بارگذاری',
|
||||
rejectLabel: 'انصراف',
|
||||
acceptClass: 'p-button-primary',
|
||||
accept: () => {
|
||||
runImport();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(loadStatus);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!-- /src/views/users/UserDetailView.vue -->
|
||||
<template>
|
||||
<div class="user-detail-view" v-if="user">
|
||||
<PageHeader :title="`${user.name || ''} ${user.surname || ''}`" :subtitle="`کد ملی: ${toPersianDigits(user.nationalId)}`">
|
||||
<PageHeader :title="user.name || ''" :subtitle="`کد ملی: ${toPersianDigits(user.nationalId)}`">
|
||||
<PermissionGate permission="users:update">
|
||||
<Button :label="$t('app.edit')" icon="pi pi-pencil" severity="warning" @click="$router.push(`/users/edit/${user._id || user.id}`)" />
|
||||
</PermissionGate>
|
||||
@@ -22,8 +22,12 @@
|
||||
<TabPanel value="0">
|
||||
<div class="grid p-3">
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">نام و نام خانوادگی</span>
|
||||
<span class="font-bold text-color text-base">{{ user.name }} {{ user.surname }}</span>
|
||||
<span class="text-muted text-xs block mb-1">نام</span>
|
||||
<span class="font-bold text-color text-base">{{ user.name }}</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">جنسیت</span>
|
||||
<span class="font-bold text-color text-base">{{ user.gender === 'male' ? 'مرد' : user.gender === 'female' ? 'زن' : '-' }}</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">کد ملی</span>
|
||||
@@ -52,6 +56,30 @@
|
||||
<span class="text-muted text-xs block mb-1">نقش سیستم</span>
|
||||
<Tag :value="user.role?.name || 'دانشجو'" severity="secondary" />
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">نام پدر</span>
|
||||
<span class="font-bold text-color text-base">{{ user.fatherName || '-' }}</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">تاریخ تولد</span>
|
||||
<span class="font-bold text-color text-base" dir="ltr">{{ user.birthDate ? new Date(user.birthDate).toLocaleDateString('fa-IR') : '-' }}</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">شماره شناسنامه</span>
|
||||
<span class="font-bold text-color text-base" dir="ltr">{{ user.birthCertificateNumber || '-' }}</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">محل صدور</span>
|
||||
<span class="font-bold text-color text-base">{{ user.placeOfIssue || '-' }}</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">کد پستی</span>
|
||||
<span class="font-bold text-color text-base" dir="ltr">{{ user.postalCode || '-' }}</span>
|
||||
</div>
|
||||
<div class="col-12 sm:col-6 md:col-4 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">تحصیلات</span>
|
||||
<span class="font-bold text-color text-base">{{ user.education || '-' }}</span>
|
||||
</div>
|
||||
<div class="col-12 mb-3">
|
||||
<span class="text-muted text-xs block mb-1">آدرس سکونت</span>
|
||||
<span class="text-color text-sm">{{ user.address || '-' }}</span>
|
||||
|
||||
@@ -12,12 +12,20 @@
|
||||
<form @submit.prevent="handleSubmit" class="grid">
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.name') }} *</label>
|
||||
<InputText v-model.trim="form.name" class="w-full text-sm" />
|
||||
<InputText v-model.trim="form.name" class="w-full text-sm" placeholder="نام کامل" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.surname') }} *</label>
|
||||
<InputText v-model.trim="form.surname" class="w-full text-sm" />
|
||||
<label class="font-semibold text-sm">{{ $t('users.gender') }}</label>
|
||||
<Dropdown
|
||||
v-model="form.gender"
|
||||
:options="genderOptions"
|
||||
optionLabel="label"
|
||||
optionValue="value"
|
||||
placeholder="انتخاب جنسیت"
|
||||
class="w-full text-sm"
|
||||
showClear
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
@@ -40,6 +48,41 @@
|
||||
<Dropdown v-model="form.role" :options="roles" optionLabel="name" optionValue="_id" placeholder="انتخاب نقش" class="w-full text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.fatherName') }}</label>
|
||||
<InputText v-model.trim="form.fatherName" class="w-full text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.birthCertificateNumber') }}</label>
|
||||
<InputText v-model.trim="form.birthCertificateNumber" class="w-full text-sm" dir="ltr" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.birthDate') }}</label>
|
||||
<InputText v-model.trim="form.birthDate" class="w-full text-sm" dir="ltr" placeholder="YYYY-MM-DD" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.placeOfIssue') }}</label>
|
||||
<InputText v-model.trim="form.placeOfIssue" class="w-full text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.postalCode') }}</label>
|
||||
<InputText v-model.trim="form.postalCode" class="w-full text-sm" dir="ltr" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.education') }}</label>
|
||||
<InputText v-model.trim="form.education" class="w-full text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.parentPhoneNumber') }}</label>
|
||||
<InputText v-model.trim="form.parentPhoneNumber" class="w-full text-sm" dir="ltr" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.preferredMessenger') }}</label>
|
||||
<MultiSelect
|
||||
@@ -56,6 +99,11 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 flex flex-column gap-2">
|
||||
<label class="font-semibold text-sm">{{ $t('users.address') }}</label>
|
||||
<InputText v-model.trim="form.address" class="w-full text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-12 md:col-6 flex flex-column gap-2" v-if="isEditMode">
|
||||
<label class="font-semibold text-sm">{{ $t('auth.username') }}</label>
|
||||
<InputText
|
||||
@@ -143,19 +191,39 @@ const messengerOptions = [
|
||||
{ label: 'ایمیل', value: 'Email' }
|
||||
];
|
||||
|
||||
const genderOptions = [
|
||||
{ label: 'مرد', value: 'male' },
|
||||
{ label: 'زن', value: 'female' }
|
||||
];
|
||||
|
||||
const toMessengerList = (value) => {
|
||||
if (!value) return [];
|
||||
return Array.isArray(value) ? [...value] : [value];
|
||||
};
|
||||
|
||||
const toDateInput = (value) => {
|
||||
if (!value) return '';
|
||||
const date = new Date(value);
|
||||
if (Number.isNaN(date.getTime())) return '';
|
||||
return date.toISOString().slice(0, 10);
|
||||
};
|
||||
|
||||
const form = reactive({
|
||||
name: '',
|
||||
surname: '',
|
||||
gender: null,
|
||||
nationalId: '',
|
||||
phone: '',
|
||||
email: '',
|
||||
role: null,
|
||||
preferredMessenger: [],
|
||||
fatherName: '',
|
||||
birthCertificateNumber: '',
|
||||
birthDate: '',
|
||||
placeOfIssue: '',
|
||||
postalCode: '',
|
||||
education: '',
|
||||
parentPhoneNumber: '',
|
||||
address: '',
|
||||
username: '',
|
||||
password: ''
|
||||
});
|
||||
@@ -163,11 +231,19 @@ const form = reactive({
|
||||
const buildPayload = () => {
|
||||
const payload = {
|
||||
name: form.name,
|
||||
surname: form.surname,
|
||||
gender: form.gender || undefined,
|
||||
nationalIdCode: form.nationalId,
|
||||
phoneNumber: form.phone,
|
||||
email: form.email || undefined,
|
||||
role: form.role || undefined
|
||||
role: form.role || undefined,
|
||||
fatherName: form.fatherName || undefined,
|
||||
birthCertificateNumber: form.birthCertificateNumber || undefined,
|
||||
birthDate: form.birthDate || undefined,
|
||||
placeOfIssue: form.placeOfIssue || undefined,
|
||||
postalCode: form.postalCode || undefined,
|
||||
education: form.education || undefined,
|
||||
parentPhoneNumber: form.parentPhoneNumber || undefined,
|
||||
address: form.address || undefined
|
||||
};
|
||||
|
||||
if (form.preferredMessenger?.length) {
|
||||
@@ -200,14 +276,24 @@ const fetchUser = async () => {
|
||||
try {
|
||||
const res = await userApi.getOne(userId);
|
||||
const data = res.data || res;
|
||||
const legacySurname = data.surname ? String(data.surname).trim() : '';
|
||||
const fullName = [data.name, legacySurname].filter(Boolean).join(' ').trim();
|
||||
Object.assign(form, {
|
||||
name: data.name || '',
|
||||
surname: data.surname || '',
|
||||
name: fullName,
|
||||
gender: data.gender || null,
|
||||
nationalId: data.nationalIdCode || data.nationalId || '',
|
||||
phone: data.phoneNumber || data.phone || '',
|
||||
email: data.email || '',
|
||||
role: data.role?._id || data.role || null,
|
||||
preferredMessenger: toMessengerList(data.preferredMessenger),
|
||||
fatherName: data.fatherName || '',
|
||||
birthCertificateNumber: data.birthCertificateNumber || '',
|
||||
birthDate: toDateInput(data.birthDate),
|
||||
placeOfIssue: data.placeOfIssue || '',
|
||||
postalCode: data.postalCode || '',
|
||||
education: data.education || '',
|
||||
parentPhoneNumber: data.parentPhoneNumber || '',
|
||||
address: data.address || '',
|
||||
username: data.username || '',
|
||||
password: ''
|
||||
});
|
||||
@@ -222,8 +308,8 @@ const finishAfterCreate = () => {
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!form.name || !form.surname) {
|
||||
showError('نام و نام خانوادگی الزامی است');
|
||||
if (!form.name) {
|
||||
showError('نام الزامی است');
|
||||
return;
|
||||
}
|
||||
if (!form.nationalId || !form.phone) {
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
@sort-change="onSort"
|
||||
@search-change="onSearch"
|
||||
>
|
||||
<Column field="name" header="نام و نام خانوادگی" sortable>
|
||||
<Column field="name" header="نام" sortable>
|
||||
<template #body="{ data }">
|
||||
<router-link :to="`/users/${data._id || data.id}`" class="font-bold text-color hover:text-primary">
|
||||
{{ data.name }} {{ data.surname }}
|
||||
{{ data.name }}
|
||||
</router-link>
|
||||
</template>
|
||||
</Column>
|
||||
|
||||
Reference in New Issue
Block a user