Files
webapp/app/pages/clientarea/company-switch.vue
Ruslan Bakiev ee7b8d0ee4
Some checks failed
Build Docker Image / build (push) Has been cancelled
Remove default layout, migrate all pages to topnav
- Add layout: 'topnav' to all 27 pages that were using default layout
- Delete app/layouts/default.vue

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 09:44:26 +07:00

162 lines
5.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<Section variant="plain">
<Stack gap="6">
<Stack gap="2">
<Heading :level="1">{{ $t('dashboard.switch_company') }}</Heading>
<Text tone="muted" size="base">{{ $t('teams.switch_description') }}</Text>
</Stack>
<Alert v-if="hasError" variant="error">
<Stack gap="2">
<Heading :level="4" weight="semibold">{{ $t('common.error') }}</Heading>
<Text tone="muted">{{ error }}</Text>
<Button @click="loadUserTeams">{{ t('clientTeam.error.retry') }}</Button>
</Stack>
</Alert>
<Card v-else-if="isLoading" tone="muted" padding="lg">
<Stack align="center" justify="center" gap="3">
<Spinner />
<Text tone="muted">{{ t('clientTeamSwitch.loading.message') }}</Text>
</Stack>
</Card>
<Card v-else-if="!userTeams.length && !showCreateForm" padding="lg">
<Stack align="center" gap="3">
<IconCircle tone="primary">🏢</IconCircle>
<Heading :level="3" align="center">{{ $t('teams.no_team') }}</Heading>
<Text tone="muted" align="center">{{ $t('teams.no_team_description') }}</Text>
<Button @click="showCreateForm = true">
{{ $t('teams.create_first_team') }}
</Button>
</Stack>
</Card>
<template v-else>
<Grid :cols="1" :md="2" :lg="3" :gap="4" v-if="!showCreateForm">
<Card
v-for="team in userTeams"
:key="team.id"
padding="lg"
:class="[
'cursor-pointer transition-all',
team.isActive ? 'ring-2 ring-primary bg-primary/5' : 'hover:shadow-md'
]"
@click="switchToTeam(team.id)"
>
<Stack gap="3">
<Stack direction="row" gap="3" align="center">
<IconCircle :tone="team.isActive ? 'primary' : 'neutral'">
{{ team.name?.charAt(0)?.toUpperCase() || '?' }}
</IconCircle>
<Stack gap="1">
<Heading :level="4" weight="semibold">{{ team.name }}</Heading>
</Stack>
</Stack>
<Pill v-if="team.isActive" variant="primary">{{ $t('teams.active') }}</Pill>
</Stack>
</Card>
<Card padding="lg" class="border-2 border-dashed border-base-300 hover:border-primary cursor-pointer transition-colors" @click="showCreateForm = true">
<Stack align="center" gap="3">
<IconCircle tone="neutral"></IconCircle>
<Heading :level="4" weight="semibold">{{ $t('teams.create_new_team') }}</Heading>
<Text tone="muted" align="center">{{ $t('teams.create_description') }}</Text>
</Stack>
</Card>
</Grid>
<TeamCreateForm
v-else
@team-created="handleTeamCreated"
@cancel="showCreateForm = false"
/>
</template>
</Stack>
</Section>
</template>
<script setup lang="ts">
import { SwitchTeamDocument } from '~/composables/graphql/user/teams-generated'
definePageMeta({
layout: 'topnav',
middleware: ['auth-oidc']
})
const localePath = useLocalePath()
const { t } = useI18n()
const { mutate } = useGraphQL()
const { setActiveTeam } = useActiveTeam()
const me = useState<{
teams?: Array<{ id?: string | null; name: string; logtoOrgId?: string | null } | null> | null
activeTeamId?: string | null
} | null>('me', () => null)
const userTeams = ref<Array<{ id: string; name: string; logtoOrgId?: string | null; isActive?: boolean }>>([])
const isLoading = ref(true)
const hasError = ref(false)
const error = ref('')
const showCreateForm = ref(false)
const currentActiveTeam = computed(() => userTeams.value.find(team => team.isActive) || null)
const otherTeams = computed(() => userTeams.value.filter(team => !team.isActive))
const markActiveTeam = (teamId: string) => {
if (me.value) {
me.value = { ...me.value, activeTeamId: teamId }
}
userTeams.value = userTeams.value.map(team => ({
...team,
isActive: team.id === teamId
}))
}
const loadUserTeams = () => {
isLoading.value = true
hasError.value = false
if (!me.value?.teams) {
hasError.value = true
error.value = t('clientTeamSwitch.error.load')
isLoading.value = false
return
}
userTeams.value = me.value.teams
.filter((t): t is NonNullable<typeof t> => t !== null)
.map(t => ({
id: t.id || '',
name: t.name,
logtoOrgId: t.logtoOrgId,
isActive: t.id === me.value?.activeTeamId
}))
isLoading.value = false
}
const switchToTeam = async (teamId: string) => {
try {
const selectedTeam = userTeams.value.find(team => team.id === teamId)
if (selectedTeam) setActiveTeam(teamId, selectedTeam.logtoOrgId)
const result = await mutate(SwitchTeamDocument, { teamId }, 'user', 'teams')
if (result.switchTeam?.user) {
const newActiveId = result.switchTeam.user.activeTeamId || teamId
setActiveTeam(newActiveId, selectedTeam?.logtoOrgId)
markActiveTeam(newActiveId)
navigateTo(localePath('/clientarea/team'))
}
} catch (err: any) {
error.value = err.message || t('clientTeamSwitch.error.switch')
hasError.value = true
}
}
const handleTeamCreated = () => {
showCreateForm.value = false
navigateTo(localePath('/clientarea/team'))
}
loadUserTeams()
</script>