create a requirements store
This commit is contained in:
147
kwa-ui/src/stores/requirements.ts
Normal file
147
kwa-ui/src/stores/requirements.ts
Normal 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,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user