refactor: remove all any types, add strict GraphQL scalar typing
All checks were successful
Build Docker Image / build (push) Successful in 4m3s
All checks were successful
Build Docker Image / build (push) Successful in 4m3s
- Add strictScalars: true to codegen.ts with proper scalar mappings (Date, Decimal, JSONString, JSON, UUID, BigInt → string/Record) - Replace all ref<any[]> with proper GraphQL-derived types - Add type guards for null filtering in arrays - Fix bugs exposed by typing (locationLatitude vs latitude, etc.) - Add interfaces for external components (MapboxSearchBox) This enables end-to-end type safety from GraphQL schema to frontend.
This commit is contained in:
@@ -48,8 +48,8 @@
|
||||
<Heading :level="2">{{ t('clientTeam.members.title') }}</Heading>
|
||||
<Grid :cols="1" :md="2" :lg="3" :gap="4">
|
||||
<Card
|
||||
v-for="member in currentTeam?.members || []"
|
||||
:key="member.user?.id"
|
||||
v-for="(member, index) in currentTeamMembers"
|
||||
:key="member.user?.id ?? `member-${index}`"
|
||||
padding="lg"
|
||||
>
|
||||
<Stack gap="3">
|
||||
@@ -67,7 +67,7 @@
|
||||
|
||||
<!-- Pending invitations -->
|
||||
<Card
|
||||
v-for="invitation in currentTeam?.invitations || []"
|
||||
v-for="invitation in currentTeamInvitations"
|
||||
:key="invitation.uuid"
|
||||
padding="lg"
|
||||
class="border-dashed border-warning"
|
||||
@@ -111,7 +111,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { GetTeamDocument } from '~/composables/graphql/user/teams-generated'
|
||||
import { GetTeamDocument, type GetTeamQueryResult } from '~/composables/graphql/user/teams-generated'
|
||||
|
||||
interface UserTeam {
|
||||
id?: string | null
|
||||
name: string
|
||||
logtoOrgId?: string | null
|
||||
}
|
||||
|
||||
type TeamWithMembers = NonNullable<GetTeamQueryResult['getTeam']>
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
@@ -129,8 +137,8 @@ const me = useState<{
|
||||
} | null>('me', () => null)
|
||||
const { setActiveTeam } = useActiveTeam()
|
||||
|
||||
const userTeams = ref<any[]>([])
|
||||
const currentTeam = ref<any>(null)
|
||||
const userTeams = ref<UserTeam[]>([])
|
||||
const currentTeam = ref<TeamWithMembers | UserTeam | null>(null)
|
||||
const isLoading = ref(true)
|
||||
const hasError = ref(false)
|
||||
const error = ref('')
|
||||
@@ -143,7 +151,7 @@ const teamHeaderActions = computed(() => {
|
||||
}
|
||||
return actions
|
||||
})
|
||||
const roleText = (role?: string) => {
|
||||
const roleText = (role?: string | null) => {
|
||||
const map: Record<string, string> = {
|
||||
OWNER: t('clientTeam.roles.owner'),
|
||||
ADMIN: t('clientTeam.roles.admin'),
|
||||
@@ -153,13 +161,30 @@ const roleText = (role?: string) => {
|
||||
return map[role || ''] || role || t('clientTeam.roles.member')
|
||||
}
|
||||
|
||||
const getMemberInitials = (user?: any) => {
|
||||
interface TeamMember {
|
||||
id?: string | null
|
||||
firstName?: string | null
|
||||
lastName?: string | null
|
||||
}
|
||||
|
||||
const getMemberInitials = (user?: TeamMember | null) => {
|
||||
if (!user) return '??'
|
||||
const first = user.firstName?.charAt(0) || ''
|
||||
const last = user.lastName?.charAt(0) || ''
|
||||
return (first + last).toUpperCase() || user.id?.charAt(0).toUpperCase() || '??'
|
||||
}
|
||||
|
||||
// Type-safe accessors for TeamWithMembers properties
|
||||
const currentTeamMembers = computed(() => {
|
||||
const team = currentTeam.value
|
||||
return team && 'members' in team ? (team.members || []).filter((m): m is NonNullable<typeof m> => m !== null) : []
|
||||
})
|
||||
|
||||
const currentTeamInvitations = computed(() => {
|
||||
const team = currentTeam.value
|
||||
return team && 'invitations' in team ? (team.invitations || []).filter((i): i is NonNullable<typeof i> => i !== null) : []
|
||||
})
|
||||
|
||||
const loadUserTeams = async () => {
|
||||
try {
|
||||
isLoading.value = true
|
||||
@@ -177,13 +202,15 @@ const loadUserTeams = async () => {
|
||||
currentTeam.value = teamData.value?.getTeam || null
|
||||
} else if (userTeams.value.length > 0) {
|
||||
const firstTeam = userTeams.value[0]
|
||||
setActiveTeam(firstTeam?.id || null, firstTeam?.logtoOrgId)
|
||||
currentTeam.value = firstTeam
|
||||
if (firstTeam) {
|
||||
setActiveTeam(firstTeam.id || null, firstTeam.logtoOrgId)
|
||||
currentTeam.value = firstTeam
|
||||
}
|
||||
}
|
||||
// Если нет команды - currentTeam остаётся null, показываем EmptyState
|
||||
} catch (err: any) {
|
||||
} catch (err: unknown) {
|
||||
hasError.value = true
|
||||
error.value = err.message || t('clientTeam.error.load')
|
||||
error.value = err instanceof Error ? err.message : t('clientTeam.error.load')
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user