From b5381ae376e1d508b9e3c99c42fb00bee5bb6c7c Mon Sep 17 00:00:00 2001 From: gulimabr Date: Fri, 28 Nov 2025 15:24:13 -0300 Subject: [PATCH] Created requirements page frontend --- frontend/src/App.tsx | 9 + frontend/src/pages/DashboardPage.tsx | 44 ++- frontend/src/pages/RequirementsPage.tsx | 402 ++++++++++++++++++++++++ frontend/src/pages/index.ts | 1 + 4 files changed, 448 insertions(+), 8 deletions(-) create mode 100644 frontend/src/pages/RequirementsPage.tsx diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 2800746..9c9dde4 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -2,6 +2,7 @@ import { Routes, Route } from 'react-router-dom' import Layout from '@/components/Layout' import HomePage from '@/pages/HomePage' import DashboardPage from '@/pages/DashboardPage' +import RequirementsPage from '@/pages/RequirementsPage' import ProtectedRoute from '@/components/ProtectedRoute' function App() { @@ -23,6 +24,14 @@ function App() { } /> + + + + } + /> ) } diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx index e0d5db2..561d839 100644 --- a/frontend/src/pages/DashboardPage.tsx +++ b/frontend/src/pages/DashboardPage.tsx @@ -1,4 +1,5 @@ import { useAuth } from '@/hooks' +import { useNavigate } from 'react-router-dom' // Icons as components for cleaner code const DataServicesIcon = () => ( @@ -76,6 +77,15 @@ const TrustworthinessIcon = () => ( export default function DashboardPage() { const { user, logout } = useAuth() + const navigate = useNavigate() + + const handleCategoryClick = (group: string) => { + navigate(`/requirements?group=${encodeURIComponent(group)}`) + } + + const handleMyRequirementsClick = () => { + navigate('/requirements') + } return (
@@ -162,8 +172,8 @@ export default function DashboardPage() {
{/* My Requirements */} -
-

My Requirements

+
+

My Requirements

View your requirements and their properties.

@@ -188,7 +198,10 @@ export default function DashboardPage() {
{/* Row 1 */} {/* Data Services - spans 2 rows */} -
+
handleCategoryClick('Data Services')} + className="row-span-2 bg-blue-200 border border-blue-300 flex flex-col items-center justify-center p-4 cursor-pointer hover:bg-blue-300 transition-colors min-h-[200px]" + >
@@ -196,7 +209,10 @@ export default function DashboardPage() {
{/* Integration - 1 row */} -
+
handleCategoryClick('Integration')} + className="bg-amber-200 border border-amber-300 flex flex-col items-center justify-center p-4 cursor-pointer hover:bg-amber-300 transition-colors min-h-[100px]" + >
@@ -204,7 +220,10 @@ export default function DashboardPage() {
{/* Intelligence - spans 2 rows */} -
+
handleCategoryClick('Intelligence')} + className="row-span-2 bg-purple-200 border border-purple-300 flex flex-col items-center justify-center p-4 cursor-pointer hover:bg-purple-300 transition-colors min-h-[200px]" + >
@@ -212,7 +231,10 @@ export default function DashboardPage() {
{/* User Experience - spans 2 rows */} -
+
handleCategoryClick('User Experience')} + className="row-span-2 bg-green-200 border border-green-300 flex flex-col items-center justify-center p-4 cursor-pointer hover:bg-green-300 transition-colors min-h-[200px]" + >
@@ -221,7 +243,10 @@ export default function DashboardPage() { {/* Row 2 - Management and Trustworthiness */} {/* Management - 1 row, spans 1 col */} -
+
handleCategoryClick('Management')} + className="bg-red-200 border border-red-300 flex flex-col items-center justify-center p-4 cursor-pointer hover:bg-red-300 transition-colors min-h-[100px]" + >
@@ -229,7 +254,10 @@ export default function DashboardPage() {
{/* Trustworthiness - 1 row */} -
+
handleCategoryClick('Trustworthiness')} + className="bg-green-100 border border-green-300 flex flex-col items-center justify-center p-4 cursor-pointer hover:bg-green-200 transition-colors min-h-[100px]" + >
diff --git a/frontend/src/pages/RequirementsPage.tsx b/frontend/src/pages/RequirementsPage.tsx new file mode 100644 index 0000000..77f974e --- /dev/null +++ b/frontend/src/pages/RequirementsPage.tsx @@ -0,0 +1,402 @@ +import { useState, useEffect } from 'react' +import { useAuth } from '@/hooks' +import { useSearchParams, Link } from 'react-router-dom' + +// Types for requirements +interface Requirement { + id: string + tag: string + title: string + validation: string + priority: number + progress: number + group: RequirementGroup +} + +type RequirementGroup = + | 'Data Services' + | 'Integration' + | 'Intelligence' + | 'User Experience' + | 'Management' + | 'Trustworthiness' + +// Color mapping for requirement groups +const groupColors: Record = { + 'Data Services': { bg: 'bg-blue-200', border: 'border-blue-300' }, + 'Integration': { bg: 'bg-amber-200', border: 'border-amber-300' }, + 'Intelligence': { bg: 'bg-purple-200', border: 'border-purple-300' }, + 'User Experience': { bg: 'bg-green-200', border: 'border-green-300' }, + 'Management': { bg: 'bg-red-200', border: 'border-red-300' }, + 'Trustworthiness': { bg: 'bg-teal-600', border: 'border-teal-700' }, +} + +// Static mock data +const mockRequirements: Requirement[] = [ + { + id: '1', + tag: 'GSR#7', + title: 'Controle e monitoramento em right-time', + validation: 'Not Validated', + priority: 0, + progress: 0, + group: 'Trustworthiness', + }, + { + id: '2', + tag: 'GSR#10', + title: 'Interface de software DT-externo bem definida.', + validation: 'Not Validated', + priority: 1, + progress: 0, + group: 'Intelligence', + }, + { + id: '3', + tag: 'GSR#12', + title: 'Visualizacao', + validation: 'Not Validated', + priority: 1, + progress: 0, + group: 'User Experience', + }, + { + id: '4', + tag: 'GSR#1', + title: 'Estado corrente atualizado', + validation: 'Not Validated', + priority: 1, + progress: 0, + group: 'Data Services', + }, + { + id: '5', + tag: 'GSR#5', + title: 'Sincronização de dados em tempo real', + validation: 'Not Validated', + priority: 2, + progress: 25, + group: 'Data Services', + }, + { + id: '6', + tag: 'GSR#8', + title: 'Integração com sistemas legados', + validation: 'Not Validated', + priority: 1, + progress: 50, + group: 'Integration', + }, + { + id: '7', + tag: 'GSR#15', + title: 'Dashboard de gestão', + validation: 'Not Validated', + priority: 3, + progress: 0, + group: 'Management', + }, +] + +// Caption types +const captionItems = [ + { abbr: 'SFR', description: 'Specific Functional Requirement', underline: true }, + { abbr: 'SNFR', description: 'Specific Non-Functional Requirement', underline: true }, + { abbr: 'GNFR', description: 'Generic Non-Functional Requirement', underline: true }, + { abbr: 'GSR', description: 'Generic Service Requirement', underline: true }, + { abbr: 'GDR', description: 'Generic Data Requirement', underline: true }, + { abbr: 'GCR', description: 'Generic Connection Requirement', underline: true }, +] + +export default function RequirementsPage() { + const { user, logout } = useAuth() + const [searchParams, setSearchParams] = useSearchParams() + + // State + const [searchQuery, setSearchQuery] = useState('') + const [selectedGroups, setSelectedGroups] = useState([]) + const [orderBy, setOrderBy] = useState<'Date' | 'Priority' | 'Name'>('Date') + const [viewMode, setViewMode] = useState<'grid' | 'list'>('list') + + // Initialize filters from URL params + useEffect(() => { + const groupParam = searchParams.get('group') + if (groupParam) { + setSelectedGroups([groupParam as RequirementGroup]) + } + }, [searchParams]) + + // Filter requirements based on search and selected groups + const filteredRequirements = mockRequirements.filter(req => { + const matchesSearch = searchQuery === '' || + req.tag.toLowerCase().includes(searchQuery.toLowerCase()) || + req.title.toLowerCase().includes(searchQuery.toLowerCase()) + + const matchesGroup = selectedGroups.length === 0 || + selectedGroups.includes(req.group) + + return matchesSearch && matchesGroup + }) + + // Sort requirements + const sortedRequirements = [...filteredRequirements].sort((a, b) => { + switch (orderBy) { + case 'Priority': + return b.priority - a.priority + case 'Name': + return a.title.localeCompare(b.title) + default: + return 0 + } + }) + + const handleGroupToggle = (group: RequirementGroup) => { + setSelectedGroups(prev => + prev.includes(group) + ? prev.filter(g => g !== group) + : [...prev, group] + ) + } + + const handleClear = () => { + setSearchQuery('') + setSelectedGroups([]) + setSearchParams({}) + } + + const handleSearch = () => { + // For now, filtering is automatic - this could trigger an API call later + } + + const handleRemove = (id: string) => { + // For now, just a placeholder - will connect to backend later + console.log('Remove requirement:', id) + } + + const handleDetails = (id: string) => { + // For now, just a placeholder - will connect to backend later + console.log('View details:', id) + } + + return ( +
+ {/* Header */} +
+

+ Digital Twin Requirements Tool +

+
+ + {/* Top Bar */} +
+
+ {/* Breadcrumb */} +
+ Projects + » + PeTWIN + » + Search Requirements +
+ + {/* Language Toggle */} +
+ English +
+ +
+ Portuguese +
+ + {/* User Info */} +
+
+ + + + + {user?.full_name || user?.preferred_username || 'Ricardo Belo'}{' '} + (admin) + +
+ +
+
+
+ + {/* Main Content */} +
+
+ {/* Main Panel */} +
+ {/* New Requirement Button */} +
+ +
+ + {/* Search Bar */} +
+ setSearchQuery(e.target.value)} + className="flex-1 px-4 py-2 border border-gray-300 rounded text-sm focus:outline-none focus:ring-2 focus:ring-teal-500 focus:border-transparent" + /> + + +
+ + {/* Filter Group */} +
+

Filter Group

+
+ {(['Data Services', 'Intelligence', 'Management', 'Integration', 'User Experience', 'Trustworthiness'] as RequirementGroup[]).map((group) => ( + + ))} +
+
+ + {/* Order By and View Toggle */} +
+
+ Order by: + + +
+ +
+ + +
+
+ + {/* Requirements List */} +
+ {sortedRequirements.map((req) => { + const colors = groupColors[req.group] + return ( +
+ {/* Colored tag section */} +
+ + {req.tag} - {req.title} + +
+ + {/* Validation status */} +
+ + Validation: {req.validation} + +
+ + {/* Priority and Progress */} +
+

Priority: {req.priority}

+

{req.progress.toFixed(2)}% Done

+
+ + {/* Action buttons */} +
+ + +
+
+ ) + })} + + {sortedRequirements.length === 0 && ( +
+ No requirements found matching your criteria. +
+ )} +
+
+ + {/* Right Sidebar - Caption */} +
+
+

Caption:

+
+ {captionItems.map((item) => ( +

+ {item.abbr} + : {item.description} +

+ ))} +
+
+
+
+
+
+
+ ) +} diff --git a/frontend/src/pages/index.ts b/frontend/src/pages/index.ts index 27cfdfc..17c9d66 100644 --- a/frontend/src/pages/index.ts +++ b/frontend/src/pages/index.ts @@ -1,2 +1,3 @@ export { default as HomePage } from './HomePage' export { default as DashboardPage } from './DashboardPage' +export { default as RequirementsPage } from './RequirementsPage'