update ui sizes and distances
This commit is contained in:
@@ -157,21 +157,21 @@ const prioritySeverity = (priority: Requirement['priority']) => {
|
||||
.detail-card {
|
||||
padding: 0.5rem;
|
||||
border: 1px solid rgba(96, 117, 156, 0.16);
|
||||
border-radius: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
box-shadow: 0 24px 60px rgba(34, 49, 77, 0.12);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.detail-card :is(.p-card-body) {
|
||||
padding: 1.5rem;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.detail-card__header {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1.5rem;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
@@ -198,14 +198,14 @@ const prioritySeverity = (priority: Requirement['priority']) => {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
gap: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.detail-badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 1rem;
|
||||
gap: 0.45rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.detail-description {
|
||||
@@ -217,23 +217,23 @@ const prioritySeverity = (priority: Requirement['priority']) => {
|
||||
.detail-grid,
|
||||
.detail-columns {
|
||||
display: grid;
|
||||
gap: 1rem;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.detail-grid {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
margin-top: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.detail-columns {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
margin-top: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.info-panel {
|
||||
padding: 1rem;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid rgba(96, 117, 156, 0.14);
|
||||
border-radius: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
background: rgba(247, 249, 255, 0.9);
|
||||
}
|
||||
|
||||
@@ -243,7 +243,7 @@ const prioritySeverity = (priority: Requirement['priority']) => {
|
||||
}
|
||||
|
||||
.info-panel p {
|
||||
margin: 0.65rem 0 0;
|
||||
margin: 0.5rem 0 0;
|
||||
color: #4c5d77;
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ const prioritySeverity = (priority: Requirement['priority']) => {
|
||||
}
|
||||
|
||||
.info-panel--warning {
|
||||
margin-top: 1rem;
|
||||
margin-top: 0.75rem;
|
||||
background: linear-gradient(180deg, rgba(255, 244, 236, 0.95), rgba(255, 249, 244, 0.95));
|
||||
border-color: rgba(223, 134, 57, 0.24);
|
||||
}
|
||||
|
||||
@@ -1,325 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import InputText from 'primevue/inputtext'
|
||||
import Listbox from 'primevue/listbox'
|
||||
import Tag from 'primevue/tag'
|
||||
|
||||
export interface Requirement {
|
||||
id: number
|
||||
reference: string
|
||||
title: string
|
||||
status: 'Draft' | 'In Review' | 'Approved' | 'Blocked' | 'Delivered'
|
||||
priority: 'Low' | 'Medium' | 'High' | 'Critical'
|
||||
owner: string
|
||||
progress: number
|
||||
dueDate: string
|
||||
description: string
|
||||
rationale: string
|
||||
acceptanceCriteria: string[]
|
||||
impactedModules: string[]
|
||||
blockers: string[]
|
||||
notes: string
|
||||
}
|
||||
|
||||
interface Props {
|
||||
requirements: Requirement[]
|
||||
searchQuery: string
|
||||
selectedId: number
|
||||
stats: {
|
||||
total: number
|
||||
approvedCount: number
|
||||
blockedCount: number
|
||||
criticalCount: number
|
||||
}
|
||||
}
|
||||
|
||||
interface Emits {
|
||||
(e: 'update:searchQuery', value: string): void
|
||||
(e: 'update:selectedId', value: number): void
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
defineEmits<Emits>()
|
||||
|
||||
const filteredRequirements = computed(() => {
|
||||
const query = props.searchQuery.trim().toLowerCase()
|
||||
|
||||
if (!query) {
|
||||
return props.requirements
|
||||
}
|
||||
|
||||
return props.requirements.filter((requirement) => {
|
||||
return [
|
||||
requirement.title,
|
||||
requirement.reference,
|
||||
requirement.owner,
|
||||
requirement.status,
|
||||
requirement.priority,
|
||||
]
|
||||
.join(' ')
|
||||
.toLowerCase()
|
||||
.includes(query)
|
||||
})
|
||||
})
|
||||
|
||||
// Helper function to get severity color for status
|
||||
const statusSeverity = (status: Requirement['status']) => {
|
||||
const map: Record<Requirement['status'], 'secondary' | 'info' | 'success' | 'danger' | 'contrast'> = {
|
||||
Draft: 'secondary',
|
||||
'In Review': 'info',
|
||||
Approved: 'success',
|
||||
Blocked: 'danger',
|
||||
Delivered: 'contrast',
|
||||
}
|
||||
return map[status]
|
||||
}
|
||||
|
||||
// Helper function to get severity color for priority
|
||||
const prioritySeverity = (priority: Requirement['priority']) => {
|
||||
const map: Record<Requirement['priority'], 'success' | 'info' | 'warn' | 'danger'> = {
|
||||
Low: 'success',
|
||||
Medium: 'info',
|
||||
High: 'warn',
|
||||
Critical: 'danger',
|
||||
}
|
||||
return map[priority]
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside class="sidebar">
|
||||
<!-- Sidebar header with title and total count -->
|
||||
<div class="sidebar__header">
|
||||
<div>
|
||||
<p class="eyebrow">Catalog</p>
|
||||
<h2>Requirements</h2>
|
||||
</div>
|
||||
<Tag :value="stats.total" severity="info" />
|
||||
</div>
|
||||
|
||||
<!-- Search field -->
|
||||
<span class="p-input-icon-left search-field">
|
||||
<i class="pi pi-search" />
|
||||
<InputText
|
||||
:value="searchQuery"
|
||||
@input="(event) => $emit('update:searchQuery', (event.target as HTMLInputElement).value)"
|
||||
placeholder="Search requirements"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<!-- Mini statistics cards -->
|
||||
<div class="mini-stats">
|
||||
<article>
|
||||
<span>Total</span>
|
||||
<strong>{{ stats.total }}</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>Approved</span>
|
||||
<strong>{{ stats.approvedCount }}</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>Blocked</span>
|
||||
<strong>{{ stats.blockedCount }}</strong>
|
||||
</article>
|
||||
<article>
|
||||
<span>Critical</span>
|
||||
<strong>{{ stats.criticalCount }}</strong>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<!-- Requirements listbox -->
|
||||
<Listbox
|
||||
:model-value="selectedId"
|
||||
@update:model-value="(value) => $emit('update:selectedId', value)"
|
||||
:options="filteredRequirements"
|
||||
option-label="title"
|
||||
option-value="id"
|
||||
class="requirements-list"
|
||||
>
|
||||
<template #option="{ option }">
|
||||
<div v-if="option" class="requirement-item">
|
||||
<div>
|
||||
<p class="requirement-item__reference">{{ option.reference }}</p>
|
||||
<strong>{{ option.title }}</strong>
|
||||
<small>{{ option.owner }}</small>
|
||||
</div>
|
||||
|
||||
<div class="requirement-item__tags">
|
||||
<Tag :value="option.status" :severity="statusSeverity(option.status)" />
|
||||
<Tag :value="option.priority" :severity="prioritySeverity(option.priority)" rounded />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #empty>
|
||||
<div class="empty-state">
|
||||
<i class="pi pi-inbox" />
|
||||
<p>No requirement matches your search.</p>
|
||||
</div>
|
||||
</template>
|
||||
</Listbox>
|
||||
</aside>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: 1.25rem;
|
||||
border: 1px solid rgba(96, 117, 156, 0.16);
|
||||
border-radius: 1.5rem;
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
box-shadow: 0 24px 60px rgba(34, 49, 77, 0.12);
|
||||
backdrop-filter: blur(18px);
|
||||
}
|
||||
|
||||
.sidebar__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.eyebrow {
|
||||
margin: 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.14em;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
color: #5c6b88;
|
||||
}
|
||||
|
||||
.sidebar h2 {
|
||||
margin: 0;
|
||||
color: #12213a;
|
||||
}
|
||||
|
||||
.search-field {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-field :is(.p-inputtext) {
|
||||
width: 100%;
|
||||
border-radius: 0.95rem;
|
||||
padding-left: 2.5rem;
|
||||
}
|
||||
|
||||
.mini-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.mini-stats article {
|
||||
padding: 0.9rem;
|
||||
border-radius: 1rem;
|
||||
background: rgba(242, 246, 255, 0.88);
|
||||
}
|
||||
|
||||
.mini-stats span {
|
||||
display: block;
|
||||
margin-bottom: 0.35rem;
|
||||
color: #667690;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.mini-stats strong {
|
||||
color: #12213a;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.requirements-list {
|
||||
flex: 1;
|
||||
min-height: 28rem;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.requirements-list :is(.p-listbox, .p-listbox-list) {
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.requirements-list :is(.p-listbox-list) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
}
|
||||
|
||||
.requirements-list :is(.p-listbox-item) {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.requirements-list :is(.p-listbox-item.p-highlight) .requirement-item {
|
||||
border-color: rgba(78, 107, 255, 0.34);
|
||||
background: linear-gradient(135deg, rgba(78, 107, 255, 0.12), rgba(255, 255, 255, 0.92));
|
||||
box-shadow: 0 12px 30px rgba(78, 107, 255, 0.12);
|
||||
}
|
||||
|
||||
.requirement-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
width: 100%;
|
||||
padding: 1rem;
|
||||
border: 1px solid rgba(96, 117, 156, 0.14);
|
||||
border-radius: 1rem;
|
||||
background: rgba(248, 250, 255, 0.88);
|
||||
transition:
|
||||
transform 0.2s ease,
|
||||
box-shadow 0.2s ease,
|
||||
border-color 0.2s ease;
|
||||
}
|
||||
|
||||
.requirement-item:hover {
|
||||
transform: translateY(-1px);
|
||||
border-color: rgba(78, 107, 255, 0.22);
|
||||
box-shadow: 0 10px 24px rgba(34, 49, 77, 0.08);
|
||||
}
|
||||
|
||||
.requirement-item__reference {
|
||||
margin: 0 0 0.25rem;
|
||||
color: #6c7b97;
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.requirement-item strong {
|
||||
display: block;
|
||||
margin-bottom: 0.25rem;
|
||||
color: #12213a;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.requirement-item small {
|
||||
color: #6b7b94;
|
||||
}
|
||||
|
||||
.requirement-item__tags {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
gap: 0.5rem;
|
||||
min-height: 12rem;
|
||||
color: #6b7b94;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.empty-state i {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.mini-stats {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -182,10 +182,10 @@ const onNodeSelect = (node: TreeNode) => {
|
||||
.requirements-tree {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
padding: 1rem;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem;
|
||||
border: 1px solid rgba(96, 117, 156, 0.16);
|
||||
border-radius: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
box-shadow: 0 24px 60px rgba(34, 49, 77, 0.12);
|
||||
backdrop-filter: blur(18px);
|
||||
@@ -196,8 +196,8 @@ const onNodeSelect = (node: TreeNode) => {
|
||||
.stats-bar {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
gap: 0.35rem;
|
||||
padding-bottom: 0.35rem;
|
||||
border-bottom: 1px solid rgba(96, 117, 156, 0.12);
|
||||
}
|
||||
|
||||
@@ -228,7 +228,7 @@ const onNodeSelect = (node: TreeNode) => {
|
||||
|
||||
.search-field :is(.p-inputtext) {
|
||||
width: 100%;
|
||||
border-radius: 0.8rem;
|
||||
border-radius: 0.5rem;
|
||||
padding-left: 0.85rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
@@ -237,7 +237,7 @@ const onNodeSelect = (node: TreeNode) => {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
overflow-y: auto;
|
||||
border-radius: 0.8rem;
|
||||
border-radius: 0.5rem;
|
||||
}
|
||||
|
||||
.requirements-tree-view {
|
||||
@@ -278,7 +278,7 @@ const onNodeSelect = (node: TreeNode) => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ const onNodeSelect = (node: TreeNode) => {
|
||||
|
||||
.node-tags {
|
||||
display: flex;
|
||||
gap: 0.3rem;
|
||||
gap: 0.25rem;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -308,8 +308,8 @@ const onNodeSelect = (node: TreeNode) => {
|
||||
}
|
||||
|
||||
.requirements-tree {
|
||||
padding: 0.75rem;
|
||||
gap: 0.5rem;
|
||||
padding: 0.6rem;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.search-field :is(.p-inputtext) {
|
||||
|
||||
@@ -115,11 +115,11 @@ const treeData = computed(() => buildTreeData())
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 1.5rem 1.75rem;
|
||||
gap: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 1rem 1.25rem;
|
||||
border: 1px solid rgba(96, 117, 156, 0.16);
|
||||
border-radius: 1.5rem;
|
||||
border-radius: 0.5rem;
|
||||
background: rgba(255, 255, 255, 0.86);
|
||||
box-shadow: 0 24px 60px rgba(34, 49, 77, 0.12);
|
||||
backdrop-filter: blur(18px);
|
||||
@@ -157,7 +157,7 @@ const treeData = computed(() => buildTreeData())
|
||||
.workspace {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(18rem, 22rem) minmax(0, 1fr);
|
||||
gap: 1.5rem;
|
||||
gap: 1rem;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user