Solution
Solution
.Net code
1. Create a UserActivityDto in the Application/Profiles feature that contains the Id, Title, Category and Date
properties.
namespace Application.Profiles.DTOs;
2. Create a GetUserActivities handler in the Profiles/Queries folder that returns a list of UserActivityDto. This list
should be able to be filtered by past events that user has attended, future events that user is attending and
events that user is hosting.
using System;
using Application.Core;
using Application.Profiles.DTOs;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using MediatR;
using Microsoft.EntityFrameworkCore;
using Persistence;
namespace Application.Profiles.Queries;
Solution 1
&& a.Attendees.Any(x => x.UserId == request.UserId)),
"hosting" => query.Where(a => a.Attendees.Any(x => x.IsHost
&& x.UserId == request.UserId)),
_ => query.Where(a => a.Date >= today
&& a.Attendees.Any(x => x.UserId == request.UserId))
};
return Result<List<UserActivityDto>>.Success(activities);
}
}
}
CreateMap<Activity, UserActivityDto>();
4. Create an endpoint in the ProfilesController which has the following route and also accepts a filter parameter
using Application.Profiles;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers
{
public class ProfilesController : BaseApiController
{
[HttpGet("{userId}/activities")]
public async Task<IActionResult> GetUserActivities(string userId, string filter)
{
return HandleResult(await Mediator.Send(new GetUserActivities.Query
{ UserId = userId, Filter = filter }));
}
}
}
Solution 2
}
});
return response.data
},
enabled: !!id && !!filter
});
return {
// other return elements omitted
userActivities,
loadingUserActivities,
setFilter,
filter
}
}
2. Create a ProfileActivities.tsx component in the Profiles folder that uses tabs to allow the user to select the
past, future or hosted activities that user is attending or hosting. Hint: Use a useEffect to set the initial filter
value when the component mounts.
useEffect(() => {
setFilter('future')
}, [setFilter])
const tabs = [
{ menuItem: 'Future Events', key: 'future' },
{ menuItem: 'Past Events', key: 'past' },
{ menuItem: 'Hosting', key: 'hosting' }
];
return (
<Box>
<Grid2 container spacing={2}>
<Grid2 size={12}>
<Tabs
value={activeTab}
onChange={handleTabChange}
>
{tabs.map((tab, index) => (
<Tab label={tab.menuItem} key={index} />
))}
</Tabs>
</Grid2>
Solution 3
</Grid2>
{(!userActivities || userActivities.length === 0)
&& !loadingUserActivities ? (
<Typography mt={2}>
No activities to show
</Typography>
) : null}
<Grid2
container
spacing={2}
sx={{ marginTop: 2, height: 400, overflow: 'auto' }}
>
{userActivities && userActivities.map((activity: Activity) => (
<Grid2 size={2} key={activity.id}>
<Link to={`/activities/${activity.id}`}
style={{ textDecoration: 'none' }}>
<Card elevation={4}>
<CardMedia
component="img"
height="100"
image={
`/images/categoryImages/${activity.category}.jpg
}
alt={activity.title}
sx={{ objectFit: 'cover' }}
/>
<CardContent>
<Typography variant="h6" textAlign="center" mb={1}>
{activity.title}
</Typography>
<Typography
variant="body2"
textAlign="center"
display='flex'
flexDirection='column'
>
<span>
{format(activity.date, 'do LLL yyyy')}
</span>
<span>{format(activity.date, 'h:mm a')}</span>
</Typography>
</CardContent>
</Card>
</Link>
</Grid2>
))}
</Grid2>
</Box>
)
}
const tabContent = [
{label: 'About', content: <ProfileAbout />},
{label: 'Photos', content: <ProfilePhotos />},
{label: 'Events', content: <ProfileActivities />},
{label: 'Followers', content: <ProfileFollowings activeTab={value} />},
Solution 4
{label: 'Following', content: <ProfileFollowings activeTab={value} />},
]
4. Test!
Challenge complete! Please commit your changes to GitHub. Suggested commit message “End of section 21”
Solution 5