Add KYC profile integration and SupplierInfoBlock component
Some checks failed
Build Docker Image / build (push) Failing after 2m51s

This commit is contained in:
Ruslan Bakiev
2026-01-21 09:19:44 +07:00
parent d8befc8b9f
commit ace458ed7e
10 changed files with 159 additions and 1 deletions

View File

@@ -26,6 +26,7 @@
:currency="getOfferData(option.sourceUuid)?.currency" :currency="getOfferData(option.sourceUuid)?.currency"
:unit="getOfferData(option.sourceUuid)?.unit" :unit="getOfferData(option.sourceUuid)?.unit"
:stages="getRouteStages(option)" :stages="getRouteStages(option)"
:kyc-profile-uuid="getKycProfileUuid(option.sourceUuid)"
/> />
</div> </div>
@@ -64,7 +65,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { GetOffersByHubDocument } from '~/composables/graphql/public/geo-generated' import { GetOffersByHubDocument } from '~/composables/graphql/public/geo-generated'
import type { RouteStageItem } from '~/components/RouteStagesList.vue' import type { RouteStageItem } from '~/components/RouteStagesList.vue'
import { GetOfferDocument } from '~/composables/graphql/public/exchange-generated' import { GetOfferDocument, GetSupplierProfileByTeamDocument } from '~/composables/graphql/public/exchange-generated'
const route = useRoute() const route = useRoute()
const searchStore = useSearchStore() const searchStore = useSearchStore()
@@ -76,6 +77,8 @@ const quantity = computed(() => (route.query.quantity as string) || (searchStore
// Offer data for prices // Offer data for prices
const offersData = ref<Map<string, any>>(new Map()) const offersData = ref<Map<string, any>>(new Map())
// Supplier data for KYC profile UUID (by team_uuid)
const suppliersData = ref<Map<string, any>>(new Map())
const summaryTitle = computed(() => `${productName.value}${locationName.value}`) const summaryTitle = computed(() => `${productName.value}${locationName.value}`)
const summaryMeta = computed(() => { const summaryMeta = computed(() => {
@@ -163,26 +166,57 @@ const getOfferData = (uuid?: string | null) => {
return offersData.value.get(uuid) return offersData.value.get(uuid)
} }
// Get KYC profile UUID by offer UUID
const getKycProfileUuid = (offerUuid?: string | null) => {
if (!offerUuid) return null
const offer = offersData.value.get(offerUuid)
if (!offer?.teamUuid) return null
const supplier = suppliersData.value.get(offer.teamUuid)
return supplier?.kycProfileUuid || null
}
// Load offer details for prices // Load offer details for prices
const loadOfferDetails = async (options: ProductRouteOption[]) => { const loadOfferDetails = async (options: ProductRouteOption[]) => {
if (options.length === 0) { if (options.length === 0) {
offersData.value.clear() offersData.value.clear()
suppliersData.value.clear()
return return
} }
const newOffersData = new Map<string, any>() const newOffersData = new Map<string, any>()
const newSuppliersData = new Map<string, any>()
const teamUuidsToLoad = new Set<string>()
// First, load all offers
await Promise.all(options.map(async (option) => { await Promise.all(options.map(async (option) => {
if (!option.sourceUuid) return if (!option.sourceUuid) return
try { try {
const data = await execute(GetOfferDocument, { uuid: option.sourceUuid }, 'public', 'exchange') const data = await execute(GetOfferDocument, { uuid: option.sourceUuid }, 'public', 'exchange')
if (data?.getOffer) { if (data?.getOffer) {
newOffersData.set(option.sourceUuid, data.getOffer) newOffersData.set(option.sourceUuid, data.getOffer)
if (data.getOffer.teamUuid) {
teamUuidsToLoad.add(data.getOffer.teamUuid)
}
} }
} catch (error) { } catch (error) {
console.error('Error loading offer:', option.sourceUuid, error) console.error('Error loading offer:', option.sourceUuid, error)
} }
})) }))
// Then, load supplier profiles for all team UUIDs
await Promise.all([...teamUuidsToLoad].map(async (teamUuid) => {
try {
const data = await execute(GetSupplierProfileByTeamDocument, { teamUuid }, 'public', 'exchange')
if (data?.getSupplierProfileByTeam) {
newSuppliersData.set(teamUuid, data.getSupplierProfileByTeam)
}
} catch (error) {
console.error('Error loading supplier:', teamUuid, error)
}
}))
offersData.value = newOffersData offersData.value = newOffersData
suppliersData.value = newSuppliersData
} }
// Watch for route options and load offers // Watch for route options and load offers

View File

@@ -0,0 +1,70 @@
<template>
<div v-if="kycProfileUuid && companyData" class="space-y-2">
<div class="flex items-center gap-2">
<span class="badge badge-outline badge-sm">{{ companyData.companyType || 'Компания' }}</span>
<span v-if="companyData.registrationYear" class="text-xs text-base-content/60">
с {{ companyData.registrationYear }} г.
</span>
<span v-if="companyData.isActive" class="badge badge-success badge-xs">Активна</span>
</div>
<div v-if="companyData.sourcesCount" class="text-xs text-base-content/60">
{{ companyData.sourcesCount }} {{ pluralize(companyData.sourcesCount, 'источник', 'источника', 'источников') }} данных
</div>
</div>
<div v-else-if="kycProfileUuid && pending" class="text-xs text-base-content/60">
Загрузка данных...
</div>
</template>
<script setup lang="ts">
import { GetKycProfileTeaserDocument } from '~/composables/graphql/public/kyc-generated'
const props = defineProps<{
kycProfileUuid?: string | null
}>()
const { execute } = useGraphQL()
const companyData = ref<{
companyType?: string | null
registrationYear?: number | null
isActive?: boolean | null
sourcesCount?: number | null
} | null>(null)
const pending = ref(false)
const loadCompanyData = async () => {
if (!props.kycProfileUuid) return
pending.value = true
try {
const data = await execute(
GetKycProfileTeaserDocument,
{ profileUuid: props.kycProfileUuid },
'public',
'kyc'
)
companyData.value = data?.companyTeaserByProfile || null
} catch (error) {
console.error('Error loading company data:', error)
} finally {
pending.value = false
}
}
watch(() => props.kycProfileUuid, (uuid) => {
if (uuid) {
loadCompanyData()
} else {
companyData.value = null
}
}, { immediate: true })
const pluralize = (n: number, one: string, few: string, many: string) => {
const mod10 = n % 10
const mod100 = n % 100
if (mod10 === 1 && mod100 !== 11) return one
if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) return few
return many
}
</script>

View File

@@ -11,6 +11,9 @@
</Text> </Text>
</div> </div>
<!-- Supplier info -->
<SupplierInfoBlock v-if="kycProfileUuid" :kyc-profile-uuid="kycProfileUuid" class="mb-3" />
<!-- Route stepper --> <!-- Route stepper -->
<RouteStepper <RouteStepper
v-if="stages.length > 0" v-if="stages.length > 0"
@@ -33,6 +36,7 @@ const props = withDefaults(defineProps<{
stages?: RouteStage[] stages?: RouteStage[]
startName?: string startName?: string
endName?: string endName?: string
kycProfileUuid?: string | null
}>(), { }>(), {
stages: () => [] stages: () => []
}) })

View File

@@ -15,6 +15,7 @@ const RESOURCE_MAP: Record<Api, string> = {
const CLIENT_MAP: Record<string, string> = { const CLIENT_MAP: Record<string, string> = {
'public:exchange': 'default', 'public:exchange': 'default',
'public:geo': 'publicGeo', 'public:geo': 'publicGeo',
'public:kyc': 'publicKyc',
'user:teams': 'teamsUser', 'user:teams': 'teamsUser',
'user:kyc': 'kycUser', 'user:kyc': 'kycUser',
'team:teams': 'teamsTeam', 'team:teams': 'teamsTeam',

View File

@@ -29,6 +29,12 @@ const config: CodegenConfig = {
plugins, plugins,
config: pluginConfig, config: pluginConfig,
}, },
'./app/composables/graphql/public/kyc-generated.ts': {
schema: 'https://kyc.optovia.ru/graphql/public/',
documents: './graphql/operations/public/kyc/*.graphql',
plugins,
config: pluginConfig,
},
// User-level operations (ID Token) // User-level operations (ID Token)
'./app/composables/graphql/user/teams-generated.ts': { './app/composables/graphql/user/teams-generated.ts': {

View File

@@ -2,6 +2,7 @@ query GetSupplierProfile($uuid: String!) {
getSupplierProfile(uuid: $uuid) { getSupplierProfile(uuid: $uuid) {
uuid uuid
teamUuid teamUuid
kycProfileUuid
name name
description description
country country

View File

@@ -0,0 +1,14 @@
query GetSupplierProfileByTeam($teamUuid: String!) {
getSupplierProfileByTeam(teamUuid: $teamUuid) {
uuid
teamUuid
kycProfileUuid
name
description
country
logoUrl
isVerified
isActive
offersCount
}
}

View File

@@ -0,0 +1,16 @@
query GetKycProfileFull($profileUuid: String!) {
companyFullByProfile(profileUuid: $profileUuid) {
inn
ogrn
name
companyType
registrationYear
isActive
address
director
capital
activities
sources
lastUpdated
}
}

View File

@@ -0,0 +1,8 @@
query GetKycProfileTeaser($profileUuid: String!) {
companyTeaserByProfile(profileUuid: $profileUuid) {
companyType
registrationYear
isActive
sourcesCount
}
}

View File

@@ -209,6 +209,10 @@ export default defineNuxtConfig({
httpEndpoint: process.env.NUXT_PUBLIC_GEO_GRAPHQL_PUBLIC || 'https://geo.optovia.ru/graphql/public/', httpEndpoint: process.env.NUXT_PUBLIC_GEO_GRAPHQL_PUBLIC || 'https://geo.optovia.ru/graphql/public/',
devtools: { enabled: process.dev } devtools: { enabled: process.dev }
}, },
publicKyc: {
httpEndpoint: process.env.NUXT_PUBLIC_KYC_GRAPHQL_PUBLIC || 'https://kyc.optovia.ru/graphql/public/',
devtools: { enabled: process.dev }
},
teamsUser: { teamsUser: {
httpEndpoint: process.env.NUXT_PUBLIC_TEAMS_GRAPHQL_USER || 'https://teams.optovia.ru/graphql/user/', httpEndpoint: process.env.NUXT_PUBLIC_TEAMS_GRAPHQL_USER || 'https://teams.optovia.ru/graphql/user/',
devtools: { enabled: process.dev } devtools: { enabled: process.dev }