Updating navigation and logged in checks.

This commit is contained in:
Brian McGonagill 2026-03-19 13:59:30 -05:00
parent 9acde36f50
commit 59e15cfde8
11 changed files with 316 additions and 43 deletions

View file

@ -15,8 +15,7 @@ const startDate = ref('');
const locationLat = ref<number | null>(null);
const locationLng = ref<number | null>(null);
const searchRadius = ref(500);
const timeLimitPerLeg = ref(30);
const timeDeductionPenalty = ref(60);
const rules = ref<string[]>(['']);
const mapContainer = ref<HTMLDivElement | null>(null);
let map: L.Map | null = null;
@ -46,6 +45,14 @@ function initMap() {
});
}
function addRule() {
rules.value.push('');
}
function removeRule(index: number) {
rules.value.splice(index, 1);
}
async function handleSubmit() {
error.value = '';
@ -59,6 +66,8 @@ async function handleSubmit() {
return;
}
const filteredRules = rules.value.filter(r => r.trim() !== '');
loading.value = true;
try {
@ -71,8 +80,7 @@ async function handleSubmit() {
locationLat: locationLat.value || undefined,
locationLng: locationLng.value || undefined,
searchRadius: searchRadius.value,
timeLimitPerLeg: timeLimitPerLeg.value,
timeDeductionPenalty: timeDeductionPenalty.value
rules: filteredRules.length > 0 ? filteredRules : undefined
});
router.push(`/games/${response.data.id}/edit`);
@ -144,14 +152,34 @@ onUnmounted(() => {
</div>
<h2>Game Rules</h2>
<p><small>Add rules for participants to follow during the game</small></p>
<label for="timeLimitPerLeg">Time Limit per Leg (minutes)</label>
<input id="timeLimitPerLeg" v-model.number="timeLimitPerLeg" type="number" min="1" />
<div v-for="(rule, index) in rules" :key="index" class="grid" style="grid-template-columns: 1fr auto;">
<label :for="`rule-${index}`">
Rule {{ index + 1 }}
<input
:id="`rule-${index}`"
v-model="rules[index]"
type="text"
placeholder="Enter a game rule..."
/>
</label>
<button
type="button"
@click="removeRule(index)"
class="contrast"
style="margin-top: 1.8rem; width: 3rem;"
v-if="rules.length > 1"
>
&times;
</button>
</div>
<label for="timeDeductionPenalty">Navigation Warning Penalty (seconds)</label>
<input id="timeDeductionPenalty" v-model.number="timeDeductionPenalty" type="number" min="0" />
<button type="button" @click="addRule" class="secondary">
+ Add Another Rule
</button>
<div class="grid">
<div class="grid" style="margin-top: 2rem;">
<button type="submit" :disabled="loading">
{{ loading ? 'Creating...' : 'Create Game' }}
</button>

View file

@ -32,6 +32,28 @@ async function loadGames() {
}
}
async function archiveGame(gameId: string) {
if (!confirm('Are you sure you want to archive this game?')) return;
try {
await gameService.archive(gameId);
await loadGames();
} catch (err) {
alert('Failed to archive game');
}
}
async function unarchiveGame(gameId: string) {
if (!confirm('Are you sure you want to unarchive this game?')) return;
try {
await gameService.unarchive(gameId);
await loadGames();
} catch (err) {
alert('Failed to unarchive game');
}
}
onMounted(() => {
loadGames();
});
@ -39,15 +61,6 @@ onMounted(() => {
<template>
<main class="container">
<nav aria-label="breadcrumb">
<ul>
<li><strong>Welcome, {{ authStore.user?.name }}</strong></li>
</ul>
<ul>
<li><button @click="authStore.logout()" class="secondary">Logout</button></li>
</ul>
</nav>
<section>
<div class="grid">
<h1>My Games</h1>
@ -79,9 +92,13 @@ onMounted(() => {
· {{ game._count?.teams || 0 }} teams
</small>
</footer>
<RouterLink :to="`/games/${game.id}`" role="button">View</RouterLink>
<RouterLink v-if="game.status === 'DRAFT'" :to="`/games/${game.id}/edit`" role="button" class="secondary">Edit</RouterLink>
<RouterLink v-if="game.status === 'LIVE'" :to="`/games/${game.id}/live`" role="button">Live Dashboard</RouterLink>
<div class="card-actions">
<RouterLink :to="`/games/${game.id}`" role="button">View</RouterLink>
<RouterLink v-if="game.status === 'DRAFT'" :to="`/games/${game.id}/edit`" role="button" class="secondary">Edit</RouterLink>
<RouterLink v-if="game.status === 'LIVE'" :to="`/games/${game.id}/live`" role="button">Live</RouterLink>
<button v-if="game.status === 'ENDED'" @click="archiveGame(game.id)" class="secondary">Archive</button>
<button v-if="game.status === 'ARCHIVED'" @click="unarchiveGame(game.id)" class="secondary">Unarchive</button>
</div>
</article>
</div>
</section>
@ -99,4 +116,11 @@ onMounted(() => {
.status.live { background: var(--pico-ins-background-color); }
.status.ended { background: var(--pico-muted-color); color: var(--pico-muted-border-color); }
.status.archived { background: var(--pico-muted-color); color: var(--pico-muted-border-color); }
.card-actions {
display: flex;
gap: 0.5rem;
flex-wrap: wrap;
margin-top: 1rem;
}
</style>

View file

@ -196,7 +196,10 @@ onUnmounted(() => {
<p><small>Total route distance: {{ getTotalDistance().toFixed(2) }} km</small></p>
<article v-if="legs.length" v-for="(leg, index) in legs" :key="leg.id">
<h3>Leg {{ index + 1 }}</h3>
<div class="leg-header">
<h3>Leg {{ index + 1 }}</h3>
<button @click="deleteLeg(leg.id)" class="secondary">Delete</button>
</div>
<p>{{ leg.description }}</p>
<footer>
<small>
@ -207,7 +210,6 @@ onUnmounted(() => {
</span>
</small>
</footer>
<button @click="deleteLeg(leg.id)" class="secondary">Delete</button>
</article>
<p v-else>No legs added yet</p>
</article>
@ -265,4 +267,15 @@ onUnmounted(() => {
border-radius: var(--pico-border-radius);
margin-bottom: 0.5rem;
}
.leg-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.leg-header h3 {
margin: 0;
}
</style>

View file

@ -145,15 +145,16 @@ onMounted(() => {
<dt>Search Radius</dt>
<dd>{{ game.searchRadius || 500 }} meters</dd>
<dt>Time Limit per Leg</dt>
<dd>{{ game.timeLimitPerLeg || 30 }} minutes</dd>
<dt>Time Deduction Penalty</dt>
<dd>{{ game.timeDeductionPenalty || 60 }} seconds</dd>
</dl>
</section>
<section v-if="game.rules?.length">
<h2>Rules</h2>
<ul>
<li v-for="(rule, index) in game.rules" :key="index">{{ rule }}</li>
</ul>
</section>
<section v-if="game.legs?.length">
<h2>Legs ({{ game.legs.length }})</h2>
<table>