Seth0330's picture
Update frontend/src/components/admin/PlanForm.jsx
bbad283 verified
// frontend/src/components/admin/PlanForm.jsx
import React, { useEffect, useState } from "react";
const BILLING_OPTIONS = [
{ value: "monthly", label: "Monthly" },
{ value: "quarterly", label: "Quarterly" },
{ value: "half_yearly", label: "Half Yearly" },
{ value: "annual", label: "Annual" },
];
export default function PlanForm({ plan, onSave, onCancel, isLoading }) {
const [name, setName] = useState("");
const [billingPeriod, setBillingPeriod] = useState("monthly");
const [price, setPrice] = useState("");
const [description, setDescription] = useState("");
const [maxStudents, setMaxStudents] = useState("1");
const [isActive, setIsActive] = useState(true);
const [isDefault, setIsDefault] = useState(false);
useEffect(() => {
if (plan) {
setName(plan.name || "");
setBillingPeriod(plan.billing_period || "monthly");
setPrice(plan.price != null ? String(plan.price) : "");
setDescription(plan.description || "");
setMaxStudents(String(plan.max_students || 1));
setIsActive(plan.is_active ?? true);
setIsDefault(plan.is_default ?? false);
} else {
setName("");
setBillingPeriod("monthly");
setPrice("");
setDescription("");
setMaxStudents("1");
setIsActive(true);
setIsDefault(false);
}
}, [plan]);
function handleSubmit(e) {
e.preventDefault();
const priceNumber = parseFloat(price || "0") || 0;
const maxStudentsNumber = parseInt(maxStudents || "1", 10) || 1;
onSave({
name,
billing_period: billingPeriod,
price: priceNumber,
description: description || null,
max_students: maxStudentsNumber,
is_active: isActive,
is_default: isDefault,
});
}
return (
<form
onSubmit={handleSubmit}
className="bg-stone-50 border border-stone-200 rounded-xl p-4 space-y-4"
>
{/* Top Row: Plan Name, Billing Period on left; Price, Max Students on right */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<div>
<label className="block text-xs font-medium text-stone-700 mb-1">
Plan name
</label>
<input
type="text"
className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
placeholder="Beginner Monthly"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-700 mb-1">
Billing period
</label>
<select
className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
value={billingPeriod}
onChange={(e) => setBillingPeriod(e.target.value)}
>
{BILLING_OPTIONS.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
</div>
</div>
<div className="space-y-2">
<div>
<label className="block text-xs font-medium text-stone-700 mb-1">
Price (CAD)
</label>
<input
type="number"
className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
placeholder="99"
value={price}
onChange={(e) => setPrice(e.target.value)}
min="0"
step="0.01"
/>
</div>
<div>
<label className="block text-xs font-medium text-stone-700 mb-1">
Max Students
</label>
<input
type="number"
className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500"
placeholder="1"
value={maxStudents}
onChange={(e) => setMaxStudents(e.target.value)}
min="1"
/>
</div>
</div>
</div>
{/* Description - Full Width */}
<div>
<label className="block text-xs font-medium text-stone-700 mb-1">
Description
</label>
<textarea
className="w-full rounded-lg border border-stone-200 px-3 py-2 text-sm focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 min-h-[60px]"
placeholder="Beginner classes 2x per week."
value={description}
onChange={(e) => setDescription(e.target.value)}
/>
</div>
{/* Bottom Row: Checkboxes and Buttons */}
<div className="flex items-center justify-between pt-1">
<div className="flex flex-col gap-2">
<label className="inline-flex items-center gap-2 text-xs text-stone-700">
<input
type="checkbox"
className="rounded border-stone-300"
checked={isActive}
onChange={(e) => setIsActive(e.target.checked)}
/>
Active / visible to students
</label>
<label className="inline-flex items-center gap-2 text-xs text-stone-700">
<input
type="checkbox"
className="rounded border-stone-300"
checked={isDefault}
onChange={(e) => setIsDefault(e.target.checked)}
/>
Default membership plan (max 2)
</label>
</div>
<div className="flex gap-2">
<button
type="button"
onClick={onCancel}
className="px-3 py-1.5 rounded-lg border border-stone-200 text-xs font-medium text-stone-700 hover:bg-stone-100"
>
Cancel
</button>
<button
type="submit"
disabled={isLoading}
className="px-3 py-1.5 rounded-lg bg-indigo-600 text-white text-xs font-medium hover:bg-indigo-700 disabled:opacity-60"
>
{isLoading ? "Saving…" : plan ? "Update plan" : "Create plan"}
</button>
</div>
</div>
</form>
);
}