feat(FN-207): Step 1 — add downgradeOffer i18n keys (tr + en) (+4 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged: - style(FN-207): apply Biome formatting - feat(FN-207): Steps 4-9 — add downgrade save-flow UI (helper, dialogs, plans query, handler) - feat(FN-207): Step 3 — add PATCH /subscriptions/downgrade and GET /subscriptions/plans endpoints - feat(FN-207): Step 2 — add downgrade() and getPlans() service methods - feat(FN-207): Step 1 — add downgradeOffer i18n keys (tr + en) Files changed: .../src/subscriptions/subscriptions.controller.ts | 13 ++ .../api/src/subscriptions/subscriptions.service.ts | 75 ++++++ apps/web/src/messages/en.json | 10 + apps/web/src/messages/tr.json | 10 + apps/web/src/routes/dashboard/index.tsx | 16 +- .../src/routes/dashboard/subscription/index.tsx | 255 ++++++++++++++++++--- 6 files changed, 340 insertions(+), 39 deletions(-) Fusion-Task-Id: FN-207
This commit is contained in:
@@ -49,6 +49,19 @@ export class SubscriptionsController {
|
||||
return this.subscriptionsService.resume(userId);
|
||||
}
|
||||
|
||||
@Patch("downgrade")
|
||||
async downgrade(
|
||||
@CurrentUser("id") userId: string,
|
||||
@Body() body: { planId: string; brandIds: string[] },
|
||||
) {
|
||||
return this.subscriptionsService.downgrade(userId, body);
|
||||
}
|
||||
|
||||
@Get("plans")
|
||||
async getPlans() {
|
||||
return this.subscriptionsService.getPlans();
|
||||
}
|
||||
|
||||
@Get()
|
||||
@UseGuards(RolesGuard)
|
||||
@Roles("admin")
|
||||
|
||||
@@ -308,6 +308,81 @@ export class SubscriptionsService {
|
||||
return subscription;
|
||||
}
|
||||
|
||||
async downgrade(userId: string, data: { planId: string; brandIds: string[] }) {
|
||||
// Find active subscription
|
||||
const [sub] = await this.db
|
||||
.select()
|
||||
.from(userSubscriptions)
|
||||
.where(and(eq(userSubscriptions.userId, userId), eq(userSubscriptions.status, "active")))
|
||||
.limit(1);
|
||||
|
||||
if (!sub) throw new NotFoundException("Aktif abonelik bulunamadı");
|
||||
|
||||
// Validate target plan exists
|
||||
const [targetPlan] = await this.db
|
||||
.select()
|
||||
.from(plans)
|
||||
.where(eq(plans.id, data.planId))
|
||||
.limit(1);
|
||||
|
||||
if (!targetPlan) throw new NotFoundException("Plan bulunamadı");
|
||||
|
||||
// Validate brand count matches plan
|
||||
if (targetPlan.brandCount > 0 && data.brandIds.length !== targetPlan.brandCount) {
|
||||
throw new BadRequestException(`Bu plan tam olarak ${targetPlan.brandCount} marka gerektirir`);
|
||||
}
|
||||
|
||||
// Update subscription plan
|
||||
const [updated] = await this.db
|
||||
.update(userSubscriptions)
|
||||
.set({ planId: data.planId, updatedAt: new Date() })
|
||||
.where(eq(userSubscriptions.id, sub.id))
|
||||
.returning();
|
||||
|
||||
// Replace brand associations
|
||||
await this.db.delete(userBrands).where(eq(userBrands.subscriptionId, sub.id));
|
||||
|
||||
if (data.brandIds.length > 0) {
|
||||
await this.db.insert(userBrands).values(
|
||||
data.brandIds.map((brandId) => ({
|
||||
userId,
|
||||
subscriptionId: sub.id,
|
||||
brandId,
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
return updated;
|
||||
}
|
||||
|
||||
async getPlans() {
|
||||
const rows = await this.db
|
||||
.select({
|
||||
id: plans.id,
|
||||
name: plans.name,
|
||||
brandCount: plans.brandCount,
|
||||
priceMonthly: plans.priceMonthly,
|
||||
priceYearly: plans.priceYearly,
|
||||
})
|
||||
.from(plans)
|
||||
.where(eq(plans.isActive, true));
|
||||
|
||||
// Map DB name to frontend key
|
||||
const nameToKey: Record<string, string> = {
|
||||
"1 Marka": "brand1",
|
||||
"2 Marka": "brand2",
|
||||
"3 Marka": "brand3",
|
||||
"Full Paket": "full",
|
||||
};
|
||||
|
||||
return rows
|
||||
.filter((p) => nameToKey[p.name])
|
||||
.map((p) => ({
|
||||
...p,
|
||||
key: nameToKey[p.name],
|
||||
}));
|
||||
}
|
||||
|
||||
async findAll(page = 1, limit = 20) {
|
||||
const offset = (page - 1) * limit;
|
||||
const items = await this.db
|
||||
|
||||
Reference in New Issue
Block a user