feat: implement core platform features including business directory, admin dashboard, authentication, and API infrastructure
Some checks failed
Build and Push App / build (push) Failing after 16m2s
Some checks failed
Build and Push App / build (push) Failing after 16m2s
This commit is contained in:
69
admin/src/components/PlanSelector.tsx
Normal file
69
admin/src/components/PlanSelector.tsx
Normal file
@@ -0,0 +1,69 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { updateBusinessPlan } from '@/app/actions/plans';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { CreditCard, Loader2 } from 'lucide-react';
|
||||
|
||||
interface PlanSelectorProps {
|
||||
businessId: string;
|
||||
currentPlan: string;
|
||||
}
|
||||
|
||||
const PLANS = [
|
||||
{ id: 'STARTER', name: 'Starter', color: 'text-slate-400', bg: 'bg-slate-400/10' },
|
||||
{ id: 'BOOSTER', name: 'Booster', color: 'text-brand-400', bg: 'bg-brand-400/10' },
|
||||
{ id: 'EMPIRE', name: 'Empire', color: 'text-amber-400', bg: 'bg-amber-400/10' }
|
||||
];
|
||||
|
||||
export default function PlanSelector({ businessId, currentPlan }: PlanSelectorProps) {
|
||||
const [isUpdating, setIsUpdating] = useState(false);
|
||||
const [plan, setPlan] = useState(currentPlan);
|
||||
|
||||
const handlePlanChange = async (newPlan: string) => {
|
||||
setIsUpdating(true);
|
||||
try {
|
||||
const result = await updateBusinessPlan(businessId, newPlan as any);
|
||||
if (result.success) {
|
||||
setPlan(newPlan);
|
||||
toast.success(`Forfait mis à jour vers ${newPlan}`);
|
||||
} else {
|
||||
toast.error(result.error || "Erreur lors du changement de forfait");
|
||||
// Reset to old plan in UI
|
||||
setPlan(currentPlan);
|
||||
}
|
||||
} catch (error) {
|
||||
toast.error("Erreur réseau");
|
||||
setPlan(currentPlan);
|
||||
} finally {
|
||||
setIsUpdating(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative inline-flex items-center group">
|
||||
<div className={`absolute left-2 text-slate-500 pointer-events-none ${isUpdating ? 'animate-spin' : ''}`}>
|
||||
{isUpdating ? <Loader2 className="w-3.5 h-3.5" /> : <CreditCard className="w-3.5 h-3.5" />}
|
||||
</div>
|
||||
<select
|
||||
value={plan}
|
||||
onChange={(e) => handlePlanChange(e.target.value)}
|
||||
disabled={isUpdating}
|
||||
className={`appearance-none bg-slate-900 border border-slate-700 pl-8 pr-8 py-1.5 rounded-lg text-xs font-bold uppercase tracking-wider focus:outline-none focus:ring-2 focus:ring-brand-500/50 transition-all cursor-pointer hover:border-slate-600 disabled:opacity-50 disabled:cursor-not-allowed ${
|
||||
PLANS.find(p => p.id === plan)?.color || 'text-white'
|
||||
}`}
|
||||
>
|
||||
{PLANS.map((p) => (
|
||||
<option key={p.id} value={p.id} className="bg-slate-900 text-white font-sans capitalize">
|
||||
{p.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<div className="absolute right-2 pointer-events-none">
|
||||
<svg className="w-3 h-3 text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M19 9l-7 7-7-7" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user