Skip to content
This repository was archived by the owner on Feb 28, 2026. It is now read-only.

Commit 222bb91

Browse files
authored
Merge pull request #62 from WildCodeSchool/feature/congratulations-pages
congratulations pages
2 parents e6e836f + d1e9b75 commit 222bb91

16 files changed

+380
-60
lines changed

packages/backend/api/src/albums/albums.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,115 @@ import { db } from '@app/backend-shared';
44

55
const albumsRouter = Router();
66

7+
function getAlbums(userId: number) {
8+
return db
9+
.selectFrom('albums')
10+
.leftJoin('artists_hired', 'artists_hired.id', 'albums.artists_hired_id')
11+
.leftJoin('artists', 'artists.id', 'artists_hired.artists_id')
12+
.leftJoin('genres', 'genres.id', 'artists.genres_id')
13+
.leftJoin(
14+
'label_artists',
15+
'label_artists.artists_hired_id',
16+
'artists_hired.id',
17+
)
18+
.leftJoin('labels', 'labels.id', 'label_artists.label_id')
19+
.leftJoin('users', 'users.id', 'labels.users_id')
20+
.where('labels.users_id', '=', userId)
21+
.select([
22+
'albums.id',
23+
'albums.artists_hired_id',
24+
'albums.exp_value',
25+
'albums.genres_id',
26+
'albums.money_earned',
27+
'albums.name as name',
28+
'albums.notoriety_gain',
29+
'albums.sales',
30+
'albums.score',
31+
'artists.alias as artist_alias',
32+
'artists.firstname as artist_firstname',
33+
'artists.lastname as artist_lastname',
34+
'genres.name as genre_name',
35+
]);
36+
}
37+
38+
export type Album = Awaited<
39+
ReturnType<ReturnType<typeof getAlbums>['execute']>
40+
>[number];
41+
42+
albumsRouter.get('/', async (req: Request, res) => {
43+
const userId = req.userId;
44+
if (userId === undefined) {
45+
res.json({
46+
ok: false,
47+
});
48+
return;
49+
}
50+
51+
try {
52+
const albums = await getAlbums(userId).execute();
53+
54+
res.json(albums);
55+
return;
56+
} catch (error) {
57+
console.error('Error fetching albums :', error);
58+
res.status(500).json({ error: 'Internal Server Error' });
59+
}
60+
});
61+
62+
albumsRouter.get('/filter', async (req: Request, res) => {
63+
const userId = req.userId;
64+
if (userId === undefined) {
65+
res.json({
66+
ok: false,
67+
});
68+
return;
69+
}
70+
71+
try {
72+
const albums = await getAlbums(userId)
73+
.orderBy('id', 'desc')
74+
.executeTakeFirst();
75+
76+
res.json(albums);
77+
return;
78+
} catch (error) {
79+
console.error('Error fetching albums :', error);
80+
res.status(500).json({ error: 'Internal Server Error' });
81+
}
82+
});
83+
84+
async function getSinglesAlbum(albumId: number) {
85+
return db
86+
.selectFrom('singles_albums')
87+
.leftJoin('singles', 'singles.id', 'singles_albums.singles_id')
88+
.leftJoin('albums', 'albums.id', 'singles_albums.albums_id')
89+
.where('singles_albums.albums_id', '=', albumId)
90+
.select([
91+
'singles_albums.singles_id',
92+
'singles_albums.albums_id',
93+
'albums.name as album_name',
94+
'singles.name as single_name',
95+
'albums.money_earned',
96+
])
97+
.execute();
98+
}
99+
100+
export type SingleAlbum = Awaited<ReturnType<typeof getSinglesAlbum>>[number];
101+
102+
albumsRouter.get('/:albumId/singles', async (req: Request, res) => {
103+
const albumId = Number(req.params.albumId);
104+
105+
try {
106+
const singlesAlbums = await getSinglesAlbum(albumId);
107+
108+
res.json(singlesAlbums);
109+
return;
110+
} catch (error) {
111+
console.error('Error fetching singles :', error);
112+
res.status(500).json({ error: 'Internal Server Error' });
113+
}
114+
});
115+
7116
albumsRouter.post('/create', async (req: Request, res) => {
8117
const userId = req.userId;
9118
if (userId === undefined) {

packages/backend/api/src/singles/singles.ts

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,36 @@ import { db } from '@app/backend-shared';
44

55
const singlesRouter = Router();
66

7-
singlesRouter.get('/:id', async (req: Request, res) => {
8-
const singleId = Number(req.params.id);
7+
function getSingles(userId: number) {
8+
return db
9+
.selectFrom('singles')
10+
.leftJoin('artists_hired', 'singles.artists_hired_id', 'artists_hired.id')
11+
.leftJoin('artists', 'artists.id', 'artists_hired.artists_id')
12+
.leftJoin(
13+
'label_artists',
14+
'label_artists.artists_hired_id',
15+
'artists_hired.id',
16+
)
17+
.leftJoin('labels', 'labels.id', 'label_artists.label_id')
18+
.leftJoin('users', 'users.id', 'labels.users_id')
19+
.where('labels.users_id', '=', userId)
20+
.select([
21+
'singles.id',
22+
'singles.artists_hired_id',
23+
'singles.name as name',
24+
'singles.listeners',
25+
'singles.money_earned',
26+
'singles.score',
27+
'artists.firstname as artist_firstname',
28+
'artists.lastname as artist_lastname',
29+
'artists.alias as artist_alias',
30+
]);
31+
}
32+
export type Single = Awaited<
33+
ReturnType<ReturnType<typeof getSingles>['execute']>
34+
>[number];
35+
36+
singlesRouter.get('/', async (req: Request, res) => {
937
const userId = req.userId;
1038
if (userId === undefined) {
1139
res.json({
@@ -15,30 +43,7 @@ singlesRouter.get('/:id', async (req: Request, res) => {
1543
}
1644

1745
try {
18-
const singles = await db
19-
.selectFrom('singles')
20-
.leftJoin('artists_hired', 'singles.artists_hired_id', 'artists_hired.id')
21-
.leftJoin(
22-
'label_artists',
23-
'label_artists.artists_hired_id',
24-
'artists_hired.id',
25-
)
26-
.leftJoin('labels', 'labels.id', 'label_artists.label_id')
27-
.leftJoin('users', 'users.id', 'labels.users_id')
28-
.leftJoin('artists', 'artists.id', 'artists_hired.artists_id')
29-
.where('singles.id', '=', singleId)
30-
.where('labels.users_id', '=', userId)
31-
.select([
32-
'singles.artists_hired_id',
33-
'singles.name',
34-
'singles.listeners',
35-
'singles.money_earned',
36-
'singles.score',
37-
'artists.firstname as artist_firstname',
38-
'artists.lastname as artist_lastname',
39-
'artists.alias as artist_alias',
40-
])
41-
.execute();
46+
const singles = await getSingles(userId).execute();
4247

4348
res.json(singles);
4449
return;
@@ -48,7 +53,30 @@ singlesRouter.get('/:id', async (req: Request, res) => {
4853
}
4954
});
5055

51-
singlesRouter.get('/', async (req: Request, res) => {
56+
singlesRouter.get('/filter', async (req: Request, res) => {
57+
const userId = req.userId;
58+
if (userId === undefined) {
59+
res.json({
60+
ok: false,
61+
});
62+
return;
63+
}
64+
65+
try {
66+
const singles = await getSingles(userId)
67+
.orderBy('id', 'desc')
68+
.executeTakeFirst();
69+
70+
res.json(singles);
71+
return;
72+
} catch (error) {
73+
console.error('Error fetching singles:', error);
74+
res.status(500).json({ error: 'Internal Server Error' });
75+
}
76+
});
77+
78+
singlesRouter.get('/:id', async (req: Request, res) => {
79+
const singleId = Number(req.params.id);
5280
const userId = req.userId;
5381
if (userId === undefined) {
5482
res.json({
@@ -69,8 +97,9 @@ singlesRouter.get('/', async (req: Request, res) => {
6997
.leftJoin('labels', 'labels.id', 'label_artists.label_id')
7098
.leftJoin('users', 'users.id', 'labels.users_id')
7199
.leftJoin('artists', 'artists.id', 'artists_hired.artists_id')
100+
.where('singles.id', '=', singleId)
101+
.where('labels.users_id', '=', userId)
72102
.select([
73-
'singles.id as id',
74103
'singles.artists_hired_id',
75104
'singles.name',
76105
'singles.listeners',
@@ -80,7 +109,6 @@ singlesRouter.get('/', async (req: Request, res) => {
80109
'artists.lastname as artist_lastname',
81110
'artists.alias as artist_alias',
82111
])
83-
.where('labels.users_id', '=', userId)
84112
.execute();
85113

86114
res.json(singles);
@@ -90,7 +118,6 @@ singlesRouter.get('/', async (req: Request, res) => {
90118
res.status(500).json({ error: 'Internal Server Error' });
91119
}
92120
});
93-
94121
singlesRouter.post('/', async (req: Request, res) => {
95122
const { artistHiredId, singleName, genreId, price } = req.body;
96123

packages/frontend/web/src/components/add-artist.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ export default function AddArtist({ onArtistSelected }: Props) {
1111
const [isModalOpen, setIsModalOpen] = useState(false);
1212

1313
return (
14-
<div className='mt-8 flex flex-col items-center justify-center'>
14+
<div className='mt-8 flex flex-col items-center justify-center hover:scale-95'>
1515
<button
1616
type='button'
1717
onClick={() => {

packages/frontend/web/src/components/add-button.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ type ButtonProps = PropsWithChildren<{
77

88
function AddButton({ onClick, children, disabled = false }: ButtonProps) {
99
return (
10-
<div className='bg-secondary flex h-9 w-10 items-center justify-center rounded-xl shadow-[3px_5px_6px_rgba(0,0,0,0.30)] inset-ring-2 inset-ring-white'>
10+
<div className='bg-secondary flex h-9 w-10 items-center justify-center rounded-xl shadow-[3px_5px_6px_rgba(0,0,0,0.30)] inset-ring-2 inset-ring-white hover:scale-95'>
1111
<button
1212
type={'button'}
1313
onClick={onClick}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import type { Album } from '../../../../backend/api/src/albums/albums';
2+
import type { Single } from '../../../../backend/api/src/singles/singles';
3+
4+
type CongratulationsProps = {
5+
readonly items: Single | Album;
6+
readonly text: string;
7+
};
8+
9+
export default function Congratulations({ text, items }: CongratulationsProps) {
10+
return (
11+
<div className='mt-5 flex flex-col items-center'>
12+
<h1 className='text-secondary text-2xl font-bold'>
13+
{'CONGRATULATIONS!!! 🎊'}
14+
</h1>
15+
16+
<h2 className='text-secondary mt-2 text-xl'>
17+
{`${items.artist_alias ?? `${items.artist_firstname} ${items.artist_lastname}`} just made a new ${text}!`}
18+
</h2>
19+
20+
<div className='mt-10 flex flex-col items-center'>
21+
<h3 className='text-secondary text-2xl'>{items.name}</h3>
22+
<p className='text-secondary text-sm font-light'>
23+
{'by '}
24+
{items.artist_alias ??
25+
`${items.artist_firstname} ${items.artist_lastname}`}
26+
</p>
27+
<img
28+
className='mt-4 h-22 w-22'
29+
src='/assets/music-note.png'
30+
alt='single cover'
31+
/>
32+
</div>
33+
<div className='mt-6 items-center'>
34+
<div className='flex'>
35+
<h2 className='text-secondary mr-2 font-bold'>
36+
{'You just earned:'}
37+
</h2>
38+
<h3 className='text-secondary flex font-bold'>
39+
{items.money_earned}
40+
<img
41+
className='mt-1.5 h-3.5 w-4'
42+
src='/assets/dollar-icon.png'
43+
alt='dollar icon'
44+
/>
45+
</h3>
46+
</div>
47+
</div>
48+
</div>
49+
);
50+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { useNavigate } from 'react-router-dom';
2+
3+
export function GoBackToMenu() {
4+
const navigate = useNavigate();
5+
return (
6+
<div className='mb-4 flex w-full items-center justify-between'>
7+
<button
8+
onClick={async () => {
9+
await navigate('/main-menu');
10+
}}
11+
type='button'
12+
className='text-primary hover:text-secondary bg-orange mt-6 h-10 w-56 rounded-md shadow-[3px_5px_6px_rgba(0,0,0,0.30)] transition-transform hover:scale-95 active:scale-95'
13+
>
14+
{'Go Back to menu '}
15+
</button>
16+
</div>
17+
);
18+
}

packages/frontend/web/src/components/modal-my-artists.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export default function ModalMyArtists({
4141
<div className='absolute inset-0' onClick={onClose} />
4242

4343
{/* Modal Content */}
44-
<div className='bg-primary relative z-10 w-full max-w-3xl rounded-2xl p-6 shadow-xl'>
44+
<div className='bg-primary relative z-10 rounded-2xl p-6 shadow-xl'>
4545
<div className='mb-4 flex items-center justify-between'>
4646
<h2 className='text-secondary w-full text-center text-xl font-bold'>
4747
{'My Artists'}
@@ -55,7 +55,7 @@ export default function ModalMyArtists({
5555
</button>
5656
</div>
5757

58-
<div className='grid grid-cols-2 gap-4'>
58+
<div className='grid grid-cols-1 gap-4 md:grid-cols-2'>
5959
{artists.map((artist) => (
6060
<div
6161
key={artist.id}

packages/frontend/web/src/components/modal-singles.tsx

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
import { useEffect, useState } from 'react';
22

3-
export type Singles = {
4-
readonly id: number;
5-
readonly name: string;
6-
readonly score: number;
7-
readonly artists_hired_id: number;
8-
};
3+
import type { Singles } from '../../../../backend/api/src/singles/singles';
94

105
export type ModalSinglesProps = {
116
readonly isOpen: boolean;

0 commit comments

Comments
 (0)