Skip to content

Commit de0bfee

Browse files
committed
transactional subject override + MJML defaults + notif center language
1 parent 73a7db2 commit de0bfee

23 files changed

Lines changed: 655 additions & 26 deletions

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [27.4] - 2026-03-01
6+
7+
- **Notification Center**: Fixed browser language auto-save overwriting contact's manually chosen language. Now only auto-detects when contact has no language set.
8+
- **Email Builder**: Fixed missing MJML component defaults (border-radius, borders, background, direction, textAlign) causing styles not to render in the WYSIWYG editor
9+
- **Transactional Emails**: Added `subject` override to `email_options`, allowing dynamic email subject lines per API call with Liquid templating support (#281)
10+
511
## [27.3] - 2026-02-28
612

713
- **Segments**: Added template filter to email activity conditions, allowing segments like "opened template X at least 3 times"

config/config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import (
1414
"github.com/spf13/viper"
1515
)
1616

17-
const VERSION = "27.3"
17+
const VERSION = "27.4"
1818

1919
type Config struct {
2020
Server ServerConfig

console/package-lock.json

Lines changed: 15 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

console/src/components/email_builder/blocks/MjSectionBlock.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,15 @@ export class MjSectionBlock extends BaseEmailBlock {
373373
}),
374374
margin: '0px auto',
375375
borderRadius: attrs.borderRadius !== '0px' ? attrs.borderRadius : undefined,
376+
overflow: attrs.borderRadius !== '0px' ? 'hidden' : undefined,
376377
...selectionStyle
377378
}
378379

379380
// MJML table style - duplicates background for email client compatibility
380381
const tableStyle: React.CSSProperties = {
381382
backgroundColor: attrs.backgroundColor,
382383
width: '100%',
384+
borderRadius: attrs.borderRadius !== '0px' ? attrs.borderRadius : undefined,
383385
borderTop: attrs.borderTop !== 'none' ? attrs.borderTop : undefined,
384386
borderRight: attrs.borderRight !== 'none' ? attrs.borderRight : undefined,
385387
borderBottom: attrs.borderBottom !== 'none' ? attrs.borderBottom : undefined,

console/src/components/email_builder/mjml-defaults.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,23 @@ export const MJ_BODY_DEFAULTS = {
1515
export const MJ_WRAPPER_DEFAULTS = {
1616
backgroundColor: 'transparent',
1717
fullWidthBackgroundColor: 'transparent',
18+
backgroundUrl: '',
19+
backgroundRepeat: 'no-repeat',
20+
backgroundSize: 'auto',
21+
backgroundPosition: 'top center',
22+
border: 'none',
23+
borderTop: 'none',
24+
borderRight: 'none',
25+
borderBottom: 'none',
26+
borderLeft: 'none',
27+
borderRadius: '0px',
28+
direction: 'ltr' as const,
29+
fullWidth: '' as const,
1830
paddingTop: '0px',
1931
paddingRight: '0px',
2032
paddingBottom: '0px',
2133
paddingLeft: '0px',
34+
textAlign: 'center' as const,
2235
cssClass: ''
2336
}
2437

@@ -52,6 +65,7 @@ export const MJ_SECTION_DEFAULTS = {
5265
export const MJ_COLUMN_DEFAULTS = {
5366
width: '100%',
5467
verticalAlign: 'top' as const,
68+
direction: 'ltr' as const,
5569
backgroundColor: 'transparent',
5670
innerBackgroundColor: 'transparent',
5771
border: 'none',
@@ -77,6 +91,7 @@ export const MJ_COLUMN_DEFAULTS = {
7791
export const MJ_TEXT_DEFAULTS = {
7892
align: 'left' as const,
7993
color: '#000000',
94+
containerBackgroundColor: 'transparent',
8095
fontFamily: 'Arial, sans-serif',
8196
fontSize: '13px',
8297
fontStyle: 'normal' as const,
@@ -88,7 +103,8 @@ export const MJ_TEXT_DEFAULTS = {
88103
paddingRight: '25px',
89104
paddingBottom: '10px',
90105
paddingLeft: '25px',
91-
backgroundColor: 'transparent'
106+
backgroundColor: 'transparent',
107+
cssClass: ''
92108
}
93109

94110
// mj-button defaults
@@ -98,6 +114,7 @@ export const MJ_BUTTON_DEFAULTS = {
98114
borderRadius: '3px',
99115
border: 'none',
100116
color: '#ffffff',
117+
containerBackgroundColor: 'transparent',
101118
fontFamily: 'Arial, sans-serif',
102119
fontSize: '13px',
103120
fontStyle: 'normal' as const,
@@ -114,7 +131,8 @@ export const MJ_BUTTON_DEFAULTS = {
114131
textDecoration: 'none',
115132
textTransform: 'none' as const,
116133
verticalAlign: 'middle' as const,
117-
rel: 'noopener noreferrer'
134+
rel: 'noopener noreferrer',
135+
cssClass: ''
118136
}
119137

120138
// mj-image defaults
@@ -126,6 +144,7 @@ export const MJ_IMAGE_DEFAULTS = {
126144
border: 'none',
127145
borderRadius: '0px',
128146
containerBackgroundColor: 'transparent',
147+
cssClass: '',
129148
fluidOnMobile: 'false' as const,
130149
height: 'auto',
131150
href: '',
@@ -199,6 +218,7 @@ export const MJ_DIVIDER_DEFAULTS = {
199218
borderStyle: 'solid' as const,
200219
borderWidth: '4px',
201220
containerBackgroundColor: 'transparent',
221+
cssClass: '',
202222
paddingTop: '10px',
203223
paddingRight: '25px',
204224
paddingBottom: '10px',
@@ -210,6 +230,7 @@ export const MJ_DIVIDER_DEFAULTS = {
210230
export const MJ_SPACER_DEFAULTS = {
211231
height: '20px',
212232
containerBackgroundColor: 'transparent',
233+
cssClass: '',
213234
paddingTop: '0px',
214235
paddingRight: '0px',
215236
paddingBottom: '0px',
@@ -221,6 +242,7 @@ export const MJ_SOCIAL_DEFAULTS = {
221242
align: 'center' as const,
222243
borderRadius: '3px',
223244
containerBackgroundColor: 'transparent',
245+
cssClass: '',
224246
iconHeight: '20px',
225247
iconSize: '20px',
226248
innerPadding: '4px',

console/src/components/transactional/ApiCommandModal.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export const ApiCommandModal: React.FC<ApiCommandModalProps> = ({
131131
}
132132
133133
interface EmailOptions {
134+
subject?: string;
134135
reply_to?: string;
135136
cc?: string[];
136137
bcc?: string[];

console/src/pages/BroadcastsPage.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,7 @@ const BroadcastCard: React.FC<BroadcastCardProps> = ({
746746
return (
747747
<Tooltip title={t`0 opens out of 0 recipients`}>
748748
<>
749-
<FontAwesomeIcon icon={faEye} style={{ opacity: 0.7 }} />
749+
<FontAwesomeIcon icon={faEye} style={{ opacity: 0.7 }} className="text-purple-500" />
750750
<span className="cursor-help ml-1">{t`N/A`}</span>
751751
</>
752752
</Tooltip>
@@ -758,7 +758,7 @@ const BroadcastCard: React.FC<BroadcastCardProps> = ({
758758
return (
759759
<Tooltip title={t`${opens} opens out of ${recipients} recipients`}>
760760
<>
761-
<FontAwesomeIcon icon={faEye} style={{ opacity: 0.7 }} />{' '}
761+
<FontAwesomeIcon icon={faEye} style={{ opacity: 0.7 }} className="text-purple-500" />{' '}
762762
<span className="cursor-help ml-1">
763763
{(openRate * 100).toFixed(1)}%
764764
</span>
@@ -780,6 +780,7 @@ const BroadcastCard: React.FC<BroadcastCardProps> = ({
780780
<FontAwesomeIcon
781781
icon={faArrowPointer}
782782
style={{ opacity: 0.7 }}
783+
className="text-cyan-500"
783784
/>{' '}
784785
<span className="cursor-help ml-1">{t`N/A`}</span>
785786
</>
@@ -792,7 +793,7 @@ const BroadcastCard: React.FC<BroadcastCardProps> = ({
792793
return (
793794
<Tooltip title={t`${clicks} clicks out of ${recipients} recipients`}>
794795
<>
795-
<FontAwesomeIcon icon={faArrowPointer} style={{ opacity: 0.7 }} />{' '}
796+
<FontAwesomeIcon icon={faArrowPointer} style={{ opacity: 0.7 }} className="text-cyan-500" />{' '}
796797
<span className="cursor-help ml-1">
797798
{(clickRate * 100).toFixed(1)}%
798799
</span>

console/src/services/api/transactional_notifications.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ export interface Attachment {
3636

3737
export interface EmailOptions {
3838
from_name?: string
39+
subject?: string
3940
reply_to?: string
4041
cc?: string[]
4142
bcc?: string[]

internal/domain/email_provider.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ func (e *EmailProvider) DecryptSecretKeys(passphrase string) error {
311311

312312
type EmailOptions struct {
313313
FromName *string `json:"from_name,omitempty"` // Override default sender from name
314+
Subject *string `json:"subject,omitempty"` // Override template subject
314315
CC []string `json:"cc,omitempty"`
315316
BCC []string `json:"bcc,omitempty"`
316317
ReplyTo string `json:"reply_to,omitempty"`
@@ -321,6 +322,7 @@ type EmailOptions struct {
321322
// IsEmpty returns true if no email options are set
322323
func (eo EmailOptions) IsEmpty() bool {
323324
return eo.FromName == nil &&
325+
eo.Subject == nil &&
324326
len(eo.CC) == 0 &&
325327
len(eo.BCC) == 0 &&
326328
eo.ReplyTo == ""
@@ -335,6 +337,7 @@ func (eo EmailOptions) ToChannelOptions() *ChannelOptions {
335337

336338
return &ChannelOptions{
337339
FromName: eo.FromName,
340+
Subject: eo.Subject,
338341
CC: eo.CC,
339342
BCC: eo.BCC,
340343
ReplyTo: eo.ReplyTo,

0 commit comments

Comments
 (0)