Files
goodgo-platform/apps/web/lib/subscription-api.ts
Ho Ngoc Hai 54670b4bd4
Some checks failed
CI / Lint → Typecheck → Test → Build (22) (push) Failing after 6s
CI / E2E Tests (push) Has been skipped
CodeQL Analysis / CodeQL (javascript-typescript) (push) Failing after 35s
Deploy / Build AI Services Image (push) Failing after 6s
CI / AI Services (Python) — Smoke (push) Failing after 5s
Deploy / Build API Image (push) Failing after 5s
Deploy / Build Web Image (push) Failing after 5s
E2E Tests / Playwright E2E (push) Failing after 18s
Security Scanning / Dependency Audit (pnpm) (push) Failing after 4s
Security Scanning / Trivy Scan — API Image (push) Failing after 43s
Security Scanning / Trivy Scan — Web Image (push) Failing after 38s
Security Scanning / Trivy Scan — AI Services Image (push) Failing after 44s
Security Scanning / Trivy Filesystem Scan (push) Failing after 36s
Deploy / Deploy to Staging (push) Has been skipped
Deploy / Deploy to Production (push) Has been skipped
Deploy / Smoke Test Production (push) Has been skipped
Security Scanning / Security Gate (push) Failing after 1s
Deploy / Rollback Staging (push) Has been skipped
Deploy / Smoke Test Staging (push) Has been skipped
Deploy / Rollback Production (push) Has been skipped
fix(web): handle null maxListings/maxSavedSearches on ENTERPRISE plan
Seed stores null (not -1) for unlimited quotas on the ENTERPRISE tier.
PlanDto now types these as `number | null`. PricingPage treats null the
same as -1 — both render 'Không giới hạn' instead of 'null tin đăng'.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 16:53:07 +07:00

81 lines
1.9 KiB
TypeScript

import { apiClient } from './api-client';
export interface PlanDto {
id: string;
tier: string;
name: string;
priceMonthlyVND: string;
priceYearlyVND: string;
maxListings: number | null; // null = unlimited (ENTERPRISE tier)
maxSavedSearches: number | null;
features: Record<string, boolean | number | string>;
isActive: boolean;
}
export interface SubscriptionInfo {
id: string;
planTier: string;
status: string;
currentPeriodStart: string;
currentPeriodEnd: string;
cancelledAt: string | null;
createdAt: string;
}
export interface BillingHistoryDto {
subscription: SubscriptionInfo | null;
payments: Array<{
id: string;
provider: string;
type: string;
amountVND: string;
status: string;
createdAt: string;
}>;
total: number;
}
export interface QuotaCheckResult {
metric: string;
used: number;
limit: number;
remaining: number;
}
export interface CreateSubscriptionResult {
subscriptionId: string;
planTier: string;
status: string;
currentPeriodStart: string;
currentPeriodEnd: string;
}
export const subscriptionApi = {
getPlans: () => apiClient.get<PlanDto[]>('/subscriptions/plans'),
getPlanByTier: (tier: string) =>
apiClient.get<PlanDto>(`/subscriptions/plans/${tier}`),
getBillingHistory: (limit = 20, offset = 0) =>
apiClient.get<BillingHistoryDto>(
`/subscriptions/billing?limit=${limit}&offset=${offset}`,
),
checkQuota: (metric: string) =>
apiClient.get<QuotaCheckResult>(`/subscriptions/quota/${metric}`),
createSubscription: (planTier: string, billingCycle: 'monthly' | 'yearly') =>
apiClient.post<CreateSubscriptionResult>('/subscriptions', {
planTier,
billingCycle,
}),
upgradeSubscription: (newPlanTier: string) =>
apiClient.post<{ message: string }>('/subscriptions/upgrade', {
newPlanTier,
}),
cancelSubscription: (_reason: string) =>
apiClient.delete<{ message: string }>('/subscriptions'),
};