From 0ca8bc70d28ad8fcbbc918108d9195d45553ca71 Mon Sep 17 00:00:00 2001 From: Fedorov Vladimir Date: Fri, 23 Oct 2026 00:46:06 +0700 Subject: [PATCH] up --- proto/main.proto | 4 + .../crabs/evening_detective_server/index.ts | 4 + src/components/GamePage.vue | 83 ++++++++++++++++--- src/components/TeamStoryPage.vue | 2 + 4 files changed, 81 insertions(+), 12 deletions(-) diff --git a/proto/main.proto b/proto/main.proto index 494b667..cab872f 100644 --- a/proto/main.proto +++ b/proto/main.proto @@ -1739,6 +1739,8 @@ message Game { string format = 10; string location = 11; GameExport export = 12; + string wifi_name = 13; // Management settings only. + string wifi_password = 14; } // Download ready ZIP through the authenticated streaming HTTP route: @@ -2190,6 +2192,8 @@ message UpdateGameReq { int32 scenarioID = 5; string format = 6; string location = 7; + optional string wifi_name = 8; + optional string wifi_password = 9; } message UpdateGameRsp { diff --git a/src/api/generated/crabs/evening_detective_server/index.ts b/src/api/generated/crabs/evening_detective_server/index.ts index f004ae8..0c34190 100644 --- a/src/api/generated/crabs/evening_detective_server/index.ts +++ b/src/api/generated/crabs/evening_detective_server/index.ts @@ -568,6 +568,8 @@ export type Game = { format: string | undefined; location: string | undefined; export: GameExport | undefined; + wifiName: string | undefined; + wifiPassword: string | undefined; }; // Download ready ZIP through the authenticated streaming HTTP route: @@ -968,6 +970,8 @@ export type UpdateGameReq = { scenarioID: number | undefined; format: string | undefined; location: string | undefined; + wifiName?: string; + wifiPassword?: string; }; export type UpdateGameRsp = { diff --git a/src/components/GamePage.vue b/src/components/GamePage.vue index e61e030..2fbc987 100644 --- a/src/components/GamePage.vue +++ b/src/components/GamePage.vue @@ -100,7 +100,15 @@ function onStateTabKeydown(event: KeyboardEvent) { const message = useMessage() -const settingsDraft = ref({ name: '', description: '', format: 'online', location: '' }) +const settingsDraft = ref({ + name: '', + description: '', + format: 'online', + location: '', + wifiName: '', + wifiPassword: '', +}) +const isSavingSettings = ref(false) const settingsDraftInitialized = ref(false) const showGiveApplicationModal = ref(false) const showAddTeamModal = ref(false) @@ -139,6 +147,8 @@ const game = ref({ scenario: undefined, teams: undefined, export: undefined, + wifiName: undefined, + wifiPassword: undefined, }) const authStore = useAuthStore() const uiStore = useUIStore() @@ -191,6 +201,8 @@ watch( scenario: undefined, teams: undefined, export: undefined, + wifiName: undefined, + wifiPassword: undefined, } void getGame(Number(gameId)) } @@ -351,6 +363,8 @@ async function getGame(id: number) { description: game.value.description || '', format: game.value.format || 'online', location: game.value.location || '', + wifiName: game.value.wifiName || '', + wifiPassword: game.value.wifiPassword || '', } settingsDraftInitialized.value = true } @@ -943,16 +957,38 @@ function toResults() { } async function updateGame() { - await client.UpdateGame({ - ID: game.value.ID, - 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, - }) - await getGame(game.value.ID!) + if (isSavingSettings.value) return + if ( + new TextEncoder().encode(settingsDraft.value.wifiName).length > 32 || + /[\p{Cc}\p{Cf}\p{Zl}\p{Zp}]/u.test(settingsDraft.value.wifiName) + ) { + message.error('Название Wi-Fi — до 32 байт UTF-8, без управляющих символов') + return + } + if (!/^[\x20-\x7e]{0,64}$/.test(settingsDraft.value.wifiPassword)) { + message.error('Пароль Wi-Fi — до 64 печатных ASCII-символов') + return + } + isSavingSettings.value = true + try { + await client.UpdateGame({ + ID: game.value.ID, + name: settingsDraft.value.name, + description: settingsDraft.value.description, + format: settingsDraft.value.format, + location: settingsDraft.value.location.trim(), + wifiName: settingsDraft.value.wifiName, + wifiPassword: settingsDraft.value.wifiPassword, + startAt: game.value.startAt, + scenarioID: game.value.scenario?.ID, + }) + await getGame(game.value.ID!) + message.success('Настройки игры сохранены') + } catch (err) { + message.error(err instanceof Error ? err.message : 'Не удалось сохранить настройки игры') + } finally { + isSavingSettings.value = false + } } const giveApplicationTeam = ref({ @@ -1394,9 +1430,32 @@ useSimplePolling(() => { type="text" placeholder="Оставьте пустым для онлайн-игры" /> +

Wi-Fi для игроков

+ + +

+ Название сети и пароль будут напечатаны рядом с QR-кодом команды в PDF и чеках. + Оставьте название сети пустым, чтобы убрать инструкцию. +

- Сохранить изменения + Сохранить изменения diff --git a/src/components/TeamStoryPage.vue b/src/components/TeamStoryPage.vue index 4844ac2..ddf5273 100644 --- a/src/components/TeamStoryPage.vue +++ b/src/components/TeamStoryPage.vue @@ -57,6 +57,8 @@ const game = ref({ scenario: undefined, teams: undefined, export: undefined, + wifiName: undefined, + wifiPassword: undefined, }) const code = ref('')