Compare commits

..
6 Commits
Author SHA1 Message Date
Esa Kataja 628f140d89 style: standardize HSL color syntax to comma-separated format 2025-11-21 15:47:47 +02:00
Esa Kataja 9ffb87475b refactor: use composable for PocketBase instance in gameroom 2025-11-21 15:47:33 +02:00
Esa Kataja bae4153201 feat: configure PocketBase URL via environment variable
- Replaced hardcoded localhost URL with process.env.POCKETBASE_URL for deployment flexibility
- Added @types/node dependency and TypeScript configuration to support process.env access
2025-11-21 15:46:46 +02:00
Esa Kataja 68c7115d8e feat: add interactive button state and PocketBase integration
- Implemented button press tracking with visual feedback and event emission
- Integrated PocketBase for real-time session and movie data management
- Added session ID routing across pages for game state persistence
2025-11-21 15:27:37 +02:00
Esa Kataja e77c606aea feat: add session validation composable
- Created checkSession composable to validate sessionid query parameter
- Automatically redirects to home page when sessionid is missing
2025-11-21 15:27:17 +02:00
Esa Kataja dcb363cf8f add default formatter 2025-11-21 15:26:27 +02:00
10 changed files with 117 additions and 97 deletions
+4
View File
@@ -0,0 +1,4 @@
{
"editor.defaultFormatter": "Vue.volar",
"editor.formatOnSave": true
}
+21 -7
View File
@@ -1,16 +1,23 @@
<script setup lang="ts">
const emit = defineEmits(['button-pressed'])
const props = defineProps<{
icon: string,
title: string,
description: string
description: string,
id: string
isPressed: boolean | undefined
}>()
const icon = computed(() => `/img/icons/${props.icon}`)
function onButtonPress(id: string) {
emit('button-pressed', id)
}
</script>
<template>
<div class="playfield-button-container">
<div>
<div class="playfield-button">
<div @click="onButtonPress(props.id)" class="playfield-button " :class="{ 'pressed': isPressed }">
<NuxtImg :src="icon" alt="" format="webp" quality="80" />
<!-- <p>{{ description }}</p> -->
</div>
@@ -20,7 +27,6 @@
</template>
<style scoped lang="scss">
$button-size: 80px;
.playfield-button-container {
@@ -33,17 +39,18 @@ $button-size: 80px;
align-self: start;
justify-items: center;
}
.playfield-button {
height: $button-size;
width: $button-size;
padding: 0.4rem;
justify-content: center;
align-items: center;
border: 1px solid hsl(0 74% 10% / 50%);
border-bottom-color: hsl(0 74% 80% / 10%);
border: 1px solid hsl(0, 74%, 10%, 0.5);
border-bottom-color: hsl(0, 74%, 80%, 0.1);
border-radius: 0.5rem;
background-image: linear-gradient(to bottom, hsl(0 74% 80%), hsl(0 74% 70%));
box-shadow: inset 3px 3px 0.5rem hsl(0 74% 20%);
background-image: linear-gradient(to bottom, hsl(0, 74%, 80%), hsl(0, 74%, 70%));
box-shadow: inset 3px 3px 0.5rem hsl(0, 74%, 20%);
font-size: 0.2rem;
overflow: hidden;
@@ -62,6 +69,12 @@ $button-size: 80px;
margin: auto;
}
&.pressed {
background-image: linear-gradient(to bottom, hsl(0, 74%, 80%), hsl(0, 74%, 70%));
box-shadow: inset 3px 3px 0.5rem hsl(0, 74%, 20%);
border: 9px solid hsl(0, 74%, 10%, 0.5);
}
p {
grid-area: description;
@@ -71,6 +84,7 @@ $button-size: 80px;
}
}
h3 {
font-weight: bold;
margin-top: 0.2rem;
+7
View File
@@ -0,0 +1,7 @@
export function checkSession() {
const sessionid = useRoute().query.sessionid
if (!sessionid) {
navigateTo('/')
}
return sessionid
}
+49 -34
View File
@@ -1,56 +1,68 @@
<script setup lang="ts">
import card_data from "@/assets/card_data.json"
const { pb } = usePocketbase()
type playfieldButton = {
id: number,
title: string,
icon: string,
color: string,
description: string,
isPressed: boolean | null
}
const movie_data = ref({
"Title": "A Maple Valley Christmas",
"Year": "2022",
"Rated": "TV-G",
"Released": "05 Nov 2022",
"Runtime": "85 min",
"Genre": "Comedy, Drama, Romance",
"Director": "Paul Ziller",
"Writer": "Joie Botkin, Jerry Todt",
"Actors": "Peyton List, Andrew W. Walker, Frances Flanagan",
"Plot": "Erica is a rancher who has spent her whole life working the family farm with her mother and sister. When Aaron arrives and disrupts her plans, she starts to question what it is she actually wants.",
"Language": "English, Italian",
"Country": "Canada, United States",
"Awards": "1 nomination total",
"Poster": "https://m.media-amazon.com/images/M/MV5BZjliMTRlMGItOGQzZC00NTI2LWJkODctZWZmMDIzOGRmYTQ3XkEyXkFqcGc@._V1_SX300.jpg",
"Ratings": [
{
"Source": "Internet Movie Database",
"Value": "6.3/10"
type movie = {
id: string,
title: string,
plot: string,
poster_url: string,
imdbid: string,
isActive: boolean,
created: string,
updated: string
}
],
"Metascore": "N/A",
"imdbRating": "6.3",
"imdbVotes": "1,577",
"imdbID": "tt21841642",
"Type": "movie",
"DVD": "N/A",
"BoxOffice": "N/A",
"Production": "N/A",
"Website": "N/A",
"Response": "True"
const sessionid = checkSession()
async function getCards() {
const data = pb.collection('cards').getFullList()
return data
}
async function get_session() {
const data = pb.collection('game_sessions').getOne(sessionid as string, { expand: 'movie_title' })
return data
}
const movie_data = ref<movie>()
movie_data.value = (await get_session()).expand?.movie_title
const cards_data = ref(await getCards())
const playfield = computed(() => {
pb.collection('game_sessions').subscribe(sessionid as string, (e) => {
console.log(e.action)
console.log(e.record)
})
})
const buttons = ref(card_data)
function onButtonPress(id: string) {
const card = cards_data.value.find((card) => card.id === id)
if (card) {
card.isPressed = !card.isPressed
}
}
</script>
<template>
<div class="game-room-container">
<div>
<h1>{{ movie_data.Title }}</h1>
<h1>{{ movie_data?.title }}</h1>
</div>
<div class="playfield">
<PlayFieldButton v-for="button in buttons" :key="button.id" :icon="button.icon" :title="button.title" :description="button.description" />
<PlayFieldButton v-for="card in cards_data" :key="card.id" :icon="card.icon" :title="card.title"
:description="card.description" :id="card.id" :isPressed="card.isPressed"
@button-pressed="onButtonPress" />
</div>
<div class="end-game">
<button class="btn btn-primary">Lopeta peli</button>
@@ -70,6 +82,7 @@ h1 {
text-align: center;
margin-bottom: 1rem;
}
.playfield {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -83,9 +96,11 @@ h1 {
place-items: center;
overflow-y: auto;
}
.end-game {
justify-self: center;
width: 100%;
button {
width: 100%;
}
+1 -1
View File
@@ -36,7 +36,7 @@ if (sessionid) {
<p v-if="isValid">Täällä on vain tyhmää läppää. Herkkänahkaisille ei suositella.</p>
<p v-else>Virheellinen koodi</p>
</div>
<NuxtLink class="btn btn-primary" to="/info" v-if="isValid">Sisään</NuxtLink>
<NuxtLink class="btn btn-primary" :to="'/info?sessionid=' + sessionid" v-if="isValid">Sisään</NuxtLink>
</main>
<footer><sup>&copy;</sup> {{ new Date().getFullYear() }} Kessinen</footer>
</div>
+9 -38
View File
@@ -1,48 +1,19 @@
<script setup lang="ts">
const { pb } = usePocketbase()
const movie_data2 = await pb.collection('movies').getFullList()
console.log(movie_data2)
const movie_data = ref({
"Title": "A Maple Valley Christmas",
"Year": "2022",
"Rated": "TV-G",
"Released": "05 Nov 2022",
"Runtime": "85 min",
"Genre": "Comedy, Drama, Romance",
"Director": "Paul Ziller",
"Writer": "Joie Botkin, Jerry Todt",
"Actors": "Peyton List, Andrew W. Walker, Frances Flanagan",
"Plot": "Erica is a rancher who has spent her whole life working the family farm with her mother and sister. When Aaron arrives and disrupts her plans, she starts to question what it is she actually wants.",
"Language": "English, Italian",
"Country": "Canada, United States",
"Awards": "1 nomination total",
"Poster": "https://m.media-amazon.com/images/M/MV5BZjliMTRlMGItOGQzZC00NTI2LWJkODctZWZmMDIzOGRmYTQ3XkEyXkFqcGc@._V1_SX300.jpg",
"Ratings": [
{
"Source": "Internet Movie Database",
"Value": "6.3/10"
}
],
"Metascore": "N/A",
"imdbRating": "6.3",
"imdbVotes": "1,577",
"imdbID": "tt21841642",
"Type": "movie",
"DVD": "N/A",
"BoxOffice": "N/A",
"Production": "N/A",
"Website": "N/A",
"Response": "True"
})
const sessionid = checkSession()
const session_data = await pb.collection("game_sessions").getOne(sessionid as string, {expand: 'movie_title'})
const movie_data = session_data.expand?.movie_title
</script>
<template>
<div class="info-container">
<h1>{{ movie_data.Title }}</h1>
<h1>{{ movie_data?.title }}</h1>
<div class="poster-content">
<img class="poster" :src="movie_data.Poster" alt="">
<p>{{ movie_data.Plot }}</p>
<NuxtLink class="btn btn-primary" to="/gameroom">Aloita</NuxtLink>
<img class="poster" :src="movie_data.poster_url" alt="">
<p>{{ movie_data.plot }}</p>
<NuxtLink class="btn btn-primary" :to="'/gameroom?sessionid=' + sessionid">Aloita</NuxtLink>
</div>
</div>
</template>
+5
View File
@@ -15,6 +15,7 @@
"vue-router": "^4.6.3",
},
"devDependencies": {
"@types/node": "^24.10.1",
"sass": "^1.94.0",
},
},
@@ -468,6 +469,8 @@
"@types/estree": ["@types/[email protected]", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/node": ["@types/[email protected]", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
"@types/parse-path": ["@types/[email protected]", "", { "dependencies": { "parse-path": "*" } }, "sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q=="],
"@types/resolve": ["@types/[email protected]", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="],
@@ -1334,6 +1337,8 @@
"unctx": ["[email protected]", "", { "dependencies": { "acorn": "^8.14.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.17", "unplugin": "^2.1.0" } }, "sha512-AbaYw0Nm4mK4qjhns67C+kgxR2YWiwlDBPzxrN8h8C6VtAdCgditAY5Dezu3IJy4XVqAnbrXt9oQJvsn3fyozg=="],
"undici-types": ["[email protected]", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"unenv": ["[email protected]", "", { "dependencies": { "pathe": "^2.0.3" } }, "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw=="],
"unhead": ["[email protected]", "", { "dependencies": { "hookable": "^5.5.3" } }, "sha512-gEEjkV11Aj+rBnY6wnRfsFtF2RxKOLaPN4i+Gx3UhBxnszvV6ApSNZbGk7WKyy/lErQ6ekPN63qdFL7sa1leow=="],
+1 -1
View File
@@ -14,6 +14,6 @@ export default defineNuxtConfig({
'pocketbase-nuxt'
],
pocketbase: {
url: 'http://localhost:8090'
url: process.env.POCKETBASE_URL
},
})
+1
View File
@@ -21,6 +21,7 @@
"vue-router": "^4.6.3"
},
"devDependencies": {
"@types/node": "^24.10.1",
"sass": "^1.94.0"
}
}
+3
View File
@@ -1,6 +1,9 @@
{
// https://nuxt.com/docs/guide/concepts/typescript
"files": [],
"compilerOptions": {
"types": ["node"]
},
"references": [
{
"path": "./.nuxt/tsconfig.app.json"