feat(settings): add edit plan modal — name/type/speed/price inline edit (M9.4)
This commit is contained in:
@@ -399,6 +399,8 @@ function PlansSettings() {
|
|||||||
const [speedUp, setSpeedUp] = useState("");
|
const [speedUp, setSpeedUp] = useState("");
|
||||||
const [price, setPrice] = useState("");
|
const [price, setPrice] = useState("");
|
||||||
const [description, setDescription] = useState("");
|
const [description, setDescription] = useState("");
|
||||||
|
const [editPlan, setEditPlan] = useState<Plan | null>(null);
|
||||||
|
const [editForm, setEditForm] = useState({ name: "", type: "POSTPAID", speedDown: "", speedUp: "", price: "", description: "" });
|
||||||
|
|
||||||
const { data, isLoading, refetch } = useQuery<Plan[]>({
|
const { data, isLoading, refetch } = useQuery<Plan[]>({
|
||||||
queryKey: ["plans"],
|
queryKey: ["plans"],
|
||||||
@@ -444,6 +446,38 @@ function PlansSettings() {
|
|||||||
onError: () => toast.error("Failed to update plan"),
|
onError: () => toast.error("Failed to update plan"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const editPlanMutation = useMutation({
|
||||||
|
mutationFn: async () => {
|
||||||
|
if (!editPlan) return;
|
||||||
|
await api.patch(`/api/v1/plans/${editPlan.id}`, {
|
||||||
|
name: editForm.name,
|
||||||
|
type: editForm.type,
|
||||||
|
speedDownMbps: parseInt(editForm.speedDown),
|
||||||
|
speedUpMbps: parseInt(editForm.speedUp),
|
||||||
|
monthlyPrice: parseFloat(editForm.price),
|
||||||
|
description: editForm.description || undefined,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Plan updated");
|
||||||
|
setEditPlan(null);
|
||||||
|
refetch();
|
||||||
|
},
|
||||||
|
onError: () => toast.error("Failed to update plan"),
|
||||||
|
});
|
||||||
|
|
||||||
|
function openEdit(p: Plan) {
|
||||||
|
setEditPlan(p);
|
||||||
|
setEditForm({
|
||||||
|
name: p.name,
|
||||||
|
type: p.type,
|
||||||
|
speedDown: String(p.speedDownMbps),
|
||||||
|
speedUp: String(p.speedUpMbps),
|
||||||
|
price: String(Number(p.monthlyPrice)),
|
||||||
|
description: p.description ?? "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function resetAdd() {
|
function resetAdd() {
|
||||||
setPlanName(""); setPlanType("POSTPAID");
|
setPlanName(""); setPlanType("POSTPAID");
|
||||||
setSpeedDown(""); setSpeedUp(""); setPrice(""); setDescription("");
|
setSpeedDown(""); setSpeedUp(""); setPrice(""); setDescription("");
|
||||||
@@ -498,6 +532,14 @@ function PlansSettings() {
|
|||||||
</Badge>
|
</Badge>
|
||||||
</Td>
|
</Td>
|
||||||
<Td>
|
<Td>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="ghost"
|
||||||
|
onClick={() => openEdit(p)}
|
||||||
|
>
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
@@ -505,6 +547,7 @@ function PlansSettings() {
|
|||||||
>
|
>
|
||||||
{p.isActive ? "Archive" : "Restore"}
|
{p.isActive ? "Archive" : "Restore"}
|
||||||
</Button>
|
</Button>
|
||||||
|
</div>
|
||||||
</Td>
|
</Td>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))
|
))
|
||||||
@@ -514,6 +557,34 @@ function PlansSettings() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
|
{/* Edit Plan Modal */}
|
||||||
|
<Modal isOpen={!!editPlan} onClose={() => setEditPlan(null)} title="Edit Plan" className="max-w-lg">
|
||||||
|
<form onSubmit={(e) => { e.preventDefault(); editPlanMutation.mutate(); }} className="space-y-4">
|
||||||
|
<Input label="Plan Name" value={editForm.name} onChange={(e) => setEditForm(f => ({ ...f, name: e.target.value }))} />
|
||||||
|
<div className="flex flex-col gap-1.5">
|
||||||
|
<label className="text-sm font-medium text-gray-700">Type</label>
|
||||||
|
<select
|
||||||
|
className="block w-full rounded-lg border border-gray-300 bg-white px-3 py-2 text-sm focus:border-blue-500 focus:outline-none focus:ring-1 focus:ring-blue-500"
|
||||||
|
value={editForm.type}
|
||||||
|
onChange={(e) => setEditForm(f => ({ ...f, type: e.target.value }))}
|
||||||
|
>
|
||||||
|
<option value="POSTPAID">Postpaid</option>
|
||||||
|
<option value="PREPAID">Prepaid</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-3">
|
||||||
|
<Input label="Download (Mbps)" type="number" min="1" value={editForm.speedDown} onChange={(e) => setEditForm(f => ({ ...f, speedDown: e.target.value }))} />
|
||||||
|
<Input label="Upload (Mbps)" type="number" min="1" value={editForm.speedUp} onChange={(e) => setEditForm(f => ({ ...f, speedUp: e.target.value }))} />
|
||||||
|
</div>
|
||||||
|
<Input label="Monthly Price (₱)" type="number" min="0" step="0.01" value={editForm.price} onChange={(e) => setEditForm(f => ({ ...f, price: e.target.value }))} />
|
||||||
|
<Input label="Description (optional)" value={editForm.description} onChange={(e) => setEditForm(f => ({ ...f, description: e.target.value }))} />
|
||||||
|
<div className="flex justify-end gap-2 pt-1">
|
||||||
|
<Button type="button" variant="outline" size="sm" onClick={() => setEditPlan(null)}>Cancel</Button>
|
||||||
|
<Button type="submit" size="sm" isLoading={editPlanMutation.isPending}>Save Changes</Button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
{/* Add Plan Modal */}
|
{/* Add Plan Modal */}
|
||||||
<Modal isOpen={showAdd} onClose={() => { setShowAdd(false); resetAdd(); }} title="Add Plan" className="max-w-lg">
|
<Modal isOpen={showAdd} onClose={() => { setShowAdd(false); resetAdd(); }} title="Add Plan" className="max-w-lg">
|
||||||
<form onSubmit={(e) => { e.preventDefault(); addPlanMutation.mutate(); }} className="space-y-4">
|
<form onSubmit={(e) => { e.preventDefault(); addPlanMutation.mutate(); }} className="space-y-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user