-
Notifications
You must be signed in to change notification settings - Fork 1
/
emailProjectLeadsWithSlides.ts
182 lines (151 loc) · 5.54 KB
/
emailProjectLeadsWithSlides.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
/**
* Script to email users with slide reminders.
*
* Author: @snario <[email protected]>
* Date: August 25 2024
*
* What it does:
* - Fetches projects from Linear API.
* - Sends emails to users with slide reminders.
*/
import {
getOrSetSecretInteractive,
getDocumentProperty,
} from "../../lib/googleAppsScript";
import {
fetchAllProjects,
isProjectDueSoon,
isProjectOverdue,
Project,
User,
} from "../../lib/linear";
import ProjectSlide from "../slides/projectSlide";
import { getSlideUrl } from "../../lib/googleSlides";
import { formatDate } from "../../lib/formatting";
export function emailProjectLeadsWithSlides(): void {
const presentation = SlidesApp.getActivePresentation();
if (!presentation)
throw new Error("Active document is not a Google Slides presentation.");
const apiKey = getOrSetSecretInteractive(SlidesApp, "LINEAR_API_KEY");
Logger.log(`Found existing presentation: ${presentation.getName()}`);
const projectSlideMap = JSON.parse(
getDocumentProperty(`${ProjectSlide.cacheKey}_${presentation.getId()}`),
);
if (projectSlideMap === null) {
Logger.log("No project slide map found in cache.");
return;
}
const projects = fetchAllProjects(apiKey);
if (projects.length === 0) {
Logger.log("No projects found. Cannot send emails.");
return;
}
const projectSlideUrlMap: Record<Project["id"], string> = {};
Object.keys(projectSlideMap).forEach((projectId) => {
const slideId = projectSlideMap[projectId];
const slideUrl = getSlideUrl(slideId, presentation);
projectSlideUrlMap[projectId] = slideUrl;
});
sendEmailsForAllUsers(projects, projectSlideUrlMap);
Logger.log("Emails sent successfully.");
}
export function sendEmailsForAllUsers(
projects: Project[],
projectSlideUrlMap: Record<Project["id"], string>,
): void {
const projectsGroupedByUser: Record<
User["email"],
{ user: User; projects: Project[] }
> = {};
projects.forEach((project) => {
const user = project.lead;
if (!user) return;
if (typeof projectsGroupedByUser[user.email] === "undefined")
projectsGroupedByUser[user.email] = { user, projects: [] };
projectsGroupedByUser[user.email].projects.push(project);
});
// Send an email to each user with their grouped projects
for (const userEmail in projectsGroupedByUser) {
const { user, projects } = projectsGroupedByUser[userEmail];
const emailContent = generateEmailContentForUser(
user,
projects,
projectSlideUrlMap,
);
MailApp.sendEmail({
to: user.email,
subject: `SLIDES ASSIGNED: ${projects.length} Projects`,
htmlBody: emailContent,
});
}
}
type ProjectSlideData = {
name: string;
slideUrl: string;
promptText: string;
};
function generateEmailContentForUser(
user: User,
userProjects: Project[],
projectSlideUrlMap: Record<Project["id"], string>,
) {
const userProjectsData: ProjectSlideData[] = userProjects.map((project) => {
const slideUrl = projectSlideUrlMap[project.id];
const targetDateFormatted = formatDate(project.targetDate);
// Check if the project is overdue or due soon using helper functions
const isOverdue = isProjectOverdue(project.targetDate);
const isDueThisWeek = isProjectDueSoon(project.targetDate);
// Determine if the project is completed or canceled
const isCompleted = project.status.name === "Completed";
const isCanceled = project.status.name === "Canceled";
let promptText = "";
if (!isCompleted && !isCanceled) {
if (isOverdue) {
promptText += `Project overdue since ${targetDateFormatted}. Update slide with new deadline and issues.<br/><br/>`;
}
if (isDueThisWeek) {
promptText += `Project due this week (${targetDateFormatted}). Update slide with current status and confidence.<br/><br/>`;
}
if (project.health === "offTrack") {
promptText += `Project off track. Explain issues and recovery plan.<br/><br/>`;
}
if (project.health === "atRisk") {
promptText += `Project at risk. Explain risks and mitigation plan.<br/><br/>`;
}
}
if (isCompleted) {
promptText += `Project completed. Summarize outcomes and learnings.<br/><br/>`;
}
if (isCanceled) {
promptText += `Project canceled. Explain reasons and next steps.<br/><br/>`;
}
return { ...project, slideUrl, promptText };
});
const emailContent = template(user, userProjectsData);
return emailContent;
}
const template = (user: User, userProjects: ProjectSlideData[]) => `
<!DOCTYPE html>
<html>
<body>
<p>Hey ${user.name},</p>
<p>You have multiple slides to fill out for the upcoming All Hands meeting!</p>
<table>
${userProjects
.map(
(project) => `
<tr>
<td style="width: 200px;">
<a href="${project.slideUrl}" style="text-decoration:none;">${project.name}</a>
</td>
<td style="width: 400px;">
${project.promptText}
</td>
</tr>
`,
)
.join("")}
</table>
</body>
</html>
`;