Files
Sergey Zotov c090bfcf47 Initial commit — Aspekter VA email builder
Full project: Svelte 5 frontend, Vite 7 backend API,
Pug email templates (email-gen), Docker deployment.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-13 01:21:00 +05:00

324 lines
17 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Reaspekt master shared factory mixins
mixin ctaButtonSection(opts = {})
- const width = opts.width || 560
- const text = opts.text || '#ТЕКСТ#'
- const href = opts.href || '#ССЫЛКА#'
- const buttonBg = opts.buttonBg || '#ffffff'
- const buttonText = opts.buttonText || '#130F33'
- const iconSrc = opts.iconSrc || 'https://574922.selcdn.ru/email.static/reaspekt/2024_newsletters/2024_09_29/icon-watch-white.png'
if width === 560
tr
td.padding-wrapper
+buttonRounded(text, href, 560, 60, buttonBg, 16, buttonText, 0, '', iconSrc, 20, 17, 1, 'right').textVerdana
else if width === 270
tr
td.padding-wrapper
+defaultTable('560')
tr
td(width='270')
+buttonRounded(text, href, 270, 60, buttonBg, 16, buttonText, 0, '', iconSrc, 20, 17, 1, 'right').textVerdana
+tdFixed(20)
td(width='270')
+buttonRounded(text, href, 270, 60, buttonBg, 16, buttonText, 0, '', iconSrc, 20, 17, 1, 'right').textVerdana
else if width === 173
tr
td.padding-wrapper
+defaultTable('560')
tr
td(width='173')
+buttonRounded(text, href, 173, 60, buttonBg, 16, buttonText, 0, '', iconSrc, 20, 17, 1, 'right').textVerdana
+tdFixed(21)
td(width='173')
+buttonRounded(text, href, 173, 60, buttonBg, 16, buttonText, 0, '', iconSrc, 20, 17, 1, 'right').textVerdana
+tdFixed(20)
td(width='173')
+buttonRounded(text, href, 173, 60, buttonBg, 16, buttonText, 0, '', iconSrc, 20, 17, 1, 'right').textVerdana
mixin ctaLinkSection(opts = {})
- const width = opts.width || 560
- const href = opts.href || ''
- const linkText = opts.linkText || (width === 173 ? 'Любой текст' : 'Как не терять наши письма?')
- const colorClass = opts.colorClass || 'color__blue'
- const linkClass = opts.linkClass || 'text__link-blue'
- const linkColorClass = opts.linkColorClass || textClass
- const tableWidth = opts.tableWidth || '560'
if width === 560
tr
td(align='center').padding-wrapper
a(href=href target='_blank').textVerdana(class=`${colorClass} text__link ${linkClass}`)= linkText
else if width === 270
tr
td.padding-wrapper
+defaultTable(tableWidth)
tr
td(width='270' align='center')
a(href=href target='_blank').textVerdana(class=`${colorClass} text__link ${linkClass}`)= linkText
+tdFixed(20)
td(width='270' align='center')
a(href=href target='_blank').textVerdana(class=`${colorClass} text__link ${linkClass}`)= linkText
else if width === 173
tr
td.padding-wrapper
+defaultTable(tableWidth)
tr
td(width='173' align='center')
a(href=href target='_blank').textVerdana(class=`${colorClass} text__link ${linkClass}`)= linkText
+tdFixed(21)
td(width='173' align='center')
a(href=href target='_blank').textVerdana(class=`${colorClass} text__link ${linkClass}`)= linkText
+tdFixed(20)
td(width='173' align='center')
a(href=href target='_blank').textVerdana(class=`${colorClass} text__link ${linkClass}`)= linkText
mixin contentCardInner(opts = {})
- const width = opts.width || 560
- const titleClass = opts.titleClass || 'color__blue'
- const textClass = opts.textClass || 'color__blue'
- const titleSizeClass = opts.titleSizeClass || 'header__h1'
- const title = opts.title || 'Контекстная реклама для увеличения продаж'
- const text = opts.text || 'Наша команда стала активнее участвовать в образовательных мероприятиях, на которых мы делимся тонкостями своей работы и рассказываем о практическом опыте. Решили поделиться с вами записями прошедших вебинаров. Вот сводка тем:'
- const buttonBg = opts.buttonBg || '#ffffff'
- const buttonText = opts.buttonText || '#130F33'
- const withImage = !!opts.withImage
- const imageSrc = opts.imageSrc || 'https://574922.selcdn.ru/email.static/reaspekt/master-tamplate/banners/banner-50-percent.jpg'
+defaultTable(`${width}`)
if withImage
tr
td
+backgroundImageBlock(imageSrc, width, 180, '#ffffff', 'left', 'top')
+spacerLine(20)
tr
td
span.textVerdana(class=`${titleSizeClass} ${titleClass}`)= title
+spacerLine(20)
if opts.firstColumnExtraGap
+spacerLine(20)
tr
td
span.textVerdana.text__normal(class=textClass)= text
+spacerLine(20)
tr
td
+buttonRounded('#ТЕКСТ#', '#ССЫЛКА#', width, 60, buttonBg, 16, buttonText, 0, '').textVerdana
mixin textSection560(opts = {})
tr
td.padding-wrapper(class=opts.bgClass || 'background__white')
+defaultTable('560')
+spacerLine(40)
tr
td
span.textVerdana.header__h1(class=opts.titleClass || 'color__blue') Мы продолжаем делать вебинары для вас, а чтобы следить за актуальными темами, подписывайтесь на наше сообщество ВКонтакте
+spacerLine(20)
tr
td
span.textVerdana.text__normal(class=opts.textClass || 'color__blue') Наша команда стала активнее участвовать в образовательных мероприятиях, на которых мы делимся тонкостями своей работы и рассказываем о практическом опыте. Решили поделиться с вами записями прошедших вебинаров. Вот сводка тем:
+spacerLine(20)
tr
td
+buttonRounded('#ТЕКСТ#', '#ССЫЛКА#', 560, 60, opts.buttonBg || '#ffffff', 16, opts.buttonText || '#130F33', 0, '').textVerdana
+spacerLine(40)
mixin textSection270(opts = {})
tr
td.padding-wrapper(class=opts.bgClass || 'background__white')
+defaultTable('560')
+spacerLine(40, 3)
tr
td(valign='top')
+contentCardInner({
width: 270,
withImage: true,
titleClass: opts.titleClass,
textClass: opts.textClass,
buttonBg: opts.buttonBg,
buttonText: opts.buttonText
})
+tdFixed(20)
td(valign='top')
+contentCardInner({
width: 270,
withImage: true,
titleClass: opts.titleClass,
textClass: opts.textClass,
buttonBg: opts.buttonBg,
buttonText: opts.buttonText
})
+spacerLine(40, 3)
mixin textSection173(opts = {})
tr
td.padding-wrapper(class=opts.bgClass || 'background__white')
+defaultTable('560')
+spacerLine(40, 5)
mixin textImageSection560(opts = {})
- const title = opts.title || 'Мы продолжаем делать вебинары для вас, а чтобы следить за актуальными темами, подписывайтесь на наше сообщество ВКонтакте'
- const text = opts.text || 'Наша команда стала активнее участвовать в образовательных мероприятиях, на которых мы делимся тонкостями своей работы и рассказываем о практическом опыте. Решили поделиться с вами записями прошедших вебинаров. Вот сводка тем:'
- const imageSrc = opts.imageSrc || 'https://574922.selcdn.ru/email.static/reaspekt/master-tamplate/banners/image.jpg'
- const bgClass = opts.bgClass || 'background__white'
- const titleClass = opts.titleClass || 'color__blue'
- const textClass = opts.textClass || 'color__blue'
- const buttonBg = opts.buttonBg || '#ffffff'
- const buttonText = opts.buttonText || '#130F33'
- const showTitle = opts.showTitle !== false
- const showText = opts.showText !== false
- const showButton = !!opts.showButton
- const textBeforeImage = !!opts.textBeforeImage
- const center = !!opts.center
- const linkMode = !!opts.linkMode
- const linkHref = opts.linkHref || ''
- const linkText = opts.linkText || 'Как не терять наши письма?'
- const linkClass = opts.linkClass || 'text__link-blue'
tr
td.padding-wrapper(class=bgClass)
+defaultTable('560')
+spacerLine(40)
if showTitle && !textBeforeImage
tr
td(class=center ? 'text__center' : '')
span.textVerdana.header__h1(class=titleClass)= title
+spacerLine(20)
if showText && textBeforeImage
tr
td(class=center ? 'text__center' : '')
span.textVerdana.text__normal(class=textClass)= text
+spacerLine(20)
tr
td
+backgroundImageBlock(imageSrc, 560, 266, '#ffffff', 'left', 'top')
if showText && !textBeforeImage
+spacerLine(20)
tr
td(class=center ? 'text__center' : '')
span.textVerdana.text__normal(class=textClass)= text
if showButton
+spacerLine(20)
tr
td
if linkMode
a(href=linkHref target='_blank' style='width: 100%;').textVerdana(class=`${linkColorClass} text__link ${linkClass}`)= linkText
else
+buttonRounded('#ТЕКСТ#', '#ССЫЛКА#', 560, 60, buttonBg, 16, buttonText, 0, '').textVerdana
+spacerLine(40)
mixin contentCardImage270(opts = {})
- const title = opts.title || 'Контекстная реклама для увеличения продаж'
- const text = opts.text || 'Наша команда стала активнее участвовать в образовательных мероприятиях, на которых мы делимся тонкостями своей работы и рассказываем о практическом опыте. Решили поделиться с вами записями прошедших вебинаров. Вот сводка тем:'
- const imageSrc = opts.imageSrc || 'https://574922.selcdn.ru/email.static/reaspekt/master-tamplate/banners/image.jpg'
- const titleClass = opts.titleClass || 'color__blue'
- const textClass = opts.textClass || 'color__blue'
- const buttonBg = opts.buttonBg || '#ffffff'
- const buttonText = opts.buttonText || '#130F33'
- const showButton = !!opts.showButton
- const linkMode = !!opts.linkMode
- const linkHref = opts.linkHref || ''
- const linkText = opts.linkText || 'Как не терять наши письма?'
- const linkClass = opts.linkClass || 'text__link-blue'
- const linkColorClass = opts.linkColorClass || textClass
+defaultTable('270')
tr
td
+backgroundImageBlock(imageSrc, 270, 270, '#ffffff', 'left', 'top')
+spacerLine(20)
tr
td
span.textVerdana.header__h2(class=titleClass)= title
+spacerLine(20)
tr
td
span.textVerdana.text__normal(class=textClass)= text
if showButton
+spacerLine(20)
tr
td
if linkMode
a(href=linkHref target='_blank' style='width: 100%;').textVerdana(class=`${linkColorClass} text__link ${linkClass}`)= linkText
else
+buttonRounded('#ТЕКСТ#', '#ССЫЛКА#', 270, 60, buttonBg, 16, buttonText, 0, '').textVerdana
mixin textImageSection270(opts = {})
- const bgClass = opts.bgClass || 'background__white'
tr
td.padding-wrapper(class=bgClass)
+defaultTable('560')
+spacerLine(40, 3)
tr
td(valign='top')
+contentCardImage270(opts)
+tdFixed(20)
td(valign='top')
+contentCardImage270(opts)
+spacerLine(40, 3)
tr
td(valign='top')
+contentCardInner({
width: 173,
withImage: false,
titleSizeClass: 'header__h2',
titleClass: opts.titleClass,
textClass: opts.textClass,
buttonBg: opts.buttonBg,
buttonText: opts.buttonText,
firstColumnExtraGap: !!opts.firstColumnExtraGap
})
+tdFixed(20)
td(valign='top')
+contentCardInner({
width: 173,
withImage: false,
titleSizeClass: 'header__h2',
titleClass: opts.titleClass,
textClass: opts.textClass,
buttonBg: opts.buttonBg,
buttonText: opts.buttonText
})
+tdFixed(21)
td(valign='top')
+contentCardInner({
width: 173,
withImage: false,
titleSizeClass: 'header__h2',
titleClass: opts.titleClass,
textClass: opts.textClass,
buttonBg: opts.buttonBg,
buttonText: opts.buttonText
})
+spacerLine(40, 5)
mixin sideImageTextSection(opts = {})
- const bgClass = opts.bgClass || 'background__blue'
- const textClass = opts.textClass || 'color__white'
- const imageSrc = opts.imageSrc || 'https://574922.selcdn.ru/email.static/reaspekt/master-tamplate/banners/icons-box-blue.png'
- const imageBg = opts.imageBg || '#130F33'
- const text1 = opts.text1 || 'Искусственный интеллект может ускорить работу SEO-специалистов и&nbsp;оптимизировать затраты. Заменяет&nbsp;ли&nbsp;chatGPT копирайтера? Всем&nbsp;ли&nbsp;поможет такой подход? Об&nbsp;этом и&nbsp;не&nbsp;только узнайте по&nbsp;ссылке.'
- const text2 = opts.text2 || 'Наша команда стала активнее участвовать в&nbsp;образовательных мероприятиях, на&nbsp;которых мы&nbsp;делимся тонкостями своей работы....'
- const showSecondText = opts.showSecondText !== false
tr
td(class=bgClass)
+defaultTable('100%')
tr
td.paddingWrapper
+defaultTable('100%')
+spacerLine(40)
tr
td
span.textVerdana.text__normal(class=textClass)!= text1
if showSecondText
tr
td
span.textVerdana.text__normal(class=textClass)!= text2
+spacerLine(40)
td(valign='bottom')
+defaultTable('')
+trtd
+backgroundImageBlock(imageSrc, 145, 270, imageBg, 'center', 'top', 'contain')