diff --git a/proto/main.proto b/proto/main.proto index c925747..470c6a1 100644 --- a/proto/main.proto +++ b/proto/main.proto @@ -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 для привязки. rpc CreateScreenSession(CreateScreenSessionReq) returns (CreateScreenSessionRsp) { option (google.api.http) = { @@ -1354,6 +1377,7 @@ message AddGameReq { google.protobuf.Timestamp start_at = 3; int32 scenario_id = 4; string format = 5; + string location = 6; } message AddGameRsp { @@ -1379,6 +1403,7 @@ message Game { optional google.protobuf.Timestamp started_at = 8; optional google.protobuf.Timestamp ended_at = 9; string format = 10; + string location = 11; } enum ScreenMode { @@ -1695,6 +1720,7 @@ message UpdateGameReq { google.protobuf.Timestamp start_at = 4; int32 scenario_id = 5; string format = 6; + string location = 7; } message UpdateGameRsp { @@ -1861,3 +1887,37 @@ message UpdateScenarioBackgroundReq { message UpdateScenarioBackgroundRsp { 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; +} diff --git a/src/api/generated/crabs/evening_detective_server/index.ts b/src/api/generated/crabs/evening_detective_server/index.ts index 4ae6c9d..ccecdad 100644 --- a/src/api/generated/crabs/evening_detective_server/index.ts +++ b/src/api/generated/crabs/evening_detective_server/index.ts @@ -474,6 +474,7 @@ export type AddGameReq = { startAt: wellKnownTimestamp | undefined; scenarioId: number | undefined; format: string | undefined; + location: string | undefined; }; export type AddGameRsp = { @@ -500,6 +501,7 @@ export type Game = { startedAt?: wellKnownTimestamp; endedAt?: wellKnownTimestamp; format: string | undefined; + location: string | undefined; }; export type ScreenState = { @@ -780,6 +782,7 @@ export type UpdateGameReq = { startAt: wellKnownTimestamp | undefined; scenarioId: number | undefined; format: string | undefined; + location: string | undefined; }; export type UpdateGameRsp = { @@ -947,6 +950,40 @@ export type UpdateScenarioBackgroundRsp = { 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 { GetParticipation(request: GetParticipationReq): Promise; AnswerParticipation(request: AnswerParticipationReq): Promise; @@ -996,6 +1033,8 @@ export interface EveningDetectiveServer { AddGame(request: AddGameReq): Promise; GetGames(request: GetGamesReq): Promise; GetGame(request: GetGameReq): Promise; + IssueGameTickets(request: IssueGameTicketsReq): Promise; + GetTicket(request: GetTicketReq): Promise; // Создать сессию экрана. Экран получает токен и показывает QR для привязки. CreateScreenSession(request: CreateScreenSessionReq): Promise; // Получить состояние экрана по его токену. Вызывается самим экраном. @@ -1882,6 +1921,46 @@ export function createEveningDetectiveServerClient( method: "GetGame", }) as Promise; }, + 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; + }, + 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; + }, CreateScreenSession(request) { // eslint-disable-line @typescript-eslint/no-unused-vars const path = `api/screens`; // eslint-disable-line quotes const body = JSON.stringify(request); diff --git a/src/components/GamePage.vue b/src/components/GamePage.vue index c8dfeb7..8b45852 100644 --- a/src/components/GamePage.vue +++ b/src/components/GamePage.vue @@ -48,7 +48,7 @@ const gameId = route.params.id const message = useMessage() const showSettingsModal = ref(false) -const settingsDraft = ref({ name: '', description: '', format: 'online' }) +const settingsDraft = ref({ name: '', description: '', format: 'online', location: '' }) const showGiveApplicationModal = ref(false) const showAddTeamModal = ref(false) const showDeleteTeamModal = ref(false) @@ -67,6 +67,7 @@ const game = ref({ startAt: undefined, status: undefined, format: undefined, + location: undefined, scenario: undefined, teams: undefined, }) @@ -321,6 +322,7 @@ function openSettings() { name: game.value.name || '', description: game.value.description || '', format: game.value.format || 'online', + location: game.value.location || '', } showSettingsModal.value = true } @@ -335,6 +337,7 @@ async function updateGame() { name: settingsDraft.value.name, description: settingsDraft.value.description, format: settingsDraft.value.format, + location: settingsDraft.value.location.trim(), startAt: game.value.startAt, scenarioId: game.value.scenario?.id, }) @@ -981,6 +984,13 @@ useSimplePolling(() => { type="textarea" placeholder="Убийство пастуха..." /> + +

Место проведения

+