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

This commit is contained in:
2026-04-18 22:10:19 +02:00
parent 6ec1a3ae84
commit 3e2063e4fa
89 changed files with 4405 additions and 967 deletions

View 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>
);
}