Initial commit: backend, storefront, vendor-panel added
This commit is contained in:
45
backend/packages/modules/resend/package.json
Normal file
45
backend/packages/modules/resend/package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"name": "@mercurjs/resend",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/__tests__",
|
||||
"!dist/**/__mocks__",
|
||||
"!dist/**/__fixtures__"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build": "rimraf dist && tsc --build",
|
||||
"migration:initial": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:create --initial",
|
||||
"migration:create": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:create",
|
||||
"migration:up": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm migration:up",
|
||||
"orm:cache:clear": " MIKRO_ORM_CLI_CONFIG=./mikro-orm.config.dev.ts medusa-mikro-orm cache:clear"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@medusajs/framework": "2.8.6",
|
||||
"@medusajs/test-utils": "2.8.6",
|
||||
"@mercurjs/framework": "*",
|
||||
"@mikro-orm/cli": "6.4.3",
|
||||
"@mikro-orm/core": "6.4.3",
|
||||
"@mikro-orm/migrations": "6.4.3",
|
||||
"@mikro-orm/postgresql": "6.4.3",
|
||||
"@swc/core": "^1.7.28",
|
||||
"@swc/jest": "^0.2.36",
|
||||
"jest": "^29.7.0",
|
||||
"rimraf": "^3.0.2",
|
||||
"tsc-alias": "^1.8.6",
|
||||
"typescript": "^5.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@medusajs/framework": "2.8.6",
|
||||
"@mikro-orm/core": "6.4.3",
|
||||
"@mikro-orm/migrations": "6.4.3",
|
||||
"@mikro-orm/postgresql": "6.4.3",
|
||||
"awilix": "^8.0.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
request_type: string
|
||||
seller_name: string
|
||||
}
|
||||
}
|
||||
|
||||
export const AdminRequestCreatedEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
Hello, <span role="img" aria-label="wave">👋</span>
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 16 }}>
|
||||
{data.seller_name} has requested to create a new {data.request_type}. Please review the request and approve it in admin panel.
|
||||
</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
request_address: string,
|
||||
seller_name: string
|
||||
}
|
||||
}
|
||||
|
||||
export const AdminSellerRequestCreatedEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
Hello, <span role="img" aria-label="wave">👋</span>
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 16 }}>
|
||||
{data.seller_name} has requested to join the platform. Please review the request and approve it in admin panel.
|
||||
</p>
|
||||
|
||||
<div>
|
||||
<a href={data.request_address}>
|
||||
<button>Review Request</button>
|
||||
</a>
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const BuyerAccountCreatedEmailTemplate: React.FC<
|
||||
Readonly<EmailTemplateProps>
|
||||
> = ({ data }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: "480px",
|
||||
margin: "40px auto",
|
||||
padding: "32px 24px",
|
||||
borderRadius: "12px",
|
||||
background: "#fff",
|
||||
boxShadow: "0 2px 12px rgba(0,0,0,0.07)",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
color: "#222",
|
||||
}}
|
||||
>
|
||||
<h1 style={{ fontSize: "2rem", marginBottom: "16px", color: "#222" }}>
|
||||
Welcome to Mercur, {data.user_name}!
|
||||
</h1>
|
||||
<p style={{ fontSize: "1.1rem", marginBottom: "24px" }}>
|
||||
We’re excited to have you join us on this journey.
|
||||
<br />
|
||||
Your account has been created successfully.
|
||||
</p>
|
||||
<a
|
||||
href="https://mercurjs.com"
|
||||
style={{
|
||||
display: "inline-block",
|
||||
padding: "12px 28px",
|
||||
background: "#222",
|
||||
color: "#fff",
|
||||
borderRadius: "6px",
|
||||
textDecoration: "none",
|
||||
fontWeight: 600,
|
||||
marginBottom: "32px",
|
||||
}}
|
||||
>
|
||||
Visit Mercur
|
||||
</a>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: "#888", marginTop: 4 }}>mercurjs.com</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
order_address: string,
|
||||
order: {
|
||||
id: string,
|
||||
display_id?: string,
|
||||
items: any[]
|
||||
item?: any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const BuyerCancelOrderEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
const { order } = data;
|
||||
const itemsArray = order.items?.flat() || [order.item].flat();
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>Your order has been canceled</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 24 }}>
|
||||
We're sorry, but your order <b>#{order.display_id || order.id}</b> has been canceled by the seller.
|
||||
</p>
|
||||
|
||||
<h3 style={{ marginTop: 32, marginBottom: 12 }}>Order items:</h3>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', marginBottom: 32 }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ textAlign: 'left', padding: '8px', borderBottom: '1px solid #eee' }}>Product</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Amount</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Qty</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{itemsArray.map((item: any, idx: number) => (
|
||||
<tr key={item.id || idx} style={{ borderBottom: '1px solid #f3f3f3' }}>
|
||||
<td style={{ padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{item.thumbnail && (
|
||||
<img
|
||||
src={item.thumbnail}
|
||||
alt={item.product_title}
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
objectFit: 'cover',
|
||||
borderRadius: 6,
|
||||
marginRight: 12,
|
||||
border: '1px solid #eee'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<div style={{ fontWeight: 600 }}>{item.product_title}</div>
|
||||
<div style={{ fontSize: 13, color: '#555' }}>
|
||||
Variant: {item.variant_title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.unit_price} eur
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.quantity}
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.unit_price * item.quantity} eur
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style={{ margin: '32px 0 16px 0', fontSize: '1rem' }}>
|
||||
If you have any questions, please contact <b>Mercur Support</b>.
|
||||
</div>
|
||||
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<a
|
||||
href={data.order_address}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '10px 24px',
|
||||
background: '#222',
|
||||
color: '#fff',
|
||||
borderRadius: 6,
|
||||
textDecoration: 'none',
|
||||
fontWeight: 600,
|
||||
marginBottom: 8
|
||||
}}
|
||||
>
|
||||
View Order Details
|
||||
</a>
|
||||
<div style={{ fontSize: 13, color: '#555', marginTop: 8 }}>
|
||||
If you can’t click the button, here’s your link: <br />
|
||||
<span style={{ color: '#0070f3' }}>{data.order_address}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ fontSize: 13, color: '#888', marginBottom: 24 }}>
|
||||
You received this email because you made a purchase or sale on the Mercur marketplace.
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,163 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string,
|
||||
order_address: string,
|
||||
order_id: string,
|
||||
order: {
|
||||
display_id: string,
|
||||
items: any[],
|
||||
currency_code: string,
|
||||
shipping_methods: {
|
||||
amount: number,
|
||||
name: string
|
||||
}[],
|
||||
total: number,
|
||||
email: string,
|
||||
shipping_address: {
|
||||
first_name: string,
|
||||
last_name: string,
|
||||
company: string,
|
||||
address_1: string,
|
||||
address_2: string,
|
||||
city: string,
|
||||
province: string,
|
||||
postal_code: string,
|
||||
phone: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const BuyerNewOrderEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
const { order } = data;
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
Thank you for your order, {data.user_name}!<br />
|
||||
Your order #{order.display_id} has been placed!
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 24 }}>
|
||||
Thank you for placing order #{order.display_id}.<br />
|
||||
</p>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<a
|
||||
href={data.order_address}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '10px 24px',
|
||||
background: '#222',
|
||||
color: '#fff',
|
||||
borderRadius: 6,
|
||||
textDecoration: 'none',
|
||||
fontWeight: 600,
|
||||
marginBottom: 8
|
||||
}}
|
||||
>
|
||||
Order details
|
||||
</a>
|
||||
<div style={{ fontSize: 13, color: '#555', marginTop: 8 }}>
|
||||
If you can’t click the button, here’s your link: <br />
|
||||
<span style={{ color: '#0070f3' }}>{data.order_address}</span>
|
||||
</div>
|
||||
</div>
|
||||
<h3 style={{ marginTop: 32, marginBottom: 12 }}>Here’s the breakdown:</h3>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', marginBottom: 32 }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ textAlign: 'left', padding: '8px', borderBottom: '1px solid #eee' }}>Product</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Amount</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Qty</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{order.items.map((item: any, index: number) => (
|
||||
<tr key={index} style={{ borderBottom: '1px solid #f3f3f3' }}>
|
||||
<td style={{ padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
<img
|
||||
src={item.thumbnail}
|
||||
alt={`Thumbnail of ${item.product_title}`}
|
||||
style={{
|
||||
width: '40px',
|
||||
height: '40px',
|
||||
objectFit: 'cover',
|
||||
marginRight: '10px',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid #eee'
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<div style={{ fontWeight: 600 }}>{item.product_title}</div>
|
||||
<div style={{ fontSize: '12px', color: '#555' }}>
|
||||
Variant: {item.variant_title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.unit_price} {order.currency_code}
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>{item.quantity}</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.unit_price * item.quantity} {order.currency_code}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td><b>Delivery:</b></td>
|
||||
<td colSpan={3}>
|
||||
{order.shipping_methods[0].amount} {order.currency_code}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><b>Total:</b></td>
|
||||
<td colSpan={3}>
|
||||
{order.total} {order.currency_code}
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<div>
|
||||
<p style={{ marginBottom: 4 }}>
|
||||
<strong>Shipping address:</strong><br />
|
||||
{order.shipping_address.first_name} {order.shipping_address.last_name},<br />
|
||||
{order.shipping_address?.company ? `${order.shipping_address.company}, ` : ''}
|
||||
{order.shipping_address.address_1}
|
||||
{order.shipping_address.address_2 && `, ${order.shipping_address.address_2}`}, {order.shipping_address.postal_code} {order.shipping_address.city}
|
||||
{order.shipping_address.province ? `, ${order.shipping_address.province}` : ''}
|
||||
<br />
|
||||
{order.email}, {order.shipping_address.phone}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>
|
||||
<strong>Delivery method:</strong><br />
|
||||
{order.shipping_methods[0].name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: '#888', marginBottom: 24 }}>
|
||||
You received this email because you made a purchase or sale on the Mercur marketplace.<br />
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercurjs.com</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string,
|
||||
host: string,
|
||||
order_id: string,
|
||||
order: {
|
||||
id: string,
|
||||
display_id: string,
|
||||
trackingNumber: string,
|
||||
items: any[],
|
||||
currency_code: string,
|
||||
item_total: number,
|
||||
shipping_methods: {
|
||||
amount: number,
|
||||
name: string
|
||||
}[],
|
||||
total: number
|
||||
email: string
|
||||
shipping_address: {
|
||||
first_name: string,
|
||||
last_name: string,
|
||||
company: string,
|
||||
address_1: string,
|
||||
address_2: string,
|
||||
city: string,
|
||||
province: string,
|
||||
postal_code: string,
|
||||
phone: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const BuyerOrderShippedEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div>
|
||||
<h1>Your order #{data.order.trackingNumber} has been shipped!</h1>
|
||||
<p>The package is on its way and will be in your hands soon.</p>
|
||||
<div>
|
||||
<p>
|
||||
<strong>Shipping address:</strong>
|
||||
</p>
|
||||
<p>
|
||||
{data.order.shipping_address.first_name} {data.order.shipping_address.last_name}
|
||||
,<br />
|
||||
{data.order.shipping_address?.company ? `${data.order.shipping_address.company}, ` : ''}
|
||||
{data.order.shipping_address.address_1}
|
||||
{data.order.shipping_address.address_2}, {data.order.shipping_address.postal_code}{' '}
|
||||
{data.order.shipping_address.city}
|
||||
{data.order.shipping_address.province ? `, ${data.order.shipping_address.province}` : ''}
|
||||
<br />
|
||||
{data.order.email}, {data.order.shipping_address.phone}
|
||||
</p>
|
||||
</div>
|
||||
<p>
|
||||
<a href={`${data.host}/orders/${data.order.id}`}>View Order Details</a>
|
||||
If you can’t click the button, no worries! Here’s your link: {`${data.host}/orders/${data.order.id}`}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
You received this email because you made a purchase or sale on the Mercur marketplace. If you have any
|
||||
questions, please contact our support team.
|
||||
</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercurjs.com</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
url: string,
|
||||
}
|
||||
}
|
||||
|
||||
export const ForgotPasswordEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1>Have you forgotten your password?</h1>
|
||||
<p>
|
||||
We have received a request to reset the password for your Mercur account. Please click the button below to set a
|
||||
new password. Please note, the link is valid for the next 24 hours only.
|
||||
</p>
|
||||
<div>
|
||||
<a href={`${data.url}`}>
|
||||
<button>Reset Password</button>
|
||||
</a>
|
||||
</div>
|
||||
<p>If you did not request this change, please ignore this email.</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercurjs.com</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
42
backend/packages/modules/resend/src/email-templates/index.ts
Normal file
42
backend/packages/modules/resend/src/email-templates/index.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { AdminRequestCreatedEmailTemplate } from "./admin-request-created";
|
||||
import { AdminSellerRequestCreatedEmailTemplate } from "./admin-seller-request-created";
|
||||
import { BuyerAccountCreatedEmailTemplate } from "./buyer-account-created";
|
||||
import { BuyerCancelOrderEmailTemplate } from "./buyer-cancel-order";
|
||||
import { BuyerNewOrderEmailTemplate } from "./buyer-new-order";
|
||||
import { BuyerOrderShippedEmailTemplate } from "./buyer-shipped-order";
|
||||
import { ForgotPasswordEmailTemplate } from "./forgot-password";
|
||||
import { NewSellerInviteEmailTemplate } from "./new-seller-invitation";
|
||||
import { SellerAccountApprovedEmailTemplate } from "./seller-account-approved";
|
||||
import { SellerAccountRejectedEmailTemplate } from "./seller-account-rejected";
|
||||
import { SellerAccountSubmissionEmailTemplate } from "./seller-account-updates-submission";
|
||||
import { SellerCanceledOrderEmailTemplate } from "./seller-canceled-order";
|
||||
import { SellerNewOrderEmailTemplate } from "./seller-new-order";
|
||||
import { SellerPayoutSummaryEmailTemplate } from "./seller-payout-summary";
|
||||
import { SellerProductApprovedEmailTemplate } from "./seller-product-approved";
|
||||
import { SellerProductRejectedEmailTemplate } from "./seller-product-rejected";
|
||||
import { SellerOrderShippingEmailTemplate } from "./seller-shipping-order";
|
||||
import { SellerTeamInviteEmailTemplate } from "./seller-team-invite";
|
||||
import { SellerEmailVerifyEmailTemplate } from "./seller-verify-email";
|
||||
|
||||
export const emailTemplates: any = {
|
||||
buyerAccountCreatedEmailTemplate: BuyerAccountCreatedEmailTemplate,
|
||||
buyerCancelOrderEmailTemplate: BuyerCancelOrderEmailTemplate,
|
||||
buyerNewOrderEmailTemplate: BuyerNewOrderEmailTemplate,
|
||||
buyerOrderShippedEmailTemplate: BuyerOrderShippedEmailTemplate,
|
||||
forgotPasswordEmailTemplate: ForgotPasswordEmailTemplate,
|
||||
sellerAccountApprovedEmailTemplate: SellerAccountApprovedEmailTemplate,
|
||||
sellerAccountRejectedEmailTemplate: SellerAccountRejectedEmailTemplate,
|
||||
sellerAccountSubmissionEmailTemplate: SellerAccountSubmissionEmailTemplate,
|
||||
sellerCanceledOrderEmailTemplate: SellerCanceledOrderEmailTemplate,
|
||||
sellerNewOrderEmailTemplate: SellerNewOrderEmailTemplate,
|
||||
sellerOrderShippingEmailTemplate: SellerOrderShippingEmailTemplate,
|
||||
sellerTeamInviteEmailTemplate: SellerTeamInviteEmailTemplate,
|
||||
sellerVerifyEmailTemplate: SellerEmailVerifyEmailTemplate,
|
||||
newSellerInvitation: NewSellerInviteEmailTemplate,
|
||||
sellerProductRejectedEmailTemplate: SellerProductRejectedEmailTemplate,
|
||||
sellerProductApprovedEmailTemplate: SellerProductApprovedEmailTemplate,
|
||||
adminRequestCreatedEmailTemplate: AdminRequestCreatedEmailTemplate,
|
||||
adminSellerRequestCreatedEmailTemplate:
|
||||
AdminSellerRequestCreatedEmailTemplate,
|
||||
sellerPayoutSummaryEmailTemplate: SellerPayoutSummaryEmailTemplate,
|
||||
};
|
||||
@@ -0,0 +1,56 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
url: string
|
||||
}
|
||||
}
|
||||
|
||||
export const NewSellerInviteEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: '8px' }}>
|
||||
You are invited to sell on MercurJS!
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: '16px' }}>
|
||||
To join the platform, please accept the invitation.<br />
|
||||
</p>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<a
|
||||
href={`${data.url}`}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '10px 24px',
|
||||
background: '#222',
|
||||
color: '#fff',
|
||||
borderRadius: 6,
|
||||
textDecoration: 'none',
|
||||
fontWeight: 600,
|
||||
marginBottom: 8
|
||||
}}
|
||||
>
|
||||
Accept Invitation
|
||||
</a>
|
||||
<div style={{ fontSize: 13, color: '#555', marginTop: 8 }}>
|
||||
If you can’t click the button, here’s your link: <br />
|
||||
<span style={{ color: '#0070f3' }}>{`${data.url}`}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: '#888', marginBottom: 24 }}>
|
||||
You received this email because you were invited to join the Mercur marketplace.<br />
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerAccountApprovedEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
Hello, {data.user_name} <span role="img" aria-label="wave">👋</span>
|
||||
<br />
|
||||
<span style={{ fontSize: '1.5rem', fontWeight: 600 }}>Your account has been approved!</span>
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 16 }}>
|
||||
We’re happy to let you know that your application has been approved! This means your account is now activated on
|
||||
the Mercur marketplace.
|
||||
</p>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 24 }}>
|
||||
Thank you for choosing us. We are excited about your success and will support you every step of the way.
|
||||
</p>
|
||||
<div style={{ fontSize: 13, color: '#888', marginBottom: 24 }}>
|
||||
You received this email because you registered as a seller on the Mercur marketplace.<br />
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
export const SellerAccountRejectedEmailTemplate: React.FC = () => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
We regret to inform you that your account application has been rejected.
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 16 }}>
|
||||
We appreciate the effort you put into your application and thank you for considering our platform.
|
||||
Unfortunately, after a careful review, we have determined that your application does not meet our current
|
||||
requirements.
|
||||
</p>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 24 }}>
|
||||
If you have any questions or need further clarification, please don’t hesitate to reach out to us. We’re here to
|
||||
assist you.
|
||||
</p>
|
||||
<div style={{ fontSize: 13, color: '#888', marginBottom: 24 }}>
|
||||
You received this email because you applied as a seller on the Mercur marketplace.<br />
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string,
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerAccountSubmissionEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1>Hello, {data.user_name} 👋</h1>
|
||||
<p>We are thrilled about your interest in collaborating with us.</p>
|
||||
<p>
|
||||
Your application is currently being reviewed by our team. Please expect a response within [three] business days.
|
||||
If your submission meets our criteria and is accepted, you will receive a confirmation email from us.
|
||||
</p>
|
||||
<p>
|
||||
In the meantime, if you have any questions or need further assistance, feel free to reach out to us.
|
||||
</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string,
|
||||
order_address: string,
|
||||
order_id: string,
|
||||
order: {
|
||||
id: string,
|
||||
display_id: string,
|
||||
trackingNumber: string,
|
||||
items: {
|
||||
name: string
|
||||
}[],
|
||||
currency_code: string,
|
||||
item_total: number,
|
||||
shipping_methods: {
|
||||
amount: number,
|
||||
name: string
|
||||
}[],
|
||||
total: number
|
||||
email: string
|
||||
shipping_address: {
|
||||
first_name: string,
|
||||
last_name: string,
|
||||
company: string,
|
||||
address_1: string,
|
||||
address_2: string,
|
||||
city: string,
|
||||
province: string,
|
||||
postal_code: string,
|
||||
phone: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerCanceledOrderEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1>
|
||||
An order #{data.order.display_id} has been cancelled.
|
||||
</h1>
|
||||
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<a
|
||||
href={data.order_address}
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
padding: '10px 24px',
|
||||
background: '#222',
|
||||
color: '#fff',
|
||||
borderRadius: 6,
|
||||
textDecoration: 'none',
|
||||
fontWeight: 600,
|
||||
marginBottom: 8
|
||||
}}
|
||||
>
|
||||
View Order Details
|
||||
</a>
|
||||
<div style={{ fontSize: 13, color: '#555', marginTop: 8 }}>
|
||||
If you can’t click the button, here’s your link: <br />
|
||||
<span style={{ color: '#0070f3' }}>{data.order_address}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>
|
||||
If you have any questions, please contact our support team.
|
||||
</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercurjs.com</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
order: {
|
||||
id: string,
|
||||
display_id: number | string,
|
||||
items: any[],
|
||||
customer: {
|
||||
first_name: string,
|
||||
last_name: string,
|
||||
id: string
|
||||
},
|
||||
seller: {
|
||||
email: string,
|
||||
name: string,
|
||||
id: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerNewOrderEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
const { order } = data;
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
Hello, {order.seller.name}!
|
||||
<br />
|
||||
You have received a new order!
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 24 }}>
|
||||
Order <b>#{order.display_id}</b> has just been placed by {order.customer.first_name} {order.customer.last_name}.
|
||||
</p>
|
||||
<h3 style={{ marginTop: 32, marginBottom: 12 }}>Order items:</h3>
|
||||
<table style={{ width: '100%', borderCollapse: 'collapse', marginBottom: 32 }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{ textAlign: 'left', padding: '8px', borderBottom: '1px solid #eee' }}>Product</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Amount</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Qty</th>
|
||||
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #eee' }}>Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{order.items.map((item: any, idx: number) => (
|
||||
<tr key={item.id || idx} style={{ borderBottom: '1px solid #f3f3f3' }}>
|
||||
<td style={{ padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center' }}>
|
||||
{item.thumbnail && (
|
||||
<img
|
||||
src={item.thumbnail}
|
||||
alt={item.product_title}
|
||||
style={{
|
||||
width: 48,
|
||||
height: 48,
|
||||
objectFit: 'cover',
|
||||
borderRadius: 6,
|
||||
marginRight: 12,
|
||||
border: '1px solid #eee'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div>
|
||||
<div style={{ fontWeight: 600 }}>{item.product_title}</div>
|
||||
<div style={{ fontSize: 13, color: '#555' }}>
|
||||
Variant: {item.variant_title}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.unit_price} eur
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.quantity}
|
||||
</td>
|
||||
<td style={{ textAlign: 'right', padding: '12px 8px', verticalAlign: 'top' }}>
|
||||
{item.unit_price * item.quantity} eur
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style={{ fontSize: 13, color: '#888', marginBottom: 24 }}>
|
||||
You received this email because you are a seller on the Mercur marketplace.<br />
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,123 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
seller: {
|
||||
email: string;
|
||||
name: string;
|
||||
};
|
||||
payouts: {
|
||||
id: string;
|
||||
created_at: Date;
|
||||
amount: number;
|
||||
currency_code: string;
|
||||
order: {
|
||||
id: string;
|
||||
display_id: number;
|
||||
created_at: Date;
|
||||
};
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
export const SellerPayoutSummaryEmailTemplate: React.FC<
|
||||
Readonly<EmailTemplateProps>
|
||||
> = ({ data }) => {
|
||||
const { seller, payouts } = data;
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 600,
|
||||
margin: "0 auto",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
color: "#222",
|
||||
background: "#fff",
|
||||
padding: 24,
|
||||
borderRadius: 10,
|
||||
}}
|
||||
>
|
||||
<h1 style={{ fontSize: "2rem", marginBottom: 8 }}>
|
||||
Hello, {seller.name}!
|
||||
<br />
|
||||
You have received new transfers to your Stripe account!
|
||||
</h1>
|
||||
<h3 style={{ marginTop: 32, marginBottom: 12 }}>Transfer list:</h3>
|
||||
<table
|
||||
style={{ width: "100%", borderCollapse: "collapse", marginBottom: 32 }}
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
style={{
|
||||
textAlign: "left",
|
||||
padding: "8px",
|
||||
borderBottom: "1px solid #eee",
|
||||
}}
|
||||
>
|
||||
Order
|
||||
</th>
|
||||
<th
|
||||
style={{
|
||||
textAlign: "right",
|
||||
padding: "8px",
|
||||
borderBottom: "1px solid #eee",
|
||||
}}
|
||||
>
|
||||
Amount
|
||||
</th>
|
||||
<th
|
||||
style={{
|
||||
textAlign: "right",
|
||||
padding: "8px",
|
||||
borderBottom: "1px solid #eee",
|
||||
}}
|
||||
>
|
||||
Date
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{payouts.map((payout, idx) => (
|
||||
<tr key={idx} style={{ borderBottom: "1px solid #f3f3f3" }}>
|
||||
<td style={{ padding: "12px 8px", verticalAlign: "top" }}>
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
<div style={{ fontWeight: 600 }}>
|
||||
Order #{payout.order.display_id}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
style={{
|
||||
textAlign: "right",
|
||||
padding: "12px 8px",
|
||||
verticalAlign: "top",
|
||||
}}
|
||||
>
|
||||
{payout.amount} {payout.currency_code}
|
||||
</td>
|
||||
<td
|
||||
style={{
|
||||
textAlign: "right",
|
||||
padding: "12px 8px",
|
||||
verticalAlign: "top",
|
||||
}}
|
||||
>
|
||||
{payout.order.created_at.toISOString()}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<div style={{ fontSize: 13, color: "#888", marginBottom: 24 }}>
|
||||
You received this email because you are a seller on the Mercur
|
||||
marketplace.
|
||||
<br />
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: "#888", marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
product_title: string
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerProductApprovedEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
Hello <span role="img" aria-label="wave">👋</span>
|
||||
</h1>
|
||||
<p style={{ fontSize: '1.1rem', marginBottom: 16 }}>
|
||||
We’re happy to let you know that your product {data.product_title} has been approved by the administrator.
|
||||
</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
product_title: string
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerProductRejectedEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div style={{
|
||||
maxWidth: 600,
|
||||
margin: '0 auto',
|
||||
fontFamily: 'Arial, sans-serif',
|
||||
color: '#222',
|
||||
background: '#fff',
|
||||
padding: 24,
|
||||
borderRadius: 10
|
||||
}}>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
Hello <span role="img" aria-label="wave">👋</span>
|
||||
</h1>
|
||||
<h1 style={{ fontSize: '2rem', marginBottom: 8 }}>
|
||||
We regret to inform you that your product {data.product_title} has been rejected.
|
||||
</h1>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string,
|
||||
host: string,
|
||||
order_id: string,
|
||||
order: {
|
||||
id: string,
|
||||
display_id: string,
|
||||
trackingNumber: string,
|
||||
items: any[],
|
||||
currency_code: string,
|
||||
item_total: number,
|
||||
shipping_methods: {
|
||||
amount: number,
|
||||
name: string
|
||||
}[],
|
||||
total: number
|
||||
email: string
|
||||
shipping_address: {
|
||||
first_name: string,
|
||||
last_name: string,
|
||||
company: string,
|
||||
address_1: string,
|
||||
address_2: string,
|
||||
city: string,
|
||||
province: string,
|
||||
postal_code: string,
|
||||
phone: string
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerOrderShippingEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div>
|
||||
<h1>The order #{data.order.display_id} has been marked as shipped.</h1>
|
||||
<p>
|
||||
<div>
|
||||
<p>
|
||||
<strong>Delivery Address:</strong>
|
||||
</p>
|
||||
<p>
|
||||
{data.order.shipping_address.first_name} {data.order.shipping_address.last_name}
|
||||
,<br />
|
||||
{data.order.shipping_address?.company ? `${data.order.shipping_address.company}, ` : ''}
|
||||
{data.order.shipping_address.address_1}
|
||||
{data.order.shipping_address.address_2}, {data.order.shipping_address.postal_code}{' '}
|
||||
{data.order.shipping_address.city}
|
||||
{data.order.shipping_address.province ? `, ${data.order.shipping_address.province}` : ''}
|
||||
<br />
|
||||
{data.order.email}, {data.order.shipping_address.phone}
|
||||
</p>
|
||||
</div>
|
||||
</p>
|
||||
<p>Thank you for updating the status of the order. If you have any questions, please contact our support team.</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercurjs.com</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string;
|
||||
store_name: string;
|
||||
host: string;
|
||||
id: string;
|
||||
email: string;
|
||||
};
|
||||
}
|
||||
|
||||
export const SellerTeamInviteEmailTemplate: React.FC<
|
||||
Readonly<EmailTemplateProps>
|
||||
> = ({ data }) => {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
maxWidth: 600,
|
||||
margin: "0 auto",
|
||||
fontFamily: "Arial, sans-serif",
|
||||
color: "#222",
|
||||
background: "#fff",
|
||||
padding: 24,
|
||||
borderRadius: 10,
|
||||
}}
|
||||
>
|
||||
<h1 style={{ fontSize: "2rem", marginBottom: "8px" }}>
|
||||
{data.user_name} has invited you to join the team at {data.store_name}.
|
||||
</h1>
|
||||
<p style={{ fontSize: "1.1rem", marginBottom: "16px" }}>
|
||||
To join the team at <b>{data.store_name}</b>, please accept the
|
||||
invitation.
|
||||
<br />
|
||||
Your login email: <b>{data.email}</b>
|
||||
</p>
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<a
|
||||
href={`${data.host}`}
|
||||
style={{
|
||||
display: "inline-block",
|
||||
padding: "10px 24px",
|
||||
background: "#222",
|
||||
color: "#fff",
|
||||
borderRadius: 6,
|
||||
textDecoration: "none",
|
||||
fontWeight: 600,
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
Accept Invitation
|
||||
</a>
|
||||
<div style={{ fontSize: 13, color: "#555", marginTop: 8 }}>
|
||||
If you can’t click the button, here’s your link: <br />
|
||||
<span style={{ color: "#0070f3" }}>{`${data.host}`}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div style={{ fontSize: 13, color: "#888", marginBottom: 24 }}>
|
||||
You received this email because you were invited to join a team on the
|
||||
Mercur marketplace.
|
||||
<br />
|
||||
If you have any questions, please contact our support team.
|
||||
</div>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: "#888", marginTop: 4 }}>mercur.js</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,28 @@
|
||||
interface EmailTemplateProps {
|
||||
data: {
|
||||
user_name: string
|
||||
link: string
|
||||
}
|
||||
}
|
||||
|
||||
export const SellerEmailVerifyEmailTemplate: React.FC<Readonly<EmailTemplateProps>> = ({ data }) => {
|
||||
return (
|
||||
<div>
|
||||
<h1>
|
||||
Hello, {data.user_name} 👋
|
||||
<br />
|
||||
Thank you for submiting your account to Mercur Marketplace
|
||||
</h1>
|
||||
<p>
|
||||
Before we proceed with account submission there is but one last thing to do - verify your email.
|
||||
Please <a href={data.link}>click here</a> to verify your email.
|
||||
</p>
|
||||
<p>Thank you for choosing us. We are excited about you joining us and will support you every step of the way.</p>
|
||||
<div style={{ marginTop: 32 }}>
|
||||
<div>Best regards,</div>
|
||||
<div style={{ fontWeight: 600 }}>The Mercur Team</div>
|
||||
<div style={{ color: '#888', marginTop: 4 }}>mercurjs.com</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
8
backend/packages/modules/resend/src/index.ts
Normal file
8
backend/packages/modules/resend/src/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { ModuleProvider, Modules } from "@medusajs/framework/utils";
|
||||
|
||||
import ResendService from "./service";
|
||||
export * from "./types";
|
||||
|
||||
export default ModuleProvider(Modules.NOTIFICATION, {
|
||||
services: [ResendService],
|
||||
});
|
||||
62
backend/packages/modules/resend/src/service.ts
Normal file
62
backend/packages/modules/resend/src/service.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { Resend } from 'resend'
|
||||
|
||||
import {
|
||||
AbstractNotificationProviderService,
|
||||
MedusaError
|
||||
} from '@medusajs/framework/utils'
|
||||
import { ProviderSendNotificationDTO } from '@medusajs/types'
|
||||
|
||||
import { emailTemplates } from './email-templates'
|
||||
|
||||
type ResendOptions = {
|
||||
api_key: string
|
||||
from: string
|
||||
}
|
||||
|
||||
class ResendNotificationProviderService extends AbstractNotificationProviderService {
|
||||
static identifier = 'notification-resend'
|
||||
private resendClient: Resend
|
||||
private options: ResendOptions
|
||||
|
||||
constructor(_, options: ResendOptions) {
|
||||
super()
|
||||
this.validateModuleOptions(options)
|
||||
this.resendClient = new Resend(options.api_key)
|
||||
this.options = options
|
||||
}
|
||||
|
||||
validateModuleOptions(options: ResendOptions) {
|
||||
for (const key in options) {
|
||||
if (!options[key]) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`No ${key} was provided in the ${ResendNotificationProviderService.identifier} options. Please add one.`
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async send(notification: ProviderSendNotificationDTO) {
|
||||
const { data, error } = await this.resendClient.emails.send({
|
||||
from: notification.from?.trim() || this.options.from,
|
||||
to: notification.to,
|
||||
subject: notification.content?.subject as string,
|
||||
react: emailTemplates[notification.template](notification.data)
|
||||
})
|
||||
|
||||
if (error) {
|
||||
throw new MedusaError(MedusaError.Types.UNEXPECTED_STATE, error.message)
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.UNEXPECTED_STATE,
|
||||
'No data returned by resend client'
|
||||
)
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
export default ResendNotificationProviderService
|
||||
1
backend/packages/modules/resend/src/types/index.ts
Normal file
1
backend/packages/modules/resend/src/types/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./templates";
|
||||
21
backend/packages/modules/resend/src/types/templates.ts
Normal file
21
backend/packages/modules/resend/src/types/templates.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export enum ResendNotificationTemplates {
|
||||
BUYER_ACCOUNT_CREATED = "buyerAccountCreatedEmailTemplate",
|
||||
BUYER_NEW_ORDER = "buyerNewOrderEmailTemplate",
|
||||
BUYER_CANCELED_ORDER = "buyerCancelOrderEmailTemplate",
|
||||
BUYER_ORDER_SHIPPED = "buyerOrderShippedEmailTemplate",
|
||||
SELLER_ACCOUNT_UPDATES_SUBMISSION = "sellerAccountSubmissionEmailTemplate",
|
||||
SELLER_ACCOUNT_UPDATES_APPROVAL = "sellerAccountApprovedEmailTemplate",
|
||||
SELLER_ACCOUNT_UPDATES_REJECTION = "sellerAccountRejectedEmailTemplate",
|
||||
SELLER_NEW_ORDER = "sellerNewOrderEmailTemplate",
|
||||
SELLER_CANCELED_ORDER = "sellerCanceledOrderEmailTemplate",
|
||||
SELLER_ORDER_SHIPPED = "sellerOrderShippingEmailTemplate",
|
||||
SELLER_TEAM_MEMBER_INVITATION = "sellerTeamInviteEmailTemplate",
|
||||
SELLER_VERIFY_EMAIL_TEMPLATE = "sellerVerifyEmailTemplate",
|
||||
FORGOT_PASSWORD = "forgotPasswordEmailTemplate",
|
||||
NEW_SELLER_INVITATION = "newSellerInvitation",
|
||||
SELLER_PRODUCT_APPROVED = "sellerProductApprovedEmailTemplate",
|
||||
SELLER_PRODUCT_REJECTED = "sellerProductRejectedEmailTemplate",
|
||||
ADMIN_REQUEST_CREATED = "adminRequestCreatedEmailTemplate",
|
||||
ADMIN_SELLER_REQUEST_CREATED = "adminSellerRequestCreatedEmailTemplate",
|
||||
SELLER_PAYOUT_SUMMARY = "sellerPayoutSummaryEmailTemplate",
|
||||
}
|
||||
28
backend/packages/modules/resend/tsconfig.json
Normal file
28
backend/packages/modules/resend/tsconfig.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"lib": ["ES2021"],
|
||||
"target": "ES2021",
|
||||
"outDir": "${configDir}/dist",
|
||||
"jsx": "react-jsx",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"noUnusedLocals": true,
|
||||
"module": "node16",
|
||||
"moduleResolution": "node16",
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitReturns": true,
|
||||
"resolveJsonModule": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"noImplicitThis": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"incremental": false
|
||||
},
|
||||
"include": ["${configDir}/src"],
|
||||
"exclude": ["${configDir}/dist", "${configDir}/node_modules"]
|
||||
}
|
||||
Reference in New Issue
Block a user