feat: turn into game!

This commit is contained in:
Julien Calixte
2025-08-06 18:40:42 +02:00
parent 807d1756c4
commit 7dd3086fd5
3 changed files with 195 additions and 9 deletions

View File

@@ -1,13 +1,23 @@
<script setup lang="ts">
import { useBoardGameStore } from '@/modules/5s/board-game-store'
import { tools } from '@/modules/5s/types/tools'
import { toDuration } from '@/modules/5s/utils'
import { ref, toValue } from 'vue'
const userInput = ref('')
const boardGameStore = useBoardGameStore()
const duration = ref<string | null>(null)
setInterval(() => {
duration.value = boardGameStore.start
? toDuration(new Date(boardGameStore.start))
: null
}, 1000)
const submit = () => {
const lastInput = toValue(userInput)
const tool = toValue(userInput)
boardGameStore.craftWithTool(tool)
userInput.value = ''
}
@@ -17,9 +27,20 @@ const submit = () => {
<div class="board-game-workshop">
<aside>
<h2>Tools</h2>
<ul>
<li v-for="tool in tools">{{ tool.name }}</li>
</ul>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Alias</th>
</tr>
</thead>
<tbody>
<tr v-for="tool in tools" :key="tool.alias">
<td>{{ tool.name }}</td>
<td>{{ tool.alias }}</td>
</tr>
</tbody>
</table>
</aside>
<div class="main">
@@ -39,6 +60,25 @@ const submit = () => {
<h3>
{{ boardGameStore.currentBoardGame.name }}
</h3>
<div v-if="boardGameStore.currentTask">
<h4>current task</h4>
<p>{{ boardGameStore.currentTask.name }}</p>
with tools:
<ul>
<li
v-for="tool in boardGameStore.currentTask.tools"
:key="tool.alias"
:class="{
'used-tool': boardGameStore.usedTools.some(
(t) => t === tool.alias
)
}"
>
{{ tool.name }}
</li>
</ul>
</div>
<hr />
<ul>
<li
v-for="part in boardGameStore.currentBoardGame.parts"
@@ -56,6 +96,12 @@ const submit = () => {
</ul>
</div>
</div>
<aside v-if="duration">
<h2>Performance</h2>
<p>{{ duration }}</p>
</aside>
</div>
</template>
@@ -71,6 +117,7 @@ const submit = () => {
min-width: 600px;
display: flex;
justify-content: space-between;
gap: 4rem;
input {
font-family: 'Google Sans Code', monospace;
@@ -80,6 +127,10 @@ const submit = () => {
}
}
.used-tool {
color: green;
}
button {
color: white;
}

View File

@@ -1,20 +1,131 @@
import { boardGames } from '@/modules/5s/types/board-games'
import { BoardGame } from '@/modules/5s/types/workshop'
import { BoardGame, Part, Task } from '@/modules/5s/types/workshop'
import { toDuration } from '@/modules/5s/utils'
import { defineStore } from 'pinia'
type State = {
boardGames: BoardGame[]
currentBoardGame: BoardGame | null
currentBoardGameIndex: number | null
currentPartIndex: number | null
currentTaskIndex: number | null
usedTools: string[]
start: string | null
end: string | null
}
export const useBoardGameStore = defineStore('day', {
export const useBoardGameStore = defineStore('board-game', {
state: (): State => ({
boardGames: [],
currentBoardGame: null
currentBoardGameIndex: null,
currentPartIndex: null,
currentTaskIndex: null,
usedTools: [],
start: null,
end: null
}),
actions: {
initGame() {
this.currentBoardGame = boardGames[0]
this.boardGames = [boardGames[0], boardGames[1]]
this.currentBoardGameIndex = 0
this.currentPartIndex = 0
this.currentTaskIndex = 0
this.start = new Date().toISOString()
},
craftWithTool(tool: string) {
if (!this.currentTask) {
return
}
if (!this.currentTask.tools.some((t) => t.alias === tool)) {
return
}
this.usedTools = [...this.usedTools, tool]
if (this.usedTools.length === this.currentTask.tools.length) {
this.usedTools = []
this.increment()
}
},
increment() {
if (
!this.currentTask ||
!this.currentPart ||
!this.currentBoardGame ||
this.currentTaskIndex === null ||
this.currentPartIndex === null ||
this.currentBoardGameIndex === null
) {
return
}
// Move to next task
if (this.currentTaskIndex + 1 < this.currentPart.tasks.length) {
this.currentTaskIndex += 1
return
}
// All tasks in part complete, move to next part
this.currentTaskIndex = 0
if (this.currentPartIndex + 1 < this.currentBoardGame.parts.length) {
this.currentPartIndex += 1
return
}
// All parts in board game complete, move to next board game
this.currentPartIndex = 0
if (this.currentBoardGameIndex + 1 < this.boardGames.length) {
this.currentBoardGameIndex += 1
return
}
// All board games complete
this.end = new Date().toISOString()
this.currentBoardGameIndex = null
this.currentPartIndex = null
this.currentTaskIndex = null
}
},
getters: {
currentBoardGame(): BoardGame | null {
if (this.currentBoardGameIndex === null) {
return null
}
if (this.currentBoardGameIndex >= this.boardGames.length) {
return null
}
return this.boardGames[this.currentBoardGameIndex]
},
currentPart(): Part | null {
if (!this.currentBoardGame || this.currentPartIndex === null) {
return null
}
if (this.currentPartIndex >= this.currentBoardGame.parts.length) {
return null
}
return this.currentBoardGame.parts[this.currentPartIndex]
},
currentTask(): Task | null {
if (!this.currentPart || this.currentTaskIndex === null) {
return null
}
if (this.currentTaskIndex >= this.currentPart.tasks.length) {
return null
}
return this.currentPart.tasks[this.currentTaskIndex]
},
durationToComplete(): string | null {
if (!this.start || !this.end) {
return null
}
return toDuration(new Date(this.start), new Date(this.end))
}
}
})

24
src/modules/5s/utils.ts Normal file
View File

@@ -0,0 +1,24 @@
export const toDuration = (startDate: Date, endDate: Date = new Date()) => {
const diffMs = endDate.getTime() - startDate.getTime()
if (diffMs < 0) {
return null
}
const seconds = Math.floor(diffMs / 1000) % 60
const minutes = Math.floor(diffMs / (1000 * 60)) % 60
const hours = Math.floor(diffMs / (1000 * 60 * 60))
const parts = []
if (hours > 0) {
parts.push(`${hours}h`)
}
if (minutes > 0) {
parts.push(`${minutes}m`)
}
parts.push(`${seconds}s`)
return parts.join(' ')
}