add newsletter and email campaigns

Subscribers with double opt-in confirmation, campaign composer with
draft/scheduled/sent lifecycle, admin dashboard with overview stats,
CSV export, and shop signup form wired into page builder blocks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-28 23:25:28 +00:00
parent 8f989d892d
commit ad2e6d1e6d
32 changed files with 2497 additions and 32 deletions

View File

@@ -132,6 +132,7 @@
.mt-8 { margin-top: 2rem; }
.mt-10 { margin-top: 2.5rem; }
.-mt-1 { margin-top: -0.25rem; }
.-mb-px { margin-bottom: -1px; }
.mb-0\.5 { margin-bottom: 0.125rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
@@ -158,6 +159,7 @@
======================================== */
.w-0 { width: 0; }
.w-1\.5 { width: 0.375rem; }
.w-3 { width: 0.75rem; }
.w-4 { width: 1rem; }
.w-5 { width: 1.25rem; }
@@ -202,6 +204,7 @@
.min-w-48 { min-width: 12rem; }
.max-h-full { max-height: 100%; }
.max-h-64 { max-height: 16rem; }
.max-w-80 { max-width: 20rem; }
.max-w-sm { max-width: 24rem; }
.max-w-md { max-width: 28rem; }
@@ -241,6 +244,7 @@
.text-wrap { text-wrap: wrap; }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.whitespace-pre-wrap { white-space: pre-wrap; }
.break-all { word-break: break-all; }
.uppercase { text-transform: uppercase; }
.capitalize { text-transform: capitalize; }
@@ -308,17 +312,20 @@
.border-green-200 { border-color: #bbf7d0; }
.bg-red-50 { background-color: #fef2f2; }
.bg-red-500 { background-color: #ef4444; }
.text-red-600 { color: #dc2626; }
.text-red-700 { color: #b91c1c; }
.bg-amber-50 { background-color: #fffbeb; }
.bg-amber-100 { background-color: #fef3c7; }
.bg-amber-500 { background-color: #f59e0b; }
.text-amber-600 { color: #d97706; }
.text-amber-700 { color: #b45309; }
.text-amber-800 { color: #92400e; }
.text-amber-900 { color: #78350f; }
.bg-blue-50 { background-color: #eff6ff; }
.bg-blue-500 { background-color: #3b82f6; }
.text-blue-700 { color: #1d4ed8; }
.bg-purple-50 { background-color: #faf5ff; }
@@ -342,6 +349,7 @@
.border-t-0 { border-top-width: 0; }
.border-dashed { border-style: dashed; }
.border-transparent { border-color: transparent; }
.border-base-200 { border-color: var(--t-surface-sunken); }
.border-base-300 { border-color: var(--t-border-default); }
.border-base-content\/20 { border-color: color-mix(in oklch, var(--t-text-primary) 20%, transparent); }
@@ -375,6 +383,10 @@
Ring (outline via box-shadow)
======================================== */
.ring-0 {
--tw-ring-shadow: 0 0 #0000;
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.ring-1 {
--tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 1px var(--tw-ring-color);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -391,6 +403,10 @@
Shadow
======================================== */
.shadow {
--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
}
.shadow-xs {
--tw-shadow: 0 1px rgb(0 0 0 / 0.05);
box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -427,6 +443,7 @@
======================================== */
.cursor-pointer { cursor: pointer; }
.pointer-events-none { pointer-events: none; }
/* ========================================
Gradient