Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions src/common/ui/form/components/richtext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,28 @@ export const RichTextEditor: React.FC<RichTextEditorProps> = ({
HTMLAttributes: {
class: "text-blue-600 hover:text-blue-800 underline",
},
}).extend({
addKeyboardShortcuts() {
return {
// Ctrl/Cmd+K: add a link on plain text, or edit the URL when the
// cursor is on hyperlinked text (the prompt pre-fills with the
// existing href). Submitting an empty string removes the link.
"Mod-k": () => {
const previousUrl: string | undefined = this.editor.getAttributes("link").href;
const url = window.prompt("Enter URL:", previousUrl ?? "");

if (url === null) return false;

if (url === "") {
this.editor.chain().focus().extendMarkRange("link").unsetLink().run();
return true;
}

this.editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run();
return true;
},
};
},
}),
],
content: value || `<p>${placeholder ?? ""}</p>`,
Expand Down
17 changes: 14 additions & 3 deletions src/entities/campaign/components/editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,24 @@ export const CampaignEditor = ({ existingData, campaignId, close }: CampaignEdit
// Keep the min attribute on datetime inputs up to date (client-side only).
useEffect(() => {
const updateMinDateTime = () => {
const newMin = Temporal.Now.instant()
.add({ minutes: 1 })
const newMinInstant = Temporal.Now.instant().add({ minutes: 1 });

const newMin = newMinInstant
.toZonedDateTimeISO(Temporal.Now.timeZoneId())
.toPlainDateTime()
.toString({ smallestUnit: "minute" });

setMinStartDateTime(newMin);

// If the form's stored start_ms has fallen into the past while the user
// was filling out the rest of the form, silently bump it to the new min.
// Otherwise submit fails with a confusing "please fill in required field"
// browser error because the input value is now below its min attribute.
const currentStartMs = form.getValues("start_ms");

if (typeof currentStartMs === "number" && currentStartMs < newMinInstant.epochMilliseconds) {
form.setValue("start_ms", newMinInstant.epochMilliseconds, { shouldDirty: true });
}
};

// Set initial value immediately on mount (client-side only)
Expand All @@ -151,7 +162,7 @@ export const CampaignEditor = ({ existingData, campaignId, close }: CampaignEdit
const interval = setInterval(updateMinDateTime, 60000);

return () => clearInterval(interval);
}, []);
}, [form]);

// "Set to current" — sets start date to right now
const handleStartNow = () => {
Expand Down
Loading