Add Didar gap features: lost-reason reports, audit logs, tags, card assignees, and saved-views API.
Ignore Graphify local artifacts and expose new modules in superadmin.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { RouterLink } from 'vue-router';
|
||||
import axios from 'axios';
|
||||
import UiIcon from '@/components/UiIcon.vue';
|
||||
import VoiceToTextButton from '@/components/VoiceToTextButton.vue';
|
||||
import JalaliDateInput from '@/components/JalaliDateInput.vue';
|
||||
import { formatLocalDateTimeInput } from '@/lib/jalali';
|
||||
import { UiButton, UiModal } from '@/components/ui';
|
||||
import { api } from '@/lib/api';
|
||||
import { brand, type ActivityType, type AskMentoSourceDto, type PersonDto, type VoiceCommandDraft } from '@mento/shared';
|
||||
@@ -93,15 +95,14 @@ function onActivityPersonEdit() {
|
||||
if (draft.value?.activity) draft.value.activity.personId = null;
|
||||
}
|
||||
|
||||
function toLocalInput(iso: string | null) {
|
||||
const dueLocal = computed(() => {
|
||||
const iso = draft.value?.activity?.dueAt;
|
||||
if (!iso) return '';
|
||||
const date = new Date(iso);
|
||||
if (Number.isNaN(date.getTime())) return '';
|
||||
const pad = (n: number) => String(n).padStart(2, '0');
|
||||
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
||||
}
|
||||
return Number.isNaN(date.getTime()) ? '' : formatLocalDateTimeInput(date);
|
||||
});
|
||||
|
||||
function onDueEdit(value: string) {
|
||||
function onDueLocal(value: string) {
|
||||
if (!draft.value?.activity) return;
|
||||
if (!value) {
|
||||
draft.value.activity.dueAt = null;
|
||||
@@ -111,6 +112,25 @@ function onDueEdit(value: string) {
|
||||
draft.value.activity.dueAt = Number.isNaN(date.getTime()) ? null : date.toISOString();
|
||||
}
|
||||
|
||||
async function rematchActivityPerson() {
|
||||
const activity = draft.value?.activity;
|
||||
const name = activity?.personName.trim();
|
||||
if (!activity || !name) {
|
||||
if (activity) activity.personId = null;
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { data } = await api.get<{ items: Array<{ id: string; fullName: string }> }>('/people', {
|
||||
params: { q: name, limit: 5 },
|
||||
});
|
||||
const exact = data.items.filter((row) => row.fullName.trim() === name);
|
||||
const hit = exact.length === 1 ? exact[0] : data.items.length === 1 ? data.items[0] : null;
|
||||
activity.personId = hit?.id ?? null;
|
||||
} catch {
|
||||
activity.personId = null;
|
||||
}
|
||||
}
|
||||
|
||||
async function parseCommand(text: string, fallbackToAsk: boolean) {
|
||||
voiceBusy.value = true;
|
||||
voiceError.value = '';
|
||||
@@ -312,15 +332,14 @@ watch(
|
||||
</label>
|
||||
<label>
|
||||
زمان
|
||||
<input
|
||||
type="datetime-local"
|
||||
:value="toLocalInput(draft.activity.dueAt)"
|
||||
@input="onDueEdit(($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
<JalaliDateInput :model-value="dueLocal" mode="datetime" @update:model-value="onDueLocal" />
|
||||
</label>
|
||||
<label>
|
||||
مشتری
|
||||
<input v-model="draft.activity.personName" @input="onActivityPersonEdit" @change="rematchActivityPerson" />
|
||||
</label>
|
||||
<label>مشتری <input v-model="draft.activity.personName" @input="onActivityPersonEdit" /></label>
|
||||
<p v-if="draft.activity.personName && !draft.activity.personId" class="warn">
|
||||
این نام به پروندهای وصل نیست؛ فعالیت بدون لینک ثبت میشود.
|
||||
در فهرست مشتریان پروندهای با این نام پیدا نشد. فعالیت ذخیره میشود، ولی داخل پروندهٔ آن شخص دیده نمیشود.
|
||||
</p>
|
||||
<label>توضیح <textarea v-model="draft.activity.description" rows="2" /></label>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user