fix(messaging): raise campaign recipient cap to 1000 and share constant
The SendCampaignCommand was using MAX_EMAIL_RECIPIENTS (=100), the cap designed for the user-mailbox compose flow. Campaigns send one message per recipient on a background worker and the server-side DTO already caps at 1000 — the frontend was just clipping the fetched person IDs earlier and silently truncating large selections (e.g. 1200 People selected → drawer showed 'Sending to 100'). - Extract MAX_CAMPAIGN_RECIPIENTS (= 1000) to twenty-shared/constants so client and server validate against the same number. - SendCampaignCommand fetches up to MAX_CAMPAIGN_RECIPIENTS person IDs. - Compose drawer shows a red warning when the cap is hit, telling the user that additional selections will be skipped.
This commit is contained in:
+7
-4
@@ -1,4 +1,4 @@
|
||||
import { MAX_EMAIL_RECIPIENTS } from 'twenty-shared/constants';
|
||||
import { MAX_CAMPAIGN_RECIPIENTS } from 'twenty-shared/constants';
|
||||
import { CoreObjectNameSingular } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
@@ -17,13 +17,16 @@ export const SendCampaignCommand = () => {
|
||||
const objectNameSingular = objectMetadataItem?.nameSingular ?? null;
|
||||
const isPerson = objectNameSingular === CoreObjectNameSingular.Person;
|
||||
|
||||
// Campaign is bulk-only in v1 — selecting a single Person should still work,
|
||||
// but in practice it's a tool for selections.
|
||||
// Campaign is bulk-only in v1. We hard-cap recipient resolution at
|
||||
// MAX_CAMPAIGN_RECIPIENTS to match the server-side validation in the
|
||||
// SendMessageCampaign DTO. If the user selected more than that, only the
|
||||
// first MAX_CAMPAIGN_RECIPIENTS will be fetched — the drawer surfaces the
|
||||
// truncation via the selectedCount vs queuedCount delta.
|
||||
const { records: personRecords, loading } = useFindManyRecords({
|
||||
objectNameSingular: CoreObjectNameSingular.Person,
|
||||
filter: graphqlFilter ?? undefined,
|
||||
recordGqlFields: { id: true },
|
||||
limit: MAX_EMAIL_RECIPIENTS,
|
||||
limit: MAX_CAMPAIGN_RECIPIENTS,
|
||||
skip: !isPerson,
|
||||
});
|
||||
|
||||
|
||||
+12
@@ -6,6 +6,7 @@ import { useCreateBlockNote } from '@blocknote/react';
|
||||
import '@blocknote/react/style.css';
|
||||
import { styled } from '@linaria/react';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { MAX_CAMPAIGN_RECIPIENTS } from 'twenty-shared/constants';
|
||||
import { IconSend } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { themeCssVariables } from 'twenty-ui/theme-constants';
|
||||
@@ -82,6 +83,11 @@ const StyledRecipientCount = styled.div`
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
`;
|
||||
|
||||
const StyledTruncationWarning = styled.div`
|
||||
color: ${themeCssVariables.font.color.danger};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
`;
|
||||
|
||||
const StyledEmptyDomainsMessage = styled.div`
|
||||
color: ${themeCssVariables.font.color.danger};
|
||||
font-size: ${themeCssVariables.font.size.sm};
|
||||
@@ -175,6 +181,12 @@ export const SidePanelComposeCampaignPage = () => {
|
||||
{t`Sending to ${recipientPersonIds.length} recipient(s)`}
|
||||
</StyledRecipientCount>
|
||||
|
||||
{recipientPersonIds.length >= MAX_CAMPAIGN_RECIPIENTS && (
|
||||
<StyledTruncationWarning>
|
||||
{t`Recipient cap of ${MAX_CAMPAIGN_RECIPIENTS} reached — any additional people selected will not receive this campaign.`}
|
||||
</StyledTruncationWarning>
|
||||
)}
|
||||
|
||||
{!domainsLoading && verifiedDomains.length === 0 && (
|
||||
<StyledEmptyDomainsMessage>
|
||||
{t`No verified sending domain. Add one in Settings → Emailing Domains first.`}
|
||||
|
||||
+1
-2
@@ -10,8 +10,7 @@ import {
|
||||
IsUUID,
|
||||
Length,
|
||||
} from 'class-validator';
|
||||
|
||||
export const MAX_CAMPAIGN_RECIPIENTS = 1000;
|
||||
import { MAX_CAMPAIGN_RECIPIENTS } from 'twenty-shared/constants';
|
||||
|
||||
@InputType()
|
||||
export class SendMessageCampaignInput {
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
// Hard cap on recipients for a single message campaign. Enforced server-side
|
||||
// in the SendMessageCampaign DTO and used client-side to limit the recipient
|
||||
// resolution query.
|
||||
export const MAX_CAMPAIGN_RECIPIENTS = 1000;
|
||||
@@ -37,6 +37,7 @@ export { GIN_COMPATIBLE_FIELD_TYPES } from './GinCompatibleFieldTypes';
|
||||
export { GROUP_BY_DATE_GRANULARITY_THAT_REQUIRE_TIME_ZONE } from './GroupByDateGranularityThatRequireTimeZone';
|
||||
export { IANA_TIME_ZONES } from './IanaTimeZones';
|
||||
export { LABEL_IDENTIFIER_FIELD_METADATA_TYPES } from './LabelIdentifierFieldMetadataTypes';
|
||||
export { MAX_CAMPAIGN_RECIPIENTS } from './MaxCampaignRecipients';
|
||||
export { MAX_CUSTOM_INDEXES_PER_OBJECT } from './MaxCustomIndexesPerObject';
|
||||
export { MAX_EMAIL_RECIPIENTS } from './MaxEmailRecipients';
|
||||
export { MULTI_ITEM_FIELD_DEFAULT_MAX_VALUES } from './MultiItemFieldDefaultMaxValues';
|
||||
|
||||
Reference in New Issue
Block a user