From 112d157fb793ecc458a3ae86b07392e3173e0663 Mon Sep 17 00:00:00 2001 From: cdxker Date: Mon, 6 Jan 2025 11:12:20 -0800 Subject: [PATCH] feature: made email templates html based and more stylized --- server/src/handlers/invitation_handler.rs | 5 +- server/src/operators/invitation_operator.rs | 55 +++++++------- server/src/public/user_email_invite.html | 84 +++++++++++++++++++++ server/static/output.css | 17 +++++ 4 files changed, 131 insertions(+), 30 deletions(-) create mode 100644 server/src/public/user_email_invite.html diff --git a/server/src/handlers/invitation_handler.rs b/server/src/handlers/invitation_handler.rs index 7b2147a8bc..e210911ac0 100644 --- a/server/src/handlers/invitation_handler.rs +++ b/server/src/handlers/invitation_handler.rs @@ -1,6 +1,6 @@ use super::auth_handler::AdminOnly; use crate::{ - data::models::{Invitation, OrganizationWithSubAndPlan, Pool, RedisPool}, + data::models::{Invitation, OrganizationWithSubAndPlan, Pool, RedisPool, Templates}, errors::ServiceError, middleware::auth_middleware::verify_admin, operators::{ @@ -64,6 +64,7 @@ pub async fn post_invitation( pool: web::Data, redis_pool: web::Data, org_with_plan_and_sub: OrganizationWithSubAndPlan, + templates: Templates<'_>, user: AdminOnly, ) -> Result { let invitation_data = invitation_data.into_inner(); @@ -119,6 +120,7 @@ pub async fn post_invitation( send_invitation_for_existing_user( email.clone(), org_with_plan_and_sub.organization.name, + templates, invitation_data.redirect_uri, ) .await?; @@ -152,6 +154,7 @@ pub async fn post_invitation( send_invitation( invitation.registration_url, invitation.invitation, + templates, org_with_plan_and_sub.organization.name, ) .await?; diff --git a/server/src/operators/invitation_operator.rs b/server/src/operators/invitation_operator.rs index 8b1cf45601..0da1e95ab2 100644 --- a/server/src/operators/invitation_operator.rs +++ b/server/src/operators/invitation_operator.rs @@ -1,9 +1,10 @@ use super::email_operator::send_email; -use crate::data::models::{Invitation, Pool}; +use crate::data::models::{Invitation, Pool, Templates}; use crate::errors::ServiceError; use actix_web::web; use diesel::prelude::*; use diesel_async::RunQueryDsl; +use minijinja::context; /// Diesel query @@ -52,23 +53,20 @@ pub async fn get_invitation_by_id_query( pub async fn send_invitation( inv_url: String, invitation: Invitation, + templates: Templates<'_>, org_name: String, ) -> Result<(), ServiceError> { - let sg_email_content = format!( - "Hello,

- You have been invited to join the Trieve organization: {}.

- To get started, simply click here or use the link below to register and activate your account:
- {}

- We look forward to having you on board!

- Cheers,
- The Trieve Team
- This email is intended for {}. If you encounter any issues or did not expect this invitation, please reach out to us directly at humans@trieve.ai.", - org_name, - inv_url, - inv_url, - inv_url.split('?').collect::>().get(0).unwrap_or(&""), - invitation.email - ); + let templ = templates.get_template("user_email_invite.html").unwrap(); + let sg_email_content = templ + .render(context! { + org_name, + org_redirect_url => inv_url, + email => invitation.email, + new_user => true + }) + .map_err(|e| { + ServiceError::InternalServerError(format!("Error rendering template {}", e)) + })?; send_email(sg_email_content, invitation.email, None) } @@ -76,6 +74,7 @@ pub async fn send_invitation( pub async fn send_invitation_for_existing_user( email: String, org_name: String, + templates: Templates<'_>, organization_url: String, ) -> Result<(), ServiceError> { let split_org_url = organization_url.split('?').collect::>(); @@ -86,19 +85,17 @@ pub async fn send_invitation_for_existing_user( split_org_url.get(1).unwrap_or(&"") ); - let sg_email_content = format!( - "You've been added to a Trieve organization: {}.

- To access this organization, simply click here or use the link below:
- {}

- Cheers,
- The Trieve Team
- This email is intended for {}. If you encounter any issues or did not expect this invitation, please reach out to us directly at humans@trieve.ai.", - org_name, - org_redirect_url, - org_redirect_url, - org_redirect_url, - email - ); + let templ = templates.get_template("user_email_invite.html").unwrap(); + let sg_email_content = templ + .render(context! { + org_name, + org_redirect_url, + email, + new_user => false, + }) + .map_err(|e| { + ServiceError::InternalServerError(format!("Error rendering template {}", e)) + })?; send_email(sg_email_content, email, None) } diff --git a/server/src/public/user_email_invite.html b/server/src/public/user_email_invite.html new file mode 100644 index 0000000000..b0c5ce359c --- /dev/null +++ b/server/src/public/user_email_invite.html @@ -0,0 +1,84 @@ + + + + + + + + + + + +
+

+ Hello! +

+

+ You've been invited to join on {{ org_name }} Trieve. Trieve is a powerful search and retrieval platform that helps teams collaborate and find information quickly. +

+ {% if new_user %} +

+ To get started, click the button below to register and activate your account: +

+ {% else %} +

+ To accept this invitation and join the organization, click the button below: +

+ {% endif %} + + + + + + + + +

+ or if you prefer, click this link directly +

+ + + + + + + +
+ + + + + + +
+ {{ org_redirect_url }} +
+
+ +

+ If you have any questions about using Trieve, please don't hesitate to reach out to our support team. +

+

+ Best regards,
+ The Trieve Team +

+
+

+ This email is intended for {{ email }}. If you received this invitation in error or this email wasn't intended for you, please contact us at support@trieve.ai. Please do not forward this email as the invitation link is unique to your email address. +

+
+ + + + + diff --git a/server/static/output.css b/server/static/output.css index e1c809c5bc..e6e0505089 100644 --- a/server/static/output.css +++ b/server/static/output.css @@ -627,6 +627,10 @@ video { display: flex; } +.table { + display: table; +} + .hidden { display: none; } @@ -705,6 +709,10 @@ video { flex-shrink: 0; } +.border-collapse { + border-collapse: collapse; +} + @keyframes pulse { 50% { opacity: .5; @@ -894,6 +902,10 @@ video { font-weight: 500; } +.capitalize { + text-transform: capitalize; +} + .tracking-tight { letter-spacing: -0.025em; } @@ -917,6 +929,11 @@ video { text-decoration-line: underline; } +.antialiased { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + .outline { outline-style: solid; }