feat: add reserved command argument forms

This commit is contained in:
2026-07-26 09:05:17 +00:00
parent f84efafc9a
commit 919a2fccda
9 changed files with 719 additions and 43 deletions
@@ -5,6 +5,7 @@ import { addMinutes, format } from 'date-fns';
import PanelCard from '../components/ui/PanelCard.vue';
import SkeletonLines from '../components/ui/SkeletonLines.vue';
import ChiefTurnCard from '../components/chief/ChiefTurnCard.vue';
import CommandArgumentForm from '../components/main/CommandArgumentForm.vue';
import CommandSelectForm from '../components/main/CommandSelectForm.vue';
import { trpc } from '../utils/trpc';
import { formatOfficerLevelText } from '../utils/nationFormat';
@@ -48,6 +49,19 @@ type CommandAvailability = {
status: 'available' | 'blocked' | 'needsInput' | 'unknown';
possible: boolean;
reason?: string;
inputFields: Array<{
key: string;
label: string;
kind: 'text' | 'number' | 'boolean' | 'select' | 'numberTuple' | 'hidden';
required: boolean;
min?: number;
max?: number;
step?: number;
constValue?: string | number;
options?: Array<{ value: string | number; label: string; color?: string }>;
optionSource?: 'cities' | 'nations' | 'generals' | 'crewTypes' | 'armTypes' | 'nationTypes' | 'colors' | 'items';
tupleLabels?: string[];
}>;
};
type CommandGroup = {
@@ -58,6 +72,16 @@ type CommandGroup = {
type CommandTable = {
general: CommandGroup[];
nation: CommandGroup[];
inputOptions: {
cities: Array<{ value: string | number; label: string; color?: string }>;
nations: Array<{ value: string | number; label: string; color?: string }>;
generals: Array<{ value: string | number; label: string; color?: string }>;
crewTypes: Array<{ value: string | number; label: string; color?: string }>;
armTypes: Array<{ value: string | number; label: string; color?: string }>;
nationTypes: Array<{ value: string | number; label: string; color?: string }>;
colors: Array<{ value: string | number; label: string; color?: string }>;
items: Record<string, Array<{ value: string | number; label: string; color?: string }>>;
};
};
const chiefApi = trpc as unknown as {
@@ -89,6 +113,8 @@ const commandTable = ref<CommandTable | null>(null);
const selectedChiefLevel = ref<number | null>(null);
const activeCategory = ref('');
const selectedCommandKey = ref<string | null>(null);
const commandArgs = ref<Record<string, unknown>>({});
const commandArgsValid = ref(false);
const isMobile = useMediaQuery('(max-width: 1024px)');
@@ -210,6 +236,11 @@ const selectedCommand = computed<CommandAvailability | null>(() => {
return null;
});
watch(selectedCommand, (command) => {
commandArgs.value = {};
commandArgsValid.value = Boolean(command && !command.reqArg);
});
const canReserveSelected = computed(() => {
if (!selectedCommand.value) {
return false;
@@ -217,10 +248,10 @@ const canReserveSelected = computed(() => {
if (!selectedCommand.value.possible) {
return false;
}
if (selectedCommand.value.status !== 'available') {
if (!['available', 'needsInput'].includes(selectedCommand.value.status)) {
return false;
}
return !selectedCommand.value.reqArg;
return commandArgsValid.value;
});
const selectedChief = computed<ChiefEntry | null>(() => {
@@ -308,7 +339,7 @@ const reserveTurn = async (turnIndex: number) => {
generalId: data.value.me.id,
turnIndex,
action: selectedCommand.value.key,
args: {},
args: commandArgs.value,
});
updateMyTurns(result.turns);
} catch (err) {
@@ -402,6 +433,14 @@ const shiftTurns = async (amount: number) => {
</div>
<div v-else class="value muted">명령을 선택하세요.</div>
</div>
<CommandArgumentForm
v-if="selectedCommand?.reqArg && commandTable"
:command-key="selectedCommand.key"
:fields="selectedCommand.inputFields"
:options="commandTable.inputOptions"
@update:args="commandArgs = $event"
@update:valid="commandArgsValid = $event"
/>
<div class="turn-actions">
<button @click="shiftTurns(-1)">앞당김</button>
<button @click="shiftTurns(1)">미루기</button>
@@ -479,6 +518,14 @@ const shiftTurns = async (amount: number) => {
</div>
<div v-else class="value muted">명령을 선택하세요.</div>
</div>
<CommandArgumentForm
v-if="selectedCommand?.reqArg && commandTable"
:command-key="selectedCommand.key"
:fields="selectedCommand.inputFields"
:options="commandTable.inputOptions"
@update:args="commandArgs = $event"
@update:valid="commandArgsValid = $event"
/>
<div class="turn-actions">
<button @click="shiftTurns(-1)">앞당김</button>
<button @click="shiftTurns(1)">미루기</button>
+4 -4
View File
@@ -55,16 +55,16 @@ const {
realtimeLabel,
} = storeToRefs(dashboard);
const reserveGeneralTurn = (payload: { index: number; action: string }) => {
void dashboard.setGeneralTurn(payload.index, payload.action);
const reserveGeneralTurn = (payload: { index: number; action: string; args: Record<string, unknown> }) => {
void dashboard.setGeneralTurn(payload.index, payload.action, payload.args);
};
const shiftGeneralTurns = (amount: number) => {
void dashboard.shiftGeneralTurns(amount);
};
const reserveNationTurn = (payload: { index: number; action: string }) => {
void dashboard.setNationTurn(payload.index, payload.action);
const reserveNationTurn = (payload: { index: number; action: string; args: Record<string, unknown> }) => {
void dashboard.setNationTurn(payload.index, payload.action, payload.args);
};
const shiftNationTurns = (amount: number) => {