You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

289 lines
9.3 KiB

"use client";
import { useEffect, useState } from "react";
import { useAuth } from "@/lib/auth-context";
import { useRouter } from "next/navigation";
import { Button } from "@/components/ui/button";
interface Subscription {
id: string;
tier: string;
status: string;
storage_limit_gb: number;
current_usage_gb: number;
created_at: string;
}
export default function SubscriptionsPage() {
const { isAuthenticated, user, logout, token } = useAuth();
const router = useRouter();
const [subscription, setSubscription] = useState<Subscription | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [authInitialized, setAuthInitialized] = useState(false);
// Wait for auth to initialize from localStorage
useEffect(() => {
// Small delay to let auth context initialize
const timer = setTimeout(() => {
setAuthInitialized(true);
}, 100);
return () => clearTimeout(timer);
}, []);
useEffect(() => {
if (!authInitialized) return;
// Check if user is authenticated
if (user && token) {
// User is authenticated, fetch subscription
fetchSubscription();
} else {
// Not authenticated, redirect to login
router.push("/login?redirect=/subscriptions");
}
}, [authInitialized, user, token, router]);
const fetchSubscription = async () => {
if (!user?.id) return;
setLoading(true);
try {
const response = await fetch("/api/subscriptions/me", {
headers: {
"user-id": user.id,
},
});
if (response.ok) {
const data = await response.json();
setSubscription(data.subscription);
} else {
setError("Failed to fetch subscription data");
}
} catch (err) {
setError("Error loading subscription");
} finally {
setLoading(false);
}
};
const handleCancelSubscription = async () => {
if (!confirm("Are you sure you want to cancel your subscription?")) {
return;
}
try {
const response = await fetch("/api/subscriptions/cancel", {
method: "POST",
headers: {
"user-id": user?.id || "",
},
});
if (response.ok) {
alert("Subscription cancelled successfully");
fetchSubscription(); // Refresh data
} else {
const data = await response.json();
alert(data.error || "Failed to cancel subscription");
}
} catch (err) {
alert("Error cancelling subscription");
}
};
// Don't show content until auth has initialized
if (!authInitialized) {
return (
<div className="min-h-screen flex items-center justify-center">
Loading...
</div>
);
}
if (!isAuthenticated) {
return null; // Will redirect to login
}
if (loading) {
return (
<div className="min-h-screen flex items-center justify-center">
Loading subscription...
</div>
);
}
if (error) {
return (
<div className="min-h-screen flex items-center justify-center text-red-500">
{error}
</div>
);
}
const getTierDisplayName = (tier: string) => {
const tierMap: Record<string, string> = {
BASIC: "Basic",
STANDARD: "Standard",
PREMIUM: "Premium",
};
return tierMap[tier.toUpperCase()] || tier;
};
const getTierPrice = (tier: string) => {
const priceMap: Record<string, string> = {
BASIC: "$9.99",
STANDARD: "$19.99",
PREMIUM: "$39.99",
};
return priceMap[tier.toUpperCase()] || "N/A";
};
return (
7 months ago
<div className="min-h-screen bg-gray-50 py-6 sm:py-12 pb-20 sm:pb-12">
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
{/* Header */}
7 months ago
<div className="mb-6 sm:mb-8">
<h1 className="text-2xl sm:text-3xl md:text-4xl font-bold text-gray-900 mb-2">
My Subscriptions
</h1>
7 months ago
<p className="text-sm sm:text-base text-gray-600">
Manage your subscription and billing information
</p>
</div>
{!subscription ? (
// No subscription
7 months ago
<div className="bg-white rounded-lg shadow-lg p-6 sm:p-8 text-center">
<div className="mb-6">
<div className="w-20 h-20 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-3xl">📦</span>
</div>
<h2 className="text-2xl font-bold text-gray-900 mb-2">
No Active Subscription
</h2>
<p className="text-gray-600 mb-6">
You don't have an active subscription. Subscribe to a plan to
start using our services.
</p>
<Button
onClick={() => router.push("/")}
className="bg-orange-500 hover:bg-orange-600 text-white font-medium px-6 py-3 rounded-md"
>
Browse Plans
</Button>
</div>
</div>
) : (
// Active subscription
7 months ago
<div className="space-y-4 sm:space-y-6">
{/* Subscription Card */}
7 months ago
<div className="bg-white rounded-lg shadow-lg p-6 sm:p-8 border-2 border-gray-200">
<div className="flex justify-between items-start mb-4 sm:mb-6">
<div>
7 months ago
<h2 className="text-xl sm:text-2xl font-bold text-gray-900 mb-2">
{getTierDisplayName(subscription.tier)} Plan
</h2>
7 months ago
<p className="text-sm sm:text-base text-gray-600">
Current subscription details
</p>
</div>
<div className="text-right">
<span
className={`px-3 py-1 rounded-full text-sm font-semibold ${
subscription.status === "ACTIVE"
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
}`}
>
{subscription.status}
</span>
</div>
</div>
{/* Subscription Details */}
7 months ago
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 mb-4 sm:mb-6">
<div>
<p className="text-sm text-gray-500 mb-1">Plan</p>
<p className="text-lg font-semibold text-gray-900">
{getTierDisplayName(subscription.tier)}
</p>
</div>
<div>
<p className="text-sm text-gray-500 mb-1">Monthly Price</p>
<p className="text-lg font-semibold text-gray-900">
{getTierPrice(subscription.tier)}/month
</p>
</div>
<div>
<p className="text-sm text-gray-500 mb-1">Storage Limit</p>
<p className="text-lg font-semibold text-gray-900">
{subscription.storage_limit_gb} GB
</p>
</div>
<div>
<p className="text-sm text-gray-500 mb-1">Current Usage</p>
<p className="text-lg font-semibold text-gray-900">
7 months ago
{typeof subscription.current_usage_gb === "number"
? subscription.current_usage_gb.toFixed(2)
: parseFloat(
String(subscription.current_usage_gb)
).toFixed(2)}{" "}
GB
</p>
</div>
</div>
{/* Billing Info */}
<div className="border-t pt-4">
<p className="text-sm text-gray-500 mb-1">Member Since</p>
<p className="text-gray-900">
{new Date(subscription.created_at).toLocaleDateString(
"en-US",
{
year: "numeric",
month: "long",
day: "numeric",
}
)}
</p>
</div>
{/* Actions */}
{subscription.status === "ACTIVE" && (
<div className="mt-6 pt-6 border-t">
<Button
onClick={handleCancelSubscription}
variant="outline"
className="border-red-300 text-red-600 hover:bg-red-50 hover:border-red-400"
>
Cancel Subscription
</Button>
</div>
)}
</div>
{/* Upgrade/Downgrade Options */}
7 months ago
<div className="bg-white rounded-lg shadow-lg p-6 mb-4 sm:mb-0">
<h3 className="text-lg sm:text-xl font-bold text-gray-900 mb-3 sm:mb-4">
Change Plan
</h3>
7 months ago
<p className="text-sm sm:text-base text-gray-600 mb-4">
Looking to upgrade or downgrade? Cancel your current
subscription and choose a new plan.
</p>
<Button
onClick={() => router.push("/")}
7 months ago
className="bg-orange-500 hover:bg-orange-600 text-white font-medium px-6 py-3 rounded-md w-full sm:w-auto"
>
Browse All Plans
</Button>
</div>
</div>
)}
</div>
</div>
);
}