Files
pichunt/app/components/TeamJoinModal.vue
T
2025-09-15 15:44:50 +02:00

143 lines
3.3 KiB
Vue

<script setup lang="ts">
import { parseError } from '#shared/utils/error';
const { t } = useI18n();
const emits = defineEmits<{
update: [];
}>();
const props = defineProps<{
huntId: number;
}>();
const isOpen = ref(false);
const data = reactive({
team: null as number | null,
password: ''
});
const submitPending = ref(false);
const {
data: teams,
pending,
refresh
} = useFetch(`/api/hunt/${props.huntId}/teams`, {
lazy: true,
server: false,
immediate: false,
default: () => []
});
const error = ref<string>();
async function openModal() {
isOpen.value = true;
await refresh();
}
async function submit() {
if (data.password.length < 3 || data.team === null || submitPending.value) {
return;
}
submitPending.value = true;
try {
await $fetch(`/api/hunt/${props.huntId}/join`, {
body: JSON.stringify(data),
method: 'POST'
});
data.team = null;
data.password = '';
emits('update');
isOpen.value = false;
error.value = undefined;
} catch (e) {
console.error(e);
if (
e instanceof Error &&
e.name === 'FetchError' &&
'statusCode' in e &&
e.statusCode === 404
) {
error.value = 'error.team_not_found';
} else {
error.value = parseError(e);
}
}
submitPending.value = false;
}
</script>
<template>
<VaButton color="info" icon="search" @click="openModal">{{
t('comp.team_join.join_a_team')
}}</VaButton>
<VaModal v-model="isOpen" close-button hide-default-actions>
<h3 class="mt-16 text-3xl">{{ t('comp.team_join.join_a_team') }}</h3>
<div class="mt-4 flex flex-col gap-4">
<!-- @vue-ignore more specific than vuestic-->
<VaSelect
v-model="data.team"
:options="teams"
value-by="id"
track-by="id"
searchable
highlight-matched-text
:placeholder="t('comp.team_join.select_a_team')"
:label="t('comp.team_join.select_a_team')"
:text-by="
(team: (typeof teams)[number]) =>
t('comp.team_join.team_preview', {
team: team.name,
owner: team.owner.name,
members: team._count.members
})
"
>
<template #prependInner>
<VaIcon name="group" />
</template>
</VaSelect>
<p v-if="teams && teams.length <= 0">
{{ t('comp.team_join.no_team_yet') }}
</p>
<VaInput
v-model="data.password"
:label="t('auth.password')"
placeholder="abc123"
>
<template #prependInner>
<VaIcon name="password" />
</template>
</VaInput>
<VaAlert v-if="error" color="danger">{{ t(error) }}</VaAlert>
</div>
<template #footer>
<VaButtonGroup
:loading="submitPending || pending"
:disabled="submitPending"
>
<VaButton color="secondary" @click="isOpen = false">{{
t('page.common.cancel')
}}</VaButton>
<VaButton
color="success"
:disabled="
data.password.length < 3 || data.team === null || submitPending
"
:loading="submitPending || pending"
@click="submit"
>{{ t('page.common.join') }}</VaButton
>
</VaButtonGroup>
</template>
</VaModal>
</template>
<style scoped></style>