Skip to content

Commit 1630764

Browse files
committed
feat(cards): implement add platform link to cards flow
1 parent d48e1ff commit 1630764

3 files changed

Lines changed: 109 additions & 3 deletions

File tree

apps/backend/src/routes/cards.ts

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as cardService from '../services/cardService'
22
import { handleDbError } from '../utils/error.util.js';
3-
import { createCardSchema ,updateCardSchema} from '../validations/card.validation';
3+
import { createCardSchema ,updateCardSchema, addPlatformLinkSchema} from '../validations/card.validation';
44

55
import type { CardResponse } from '../services/cardService';
66
import type { Card } from '@devcard/shared';
@@ -89,7 +89,6 @@ export async function cardRoutes(app: FastifyInstance): Promise<void> {
8989
});
9090

9191
// ─── Update Card ───
92-
9392
app.put('/:id', async (request: FastifyRequest<{ Params: CardParams; Body: UpdateCardBody }>, reply: FastifyReply): Promise<CardResponse> => {
9493
const userId = (request.user as { id: string }).id;
9594
const { id } = request.params;
@@ -143,4 +142,44 @@ export async function cardRoutes(app: FastifyInstance): Promise<void> {
143142
return handleDbError(error, request, reply)
144143
}
145144
});
145+
146+
//Add platform-link
147+
app.put('/:id/platform-link', async(request: FastifyRequest<{Params:{id: string}, Body: {platformLinkId: string}}>, reply: FastifyReply) => {
148+
const cardId = request.params.id;
149+
const userId = request.user.id;
150+
const parsed = addPlatformLinkSchema.safeParse(request.body);
151+
152+
if (!parsed.success) {
153+
return reply.status(400).send({ error: 'Validation failed', details: parsed.error.flatten() });
154+
}
155+
156+
try {
157+
158+
const platformLinkId = parsed.data.platformLinkId
159+
await cardService.addPlatFormLinks(app, userId, cardId, platformLinkId)
160+
161+
return reply.status(200).send('Platform link added successfully')
162+
163+
} catch (error: any) {
164+
if (error?.code === 'CARD_NOT_FOUND') {
165+
return reply.status(404).send({
166+
error: error.message,
167+
});
168+
}
169+
170+
if (error?.code === 'PLATFORM_LINK_NOT_FOUND') {
171+
return reply.status(403).send({
172+
error: error.message,
173+
});
174+
}
175+
176+
if (error?.code === 'LINK_ALREADY_EXISTS') {
177+
return reply.status(409).send({
178+
error: error.message,
179+
});
180+
}
181+
app.log.error(error)
182+
handleDbError(error, request, reply)
183+
}
184+
})
146185
}

apps/backend/src/services/cardService.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ import { CardVisibility, type Prisma } from '@prisma/client';
22

33
import { generateUniqueSlug } from '../utils/slug';
44

5-
import type { FastifyInstance } from 'fastify';
65
import type { CreateCardBody } from '../routes/cards';
6+
import type { FastifyInstance } from 'fastify';
77

88
type CardLinkResponse = { platformLink: unknown };
99
type RawCard = { id: string; title: string; isDefault: boolean; cardLinks: CardLinkResponse[] };
1010
export type CardResponse = { id: string; title: string; isDefault: boolean; links: unknown[] };
1111

12+
1213
function mapCard(card: RawCard): CardResponse {
1314
return {
1415
id: card.id,
@@ -18,6 +19,7 @@ function mapCard(card: RawCard): CardResponse {
1819
};
1920
}
2021

22+
//List card service
2123
export async function listCards(app: FastifyInstance, userId: string): Promise<CardResponse[]> {
2224
const cards = (await app.prisma.card.findMany({
2325
where: { userId },
@@ -29,6 +31,7 @@ export async function listCards(app: FastifyInstance, userId: string): Promise<C
2931
return cards.map(mapCard);
3032
}
3133

34+
//Creates card service
3235
export async function createCard(app: FastifyInstance, userId: string, body: CreateCardBody): Promise<CardResponse> {
3336
const {title , description , linkIds , visibility} = body
3437

@@ -97,6 +100,7 @@ export async function createCard(app: FastifyInstance, userId: string, body: Cre
97100
throw new Error('Failed to create card after retrying serialization conflicts');
98101
}
99102

103+
//Update card service
100104
export async function updateCard(
101105
app: FastifyInstance,
102106
userId: string,
@@ -147,6 +151,7 @@ export async function updateCard(
147151
return mapCard(updated);
148152
}
149153

154+
//Delete card service
150155
export async function deleteCard(app: FastifyInstance, userId: string, id: string): Promise<null> {
151156
return await app.prisma.$transaction(async (tx: Prisma.TransactionClient) => {
152157
const existing = await tx.card.findFirst({ where: { id, userId } });
@@ -175,6 +180,7 @@ export async function deleteCard(app: FastifyInstance, userId: string, id: strin
175180
});
176181
}
177182

183+
//Set default card service
178184
export async function setDefaultCard(app: FastifyInstance, userId: string, id: string): Promise<{ message: string } | null> {
179185
const existing = await app.prisma.card.findFirst({ where: { id, userId } });
180186
if (!existing) {
@@ -188,3 +194,58 @@ export async function setDefaultCard(app: FastifyInstance, userId: string, id: s
188194

189195
return { message: 'Default card updated' };
190196
}
197+
198+
//Adds platfrom link
199+
export async function addPlatFormLinks(app: FastifyInstance, userId: string, id:string, platformLinkId: string){
200+
const ownedCard = await app.prisma.card.findFirst({
201+
where: {
202+
id,
203+
userId
204+
}
205+
})
206+
207+
if (!ownedCard) {
208+
throw Object.assign(
209+
new Error('Card not found or you do not have permission to modify it'),
210+
{ code: 'CARD_NOT_FOUND' }
211+
);
212+
}
213+
const [existingLink, platformLink] = await Promise.all([
214+
app.prisma.cardLink.findUnique({
215+
where: {
216+
cardId_platformLinkId: {
217+
cardId: id,
218+
platformLinkId,
219+
},
220+
},
221+
}),
222+
223+
app.prisma.platformLink.findFirst({
224+
where: {
225+
id: platformLinkId,
226+
userId,
227+
},
228+
}),
229+
]);
230+
231+
if (!platformLink) {
232+
throw Object.assign(
233+
new Error('Platform link not found or does not belong to your account'),
234+
{ code: 'PLATFORM_LINK_NOT_FOUND' }
235+
);
236+
}
237+
238+
if (existingLink) {
239+
throw Object.assign(
240+
new Error('This platform link has already been added to the card'),
241+
{ code: 'LINK_ALREADY_EXISTS' }
242+
);
243+
}
244+
245+
await app.prisma.cardLink.create({
246+
data: {
247+
cardId: id,
248+
platformLinkId
249+
}
250+
})
251+
}

apps/backend/src/validations/card.validation.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,9 @@ export const updateCardSchema = z.object({
2323
title: z.string().min(1).max(100).optional(),
2424
linkIds: z.array(z.string().uuid()).optional(),
2525
});
26+
27+
export const addPlatformLinkSchema = z.object({
28+
platformLinkId: z.string().uuid({
29+
message: 'Invalid platform link ID',
30+
}),
31+
});

0 commit comments

Comments
 (0)