@locusgraph/email 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +27 -2
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/kit/email-layout.d.ts +8 -0
- package/dist/kit/email-layout.js +20 -0
- package/dist/kit/parts.d.ts +18 -0
- package/dist/kit/parts.js +49 -0
- package/dist/kit/theme.d.ts +21 -0
- package/dist/kit/theme.js +8 -0
- package/dist/render.d.ts +7 -0
- package/dist/render.js +19 -0
- package/dist/templates/invitation.d.ts +9 -0
- package/dist/templates/invitation.js +10 -0
- package/dist/templates/low-credits.d.ts +7 -0
- package/dist/templates/low-credits.js +15 -0
- package/dist/templates/receipt.d.ts +9 -0
- package/dist/templates/receipt.js +15 -0
- package/dist/templates/usage-over-plan.d.ts +13 -0
- package/dist/templates/usage-over-plan.js +14 -0
- package/package.json +48 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 LocusGraph
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @locusgraph/email
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
LocusGraph transactional emails as React templates. Square panel, hairline borders and a pill button, with colors from `@locusgraph/tokens`. Everything is inline-styled tables, so it renders in Gmail, Outlook and Apple Mail. No React Email dependency.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pnpm add @locusgraph/email react react-dom
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { invitationEmail } from "@locusgraph/email";
|
|
11
|
+
|
|
12
|
+
const { subject, html, text } = invitationEmail({
|
|
13
|
+
organizationName: "Acme Inc",
|
|
14
|
+
inviterName: "Dana Rivera",
|
|
15
|
+
role: "member",
|
|
16
|
+
acceptUrl,
|
|
17
|
+
expiresOn: "Oct 24, 2026",
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Function | Sends |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `invitationEmail` | Invitation to join an organization |
|
|
24
|
+
| `receiptEmail` | A payment receipt, amount in cents |
|
|
25
|
+
| `lowCreditsEmail` | Low or empty wallet, balance in micro-USD |
|
|
26
|
+
| `usageOverPlanEmail` | Quotas over the plan |
|
|
27
|
+
|
|
28
|
+
Each returns `{ subject, html, text }`; pass all three to your mail provider. Build new templates from `EmailLayout`, `EmailLabel`, `EmailHeading`, `EmailText`, `EmailButton` and `EmailRows`, then `renderEmail(subject, element)`.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { EmailLayout } from "./kit/email-layout.js";
|
|
2
|
+
export { EmailButton, EmailHeading, EmailLabel, EmailRows, EmailText } from "./kit/parts.js";
|
|
3
|
+
export { type RenderedEmail, renderEmail } from "./render.js";
|
|
4
|
+
export * from "./templates/invitation.js";
|
|
5
|
+
export * from "./templates/low-credits.js";
|
|
6
|
+
export * from "./templates/receipt.js";
|
|
7
|
+
export * from "./templates/usage-over-plan.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { EmailLayout } from "./kit/email-layout.js";
|
|
2
|
+
export { EmailButton, EmailHeading, EmailLabel, EmailRows, EmailText } from "./kit/parts.js";
|
|
3
|
+
export { renderEmail } from "./render.js";
|
|
4
|
+
export * from "./templates/invitation.js";
|
|
5
|
+
export * from "./templates/low-credits.js";
|
|
6
|
+
export * from "./templates/receipt.js";
|
|
7
|
+
export * from "./templates/usage-over-plan.js";
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { contentWidth, emailColors, emailFonts } from "./theme.js";
|
|
3
|
+
export function EmailLayout({ preview, children, footer }) {
|
|
4
|
+
return (_jsxs("html", { lang: "en", children: [_jsxs("head", { children: [_jsx("meta", { httpEquiv: "Content-Type", content: "text/html; charset=UTF-8" }), _jsx("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }), _jsx("meta", { name: "color-scheme", content: "light" })] }), _jsxs("body", { style: {
|
|
5
|
+
margin: 0,
|
|
6
|
+
padding: 0,
|
|
7
|
+
backgroundColor: emailColors.page,
|
|
8
|
+
fontFamily: emailFonts.sans,
|
|
9
|
+
}, children: [_jsx("div", { style: { display: "none", maxHeight: 0, overflow: "hidden" }, children: preview }), _jsx("table", { role: "presentation", width: "100%", cellPadding: 0, cellSpacing: 0, style: { backgroundColor: emailColors.page }, children: _jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { align: "center", style: { padding: "40px 16px" }, children: _jsx("table", { role: "presentation", width: contentWidth, cellPadding: 0, cellSpacing: 0, style: { maxWidth: contentWidth, width: "100%" }, children: _jsxs("tbody", { children: [_jsx("tr", { children: _jsxs("td", { style: { paddingBottom: 16, fontSize: 16, color: emailColors.foreground }, children: [_jsx("strong", { children: "locus" }), _jsx("span", { style: { fontWeight: 300 }, children: "graph" })] }) }), _jsx("tr", { children: _jsx("td", { style: {
|
|
10
|
+
backgroundColor: emailColors.surface,
|
|
11
|
+
border: `1px solid ${emailColors.line}`,
|
|
12
|
+
padding: 32,
|
|
13
|
+
color: emailColors.foreground,
|
|
14
|
+
}, children: children }) }), footer && (_jsx("tr", { children: _jsx("td", { style: {
|
|
15
|
+
paddingTop: 16,
|
|
16
|
+
fontSize: 12,
|
|
17
|
+
lineHeight: "18px",
|
|
18
|
+
color: emailColors.muted,
|
|
19
|
+
}, children: footer }) }))] }) }) }) }) }) })] })] }));
|
|
20
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
export declare function EmailLabel({ children }: {
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
}): React.JSX.Element;
|
|
5
|
+
export declare function EmailHeading({ children }: {
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
}): React.JSX.Element;
|
|
8
|
+
export declare function EmailText({ children, muted, }: {
|
|
9
|
+
children: React.ReactNode;
|
|
10
|
+
muted?: boolean;
|
|
11
|
+
}): React.JSX.Element;
|
|
12
|
+
export declare function EmailButton({ href, children }: {
|
|
13
|
+
href: string;
|
|
14
|
+
children: React.ReactNode;
|
|
15
|
+
}): React.JSX.Element;
|
|
16
|
+
export declare function EmailRows({ rows }: {
|
|
17
|
+
rows: [label: string, value: string][];
|
|
18
|
+
}): React.JSX.Element;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { emailColors, emailFonts } from "./theme.js";
|
|
3
|
+
export function EmailLabel({ children }) {
|
|
4
|
+
return (_jsx("p", { style: {
|
|
5
|
+
margin: "0 0 8px",
|
|
6
|
+
fontFamily: emailFonts.mono,
|
|
7
|
+
fontSize: 12,
|
|
8
|
+
color: emailColors.muted,
|
|
9
|
+
}, children: children }));
|
|
10
|
+
}
|
|
11
|
+
export function EmailHeading({ children }) {
|
|
12
|
+
return (_jsx("h1", { style: {
|
|
13
|
+
margin: "0 0 12px",
|
|
14
|
+
fontSize: 22,
|
|
15
|
+
lineHeight: "28px",
|
|
16
|
+
fontWeight: 600,
|
|
17
|
+
letterSpacing: "-0.02em",
|
|
18
|
+
}, children: children }));
|
|
19
|
+
}
|
|
20
|
+
export function EmailText({ children, muted = false, }) {
|
|
21
|
+
return (_jsx("p", { style: {
|
|
22
|
+
margin: "0 0 16px",
|
|
23
|
+
fontSize: 15,
|
|
24
|
+
lineHeight: "24px",
|
|
25
|
+
color: muted ? emailColors.muted : emailColors.foreground,
|
|
26
|
+
}, children: children }));
|
|
27
|
+
}
|
|
28
|
+
export function EmailButton({ href, children }) {
|
|
29
|
+
return (_jsx("table", { role: "presentation", cellPadding: 0, cellSpacing: 0, style: { margin: "8px 0 16px" }, children: _jsx("tbody", { children: _jsx("tr", { children: _jsx("td", { style: { backgroundColor: emailColors.btn, borderRadius: 9999 }, children: _jsx("a", { href: href, style: {
|
|
30
|
+
display: "inline-block",
|
|
31
|
+
padding: "10px 20px",
|
|
32
|
+
fontSize: 14,
|
|
33
|
+
fontWeight: 600,
|
|
34
|
+
color: emailColors.btnInk,
|
|
35
|
+
textDecoration: "none",
|
|
36
|
+
}, children: children }) }) }) }) }));
|
|
37
|
+
}
|
|
38
|
+
export function EmailRows({ rows }) {
|
|
39
|
+
return (_jsx("table", { role: "presentation", width: "100%", cellPadding: 0, cellSpacing: 0, style: { margin: "8px 0 16px", borderTop: `1px solid ${emailColors.line}` }, children: _jsx("tbody", { children: rows.map(([label, value]) => (_jsxs("tr", { children: [_jsx("td", { style: {
|
|
40
|
+
padding: "10px 0",
|
|
41
|
+
fontSize: 14,
|
|
42
|
+
color: emailColors.muted,
|
|
43
|
+
borderBottom: `1px solid ${emailColors.line}`,
|
|
44
|
+
}, children: label }), _jsx("td", { align: "right", style: {
|
|
45
|
+
padding: "10px 0",
|
|
46
|
+
fontSize: 14,
|
|
47
|
+
borderBottom: `1px solid ${emailColors.line}`,
|
|
48
|
+
}, children: value })] }, label))) }) }));
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export declare const emailColors: {
|
|
2
|
+
page: string;
|
|
3
|
+
line: string;
|
|
4
|
+
bad: string;
|
|
5
|
+
brandGlow: string;
|
|
6
|
+
btn: string;
|
|
7
|
+
btnInk: string;
|
|
8
|
+
faint: string;
|
|
9
|
+
foreground: string;
|
|
10
|
+
ghost: string;
|
|
11
|
+
good: string;
|
|
12
|
+
info: string;
|
|
13
|
+
muted: string;
|
|
14
|
+
surface: string;
|
|
15
|
+
warn: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const emailFonts: {
|
|
18
|
+
sans: "Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif";
|
|
19
|
+
mono: "'Fira Code', SFMono-Regular, Menlo, Consolas, monospace";
|
|
20
|
+
};
|
|
21
|
+
export declare const contentWidth = 560;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { fonts, hairline, lightColors } from "@locusgraph/tokens";
|
|
2
|
+
export const emailColors = {
|
|
3
|
+
...lightColors,
|
|
4
|
+
page: "#f4f4f5",
|
|
5
|
+
line: hairline(lightColors),
|
|
6
|
+
};
|
|
7
|
+
export const emailFonts = { sans: fonts.emailSafeSans, mono: fonts.emailSafeMono };
|
|
8
|
+
export const contentWidth = 560;
|
package/dist/render.d.ts
ADDED
package/dist/render.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
2
|
+
function plainText(html) {
|
|
3
|
+
return html
|
|
4
|
+
.replace(/<div style="display:none[^>]*>[\s\S]*?<\/div>/, "")
|
|
5
|
+
.replace(/<a [^>]*href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g, "$2: $1")
|
|
6
|
+
.replace(/<\/(p|h1|tr|table)>/g, "\n")
|
|
7
|
+
.replace(/<\/td>/g, " ")
|
|
8
|
+
.replace(/<[^>]+>/g, "")
|
|
9
|
+
.replace(/&/g, "&")
|
|
10
|
+
.replace(/'|'/g, "'")
|
|
11
|
+
.replace(/"/g, '"')
|
|
12
|
+
.replace(/[ \t]+/g, " ")
|
|
13
|
+
.replace(/\n\s*\n+/g, "\n\n")
|
|
14
|
+
.trim();
|
|
15
|
+
}
|
|
16
|
+
export function renderEmail(subject, email) {
|
|
17
|
+
const html = `<!DOCTYPE html>${renderToStaticMarkup(email)}`;
|
|
18
|
+
return { subject, html, text: plainText(html) };
|
|
19
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type InvitationEmailProps = {
|
|
2
|
+
organizationName: string;
|
|
3
|
+
inviterName: string;
|
|
4
|
+
role: string;
|
|
5
|
+
acceptUrl: string;
|
|
6
|
+
expiresOn: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function InvitationEmail({ organizationName, inviterName, role, acceptUrl, expiresOn, }: InvitationEmailProps): import("react").JSX.Element;
|
|
9
|
+
export declare function invitationEmail(props: InvitationEmailProps): import("..").RenderedEmail;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { EmailLayout } from "../kit/email-layout.js";
|
|
3
|
+
import { EmailButton, EmailHeading, EmailLabel, EmailText } from "../kit/parts.js";
|
|
4
|
+
import { renderEmail } from "../render.js";
|
|
5
|
+
export function InvitationEmail({ organizationName, inviterName, role, acceptUrl, expiresOn, }) {
|
|
6
|
+
return (_jsxs(EmailLayout, { preview: `${inviterName} invited you to ${organizationName}`, footer: `The invitation expires on ${expiresOn}. If you did not expect it, ignore this email.`, children: [_jsx(EmailLabel, { children: "Invitation" }), _jsxs(EmailHeading, { children: ["Join ", organizationName, " on LocusGraph"] }), _jsxs(EmailText, { children: [inviterName, " invited you to ", organizationName, " as ", role, ". Sign in with Google or GitHub to accept."] }), _jsx(EmailButton, { href: acceptUrl, children: "Accept invitation" })] }));
|
|
7
|
+
}
|
|
8
|
+
export function invitationEmail(props) {
|
|
9
|
+
return renderEmail(`Join ${props.organizationName} on LocusGraph`, _jsx(InvitationEmail, { ...props }));
|
|
10
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type LowCreditsEmailProps = {
|
|
2
|
+
organizationName: string;
|
|
3
|
+
balanceMicroUsd: number;
|
|
4
|
+
buyCreditsUrl: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function LowCreditsEmail({ organizationName, balanceMicroUsd, buyCreditsUrl, }: LowCreditsEmailProps): import("react").JSX.Element;
|
|
7
|
+
export declare function lowCreditsEmail(props: LowCreditsEmailProps): import("..").RenderedEmail;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { formatMicroUsd } from "@locusgraph/utils";
|
|
3
|
+
import { EmailLayout } from "../kit/email-layout.js";
|
|
4
|
+
import { EmailButton, EmailHeading, EmailLabel, EmailText } from "../kit/parts.js";
|
|
5
|
+
import { renderEmail } from "../render.js";
|
|
6
|
+
export function LowCreditsEmail({ organizationName, balanceMicroUsd, buyCreditsUrl, }) {
|
|
7
|
+
const empty = balanceMicroUsd <= 0;
|
|
8
|
+
return (_jsxs(EmailLayout, { preview: empty ? `${organizationName} is out of credits` : `${organizationName} is low on credits`, children: [_jsx(EmailLabel, { children: "Credits" }), _jsx(EmailHeading, { children: empty ? "Out of credits" : `${formatMicroUsd(balanceMicroUsd)} of credits left` }), _jsx(EmailText, { children: empty
|
|
9
|
+
? `Paid usage for ${organizationName} is paused until you add credits.`
|
|
10
|
+
: `Add credits so paid usage for ${organizationName} does not stop.` }), _jsx(EmailButton, { href: buyCreditsUrl, children: "Buy credits" })] }));
|
|
11
|
+
}
|
|
12
|
+
export function lowCreditsEmail(props) {
|
|
13
|
+
const subject = props.balanceMicroUsd <= 0 ? "You are out of credits" : "Your credits are running low";
|
|
14
|
+
return renderEmail(`${subject} on ${props.organizationName}`, _jsx(LowCreditsEmail, { ...props }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type ReceiptEmailProps = {
|
|
2
|
+
organizationName: string;
|
|
3
|
+
description: string;
|
|
4
|
+
amountCents: number;
|
|
5
|
+
paidOn: string;
|
|
6
|
+
receiptUrl: string;
|
|
7
|
+
};
|
|
8
|
+
export declare function ReceiptEmail({ organizationName, description, amountCents, paidOn, receiptUrl, }: ReceiptEmailProps): import("react").JSX.Element;
|
|
9
|
+
export declare function receiptEmail(props: ReceiptEmailProps): import("..").RenderedEmail;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { formatCents } from "@locusgraph/utils";
|
|
3
|
+
import { EmailLayout } from "../kit/email-layout.js";
|
|
4
|
+
import { EmailButton, EmailHeading, EmailLabel, EmailRows, EmailText } from "../kit/parts.js";
|
|
5
|
+
import { renderEmail } from "../render.js";
|
|
6
|
+
export function ReceiptEmail({ organizationName, description, amountCents, paidOn, receiptUrl, }) {
|
|
7
|
+
return (_jsxs(EmailLayout, { preview: `${formatCents(amountCents)} paid for ${organizationName}`, footer: "Stripe processed this payment. The receipt link shows the full invoice.", children: [_jsx(EmailLabel, { children: "Receipt" }), _jsxs(EmailHeading, { children: [formatCents(amountCents), " paid"] }), _jsxs(EmailText, { children: ["Thanks. Here is what ", organizationName, " paid for."] }), _jsx(EmailRows, { rows: [
|
|
8
|
+
["Item", description],
|
|
9
|
+
["Date", paidOn],
|
|
10
|
+
["Total", formatCents(amountCents)],
|
|
11
|
+
] }), _jsx(EmailButton, { href: receiptUrl, children: "View receipt" })] }));
|
|
12
|
+
}
|
|
13
|
+
export function receiptEmail(props) {
|
|
14
|
+
return renderEmail(`Receipt: ${formatCents(props.amountCents)} for ${props.organizationName}`, _jsx(ReceiptEmail, { ...props }));
|
|
15
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type OverQuota = {
|
|
2
|
+
key: string;
|
|
3
|
+
used: number;
|
|
4
|
+
included: number;
|
|
5
|
+
periodLabel: string;
|
|
6
|
+
};
|
|
7
|
+
export type UsageOverPlanEmailProps = {
|
|
8
|
+
organizationName: string;
|
|
9
|
+
quotas: OverQuota[];
|
|
10
|
+
usageUrl: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function UsageOverPlanEmail({ organizationName, quotas, usageUrl, }: UsageOverPlanEmailProps): import("react").JSX.Element;
|
|
13
|
+
export declare function usageOverPlanEmail(props: UsageOverPlanEmailProps): import("..").RenderedEmail;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { formatCount } from "@locusgraph/utils";
|
|
3
|
+
import { EmailLayout } from "../kit/email-layout.js";
|
|
4
|
+
import { EmailButton, EmailHeading, EmailLabel, EmailRows, EmailText } from "../kit/parts.js";
|
|
5
|
+
import { renderEmail } from "../render.js";
|
|
6
|
+
export function UsageOverPlanEmail({ organizationName, quotas, usageUrl, }) {
|
|
7
|
+
return (_jsxs(EmailLayout, { preview: `${organizationName} is over its plan`, children: [_jsx(EmailLabel, { children: "Usage" }), _jsx(EmailHeading, { children: "Usage is over your plan" }), _jsxs(EmailText, { children: [organizationName, " used more than its plan includes. Usage past the plan comes from prepaid credits, or stops when overage is off."] }), _jsx(EmailRows, { rows: quotas.map((quota) => [
|
|
8
|
+
quota.key,
|
|
9
|
+
`${formatCount(quota.used)} of ${formatCount(quota.included)} ${quota.periodLabel}`,
|
|
10
|
+
]) }), _jsx(EmailButton, { href: usageUrl, children: "See usage" })] }));
|
|
11
|
+
}
|
|
12
|
+
export function usageOverPlanEmail(props) {
|
|
13
|
+
return renderEmail(`${props.organizationName} is over its plan`, _jsx(UsageOverPlanEmail, { ...props }));
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@locusgraph/email",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "LocusGraph transactional emails as React templates with inline styles: invitation, receipt, low credits and usage over plan.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/locusgraph/design.git",
|
|
9
|
+
"directory": "packages/email"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://design.locusgraph.com",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"files": [
|
|
15
|
+
"dist"
|
|
16
|
+
],
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"registry": "https://registry.npmjs.org",
|
|
25
|
+
"access": "public",
|
|
26
|
+
"provenance": true
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": "^19.2.0",
|
|
30
|
+
"react-dom": "^19.2.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@locusgraph/tokens": "0.1.1",
|
|
34
|
+
"@locusgraph/utils": "0.1.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@locusgraph/config": "0.1.0",
|
|
38
|
+
"@types/node": "^22.0.0",
|
|
39
|
+
"@types/react": "^19.2.0",
|
|
40
|
+
"@types/react-dom": "^19.2.0",
|
|
41
|
+
"react": "19.2.8",
|
|
42
|
+
"react-dom": "19.2.8",
|
|
43
|
+
"typescript": "^7.0.2",
|
|
44
|
+
"vitest": "^5.0.3"
|
|
45
|
+
},
|
|
46
|
+
"scripts": {
|
|
47
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && node ../../scripts/esm-extensions.mjs dist",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
|
+
"test": "vitest run"
|
|
50
|
+
}
|
|
6
51
|
}
|