From bc63d8662924619ab08534b13a0ef9d3d2e0c2e2 Mon Sep 17 00:00:00 2001 From: Dmatrushka19 Date: Thu, 23 Apr 2026 21:55:16 +0300 Subject: [PATCH 1/7] =?UTF-8?q?=D1=84=D0=B0=D0=B9=D0=BB,=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B4=D0=B0=D1=8E=D1=89=D0=B8=D0=B9=20ZachetCard.vue=20=D0=BD?= =?UTF-8?q?=D0=B0=D1=80=D1=83=D0=B6=D1=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/ZachetCard/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/modules/ZachetCard/index.ts diff --git a/src/modules/ZachetCard/index.ts b/src/modules/ZachetCard/index.ts new file mode 100644 index 0000000..8a947c7 --- /dev/null +++ b/src/modules/ZachetCard/index.ts @@ -0,0 +1 @@ +export { default as ZachetCard } from './ZachetCard.vue'; From 29ea5299909fe339ad44e99fb941c5d7608f357f Mon Sep 17 00:00:00 2001 From: Dmatrushka19 Date: Thu, 23 Apr 2026 21:55:48 +0300 Subject: [PATCH 2/7] =?UTF-8?q?=D0=BF=D1=80=D0=B5=D0=BE=D0=B1=D1=80=D0=B0?= =?UTF-8?q?=D0=B7=D1=83=D0=B5=D1=82=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82=20API?= =?UTF-8?q?=20=D0=B2=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5,=20=D1=83?= =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=BD=D1=8B=D0=B5=20=D0=B4=D0=BB=D1=8F=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/ZachetCard/controller/mapper.ts | 66 +++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 src/modules/ZachetCard/controller/mapper.ts diff --git a/src/modules/ZachetCard/controller/mapper.ts b/src/modules/ZachetCard/controller/mapper.ts new file mode 100644 index 0000000..845539c --- /dev/null +++ b/src/modules/ZachetCard/controller/mapper.ts @@ -0,0 +1,66 @@ +import type { UserdataItem, ZachetCardData } from './types'; + +const FALLBACK = '—'; + +function normalizeValue(value?: string | null): string | null { + if (typeof value !== 'string') return null; + + const trimmed = value.trim(); + + return trimmed.length ? trimmed : null; +} + +function getValue(items: UserdataItem[], category: string, param: string): string | null { + const item = items.find(entry => entry.category === category && entry.param === param); + + return normalizeValue(item?.value); +} + +function resolveUnionCardNumber(items: UserdataItem[]): string { + return ( + getValue(items, 'Учёба', 'Номер профсоюзного билета') ?? + getValue(items, 'Учетные данные', 'Номер профсоюзного билета') ?? + FALLBACK + ); +} + +function resolvePhotoUrl(items: UserdataItem[]): string | undefined { + const value = getValue(items, 'Личная информация', 'Фото'); + + if (!value) return undefined; + + return value; +} + +export function mapUserdataToZachetCard(items: UserdataItem[]): ZachetCardData { + logZachetCardMapper('start mapping items', { items }); + + const mappedCard: ZachetCardData = { + unionCardNumber: resolveUnionCardNumber(items), + fullNameRu: getValue(items, 'Личная информация', 'Полное имя') ?? FALLBACK, + fullNameEn: FALLBACK, + birthDate: getValue(items, 'Личная информация', 'Дата рождения') ?? FALLBACK, + facultyRu: getValue(items, 'Учёба', 'Факультет') ?? FALLBACK, + facultyEn: FALLBACK, + statusRu: getValue(items, 'Учёба', 'Ступень обучения') ?? FALLBACK, + statusEn: FALLBACK, + photoUrl: resolvePhotoUrl(items), + }; + + logZachetCardMapper('mapped card result', mappedCard); + + return mappedCard; +} + +function logZachetCardMapper(message: string, payload?: unknown) { + if (!import.meta.env.DEV) { + return; + } + + if (payload === undefined) { + console.log('[ZachetCard][mapper]', message); + return; + } + + console.log('[ZachetCard][mapper]', message, payload); +} From d66f584eaa01d9488e754f37962acffbce1fb4cd Mon Sep 17 00:00:00 2001 From: Dmatrushka19 Date: Thu, 23 Apr 2026 21:56:37 +0300 Subject: [PATCH 3/7] =?UTF-8?q?Vue-=D0=BA=D0=BE=D0=BC=D0=BF=D0=BE=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D1=82=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D1=8F,=20?= =?UTF-8?q?=D0=BA=D0=BE=D1=82=D0=BE=D1=80=D1=8B=D0=B9=20=D1=80=D0=B8=D1=81?= =?UTF-8?q?=D1=83=D0=B5=D1=82=20=D0=BA=D0=B0=D1=80=D1=82=D1=83=20=D0=97?= =?UTF-8?q?=D0=B0=D1=87=D0=B5=D1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/ZachetCard/ZachetCard.vue | 278 ++++++++++++++++++++++++++ 1 file changed, 278 insertions(+) create mode 100644 src/modules/ZachetCard/ZachetCard.vue diff --git a/src/modules/ZachetCard/ZachetCard.vue b/src/modules/ZachetCard/ZachetCard.vue new file mode 100644 index 0000000..3751e60 --- /dev/null +++ b/src/modules/ZachetCard/ZachetCard.vue @@ -0,0 +1,278 @@ + + + + + From 1cb028782b32ba5731b7c388c50f713fc70a4900 Mon Sep 17 00:00:00 2001 From: Dmatrushka19 Date: Thu, 23 Apr 2026 21:58:10 +0300 Subject: [PATCH 4/7] =?UTF-8?q?=D0=B4=D0=B5=D1=80=D0=B3=D0=B0=D0=B5=D1=82?= =?UTF-8?q?=20=D1=80=D1=83=D1=87=D0=BA=D1=83=20=D0=B8=20=D1=85=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D1=82=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/ZachetCard/controller/store.ts | 91 ++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/modules/ZachetCard/controller/store.ts diff --git a/src/modules/ZachetCard/controller/store.ts b/src/modules/ZachetCard/controller/store.ts new file mode 100644 index 0000000..2718489 --- /dev/null +++ b/src/modules/ZachetCard/controller/store.ts @@ -0,0 +1,91 @@ +import { defineStore } from 'pinia'; +import { ref } from 'vue'; +import { UserdataApi } from '@/api/controllers/UserdataApi'; +import { mapUserdataToZachetCard } from './mapper'; +import type { ZachetCardData, UserdataResponse } from './types'; + +export const useZachetCardStore = defineStore('zachetCard', () => { + const cards = ref>({}); + const loadingByUserId = ref>({}); + const errorByUserId = ref>({}); + + async function fetchCard(userId: number, force = false): Promise { + logZachetCardStore('fetchCard called', { userId, force }); + + if (!force && cards.value[userId]) { + logZachetCardStore('return cached card', { + userId, + card: cards.value[userId], + }); + + return cards.value[userId]; + } + + loadingByUserId.value[userId] = true; + errorByUserId.value[userId] = null; + + try { + const response = await UserdataApi.getUser(userId); + const data = response?.data as UserdataResponse | undefined; + + logZachetCardStore('raw response data', data); + + const items = Array.isArray(data?.items) ? data.items : []; + const mappedCard = mapUserdataToZachetCard(items); + + cards.value[userId] = mappedCard; + + logZachetCardStore('card saved to store', { + userId, + card: mappedCard, + }); + + return mappedCard; + } catch (error) { + errorByUserId.value[userId] = 'Не удалось загрузить данные карты'; + errorZachetCardStore('fetchCard error', error); + + return null; + } finally { + loadingByUserId.value[userId] = false; + logZachetCardStore('fetchCard finished', { + userId, + loading: loadingByUserId.value[userId], + error: errorByUserId.value[userId], + }); + } + } + + return { + cards, + loadingByUserId, + errorByUserId, + fetchCard, + }; +}); + +function logZachetCardStore(message: string, payload?: unknown) { + if (!import.meta.env.DEV) { + return; + } + + if (payload === undefined) { + console.log('[ZachetCard][store]', message); + return; + } + + console.log('[ZachetCard][store]', message, payload); +} + +function errorZachetCardStore(message: string, payload?: unknown) { + if (!import.meta.env.DEV) { + return; + } + + if (payload === undefined) { + console.error('[ZachetCard][store]', message); + return; + } + + console.error('[ZachetCard][store]', message, payload); +} From ec5ea90d547705990ce93a1c847af1e3d124b7c4 Mon Sep 17 00:00:00 2001 From: Dmatrushka19 Date: Thu, 23 Apr 2026 22:00:17 +0300 Subject: [PATCH 5/7] =?UTF-8?q?=D1=81=D0=BB=D0=BE=D0=B2=D0=B0=D1=80=D1=8C?= =?UTF-8?q?=20=D1=84=D0=BE=D1=80=D0=BC=D0=B0=D1=82=D0=BE=D0=B2=20=D0=B4?= =?UTF-8?q?=D0=B0=D0=BD=D0=BD=D1=8B=D1=85=20=D0=B4=D0=BB=D1=8F=20=D0=BC?= =?UTF-8?q?=D0=BE=D0=B4=D1=83=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/ZachetCard/controller/types.ts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/modules/ZachetCard/controller/types.ts diff --git a/src/modules/ZachetCard/controller/types.ts b/src/modules/ZachetCard/controller/types.ts new file mode 100644 index 0000000..810a31e --- /dev/null +++ b/src/modules/ZachetCard/controller/types.ts @@ -0,0 +1,21 @@ +export interface UserdataItem { + category: string; + param: string; + value?: string | null; +} + +export interface UserdataResponse { + items: UserdataItem[]; +} + +export interface ZachetCardData { + unionCardNumber: string; + fullNameRu: string; + fullNameEn: string; + birthDate: string; + facultyRu: string; + facultyEn: string; + statusRu: string; + statusEn: string; + photoUrl?: string; +} From 76e54e87b57f99bac8637d8f87bfaae6ea904ca5 Mon Sep 17 00:00:00 2001 From: Dmatrushka19 Date: Thu, 23 Apr 2026 22:01:38 +0300 Subject: [PATCH 6/7] =?UTF-8?q?=D1=83=D0=BF=D1=80=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA?= =?UTF-8?q?=D0=BE=D0=B9=20=D0=BA=D0=B0=D1=80=D1=82=D0=BE=D1=87=D0=BA=D0=B8?= =?UTF-8?q?=20=D0=B8=20=D0=B2=D1=8B=D0=B4=D0=B0=D1=87=D0=B0=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BF=D0=BE=D0=BD=D0=B5=D0=BD=D1=82=D1=83=20=D1=83=D0=B6?= =?UTF-8?q?=D0=B5=20=D0=B3=D0=BE=D1=82=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE=20?= =?UTF-8?q?=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BE=D1=82=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/useZachetCardController.ts | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/modules/ZachetCard/controller/useZachetCardController.ts diff --git a/src/modules/ZachetCard/controller/useZachetCardController.ts b/src/modules/ZachetCard/controller/useZachetCardController.ts new file mode 100644 index 0000000..c14cb0f --- /dev/null +++ b/src/modules/ZachetCard/controller/useZachetCardController.ts @@ -0,0 +1,102 @@ +import { computed, onMounted, watch } from 'vue'; +import { useProfileStore } from '@/store/profile'; +import { useZachetCardStore } from './store'; + +interface UseZachetCardControllerProps { + userId?: number; +} + +export function useZachetCardController(props: UseZachetCardControllerProps) { + const profileStore = useProfileStore(); + const zachetCardStore = useZachetCardStore(); + + const resolvedUserId = computed(() => props.userId ?? profileStore.id ?? null); + + const card = computed(() => { + const userId = resolvedUserId.value; + + if (!userId) return null; + + return zachetCardStore.cards[userId] ?? null; + }); + + const loading = computed(() => { + const userId = resolvedUserId.value; + + if (!userId) return false; + + return Boolean(zachetCardStore.loadingByUserId[userId]); + }); + + const error = computed(() => { + const userId = resolvedUserId.value; + + if (!userId) return 'Не найден id пользователя'; + + return zachetCardStore.errorByUserId[userId] ?? null; + }); + + async function load(force = false) { + const userId = resolvedUserId.value; + + logZachetCardController('load called', { + userId, + force, + }); + + if (!userId) { + logZachetCardController('load skipped because userId is empty'); + return; + } + + await zachetCardStore.fetchCard(userId, force); + } + + async function reload() { + logZachetCardController('reload called'); + await load(true); + } + + onMounted(() => { + logZachetCardController('controller mounted', { + resolvedUserId: resolvedUserId.value, + }); + + void load(); + }); + + watch( + resolvedUserId, + (nextUserId, prevUserId) => { + logZachetCardController('resolvedUserId changed', { + prevUserId, + nextUserId, + }); + + if (nextUserId && nextUserId !== prevUserId) { + void load(); + } + }, + { immediate: false } + ); + + return { + card, + loading, + error, + reload, + }; +} + +function logZachetCardController(message: string, payload?: unknown) { + if (!import.meta.env.DEV) { + return; + } + + if (payload === undefined) { + console.log('[ZachetCard][controller]', message); + return; + } + + console.log('[ZachetCard][controller]', message, payload); +} From ac7476bcb0971860002a9efc741da1276cc77713 Mon Sep 17 00:00:00 2001 From: Dmatrushka19 Date: Thu, 23 Apr 2026 22:02:23 +0300 Subject: [PATCH 7/7] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=20=D0=BC=D0=B0=D1=80=D1=88=D1=80=D1=83=D1=82=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=B4=D0=B5=D0=B1=D0=B0=D0=B3=D0=B0=20=D0=B8=20?= =?UTF-8?q?=D1=81=D1=82=D1=80=D0=B0=D0=BD=D0=B8=D1=86=D0=B0=20=D0=BE=D1=82?= =?UTF-8?q?=D0=BE=D0=B1=D1=80=D0=B0=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA?= =?UTF-8?q?=D0=B0=D1=80=D1=82=D0=BE=D1=87=D0=BA=D0=B8=20=D1=82=D0=B0=D0=BA?= =?UTF-8?q?=D0=B6=D0=B5=20=D0=B4=D0=BB=D1=8F=20=D0=B4=D0=B5=D0=B1=D0=B0?= =?UTF-8?q?=D0=B3=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/router/index.ts | 11 ++++ src/views/debug/ZachetCardDebugView.vue | 77 +++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/views/debug/ZachetCardDebugView.vue diff --git a/src/router/index.ts b/src/router/index.ts index fbf1afc..d06c660 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -69,6 +69,17 @@ const routes: RouteRecordRaw[] = [ path: '/:pathMatch(.*)', component: () => import('@/views/error/Error404View.vue'), }, + { + path: '/debug/zachet-card', + component: () => import('@/views/debug/ZachetCardDebugView.vue'), + beforeEnter: () => { + const token = LocalStorage.get(LocalStorageItem.Token); + + if (!token) { + return { path: '/auth' }; + } + }, + }, ]; const router = createRouter({ diff --git a/src/views/debug/ZachetCardDebugView.vue b/src/views/debug/ZachetCardDebugView.vue new file mode 100644 index 0000000..0e2be5a --- /dev/null +++ b/src/views/debug/ZachetCardDebugView.vue @@ -0,0 +1,77 @@ + + + + +