diff --git a/src/app/pricing/page.tsx b/src/app/pricing/page.tsx
new file mode 100644
index 0000000..a3a0b56
--- /dev/null
+++ b/src/app/pricing/page.tsx
@@ -0,0 +1,16 @@
+import React from 'react';
+import { Metadata } from 'next';
+import PricingTable from '@/components/pricing/PricingTable';
+
+export const metadata: Metadata = {
+ title: 'Pricing | StrellerMinds',
+ description: 'Choose the best plan for your learning journey.',
+};
+
+export default function PricingPage() {
+ return (
+
+
+
+ );
+}
diff --git a/src/components/pricing/PricingTable.tsx b/src/components/pricing/PricingTable.tsx
new file mode 100644
index 0000000..79e22c9
--- /dev/null
+++ b/src/components/pricing/PricingTable.tsx
@@ -0,0 +1,166 @@
+import React, { useState } from 'react';
+import { Check, X } from 'lucide-react';
+import { usePricing } from './usePricing';
+
+interface PricingTier {
+ name: string;
+ description: string;
+ monthlyPrice: number;
+ annualPrice: number;
+ popular: boolean;
+ features: { name: string; included: boolean }[];
+ ctaText: string;
+}
+
+const tiers: PricingTier[] = [
+ {
+ name: 'Basic',
+ description: 'Essential features for individuals and beginners.',
+ monthlyPrice: 9.99,
+ annualPrice: 99,
+ popular: false,
+ features: [
+ { name: 'Access to all basic courses', included: true },
+ { name: 'Community forum access', included: true },
+ { name: 'Email support', included: true },
+ { name: 'Certificate of completion', included: false },
+ { name: '1-on-1 mentorship', included: false },
+ ],
+ ctaText: 'Get Started',
+ },
+ {
+ name: 'Pro',
+ description: 'Perfect for professionals looking to advance their career.',
+ monthlyPrice: 29.99,
+ annualPrice: 299,
+ popular: true,
+ features: [
+ { name: 'Access to all basic courses', included: true },
+ { name: 'Community forum access', included: true },
+ { name: 'Priority email support', included: true },
+ { name: 'Certificate of completion', included: true },
+ { name: '1-on-1 mentorship', included: false },
+ ],
+ ctaText: 'Start Free Trial',
+ },
+ {
+ name: 'Enterprise',
+ description: 'Advanced features for teams and organizations.',
+ monthlyPrice: 99.99,
+ annualPrice: 999,
+ popular: false,
+ features: [
+ { name: 'Access to all basic courses', included: true },
+ { name: 'Community forum access', included: true },
+ { name: '24/7 Phone & Email support', included: true },
+ { name: 'Certificate of completion', included: true },
+ { name: '1-on-1 mentorship', included: true },
+ ],
+ ctaText: 'Contact Sales',
+ },
+];
+
+export const PricingTable: React.FC = () => {
+ const [isAnnual, setIsAnnual] = useState(true);
+
+ // Example of using dynamic pricing data hook if applicable.
+ // In a real scenario, this hook might override the default prices.
+ const { price, loading } = usePricing({ country: 'US', currency: 'USD' });
+
+ return (
+
+
+
+
+ Simple, transparent pricing
+
+
+ Choose the plan that best fits your needs. No hidden fees.
+
+
+
+
+ Monthly
+
+
+ Annually (Save 20%)
+
+
+
+
+ {tiers.map((tier) => (
+
+
+ {tier.popular && (
+
+ Most Popular
+
+ )}
+
{tier.name}
+
{tier.description}
+
+
+
+
+ ${isAnnual ? tier.annualPrice : tier.monthlyPrice}
+
+
+ /{isAnnual ? 'year' : 'month'}
+
+
+
+
+ {tier.features.map((feature, index) => (
+ -
+ {feature.included ? (
+
+ ) : (
+
+ )}
+
+ {feature.name}
+
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default PricingTable;
diff --git a/src/components/pricing/usePricing.ts b/src/components/pricing/usePricing.ts
index 5d1ce6c..ffd93eb 100644
--- a/src/components/pricing/usePricing.ts
+++ b/src/components/pricing/usePricing.ts
@@ -1,15 +1,36 @@
// usePricing.ts
import { useEffect, useState } from "react";
-import { fetchPrice } from "../services/pricing.api";
-export function usePricing({ country, currency }) {
+// Mock implementation of fetchPrice since pricing.api does not exist
+const fetchPrice = async (
+ params: { country: string; currency: string; options: any; promoCode: string },
+ options: { signal: AbortSignal }
+) => {
+ return new Promise((resolve, reject) => {
+ const timeout = setTimeout(() => {
+ if (options.signal.aborted) {
+ reject(new Error("Aborted"));
+ } else {
+ resolve({ finalPrice: params.options.quantity * 10, currency: params.currency });
+ }
+ }, 1000);
+ options.signal.addEventListener("abort", () => clearTimeout(timeout));
+ });
+};
+
+interface UsePricingProps {
+ country: string;
+ currency: string;
+}
+
+export function usePricing({ country, currency }: UsePricingProps) {
const [options, setOptions] = useState({
quantity: 1,
premium: false,
});
const [promoCode, setPromoCode] = useState("");
- const [price, setPrice] = useState(null);
+ const [price, setPrice] = useState(null);
const [loading, setLoading] = useState(false);
useEffect(() => {
@@ -23,9 +44,10 @@ export function usePricing({ country, currency }) {
{ signal: controller.signal }
);
setPrice(data);
- } catch (err) {
+ } catch (err: any) {
+ if (err.name === 'AbortError' || err.message === 'Aborted') return;
// fallback handling
- setPrice((prev) => prev || { finalPrice: 0, currency });
+ setPrice((prev: any) => prev || { finalPrice: 0, currency });
} finally {
setLoading(false);
}