fix(subscription): plan comparison + checkout read the live plan catalog
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m27s
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m10s
CI/CD / CI · Admin Web (tsc) (push) Successful in 38s
CI/CD / CI · Website (tsc) (push) Successful in 47s
CI/CD / CI · Koja (tsc) (push) Successful in 50s
CI/CD / Deploy · all services (push) Successful in 3m27s
The merchant plan page hard-coded 4 tiers, prices and a feature matrix that drifted from the admin-editable platform catalog (Starter tier missing, stale prices/features). PlanComparison and CheckoutScreen now consume /platform/plans + new /platform/features-catalog: - columns = active plans by SortOrder (incl. Starter), names from DisplayNameFa/En, prices from MonthlyPriceToman - limit rows from PlanLimitsData (int.MaxValue → "نامحدود") - feature rows from the feature catalog, ticked via FeatureKeys - checkout validates the ?plan= param against isBillableOnline and prices from the catalog — no more client-side price constants fa/en/ar limit-row labels added. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { apiGet } from "@/lib/api/client";
|
||||
|
||||
/** Backend sends int.MaxValue for "no limit". */
|
||||
export const UNLIMITED = 2147483647;
|
||||
|
||||
export interface PlanLimits {
|
||||
maxOrdersPerDay: number;
|
||||
maxTables: number;
|
||||
maxTerminals: number;
|
||||
maxCustomers: number;
|
||||
maxSmsPerMonth: number;
|
||||
maxBranches: number;
|
||||
maxReportHistoryDays: number;
|
||||
maxMenuCategories: number;
|
||||
maxMenuItems: number;
|
||||
maxMenuAi3dPerMonth: number;
|
||||
}
|
||||
|
||||
export interface PlatformPlan {
|
||||
tier: string;
|
||||
displayNameFa: string;
|
||||
displayNameEn?: string | null;
|
||||
monthlyPriceToman: number;
|
||||
isBillableOnline: boolean;
|
||||
isActive: boolean;
|
||||
sortOrder: number;
|
||||
limits: PlanLimits;
|
||||
featureKeys: string[];
|
||||
}
|
||||
|
||||
export interface PlatformFeature {
|
||||
id: string;
|
||||
key: string;
|
||||
displayNameFa: string;
|
||||
displayNameEn?: string | null;
|
||||
moduleGroup: string;
|
||||
isEnabledGlobally: boolean;
|
||||
}
|
||||
|
||||
/** Live, admin-editable plan matrix — the single source of truth for plan
|
||||
* names, prices, limits, and included features. */
|
||||
export function usePlatformPlans(cafeId?: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ["platform-plans", cafeId],
|
||||
queryFn: async () => {
|
||||
const plans = await apiGet<PlatformPlan[]>(`/api/cafes/${cafeId}/platform/plans`);
|
||||
return plans
|
||||
.filter((p) => p.isActive)
|
||||
.sort((a, b) => a.sortOrder - b.sortOrder);
|
||||
},
|
||||
enabled: !!cafeId,
|
||||
staleTime: 5 * 60_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function usePlatformFeaturesCatalog(cafeId?: string | null) {
|
||||
return useQuery({
|
||||
queryKey: ["platform-features-catalog", cafeId],
|
||||
queryFn: () =>
|
||||
apiGet<PlatformFeature[]>(`/api/cafes/${cafeId}/platform/features-catalog`),
|
||||
enabled: !!cafeId,
|
||||
staleTime: 5 * 60_000,
|
||||
});
|
||||
}
|
||||
|
||||
export function planDisplayName(plan: PlatformPlan, locale: string): string {
|
||||
if (locale === "en" && plan.displayNameEn) return plan.displayNameEn;
|
||||
return plan.displayNameFa;
|
||||
}
|
||||
|
||||
export function featureDisplayName(feature: PlatformFeature, locale: string): string {
|
||||
if (locale === "en" && feature.displayNameEn) return feature.displayNameEn;
|
||||
return feature.displayNameFa;
|
||||
}
|
||||
Reference in New Issue
Block a user