Fix SMS campaigns people list 400 by capping limit at 100 and searching via API.

This commit is contained in:
2026-09-26 16:39:11 +03:30
parent ef600d149d
commit 499d13d1df
+33 -19
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue';
import { onMounted, ref, watch } from 'vue';
import axios from 'axios';
import { api } from '@/lib/api';
import type {
@@ -21,6 +21,7 @@ import UiIcon from '@/components/UiIcon.vue';
import { formatJalaliNumeric, toFaDigits } from '@/lib/jalali';
const loading = ref(false);
const peopleLoading = ref(false);
const saving = ref(false);
const error = ref('');
const ok = ref('');
@@ -32,17 +33,6 @@ const body = ref('');
const queueNow = ref(true);
const peopleQ = ref('');
const filteredPeople = computed(() => {
const q = peopleQ.value.trim();
if (!q) return people.value.slice(0, 80);
return people.value
.filter((p) => {
const hay = `${p.fullName} ${p.mobile ?? ''} ${p.email ?? ''}`.toLowerCase();
return hay.includes(q.toLowerCase());
})
.slice(0, 80);
});
const statusLabel: Record<string, string> = {
draft: 'پیش‌نویس',
queued: 'در صف',
@@ -51,18 +41,31 @@ const statusLabel: Record<string, string> = {
cancelled: 'لغو شده',
};
async function loadPeople(q?: string) {
peopleLoading.value = true;
try {
const { data } = await api.get<PaginatedResponse<PersonDto>>('/people', {
params: {
page: 1,
limit: 100,
...(q?.trim() ? { q: q.trim() } : {}),
},
});
people.value = data.items;
} finally {
peopleLoading.value = false;
}
}
async function load() {
loading.value = true;
error.value = '';
try {
const [campRes, peopleRes] = await Promise.all([
const [campRes] = await Promise.all([
api.get<SmsCampaignListResponse>('/sms-campaigns'),
api.get<PaginatedResponse<PersonDto>>('/people', {
params: { page: 1, limit: 200 },
}),
loadPeople(peopleQ.value),
]);
campaigns.value = campRes.data.items;
people.value = peopleRes.data.items;
} catch (e) {
if (axios.isAxiosError(e) && e.response?.status === 403) {
error.value = 'ماژول پیامک گروهی برای این محیط فعال نیست.';
@@ -74,6 +77,16 @@ async function load() {
}
}
let peopleSearchTimer: ReturnType<typeof setTimeout> | null = null;
watch(peopleQ, (q) => {
if (peopleSearchTimer) clearTimeout(peopleSearchTimer);
peopleSearchTimer = setTimeout(() => {
void loadPeople(q).catch(() => {
error.value = 'جستجوی مخاطب ناموفق بود.';
});
}, 300);
});
function togglePerson(id: string) {
if (selectedIds.value.includes(id)) {
selectedIds.value = selectedIds.value.filter((x) => x !== id);
@@ -83,7 +96,7 @@ function togglePerson(id: string) {
}
function selectVisible() {
const ids = filteredPeople.value.map((p) => p.id);
const ids = people.value.map((p) => p.id);
selectedIds.value = [...new Set([...selectedIds.value, ...ids])];
}
@@ -194,8 +207,9 @@ onMounted(() => {
<p class="muted">
انتخاب‌شده: {{ toFaDigits(selectedIds.length) }} نفر
</p>
<p v-if="peopleLoading" class="muted">در حال بارگذاری مخاطبان…</p>
<div class="people-list">
<label v-for="p in filteredPeople" :key="p.id" class="person">
<label v-for="p in people" :key="p.id" class="person">
<input
type="checkbox"
:checked="selectedIds.includes(p.id)"