add password in admin

This commit is contained in:
2026-05-25 20:51:51 +03:30
parent 43f0524ae2
commit 7bc963b9e1
4 changed files with 79 additions and 5 deletions
+5
View File
@@ -109,6 +109,11 @@ export type UpdateUserPayload = {
isActive?: boolean;
};
export type AdminSetUserPasswordPayload = {
password: string;
confirmPassword: string;
};
export type AssignOwnerPayload = {
mobile: string;
fullName?: string | null;
+8 -1
View File
@@ -1,5 +1,5 @@
import { api } from './client';
import type { UpdateUserPayload, User } from './types';
import type { AdminSetUserPasswordPayload, UpdateUserPayload, User } from './types';
export function fetchUsers() {
return api<User[]>('/users');
@@ -15,3 +15,10 @@ export function updateUser(id: string, payload: UpdateUserPayload) {
body: JSON.stringify(payload),
});
}
export function setUserPassword(id: string, payload: AdminSetUserPasswordPayload) {
return api<User>(`/users/${id}/password`, {
method: 'PATCH',
body: JSON.stringify(payload),
});
}
@@ -130,9 +130,18 @@ async function load() {
}
}
function parsePrice(value: string | number): number | null {
if (value === '' || value == null) return null;
if (typeof value === 'number') {
return Number.isFinite(value) && value >= 0 ? value : null;
}
const n = Number.parseFloat(String(value).replace(/,/g, '').trim());
return Number.isFinite(n) && n >= 0 ? n : null;
}
async function onAdd() {
const price = Number.parseFloat(form.value.price.replace(/,/g, ''));
if (!form.value.serviceTemplateId || !Number.isFinite(price) || price < 0) {
const price = parsePrice(form.value.price);
if (!form.value.serviceTemplateId || price === null) {
error.value = 'نوع خدمت و قیمت معتبر را وارد کنید.';
return;
}
+55 -2
View File
@@ -37,6 +37,9 @@
<AppBadge :tone="user.isActive ? 'success' : 'danger'">
{{ activeLabel(user.isActive) }}
</AppBadge>
<AppBadge :tone="user.hasPassword ? 'default' : 'warning'">
{{ user.hasPassword ? 'رمز دارد' : 'بدون رمز' }}
</AppBadge>
</div>
</div>
<AppButton variant="ghost" class="!w-auto shrink-0 px-3" @click="openEdit(user)">
@@ -64,6 +67,31 @@
حساب فعال است
</label>
<section class="space-y-3 rounded-xl border border-border bg-surface-3 p-3">
<h3 class="text-sm font-semibold text-text">
{{ editing.hasPassword ? 'تغییر رمز عبور' : 'تنظیم رمز عبور' }}
</h3>
<p class="text-xs text-muted">
اختیاری — در صورت پر کردن، رمز کاربر بدون نیاز به رمز قبلی تنظیم می‌شود.
</p>
<AppInput
id="userNewPassword"
v-model="form.password"
label="رمز جدید"
type="password"
autocomplete="new-password"
ltr
/>
<AppInput
id="userConfirmPassword"
v-model="form.confirmPassword"
label="تکرار رمز جدید"
type="password"
autocomplete="new-password"
ltr
/>
</section>
<AppButton type="submit" :loading="saving">ذخیره</AppButton>
</form>
</AppModal>
@@ -72,7 +100,7 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { fetchUsers, updateUser } from '@/api/users';
import { fetchUsers, setUserPassword, updateUser } from '@/api/users';
import type { User, UserRole } from '@/api/types';
import { ApiError } from '@/api/client';
import AppBadge from '@/components/ui/AppBadge.vue';
@@ -106,6 +134,8 @@ const form = ref({
fullName: '',
role: 'customer' as UserRole,
isActive: true,
password: '',
confirmPassword: '',
});
const roleOptions = [
@@ -132,6 +162,8 @@ function openEdit(user: User) {
fullName: user.fullName ?? '',
role: user.role,
isActive: user.isActive,
password: '',
confirmPassword: '',
};
}
@@ -141,14 +173,35 @@ function closeEdit() {
async function onSave() {
if (!editing.value) return;
const wantsPassword = Boolean(
form.value.password.trim() || form.value.confirmPassword.trim(),
);
if (wantsPassword) {
if (form.value.password.length < 8) {
error.value = 'رمز باید حداقل ۸ کاراکتر باشد.';
return;
}
if (form.value.password !== form.value.confirmPassword) {
error.value = 'تکرار رمز با رمز جدید یکسان نیست.';
return;
}
}
saving.value = true;
error.value = '';
try {
const updated = await updateUser(editing.value.id, {
let updated = await updateUser(editing.value.id, {
fullName: form.value.fullName.trim() || null,
role: form.value.role,
isActive: form.value.isActive,
});
if (wantsPassword) {
updated = await setUserPassword(editing.value.id, {
password: form.value.password,
confirmPassword: form.value.confirmPassword,
});
}
const idx = users.value.findIndex((u) => u.id === updated.id);
if (idx >= 0) users.value[idx] = updated;
closeEdit();