Added DB connection and started creating api calls for the pages
This commit is contained in:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user