parent
3194f76839
commit
4434609ec9
@ -0,0 +1,5 @@ |
|||||||
|
This command is being called by someone who is not a developer. Your job is to take steps to accomplish the stated goal. |
||||||
|
|
||||||
|
ultrathink about the root cause of the following problem. Use subagents to explore if needed. Create a plan to address the root cause. Follow this use plan and then fix it. |
||||||
|
|
||||||
|
Optional context: #$ARGUMENTS |
@ -0,0 +1,80 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"id": "1", |
||||||
|
"name": "React Developers", |
||||||
|
"description": "A group for React developers to share knowledge and collaborate on projects", |
||||||
|
"memberCount": 15, |
||||||
|
"memberIds": ["1", "2", "3"], |
||||||
|
"createdBy": "1", |
||||||
|
"createdAt": "2023-08-01T10:00:00Z", |
||||||
|
"updatedAt": "2023-10-15T14:30:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["react", "frontend", "development"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=react" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "2", |
||||||
|
"name": "Product Management Circle", |
||||||
|
"description": "Product managers discussing strategies, tools, and best practices", |
||||||
|
"memberCount": 8, |
||||||
|
"memberIds": ["2", "4"], |
||||||
|
"createdBy": "2", |
||||||
|
"createdAt": "2023-09-10T09:00:00Z", |
||||||
|
"updatedAt": "2023-10-20T16:45:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["product", "management", "strategy"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=product" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "3", |
||||||
|
"name": "UX Design Collective", |
||||||
|
"description": "Designers sharing insights, resources, and feedback on user experience", |
||||||
|
"memberCount": 12, |
||||||
|
"memberIds": ["3", "6"], |
||||||
|
"createdBy": "3", |
||||||
|
"createdAt": "2023-07-15T11:30:00Z", |
||||||
|
"updatedAt": "2023-09-25T13:15:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["design", "ux", "ui"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=design" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "4", |
||||||
|
"name": "Engineering Leadership", |
||||||
|
"description": "Engineering managers and tech leads discussing team management and technical strategy", |
||||||
|
"memberCount": 6, |
||||||
|
"memberIds": ["4", "1"], |
||||||
|
"createdBy": "4", |
||||||
|
"createdAt": "2023-06-20T08:00:00Z", |
||||||
|
"updatedAt": "2023-08-30T10:20:00Z", |
||||||
|
"isPrivate": true, |
||||||
|
"tags": ["engineering", "leadership", "management"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=leadership" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "5", |
||||||
|
"name": "Business Strategy Network", |
||||||
|
"description": "Business consultants and strategists sharing market insights and methodologies", |
||||||
|
"memberCount": 10, |
||||||
|
"memberIds": ["5", "2", "6"], |
||||||
|
"createdBy": "5", |
||||||
|
"createdAt": "2023-05-05T14:00:00Z", |
||||||
|
"updatedAt": "2023-07-20T09:30:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["business", "strategy", "consulting"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=business" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "6", |
||||||
|
"name": "Digital Marketing Hub", |
||||||
|
"description": "Marketing professionals sharing campaigns, tools, and growth strategies", |
||||||
|
"memberCount": 20, |
||||||
|
"memberIds": ["6", "2", "5"], |
||||||
|
"createdBy": "6", |
||||||
|
"createdAt": "2023-04-10T12:00:00Z", |
||||||
|
"updatedAt": "2023-06-25T15:45:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["marketing", "digital", "growth"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=marketing" |
||||||
|
} |
||||||
|
] |
@ -0,0 +1,80 @@ |
|||||||
|
[ |
||||||
|
{ |
||||||
|
"id": "1", |
||||||
|
"name": "React Developers", |
||||||
|
"description": "A group for React developers to share knowledge and collaborate on projects", |
||||||
|
"memberCount": 15, |
||||||
|
"memberIds": ["1", "2", "3"], |
||||||
|
"createdBy": "1", |
||||||
|
"createdAt": "2023-08-01T10:00:00Z", |
||||||
|
"updatedAt": "2023-10-15T14:30:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["react", "frontend", "development"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=react" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "2", |
||||||
|
"name": "Product Management Circle", |
||||||
|
"description": "Product managers discussing strategies, tools, and best practices", |
||||||
|
"memberCount": 8, |
||||||
|
"memberIds": ["2", "4"], |
||||||
|
"createdBy": "2", |
||||||
|
"createdAt": "2023-09-10T09:00:00Z", |
||||||
|
"updatedAt": "2023-10-20T16:45:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["product", "management", "strategy"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=product" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "3", |
||||||
|
"name": "UX Design Collective", |
||||||
|
"description": "Designers sharing insights, resources, and feedback on user experience", |
||||||
|
"memberCount": 12, |
||||||
|
"memberIds": ["3", "6"], |
||||||
|
"createdBy": "3", |
||||||
|
"createdAt": "2023-07-15T11:30:00Z", |
||||||
|
"updatedAt": "2023-09-25T13:15:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["design", "ux", "ui"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=design" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "4", |
||||||
|
"name": "Engineering Leadership", |
||||||
|
"description": "Engineering managers and tech leads discussing team management and technical strategy", |
||||||
|
"memberCount": 6, |
||||||
|
"memberIds": ["4", "1"], |
||||||
|
"createdBy": "4", |
||||||
|
"createdAt": "2023-06-20T08:00:00Z", |
||||||
|
"updatedAt": "2023-08-30T10:20:00Z", |
||||||
|
"isPrivate": true, |
||||||
|
"tags": ["engineering", "leadership", "management"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=leadership" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "5", |
||||||
|
"name": "Business Strategy Network", |
||||||
|
"description": "Business consultants and strategists sharing market insights and methodologies", |
||||||
|
"memberCount": 10, |
||||||
|
"memberIds": ["5", "2", "6"], |
||||||
|
"createdBy": "5", |
||||||
|
"createdAt": "2023-05-05T14:00:00Z", |
||||||
|
"updatedAt": "2023-07-20T09:30:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["business", "strategy", "consulting"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=business" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"id": "6", |
||||||
|
"name": "Digital Marketing Hub", |
||||||
|
"description": "Marketing professionals sharing campaigns, tools, and growth strategies", |
||||||
|
"memberCount": 20, |
||||||
|
"memberIds": ["6", "2", "5"], |
||||||
|
"createdBy": "6", |
||||||
|
"createdAt": "2023-04-10T12:00:00Z", |
||||||
|
"updatedAt": "2023-06-25T15:45:00Z", |
||||||
|
"isPrivate": false, |
||||||
|
"tags": ["marketing", "digital", "growth"], |
||||||
|
"image": "https://i.pravatar.cc/200?img=marketing" |
||||||
|
} |
||||||
|
] |
@ -0,0 +1,243 @@ |
|||||||
|
import { useState, useEffect } from 'react'; |
||||||
|
import { useNavigate } from 'react-router-dom'; |
||||||
|
import { |
||||||
|
Typography, |
||||||
|
Box, |
||||||
|
Avatar, |
||||||
|
Chip, |
||||||
|
TextField, |
||||||
|
InputAdornment, |
||||||
|
Button, |
||||||
|
Card, |
||||||
|
CardContent, |
||||||
|
Grid, |
||||||
|
alpha, |
||||||
|
useTheme, |
||||||
|
Badge |
||||||
|
} from '@mui/material'; |
||||||
|
import { |
||||||
|
Search, |
||||||
|
Add, |
||||||
|
Group, |
||||||
|
Public, |
||||||
|
Lock, |
||||||
|
People |
||||||
|
} from '@mui/icons-material'; |
||||||
|
import { dataService } from '../services/dataService'; |
||||||
|
import type { Group as GroupType } from '../types/group'; |
||||||
|
|
||||||
|
const GroupPage = () => { |
||||||
|
const [groups, setGroups] = useState<GroupType[]>([]); |
||||||
|
const [filteredGroups, setFilteredGroups] = useState<GroupType[]>([]); |
||||||
|
const [searchQuery, setSearchQuery] = useState(''); |
||||||
|
const [isLoading, setIsLoading] = useState(true); |
||||||
|
const theme = useTheme(); |
||||||
|
const navigate = useNavigate(); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
const loadGroups = async () => { |
||||||
|
setIsLoading(true); |
||||||
|
try { |
||||||
|
const groupsData = await dataService.getGroups(); |
||||||
|
setGroups(groupsData); |
||||||
|
setFilteredGroups(groupsData); |
||||||
|
} catch (error) { |
||||||
|
console.error('Failed to load groups:', error); |
||||||
|
} finally { |
||||||
|
setIsLoading(false); |
||||||
|
} |
||||||
|
}; |
||||||
|
loadGroups(); |
||||||
|
}, []); |
||||||
|
|
||||||
|
useEffect(() => { |
||||||
|
const filtered = groups.filter(group => |
||||||
|
group.name.toLowerCase().includes(searchQuery.toLowerCase()) || |
||||||
|
group.description?.toLowerCase().includes(searchQuery.toLowerCase()) || |
||||||
|
group.tags?.some(tag => tag.toLowerCase().includes(searchQuery.toLowerCase())) |
||||||
|
); |
||||||
|
setFilteredGroups(filtered); |
||||||
|
}, [searchQuery, groups]); |
||||||
|
|
||||||
|
const handleGroupClick = (groupId: string) => { |
||||||
|
navigate(`/groups/${groupId}`); |
||||||
|
}; |
||||||
|
|
||||||
|
const handleCreateGroup = () => { |
||||||
|
// TODO: Navigate to create group page
|
||||||
|
console.log('Create group functionality coming soon'); |
||||||
|
}; |
||||||
|
|
||||||
|
const getPrivacyIcon = (isPrivate: boolean) => { |
||||||
|
return isPrivate ? ( |
||||||
|
<Lock sx={{ fontSize: 16, color: '#ff9800' }} /> |
||||||
|
) : ( |
||||||
|
<Public sx={{ fontSize: 16, color: '#4caf50' }} /> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const formatDate = (date: Date) => { |
||||||
|
return new Intl.DateTimeFormat('en-US', { |
||||||
|
year: 'numeric', |
||||||
|
month: 'short', |
||||||
|
day: 'numeric' |
||||||
|
}).format(date); |
||||||
|
}; |
||||||
|
|
||||||
|
return ( |
||||||
|
<Box sx={{ height: '100%' }}> |
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}> |
||||||
|
<Box> |
||||||
|
<Typography variant="h4" component="h1" sx={{ fontWeight: 700, mb: 1 }}> |
||||||
|
Groups |
||||||
|
</Typography> |
||||||
|
<Typography variant="body1" color="text.secondary"> |
||||||
|
Explore and manage your {filteredGroups.length} groups |
||||||
|
</Typography> |
||||||
|
</Box> |
||||||
|
<Box sx={{ display: 'flex', gap: 1 }}> |
||||||
|
<Button |
||||||
|
variant="contained" |
||||||
|
startIcon={<Add />} |
||||||
|
onClick={handleCreateGroup} |
||||||
|
sx={{ borderRadius: 2 }} |
||||||
|
> |
||||||
|
Create Group |
||||||
|
</Button> |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
|
||||||
|
<Card sx={{ mb: 3 }}> |
||||||
|
<Box sx={{ p: 3 }}> |
||||||
|
<TextField |
||||||
|
fullWidth |
||||||
|
placeholder="Search groups..." |
||||||
|
value={searchQuery} |
||||||
|
onChange={(e) => setSearchQuery(e.target.value)} |
||||||
|
InputProps={{ |
||||||
|
startAdornment: ( |
||||||
|
<InputAdornment position="start"> |
||||||
|
<Search /> |
||||||
|
</InputAdornment> |
||||||
|
), |
||||||
|
}} |
||||||
|
sx={{ mb: 3 }} |
||||||
|
/> |
||||||
|
|
||||||
|
{isLoading ? ( |
||||||
|
<Box sx={{ textAlign: 'center', py: 8 }}> |
||||||
|
<Typography variant="h6" color="text.secondary" gutterBottom> |
||||||
|
Loading groups... |
||||||
|
</Typography> |
||||||
|
<Typography variant="body2" color="text.secondary"> |
||||||
|
Please wait while we fetch your groups |
||||||
|
</Typography> |
||||||
|
</Box> |
||||||
|
) : filteredGroups.length === 0 ? ( |
||||||
|
<Box sx={{ textAlign: 'center', py: 8 }}> |
||||||
|
<Typography variant="h6" color="text.secondary" gutterBottom> |
||||||
|
{searchQuery ? 'No groups found' : 'No groups yet'} |
||||||
|
</Typography> |
||||||
|
<Typography variant="body2" color="text.secondary"> |
||||||
|
{searchQuery ? 'Try adjusting your search terms.' : 'Create your first group to get started!'} |
||||||
|
</Typography> |
||||||
|
</Box> |
||||||
|
) : ( |
||||||
|
<Grid container spacing={2}> |
||||||
|
{filteredGroups.map((group) => ( |
||||||
|
<Grid size={{ xs: 12, md: 6, lg: 4 }} key={group.id}> |
||||||
|
<Card |
||||||
|
onClick={() => handleGroupClick(group.id)} |
||||||
|
sx={{ |
||||||
|
cursor: 'pointer', |
||||||
|
transition: 'all 0.2s ease-in-out', |
||||||
|
border: 1, |
||||||
|
borderColor: 'divider', |
||||||
|
height: '100%', |
||||||
|
'&:hover': { |
||||||
|
borderColor: 'primary.main', |
||||||
|
boxShadow: theme.shadows[4], |
||||||
|
transform: 'translateY(-2px)', |
||||||
|
}, |
||||||
|
}} |
||||||
|
> |
||||||
|
<CardContent sx={{ p: 3 }}> |
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mb: 2 }}> |
||||||
|
<Avatar |
||||||
|
src={group.image} |
||||||
|
alt={group.name} |
||||||
|
sx={{ width: 48, height: 48, bgcolor: 'primary.main' }} |
||||||
|
> |
||||||
|
<Group /> |
||||||
|
</Avatar> |
||||||
|
|
||||||
|
<Box sx={{ flexGrow: 1 }}> |
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, mb: 1 }}> |
||||||
|
<Typography variant="h6" component="div" sx={{ fontWeight: 600 }}> |
||||||
|
{group.name} |
||||||
|
</Typography> |
||||||
|
{getPrivacyIcon(group.isPrivate)} |
||||||
|
</Box> |
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}> |
||||||
|
<Badge |
||||||
|
badgeContent={group.memberCount} |
||||||
|
color="primary" |
||||||
|
sx={{
|
||||||
|
'& .MuiBadge-badge': {
|
||||||
|
position: 'static',
|
||||||
|
transform: 'none', |
||||||
|
fontSize: '0.75rem', |
||||||
|
minWidth: 'auto', |
||||||
|
height: 20, |
||||||
|
borderRadius: 1 |
||||||
|
}
|
||||||
|
}} |
||||||
|
> |
||||||
|
<People sx={{ fontSize: 16, color: 'text.secondary' }} /> |
||||||
|
</Badge> |
||||||
|
<Typography variant="body2" color="text.secondary"> |
||||||
|
members |
||||||
|
</Typography> |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
</Box> |
||||||
|
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ mb: 2, minHeight: 40 }}> |
||||||
|
{group.description} |
||||||
|
</Typography> |
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap', mb: 2 }}> |
||||||
|
{group.tags?.map((tag) => ( |
||||||
|
<Chip
|
||||||
|
key={tag}
|
||||||
|
label={tag}
|
||||||
|
size="small"
|
||||||
|
variant="outlined" |
||||||
|
sx={{
|
||||||
|
borderRadius: 1, |
||||||
|
backgroundColor: alpha(theme.palette.primary.main, 0.04), |
||||||
|
borderColor: alpha(theme.palette.primary.main, 0.12), |
||||||
|
color: 'primary.main', |
||||||
|
fontWeight: 500, |
||||||
|
}} |
||||||
|
/> |
||||||
|
))} |
||||||
|
</Box> |
||||||
|
|
||||||
|
<Typography variant="caption" color="text.secondary"> |
||||||
|
Created {formatDate(group.createdAt)} |
||||||
|
</Typography> |
||||||
|
</CardContent> |
||||||
|
</Card> |
||||||
|
</Grid> |
||||||
|
))} |
||||||
|
</Grid> |
||||||
|
)} |
||||||
|
</Box> |
||||||
|
</Card> |
||||||
|
</Box> |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
export default GroupPage; |
@ -0,0 +1,20 @@ |
|||||||
|
export interface Group { |
||||||
|
id: string; |
||||||
|
name: string; |
||||||
|
description?: string; |
||||||
|
memberCount: number; |
||||||
|
memberIds: string[]; |
||||||
|
createdBy: string; |
||||||
|
createdAt: Date; |
||||||
|
updatedAt: Date; |
||||||
|
isPrivate: boolean; |
||||||
|
tags?: string[]; |
||||||
|
image?: string; |
||||||
|
} |
||||||
|
|
||||||
|
export interface GroupMember { |
||||||
|
userId: string; |
||||||
|
groupId: string; |
||||||
|
joinedAt: Date; |
||||||
|
role: 'admin' | 'member' | 'moderator'; |
||||||
|
} |
Loading…
Reference in new issue