Allow skipping confirmation email when adding mailing list subscribers

When creating a new subscriber, operators can now check "Skip confirmation email" if they already have the recipient's consent. This creates the subscriber in confirmed status and skips sending the notification email.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-19 22:12:08 +01:00
parent bedfd61168
commit c5589793da
4 changed files with 32 additions and 9 deletions

View File

@@ -1,5 +1,5 @@
import { useTranslate } from "@probo/i18n"; import { useTranslate } from "@probo/i18n";
import { Button, Dialog, DialogContent, DialogFooter, type DialogRef, Field, Spinner } from "@probo/ui"; import { Button, Checkbox, Dialog, DialogContent, DialogFooter, type DialogRef, Field, Spinner } from "@probo/ui";
import { type DataID, graphql } from "relay-runtime"; import { type DataID, graphql } from "relay-runtime";
import { z } from "zod"; import { z } from "zod";
@@ -42,10 +42,11 @@ export function NewCompliancePageSubscriberDialog(props: {
.min(1, __("Email is required")) .min(1, __("Email is required"))
.trim() .trim()
.email(__("Please enter a valid email address")), .email(__("Please enter a valid email address")),
confirmed: z.boolean(),
}); });
const form = useFormWithSchema(schema, { const form = useFormWithSchema(schema, {
defaultValues: { fullName: "", email: "" }, defaultValues: { fullName: "", email: "", confirmed: false },
}); });
const [createSubscriber, isCreating] = useMutationWithToasts<NewCompliancePageSubscriberDialogMutation>( const [createSubscriber, isCreating] = useMutationWithToasts<NewCompliancePageSubscriberDialogMutation>(
@@ -63,6 +64,7 @@ export function NewCompliancePageSubscriberDialog(props: {
mailingListId, mailingListId,
fullName: data.fullName.trim(), fullName: data.fullName.trim(),
email: data.email.trim(), email: data.email.trim(),
confirmed: data.confirmed || undefined,
}, },
connections: connectionId ? [connectionId] : [], connections: connectionId ? [connectionId] : [],
}, },
@@ -102,6 +104,15 @@ export function NewCompliancePageSubscriberDialog(props: {
{...form.register("email")} {...form.register("email")}
placeholder={__("john@example.com")} placeholder={__("john@example.com")}
/> />
<label className="flex items-center gap-2 cursor-pointer">
<Checkbox
checked={form.watch("confirmed")}
onChange={checked => form.setValue("confirmed", checked)}
/>
<span className="text-sm">
{__("Skip confirmation email (I already have consent)")}
</span>
</label>
</DialogContent> </DialogContent>
<DialogFooter> <DialogFooter>
<Button type="submit" disabled={isCreating}> <Button type="submit" disabled={isCreating}>

View File

@@ -75,6 +75,7 @@ type (
MailingListID gid.GID MailingListID gid.GID
Email mail.Addr Email mail.Addr
FullName string FullName string
Confirmed bool
} }
) )
@@ -182,12 +183,19 @@ func (s *Service) CreateSubscriber(
mailingListID := req.MailingListID mailingListID := req.MailingListID
email := req.Email email := req.Email
fullName := req.FullName fullName := req.FullName
scope := coredata.NewScopeFromObjectID(mailingListID) scope := coredata.NewScopeFromObjectID(mailingListID)
emailRecord, err := s.buildConfirmationMail(ctx, mailingListID, email, fullName)
status := coredata.MailingListSubscriberStatusPending
var emailRecord *coredata.Email
if req.Confirmed {
status = coredata.MailingListSubscriberStatusConfirmed
} else {
var err error
emailRecord, err = s.buildConfirmationMail(ctx, mailingListID, email, fullName)
if err != nil { if err != nil {
return nil, fmt.Errorf("cannot build confirmation mail: %w", err) return nil, fmt.Errorf("cannot build confirmation mail: %w", err)
} }
}
now := time.Now() now := time.Now()
subscriber := &coredata.MailingListSubscriber{ subscriber := &coredata.MailingListSubscriber{
@@ -195,7 +203,7 @@ func (s *Service) CreateSubscriber(
MailingListID: mailingListID, MailingListID: mailingListID,
FullName: fullName, FullName: fullName,
Email: email, Email: email,
Status: coredata.MailingListSubscriberStatusPending, Status: status,
CreatedAt: now, CreatedAt: now,
UpdatedAt: now, UpdatedAt: now,
} }
@@ -216,9 +224,11 @@ func (s *Service) CreateSubscriber(
return fmt.Errorf("cannot insert mailing list subscriber: %w", err) return fmt.Errorf("cannot insert mailing list subscriber: %w", err)
} }
if emailRecord != nil {
if err := emailRecord.Insert(ctx, tx); err != nil { if err := emailRecord.Insert(ctx, tx); err != nil {
return fmt.Errorf("cannot insert subscription confirmation email: %w", err) return fmt.Errorf("cannot insert subscription confirmation email: %w", err)
} }
}
return nil return nil
}, },

View File

@@ -3925,6 +3925,7 @@ input CreateMailingListSubscriberInput {
mailingListId: ID! mailingListId: ID!
fullName: String! fullName: String!
email: EmailAddr! email: EmailAddr!
confirmed: Boolean
} }
input DeleteMailingListSubscriberInput { input DeleteMailingListSubscriberInput {

View File

@@ -2404,6 +2404,7 @@ func (r *mutationResolver) CreateMailingListSubscriber(ctx context.Context, inpu
MailingListID: input.MailingListID, MailingListID: input.MailingListID,
Email: input.Email, Email: input.Email,
FullName: input.FullName, FullName: input.FullName,
Confirmed: input.Confirmed != nil && *input.Confirmed,
}, },
) )
if err != nil { if err != nil {