161 lines
5.4 KiB
Vue
161 lines
5.4 KiB
Vue
<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({
|
||
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>
|