generated from VLADIMIR/template_frontend
ups
This commit is contained in:
@@ -516,6 +516,29 @@ service EveningDetectiveServer {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpc IssueGameTickets(IssueGameTicketsReq) returns (IssueGameTicketsRsp) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
post: "/api/games/{gameId}/tickets"
|
||||||
|
body: "*"
|
||||||
|
};
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
security: { security_requirement: {} }
|
||||||
|
tags: "Билеты";
|
||||||
|
summary: "Выпустить билеты на игру";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
rpc GetTicket(GetTicketReq) returns (GetTicketRsp) {
|
||||||
|
option (google.api.http) = {
|
||||||
|
get: "/api/tickets/{token}"
|
||||||
|
};
|
||||||
|
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = {
|
||||||
|
security: { security_requirement: {} }
|
||||||
|
tags: "Билеты";
|
||||||
|
summary: "Получить данные билета";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Создать сессию экрана. Экран получает токен и показывает QR для привязки.
|
// Создать сессию экрана. Экран получает токен и показывает QR для привязки.
|
||||||
rpc CreateScreenSession(CreateScreenSessionReq) returns (CreateScreenSessionRsp) {
|
rpc CreateScreenSession(CreateScreenSessionReq) returns (CreateScreenSessionRsp) {
|
||||||
option (google.api.http) = {
|
option (google.api.http) = {
|
||||||
@@ -1354,6 +1377,7 @@ message AddGameReq {
|
|||||||
google.protobuf.Timestamp start_at = 3;
|
google.protobuf.Timestamp start_at = 3;
|
||||||
int32 scenario_id = 4;
|
int32 scenario_id = 4;
|
||||||
string format = 5;
|
string format = 5;
|
||||||
|
string location = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AddGameRsp {
|
message AddGameRsp {
|
||||||
@@ -1379,6 +1403,7 @@ message Game {
|
|||||||
optional google.protobuf.Timestamp started_at = 8;
|
optional google.protobuf.Timestamp started_at = 8;
|
||||||
optional google.protobuf.Timestamp ended_at = 9;
|
optional google.protobuf.Timestamp ended_at = 9;
|
||||||
string format = 10;
|
string format = 10;
|
||||||
|
string location = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum ScreenMode {
|
enum ScreenMode {
|
||||||
@@ -1695,6 +1720,7 @@ message UpdateGameReq {
|
|||||||
google.protobuf.Timestamp start_at = 4;
|
google.protobuf.Timestamp start_at = 4;
|
||||||
int32 scenario_id = 5;
|
int32 scenario_id = 5;
|
||||||
string format = 6;
|
string format = 6;
|
||||||
|
string location = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateGameRsp {
|
message UpdateGameRsp {
|
||||||
@@ -1861,3 +1887,37 @@ message UpdateScenarioBackgroundReq {
|
|||||||
message UpdateScenarioBackgroundRsp {
|
message UpdateScenarioBackgroundRsp {
|
||||||
string error = 1;
|
string error = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message IssueGameTicketsReq {
|
||||||
|
int32 gameId = 1;
|
||||||
|
int32 count = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message IssueGameTicketsRsp {
|
||||||
|
string error = 1;
|
||||||
|
repeated Ticket tickets = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message Ticket {
|
||||||
|
int32 id = 1;
|
||||||
|
string url = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTicketReq {
|
||||||
|
string token = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GetTicketRsp {
|
||||||
|
string error = 1;
|
||||||
|
TicketPage ticket = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TicketPage {
|
||||||
|
int32 id = 1;
|
||||||
|
string game_name = 2;
|
||||||
|
google.protobuf.Timestamp start_at = 3;
|
||||||
|
string location = 4;
|
||||||
|
string status = 5;
|
||||||
|
string game_status = 6;
|
||||||
|
optional google.protobuf.Timestamp used_at = 7;
|
||||||
|
}
|
||||||
|
|||||||
@@ -474,6 +474,7 @@ export type AddGameReq = {
|
|||||||
startAt: wellKnownTimestamp | undefined;
|
startAt: wellKnownTimestamp | undefined;
|
||||||
scenarioId: number | undefined;
|
scenarioId: number | undefined;
|
||||||
format: string | undefined;
|
format: string | undefined;
|
||||||
|
location: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AddGameRsp = {
|
export type AddGameRsp = {
|
||||||
@@ -500,6 +501,7 @@ export type Game = {
|
|||||||
startedAt?: wellKnownTimestamp;
|
startedAt?: wellKnownTimestamp;
|
||||||
endedAt?: wellKnownTimestamp;
|
endedAt?: wellKnownTimestamp;
|
||||||
format: string | undefined;
|
format: string | undefined;
|
||||||
|
location: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ScreenState = {
|
export type ScreenState = {
|
||||||
@@ -780,6 +782,7 @@ export type UpdateGameReq = {
|
|||||||
startAt: wellKnownTimestamp | undefined;
|
startAt: wellKnownTimestamp | undefined;
|
||||||
scenarioId: number | undefined;
|
scenarioId: number | undefined;
|
||||||
format: string | undefined;
|
format: string | undefined;
|
||||||
|
location: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UpdateGameRsp = {
|
export type UpdateGameRsp = {
|
||||||
@@ -947,6 +950,40 @@ export type UpdateScenarioBackgroundRsp = {
|
|||||||
error: string | undefined;
|
error: string | undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type IssueGameTicketsReq = {
|
||||||
|
gameId: number | undefined;
|
||||||
|
count: number | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type IssueGameTicketsRsp = {
|
||||||
|
error: string | undefined;
|
||||||
|
tickets: Ticket[] | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Ticket = {
|
||||||
|
id: number | undefined;
|
||||||
|
url: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetTicketReq = {
|
||||||
|
token: string | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type GetTicketRsp = {
|
||||||
|
error: string | undefined;
|
||||||
|
ticket: TicketPage | undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TicketPage = {
|
||||||
|
id: number | undefined;
|
||||||
|
gameName: string | undefined;
|
||||||
|
startAt: wellKnownTimestamp | undefined;
|
||||||
|
location: string | undefined;
|
||||||
|
status: string | undefined;
|
||||||
|
gameStatus: string | undefined;
|
||||||
|
usedAt?: wellKnownTimestamp;
|
||||||
|
};
|
||||||
|
|
||||||
export interface EveningDetectiveServer {
|
export interface EveningDetectiveServer {
|
||||||
GetParticipation(request: GetParticipationReq): Promise<ParticipationRsp>;
|
GetParticipation(request: GetParticipationReq): Promise<ParticipationRsp>;
|
||||||
AnswerParticipation(request: AnswerParticipationReq): Promise<ParticipationRsp>;
|
AnswerParticipation(request: AnswerParticipationReq): Promise<ParticipationRsp>;
|
||||||
@@ -996,6 +1033,8 @@ export interface EveningDetectiveServer {
|
|||||||
AddGame(request: AddGameReq): Promise<AddGameRsp>;
|
AddGame(request: AddGameReq): Promise<AddGameRsp>;
|
||||||
GetGames(request: GetGamesReq): Promise<GetGamesRsp>;
|
GetGames(request: GetGamesReq): Promise<GetGamesRsp>;
|
||||||
GetGame(request: GetGameReq): Promise<GetGameRsp>;
|
GetGame(request: GetGameReq): Promise<GetGameRsp>;
|
||||||
|
IssueGameTickets(request: IssueGameTicketsReq): Promise<IssueGameTicketsRsp>;
|
||||||
|
GetTicket(request: GetTicketReq): Promise<GetTicketRsp>;
|
||||||
// Создать сессию экрана. Экран получает токен и показывает QR для привязки.
|
// Создать сессию экрана. Экран получает токен и показывает QR для привязки.
|
||||||
CreateScreenSession(request: CreateScreenSessionReq): Promise<CreateScreenSessionRsp>;
|
CreateScreenSession(request: CreateScreenSessionReq): Promise<CreateScreenSessionRsp>;
|
||||||
// Получить состояние экрана по его токену. Вызывается самим экраном.
|
// Получить состояние экрана по его токену. Вызывается самим экраном.
|
||||||
@@ -1882,6 +1921,46 @@ export function createEveningDetectiveServerClient(
|
|||||||
method: "GetGame",
|
method: "GetGame",
|
||||||
}) as Promise<GetGameRsp>;
|
}) as Promise<GetGameRsp>;
|
||||||
},
|
},
|
||||||
|
IssueGameTickets(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
|
if (!request.gameId) {
|
||||||
|
throw new Error("missing required field request.gameId");
|
||||||
|
}
|
||||||
|
const path = `api/games/${request.gameId}/tickets`; // eslint-disable-line quotes
|
||||||
|
const body = JSON.stringify(request);
|
||||||
|
const queryParams: string[] = [];
|
||||||
|
let uri = path;
|
||||||
|
if (queryParams.length > 0) {
|
||||||
|
uri += `?${queryParams.join("&")}`
|
||||||
|
}
|
||||||
|
return handler({
|
||||||
|
path: uri,
|
||||||
|
method: "POST",
|
||||||
|
body,
|
||||||
|
}, {
|
||||||
|
service: "EveningDetectiveServer",
|
||||||
|
method: "IssueGameTickets",
|
||||||
|
}) as Promise<IssueGameTicketsRsp>;
|
||||||
|
},
|
||||||
|
GetTicket(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
|
if (!request.token) {
|
||||||
|
throw new Error("missing required field request.token");
|
||||||
|
}
|
||||||
|
const path = `api/tickets/${request.token}`; // eslint-disable-line quotes
|
||||||
|
const body = null;
|
||||||
|
const queryParams: string[] = [];
|
||||||
|
let uri = path;
|
||||||
|
if (queryParams.length > 0) {
|
||||||
|
uri += `?${queryParams.join("&")}`
|
||||||
|
}
|
||||||
|
return handler({
|
||||||
|
path: uri,
|
||||||
|
method: "GET",
|
||||||
|
body,
|
||||||
|
}, {
|
||||||
|
service: "EveningDetectiveServer",
|
||||||
|
method: "GetTicket",
|
||||||
|
}) as Promise<GetTicketRsp>;
|
||||||
|
},
|
||||||
CreateScreenSession(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
CreateScreenSession(request) { // eslint-disable-line @typescript-eslint/no-unused-vars
|
||||||
const path = `api/screens`; // eslint-disable-line quotes
|
const path = `api/screens`; // eslint-disable-line quotes
|
||||||
const body = JSON.stringify(request);
|
const body = JSON.stringify(request);
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const gameId = route.params.id
|
|||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const showSettingsModal = ref(false)
|
const showSettingsModal = ref(false)
|
||||||
const settingsDraft = ref({ name: '', description: '', format: 'online' })
|
const settingsDraft = ref({ name: '', description: '', format: 'online', location: '' })
|
||||||
const showGiveApplicationModal = ref(false)
|
const showGiveApplicationModal = ref(false)
|
||||||
const showAddTeamModal = ref(false)
|
const showAddTeamModal = ref(false)
|
||||||
const showDeleteTeamModal = ref(false)
|
const showDeleteTeamModal = ref(false)
|
||||||
@@ -67,6 +67,7 @@ const game = ref<Game>({
|
|||||||
startAt: undefined,
|
startAt: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
format: undefined,
|
format: undefined,
|
||||||
|
location: undefined,
|
||||||
scenario: undefined,
|
scenario: undefined,
|
||||||
teams: undefined,
|
teams: undefined,
|
||||||
})
|
})
|
||||||
@@ -321,6 +322,7 @@ function openSettings() {
|
|||||||
name: game.value.name || '',
|
name: game.value.name || '',
|
||||||
description: game.value.description || '',
|
description: game.value.description || '',
|
||||||
format: game.value.format || 'online',
|
format: game.value.format || 'online',
|
||||||
|
location: game.value.location || '',
|
||||||
}
|
}
|
||||||
showSettingsModal.value = true
|
showSettingsModal.value = true
|
||||||
}
|
}
|
||||||
@@ -335,6 +337,7 @@ async function updateGame() {
|
|||||||
name: settingsDraft.value.name,
|
name: settingsDraft.value.name,
|
||||||
description: settingsDraft.value.description,
|
description: settingsDraft.value.description,
|
||||||
format: settingsDraft.value.format,
|
format: settingsDraft.value.format,
|
||||||
|
location: settingsDraft.value.location.trim(),
|
||||||
startAt: game.value.startAt,
|
startAt: game.value.startAt,
|
||||||
scenarioId: game.value.scenario?.id,
|
scenarioId: game.value.scenario?.id,
|
||||||
})
|
})
|
||||||
@@ -981,6 +984,13 @@ useSimplePolling(() => {
|
|||||||
type="textarea"
|
type="textarea"
|
||||||
placeholder="Убийство пастуха..."
|
placeholder="Убийство пастуха..."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<p class="settings-header">Место проведения</p>
|
||||||
|
<n-input
|
||||||
|
v-model:value="settingsDraft.location"
|
||||||
|
type="text"
|
||||||
|
placeholder="Оставьте пустым для онлайн-игры"
|
||||||
|
/>
|
||||||
</n-space>
|
</n-space>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<n-flex justify="end">
|
<n-flex justify="end">
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const showCreateGameModal = ref(false)
|
|||||||
const newGameName = ref('')
|
const newGameName = ref('')
|
||||||
const newGameFormat = ref('online')
|
const newGameFormat = ref('online')
|
||||||
const newGameDescription = ref('')
|
const newGameDescription = ref('')
|
||||||
|
const newGameLocation = ref('')
|
||||||
const newGameScenarioId = ref<number | null>(null)
|
const newGameScenarioId = ref<number | null>(null)
|
||||||
const newGameStartAt = ref<number | null>(null)
|
const newGameStartAt = ref<number | null>(null)
|
||||||
const isCreating = ref(false)
|
const isCreating = ref(false)
|
||||||
@@ -85,6 +86,7 @@ function openCreateGame() {
|
|||||||
newGameName.value = ''
|
newGameName.value = ''
|
||||||
newGameFormat.value = 'online'
|
newGameFormat.value = 'online'
|
||||||
newGameDescription.value = ''
|
newGameDescription.value = ''
|
||||||
|
newGameLocation.value = ''
|
||||||
newGameScenarioId.value = null
|
newGameScenarioId.value = null
|
||||||
newGameStartAt.value = null
|
newGameStartAt.value = null
|
||||||
showCreateGameModal.value = true
|
showCreateGameModal.value = true
|
||||||
@@ -103,6 +105,7 @@ async function createGame() {
|
|||||||
const res = await client.AddGame({
|
const res = await client.AddGame({
|
||||||
name: name,
|
name: name,
|
||||||
description: newGameDescription.value,
|
description: newGameDescription.value,
|
||||||
|
location: newGameLocation.value.trim(),
|
||||||
format: newGameFormat.value,
|
format: newGameFormat.value,
|
||||||
// Сервер парсит startAt как google.protobuf.Timestamp — нужен RFC 3339 с таймзоной
|
// Сервер парсит startAt как google.protobuf.Timestamp — нужен RFC 3339 с таймзоной
|
||||||
startAt: new Date(startAtMs).toISOString(),
|
startAt: new Date(startAtMs).toISOString(),
|
||||||
@@ -143,6 +146,7 @@ async function createGame() {
|
|||||||
<div v-if="formatGameDateTime(game.startAt)" class="game-date">
|
<div v-if="formatGameDateTime(game.startAt)" class="game-date">
|
||||||
Начало: {{ formatGameDateTime(game.startAt) }}
|
Начало: {{ formatGameDateTime(game.startAt) }}
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="game.location" class="game-date">Место: {{ game.location }}</div>
|
||||||
<div v-if="game.status == 'finish'" class="game-actions">
|
<div v-if="game.status == 'finish'" class="game-actions">
|
||||||
<n-button size="small" ghost @click.stop="toResults(game.id!)">Результаты</n-button>
|
<n-button size="small" ghost @click.stop="toResults(game.id!)">Результаты</n-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -185,6 +189,13 @@ async function createGame() {
|
|||||||
placeholder="Убийство пастуха..."
|
placeholder="Убийство пастуха..."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<p>Место проведения</p>
|
||||||
|
<n-input
|
||||||
|
v-model:value="newGameLocation"
|
||||||
|
type="text"
|
||||||
|
placeholder="Оставьте пустым для онлайн-игры"
|
||||||
|
/>
|
||||||
|
|
||||||
<p>Сценарий (дело из каталога)</p>
|
<p>Сценарий (дело из каталога)</p>
|
||||||
<n-select
|
<n-select
|
||||||
v-model:value="newGameScenarioId"
|
v-model:value="newGameScenarioId"
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ async function addGame(name: string, id: number) {
|
|||||||
const resAddGame = await client.AddGame({
|
const resAddGame = await client.AddGame({
|
||||||
name: ('Тестовая игра: ' + name).trim(),
|
name: ('Тестовая игра: ' + name).trim(),
|
||||||
description: 'Тестовая игра',
|
description: 'Тестовая игра',
|
||||||
|
location: '',
|
||||||
format: 'online',
|
format: 'online',
|
||||||
startAt: now,
|
startAt: now,
|
||||||
scenarioId: id,
|
scenarioId: id,
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ const game = ref<Game | undefined>({
|
|||||||
startAt: undefined,
|
startAt: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
format: undefined,
|
format: undefined,
|
||||||
|
location: undefined,
|
||||||
scenario: undefined,
|
scenario: undefined,
|
||||||
teams: undefined,
|
teams: undefined,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { NButton, NCard, NQrCode } from 'naive-ui'
|
||||||
|
import { computed, onMounted, ref } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
|
import type { TicketPage as TicketData } from '@/api/generated/crabs/evening_detective_server'
|
||||||
|
import { buildClient } from '@/api/generated/crabs/evening_detective_server/client'
|
||||||
|
import { formatGameDateTime } from '@/utils/datetime'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const client = buildClient()
|
||||||
|
const token = computed(() => String(route.params.token || ''))
|
||||||
|
const ticket = ref<TicketData>()
|
||||||
|
const loading = ref(false)
|
||||||
|
const error = ref('')
|
||||||
|
const ticketURL = computed(() => window.location.href)
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
loading.value = true
|
||||||
|
error.value = ''
|
||||||
|
try {
|
||||||
|
const result = await client.GetTicket({ token: token.value })
|
||||||
|
if (result.error || !result.ticket) {
|
||||||
|
error.value = result.error || 'Билет не найден'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ticket.value = result.ticket
|
||||||
|
} catch {
|
||||||
|
error.value = 'Не удалось загрузить билет. Попробуйте ещё раз.'
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(load)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="ticket-page center-block-custom">
|
||||||
|
<div class="width700">
|
||||||
|
<n-card class="ticket-card" title="Билет на игру">
|
||||||
|
<p v-if="error" class="error" role="alert">{{ error }}</p>
|
||||||
|
<template v-else-if="ticket">
|
||||||
|
<h2>{{ ticket.gameName }}</h2>
|
||||||
|
<dl>
|
||||||
|
<div>
|
||||||
|
<dt>Начало</dt>
|
||||||
|
<dd>{{ formatGameDateTime(ticket.startAt) }}</dd>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<dt>Место</dt>
|
||||||
|
<dd>{{ ticket.location || 'Онлайн' }}</dd>
|
||||||
|
</div>
|
||||||
|
</dl>
|
||||||
|
|
||||||
|
<div class="qr-block">
|
||||||
|
<n-qr-code :value="ticketURL" :size="240" />
|
||||||
|
<p>Покажите этот QR-код организатору на входе.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-if="ticket.status === 'used'" class="used" role="status">
|
||||||
|
Билет уже использован{{ ticket.usedAt ? ` ${formatGameDateTime(ticket.usedAt)}` : '' }}.
|
||||||
|
</p>
|
||||||
|
<p v-else-if="ticket.gameStatus === 'finish'" class="used" role="status">
|
||||||
|
Игра уже завершена.
|
||||||
|
</p>
|
||||||
|
<p v-else class="active" role="status">Билет действителен.</p>
|
||||||
|
</template>
|
||||||
|
<n-button v-else :loading="loading" @click="load">
|
||||||
|
{{ loading ? 'Загрузка' : 'Повторить' }}
|
||||||
|
</n-button>
|
||||||
|
</n-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.ticket-card {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dt {
|
||||||
|
color: #aaa;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.qr-block {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
justify-items: center;
|
||||||
|
margin: 28px 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error,
|
||||||
|
.used {
|
||||||
|
color: #aaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
color: #63e2b7;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -8,6 +8,11 @@ const router = createRouter({
|
|||||||
name: 'participation',
|
name: 'participation',
|
||||||
component: () => import('../views/ParticipationView.vue'),
|
component: () => import('../views/ParticipationView.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/tickets/:token',
|
||||||
|
name: 'ticket',
|
||||||
|
component: () => import('../views/TicketView.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/',
|
path: '/',
|
||||||
name: 'home',
|
name: 'home',
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import TicketPage from '@/components/TicketPage.vue'
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TicketPage />
|
||||||
|
</template>
|
||||||
Reference in New Issue
Block a user