refacto: extract the toolbox component

This commit is contained in:
Julien Calixte
2025-08-09 11:27:15 +02:00
parent 8932ddf87f
commit 839eec1acd
5 changed files with 135 additions and 69 deletions

View File

@@ -0,0 +1,44 @@
<script setup lang="ts">
import { useBoardGameStore } from '@/modules/5s/board-game-store'
import { computed } from 'vue'
const boardGameStore = useBoardGameStore()
const isSeitonActivated = computed(() =>
boardGameStore.sUsed.includes('seiton')
)
const rawTools = computed(() =>
boardGameStore.tools.map((t) => `${t.name} (${t.alias})`).join(', ')
)
</script>
<template>
<aside class="board-game-tools prose">
<h2>Toolbox</h2>
<div class="overflow-x-auto" v-if="isSeitonActivated">
<table class="table table-zebra">
<thead>
<tr>
<th>Tool</th>
<th>Alias</th>
</tr>
</thead>
<tbody>
<tr v-for="tool in boardGameStore.tools" :key="tool.alias">
<td>{{ tool.name }}</td>
<td>{{ tool.alias }}</td>
</tr>
</tbody>
</table>
</div>
<div v-else>
{{ rawTools }}
</div>
</aside>
</template>
<style scoped lang="scss">
.board-game-tools {
flex: 1;
}
</style>

View File

@@ -1,54 +1,56 @@
<script setup lang="ts"> <script setup lang="ts">
import { useBoardGameStore } from '@/modules/5s/board-game-store' import { useBoardGameStore } from '@/modules/5s/board-game-store'
import BoardGameToolbox from '@/modules/5s/BoardGameToolbox.vue'
import { _5S, is5S } from '@/modules/5s/types/5s'
import { toDuration } from '@/modules/5s/utils' import { toDuration } from '@/modules/5s/utils'
import { ref, toValue } from 'vue' import { ref, toValue } from 'vue'
const userInput = ref('') const userInput = ref('')
const mode = ref<_5S | null>(null)
const boardGameStore = useBoardGameStore() const boardGameStore = useBoardGameStore()
const duration = ref<string | null>(null) const duration = ref<string | null>(null)
setInterval(() => { setInterval(() => {
duration.value = boardGameStore.start duration.value = boardGameStore.meta.start
? toDuration( ? toDuration(
new Date(boardGameStore.start), new Date(boardGameStore.meta.start),
boardGameStore.end ? new Date(boardGameStore.end) : new Date() boardGameStore.meta.end ? new Date(boardGameStore.meta.end) : new Date()
) )
: null : null
}, 1000) }, 1000)
const submit = () => { const submit = () => {
const lastInput = toValue(userInput) const lastInput = toValue(userInput)
boardGameStore.craftWithTool(lastInput)
userInput.value = '' userInput.value = ''
if (!lastInput.startsWith('/')) {
boardGameStore.craftWithTool(lastInput)
return
}
if (lastInput === '/') {
mode.value = null
return
}
const command = lastInput.split('/').pop()
if (!command) {
return
}
if (is5S(command)) {
boardGameStore.activateS(command)
return
}
} }
</script> </script>
<template> <template>
<div class="board-game-workshop prose"> <div class="board-game-workshop">
<aside> <BoardGameToolbox class="aside" />
<h2>Tools</h2> <div class="main prose">
<div class="overflow-x-auto"> <h2 class="title">Workshop</h2>
<table class="table table-zebra">
<thead>
<tr>
<th>Tool</th>
<th>Alias</th>
</tr>
</thead>
<tbody>
<tr v-for="tool in boardGameStore.tools" :key="tool.alias">
<td>{{ tool.name }}</td>
<td>{{ tool.alias }}</td>
</tr>
</tbody>
</table>
</div>
</aside>
<div class="main">
<h2>Workshop</h2>
<button <button
class="btn" class="btn"
v-if="!boardGameStore.currentBoardGame" v-if="!boardGameStore.currentBoardGame"
@@ -135,26 +137,25 @@ const submit = () => {
</div> </div>
</div> </div>
</div> </div>
</div>
<aside <aside
class="performance" class="performance"
v-if="duration !== null || boardGameStore.perfs.length > 0" v-if="duration !== null || boardGameStore.meta.perfs.length > 0"
> >
<h2>Performance</h2> <h2>Performance</h2>
<p>{{ duration }}</p> <p>{{ duration }}</p>
<template v-if="boardGameStore.perfs.length > 0"> <template v-if="boardGameStore.meta.perfs.length > 0">
<h3>Last performances</h3> <h3>Last performances</h3>
<ul> <ul>
<li v-for="perf in boardGameStore.perfs"> <li v-for="perf in boardGameStore.meta.perfs">
{{ toDuration(new Date(perf[0]), new Date(perf[1])) }} {{ toDuration(new Date(perf[0]), new Date(perf[1])) }}
</li> </li>
</ul> </ul>
</template> </template>
</aside> </aside>
</div>
</template> </template>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -187,12 +188,13 @@ form {
color: green; color: green;
} }
aside { aside,
.aside {
flex: 1; flex: 1;
} }
.main { .main {
flex: 2; flex: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;

View File

@@ -1,3 +1,4 @@
import { _5S } from '@/modules/5s/types/5s'
import { boardGames } from '@/modules/5s/types/board-games' import { boardGames } from '@/modules/5s/types/board-games'
import { tools } from '@/modules/5s/types/tools' import { tools } from '@/modules/5s/types/tools'
import { BoardGame, Part, Task, Tool } from '@/modules/5s/types/workshop' import { BoardGame, Part, Task, Tool } from '@/modules/5s/types/workshop'
@@ -12,11 +13,21 @@ type State = {
currentPartIndex: number | null currentPartIndex: number | null
currentTaskIndex: number | null currentTaskIndex: number | null
usedTools: string[] usedTools: string[]
sUsed: _5S[]
meta: {
start: string | null start: string | null
end: string | null end: string | null
perfs: Array<[string, string]> perfs: Array<[string, string]>
}
} }
const firstDemands = [
boardGames[0]
// boardGames[0],
// boardGames[0],
// boardGames[0]
]
export const useBoardGameStore = defineStore('board-game', { export const useBoardGameStore = defineStore('board-game', {
state: (): State => ({ state: (): State => ({
tools: [], tools: [],
@@ -25,23 +36,25 @@ export const useBoardGameStore = defineStore('board-game', {
currentPartIndex: null, currentPartIndex: null,
currentTaskIndex: null, currentTaskIndex: null,
usedTools: [], usedTools: [],
sUsed: [],
meta: {
start: null, start: null,
end: null, end: null,
perfs: [] perfs: []
}
}), }),
actions: { actions: {
initGame() { initGame() {
// this.boardGames = [boardGames[0], boardGames[1]]
this.tools = tools.map((t) => ({ this.tools = tools.map((t) => ({
...t, ...t,
alias: randomAlias() alias: randomAlias()
})) }))
this.boardGames = [boardGames[0]] this.boardGames = firstDemands
this.currentBoardGameIndex = 0 this.currentBoardGameIndex = 0
this.currentPartIndex = 0 this.currentPartIndex = 0
this.currentTaskIndex = 0 this.currentTaskIndex = 0
this.start = new Date().toISOString() this.meta.start = new Date().toISOString()
this.end = null this.meta.end = null
}, },
craftWithTool(alias: string) { craftWithTool(alias: string) {
if (!this.currentTask) { if (!this.currentTask) {
@@ -67,7 +80,7 @@ export const useBoardGameStore = defineStore('board-game', {
}, },
increment() { increment() {
if ( if (
!this.start || !this.meta.start ||
!this.currentTask || !this.currentTask ||
!this.currentPart || !this.currentPart ||
!this.currentBoardGame || !this.currentBoardGame ||
@@ -99,11 +112,14 @@ export const useBoardGameStore = defineStore('board-game', {
} }
// All board games complete // All board games complete
this.end = new Date().toISOString() this.meta.end = new Date().toISOString()
this.perfs = [...this.perfs, [this.start, this.end]] this.meta.perfs = [...this.meta.perfs, [this.meta.start, this.meta.end]]
this.currentBoardGameIndex = null this.currentBoardGameIndex = null
this.currentPartIndex = null this.currentPartIndex = null
this.currentTaskIndex = null this.currentTaskIndex = null
},
activateS(s: _5S) {
this.sUsed = [...this.sUsed, s]
} }
}, },
getters: { getters: {
@@ -141,11 +157,11 @@ export const useBoardGameStore = defineStore('board-game', {
return this.currentPart.tasks[this.currentTaskIndex] return this.currentPart.tasks[this.currentTaskIndex]
}, },
durationToComplete(): string | null { durationToComplete(): string | null {
if (!this.start || !this.end) { if (!this.meta.start || !this.meta.end) {
return null return null
} }
return toDuration(new Date(this.start), new Date(this.end)) return toDuration(new Date(this.meta.start), new Date(this.meta.end))
} }
} }
}) })

View File

@@ -0,0 +1,6 @@
export type _5S = 'seiri' | 'seiton' | 'seiso' | 'seiketsu' | 'shitsuke'
const all5S: _5S[] = ['seiri', 'seiton', 'seiso', 'seiketsu', 'shitsuke']
export const is5S = (s: string): s is _5S => {
return all5S.includes(s as _5S)
}

View File

@@ -11,10 +11,8 @@ export const randomFloat = (min: number, max: number) => {
} }
export const randomAlias = () => export const randomAlias = () =>
Array.from({ length: 5 }, () => Array.from({ length: 4 }, () =>
Math.random() < 0.9 String.fromCharCode(97 + Math.floor(Math.random() * 26))
? String.fromCharCode(97 + Math.floor(Math.random() * 26))
: '-'
).join('') ).join('')
export const getMean = (data: number[]) => export const getMean = (data: number[]) =>