diff --git a/apps/web/src/components/TeamsSettingsPanel.vue b/apps/web/src/components/TeamsSettingsPanel.vue index 398e80b..b91ee53 100644 --- a/apps/web/src/components/TeamsSettingsPanel.vue +++ b/apps/web/src/components/TeamsSettingsPanel.vue @@ -3,10 +3,13 @@ import { computed, onMounted, ref } from 'vue'; import axios from 'axios'; import { api } from '@/lib/api'; import { useAuthStore } from '@/stores/auth'; +import JalaliDateInput from '@/components/JalaliDateInput.vue'; import type { + OrganizationDto, OrgUserDto, TeamDto, TeamMemberRole, + UpdatePriorArchiveCutoffRequest, UpdateUserVisibilityRequest, } from '@mento/shared'; import { @@ -17,7 +20,7 @@ import { type DataVisibility, } from '@mento/shared'; import { UiButton, UiEmptyState, UiField, UiInput } from '@/components/ui'; -import { toFaDigits } from '@/lib/jalali'; +import { formatLocalDateInput, parseLocalInput, toFaDigits } from '@/lib/jalali'; const props = defineProps<{ users: OrgUserDto[]; @@ -31,7 +34,22 @@ const showPriorArchive = computed( Boolean(auth.user.enabledModules?.includes('prior_data_archive')), ); const priorUsers = computed(() => props.users.filter((u) => u.isActive !== false)); +const selectedPriorUsers = computed(() => priorUsers.value.filter((u) => u.priorArchiveEnabled)); +const priorSearch = ref(''); +const priorPick = ref(''); const priorSavingId = ref(''); +const cutoffLocal = ref(''); +const cutoffSaving = ref(false); +const cutoffLoading = ref(false); + +const availablePriorUsers = computed(() => { + const q = priorSearch.value.trim().toLowerCase(); + return priorUsers.value.filter((u) => { + if (u.priorArchiveEnabled) return false; + if (!q) return true; + return [u.fullName, u.email, u.mobile].filter(Boolean).some((v) => String(v).toLowerCase().includes(q)); + }); +}); const emit = defineEmits<{ refreshUsers: []; @@ -57,6 +75,7 @@ const editableUsers = computed(() => async function setPriorArchive(userId: string, enabled: boolean) { priorSavingId.value = userId; + priorPick.value = ''; error.value = ''; ok.value = ''; try { @@ -74,6 +93,51 @@ async function setPriorArchive(userId: string, enabled: boolean) { } } +function cutoffPayload(value: string): UpdatePriorArchiveCutoffRequest { + const parsed = parseLocalInput(value); + return { cutoffAt: parsed ? parsed.toISOString() : null }; +} + +async function loadPriorArchiveCutoff() { + cutoffLoading.value = true; + try { + const { data } = await api.get('/organizations/current'); + cutoffLocal.value = data.priorArchiveCutoffAt + ? formatLocalDateInput(new Date(data.priorArchiveCutoffAt)) + : ''; + } catch (e) { + error.value = axios.isAxiosError(e) + ? String((e.response?.data as { message?: string })?.message ?? 'بارگذاری تاریخ بایگانی ناموفق بود.') + : 'بارگذاری تاریخ بایگانی ناموفق بود.'; + } finally { + cutoffLoading.value = false; + } +} + +async function onCutoffChange(value: string) { + cutoffLocal.value = value; + cutoffSaving.value = true; + error.value = ''; + ok.value = ''; + try { + await api.patch('/organizations/current/prior-archive-cutoff', cutoffPayload(value)); + ok.value = value ? 'تاریخ بایگانی اطلاعات قبل ذخیره شد.' : 'تاریخ بایگانی اطلاعات قبل پاک شد.'; + await auth.hydrate(); + } catch (e) { + error.value = axios.isAxiosError(e) + ? String((e.response?.data as { message?: string })?.message ?? 'ذخیره تاریخ بایگانی ناموفق بود.') + : 'ذخیره تاریخ بایگانی ناموفق بود.'; + await loadPriorArchiveCutoff(); + } finally { + cutoffSaving.value = false; + } +} + +function pickPriorUser(userId: string) { + if (!userId) return; + void setPriorArchive(userId, true); +} + async function load() { loading.value = true; error.value = ''; @@ -229,6 +293,7 @@ async function setVisibility(userId: string, payload: UpdateUserVisibilityReques onMounted(() => { void load(); + if (showPriorArchive.value) void loadPriorArchiveCutoff(); }); @@ -326,20 +391,49 @@ onMounted(() => {
-

بایگانی اطلاعات قبل

+
+

بایگانی اطلاعات قبل

+ +

فقط کاربرانی که تیک می‌خورند سوئیچ کار جاری و بایگانی را می‌بینند. بقیهٔ محیط مثل قبل همهٔ رکوردها را مطابق سطح دسترسی خودشان می‌بینند.

- +
+ + +
+
+ + {{ u.fullName }} + + +
+

هنوز کاربری برای دیدن سوئیچ بایگانی انتخاب نشده است.

@@ -429,6 +523,57 @@ onMounted(() => { margin: 0 0 0.75rem; font-size: 1rem; } +.prior-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 0.75rem; + flex-wrap: wrap; + margin-bottom: 0.75rem; +} +.prior-head h3 { + margin: 0; +} +.prior-head :deep(.trigger) { + min-width: 12rem; +} +.prior-picker { + display: grid; + grid-template-columns: minmax(10rem, 1fr) minmax(13rem, 1fr); + gap: 0.6rem; + margin-top: 0.75rem; +} +.prior-chips { + display: flex; + flex-wrap: wrap; + gap: 0.45rem; + margin-top: 0.75rem; +} +.prior-chip { + display: inline-flex; + align-items: center; + gap: 0.35rem; + border: 1px solid #ccfbf1; + border-radius: 999px; + background: #f0fdfa; + color: #0f766e; + padding: 0.25rem 0.6rem; + font-size: 0.85rem; + font-weight: 600; +} +.prior-chip button { + border: 0; + background: transparent; + color: inherit; + cursor: pointer; + font: inherit; + line-height: 1; + padding: 0; +} +.prior-chip button:disabled { + opacity: 0.45; + cursor: wait; +} .row { display: grid; grid-template-columns: 1fr 1fr auto; @@ -534,7 +679,8 @@ select { } @media (max-width: 800px) { .row, - .vis-row { + .vis-row, + .prior-picker { grid-template-columns: 1fr; } }