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