Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(profile): show created events #42

Merged
merged 2 commits into from
Dec 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 24 additions & 2 deletions apps/server/src/core/profile/profile.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,24 @@ import {
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { EditProfileDTO, GetMlbbAccountDTO } from '@v6/dto';
import {
EditProfileDTO,
GetMlbbAccountDTO,
GetRegisteredEvents,
} from '@v6/dto';

import { SupabaseGuard } from '~/core/auth/supabase/supabase.guard';
import { AuthUser } from '~/core/auth/types';
import { User } from '~/core/auth/user.decorator';
import { PaginationService } from '../pagination/pagination.service';
import { ProfileService } from './profile.service';

@Controller('profiles')
export class ProfileController {
constructor(private readonly profileService: ProfileService) {}
constructor(
private readonly profileService: ProfileService,
private readonly paginationService: PaginationService,
) {}

@Get()
@UseGuards(SupabaseGuard)
Expand Down Expand Up @@ -71,4 +79,18 @@ export class ProfileController {

return mlbbAccountUsername;
}

@Get('/event-registrations')
@UseGuards(SupabaseGuard)
public async getUserRegisteredEvents(
@User() user: AuthUser,
@Query() query: GetRegisteredEvents,
) {
const { count, records } =
await this.profileService.getUserRegisteredEvents(user.sub, query);

return await this.paginationService.buildPaginationResponse(records, {
count,
});
}
}
8 changes: 7 additions & 1 deletion apps/server/src/core/profile/profile.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Module } from '@nestjs/common';
import { config } from '~/config';
import { PrismaService } from '~/lib/prisma.service';
import { SupabaseService } from '~/lib/supabase.service';
import { PaginationService } from '../pagination/pagination.service';
import { ProfileController } from './profile.controller';
import { ProfileService } from './profile.service';

Expand All @@ -14,6 +15,11 @@ import { ProfileService } from './profile.service';
}),
],
controllers: [ProfileController],
providers: [ProfileService, PrismaService, SupabaseService],
providers: [
ProfileService,
PrismaService,
SupabaseService,
PaginationService,
],
})
export class ProfileModule {}
42 changes: 41 additions & 1 deletion apps/server/src/core/profile/profile.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpService } from '@nestjs/axios';
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { MlbbRole, Prisma } from '@prisma/client';
import { EditProfileDTO } from '@v6/dto';
import { EditProfileDTO, GetRegisteredEvents } from '@v6/dto';

import { config } from '~/config';
import { PrismaService } from '~/lib/prisma.service';
Expand Down Expand Up @@ -101,4 +101,44 @@ export class ProfileService {

return data.data?.username as string;
}

public async getUserRegisteredEvents(
userId: string,
query: GetRegisteredEvents,
) {
const { limit = 10, page = 1 } = query;

const paginationParams: Pick<Prisma.EventCountArgs, 'skip' | 'take'> = {
skip: (page - 1) * limit,
take: limit,
};

const [records, count] = await this.prismaService.$transaction([
this.prismaService.eventRegistration.findMany({
...paginationParams,
where: {
profileUserId: userId,
},
include: {
event: {
include: {
creator: true,
_count: {
select: {
EventRegistration: true,
},
},
},
},
},
}),
this.prismaService.eventRegistration.count({
where: {
profileUserId: userId,
},
}),
]);

return { records, count };
}
}
26 changes: 26 additions & 0 deletions apps/server/src/core/profile/test/fixtures.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { faker } from '@faker-js/faker';
import { Prisma } from '@prisma/client';
import { addMonths } from 'date-fns';

export const profileSeeder: Prisma.ProfileCreateInput = {
userId: '36d23b5f-241d-40b0-96c0-065a6b2a6638',
displayName: faker.person.fullName(),
mlbbUserId: faker.string.numeric({ length: 8 }),
mlbbServerId: faker.string.numeric({ length: 4 }),
// mlbbUsername: 'testing 123',
};

export const eventSeeder: Prisma.EventCreateInput = {
description: 'description',
startRegistrationDate: new Date(),
endRegistrationDate: addMonths(new Date(), 1),
title: 'Event 1',
creator: {
connectOrCreate: {
where: { userId: profileSeeder.userId },
create: {
...profileSeeder,
},
},
},
};
53 changes: 44 additions & 9 deletions apps/server/src/core/profile/test/profile.service.int-spec.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,47 @@
import { HttpModule } from '@nestjs/axios';
import { Test, TestingModule } from '@nestjs/testing';
import { MlbbRole, Profile } from '@prisma/client';

import { config } from '~/config';
import { PrismaService } from '~/lib/prisma.service';
import { SupabaseService } from '~/lib/supabase.service';
import { ProfileModule } from '../profile.module';
import { ProfileService } from '../profile.service';
import { eventSeeder, profileSeeder } from './fixtures';

describe('ProfileService', () => {
let profileService: ProfileService;
let prismaService: PrismaService;
let profile: Profile;

beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
HttpModule.register({
baseURL: config.apiGamesBaseUrl,
}),
],
providers: [ProfileService, PrismaService, SupabaseService],
imports: [ProfileModule],
}).compile();

profileService = module.get<ProfileService>(ProfileService);
prismaService = module.get<PrismaService>(PrismaService);

profile = await prismaService.profile.create({
data: profileSeeder,
});

const event = await prismaService.event.create({
data: eventSeeder,
});

await prismaService.eventRegistration.create({
data: {
role: MlbbRole.GOLD,
event: {
connect: {
id: event.id,
},
},
player: {
connect: {
userId: profile.userId,
},
},
},
});
});

afterAll(async () => {
Expand All @@ -31,4 +51,19 @@ describe('ProfileService', () => {
it('should be defined', () => {
expect(profileService).toBeDefined();
});

describe('getUserRegisteredEvents', () => {
it.todo(
'should return a list of events the user has registered in',
async () => {
const registeredEvents = await profileService.getUserRegisteredEvents(
profile.userId,
{},
);

expect(registeredEvents.records.length).toBe(1);
expect(registeredEvents.count).toBe(1);
},
);
});
});
29 changes: 29 additions & 0 deletions apps/web/src/features/profile/components/RegisteredEventsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { useGetRegisteredEventsQuery } from '@v6/api';

import { EventCard } from '~/features/events/components';

export const RegisteredEventsList = () => {
const { data } = useGetRegisteredEventsQuery({
query: {},
});

return (
<div>
<h1>Registered Events List</h1>
<div className="grid grid-cols-1 gap-12 md:gap-6 xl:grid-cols-2">
{data?.records.map((val) => {
return (
<EventCard
username={val.event.creator.mlbbUsername as string}
profilePictureUrl={val.event.creator.profilePictureUrl as string}
playersJoined={val.event._count.EventRegistration}
displayName={val.event.creator.displayName as string}
{...val.event}
key={val.eventId}
/>
);
})}
</div>
</div>
);
};
1 change: 1 addition & 0 deletions apps/web/src/features/profile/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './ProfileInfoItem';
export * from './EditProfileFormInner';
export * from './ProfileDisplaySection';
export * from './RegisteredEventsList';
4 changes: 3 additions & 1 deletion apps/web/src/pages/profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { HeadMetaData } from '~/components/meta/HeadMetaData';
import {
EditProfileFormInner,
ProfileDisplaySection,
RegisteredEventsList,
} from '~/features/profile/components';
import { EditProfileFormSchema } from '~/features/profile/forms/edit-profile';
import { queryClient } from '~/lib/react-query';
Expand Down Expand Up @@ -42,7 +43,7 @@ const ProfilePage = () => {
return (
<AuthenticatedRoute>
<HeadMetaData title="Profile" />
<main className="container min-h-screen max-w-screen-md">
<main className="container flex min-h-screen max-w-screen-md flex-col gap-8 lg:gap-10">
{isEditMode ? (
<>
<EditProfileFormInner
Expand All @@ -53,6 +54,7 @@ const ProfilePage = () => {
) : (
<ProfileDisplaySection onEditProfile={() => setIsEditMode(true)} />
)}
<RegisteredEventsList />
</main>
</AuthenticatedRoute>
);
Expand Down
69 changes: 69 additions & 0 deletions packages/api/src/handlers/profile/getRegisteredEvents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { useQuery, UseQueryOptions } from '@tanstack/react-query';
import { Prisma } from '@v6/db';
import { GetRegisteredEvents } from '@v6/dto';
import defaultAxios, { AxiosPromise } from 'axios';
import { SetOptional } from 'type-fest';

import { ApiFn, ExtractFnReturnType, QueryConfig } from '../../lib/react-query';
import { useApiClient } from '../../providers';
import { PageableResponse } from '../../types/pagination';

const userRegisteredEvents =
Prisma.validator<Prisma.EventRegistrationDefaultArgs>()({
include: {
event: {
include: {
creator: true,
_count: {
select: {
EventRegistration: true,
},
},
},
},
},
});

export type UserRegisteredEvents = Prisma.EventRegistrationGetPayload<
typeof userRegisteredEvents
>;

export const getRegisteredEvents: ApiFn<
GetRegisteredEvents,
AxiosPromise<PageableResponse<UserRegisteredEvents>>
> = ({ limit = 10, page = 1 }, { axios = defaultAxios }) => {
return axios.get('/profiles/event-registrations', {
params: {
limit,
page,
},
});
};

type UseGetRegisteredEventsOptions = {
options?: SetOptional<
UseQueryOptions<
unknown,
unknown,
PageableResponse<UserRegisteredEvents>,
any[]
>,
'queryKey'
>;
query: GetRegisteredEvents;
};

export const useGetRegisteredEventsQuery = ({
options,
query,
}: UseGetRegisteredEventsOptions) => {
const { axios, api } = useApiClient();

return useQuery({
queryKey: ['profile', 'registered-events'],
queryFn: async () => {
return await api(getRegisteredEvents(query, { axios }));
},
...options,
});
};
1 change: 1 addition & 0 deletions packages/api/src/handlers/profile/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './getProfile';
export * from './editProfile';
export * from './getMlbbAccountUsername';
export * from './getRegisteredEvents';
3 changes: 3 additions & 0 deletions packages/dto/src/profile/get-registered-events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Paginable } from '../pagination';

export class GetRegisteredEvents extends Paginable {}
1 change: 1 addition & 0 deletions packages/dto/src/profile/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './edit-profile.dto';
export * from './get-mlbb-account.dto';
export * from './get-registered-events';
Loading