Added DB connection and started creating api calls for the pages

This commit is contained in:
gulimabr
2025-11-30 15:17:23 -03:00
parent b5381ae376
commit bbbe65067b
20 changed files with 1403 additions and 152 deletions

View File

@@ -1,86 +1,94 @@
import { useState, useEffect } from 'react'
import { useAuth } from '@/hooks'
import { useNavigate } from 'react-router-dom'
import { groupService, Group } from '@/services'
// Icons as components for cleaner code
const DataServicesIcon = () => (
<svg className="w-16 h-16" viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="12" y="8" width="40" height="48" rx="2" />
<line x1="20" y1="20" x2="44" y2="20" />
<line x1="20" y1="28" x2="44" y2="28" />
<line x1="20" y1="36" x2="44" y2="36" />
<line x1="20" y1="44" x2="36" y2="44" />
<rect x="36" y="40" width="12" height="12" rx="1" />
<line x1="40" y1="44" x2="44" y2="44" />
<line x1="40" y1="48" x2="44" y2="48" />
</svg>
)
/**
* Helper function to convert hex color to RGB values
*/
function hexToRgb(hex: string): { r: number; g: number; b: number } | null {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
return result
? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16),
}
: null
}
const IntegrationIcon = () => (
<svg className="w-16 h-16" viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="2">
<rect x="8" y="16" width="20" height="14" rx="2" />
<rect x="36" y="16" width="20" height="14" rx="2" />
<rect x="22" y="38" width="20" height="14" rx="2" />
<line x1="18" y1="30" x2="18" y2="38" />
<line x1="18" y1="38" x2="32" y2="38" />
<line x1="46" y1="30" x2="46" y2="38" />
<line x1="46" y1="38" x2="32" y2="38" />
</svg>
)
/**
* Helper function to determine if text should be light or dark based on background color
*/
function getContrastTextColor(hexColor: string): string {
const rgb = hexToRgb(hexColor)
if (!rgb) return '#000000'
// Calculate luminance
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255
return luminance > 0.5 ? '#000000' : '#ffffff'
}
const IntelligenceIcon = () => (
<svg className="w-16 h-16" viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="28" cy="28" r="14" />
<line x1="38" y1="38" x2="52" y2="52" strokeWidth="4" strokeLinecap="round" />
<path d="M22 28 L26 32 L34 24" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
/**
* Helper function to lighten a hex color for hover state
*/
function lightenColor(hex: string, percent: number): string {
const rgb = hexToRgb(hex)
if (!rgb) return hex
const lighten = (value: number) => Math.min(255, Math.floor(value + (255 - value) * percent))
const r = lighten(rgb.r).toString(16).padStart(2, '0')
const g = lighten(rgb.g).toString(16).padStart(2, '0')
const b = lighten(rgb.b).toString(16).padStart(2, '0')
return `#${r}${g}${b}`
}
const UserExperienceIcon = () => (
<svg className="w-16 h-16" viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="32" cy="20" r="8" />
<circle cx="16" cy="36" r="4" />
<circle cx="48" cy="36" r="4" />
<circle cx="24" cy="52" r="4" />
<circle cx="40" cy="52" r="4" />
<line x1="32" y1="28" x2="32" y2="36" />
<line x1="32" y1="36" x2="16" y2="36" />
<line x1="32" y1="36" x2="48" y2="36" />
<line x1="20" y1="40" x2="24" y2="48" />
<line x1="44" y1="40" x2="40" y2="48" />
</svg>
)
const ManagementIcon = () => (
<svg className="w-16 h-16" viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="32" cy="32" r="12" />
<path d="M32 16 L32 20" />
<path d="M32 44 L32 48" />
<path d="M16 32 L20 32" />
<path d="M44 32 L48 32" />
<path d="M20.7 20.7 L23.5 23.5" />
<path d="M40.5 40.5 L43.3 43.3" />
<path d="M20.7 43.3 L23.5 40.5" />
<path d="M40.5 23.5 L43.3 20.7" />
<rect x="26" y="28" width="12" height="10" rx="1" />
<path d="M29 28 L29 26 L35 26 L35 28" />
<line x1="29" y1="32" x2="35" y2="32" />
</svg>
)
const TrustworthinessIcon = () => (
<svg className="w-16 h-16" viewBox="0 0 64 64" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M12 36 C12 36 20 28 32 36 C44 44 52 36 52 36" strokeLinecap="round" />
<path d="M12 36 L24 48 L32 36" strokeLinecap="round" strokeLinejoin="round" />
<path d="M52 36 L40 48 L32 36" strokeLinecap="round" strokeLinejoin="round" />
</svg>
)
/**
* Helper function to darken a hex color for border
*/
function darkenColor(hex: string, percent: number): string {
const rgb = hexToRgb(hex)
if (!rgb) return hex
const darken = (value: number) => Math.max(0, Math.floor(value * (1 - percent)))
const r = darken(rgb.r).toString(16).padStart(2, '0')
const g = darken(rgb.g).toString(16).padStart(2, '0')
const b = darken(rgb.b).toString(16).padStart(2, '0')
return `#${r}${g}${b}`
}
export default function DashboardPage() {
const { user, logout } = useAuth()
const navigate = useNavigate()
const [groups, setGroups] = useState<Group[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [hoveredGroup, setHoveredGroup] = useState<number | null>(null)
const handleCategoryClick = (group: string) => {
navigate(`/requirements?group=${encodeURIComponent(group)}`)
useEffect(() => {
const fetchGroups = async () => {
try {
setLoading(true)
const fetchedGroups = await groupService.getGroups()
setGroups(fetchedGroups)
setError(null)
} catch (err) {
console.error('Failed to fetch groups:', err)
setError('Failed to load groups')
} finally {
setLoading(false)
}
}
fetchGroups()
}, [])
const handleCategoryClick = (groupName: string) => {
navigate(`/requirements?group=${encodeURIComponent(groupName)}`)
}
const handleMyRequirementsClick = () => {
@@ -194,76 +202,63 @@ export default function DashboardPage() {
Quick Search Filters
</h2>
{/* Grid Layout matching the screenshot */}
<div className="grid grid-cols-4 gap-0 max-w-2xl mx-auto">
{/* Row 1 */}
{/* Data Services - spans 2 rows */}
<div
onClick={() => 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]"
>
<div className="text-blue-800">
<DataServicesIcon />
</div>
<span className="mt-3 text-sm font-semibold text-blue-900">Data Services</span>
{/* Loading State */}
{loading && (
<div className="flex justify-center items-center min-h-[200px]">
<div className="text-gray-500">Loading groups...</div>
</div>
)}
{/* Integration - 1 row */}
<div
onClick={() => 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]"
>
<div className="text-amber-800">
<IntegrationIcon />
</div>
<span className="mt-2 text-sm font-semibold text-amber-900">Integration</span>
{/* Error State */}
{error && (
<div className="flex justify-center items-center min-h-[200px]">
<div className="text-red-500">{error}</div>
</div>
)}
{/* Intelligence - spans 2 rows */}
<div
onClick={() => 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]"
>
<div className="text-purple-800">
<IntelligenceIcon />
</div>
<span className="mt-3 text-sm font-semibold text-purple-900">Intelligence</span>
</div>
{/* Groups Grid */}
{!loading && !error && groups.length > 0 && (
<div className="grid grid-cols-3 gap-4 max-w-2xl mx-auto">
{groups.map((group) => {
const isHovered = hoveredGroup === group.id
const bgColor = isHovered
? lightenColor(group.hex_color, 0.2)
: group.hex_color
const borderColor = darkenColor(group.hex_color, 0.2)
const textColor = getContrastTextColor(group.hex_color)
{/* User Experience - spans 2 rows */}
<div
onClick={() => 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]"
>
<div className="text-green-800">
<UserExperienceIcon />
</div>
<span className="mt-3 text-sm font-semibold text-green-900">User Experience</span>
return (
<div
key={group.id}
onClick={() => handleCategoryClick(group.group_name)}
onMouseEnter={() => setHoveredGroup(group.id)}
onMouseLeave={() => setHoveredGroup(null)}
className="flex flex-col items-center justify-center p-6 cursor-pointer transition-colors min-h-[120px] rounded-lg"
style={{
backgroundColor: bgColor,
borderWidth: '2px',
borderStyle: 'solid',
borderColor: borderColor,
}}
>
<span
className="text-sm font-semibold text-center"
style={{ color: textColor }}
>
{group.group_name}
</span>
</div>
)
})}
</div>
)}
{/* Row 2 - Management and Trustworthiness */}
{/* Management - 1 row, spans 1 col */}
<div
onClick={() => 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]"
>
<div className="text-red-800">
<ManagementIcon />
</div>
<span className="mt-2 text-sm font-semibold text-red-900">Management</span>
{/* Empty State */}
{!loading && !error && groups.length === 0 && (
<div className="flex justify-center items-center min-h-[200px]">
<div className="text-gray-500">No groups found</div>
</div>
{/* Trustworthiness - 1 row */}
<div
onClick={() => 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]"
>
<div className="text-red-400">
<TrustworthinessIcon />
</div>
<span className="mt-2 text-sm font-semibold text-red-600">Trustworthiness</span>
</div>
</div>
)}
</div>
</div>
</div>