améliore la création de campagnes (proposition de paliers harmonieux automatiques)

This commit is contained in:
Yannick Le Duc
2025-08-26 23:58:00 +02:00
parent 4ce52f300f
commit ba3a7c3ea1
3 changed files with 77 additions and 5 deletions

View File

@@ -239,9 +239,9 @@ function AdminPageContent() {
</Button> </Button>
</div> </div>
) : ( ) : (
<div className="grid gap-6"> <div className="grid gap-6 group/campaigns">
{campaigns.map((campaign) => ( {campaigns.map((campaign) => (
<Card key={campaign.id} className="group hover:shadow-xl hover:shadow-slate-100 dark:hover:shadow-slate-900/20 transition-all duration-300 border-slate-200 dark:border-slate-700 overflow-hidden"> <Card key={campaign.id} className="group hover:shadow-xl hover:shadow-slate-100 dark:hover:shadow-slate-900/20 transition-all duration-300 border-slate-200 dark:border-slate-700 overflow-hidden group-hover/campaigns:opacity-50 hover:!opacity-100">
<div className="relative"> <div className="relative">
<CardHeader className="pb-4"> <CardHeader className="pb-4">

View File

@@ -47,13 +47,79 @@ export default function CreateCampaignModal({ isOpen, onClose, onSuccess }: Crea
} }
}; };
const generateOptimalTiers = (budget: number): string => {
if (budget <= 0) return "0";
// Cas spéciaux pour des budgets courants
if (budget === 10000) {
return "0, 500, 1000, 2000, 3000, 5000, 7500, 10000";
}
if (budget === 8000) {
return "0, 500, 1000, 2000, 3000, 4000, 6000, 8000";
}
const tiers = [0];
// Déterminer les paliers "ronds" selon la taille du budget
let roundValues: number[] = [];
if (budget <= 100) {
// Petits budgets : multiples de 5, 10, 25
roundValues = [5, 10, 25, 50, 75, 100];
} else if (budget <= 500) {
// Budgets moyens : multiples de 25, 50, 100
roundValues = [25, 50, 75, 100, 150, 200, 250, 300, 400, 500];
} else if (budget <= 2000) {
// Budgets moyens-grands : multiples de 100, 250, 500
roundValues = [100, 250, 500, 750, 1000, 1250, 1500, 1750, 2000];
} else if (budget <= 10000) {
// Gros budgets : multiples de 500, 1000, 2000
roundValues = [500, 1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000, 7500, 10000];
} else {
// Très gros budgets : multiples de 1000, 2000, 5000
roundValues = [1000, 2000, 3000, 5000, 7500, 10000, 15000, 20000, 25000, 50000];
}
// Sélectionner les paliers qui sont inférieurs ou égaux au budget
const validTiers = roundValues.filter(tier => tier <= budget);
// Prendre 6-8 paliers intermédiaires + 0 et le budget final
const targetCount = Math.min(8, Math.max(6, validTiers.length));
const step = Math.max(1, Math.floor(validTiers.length / targetCount));
for (let i = 0; i < validTiers.length && tiers.length < targetCount + 1; i += step) {
if (!tiers.includes(validTiers[i])) {
tiers.push(validTiers[i]);
}
}
// Ajouter le budget final s'il n'est pas déjà présent
if (!tiers.includes(budget)) {
tiers.push(budget);
}
// Trier et retourner
return tiers.sort((a, b) => a - b).join(', ');
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => { const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ setFormData(prev => ({
...prev, ...prev,
[e.target.name]: e.target.value [name]: value
})); }));
}; };
const handleBudgetBlur = (e: React.FocusEvent<HTMLInputElement>) => {
const budget = parseInt(e.target.value);
if (!isNaN(budget) && budget > 0 && !formData.spending_tiers) {
setFormData(prev => ({
...prev,
spending_tiers: generateOptimalTiers(budget)
}));
}
};
const handleClose = () => { const handleClose = () => {
setFormData({ setFormData({
title: '', title: '',
@@ -108,13 +174,14 @@ export default function CreateCampaignModal({ isOpen, onClose, onSuccess }: Crea
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="budget_per_user">Budget par utilisateur () *</Label> <Label htmlFor="budget_per_user">Budget () *</Label>
<Input <Input
id="budget_per_user" id="budget_per_user"
name="budget_per_user" name="budget_per_user"
type="number" type="number"
value={formData.budget_per_user} value={formData.budget_per_user}
onChange={handleChange} onChange={handleChange}
onBlur={handleBudgetBlur}
placeholder="100" placeholder="100"
min="1" min="1"
required required
@@ -133,6 +200,11 @@ export default function CreateCampaignModal({ isOpen, onClose, onSuccess }: Crea
/> />
<p className="text-xs text-slate-500 dark:text-slate-400"> <p className="text-xs text-slate-500 dark:text-slate-400">
Séparez les montants par des virgules (ex: 0, 10, 25, 50, 100) Séparez les montants par des virgules (ex: 0, 10, 25, 50, 100)
{formData.budget_per_user && !formData.spending_tiers && (
<span className="block mt-1 text-blue-600 dark:text-blue-400">
💡 Les paliers seront générés automatiquement après avoir saisi le budget
</span>
)}
</p> </p>
</div> </div>

View File

@@ -137,7 +137,7 @@ export default function EditCampaignModal({ isOpen, onClose, onSuccess, campaign
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="budget_per_user">Budget par utilisateur () *</Label> <Label htmlFor="budget_per_user">Budget () *</Label>
<Input <Input
id="budget_per_user" id="budget_per_user"
name="budget_per_user" name="budget_per_user"