remove board service and replace with function + pinia store ready
This commit is contained in:
@@ -1,68 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import FeatureStep from '@/modules/feature/FeatureStep.vue'
|
import FeatureStep from '@/modules/feature/FeatureStep.vue'
|
||||||
import { Feature } from '@/modules/feature/feature'
|
|
||||||
import { createFeatureBoard } from '@/modules/feature/feature-board'
|
|
||||||
import { featureSteps } from '@/modules/feature/feature-steps'
|
import { featureSteps } from '@/modules/feature/feature-steps'
|
||||||
import { sumElements } from '@/utils'
|
import { useFeatureStore } from '@/modules/feature/store'
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { onMounted } from 'vue'
|
||||||
|
|
||||||
const featureBoard = createFeatureBoard()
|
const featureStore = useFeatureStore()
|
||||||
|
|
||||||
const totalDays = ref(0)
|
onMounted(() => featureStore.initBoard())
|
||||||
const features = ref<Feature[]>([])
|
|
||||||
|
|
||||||
const meanComplexity = computed(
|
|
||||||
() =>
|
|
||||||
Math.round(
|
|
||||||
100 *
|
|
||||||
(sumElements(features.value.map((feature) => feature.complexity)) /
|
|
||||||
features.value.length)
|
|
||||||
) / 100
|
|
||||||
)
|
|
||||||
|
|
||||||
const meanLeadTime = computed(
|
|
||||||
() =>
|
|
||||||
Math.round(
|
|
||||||
100 *
|
|
||||||
(sumElements(features.value.map((feature) => feature.leadTime)) /
|
|
||||||
features.value.length)
|
|
||||||
) / 100
|
|
||||||
)
|
|
||||||
|
|
||||||
onMounted(() => (features.value = featureBoard.initBoard(featureSteps)))
|
|
||||||
|
|
||||||
const nextDay = () => {
|
|
||||||
totalDays.value++
|
|
||||||
features.value = featureBoard.nextDay(
|
|
||||||
features.value,
|
|
||||||
featureSteps[0].stepIndex
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const featuresGroupedByStep = computed(() => {
|
|
||||||
const groupedByStep: Record<number, Feature[]> = {}
|
|
||||||
|
|
||||||
features.value.forEach((feature) => {
|
|
||||||
if (!groupedByStep[feature.step]) {
|
|
||||||
groupedByStep[feature.step] = [feature]
|
|
||||||
} else {
|
|
||||||
groupedByStep[feature.step].push(feature)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return groupedByStep
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="dashboard">
|
<div class="dashboard">
|
||||||
<div>
|
<div>
|
||||||
{{ features.length }} features | mean complexity : {{ meanComplexity }} |
|
{{ featureStore.features.length }} features | mean complexity :
|
||||||
mean lead time : {{ meanLeadTime }} days
|
{{ featureStore.meanComplexity }} | mean lead time :
|
||||||
|
{{ featureStore.meanLeadTime }} days
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<button @click="nextDay">next day</button>
|
<button @click="featureStore.nextDay()">next day</button>
|
||||||
Total days: {{ totalDays }}
|
Total days: {{ featureStore.meta.totalDays }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ul class="features-steps">
|
<ul class="features-steps">
|
||||||
@@ -70,7 +26,7 @@ const featuresGroupedByStep = computed(() => {
|
|||||||
v-for="step in featureSteps"
|
v-for="step in featureSteps"
|
||||||
:key="step.title"
|
:key="step.title"
|
||||||
:step="step"
|
:step="step"
|
||||||
:features="featuresGroupedByStep[step.stepIndex] ?? []"
|
:features="featureStore.featuresGroupedByStep[step.stepIndex] ?? []"
|
||||||
/>
|
/>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -53,64 +53,71 @@ const hasQualityIssue = (
|
|||||||
return Math.random() > probabilityOfQualityIssue / multiplicator
|
return Math.random() > probabilityOfQualityIssue / multiplicator
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createFeatureBoard = () => {
|
export const newBoard = () => shuffleArray(features)
|
||||||
const boardFeatures = shuffleArray(features)
|
|
||||||
|
|
||||||
const initBoard = (steps: FeatureStep[]): Feature[] => {
|
export const initBoard = (
|
||||||
const initialFeatures = popNElement(boardFeatures, 10)
|
steps: FeatureStep[],
|
||||||
|
features: Feature[]
|
||||||
|
): Feature[] => {
|
||||||
|
const initialFeatures = popNElement(features, 10)
|
||||||
|
|
||||||
initialFeatures.forEach((feature) => {
|
initialFeatures.forEach((feature) => {
|
||||||
const step = pickRandomElement(steps)
|
const step = pickRandomElement(steps)
|
||||||
feature.status = pickRandomElement(['doing', 'done'])
|
feature.status = pickRandomElement(['doing', 'done'])
|
||||||
feature.step = step.stepIndex
|
feature.step = step.stepIndex
|
||||||
})
|
})
|
||||||
|
|
||||||
return initialFeatures
|
return initialFeatures
|
||||||
}
|
}
|
||||||
|
|
||||||
const nextDay = (features: Feature[], initialStep: number): Feature[] => {
|
export const nextDay = ({
|
||||||
features.forEach((feature) => {
|
backlog,
|
||||||
const isFeatureLive = feature.step === 0 && feature.status === 'done'
|
features,
|
||||||
if (isFeatureLive) {
|
initialStep
|
||||||
return
|
}: {
|
||||||
}
|
backlog: Feature[]
|
||||||
|
features: Feature[]
|
||||||
feature.leadTime++
|
initialStep: number
|
||||||
|
}): Feature[] => {
|
||||||
switch (feature.status) {
|
features.forEach((feature) => {
|
||||||
case 'doing':
|
const isFeatureLive = feature.step === 0 && feature.status === 'done'
|
||||||
feature.status = 'done'
|
if (isFeatureLive) {
|
||||||
break
|
return
|
||||||
case 'done':
|
|
||||||
feature.status = 'doing'
|
|
||||||
|
|
||||||
if (
|
|
||||||
hasQualityIssue(
|
|
||||||
feature.complexity,
|
|
||||||
features.filter(
|
|
||||||
(f) => f.status === 'doing' && f.step === feature.step
|
|
||||||
).length
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
feature.step = Math.min(4, feature.step + 1)
|
|
||||||
feature.qualityIssue++
|
|
||||||
} else {
|
|
||||||
feature.step--
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (features.length < MAX_FEATURES) {
|
|
||||||
const [newFeature] = popNElement(boardFeatures, 1)
|
|
||||||
|
|
||||||
if (newFeature) {
|
|
||||||
features.push({ ...newFeature, step: initialStep })
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return features
|
feature.leadTime++
|
||||||
|
|
||||||
|
switch (feature.status) {
|
||||||
|
case 'doing':
|
||||||
|
feature.status = 'done'
|
||||||
|
break
|
||||||
|
case 'done':
|
||||||
|
feature.status = 'doing'
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasQualityIssue(
|
||||||
|
feature.complexity,
|
||||||
|
features.filter(
|
||||||
|
(f) => f.status === 'doing' && f.step === feature.step
|
||||||
|
).length
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
feature.step = Math.min(4, feature.step + 1)
|
||||||
|
feature.qualityIssue++
|
||||||
|
} else {
|
||||||
|
feature.step--
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (features.length < MAX_FEATURES) {
|
||||||
|
const [newFeature] = popNElement(backlog, 1)
|
||||||
|
|
||||||
|
if (newFeature) {
|
||||||
|
features.push({ ...newFeature, step: initialStep })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { initBoard, nextDay }
|
return features
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,74 @@
|
|||||||
import { Feature } from '@/modules/feature/feature'
|
import { Feature } from '@/modules/feature/feature'
|
||||||
import { FeatureStep } from '@/modules/feature/feature-steps'
|
import { initBoard, newBoard, nextDay } from '@/modules/feature/feature-board'
|
||||||
|
import { FeatureStep, featureSteps } from '@/modules/feature/feature-steps'
|
||||||
|
import { sumElements } from '@/utils'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
steps: FeatureStep[]
|
steps: FeatureStep[]
|
||||||
features: Feature[]
|
features: Feature[]
|
||||||
|
backlog: Feature[]
|
||||||
|
meta: {
|
||||||
|
totalDays: number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const featureStore = defineStore('feature', {
|
export const useFeatureStore = defineStore('feature', {
|
||||||
state: (): State => ({
|
state: (): State => ({
|
||||||
steps: [],
|
steps: [],
|
||||||
features: []
|
features: [],
|
||||||
})
|
backlog: [],
|
||||||
|
meta: {
|
||||||
|
totalDays: 0
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
initBoard() {
|
||||||
|
this.backlog = newBoard()
|
||||||
|
this.steps = featureSteps
|
||||||
|
this.features = initBoard(this.steps, this.backlog)
|
||||||
|
this.meta.totalDays = 0
|
||||||
|
},
|
||||||
|
nextDay() {
|
||||||
|
this.features = nextDay({
|
||||||
|
backlog: this.backlog,
|
||||||
|
features: this.features,
|
||||||
|
initialStep: this.steps[0].stepIndex
|
||||||
|
})
|
||||||
|
this.meta.totalDays++
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getters: {
|
||||||
|
meanComplexity: (state) => {
|
||||||
|
return (
|
||||||
|
Math.round(
|
||||||
|
100 *
|
||||||
|
(sumElements(state.features.map((feature) => feature.complexity)) /
|
||||||
|
state.features.length)
|
||||||
|
) / 100
|
||||||
|
)
|
||||||
|
},
|
||||||
|
meanLeadTime: (state) => {
|
||||||
|
return (
|
||||||
|
Math.round(
|
||||||
|
100 *
|
||||||
|
(sumElements(state.features.map((feature) => feature.leadTime)) /
|
||||||
|
state.features.length)
|
||||||
|
) / 100
|
||||||
|
)
|
||||||
|
},
|
||||||
|
featuresGroupedByStep: (state) => {
|
||||||
|
const groupedByStep: Record<number, Feature[]> = {}
|
||||||
|
|
||||||
|
state.features.forEach((feature) => {
|
||||||
|
if (!groupedByStep[feature.step]) {
|
||||||
|
groupedByStep[feature.step] = [feature]
|
||||||
|
} else {
|
||||||
|
groupedByStep[feature.step].push(feature)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return groupedByStep
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user