1use crate::prelude::*;
2
3#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
4#[cfg_attr(feature = "ts_rs", derive(TS))]
5pub struct EmailTemplate {
6 pub id: Uuid,
7 pub created_at: DateTime<Utc>,
8 pub updated_at: DateTime<Utc>,
9 pub deleted_at: Option<DateTime<Utc>>,
10 pub content: Option<serde_json::Value>,
11 pub name: String,
12 pub subject: Option<String>,
13 pub exercise_completions_threshold: Option<i32>,
14 pub points_threshold: Option<i32>,
15 pub course_instance_id: Option<Uuid>,
16 pub language: Option<String>,
17}
18
19#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
20#[cfg_attr(feature = "ts_rs", derive(TS))]
21pub struct EmailTemplateNew {
22 pub name: String,
23 pub language: Option<String>,
24 pub content: Option<serde_json::Value>,
25}
26
27#[derive(Debug, Serialize, Deserialize, PartialEq, Clone)]
28#[cfg_attr(feature = "ts_rs", derive(TS))]
29pub struct EmailTemplateUpdate {
30 pub name: String,
31 pub subject: String,
32 pub content: serde_json::Value,
33 pub exercise_completions_threshold: Option<i32>,
34 pub points_threshold: Option<i32>,
35}
36
37pub async fn get_email_templates(
38 conn: &mut PgConnection,
39 course_instance_id: Uuid,
40) -> ModelResult<Vec<EmailTemplate>> {
41 let res = sqlx::query_as!(
42 EmailTemplate,
43 "SELECT *
44FROM email_templates
45WHERE course_instance_id = $1
46 AND deleted_at IS NULL",
47 course_instance_id
48 )
49 .fetch_all(conn)
50 .await?;
51 Ok(res)
52}
53
54pub async fn get_generic_email_template_by_name_and_language(
55 conn: &mut PgConnection,
56 name: &str,
57 language: &str,
58) -> ModelResult<EmailTemplate> {
59 let res = sqlx::query_as!(
60 EmailTemplate,
61 r#"
62SELECT *
63FROM email_templates
64WHERE name = $1
65 AND course_instance_id IS NULL
66 AND deleted_at IS NULL
67 AND (language = $2 OR language = 'en' OR language IS NULL)
68ORDER BY CASE
69 WHEN language = $2 THEN 0
70 WHEN language = 'en' THEN 1
71 WHEN language IS NULL THEN 2
72 ELSE 3
73 END
74LIMIT 1
75 "#,
76 name,
77 language
78 )
79 .fetch_one(conn)
80 .await?;
81 Ok(res)
82}
83
84pub async fn insert_email_template(
85 conn: &mut PgConnection,
86 course_instance_id: Option<Uuid>,
87 email_template: EmailTemplateNew,
88 subject: Option<&'_ str>,
89) -> ModelResult<EmailTemplate> {
90 let res = sqlx::query_as!(
91 EmailTemplate,
92 "
93INSERT INTO email_templates (
94 name,
95 course_instance_id,
96 subject,
97 language,
98 content
99 )
100VALUES ($1, $2, $3, $4, $5) ON CONFLICT (name, language)
101WHERE course_instance_id IS NULL
102 AND deleted_at IS NULL DO
103UPDATE
104SET subject = COALESCE(EXCLUDED.subject, email_templates.subject),
105 content = COALESCE(EXCLUDED.content, email_templates.content),
106 updated_at = NOW()
107RETURNING *
108",
109 email_template.name,
110 course_instance_id,
111 subject,
112 email_template.language,
113 email_template.content,
114 )
115 .fetch_one(conn)
116 .await?;
117 Ok(res)
118}
119
120pub async fn get_email_template(
121 conn: &mut PgConnection,
122 email_template_id: Uuid,
123) -> ModelResult<EmailTemplate> {
124 let res = sqlx::query_as!(
125 EmailTemplate,
126 "SELECT *
127FROM email_templates
128WHERE id = $1
129 AND deleted_at IS NULL",
130 email_template_id
131 )
132 .fetch_one(conn)
133 .await?;
134 Ok(res)
135}
136
137pub async fn update_email_template(
138 conn: &mut PgConnection,
139 email_template_id: Uuid,
140 email_template_update: EmailTemplateUpdate,
141) -> ModelResult<EmailTemplate> {
142 let res = sqlx::query_as!(
143 EmailTemplate,
144 r#"
145UPDATE email_templates
146SET name = $1,
147 subject = $2,
148 content = $3,
149 exercise_completions_threshold = $4,
150 points_threshold = $5
151WHERE id = $6
152RETURNING *
153 "#,
154 email_template_update.name,
155 email_template_update.subject,
156 email_template_update.content,
157 email_template_update.exercise_completions_threshold,
158 email_template_update.points_threshold,
159 email_template_id
160 )
161 .fetch_one(conn)
162 .await?;
163 Ok(res)
164}
165
166pub async fn delete_email_template(
167 conn: &mut PgConnection,
168 email_template_id: Uuid,
169) -> ModelResult<EmailTemplate> {
170 let deleted = sqlx::query_as!(
171 EmailTemplate,
172 r#"
173UPDATE email_templates
174SET deleted_at = now()
175WHERE id = $1
176AND deleted_at IS NULL
177RETURNING *
178 "#,
179 email_template_id
180 )
181 .fetch_one(conn)
182 .await?;
183 Ok(deleted)
184}