feat(01-06): add subscription management page

- Display all plans (Free, Creator, Pro) in card grid
- Highlight current plan with 'Piano attuale' badge
- Add feature comparison table
- Include FAQ section
- Show payment deferral notice
- All text in Italian
This commit is contained in:
Michele
2026-01-31 13:43:29 +01:00
parent 8789f26b36
commit e4e04fa784

View File

@@ -0,0 +1,179 @@
import { createClient } from '@/lib/supabase/server'
import { redirect } from 'next/navigation'
import { PlanCard } from '@/components/subscription/plan-card'
import { Plan, PlanFeatures } from '@/types/database'
import { PLAN_DISPLAY_ORDER } from '@/lib/plans'
export default async function SubscriptionPage() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
redirect('/login')
}
// Get user's current plan
const { data: profile } = await supabase
.from('profiles')
.select('plan_id')
.eq('id', user.id)
.single()
// Get all plans
const { data: plans, error: plansError } = await supabase
.from('plans')
.select('*')
.order('price_monthly', { ascending: true })
if (plansError || !plans) {
return (
<div className="text-center py-12">
<p className="text-red-600">Errore nel caricamento dei piani</p>
</div>
)
}
// Sort plans by our display order
const sortedPlans = [...plans].sort((a, b) => {
return PLAN_DISPLAY_ORDER.indexOf(a.name as typeof PLAN_DISPLAY_ORDER[number]) -
PLAN_DISPLAY_ORDER.indexOf(b.name as typeof PLAN_DISPLAY_ORDER[number])
})
return (
<div className="space-y-8">
<div>
<h1 className="text-2xl font-bold text-gray-900">Il tuo abbonamento</h1>
<p className="text-gray-500 mt-1">
Scegli il piano piu adatto alle tue esigenze
</p>
</div>
{/* Info banner */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<p className="text-sm text-blue-800">
<strong>Nota:</strong> Il pagamento verra implementato nelle prossime versioni.
Per ora puoi passare liberamente tra i piani per testare le funzionalita.
</p>
</div>
{/* Plans grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{sortedPlans.map((plan) => (
<PlanCard
key={plan.id}
plan={plan as Plan}
isCurrentPlan={plan.id === profile?.plan_id}
/>
))}
</div>
{/* Feature comparison */}
<div className="mt-12">
<h2 className="text-xl font-semibold text-gray-900 mb-4">
Confronto funzionalita
</h2>
<div className="overflow-x-auto">
<table className="w-full border-collapse">
<thead>
<tr className="border-b">
<th className="text-left py-3 px-4 font-medium text-gray-600">
Funzionalita
</th>
{sortedPlans.map((plan) => (
<th key={plan.id} className="text-center py-3 px-4 font-medium text-gray-900">
{plan.display_name_it}
</th>
))}
</tr>
</thead>
<tbody>
<FeatureRow
feature="Post al mese"
plans={sortedPlans as Plan[]}
getValue={(p) => (p.features as PlanFeatures).posts_per_month.toString()}
/>
<FeatureRow
feature="Account social"
plans={sortedPlans as Plan[]}
getValue={(p) => (p.features as PlanFeatures).social_accounts.toString()}
/>
<FeatureRow
feature="Modelli AI"
plans={sortedPlans as Plan[]}
getValue={(p) => (p.features as PlanFeatures).ai_models.length.toString()}
/>
<FeatureRow
feature="Generazione immagini"
plans={sortedPlans as Plan[]}
getValue={(p) => (p.features as PlanFeatures).image_generation ? '\u2713' : '\u2014'}
/>
<FeatureRow
feature="Automazione"
plans={sortedPlans as Plan[]}
getValue={(p) => {
const auto = (p.features as PlanFeatures).automation
if (auto === false) return '\u2014'
if (auto === 'manual') return 'Manuale'
if (auto === 'full') return 'Completa'
return '\u2014'
}}
/>
</tbody>
</table>
</div>
</div>
{/* FAQ */}
<div className="mt-12">
<h2 className="text-xl font-semibold text-gray-900 mb-4">
Domande frequenti
</h2>
<div className="space-y-4">
<FaqItem
question="Posso cambiare piano in qualsiasi momento?"
answer="Si, puoi passare a un piano superiore o inferiore quando vuoi. Le modifiche sono immediate."
/>
<FaqItem
question="Cosa succede se supero i limiti del mio piano?"
answer="Riceverai un avviso quando ti avvicini al limite mensile. Non potrai creare nuovi post fino al rinnovo o all'upgrade."
/>
<FaqItem
question="Come funziona il pagamento?"
answer="Il sistema di pagamento verra implementato a breve. Per ora tutti i piani sono disponibili gratuitamente per i test."
/>
</div>
</div>
</div>
)
}
function FeatureRow({
feature,
plans,
getValue,
}: {
feature: string
plans: Plan[]
getValue: (plan: Plan) => string
}) {
return (
<tr className="border-b">
<td className="py-3 px-4 text-gray-600">{feature}</td>
{plans.map((plan) => (
<td key={plan.id} className="text-center py-3 px-4">
{getValue(plan)}
</td>
))}
</tr>
)
}
function FaqItem({ question, answer }: { question: string; answer: string }) {
return (
<div className="bg-gray-50 rounded-lg p-4">
<h3 className="font-medium text-gray-900 mb-2">{question}</h3>
<p className="text-sm text-gray-600">{answer}</p>
</div>
)
}