diff --git a/app/game-api/src/battleSim/schema.ts b/app/game-api/src/battleSim/schema.ts index 09fb8d9b..71b092dc 100644 --- a/app/game-api/src/battleSim/schema.ts +++ b/app/game-api/src/battleSim/schema.ts @@ -1,5 +1,7 @@ import { z } from 'zod'; +import { isAvailableNationTraitKey } from '@sammo-ts/logic'; + import type { BattleSimRequestPayload } from './types.js'; const zBattleSimGeneral = z.object({ @@ -71,7 +73,7 @@ const zBattleSimCity = z.object({ }); const zBattleSimNation = z.object({ - type: z.string().min(1), + type: z.string().refine(isAvailableNationTraitKey), tech: z.number().min(0), level: z.number().int().min(0), capital: z.number().int().min(0), diff --git a/app/game-api/src/battleSim/simulatorOptions.ts b/app/game-api/src/battleSim/simulatorOptions.ts index 51b80ddf..0d084147 100644 --- a/app/game-api/src/battleSim/simulatorOptions.ts +++ b/app/game-api/src/battleSim/simulatorOptions.ts @@ -1,4 +1,5 @@ import { + AVAILABLE_NATION_TRAIT_KEYS, ITEM_KEYS, EVENT_DOMESTIC_TRAIT_KEYS, loadEventDomesticTraitModules, @@ -6,7 +7,6 @@ import { loadNationTraitModules, loadPersonalityTraitModules, loadWarTraitModules, - NATION_TRAIT_KEYS, PERSONALITY_TRAIT_KEYS, WAR_TRAIT_KEYS, type ItemModule, @@ -110,7 +110,7 @@ export const loadBattleSimTraitOptions = async (): Promise<{ }> => { if (!cachedTraitOptions) { cachedTraitOptions = Promise.all([ - loadNationTraitModules([...NATION_TRAIT_KEYS]), + loadNationTraitModules([...AVAILABLE_NATION_TRAIT_KEYS]), loadEventDomesticTraitModules([...EVENT_DOMESTIC_TRAIT_KEYS]), loadWarTraitModules([...WAR_TRAIT_KEYS]), loadPersonalityTraitModules([...PERSONALITY_TRAIT_KEYS]), diff --git a/app/game-api/test/battleSimRouter.test.ts b/app/game-api/test/battleSimRouter.test.ts index 3e765fe0..7d60b37c 100644 --- a/app/game-api/test/battleSimRouter.test.ts +++ b/app/game-api/test/battleSimRouter.test.ts @@ -117,7 +117,7 @@ const buildBattleRequest = () => ({ conflict: '{}', }, attackerNation: { - type: 'test', + type: 'che_도적', tech: 1000, level: 1, capital: 1, @@ -193,7 +193,7 @@ const buildBattleRequest = () => ({ conflict: '{}', }, defenderNation: { - type: 'test', + type: 'che_도적', tech: 1000, level: 1, capital: 2, @@ -309,6 +309,27 @@ describe('battle router orchestration', () => { expect(battleSim.simulateCalls).toBe(0); }); + it('rejects the neutral storage nation type before preparing or queuing a simulation', async () => { + const battleSim = new QueuedBattleSimTransport(); + const state: WorldStateRow = { + id: 1, + scenarioCode: 'default', + currentYear: 200, + currentMonth: 1, + tickSeconds: 600, + config: {}, + meta: {}, + updatedAt: new Date('2026-01-01T00:00:00Z'), + }; + const caller = appRouter.createCaller(buildContext({ state, battleSim })); + const request = buildBattleRequest(); + request.attackerNation.type = 'che_중립'; + + await expect(caller.battle.prepareSimulation(request)).rejects.toMatchObject({ code: 'BAD_REQUEST' }); + await expect(caller.battle.simulate(request)).rejects.toMatchObject({ code: 'BAD_REQUEST' }); + expect(battleSim.simulateCalls).toBe(0); + }); + it('returns queued then completed results via transport', async () => { const battleSim = new QueuedBattleSimTransport(); const state: WorldStateRow = { diff --git a/app/game-api/test/simulatorOptions.test.ts b/app/game-api/test/simulatorOptions.test.ts new file mode 100644 index 00000000..f2a256fd --- /dev/null +++ b/app/game-api/test/simulatorOptions.test.ts @@ -0,0 +1,16 @@ +import { describe, expect, it } from 'vitest'; + +import { AVAILABLE_NATION_TRAIT_KEYS } from '@sammo-ts/logic'; + +import { loadBattleSimTraitOptions } from '../src/battleSim/simulatorOptions.js'; + +describe('selectable trait options', () => { + it('uses the Ref available nation-type list for founding and battle simulation inputs', async () => { + const options = await loadBattleSimTraitOptions(); + + expect(options.nationTypes.map((entry) => entry.key)).toEqual(AVAILABLE_NATION_TRAIT_KEYS); + expect(options.nationTypes).not.toEqual( + expect.arrayContaining([expect.objectContaining({ key: 'che_중립' })]) + ); + }); +}); diff --git a/app/game-engine/src/turn/ai/generalAi/core.ts b/app/game-engine/src/turn/ai/generalAi/core.ts index 17740a19..3b130819 100644 --- a/app/game-engine/src/turn/ai/generalAi/core.ts +++ b/app/game-engine/src/turn/ai/generalAi/core.ts @@ -13,7 +13,10 @@ import type { ConstraintContext } from '@sammo-ts/logic'; import { GAME_TICKS_PER_TURN, LiteHashDRBG, RandUtil } from '@sammo-ts/common'; import { simpleSerialize } from '@sammo-ts/logic/war/utils.js'; import { resolveStartYear, resolveTurnTermMinutes } from '@sammo-ts/logic/actions/turn/actionContextHelpers.js'; -import { NATION_TRAIT_KEYS } from '@sammo-ts/logic/actionModules/traits/nation/index.js'; +import { + AVAILABLE_NATION_TRAIT_KEYS, + isAvailableNationTraitKey, +} from '@sammo-ts/logic/actionModules/traits/nation/index.js'; import { GeneralActionPipeline } from '@sammo-ts/logic/actionModules/general.js'; import type { ReservedTurnEntry } from '../../reservedTurnStore.js'; @@ -348,8 +351,10 @@ export class GeneralAI { chiefStatMin: this.scenarioConfig.stat.chiefMin, npcMessageFreqByDay: readNumber(constValues.npcMessageFreqByDay, 0), availableNationTypes: Array.isArray(constValues.availableNationType) - ? constValues.availableNationType.filter((value) => typeof value === 'string') - : NATION_TRAIT_KEYS.filter((value) => value !== 'che_중립'), + ? constValues.availableNationType.filter( + (value): value is string => typeof value === 'string' && isAvailableNationTraitKey(value) + ) + : [...AVAILABLE_NATION_TRAIT_KEYS], }; const generalPolicy = new AutorunGeneralPolicy( diff --git a/app/game-engine/src/turn/monthlyRaiseNpcNationAction.ts b/app/game-engine/src/turn/monthlyRaiseNpcNationAction.ts index 5e24209e..2f5beb1e 100644 --- a/app/game-engine/src/turn/monthlyRaiseNpcNationAction.ts +++ b/app/game-engine/src/turn/monthlyRaiseNpcNationAction.ts @@ -3,7 +3,7 @@ import { LogCategory, LogFormat, LogScope, - NATION_TRAIT_KEYS, + AVAILABLE_NATION_TRAIT_KEYS, getCityDistance, type City, type MapDefinition, @@ -54,7 +54,6 @@ const NATION_COLORS = [ '#FFFFFF', '#A9A9A9', ] as const; -const AVAILABLE_NATION_TYPES = NATION_TRAIT_KEYS.filter((key) => key !== 'che_중립'); const NPC_TYPE = 6; const NPC_PREFIX = 'ⓤ'; const STAT_TYPE_WEIGHTS = { 무: 1, 지: 1 } as const; @@ -301,7 +300,7 @@ export const createRaiseNpcNationHandler = (options: { const nationId = world.getNextNationId(); const color = rng.choice([...NATION_COLORS]); - const typeCode = rng.choice([...AVAILABLE_NATION_TYPES]); + const typeCode = rng.choice([...AVAILABLE_NATION_TRAIT_KEYS]); const nation: Nation = { id: nationId, name: `${NPC_PREFIX}${city.name}`, diff --git a/app/game-frontend/e2e/battleSimulator.spec.ts b/app/game-frontend/e2e/battleSimulator.spec.ts index 60b9d3ec..64207b26 100644 --- a/app/game-frontend/e2e/battleSimulator.spec.ts +++ b/app/game-frontend/e2e/battleSimulator.spec.ts @@ -58,7 +58,7 @@ const simulatorOptions = { { id: 200, name: '궁병', armType: 2 }, ], }, - nationTypes: [{ key: 'che_중립', name: '중립', info: '특별한 효과 없음' }], + nationTypes: [{ key: 'che_도적', name: '도적', info: '금 수입 증가, 쌀 수입 감소' }], eventDomesticTraits: [{ key: 'che_event_신산', name: '신산', info: '계략 강화' }], warTraits: [{ key: 'che_필살', name: '필살', info: '필살 확률 증가' }], personalities: [{ key: 'che_대담', name: '대담', info: '공격적인 성격' }], @@ -356,6 +356,10 @@ test('operates independent/game presets, imports my general, and renders battle await page.setViewportSize({ width: 1280, height: 900 }); await gotoSimulator(page); + const nationTypeSelects = page.locator('[data-parity-id="attacker-nation"] select').first(); + await expect(nationTypeSelects).toHaveValue('che_도적'); + await expect(nationTypeSelects.locator('option[value="che_중립"]')).toHaveCount(0); + const notice = page.getByLabel('시뮬레이터 데이터 안내'); const noticeRect = await notice.boundingBox(); expect(noticeRect?.width).toBeLessThan(100); diff --git a/app/game-frontend/e2e/commandArguments.spec.ts b/app/game-frontend/e2e/commandArguments.spec.ts index 656dfc91..7fef742f 100644 --- a/app/game-frontend/e2e/commandArguments.spec.ts +++ b/app/game-frontend/e2e/commandArguments.spec.ts @@ -66,7 +66,7 @@ const inputOptions = { }, crewTypes: [{ value: 1100, label: '보병' }], armTypes: [{ value: 1, label: '보병' }], - nationTypes: [{ value: 'che_중립', label: '중립' }], + nationTypes: [{ value: 'che_도적', label: '도적', description: '금 수입 증가, 쌀 수입 감소' }], colors: [{ value: 0, label: '색상 1', color: '#ff0000' }], items: { horse: [{ value: 'None', label: '판매/해제' }] }, recruitment: { @@ -579,6 +579,61 @@ test('renders and accepts every Ref strategy command at mobile width', async ({ await picker.screenshot({ path: test.info().outputPath('all-strategy-commands-mobile.png') }); }); +test('defaults founding to a Ref-selectable nation trait without exposing the neutral storage trait', async ({ + page, +}) => { + const foundingCommandTable = { + general: [ + { + category: '국가', + values: [ + { + key: 'che_건국', + name: '건국', + reqArg: true, + possible: true, + status: 'needsInput', + inputFields: [ + { key: 'nationName', label: '국가명', kind: 'text', required: true, min: 1, max: 18 }, + { + key: 'nationType', + label: '국가 성향', + kind: 'select', + required: true, + optionSource: 'nationTypes', + }, + { + key: 'colorType', + label: '국기 색상', + kind: 'select', + required: true, + optionSource: 'colors', + }, + ], + }, + ], + }, + ], + nation: [], + inputOptions, + }; + await install(page, false, foundingCommandTable); + await page.setViewportSize({ width: 1200, height: 900 }); + await page.goto('/'); + await page.getByRole('button', { name: '1턴 명령 입력', exact: true }).click(); + + const picker = page.getByTestId('command-picker'); + await picker.getByRole('button', { name: '국가', exact: true }).click(); + await picker.getByRole('button', { name: '건국', exact: true }).click(); + const nationType = picker.getByLabel('국가 성향'); + await expect(nationType).toHaveValue('che_도적'); + await expect(nationType.locator('option[value="che_중립"]')).toHaveCount(0); + await expect(nationType.locator('option')).toHaveText(['도적']); + await nationType.focus(); + await expect(nationType).toBeFocused(); + await picker.screenshot({ path: test.info().outputPath('founding-selectable-nation-trait-desktop-1200.png') }); +}); + test('reserves force move, retirement, and resignation from the user command picker', async ({ page }) => { const specialCommandTable = { general: [ diff --git a/app/game-frontend/e2e/mainNavigation.spec.ts b/app/game-frontend/e2e/mainNavigation.spec.ts index 1675c1d5..bf51dec7 100644 --- a/app/game-frontend/e2e/mainNavigation.spec.ts +++ b/app/game-frontend/e2e/mainNavigation.spec.ts @@ -810,6 +810,7 @@ test('desktop menus preserve ref columns, prefix-safe routes, and controlled dro await installFixture(page, state); await page.setViewportSize({ width: 1200, height: 900 }); await waitForMain(page); + if (artifactRoot) await mkdir(resolve(artifactRoot), { recursive: true }); await expect(page.locator('.main-global-menu')).toHaveCount(3); expect(await gridColumnCount(page, '.main-global-menu')).toBe(8); @@ -2238,6 +2239,222 @@ test('nation menu presentation follows the server-derived permission matrix', as ); }); +test('all main Lumen button families share the rounded pressed geometry', async ({ page }) => { + const state: NavigationFixture = { + officerLevel: 5, + permission: 2, + nationLevel: 3, + stage: 0, + npcMode: 1, + generalMeCalls: 0, + operations: [], + nationColor: '#663399', + }; + await installFixture(page, state); + await page.setViewportSize({ width: 1200, height: 900 }); + await waitForMain(page); + + const controls: Array<[string, Locator]> = [ + [ + '천통국 베팅', + page.locator('.main-global-menu[data-menu-position="top"] [data-navigation-id="nation-betting"]'), + ], + [ + '게임정보', + page.locator('.main-global-menu[data-menu-position="top"]').getByRole('button', { + name: '게임정보', + exact: true, + }), + ], + ['회 의 실', page.locator('.layout-desktop [data-navigation-id="meeting"]')], + ['기 밀 실', page.locator('.layout-desktop [data-navigation-id="secret-board"]')], + [ + '당기기', + page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '당기기' }), + ], + [ + '미루기', + page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '미루기' }), + ], + [ + '펼치기', + page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '펼치기' }), + ], + ['실시간 동기화', page.locator('.desktop-action-controls').getByRole('button', { name: /실시간 동기화:/u })], + ['갱 신', page.locator('.desktop-action-controls').getByRole('button', { name: '갱 신' })], + ['로비로', page.locator('.desktop-action-controls').getByRole('button', { name: '로비로' })], + ]; + + const measure = (control: Locator) => + control.evaluate((element) => { + const rect = element.getBoundingClientRect(); + const style = getComputedStyle(element); + return { + top: rect.top, + bottom: rect.bottom, + height: rect.height, + marginTop: style.marginTop, + borderTop: style.borderTopWidth, + borderRight: style.borderRightWidth, + borderBottom: style.borderBottomWidth, + borderLeft: style.borderLeftWidth, + radius: style.borderRadius, + background: style.backgroundColor, + filter: style.filter, + }; + }); + + const evidence: Record> = {}; + for (const [index, [label, control]] of controls.entries()) { + await expect(control, `${label} control`).toBeVisible(); + await expect(control).toHaveClass(/legacy-button/u); + await control.scrollIntoViewIfNeeded(); + await page.mouse.move(1195, 895); + const base = await measure(control); + evidence[label] = { default: base }; + if (artifactRoot) { + await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-default.png`) }); + } + expect(base, `${label} default geometry`).toMatchObject({ + marginTop: '0px', + borderTop: '0px', + borderRight: '1px', + borderBottom: '4px', + borderLeft: '1px', + radius: '5.25px', + filter: 'none', + }); + + await control.focus(); + await expect(control, `${label} keyboard focus`).toBeFocused(); + const focused = await measure(control); + evidence[label].focus = focused; + if (artifactRoot) { + await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-focus.png`) }); + } + expect(focused.borderBottom, `${label} focus edge`).toBe('4px'); + expect(focused.marginTop, `${label} focus position`).toBe('0px'); + + await control.hover(); + const hovered = await measure(control); + evidence[label].hover = hovered; + if (artifactRoot) { + await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-hover.png`) }); + } + expect(hovered.borderBottom, `${label} hover edge`).toBe('3px'); + expect(hovered.marginTop, `${label} hover position`).toBe('1px'); + expect(hovered.top, `${label} hover top`).toBeCloseTo(base.top + 1, 2); + expect(hovered.height, `${label} hover height`).toBeCloseTo(base.height - 1, 2); + expect(hovered.bottom, `${label} hover bottom`).toBeCloseTo(base.bottom, 2); + expect(hovered.background, `${label} hover face`).toBe(base.background); + + const box = await control.boundingBox(); + if (!box) throw new Error(`${label} control has no bounding box`); + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await page.mouse.down(); + const pressed = await measure(control); + evidence[label].pointerDown = pressed; + if (artifactRoot) { + await control.screenshot({ path: resolve(artifactRoot, `${index + 1}-pointer-down.png`) }); + } + expect(pressed.borderBottom, `${label} pressed edge`).toBe('2px'); + expect(pressed.marginTop, `${label} pressed position`).toBe('2px'); + expect(pressed.top, `${label} pressed top`).toBeCloseTo(base.top + 2, 2); + expect(pressed.height, `${label} pressed height`).toBeCloseTo(base.height - 2, 2); + expect(pressed.bottom, `${label} pressed bottom`).toBeCloseTo(base.bottom, 2); + expect(pressed.background, `${label} pressed face`).toBe(base.background); + await page.mouse.move(1195, 895); + await page.mouse.up(); + } + + state.permission = 0; + await page.locator('.desktop-action-controls').getByRole('button', { name: '갱 신' }).click(); + const disabledSecret = page.locator('.layout-desktop [data-navigation-id="secret-board"]'); + await expect(disabledSecret).toHaveAttribute('aria-disabled', 'true'); + await disabledSecret.scrollIntoViewIfNeeded(); + const disabledBase = await measure(disabledSecret); + await disabledSecret.hover({ force: true }); + const disabledHover = await measure(disabledSecret); + evidence['기 밀 실 disabled'] = { default: disabledBase, hover: disabledHover }; + expect(disabledHover.borderBottom).toBe('4px'); + expect(disabledHover.marginTop).toBe('0px'); + expect(disabledHover.top).toBeCloseTo(disabledBase.top, 2); + if (artifactRoot) { + await disabledSecret.screenshot({ path: resolve(artifactRoot, 'disabled-secret-hover.png') }); + await writeFile( + resolve(artifactRoot, 'main-lumen-button-states.json'), + `${JSON.stringify(evidence, null, 2)}\n` + ); + } + + await persistArtifact(page, `${basePath.slice(1)}-main-lumen-button-families`); +}); + +test('mobile main Lumen button families keep the same state geometry without overflow', async ({ page }) => { + const state: NavigationFixture = { + officerLevel: 5, + permission: 2, + nationLevel: 3, + stage: 0, + npcMode: 1, + generalMeCalls: 0, + operations: [], + nationColor: '#663399', + }; + await installFixture(page, state); + await page.setViewportSize({ width: 500, height: 900 }); + await waitForMain(page); + + const controls = [ + page.locator('.main-global-menu[data-menu-position="top"] [data-navigation-id="nation-betting"]'), + page.locator('.main-global-menu[data-menu-position="top"]').getByRole('button', { + name: '게임정보', + exact: true, + }), + page.locator('.layout-mobile [data-navigation-id="meeting"]'), + page.locator('.layout-mobile [data-navigation-id="secret-board"]'), + page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '당기기' }), + page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '미루기' }), + page.locator('[data-main-target="commands"] .bottom-actions').getByRole('button', { name: '펼치기' }), + page.locator('.desktop-action-controls').getByRole('button', { name: /실시간 동기화:/u }), + page.locator('.desktop-action-controls').getByRole('button', { name: '갱 신' }), + page.locator('.desktop-action-controls').getByRole('button', { name: '로비로' }), + ]; + for (const control of controls) { + await expect(control).toBeVisible(); + await expect(control).toHaveClass(/legacy-button/u); + await expect(control).toHaveCSS('border-radius', '5.25px'); + await expect(control).toHaveCSS('border-bottom-width', '4px'); + } + + for (const control of [controls[0], controls[2], controls[4], controls[7]]) { + if (!control) throw new Error('mobile Lumen control is missing'); + await control.scrollIntoViewIfNeeded(); + await control.focus(); + await expect(control).toBeFocused(); + await expect(control).toHaveCSS('border-bottom-width', '4px'); + await control.hover(); + await expect(control).toHaveCSS('border-bottom-width', '3px'); + await expect(control).toHaveCSS('margin-top', '1px'); + const box = await control.boundingBox(); + if (!box) throw new Error('mobile Lumen control is not measurable'); + await page.mouse.move(box.x + box.width / 2, box.y + box.height / 2); + await page.mouse.down(); + await expect(control).toHaveCSS('border-bottom-width', '2px'); + await expect(control).toHaveCSS('margin-top', '2px'); + await page.mouse.move(499, 899); + await page.mouse.up(); + } + + expect( + await page.evaluate(() => ({ + document: document.documentElement.scrollWidth - document.documentElement.clientWidth, + body: document.body.scrollWidth - document.body.clientWidth, + })) + ).toEqual({ document: 0, body: 0 }); + await persistArtifact(page, `${basePath.slice(1)}-mobile-main-lumen-button-families`); +}); + test('mobile single document refreshes once and preserves tokens on lobby return', async ({ page }) => { const state: NavigationFixture = { officerLevel: 5, diff --git a/app/game-frontend/src/assets/styles/game-shell.css b/app/game-frontend/src/assets/styles/game-shell.css index 392ed991..9929c557 100644 --- a/app/game-frontend/src/assets/styles/game-shell.css +++ b/app/game-frontend/src/assets/styles/game-shell.css @@ -48,26 +48,6 @@ cursor: pointer; } -/* - * Ref renders the dashboard reload control with the Lumen navigation family: - * the bottom edge shortens on hover and again while pressed. - */ -.game-shell__action--navigation { - border-color: var(--sammo-button-navigation-border); - border-width: 0 1px 4px; - background: var(--sammo-button-navigation-bg); -} - -.game-shell__action--navigation:not(:disabled):hover { - margin-top: 1px; - border-bottom-width: 3px; -} - -.game-shell__action--navigation:not(:disabled):active { - margin-top: 2px; - border-bottom-width: 2px; -} - .game-feedback--error { color: var(--sammo-color-error); font-size: 0.85rem; diff --git a/app/game-frontend/src/assets/styles/legacy-controls.css b/app/game-frontend/src/assets/styles/legacy-controls.css index 47a9d79a..be50525f 100644 --- a/app/game-frontend/src/assets/styles/legacy-controls.css +++ b/app/game-frontend/src/assets/styles/legacy-controls.css @@ -56,7 +56,6 @@ --legacy-button-bg: var(--sammo-button-primary-bg); --legacy-button-border: var(--sammo-button-primary-border); --legacy-button-color: #fff; - min-height: 35.5px; margin-top: 0; border-color: var(--legacy-button-border); border-style: solid; @@ -65,6 +64,7 @@ padding: 5.25px 10.5px; background: var(--legacy-button-bg); color: var(--legacy-button-color); + filter: none; line-height: 21px; /* Ref's framework baseline for these controls. */ vertical-align: middle; diff --git a/app/game-frontend/src/components/command/ReservedCommandEditor.vue b/app/game-frontend/src/components/command/ReservedCommandEditor.vue index 6b3c2fec..4824b2aa 100644 --- a/app/game-frontend/src/components/command/ReservedCommandEditor.vue +++ b/app/game-frontend/src/components/command/ReservedCommandEditor.vue @@ -635,9 +635,15 @@ const clickOutsideMenu = (event: Event) => {
- - - + + +
@@ -762,7 +768,6 @@ const clickOutsideMenu = (event: Event) => { .control-pad > button, .clock, .legacy-menu > summary, -.bottom-actions button, .select-command { box-sizing: border-box; min-height: 34px; diff --git a/app/game-frontend/src/components/main/MainGlobalMenu.vue b/app/game-frontend/src/components/main/MainGlobalMenu.vue index 0ff82433..419fe7e5 100644 --- a/app/game-frontend/src/components/main/MainGlobalMenu.vue +++ b/app/game-frontend/src/components/main/MainGlobalMenu.vue @@ -26,10 +26,11 @@ const isActive = (link: MainNavigationLinkItem) => link.id === 'survey' && props :link="entry" :enabled="isNavigationConfigured(entry)" :active="isActive(entry)" + lumen-variant="navigation" />