multi line paste for tier plans

This commit is contained in:
rgrgogu
2026-07-31 01:28:43 +08:00
parent fda50436a4
commit cf6e6fc54c
2 changed files with 30 additions and 2 deletions
+15 -1
View File
@@ -160,9 +160,22 @@ export default function AddPlan() {
defaultValues: { tier_category_id: "", label: "", description: "", features: [], duration_value: 30, duration_unit: "day", price: "", currency: "USD" }, defaultValues: { tier_category_id: "", label: "", description: "", features: [], duration_value: 30, duration_unit: "day", price: "", currency: "USD" },
}); });
const { fields: featureFields, append: appendFeature, remove: removeFeature } = const { fields: featureFields, append: appendFeature, remove: removeFeature, insert: insertFeature } =
useFieldArray({ control, name: "features" }); useFieldArray({ control, name: "features" });
// Pasting a multi-line list should fan out into separate feature rows
// instead of dumping every line into a single input.
const handleFeaturePaste = (e, index) => {
const lines = e.clipboardData.getData("text")
.split(/\r\n|\r|\n/)
.map((l) => l.trim())
.filter(Boolean);
if (lines.length <= 1) return;
e.preventDefault();
setValue(`features.${index}.text`, lines[0], { shouldDirty: true });
insertFeature(index + 1, lines.slice(1).map((text) => ({ text })));
};
// selectedCourseIds lives outside the form — this is a create page so it // selectedCourseIds lives outside the form — this is a create page so it
// always starts empty, meaning any selection is a genuine unsaved change. // always starts empty, meaning any selection is a genuine unsaved change.
const { bypassOnce, dialog: unsavedChangesDialog } = useUnsavedChangesGuard( const { bypassOnce, dialog: unsavedChangesDialog } = useUnsavedChangesGuard(
@@ -309,6 +322,7 @@ export default function AddPlan() {
<div className="flex-1 space-y-1"> <div className="flex-1 space-y-1">
<Input <Input
placeholder={`e.g. Access to all Premium courses`} placeholder={`e.g. Access to all Premium courses`}
onPaste={(e) => handleFeaturePaste(e, index)}
{...register(`features.${index}.text`)} {...register(`features.${index}.text`)}
/> />
<FieldError message={errors.features?.[index]?.text?.message} /> <FieldError message={errors.features?.[index]?.text?.message} />
+15 -1
View File
@@ -97,9 +97,22 @@ export default function EditPlan() {
resolver: zodResolver(schema), resolver: zodResolver(schema),
}); });
const { fields: featureFields, append: appendFeature, remove: removeFeature } = const { fields: featureFields, append: appendFeature, remove: removeFeature, insert: insertFeature } =
useFieldArray({ control, name: "features" }); useFieldArray({ control, name: "features" });
// Pasting a multi-line list should fan out into separate feature rows
// instead of dumping every line into a single input.
const handleFeaturePaste = (e, index) => {
const lines = e.clipboardData.getData("text")
.split(/\r\n|\r|\n/)
.map((l) => l.trim())
.filter(Boolean);
if (lines.length <= 1) return;
e.preventDefault();
setValue(`features.${index}.text`, lines[0], { shouldDirty: true });
insertFeature(index + 1, lines.slice(1).map((text) => ({ text })));
};
const { bypassOnce, dialog: unsavedChangesDialog } = useUnsavedChangesGuard(isDirty); const { bypassOnce, dialog: unsavedChangesDialog } = useUnsavedChangesGuard(isDirty);
useEffect(() => { useEffect(() => {
@@ -242,6 +255,7 @@ export default function EditPlan() {
<div className="flex-1 space-y-1"> <div className="flex-1 space-y-1">
<Input <Input
placeholder="e.g. Access to all Premium courses" placeholder="e.g. Access to all Premium courses"
onPaste={(e) => handleFeaturePaste(e, index)}
{...register(`features.${index}.text`)} {...register(`features.${index}.text`)}
/> />
<FieldError message={errors.features?.[index]?.text?.message} /> <FieldError message={errors.features?.[index]?.text?.message} />