Track person import source and previous names; add source report and edit UX.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-04 12:01:21 +03:30
co-authored by Cursor
parent e144b2277a
commit 8b37525f2f
15 changed files with 632 additions and 25 deletions
@@ -197,6 +197,7 @@ async function importSelected() {
mobile: row.mobile,
phone: row.phone,
email: row.email,
importSource: 'phone',
};
await api.post<PersonDto>('/people', body);
}
@@ -33,6 +33,9 @@ const teams = ref<TeamDto[]>([]);
const newName = ref('');
const newDesc = ref('');
const editingId = ref<string | null>(null);
const editingMetaId = ref<string | null>(null);
const editName = ref('');
const editDesc = ref('');
const memberDraft = ref<Record<string, TeamMemberRole>>({});
const editableUsers = computed(() =>
@@ -83,6 +86,7 @@ async function createTeam() {
function startEdit(team: TeamDto) {
editingId.value = team.id;
editingMetaId.value = null;
const draft: Record<string, TeamMemberRole> = {};
for (const m of team.members) {
draft[m.userId] = m.role;
@@ -90,6 +94,38 @@ function startEdit(team: TeamDto) {
memberDraft.value = draft;
}
function startMetaEdit(team: TeamDto) {
editingMetaId.value = team.id;
editingId.value = null;
editName.value = team.name;
editDesc.value = team.description || '';
}
async function saveMeta() {
if (!editingMetaId.value) return;
if (!editName.value.trim()) {
error.value = 'نام تیم را وارد کنید.';
return;
}
saving.value = true;
error.value = '';
try {
await api.patch(`/teams/${editingMetaId.value}`, {
name: editName.value.trim(),
description: editDesc.value.trim(),
});
ok.value = 'اطلاعات تیم ذخیره شد.';
editingMetaId.value = null;
await load();
} catch (e) {
error.value = axios.isAxiosError(e)
? String((e.response?.data as { message?: string })?.message ?? 'ذخیره تیم ناموفق بود.')
: 'ذخیره تیم ناموفق بود.';
} finally {
saving.value = false;
}
}
function toggleMember(userId: string) {
if (memberDraft.value[userId]) {
const next = { ...memberDraft.value };
@@ -194,6 +230,7 @@ onMounted(() => {
</p>
</div>
<div v-if="canEdit" class="actions">
<UiButton variant="secondary" @click="startMetaEdit(t)">ویرایش</UiButton>
<UiButton variant="secondary" @click="startEdit(t)">اعضا</UiButton>
<UiButton variant="secondary" @click="removeTeam(t.id)">حذف</UiButton>
</div>
@@ -208,6 +245,21 @@ onMounted(() => {
}}
</p>
<div v-if="editingMetaId === t.id" class="editor">
<div class="row">
<UiField label="نام تیم">
<UiInput v-model="editName" />
</UiField>
<UiField label="توضیح">
<UiInput v-model="editDesc" />
</UiField>
</div>
<div class="actions">
<UiButton :loading="saving" @click="saveMeta">ذخیره</UiButton>
<UiButton variant="secondary" @click="editingMetaId = null">انصراف</UiButton>
</div>
</div>
<div v-if="editingId === t.id" class="editor">
<p class="hint">اعضا را انتخاب کنید؛ سرپرست همان عضو تیم است.</p>
<label v-for="u in users.filter((x) => x.isActive !== false)" :key="u.id" class="person">