Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed Dec 27, 2024
1 parent e00a5ed commit 4f0a863
Show file tree
Hide file tree
Showing 14 changed files with 290 additions and 0 deletions.
Binary file modified bun.lockb
Binary file not shown.
Binary file added examples/email/emails/static/vercel-arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/email/emails/static/vercel-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/email/emails/static/vercel-team.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added examples/email/emails/static/vercel-user.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
158 changes: 158 additions & 0 deletions examples/email/emails/vercel-invite-user.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { withI18n } from "@languine/react-email";
import {
Body,
Button,
Column,
Container,
Head,
Heading,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Tailwind,
Text,
} from "@react-email/components";
import * as React from "react";

interface VercelInviteUserEmailProps {
username?: string;
userImage?: string;
invitedByUsername?: string;
invitedByEmail?: string;
teamName?: string;
teamImage?: string;
inviteLink?: string;
inviteFromIp?: string;
inviteFromLocation?: string;
}

const baseUrl = process.env.VERCEL_URL
? `https://${process.env.VERCEL_URL}`
: "";

export const VercelInviteUserEmail = withI18n(
({
username,
userImage,
invitedByUsername,
invitedByEmail,
teamName,
teamImage,
inviteLink,
inviteFromIp,
inviteFromLocation,
}: VercelInviteUserEmailProps) => {
const previewText = `Join ${invitedByUsername} on Vercel`;

return (
<Html>
<Head />
<Preview>{previewText}</Preview>
<Tailwind>
<Body className="bg-white my-auto mx-auto font-sans px-2">
<Container className="border border-solid border-[#eaeaea] rounded my-[40px] mx-auto p-[20px] max-w-[465px]">
<Section className="mt-[32px]">
<Img
src={`${baseUrl}/static/vercel-logo.png`}
width="40"
height="37"
alt="Vercel"
className="my-0 mx-auto"
/>
</Section>
<Heading className="text-black text-[24px] font-normal text-center p-0 my-[30px] mx-0">
Join <strong>{teamName}</strong> on <strong>Vercel</strong>
</Heading>
<Text className="text-black text-[14px] leading-[24px]">
Hello {username},
</Text>
<Text className="text-black text-[14px] leading-[24px]">
<strong>{invitedByUsername}</strong> (
<Link
href={`mailto:${invitedByEmail}`}
className="text-blue-600 no-underline"
>
{invitedByEmail}
</Link>
) has invited you to the <strong>{teamName}</strong> team on{" "}
<strong>Vercel</strong>.
</Text>
<Section>
<Row>
<Column align="right">
<Img
className="rounded-full"
src={userImage}
width="64"
height="64"
/>
</Column>
<Column align="center">
<Img
src={`${baseUrl}/static/vercel-arrow.png`}
width="12"
height="9"
alt="invited you to"
/>
</Column>
<Column align="left">
<Img
className="rounded-full"
src={teamImage}
width="64"
height="64"
/>
</Column>
</Row>
</Section>
<Section className="text-center mt-[32px] mb-[32px]">
<Button
className="bg-[#000000] rounded text-white text-[12px] font-semibold no-underline text-center px-5 py-3"
href={inviteLink}
>
Join the team
</Button>
</Section>
<Text className="text-black text-[14px] leading-[24px]">
or copy and paste this URL into your browser:{" "}
<Link href={inviteLink} className="text-blue-600 no-underline">
{inviteLink}
</Link>
</Text>
<Hr className="border border-solid border-[#eaeaea] my-[26px] mx-0 w-full" />
<Text className="text-[#666666] text-[12px] leading-[24px]">
This invitation was intended for{" "}
<span className="text-black">{username}</span>. This invite was
sent from <span className="text-black">{inviteFromIp}</span>{" "}
located in{" "}
<span className="text-black">{inviteFromLocation}</span>. If you
were not expecting this invitation, you can ignore this email.
If you are concerned about your account's safety, please reply
to this email to get in touch with us.
</Text>
</Container>
</Body>
</Tailwind>
</Html>
);
},
"en",
);

VercelInviteUserEmail.PreviewProps = {
username: "alanturing",
userImage: `${baseUrl}/static/vercel-user.png`,
invitedByUsername: "Alan",
invitedByEmail: "[email protected]",
teamName: "Enigma",
teamImage: `${baseUrl}/static/vercel-team.png`,
inviteLink: "https://vercel.com/teams/invite/foo",
inviteFromIp: "204.13.186.218",
inviteFromLocation: "São Paulo, Brazil",
} as VercelInviteUserEmailProps;

export default VercelInviteUserEmail;
Empty file added examples/email/locales/en.json
Empty file.
4 changes: 4 additions & 0 deletions examples/email/locales/i18n.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default {
en: require("./en.json"),
// es: require("./es.json"),
};
21 changes: 21 additions & 0 deletions examples/email/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "react-email-starter",
"version": "0.1.6",
"private": true,
"scripts": {
"build": "email build",
"dev": "email dev",
"export": "email export"
},
"dependencies": {
"@languine/react-email": "workspace:*",
"@react-email/components": "0.0.31",
"react-dom": "19.0.0",
"react": "19.0.0"
},
"devDependencies": {
"@types/react": "19.0.1",
"@types/react-dom": "19.0.1",
"react-email": "3.0.4"
}
}
27 changes: 27 additions & 0 deletions examples/email/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# React Email Starter

A live preview right in your browser so you don't need to keep sending real emails during development.

## Getting Started

First, install the dependencies:

```sh
npm install
# or
yarn
```

Then, run the development server:

```sh
npm run dev
# or
yarn dev
```

Open [localhost:3000](http://localhost:3000) with your browser to see the result.

## License

MIT License
22 changes: 22 additions & 0 deletions packages/react-email/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "@languine/react-email",
"version": "1.0.0",
"files": ["dist", "README.md"],
"main": "dist/index.mjs",
"types": "dist/index.d.ts",
"license": "MIT",
"scripts": {
"clean": "rm -rf .turbo node_modules",
"lint": "biome check .",
"format": "biome format --write .",
"typecheck": "tsc --noEmit",
"build": "tsup --clean src/index.tsx"
},
"peerDependencies": {
"i18n-js": "^4.5.1"
},
"devDependencies": {
"tsup": "^8.3.5",
"typescript": "^5.7.2"
}
}
30 changes: 30 additions & 0 deletions packages/react-email/src/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { join } from "node:path";
import { I18n } from "i18n-js";

export function withI18n<Props extends object>(
Component: React.ComponentType<Props>,
locale: string,
) {
const WithI18nComponent = (props: Props) => {
// Read and parse i18n config file
const configPath = join(process.cwd(), "locales/i18n.config.ts");
const config = require(configPath);

if (!config) {
throw new Error("i18n.config.ts not found");
}

const i18n = new I18n(config);

i18n.locale = locale;
i18n.enableFallback = true;

return <Component {...props} locale={locale} i18n={i18n} />;
};

WithI18nComponent.displayName = `withI18n(${
Component.displayName || Component.name || "Component"
})`;

return WithI18nComponent;
}
21 changes: 21 additions & 0 deletions packages/react-email/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"include": ["src/**/*.ts", "src/**/*.tsx"],
"compilerOptions": {
"target": "esnext",
"module": "NodeNext",
"moduleResolution": "nodenext",
"allowJs": true,
"strict": true,
"skipLibCheck": true,
"isolatedModules": true,
"esModuleInterop": true,
"outDir": "dist",
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noEmit": false,
"customConditions": ["source"],
"jsx": "react-jsx"
}
}
7 changes: 7 additions & 0 deletions packages/react-email/tsup.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { defineConfig } from "tsup";

export default defineConfig({
dts: true,
format: "esm",
entry: ["src/index.ts"],
});

0 comments on commit 4f0a863

Please sign in to comment.