create a requirements store

This commit is contained in:
Sylvain Schneider
2026-05-11 23:14:20 +02:00
parent a07d217953
commit e73ba429e7
2 changed files with 164 additions and 145 deletions

View File

@@ -0,0 +1,147 @@
import { computed, ref } from 'vue'
import { defineStore } from 'pinia'
export type RequirementStatus = 'Draft' | 'In Review' | 'Approved' | 'Blocked' | 'Delivered'
export type RequirementPriority = 'Low' | 'Medium' | 'High' | 'Critical'
export interface Requirement {
id: number
reference: string
title: string
status: RequirementStatus
priority: RequirementPriority
owner: string
progress: number
dueDate: string
description: string
rationale: string
acceptanceCriteria: string[]
impactedModules: string[]
blockers: string[]
notes: string
}
export const useRequirementsStore = defineStore('requirements', () => {
const requirements = ref<Requirement[]>([
{
id: 101,
reference: 'REQ-101',
title: 'Full requirement traceability',
status: 'Approved',
priority: 'Critical',
owner: 'Claire Martin',
progress: 86,
dueDate: 'May 21, 2026',
description:
'Each requirement must be linked to a source, a validation status, and the design or test artifacts that depend on it.',
rationale:
'The team must be able to audit functional decisions, reduce scope drift, and prepare compliance reviews.',
acceptanceCriteria: [
'The source for each requirement is visible in the detail view.',
'The validation status is recorded in the history.',
'Links to related test cases are available from the detail panel.',
],
impactedModules: ['Backlog', 'Tests', 'Audit', 'Reporting'],
blockers: [],
notes: 'Plan a PDF export for milestone reviews.',
},
{
id: 102,
reference: 'REQ-102',
title: 'Business status management',
status: 'In Review',
priority: 'High',
owner: 'Julien Bernard',
progress: 54,
dueDate: 'May 24, 2026',
description:
'The product must provide consistent statuses to track a requirement from creation to delivery.',
rationale:
'Business and product teams do not use the same words at the same time. A stable naming scheme avoids ambiguity.',
acceptanceCriteria: [
'Statuses are readable at a glance.',
'The status list is consistent across the application.',
'Blocked items are visually highlighted.',
],
impactedModules: ['Workflow', 'Detail', 'Dashboard'],
blockers: ['Final label approval from the Product Owner'],
notes: 'Can be connected to workflow transitions later.',
},
{
id: 103,
reference: 'REQ-103',
title: 'Fast search and filtering',
status: 'Draft',
priority: 'Medium',
owner: 'Nadia Petit',
progress: 28,
dueDate: 'May 29, 2026',
description:
'Users must be able to find a requirement by title, reference, owner, or functional keyword.',
rationale:
'Once the project reaches a moderate volume, manual navigation becomes too expensive and slows down reviews.',
acceptanceCriteria: [
'Search filters the list instantly.',
'The number of visible items remains clear.',
'No result state is displayed explicitly.',
],
impactedModules: ['Sidebar', 'Search', 'Performance'],
blockers: ['Definition of the priority search criteria'],
notes: 'To be extended with type and batch filters.',
},
{
id: 104,
reference: 'REQ-104',
title: 'Delivery scope by release',
status: 'Blocked',
priority: 'Critical',
owner: 'Sophie Laurent',
progress: 41,
dueDate: 'May 31, 2026',
description:
'Each requirement must be linked to a target release so batch and delivery decisions can be prepared.',
rationale:
'Decision makers need a concise view to balance load and dependencies across releases.',
acceptanceCriteria: [
'The target release appears in the interface.',
'Blocked requirements are visually identified.',
'The delivery batch is readable in the detail view.',
],
impactedModules: ['Release', 'Roadmap', 'Governance'],
blockers: ['Architecture decision pending'],
notes: 'To be linked to a milestone calendar.',
},
])
const selectedId = ref(101)
const searchQuery = ref('')
const selectedRequirement = computed<Requirement>(() => {
const fallback = requirements.value[0]
const found = requirements.value.find((requirement) => requirement.id === selectedId.value)
return found ?? fallback!
})
const stats = computed(() => {
const total = requirements.value.length
const approvedCount = requirements.value.filter(
(requirement) => requirement.status === 'Approved'
).length
const blockedCount = requirements.value.filter(
(requirement) => requirement.status === 'Blocked'
).length
const criticalCount = requirements.value.filter(
(requirement) => requirement.priority === 'Critical'
).length
return { total, approvedCount, blockedCount, criticalCount }
})
return {
requirements,
selectedId,
searchQuery,
selectedRequirement,
stats,
}
})

View File

@@ -1,123 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue' import { computed } from 'vue'
import { storeToRefs } from 'pinia'
import Button from 'primevue/button' import Button from 'primevue/button'
import RequirementsTreeList from '../components/RequirementsTreeList.vue' import RequirementsTreeList from '../components/RequirementsTreeList.vue'
import RequirementDetail from '../components/RequirementDetail.vue' import RequirementDetail from '../components/RequirementDetail.vue'
import { useRequirementsStore } from '../stores/requirements'
import type { Requirement } from '../stores/requirements'
import type { TreeNode } from '../components/RequirementsTreeList.vue' import type { TreeNode } from '../components/RequirementsTreeList.vue'
type RequirementStatus = 'Draft' | 'In Review' | 'Approved' | 'Blocked' | 'Delivered' const requirementsStore = useRequirementsStore()
type RequirementPriority = 'Low' | 'Medium' | 'High' | 'Critical' const { requirements, selectedId, searchQuery, selectedRequirement, stats } =
storeToRefs(requirementsStore)
interface Requirement {
id: number
reference: string
title: string
status: RequirementStatus
priority: RequirementPriority
owner: string
progress: number
dueDate: string
description: string
rationale: string
acceptanceCriteria: string[]
impactedModules: string[]
blockers: string[]
notes: string
}
// All requirements data
const allRequirements: Requirement[] = [
{
id: 101,
reference: 'REQ-101',
title: 'Full requirement traceability',
status: 'Approved',
priority: 'Critical',
owner: 'Claire Martin',
progress: 86,
dueDate: 'May 21, 2026',
description:
'Each requirement must be linked to a source, a validation status, and the design or test artifacts that depend on it.',
rationale:
'The team must be able to audit functional decisions, reduce scope drift, and prepare compliance reviews.',
acceptanceCriteria: [
'The source for each requirement is visible in the detail view.',
'The validation status is recorded in the history.',
'Links to related test cases are available from the detail panel.',
],
impactedModules: ['Backlog', 'Tests', 'Audit', 'Reporting'],
blockers: [],
notes: 'Plan a PDF export for milestone reviews.',
},
{
id: 102,
reference: 'REQ-102',
title: 'Business status management',
status: 'In Review',
priority: 'High',
owner: 'Julien Bernard',
progress: 54,
dueDate: 'May 24, 2026',
description:
'The product must provide consistent statuses to track a requirement from creation to delivery.',
rationale:
'Business and product teams do not use the same words at the same time. A stable naming scheme avoids ambiguity.',
acceptanceCriteria: [
'Statuses are readable at a glance.',
'The status list is consistent across the application.',
'Blocked items are visually highlighted.',
],
impactedModules: ['Workflow', 'Detail', 'Dashboard'],
blockers: ['Final label approval from the Product Owner'],
notes: 'Can be connected to workflow transitions later.',
},
{
id: 103,
reference: 'REQ-103',
title: 'Fast search and filtering',
status: 'Draft',
priority: 'Medium',
owner: 'Nadia Petit',
progress: 28,
dueDate: 'May 29, 2026',
description:
'Users must be able to find a requirement by title, reference, owner, or functional keyword.',
rationale:
'Once the project reaches a moderate volume, manual navigation becomes too expensive and slows down reviews.',
acceptanceCriteria: [
'Search filters the list instantly.',
'The number of visible items remains clear.',
'No result state is displayed explicitly.',
],
impactedModules: ['Sidebar', 'Search', 'Performance'],
blockers: ['Definition of the priority search criteria'],
notes: 'To be extended with type and batch filters.',
},
{
id: 104,
reference: 'REQ-104',
title: 'Delivery scope by release',
status: 'Blocked',
priority: 'Critical',
owner: 'Sophie Laurent',
progress: 41,
dueDate: 'May 31, 2026',
description:
'Each requirement must be linked to a target release so batch and delivery decisions can be prepared.',
rationale:
'Decision makers need a concise view to balance load and dependencies across releases.',
acceptanceCriteria: [
'The target release appears in the interface.',
'Blocked requirements are visually identified.',
'The delivery batch is readable in the detail view.',
],
impactedModules: ['Release', 'Roadmap', 'Governance'],
blockers: ['Architecture decision pending'],
notes: 'To be linked to a milestone calendar.',
},
]
// Helper function to create tree nodes from requirements // Helper function to create tree nodes from requirements
const buildTreeData = (): TreeNode[] => { const buildTreeData = (): TreeNode[] => {
@@ -129,13 +24,13 @@ const buildTreeData = (): TreeNode[] => {
children: [ children: [
{ {
key: '101', key: '101',
label: `${allRequirements[0]!.reference} - ${allRequirements[0]!.title}`, label: `${requirements.value[0]!.reference} - ${requirements.value[0]!.title}`,
data: allRequirements[0]!, data: requirements.value[0]!,
}, },
{ {
key: '102-child', key: '102-child',
label: 'Sub-requirement: Power management', label: 'Sub-requirement: Power management',
data: { ...allRequirements[1]!, id: 1021, reference: 'REQ-102.1' } as Requirement, data: { ...requirements.value[1]!, id: 1021, reference: 'REQ-102.1' } as Requirement,
}, },
], ],
}, },
@@ -146,13 +41,13 @@ const buildTreeData = (): TreeNode[] => {
children: [ children: [
{ {
key: '102', key: '102',
label: `${allRequirements[1]!.reference} - ${allRequirements[1]!.title}`, label: `${requirements.value[1]!.reference} - ${requirements.value[1]!.title}`,
data: allRequirements[1]!, data: requirements.value[1]!,
}, },
{ {
key: '103', key: '103',
label: `${allRequirements[2]!.reference} - ${allRequirements[2]!.title}`, label: `${requirements.value[2]!.reference} - ${requirements.value[2]!.title}`,
data: allRequirements[2]!, data: requirements.value[2]!,
}, },
], ],
}, },
@@ -163,38 +58,15 @@ const buildTreeData = (): TreeNode[] => {
children: [ children: [
{ {
key: '104', key: '104',
label: `${allRequirements[3]!.reference} - ${allRequirements[3]!.title}`, label: `${requirements.value[3]!.reference} - ${requirements.value[3]!.title}`,
data: allRequirements[3]!, data: requirements.value[3]!,
}, },
], ],
}, },
] ]
} }
const treeData = buildTreeData() const treeData = computed(() => buildTreeData())
const searchQuery = ref('')
const selectedId = ref(101)
const fallbackRequirement = allRequirements[0]!
const selectedRequirement = computed<Requirement>(() => {
const found = allRequirements.find((req) => req.id === selectedId.value)
return found ?? fallbackRequirement
})
const stats = computed(() => {
const total = allRequirements.length
const approvedCount = allRequirements.filter(
(requirement) => requirement.status === 'Approved'
).length
const blockedCount = allRequirements.filter(
(requirement) => requirement.status === 'Blocked'
).length
const criticalCount = allRequirements.filter(
(requirement) => requirement.priority === 'Critical'
).length
return { total, approvedCount, blockedCount, criticalCount }
})
</script> </script>
<template> <template>