- Rebranding: BRAND_NAME env var (default AcadeDoc) replaces hardcoded "DocAdenice" in index.html title/meta, PWA manifest, app-header logo text, email footer/body - lib/config.ts: getAppName() reads BRAND_NAME; new getBrandLogoUrl/PrimaryColor/AccentColor helpers - vite.config.ts: BRAND_* vars exposed via define block to client - brand-theme.ts: getBrandTheme() generates 10-shade MantineColorsTuple from hex (no @mantine/colors-generator dep); merged into MantineProvider at boot - theme/__tests__/brand-theme.test.ts: 11 vitest tests (generateColorTuple + getBrandTheme) - Workspace branding: migration adds primary_color/accent_color to workspaces table WorkspaceBrandingService + WorkspaceBrandingController (POST /workspace/branding, POST /workspace/branding/update — admin only) + DTO hex validation - Settings: /settings/branding page (WorkspaceBranding) + sidebar entry (admin-only) - workspace-branding.spec.ts: 13 vitest tests (service + controller + DTO validation) - SMTP Brevo: .env.example preset block + transactional/README.md ops guide (key gen, port 587 STARTTLS, 300/day free limit, swaks/curl test) - environment.service.ts: getMailFromName() falls back to BRAND_NAME if MAIL_FROM_NAME unset - vitest.config.ts server: include pattern extended to src/core/workspace/spec/** - i18n: 11 branding keys added to en-US and fr-FR translations - 0 TypeScript errors client + server, 11 client + 13 server new tests all green Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
94 lines
2 KiB
TypeScript
94 lines
2 KiB
TypeScript
import { button as buttonStyle, container, footer, h1, logo, main } from '../css/styles';
|
|
import {
|
|
Body,
|
|
Container,
|
|
Head,
|
|
Html,
|
|
Row,
|
|
Section,
|
|
Text,
|
|
} from 'react-email';
|
|
import * as React from 'react';
|
|
|
|
interface MailBodyProps {
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export function MailBody({ children }: MailBodyProps) {
|
|
return (
|
|
<Html>
|
|
<Head />
|
|
<Body style={main}>
|
|
<MailHeader />
|
|
<Container style={container}>{children}</Container>
|
|
<MailFooter />
|
|
</Body>
|
|
</Html>
|
|
);
|
|
}
|
|
|
|
export function MailHeader() {
|
|
return (
|
|
<Section style={logo}>
|
|
{/* <Heading style={h1}>docmost</Heading> */}
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
interface EmailButtonProps {
|
|
href: string;
|
|
children: React.ReactNode;
|
|
}
|
|
|
|
export function EmailButton({ href, children }: EmailButtonProps) {
|
|
return (
|
|
<table
|
|
role="presentation"
|
|
cellPadding="0"
|
|
cellSpacing="0"
|
|
style={{ margin: '0 0 15px 15px' }}
|
|
>
|
|
<tr>
|
|
<td
|
|
style={{
|
|
backgroundColor: buttonStyle.backgroundColor,
|
|
borderRadius: buttonStyle.borderRadius,
|
|
textAlign: 'center' as const,
|
|
}}
|
|
>
|
|
<a
|
|
href={href}
|
|
target="_blank"
|
|
style={{
|
|
color: buttonStyle.color,
|
|
fontFamily: buttonStyle.fontFamily,
|
|
fontSize: buttonStyle.fontSize,
|
|
textDecoration: 'none',
|
|
display: 'inline-block',
|
|
padding: '8px 16px',
|
|
}}
|
|
>
|
|
{children}
|
|
</a>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
);
|
|
}
|
|
|
|
export function MailFooter() {
|
|
const brandName = process.env.BRAND_NAME || 'AcadeDoc';
|
|
return (
|
|
<Section style={footer}>
|
|
<Row>
|
|
<Text style={{ textAlign: 'center', color: '#706a7b' }}>
|
|
© {new Date().getFullYear()} {brandName}, All Rights Reserved <br />
|
|
</Text>
|
|
</Row>
|
|
</Section>
|
|
);
|
|
}
|
|
|
|
export function getGreetingName(name?: string): string {
|
|
return name?.split(' ')[0] || 'there';
|
|
}
|