diff --git a/packages/core/deno.json b/packages/core/deno.json index 47d7eca..1788b06 100644 --- a/packages/core/deno.json +++ b/packages/core/deno.json @@ -10,7 +10,6 @@ "imports": { "@google-cloud/storage": "npm:@google-cloud/storage@^7.16.0", "@oak/oak": "jsr:@oak/oak@^17.1.4", - "@react-email/components": "npm:@react-email/components@^0.1.0", "@sentry/deno": "npm:@sentry/deno@^9.28.1", "@std/assert": "jsr:@std/assert@1", "@std/expect": "jsr:@std/expect@^1.0.16", diff --git a/packages/core/src/email/templates/DowngradeEmail.tsx b/packages/core/src/email/templates/DowngradeEmail.tsx index 0b0fc3b..8da1437 100644 --- a/packages/core/src/email/templates/DowngradeEmail.tsx +++ b/packages/core/src/email/templates/DowngradeEmail.tsx @@ -1,6 +1,5 @@ -import { Heading, Section, Text } from "@react-email/components"; import { baseTemplate } from "./base.tsx"; -import { headingStyle } from "./styles.tsx"; +import { sharedStyles } from "./sharedStyles.ts"; import type { StripeBillingCycle, StripeProduct, @@ -20,47 +19,130 @@ const DowngradeEmail = (props: { newSubscriptionDate: string; }) => { const previewText = "Confirmation of your subscription downgrade | NanoAPI"; + return baseTemplate( previewText, <> -
- - We are sorry to see you go - - - Your subscription for workspace {props.workspaceName}{" "} - has been scheduled for downgrade. - -
-
- - Previous subscription: {props.oldSubscription.product}{" "} - ({props.oldSubscription.billingCycle ?? "Custom billing cycle"}) - - +
+ 📋 +
+

Subscription Change Confirmed

+

+ Your subscription for "{props.workspaceName}"{" "} + has been updated +

+ + + {/* Plan Comparison */} +
+

+ Your Plan Changes +

+ +
+
+
+ Current Plan +
+
+ {props.oldSubscription.product} +
+
+ {props.oldSubscription.billingCycle ?? "Custom billing cycle"} +
+
+ +
+ ⬇️ +
+ +
+
+ New Plan +
+
+ {props.newSubscription.product} +
+
+ {props.newSubscription.billingCycle ?? "Custom billing cycle"} +
+
+
+
+ + {/* Effective Date */} +
+

- New subscription: {props.newSubscription.product}{" "} - ({props.newSubscription.billingCycle ?? "Custom billing cycle"}) - - - Effective date: {props.newSubscriptionDate} - -

-
- + Effective Date + +

Your current subscription will remain active until the end of your - billing period, and the new subscription will take effect on{" "} - {props.newSubscriptionDate}. - - - If you have any questions, please don't hesitate to contact our - support team. - - Best regards - Team Nano -

+ billing period. The new subscription will take effect on{" "} + {props.newSubscriptionDate}. +

+ + + {/* Support */} +
+

+ Have questions about your subscription change? Our{" "} + + support team + {" "} + is here to help you. +

+
+ + {/* Footer */} +
+

+ Thank you for being part of our community! 🙏 +

+

+ Best regards,
+ The NanoAPI Team +

+
, ); }; diff --git a/packages/core/src/email/templates/InvitationEmail.tsx b/packages/core/src/email/templates/InvitationEmail.tsx index 4f11aa8..78dd26d 100644 --- a/packages/core/src/email/templates/InvitationEmail.tsx +++ b/packages/core/src/email/templates/InvitationEmail.tsx @@ -1,52 +1,98 @@ -import { Button, Heading, Section, Text } from "@react-email/components"; import { baseTemplate } from "./base.tsx"; -import { headingStyle } from "./styles.tsx"; +import { sharedStyles } from "./sharedStyles.ts"; const InvitationEmail = (props: { workspaceName: string; invitationLink: string; }) => { const previewText = `Invitation to join "${props.workspaceName}" on NanoAPI`; + return baseTemplate( previewText, <> -
- - You have been invited to join "{props.workspaceName}" - - - We're excited to have you as part of our team. To get started, simply - click the link below to accept your invitation and set up your - account: - -
-
+
+ 👥 +
+

You're Invited!

+

+ Join "{props.workspaceName}" on NanoAPI +

+ + + {/* Invitation Details */} +
+

+ We're excited to have you as part of our team! To get started, simply + click the button below to accept your invitation and set up your + account. +

+ + Accept Invitation + +
+ + {/* What you'll get */} +
+

+ What you'll get access to: +

+
+
    +
  • + Collaborate on software architecture projects +
  • +
  • + Access to shared workspaces and resources +
  • +
  • + Use powerful development tools and CLI +
  • +
  • + Team documentation and knowledge sharing +
  • +
+
+
+ + {/* Security Notice */} +
- -
-
- + Secure Invitation + +

This invitation link is unique to you and will expire after a period - for security reasons. If you have any questions or need assistance, - please don't hesitate to reach out to your team administrator. - - We look forward to collaborating with you! - Best regards - Team Nano -

+ for security reasons. If you have any questions, please contact your + team administrator. +

+ + + {/* Footer */} +
+

+ We look forward to collaborating with you! 🚀 +

+

+ Best regards,
+ The NanoAPI Team +

+
, ); }; diff --git a/packages/core/src/email/templates/OtpEmail.tsx b/packages/core/src/email/templates/OtpEmail.tsx index ec97552..3fd9a6c 100644 --- a/packages/core/src/email/templates/OtpEmail.tsx +++ b/packages/core/src/email/templates/OtpEmail.tsx @@ -1,55 +1,105 @@ -import { Heading, Section, Text } from "@react-email/components"; import { baseTemplate } from "./base.tsx"; -import { headingStyle } from "./styles.tsx"; +import { sharedStyles } from "./sharedStyles.ts"; const OtpEmail = ({ otp }: { otp: string; }) => { const previewText = `Your one-time password (OTP) code is ${otp}`; + return baseTemplate( previewText, -
- - One-Time Password (OTP) Code - - Hi there, - - - You've requested a one-time password to access your account. Please use - the code below to complete your authentication: - - - + {/* Header */} +
+
+ 🔐 +
+

One-Time Password

+

+ Use the code below to complete your authentication +

+
+ + {/* OTP Code */} +
- {otp} - +
+ Your One-Time Password +
+
+ {otp} +
+
- - This code is valid for a limited time and can only be used once. For - your security, please do not share this code with anyone. - + {/* Security Notice */} +
+

+ Security Notice +

+

+ This code is valid for a limited time and can only be used once. For + your security, please do not share this code with anyone. +

+
- - If you didn't request this code, please ignore this email or contact our - support team if you have concerns about your account security. - + {/* Support */} +
+

+ Didn't request this code? Please ignore this email or contact our{" "} + + support team + {" "} + if you have concerns about your account security. +

+
- Best regards - Team Nano -
, + {/* Footer */} +
+

+ Stay secure,
+ The NanoAPI Team +

+
+ , ); }; diff --git a/packages/core/src/email/templates/UpgradeEmail.tsx b/packages/core/src/email/templates/UpgradeEmail.tsx index 22fc57a..40eac49 100644 --- a/packages/core/src/email/templates/UpgradeEmail.tsx +++ b/packages/core/src/email/templates/UpgradeEmail.tsx @@ -1,6 +1,5 @@ -import { Heading, Section, Text } from "@react-email/components"; import { baseTemplate } from "./base.tsx"; -import { headingStyle } from "./styles.tsx"; +import { sharedStyles } from "./sharedStyles.ts"; import type { StripeBillingCycle, StripeProduct, @@ -19,43 +18,127 @@ const UpgradeEmail = (props: { }; }) => { const previewText = "Confirmation of your subscription upgrade | NanoAPI"; + return baseTemplate( previewText, <> -
- - Congratulations on your upgrade! 🎉 - - - Your subscription for workspace {props.workspaceName}{" "} - has been successfully upgraded! - -
-
- - ❌ Previous subscription: {props.oldSubscription.product}{" "} - ({props.oldSubscription.billingCycle ?? "Custom billing cycle"}) - - +
+ 🚀 +
+

Upgrade Successful! 🎉

+

+ Your subscription for "{props.workspaceName}"{" "} + has been upgraded +

+ + + {/* Plan Comparison */} +
+

+ Your Plan Changes +

+ +
+
+
+ Previous Plan +
+
+ {props.oldSubscription.product} +
+
+ {props.oldSubscription.billingCycle ?? "Custom billing cycle"} +
+
+ +
+ ⬇️ +
+ +
+
+ New Plan +
+
+ {props.newSubscription.product} +
+
+ {props.newSubscription.billingCycle ?? "Custom billing cycle"} +
+
+
+
+ + {/* Immediate Access Notice */} +
+

- ✅ New subscription: {props.newSubscription.product}{" "} - ({props.newSubscription.billingCycle ?? "Custom billing cycle"}) - -

-
- + Immediate Access + +

This change is effective immediately, and you now have access to all the features included in your new subscription. - - - If you have any questions about your upgraded subscription, please - don't hesitate to contact our support team. - - Best regards - Team Nano -

+

+ + + {/* Support */} +
+

+ Have questions about your upgraded subscription? Contact our{" "} + + support team + {" "} + - we're here to help! +

+
+ + {/* Footer */} +
+

+ Thank you for choosing NanoAPI! 🎉 +

+

+ Best regards,
+ The NanoAPI Team +

+
, ); }; diff --git a/packages/core/src/email/templates/WelcomeEmail.tsx b/packages/core/src/email/templates/WelcomeEmail.tsx index 8487d0c..a3ec76f 100644 --- a/packages/core/src/email/templates/WelcomeEmail.tsx +++ b/packages/core/src/email/templates/WelcomeEmail.tsx @@ -1,117 +1,112 @@ -import { - Button, - Column, - Heading, - Link, - Row, - Section, - Text, -} from "@react-email/components"; import { baseTemplate } from "./base.tsx"; -import { headingStyle } from "./styles.tsx"; +import { sharedStyles } from "./sharedStyles.ts"; const WelcomeEmail = () => { const previewText = "Welcome to NanoAPI - Your account has been created!"; + return baseTemplate( previewText, <> -
- - Welcome to our platform! We're thrilled to have you join our - community. - - - Your account has been successfully created and you're all set to get - started. Here's what you can do next: - -
+ github.com/nanoapi-io/napi + + +
  • + Create your first workspace or join an existing one +
  • +
  • + Invite team members to collaborate with you +
  • + + + + + {/* Call to Action */} +
    +

    Ready to get started?

    +

    + Jump into the app or explore our documentation to begin your journey. +

    +
    + + Open the App + + + Read the Docs + +
    +
    + + {/* Support */} +
    +

    + Need help? Our support team is here for you! Contact us at{" "} + + support@nanoapi.io + +

    +
    + + {/* Footer */} +
    +

    + We're excited to see what you'll build with NanoAPI! 🚀 +

    +

    + Best regards,
    + The NanoAPI Team +

    +
    , ); }; diff --git a/packages/core/src/email/templates/base.tsx b/packages/core/src/email/templates/base.tsx index e74e0df..d770623 100644 --- a/packages/core/src/email/templates/base.tsx +++ b/packages/core/src/email/templates/base.tsx @@ -1,169 +1,242 @@ import type { ReactNode } from "react"; -import { - Body, - Column, - Container, - Head, - Heading, - Hr, - Html, - Img, - Link, - Preview, - Row, - Section, - Text, -} from "@react-email/components"; +import type { CSSProperties } from "react"; export const baseTemplate = (previewText: string, children: ReactNode) => { + const socialLinkStyle: CSSProperties = { + display: "inline-block", + padding: "8px", + borderRadius: "6px", + backgroundColor: "#ffffff", + border: "1px solid #e5e7eb", + transition: "all 0.2s ease", + textDecoration: "none", + }; + + const socialIconStyle: CSSProperties = { + display: "block", + width: "24px", + height: "24px", + }; + return ( - - - + + + + + NanoAPI + + - {previewText} - + {/* Header */}
    - NanoAPI logo - + NanoAPI logo +

    + NanoAPI +

    +
    +

    - NanoAPI - + Software Architecture for the AI Age +

    -
    - {children} -
    -
    + {children} + + + {/* Footer */} +
    - - - - - + + GitHub + + + + {/* Footer Text */} +
    -
    - - - - -
    - React Email logo + Discord Server + + + YouTube + + + LinkedIn -
    - - NanoAPI - - + NanoAPI + +
    - + +
    + Need help? Contact us at{" "} + - - - Discord Server - - - - - YouTube - - - - - Linkedin - - - -
    -
    -
    - - + support@nanoapi.io + + + + + + + + ); }; diff --git a/packages/core/src/email/templates/sharedStyles.ts b/packages/core/src/email/templates/sharedStyles.ts new file mode 100644 index 0000000..5f9f362 --- /dev/null +++ b/packages/core/src/email/templates/sharedStyles.ts @@ -0,0 +1,125 @@ +// Shared styles for email templates +export const sharedStyles = { + // Typography + heading: { + fontSize: "24px", + fontWeight: "700", + color: "#111827", + margin: "0 0 16px 0", + letterSpacing: "-0.025em", + }, + + subheading: { + fontSize: "18px", + fontWeight: "600", + color: "#111827", + margin: "0 0 16px 0", + }, + + body: { + fontSize: "16px", + color: "#374151", + margin: "0 0 16px 0", + lineHeight: "1.6", + }, + + small: { + fontSize: "14px", + color: "#6b7280", + margin: "0 0 12px 0", + lineHeight: "1.5", + }, + + // Layout + section: { + marginBottom: "32px", + }, + + centerText: { + textAlign: "center" as const, + }, + + // Buttons + primaryButton: { + display: "inline-block", + padding: "12px 24px", + borderRadius: "6px", + textDecoration: "none", + fontWeight: "600", + fontSize: "16px", + textAlign: "center" as const, + backgroundColor: "#4f46e5", + color: "#ffffff", + border: "none", + }, + + secondaryButton: { + display: "inline-block", + padding: "12px 24px", + borderRadius: "6px", + textDecoration: "none", + fontWeight: "600", + fontSize: "16px", + textAlign: "center" as const, + backgroundColor: "#ffffff", + color: "#374151", + border: "2px solid #e5e7eb", + }, + + // Cards and containers + card: { + backgroundColor: "#f8fafc", + borderRadius: "8px", + padding: "24px", + border: "1px solid #e2e8f0", + marginBottom: "24px", + }, + + infoBox: { + backgroundColor: "#dbeafe", + borderRadius: "6px", + padding: "16px", + border: "1px solid #3b82f6", + marginBottom: "24px", + }, + + warningBox: { + backgroundColor: "#fef3c7", + borderRadius: "6px", + padding: "16px", + border: "1px solid #f59e0b", + marginBottom: "24px", + }, + + // Icons + iconCircle: { + width: "48px", + height: "48px", + borderRadius: "50%", + display: "flex", + alignItems: "center", + justifyContent: "center", + margin: "0 auto 16px auto", + fontSize: "24px", + color: "#ffffff", + }, + + // Links + link: { + color: "#4f46e5", + textDecoration: "underline", + fontWeight: "500", + }, + + // Divider + divider: { + borderTop: "1px solid #e5e7eb", + marginTop: "32px", + paddingTop: "24px", + }, + + // Content spacing + contentSpacing: { + marginBottom: "24px", + }, +}; diff --git a/packages/core/src/email/templates/styles.tsx b/packages/core/src/email/templates/styles.tsx deleted file mode 100644 index d9159ce..0000000 --- a/packages/core/src/email/templates/styles.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export const headingStyle = { - // deno-lint-ignore no-explicit-any - textAlign: "center" as any, - fontFamily: "Arial, sans-serif", - marginTop: "30px", - marginBottom: "40px", -};