chore(server): clean mail-templates and add tailwind style (#11296)

With this commit I wanted to complete the react-mail
 structure by properly define the templates styles by
 including tailwind css framework.

The framework is extended by both react-mail and
 tailwindcss-preset-email. Those packages help the rendering
 for various email clients.

If in future there is the necessity to target specific mail
 clients the package `tailwindcss-email-variants` and
 `tailwindcss-mso` can help too. The latter has some
 workarounds for the Ms Outlook that is still lacking
 a lot of the CSS3 funcitonality.
 to target

Signed-off-by: hitech95 <nicveronese@gmail.com>
This commit is contained in:
Nicolò 2024-07-26 22:41:11 +02:00 committed by GitHub
parent a444ea7361
commit ee6f1a010c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 516 additions and 724 deletions

175
server/package-lock.json generated
View File

@ -60,6 +60,7 @@
"semver": "^7.6.2", "semver": "^7.6.2",
"sharp": "^0.33.0", "sharp": "^0.33.0",
"sirv": "^2.0.4", "sirv": "^2.0.4",
"tailwindcss-preset-email": "^1.3.2",
"thumbhash": "^0.1.1", "thumbhash": "^0.1.1",
"typeorm": "^0.3.17", "typeorm": "^0.3.17",
"ua-parser-js": "^1.0.35" "ua-parser-js": "^1.0.35"
@ -13831,6 +13832,11 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"node_modules/react-email/node_modules/arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"node_modules/react-email/node_modules/brace-expansion": { "node_modules/react-email/node_modules/brace-expansion": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@ -13981,6 +13987,53 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/react-email/node_modules/tailwindcss": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz",
"integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==",
"dependencies": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.5.3",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.0",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.19.1",
"lilconfig": "^2.1.0",
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.0.0",
"postcss": "^8.4.23",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.1",
"postcss-nested": "^6.0.1",
"postcss-selector-parser": "^6.0.11",
"resolve": "^1.22.2",
"sucrase": "^3.32.0"
},
"bin": {
"tailwind": "lib/cli.js",
"tailwindcss": "lib/cli.js"
},
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/react-email/node_modules/tailwindcss/node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"dependencies": {
"is-glob": "^4.0.3"
},
"engines": {
"node": ">=10.13.0"
}
},
"node_modules/react-email/node_modules/typescript": { "node_modules/react-email/node_modules/typescript": {
"version": "5.1.6", "version": "5.1.6",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
@ -15507,9 +15560,10 @@
} }
}, },
"node_modules/tailwindcss": { "node_modules/tailwindcss": {
"version": "3.4.0", "version": "3.4.6",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.6.tgz",
"integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", "integrity": "sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==",
"peer": true,
"dependencies": { "dependencies": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
@ -15519,7 +15573,7 @@
"fast-glob": "^3.3.0", "fast-glob": "^3.3.0",
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"jiti": "^1.19.1", "jiti": "^1.21.0",
"lilconfig": "^2.1.0", "lilconfig": "^2.1.0",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
@ -15542,15 +15596,48 @@
"node": ">=14.0.0" "node": ">=14.0.0"
} }
}, },
"node_modules/tailwindcss-email-variants": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tailwindcss-email-variants/-/tailwindcss-email-variants-3.0.1.tgz",
"integrity": "sha512-bRk4R2jnfaW7BBaL2kDgOdBl0SpVP/JPDE/yCkZb1n3YrPK9ZQyQGZoVX3OX06GxjMOrNO3wZACVdHJce7dm8w==",
"engines": {
"node": ">=18"
},
"peerDependencies": {
"tailwindcss": ">=3.4.0"
}
},
"node_modules/tailwindcss-mso": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/tailwindcss-mso/-/tailwindcss-mso-1.4.3.tgz",
"integrity": "sha512-8YfZ4xnIComDrhoSr8FUwm7EGz1FkxsZy07Fs4Jm/JxHrFiubdiZjyxLuHMc3S8o02+U4fjRGHPOzoVXRus10A==",
"peerDependencies": {
"tailwindcss": ">=3.4.0"
}
},
"node_modules/tailwindcss-preset-email": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/tailwindcss-preset-email/-/tailwindcss-preset-email-1.3.2.tgz",
"integrity": "sha512-kSPNZM5+tSi+uhCb4rk1XF9Q6zp8lhoNLCa3GQqe6gKmfI/nTqY8Y+5/DYNpwqhmUPCSHULlyI/LUCaF/q8sLg==",
"dependencies": {
"tailwindcss-email-variants": "^3.0.0",
"tailwindcss-mso": "^1.4.3"
},
"peerDependencies": {
"tailwindcss": ">=3.4.6"
}
},
"node_modules/tailwindcss/node_modules/arg": { "node_modules/tailwindcss/node_modules/arg": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"peer": true
}, },
"node_modules/tailwindcss/node_modules/glob-parent": { "node_modules/tailwindcss/node_modules/glob-parent": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"peer": true,
"dependencies": { "dependencies": {
"is-glob": "^4.0.3" "is-glob": "^4.0.3"
}, },
@ -26373,6 +26460,11 @@
"csstype": "^3.0.2" "csstype": "^3.0.2"
} }
}, },
"arg": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
},
"brace-expansion": { "brace-expansion": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
@ -26476,6 +26568,45 @@
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
}, },
"tailwindcss": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz",
"integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==",
"requires": {
"@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2",
"chokidar": "^3.5.3",
"didyoumean": "^1.2.2",
"dlv": "^1.1.3",
"fast-glob": "^3.3.0",
"glob-parent": "^6.0.2",
"is-glob": "^4.0.3",
"jiti": "^1.19.1",
"lilconfig": "^2.1.0",
"micromatch": "^4.0.5",
"normalize-path": "^3.0.0",
"object-hash": "^3.0.0",
"picocolors": "^1.0.0",
"postcss": "^8.4.23",
"postcss-import": "^15.1.0",
"postcss-js": "^4.0.1",
"postcss-load-config": "^4.0.1",
"postcss-nested": "^6.0.1",
"postcss-selector-parser": "^6.0.11",
"resolve": "^1.22.2",
"sucrase": "^3.32.0"
},
"dependencies": {
"glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"requires": {
"is-glob": "^4.0.3"
}
}
}
},
"typescript": { "typescript": {
"version": "5.1.6", "version": "5.1.6",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz",
@ -27591,9 +27722,10 @@
} }
}, },
"tailwindcss": { "tailwindcss": {
"version": "3.4.0", "version": "3.4.6",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.0.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.6.tgz",
"integrity": "sha512-VigzymniH77knD1dryXbyxR+ePHihHociZbXnLZHUyzf2MMs2ZVqlUrZ3FvpXP8pno9JzmILt1sZPD19M3IxtA==", "integrity": "sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==",
"peer": true,
"requires": { "requires": {
"@alloc/quick-lru": "^5.2.0", "@alloc/quick-lru": "^5.2.0",
"arg": "^5.0.2", "arg": "^5.0.2",
@ -27603,7 +27735,7 @@
"fast-glob": "^3.3.0", "fast-glob": "^3.3.0",
"glob-parent": "^6.0.2", "glob-parent": "^6.0.2",
"is-glob": "^4.0.3", "is-glob": "^4.0.3",
"jiti": "^1.19.1", "jiti": "^1.21.0",
"lilconfig": "^2.1.0", "lilconfig": "^2.1.0",
"micromatch": "^4.0.5", "micromatch": "^4.0.5",
"normalize-path": "^3.0.0", "normalize-path": "^3.0.0",
@ -27622,18 +27754,41 @@
"arg": { "arg": {
"version": "5.0.2", "version": "5.0.2",
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==" "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
"peer": true
}, },
"glob-parent": { "glob-parent": {
"version": "6.0.2", "version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
"peer": true,
"requires": { "requires": {
"is-glob": "^4.0.3" "is-glob": "^4.0.3"
} }
} }
} }
}, },
"tailwindcss-email-variants": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/tailwindcss-email-variants/-/tailwindcss-email-variants-3.0.1.tgz",
"integrity": "sha512-bRk4R2jnfaW7BBaL2kDgOdBl0SpVP/JPDE/yCkZb1n3YrPK9ZQyQGZoVX3OX06GxjMOrNO3wZACVdHJce7dm8w==",
"requires": {}
},
"tailwindcss-mso": {
"version": "1.4.3",
"resolved": "https://registry.npmjs.org/tailwindcss-mso/-/tailwindcss-mso-1.4.3.tgz",
"integrity": "sha512-8YfZ4xnIComDrhoSr8FUwm7EGz1FkxsZy07Fs4Jm/JxHrFiubdiZjyxLuHMc3S8o02+U4fjRGHPOzoVXRus10A==",
"requires": {}
},
"tailwindcss-preset-email": {
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/tailwindcss-preset-email/-/tailwindcss-preset-email-1.3.2.tgz",
"integrity": "sha512-kSPNZM5+tSi+uhCb4rk1XF9Q6zp8lhoNLCa3GQqe6gKmfI/nTqY8Y+5/DYNpwqhmUPCSHULlyI/LUCaF/q8sLg==",
"requires": {
"tailwindcss-email-variants": "^3.0.0",
"tailwindcss-mso": "^1.4.3"
}
},
"tapable": { "tapable": {
"version": "2.2.1", "version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",

View File

@ -86,6 +86,7 @@
"semver": "^7.6.2", "semver": "^7.6.2",
"sharp": "^0.33.0", "sharp": "^0.33.0",
"sirv": "^2.0.4", "sirv": "^2.0.4",
"tailwindcss-preset-email": "^1.3.2",
"thumbhash": "^0.1.1", "thumbhash": "^0.1.1",
"typeorm": "^0.3.17", "typeorm": "^0.3.17",
"ua-parser-js": "^1.0.35" "ua-parser-js": "^1.0.35"

View File

@ -1,21 +1,8 @@
import { import { Img, Link, Section, Text } from '@react-email/components';
Body,
Button,
Column,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
import * as CSS from 'csstype';
import * as React from 'react'; import * as React from 'react';
import { AlbumInviteEmailProps } from 'src/interfaces/notification.interface'; import { AlbumInviteEmailProps } from 'src/interfaces/notification.interface';
import { ImmichButton } from './components/button.component';
import ImmichLayout from './components/immich.layout';
export const AlbumInviteEmail = ({ export const AlbumInviteEmail = ({
baseUrl, baseUrl,
@ -25,122 +12,37 @@ export const AlbumInviteEmail = ({
albumId, albumId,
cid, cid,
}: AlbumInviteEmailProps) => ( }: AlbumInviteEmailProps) => (
<Html> <ImmichLayout preview="You have been added to a shared album.">
<Head /> <Text className="m-0 text-2xl">
<Preview>You have been added to a shared album.</Preview> Hey <strong>{recipientName}</strong>!
<Body </Text>
style={{
margin: 0,
padding: 0,
backgroundColor: '#ffffff',
color: 'rgb(28,28,28)',
fontFamily: 'Overpass, sans-serif',
fontSize: '18px',
lineHeight: '24px',
}}
>
<Container
style={{
width: '540px',
maxWidth: '100%',
padding: '10px',
margin: '0 auto',
}}
>
<Section
style={{
padding: '36px',
tableLayout: 'fixed',
backgroundColor: 'rgb(226, 232, 240)',
border: 'solid 0px rgb(248 113 113)',
borderRadius: '50px',
textAlign: 'center' as const,
}}
>
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
alt="Immich"
style={{
height: 'auto',
margin: '0 auto 48px auto',
width: '50%',
alignSelf: 'center',
color: 'white',
}}
/>
<Text style={text}>Hey {recipientName}!</Text> <Text>
<Text style={text}>
{senderName} has added you to the album <strong>{albumName}</strong>. {senderName} has added you to the album <strong>{albumName}</strong>.
</Text> </Text>
{cid && ( {cid && (
<Row> <Section className="flex justify-center my-0">
<Column align="center">
<Img <Img
className="max-w-[300px] w-full rounded-lg"
src={`cid:${cid}`} src={`cid:${cid}`}
width="300"
style={{ style={{
borderRadius: '20px',
boxShadow: 'rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px', boxShadow: 'rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px',
}} }}
/> />
</Column> </Section>
</Row>
)} )}
<Row style={{ marginBottom: '36px', marginTop: '36px' }}> <Section className="flex justify-center my-6">
<Text style={{ ...text }}>To view the album, open the link in a browser, or click the button below.</Text> <ImmichButton href={`${baseUrl}/albums/${albumId}`}>View Album</ImmichButton>
</Row>
<Row>
<Column align="center">
<Link style={{ marginTop: '50px' }} href={`${baseUrl}/albums/${albumId}`}>
{baseUrl}/albums/{albumId}
</Link>
</Column>
</Row>
<Row>
<Column align="center">
<Button style={button} href={`${baseUrl}/albums/${albumId}`}>
View album
</Button>
</Column>
</Row>
</Section> </Section>
<Hr style={{ color: 'rgb(66, 80, 175)', marginTop: '24px' }} /> <Text className="text-xs">
If you cannot click the button use the link below to view the album.
<Section style={{ textAlign: 'center' }}> <br />
<Row> <Link href={`${baseUrl}/albums/${albumId}`}>{`${baseUrl}/albums/${albumId}`}</Link>
<Column align="center">
<Link href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<Img src={`https://immich.app/img/google-play-badge.png`} height="96px" alt="Immich" />
</Link>
<Link href="https://apps.apple.com/sg/app/immich/id1613945652">
<Img
src={`https://immich.app/img/ios-app-store-badge.png`}
alt="Immich"
style={{ height: '72px', padding: '14px' }}
/>
</Link>
</Column>
</Row>
</Section>
<Text
style={{
color: '#6a737d',
fontSize: '0.8rem',
textAlign: 'center' as const,
marginTop: '12px',
}}
>
<Link href="https://immich.app">Immich</Link> project is available under GNU AGPL v3 license.
</Text> </Text>
</Container> </ImmichLayout>
</Body>
</Html>
); );
AlbumInviteEmail.PreviewProps = { AlbumInviteEmail.PreviewProps = {
@ -148,27 +50,7 @@ AlbumInviteEmail.PreviewProps = {
albumName: 'Trip to Europe', albumName: 'Trip to Europe',
albumId: 'b63f6dae-e1c9-401b-9a85-9dbbf5612539', albumId: 'b63f6dae-e1c9-401b-9a85-9dbbf5612539',
senderName: 'Owner User', senderName: 'Owner User',
recipientName: 'Guest User', recipientName: 'Alan Turing',
cid: '',
} as AlbumInviteEmailProps; } as AlbumInviteEmailProps;
export default AlbumInviteEmail; export default AlbumInviteEmail;
const text = {
margin: '0 0 24px 0',
textAlign: 'left' as const,
fontSize: '18px',
lineHeight: '24px',
};
const button: CSS.Properties = {
backgroundColor: 'rgb(66, 80, 175)',
margin: '1em 0',
padding: '0.75em 3em',
color: '#fff',
fontSize: '1em',
fontWeight: 700,
lineHeight: 1.5,
textTransform: 'uppercase',
borderRadius: '9999px',
};

View File

@ -1,165 +1,49 @@
import { import { Img, Link, Section, Text } from '@react-email/components';
Body,
Button,
Column,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
import * as CSS from 'csstype';
import * as React from 'react'; import * as React from 'react';
import { AlbumUpdateEmailProps } from 'src/interfaces/notification.interface'; import { AlbumUpdateEmailProps } from 'src/interfaces/notification.interface';
import { ImmichButton } from './components/button.component';
import ImmichLayout from './components/immich.layout';
export const AlbumUpdateEmail = ({ baseUrl, albumName, recipientName, albumId, cid }: AlbumUpdateEmailProps) => ( export const AlbumUpdateEmail = ({ baseUrl, albumName, recipientName, albumId, cid }: AlbumUpdateEmailProps) => (
<Html> <ImmichLayout preview="New media has been added to a shared album.">
<Head /> <Text className="m-0 text-2xl">
<Preview>New media has been added to a shared album.</Preview> Hey <strong>{recipientName}</strong>!
<Body </Text>
style={{
margin: 0,
padding: 0,
backgroundColor: '#ffffff',
color: 'rgb(28,28,28)',
fontFamily: 'Overpass, sans-serif',
fontSize: '18px',
lineHeight: '24px',
}}
>
<Container
style={{
width: '540px',
maxWidth: '100%',
padding: '10px',
margin: '0 auto',
}}
>
<Section
style={{
padding: '36px',
tableLayout: 'fixed',
backgroundColor: 'rgb(226, 232, 240)',
border: 'solid 0px rgb(248 113 113)',
borderRadius: '50px',
textAlign: 'center' as const,
}}
>
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
alt="Immich"
style={{
height: 'auto',
margin: '0 auto 48px auto',
width: '50%',
alignSelf: 'center',
color: 'white',
}}
/>
<Text style={text}>Hey {recipientName}!</Text> <Text>
New media has been added to <strong>{albumName}</strong>,
<Text style={text}> <br /> check it out!
New media has been added to <strong>{albumName}</strong>, check it out!
</Text> </Text>
{cid && ( {cid && (
<Row> <Section className="flex justify-center my-0">
<Column align="center">
<Img <Img
className="max-w-[300px] w-full rounded-lg"
src={`cid:${cid}`} src={`cid:${cid}`}
width="300"
style={{ style={{
borderRadius: '20px',
boxShadow: 'rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px', boxShadow: 'rgba(50, 50, 93, 0.25) 0px 13px 27px -5px, rgba(0, 0, 0, 0.3) 0px 8px 16px -8px',
}} }}
/> />
</Column> </Section>
</Row>
)} )}
<Row style={{ marginBottom: '36px', marginTop: '36px' }}> <Section className="flex justify-center my-6">
<Text style={{ ...text }}>To view the album, open the link in a browser, or click the button below.</Text> <ImmichButton href={`${baseUrl}/albums/${albumId}`}>View Album</ImmichButton>
</Row>
<Row>
<Column align="center">
<Link style={{ marginTop: '50px' }} href={`${baseUrl}/albums/${albumId}`}>
{baseUrl}/albums/{albumId}
</Link>
</Column>
</Row>
<Row>
<Column align="center">
<Button style={button} href={`${baseUrl}/albums/${albumId}`}>
View album
</Button>
</Column>
</Row>
</Section> </Section>
<Hr style={{ color: 'rgb(66, 80, 175)', marginTop: '24px' }} /> <Text className="text-xs">
If you cannot click the button use the link below to view the album.
<Section style={{ textAlign: 'center' }}> <br />
<Row> <Link href={`${baseUrl}/albums/${albumId}`}>{`${baseUrl}/albums/${albumId}`}</Link>
<Column align="center">
<Link href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<Img src={`https://immich.app/img/google-play-badge.png`} height="96px" alt="Immich" />
</Link>
<Link href="https://apps.apple.com/sg/app/immich/id1613945652">
<Img
src={`https://immich.app/img/ios-app-store-badge.png`}
alt="Immich"
style={{ height: '72px', padding: '14px' }}
/>
</Link>
</Column>
</Row>
</Section>
<Text
style={{
color: '#6a737d',
fontSize: '0.8rem',
textAlign: 'center' as const,
marginTop: '12px',
}}
>
<Link href="https://immich.app">Immich</Link> project is available under GNU AGPL v3 license.
</Text> </Text>
</Container> </ImmichLayout>
</Body>
</Html>
); );
AlbumUpdateEmail.PreviewProps = { AlbumUpdateEmail.PreviewProps = {
baseUrl: 'https://demo.immich.app', baseUrl: 'https://demo.immich.app',
albumName: 'Trip to Europe', albumName: 'Trip to Europe',
albumId: 'b63f6dae-e1c9-401b-9a85-9dbbf5612539', albumId: 'b63f6dae-e1c9-401b-9a85-9dbbf5612539',
recipientName: 'Alex Tran', recipientName: 'Alan Turing',
} as AlbumUpdateEmailProps; } as AlbumUpdateEmailProps;
export default AlbumUpdateEmail; export default AlbumUpdateEmail;
const text = {
margin: '0 0 24px 0',
textAlign: 'left' as const,
fontSize: '18px',
lineHeight: '24px',
};
const button: CSS.Properties = {
backgroundColor: 'rgb(66, 80, 175)',
margin: '1em 0',
padding: '0.75em 3em',
color: '#fff',
fontSize: '1em',
fontWeight: 700,
lineHeight: 1.5,
textTransform: 'uppercase',
borderRadius: '9999px',
};

View File

@ -0,0 +1,14 @@
import React from 'react';
import { Button, ButtonProps } from '@react-email/components';
interface ImmichButtonProps extends ButtonProps {}
export const ImmichButton = ({ children, ...props }: ImmichButtonProps) => (
<Button
{...props}
className="py-3 px-8 border bg-immich-primary rounded-full no-underline hover:no-underline text-white hover:text-gray-50 font-bold uppercase"
>
{children}
</Button>
);

View File

@ -0,0 +1,25 @@
import { Column, Img, Link, Row, Text } from '@react-email/components';
import * as React from 'react';
export const ImmichFooter = () => (
<>
<Row className="h-18 w-full">
<Column align="center" className="w-6/12 sm:w-full">
<Link href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<Img className="h-full max-w-full" src={`https://immich.app/img/google-play-badge.png`} />
</Link>
</Column>
<Column align="center" className="w-6/12 sm:w-full">
<div className="h-full p-3">
<Link href="https://apps.apple.com/sg/app/immich/id1613945652">
<Img src={`https://immich.app/img/ios-app-store-badge.png`} alt="Immich" className="max-w-full" />
</Link>
</div>
</Column>
</Row>
<Text className="text-center text-sm text-immich-footer">
<Link href="https://immich.app">Immich</Link> project is available under GNU AGPL v3 license.
</Text>
</>
);

View File

@ -0,0 +1,102 @@
import {
Body,
Container,
Font,
Head,
Hr,
Html,
Img,
Link,
Preview,
Section,
Tailwind,
Text,
} from '@react-email/components';
import * as React from 'react';
import { ImmichFooter } from './footer.template';
interface FutoLayoutProps {
children: React.ReactNode;
preview: string;
}
export const FutoLayout = ({ children, preview }: FutoLayoutProps) => (
<Html>
<Tailwind
config={{
presets: [require('tailwindcss-preset-email')],
theme: {
extend: {
colors: {
// Light Theme
'immich-primary': '#4250AF',
'futo-primary': '#000000',
'futo-bg': '#F4F4f4',
'futo-gray': '#F6F6F4',
'futo-footer': '#6A737D',
},
fontFamily: {
sans: ['Overpass', 'sans-serif'],
mono: ['Overpass Mono', 'monospace'],
},
},
},
}}
>
<Head>
<Font
fontFamily="Overpass"
fallbackFontFamily="sans-serif"
webFont={{
url: 'https://fonts.gstatic.com/s/overpass/v13/qFdH35WCmI96Ajtm81GrU9vyww.woff2',
format: 'woff2',
}}
fontWeight={'100 900'}
fontStyle="normal"
/>
</Head>
<Preview>{preview}</Preview>
<Body className="bg-futo-bg my-auto mx-auto px-2 font-sans text-base text-futo-primary">
<Container className="my-[40px] mx-auto max-w-[465px]">
<Section className="my-6 p-12 border border-red-400 rounded-[50px] bg-gray-50">
<Section className="flex justify-center mb-12">
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
className="h-12 antialiased rounded-none"
alt="Immich"
/>
</Section>
{children}
</Section>
<Section className="flex justify-center my-6">
<Link href="https://futo.org">
<Img
className="h-6"
src="https://futo.org/images/FutoMainLogo.svg"
alt="FUTO"
// style={{
// height: '24px',
// marginTop: '25px',
// marginBottom: '25px',
// }}
/>
</Link>
</Section>
<Hr className="my-2 text-futo-gray" />
<ImmichFooter />
</Container>
</Body>
</Tailwind>
</Html>
);
FutoLayout.PreviewProps = {
preview: 'This is the preview shown on some mail clients',
children: <Text>Email body goes here.</Text>,
} as FutoLayoutProps;
export default FutoLayout;

View File

@ -0,0 +1,74 @@
import { Body, Container, Font, Head, Hr, Html, Img, Preview, Section, Tailwind, Text } from '@react-email/components';
import * as React from 'react';
import { ImmichFooter } from './footer.template';
interface ImmichLayoutProps {
children: React.ReactNode;
preview: string;
}
export const ImmichLayout = ({ children, preview }: ImmichLayoutProps) => (
<Html>
<Tailwind
config={{
presets: [require('tailwindcss-preset-email')],
theme: {
extend: {
colors: {
// Light Theme
'immich-primary': '#4250AF',
'immich-bg': 'white',
'immich-fg': 'black',
'immich-gray': '#F6F6F4',
'immich-footer': '#6A737D',
},
fontFamily: {
sans: ['Overpass', 'sans-serif'],
mono: ['Overpass Mono', 'monospace'],
},
},
},
}}
>
<Head>
<Font
fontFamily="Overpass"
fallbackFontFamily="sans-serif"
webFont={{
url: 'https://fonts.gstatic.com/s/overpass/v13/qFdH35WCmI96Ajtm81GrU9vyww.woff2',
format: 'woff2',
}}
fontWeight={'100 900'}
fontStyle="normal"
/>
</Head>
<Preview>{preview}</Preview>
<Body className="my-auto mx-auto px-2 font-sans text-base text-immich-primary">
<Container className="my-[40px] mx-auto max-w-[465px]">
<Section className="my-6 p-12 border border-red-400 rounded-[50px] bg-slate-200">
<Section className="flex justify-center mb-12">
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
className="h-12 antialiased rounded-none"
alt="Immich"
/>
</Section>
{children}
</Section>
<Hr className="my-2 text-immich-gray" />
<ImmichFooter />
</Container>
</Body>
</Tailwind>
</Html>
);
ImmichLayout.PreviewProps = {
preview: 'This is the preview shown on some mail clients',
children: <Text>Email body goes here.</Text>,
} as ImmichLayoutProps;
export default ImmichLayout;

View File

@ -15,172 +15,50 @@ import {
} from '@react-email/components'; } from '@react-email/components';
import * as CSS from 'csstype'; import * as CSS from 'csstype';
import * as React from 'react'; import * as React from 'react';
import { ImmichButton } from './components/button.component';
import FutoLayout from './components/futo.layout';
/** /**
* Template to be used for FUTOPay project * Template to be used for FUTOPay project
* Variable is {{LICENSEKEY}} * Variable is {{LICENSEKEY}}
* */ * */
export const LicenseEmail = () => ( export const LicenseEmail = () => (
<Html> <FutoLayout preview="Your Immich Server License">
<Head /> <Text>Thank you for supporting Immich and open-source software</Text>
<Preview>Your Immich Server License</Preview>
<Body
style={{
margin: 0,
padding: 0,
backgroundColor: '#f4f4f4',
color: 'rgb(28,28,28)',
fontFamily: 'Overpass, sans-serif',
fontSize: '18px',
lineHeight: '24px',
}}
>
<Container
style={{
width: '540px',
maxWidth: '100%',
padding: '10px',
margin: '0 auto',
}}
>
<Section
style={{
padding: '36px',
tableLayout: 'fixed',
backgroundColor: '#fefefe',
borderRadius: '16px',
textAlign: 'center' as const,
}}
>
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
alt="Immich"
style={{
height: 'auto',
margin: '0 auto 48px auto',
width: '50%',
alignSelf: 'center',
color: 'white',
}}
/>
<Text style={text}>Thank you for supporting Immich and open-source software</Text> <Text>
<Text style={text}>
Your <strong>Immich</strong> license key is Your <strong>Immich</strong> license key is
</Text> </Text>
<Section <Section className="my-2 bg-gray-200 rounded-2xl text-center p-4">
style={{ <Text className="m-0 text-monospace font-bold text-immich-primary">{'{{LICENSEKEY}}'}</Text>
textAlign: 'center',
background: 'rgb(225, 225, 225)',
borderRadius: '16px',
marginBottom: '25px',
}}
>
<Text style={{ fontFamily: 'monospace', fontWeight: 600, color: 'rgb(66, 80, 175)' }}>
{'{{LICENSEKEY}}'}
</Text>
</Section> </Section>
{/* <Text style={text}> {/* <Text>
To activate your instance, you can click the following button or copy and paste the link below to your To activate your instance, you can click the following button or copy and paste the link below to your browser.
browser
</Text> </Text>
<Row> <Section className="flex justify-center my-6">
<Column align="center"> <ImmichButton
<Button
style={button}
href={`https://my.immich.app/link?target=activate_license&licenseKey={{LICENSEKEY}}&activationKey={{ACTIVATIONKEY}}`} href={`https://my.immich.app/link?target=activate_license&licenseKey={{LICENSEKEY}}&activationKey={{ACTIVATIONKEY}}`}
> >
Activate Activate
</Button> </ImmichButton>
</Column> </Section>
</Row>
<Row> <Text className="text-center">
<Column align="center"> <Link
<a className="text-immich-primary text-sm"
style={{ marginTop: '50px', color: 'rgb(66, 80, 175)', fontSize: '0.9rem' }} // style={{ marginTop: '50px', color: 'rgb(66, 80, 175)', fontSize: '0.9rem' }}
href={`https://my.immich.app/link?target=activate_license&licenseKey={{LICENSEKEY}}&activationKey={{ACTIVATIONKEY}}`} href={`https://my.immich.app/link?target=activate_license&licenseKey={{LICENSEKEY}}&activationKey={{ACTIVATIONKEY}}`}
> >
https://my.immich.app/link?target=activate_license&licenseKey={'{{LICENSEKEY}}'}&activationKey= https://my.immich.app/link?target=activate_license&licenseKey={'{{LICENSEKEY}}'}&activationKey=
{'{{ACTIVATIONKEY}}'} {'{{ACTIVATIONKEY}}'}
</a>
</Column>
</Row> */}
</Section>
<Section style={{ textAlign: 'center' }}>
<Row>
<Column align="center">
<Link href="https://futo.org">
<Img
src="https://futo.org/images/FutoMainLogo.svg"
alt="FUTO"
style={{
height: '24px',
marginTop: '25px',
marginBottom: '25px',
}}
/>
</Link> </Link>
</Column> </Text> */}
</Row> </FutoLayout>
</Section>
<Hr style={{ color: 'rgb(66, 80, 175)', marginTop: '0' }} />
<Section style={{ textAlign: 'center' }}>
<Column align="center">
<Link href="https://apps.apple.com/sg/app/immich/id1613945652">
<Img
src={`https://immich.app/img/ios-app-store-badge.png`}
alt="Immich"
style={{ height: '72px', padding: '14px' }}
/>
</Link>
<Link href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<Img src={`https://immich.app/img/google-play-badge.png`} height="96px" alt="Immich" />
</Link>
</Column>
</Section>
<Text
style={{
color: '#6a737d',
fontSize: '0.8rem',
textAlign: 'center' as const,
marginTop: '14px',
}}
>
<Link href="https://immich.app">Immich</Link> project is available under GNU AGPL v3 license.
</Text>
</Container>
</Body>
</Html>
); );
LicenseEmail.PreviewProps = {}; LicenseEmail.PreviewProps = {};
export default LicenseEmail; export default LicenseEmail;
const text = {
margin: '0 0 24px 0',
textAlign: 'left' as const,
fontSize: '16px',
lineHeight: '24px',
};
const button: CSS.Properties = {
backgroundColor: 'rgb(66, 80, 175)',
margin: '1em 0',
padding: '0.75em 3em',
color: '#fff',
fontSize: '1em',
fontWeight: 600,
lineHeight: 1.5,
textTransform: 'uppercase',
borderRadius: '9999px',
};

View File

@ -1,134 +1,25 @@
import { import { Link, Row, Text } from '@react-email/components';
Body,
Button,
Column,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
import * as CSS from 'csstype';
import * as React from 'react'; import * as React from 'react';
import { TestEmailProps } from 'src/interfaces/notification.interface'; import { TestEmailProps } from 'src/interfaces/notification.interface';
import ImmichLayout from './components/immich.layout';
export const TestEmail = ({ baseUrl, displayName }: TestEmailProps) => ( export const TestEmail = ({ baseUrl, displayName }: TestEmailProps) => (
<Html> <ImmichLayout preview="This is a test email from Immich.">
<Head /> <Text className="m-0 text-2xl">
<Preview>This is a test email from Immich</Preview> Hey <strong>{displayName}</strong>!
<Body
style={{
margin: 0,
padding: 0,
backgroundColor: '#ffffff',
color: 'rgb(66, 80, 175)',
fontFamily: 'Overpass, sans-serif',
fontSize: '18px',
lineHeight: '24px',
}}
>
<Container
style={{
width: '480px',
maxWidth: '100%',
padding: '10px',
margin: '0 auto',
}}
>
<Section
style={{
padding: '36px',
tableLayout: 'fixed',
backgroundColor: 'rgb(226, 232, 240)',
border: 'solid 0px rgb(248 113 113)',
borderRadius: '50px',
textAlign: 'center' as const,
}}
>
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
alt="Immich"
style={{
height: 'auto',
margin: '0 auto 48px auto',
width: '50%',
alignSelf: 'center',
color: 'white',
}}
/>
<Text style={text}>
Hey <strong>{displayName}</strong>, this is the test email from your Immich Instance
</Text> </Text>
<Text>This is a test email from your Immich Instance!</Text>
<Row> <Row>
<Link style={{ marginTop: '50px' }} href={baseUrl}> <Link href={baseUrl}>{baseUrl}</Link>
{baseUrl}
</Link>
</Row> </Row>
</Section> </ImmichLayout>
<Hr style={{ color: 'rgb(66, 80, 175)', marginTop: '24px' }} />
<Section style={{ textAlign: 'center' }}>
<Row>
<Column align="center">
<Link href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<Img src={`https://immich.app/img/google-play-badge.png`} height="96px" alt="Immich" />
</Link>
<Link href="https://apps.apple.com/sg/app/immich/id1613945652">
<Img
src={`https://immich.app/img/ios-app-store-badge.png`}
alt="Immich"
style={{ height: '72px', padding: '14px' }}
/>
</Link>
</Column>
</Row>
</Section>
<Text
style={{
color: '#6a737d',
fontSize: '0.8rem',
textAlign: 'center' as const,
marginTop: '12px',
}}
>
<Link href="https://immich.app">Immich</Link> project is available under GNU AGPL v3 license.
</Text>
</Container>
</Body>
</Html>
); );
TestEmail.PreviewProps = { TestEmail.PreviewProps = {
baseUrl: 'https://demo.immich.app/auth/login', baseUrl: 'https://demo.immich.app',
displayName: 'Alan Turing', displayName: 'Alan Turing',
} as TestEmailProps; } as TestEmailProps;
export default TestEmail; export default TestEmail;
const text = {
margin: '0 0 24px 0',
textAlign: 'left' as const,
fontSize: '18px',
lineHeight: '24px',
};
const button: CSS.Properties = {
backgroundColor: 'rgb(66, 80, 175)',
margin: '1em 0',
padding: '0.75em 3em',
color: '#fff',
fontSize: '1em',
fontWeight: 700,
lineHeight: 1.5,
textTransform: 'uppercase',
borderRadius: '9999px',
};

View File

@ -1,74 +1,18 @@
import { import { Link, Section, Text } from '@react-email/components';
Body,
Button,
Column,
Container,
Head,
Hr,
Html,
Img,
Link,
Preview,
Row,
Section,
Text,
} from '@react-email/components';
import * as CSS from 'csstype';
import * as React from 'react'; import * as React from 'react';
import { WelcomeEmailProps } from 'src/interfaces/notification.interface'; import { WelcomeEmailProps } from 'src/interfaces/notification.interface';
import { ImmichButton } from './components/button.component';
import ImmichLayout from './components/immich.layout';
export const WelcomeEmail = ({ baseUrl, displayName, username, password }: WelcomeEmailProps) => ( export const WelcomeEmail = ({ baseUrl, displayName, username, password }: WelcomeEmailProps) => (
<Html> <ImmichLayout preview="You have been invited to a new Immich instance.">
<Head /> <Text className="m-0 text-2xl">
<Preview>You have been invited to a new Immich instance.</Preview>
<Body
style={{
margin: 0,
padding: 0,
backgroundColor: '#ffffff',
color: 'rgb(66, 80, 175)',
fontFamily: 'Overpass, sans-serif',
fontSize: '18px',
lineHeight: '24px',
}}
>
<Container
style={{
width: '480px',
maxWidth: '100%',
padding: '10px',
margin: '0 auto',
}}
>
<Section
style={{
padding: '36px',
tableLayout: 'fixed',
backgroundColor: 'rgb(226, 232, 240)',
border: 'solid 0px rgb(248 113 113)',
borderRadius: '50px',
textAlign: 'center' as const,
}}
>
<Img
src="https://immich.app/img/immich-logo-inline-light.png"
alt="Immich"
style={{
height: 'auto',
margin: '0 auto 48px auto',
width: '50%',
alignSelf: 'center',
color: 'white',
}}
/>
<Text style={text}>
Hey <strong>{displayName}</strong>! Hey <strong>{displayName}</strong>!
</Text> </Text>
<Text style={text}>A new account has been created for you.</Text> <Text>A new account has been created for you.</Text>
<Text style={text}> <Text>
<strong>Username</strong>: {username} <strong>Username</strong>: {username}
{password && ( {password && (
<> <>
@ -78,55 +22,16 @@ export const WelcomeEmail = ({ baseUrl, displayName, username, password }: Welco
)} )}
</Text> </Text>
<Row> <Section className="flex justify-center my-6">
<Text style={{ ...text, marginBottom: '36px' }}> <ImmichButton href={`${baseUrl}/auth/login`}>Login</ImmichButton>
To login, open the link in a browser, or click the button below.
</Text>
</Row>
<Row>
<Link style={{ marginTop: '50px' }} href={baseUrl}>
{baseUrl}
</Link>
</Row>
<Row>
<Button style={button} href={`${baseUrl}/auth/login`}>
Login
</Button>
</Row>
</Section> </Section>
<Hr style={{ color: 'rgb(66, 80, 175)', marginTop: '24px' }} /> <Text className="text-xs">
If you cannot click the button use the link below to proceed with first login.
<Section style={{ textAlign: 'center' }}> <br />
<Row> <Link href={baseUrl}>{baseUrl}</Link>
<Column align="center">
<Link href="https://play.google.com/store/apps/details?id=app.alextran.immich">
<Img src={`https://immich.app/img/google-play-badge.png`} height="96px" alt="Immich" />
</Link>
<Link href="https://apps.apple.com/sg/app/immich/id1613945652">
<Img
src={`https://immich.app/img/ios-app-store-badge.png`}
alt="Immich"
style={{ height: '72px', padding: '14px' }}
/>
</Link>
</Column>
</Row>
</Section>
<Text
style={{
color: '#6a737d',
fontSize: '0.8rem',
textAlign: 'center' as const,
marginTop: '12px',
}}
>
<Link href="https://immich.app">Immich</Link> project is available under GNU AGPL v3 license.
</Text> </Text>
</Container> </ImmichLayout>
</Body>
</Html>
); );
WelcomeEmail.PreviewProps = { WelcomeEmail.PreviewProps = {
@ -137,22 +42,3 @@ WelcomeEmail.PreviewProps = {
} as WelcomeEmailProps; } as WelcomeEmailProps;
export default WelcomeEmail; export default WelcomeEmail;
const text = {
margin: '0 0 24px 0',
textAlign: 'left' as const,
fontSize: '18px',
lineHeight: '24px',
};
const button: CSS.Properties = {
backgroundColor: 'rgb(66, 80, 175)',
margin: '1em 0',
padding: '0.75em 3em',
color: '#fff',
fontSize: '1em',
fontWeight: 700,
lineHeight: 1.5,
textTransform: 'uppercase',
borderRadius: '9999px',
};