@oneuptime/common 8.0.5580 → 8.0.5581
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/AlertInternalNote.ts +58 -1
- package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +1 -0
- package/Models/DatabaseModels/File.ts +1 -1
- package/Models/DatabaseModels/IncidentInternalNote.ts +58 -1
- package/Models/DatabaseModels/IncidentPublicNote.ts +58 -1
- package/Models/DatabaseModels/ScheduledMaintenanceInternalNote.ts +58 -1
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +58 -1
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +49 -0
- package/Server/API/AlertInternalNoteAPI.ts +96 -0
- package/Server/API/IncidentInternalNoteAPI.ts +96 -0
- package/Server/API/IncidentPublicNoteAPI.ts +96 -0
- package/Server/API/ScheduledMaintenanceInternalNoteAPI.ts +100 -0
- package/Server/API/ScheduledMaintenancePublicNoteAPI.ts +100 -0
- package/Server/API/StatusPageAPI.ts +585 -59
- package/Server/API/StatusPageAnnouncementAPI.ts +98 -0
- package/Server/API/UserAPI.ts +95 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.ts +79 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.ts +81 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.ts +79 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/ProjectAuthorization.ts +3 -1
- package/Server/Services/AlertInternalNoteService.ts +75 -2
- package/Server/Services/IncidentInternalNoteService.ts +76 -2
- package/Server/Services/IncidentPublicNoteService.ts +76 -2
- package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +76 -2
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +76 -2
- package/Server/Services/ScheduledMaintenanceService.ts +10 -7
- package/Server/Services/StatusPagePrivateUserService.ts +10 -7
- package/Server/Services/StatusPageService.ts +12 -7
- package/Server/Services/StatusPageSubscriberService.ts +19 -13
- package/Server/Utils/FileAttachmentMarkdownUtil.ts +98 -0
- package/Server/Utils/Response.ts +13 -0
- package/Types/File/MimeType.ts +18 -0
- package/UI/Components/AttachmentList/EventAttachmentList.tsx +121 -0
- package/UI/Components/EventItem/EventItem.tsx +22 -0
- package/UI/Components/Feed/FeedItem.tsx +9 -16
- package/UI/Components/FilePicker/FilePicker.tsx +441 -145
- package/UI/Components/Forms/Fields/FormField.tsx +32 -15
- package/UI/Components/Forms/FormSummary.tsx +168 -1
- package/UI/Components/Forms/ModelForm.tsx +46 -24
- package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
- package/UI/Components/Icon/Icon.tsx +1 -1
- package/UI/Utils/API/RequestOptions.ts +2 -0
- package/UI/Utils/ModelAPI/ModelAPI.ts +18 -0
- package/UI/Utils/User.ts +8 -0
- package/Utils/API.ts +11 -1
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js +49 -1
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +1 -0
- package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
- package/build/dist/Models/DatabaseModels/File.js +1 -1
- package/build/dist/Models/DatabaseModels/File.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +49 -1
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +49 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js +49 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +49 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +48 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Server/API/AlertInternalNoteAPI.js +68 -0
- package/build/dist/Server/API/AlertInternalNoteAPI.js.map +1 -0
- package/build/dist/Server/API/IncidentInternalNoteAPI.js +68 -0
- package/build/dist/Server/API/IncidentInternalNoteAPI.js.map +1 -0
- package/build/dist/Server/API/IncidentPublicNoteAPI.js +68 -0
- package/build/dist/Server/API/IncidentPublicNoteAPI.js.map +1 -0
- package/build/dist/Server/API/ScheduledMaintenanceInternalNoteAPI.js +68 -0
- package/build/dist/Server/API/ScheduledMaintenanceInternalNoteAPI.js.map +1 -0
- package/build/dist/Server/API/ScheduledMaintenancePublicNoteAPI.js +68 -0
- package/build/dist/Server/API/ScheduledMaintenancePublicNoteAPI.js.map +1 -0
- package/build/dist/Server/API/StatusPageAPI.js +488 -85
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAnnouncementAPI.js +68 -0
- package/build/dist/Server/API/StatusPageAnnouncementAPI.js.map +1 -0
- package/build/dist/Server/API/UserAPI.js +66 -0
- package/build/dist/Server/API/UserAPI.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js +4 -1
- package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AlertInternalNoteService.js +54 -2
- package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentInternalNoteService.js +54 -2
- package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +54 -2
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +54 -2
- package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +54 -2
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +6 -5
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPagePrivateUserService.js +6 -4
- package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +7 -4
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +11 -7
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Utils/FileAttachmentMarkdownUtil.js +67 -0
- package/build/dist/Server/Utils/FileAttachmentMarkdownUtil.js.map +1 -0
- package/build/dist/Server/Utils/Response.js +8 -0
- package/build/dist/Server/Utils/Response.js.map +1 -1
- package/build/dist/Types/File/MimeType.js +18 -0
- package/build/dist/Types/File/MimeType.js.map +1 -1
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +42 -0
- package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -0
- package/build/dist/UI/Components/EventItem/EventItem.js +5 -1
- package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
- package/build/dist/UI/Components/Feed/FeedItem.js +6 -4
- package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
- package/build/dist/UI/Components/FilePicker/FilePicker.js +262 -77
- package/build/dist/UI/Components/FilePicker/FilePicker.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +24 -12
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/FormSummary.js +77 -1
- package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +32 -18
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +1 -1
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +30 -45
- package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
- package/build/dist/UI/Utils/User.js +7 -0
- package/build/dist/UI/Utils/User.js.map +1 -1
- package/build/dist/Utils/API.js +3 -0
- package/build/dist/Utils/API.js.map +1 -1
- package/package.json +6 -6
|
@@ -16,7 +16,7 @@ import ProjectCallSMSConfigService from "./ProjectCallSMSConfigService";
|
|
|
16
16
|
import ProjectService, { CurrentPlan } from "./ProjectService";
|
|
17
17
|
import SmsService from "./SmsService";
|
|
18
18
|
import StatusPageService from "./StatusPageService";
|
|
19
|
-
import {
|
|
19
|
+
import { StatusPageApiRoute } from "../../ServiceRoute";
|
|
20
20
|
import Hostname from "../../Types/API/Hostname";
|
|
21
21
|
import Protocol from "../../Types/API/Protocol";
|
|
22
22
|
import URL from "../../Types/API/URL";
|
|
@@ -522,6 +522,8 @@ Stay informed about service availability! 🚀`;
|
|
|
522
522
|
|
|
523
523
|
const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
|
|
524
524
|
logger.debug(`HTTP Protocol: ${httpProtocol}`);
|
|
525
|
+
const statusPageIdString: string | null =
|
|
526
|
+
statusPage.id?.toString() || statusPage._id?.toString() || null;
|
|
525
527
|
|
|
526
528
|
const confirmSubscriptionLink: string = this.getConfirmSubscriptionLink({
|
|
527
529
|
statusPageUrl: statusPageURL,
|
|
@@ -547,12 +549,13 @@ Stay informed about service availability! 🚀`;
|
|
|
547
549
|
templateType: EmailTemplateType.ConfirmStatusPageSubscription,
|
|
548
550
|
vars: {
|
|
549
551
|
statusPageName: statusPageName,
|
|
550
|
-
logoUrl:
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
552
|
+
logoUrl:
|
|
553
|
+
statusPage.logoFileId && statusPageIdString
|
|
554
|
+
? new URL(httpProtocol, host)
|
|
555
|
+
.addRoute(StatusPageApiRoute)
|
|
556
|
+
.addRoute(`/logo/${statusPageIdString}`)
|
|
557
|
+
.toString()
|
|
558
|
+
: "",
|
|
556
559
|
statusPageUrl: statusPageURL,
|
|
557
560
|
isPublicStatusPage: statusPage.isPublicStatusPage
|
|
558
561
|
? "true"
|
|
@@ -656,6 +659,8 @@ Stay informed about service availability! 🚀`;
|
|
|
656
659
|
|
|
657
660
|
const httpProtocol: Protocol = await DatabaseConfig.getHttpProtocol();
|
|
658
661
|
logger.debug(`HTTP Protocol: ${httpProtocol}`);
|
|
662
|
+
const statusPageIdString: string | null =
|
|
663
|
+
statusPage.id?.toString() || statusPage._id?.toString() || null;
|
|
659
664
|
|
|
660
665
|
const unsubscribeLink: string = this.getUnsubscribeLink(
|
|
661
666
|
URL.fromString(statusPageURL),
|
|
@@ -675,12 +680,13 @@ Stay informed about service availability! 🚀`;
|
|
|
675
680
|
templateType: EmailTemplateType.SubscribedToStatusPage,
|
|
676
681
|
vars: {
|
|
677
682
|
statusPageName: statusPageName,
|
|
678
|
-
logoUrl:
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
683
|
+
logoUrl:
|
|
684
|
+
statusPage.logoFileId && statusPageIdString
|
|
685
|
+
? new URL(httpProtocol, host)
|
|
686
|
+
.addRoute(StatusPageApiRoute)
|
|
687
|
+
.addRoute(`/logo/${statusPageIdString}`)
|
|
688
|
+
.toString()
|
|
689
|
+
: "",
|
|
684
690
|
statusPageUrl: statusPageURL,
|
|
685
691
|
isPublicStatusPage: statusPage.isPublicStatusPage
|
|
686
692
|
? "true"
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import File from "../../Models/DatabaseModels/File";
|
|
2
|
+
import { AppApiRoute } from "../../ServiceRoute";
|
|
3
|
+
import Route from "../../Types/API/Route";
|
|
4
|
+
import ObjectID from "../../Types/ObjectID";
|
|
5
|
+
import FileService from "../Services/FileService";
|
|
6
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
7
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
8
|
+
|
|
9
|
+
export interface FileAttachmentMarkdownInput {
|
|
10
|
+
modelId: ObjectID;
|
|
11
|
+
attachmentIds: Array<ObjectID>;
|
|
12
|
+
attachmentApiPath: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export default class FileAttachmentMarkdownUtil {
|
|
16
|
+
public static async buildAttachmentMarkdown(
|
|
17
|
+
input: FileAttachmentMarkdownInput,
|
|
18
|
+
): Promise<string> {
|
|
19
|
+
if (
|
|
20
|
+
!input.modelId ||
|
|
21
|
+
!input.attachmentIds ||
|
|
22
|
+
input.attachmentIds.length === 0
|
|
23
|
+
) {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const uniqueIds: Array<string> = Array.from(
|
|
28
|
+
new Set(
|
|
29
|
+
input.attachmentIds
|
|
30
|
+
.map((id: ObjectID) => {
|
|
31
|
+
return id.toString();
|
|
32
|
+
})
|
|
33
|
+
.filter((value: string) => {
|
|
34
|
+
return Boolean(value);
|
|
35
|
+
}),
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
if (uniqueIds.length === 0) {
|
|
40
|
+
return "";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const files: Array<File> = await FileService.findBy({
|
|
44
|
+
query: {
|
|
45
|
+
_id: QueryHelper.any(uniqueIds),
|
|
46
|
+
},
|
|
47
|
+
limit: LIMIT_PER_PROJECT,
|
|
48
|
+
skip: 0,
|
|
49
|
+
select: {
|
|
50
|
+
_id: true,
|
|
51
|
+
name: true,
|
|
52
|
+
},
|
|
53
|
+
props: {
|
|
54
|
+
isRoot: true,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
if (!files.length) {
|
|
59
|
+
return "";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const fileById: Map<string, File> = new Map(
|
|
63
|
+
files
|
|
64
|
+
.filter((file: File) => {
|
|
65
|
+
return Boolean(file._id);
|
|
66
|
+
})
|
|
67
|
+
.map((file: File) => {
|
|
68
|
+
return [file._id!.toString(), file];
|
|
69
|
+
}),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const attachmentLines: Array<string> = [];
|
|
73
|
+
|
|
74
|
+
for (const id of input.attachmentIds) {
|
|
75
|
+
const key: string = id.toString();
|
|
76
|
+
const file: File | undefined = fileById.get(key);
|
|
77
|
+
|
|
78
|
+
if (!file) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const fileName: string = file.name || "Attachment";
|
|
83
|
+
|
|
84
|
+
const route: Route = Route.fromString(AppApiRoute.toString())
|
|
85
|
+
.addRoute(input.attachmentApiPath)
|
|
86
|
+
.addRoute(`/${input.modelId.toString()}`)
|
|
87
|
+
.addRoute(`/${key}`);
|
|
88
|
+
|
|
89
|
+
attachmentLines.push(`- [${fileName}](${route.toString()})`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!attachmentLines.length) {
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return `\n\n**Attachments:**\n${attachmentLines.join("\n")}\n`;
|
|
97
|
+
}
|
|
98
|
+
}
|
package/Server/Utils/Response.ts
CHANGED
|
@@ -42,6 +42,7 @@ export default class Response {
|
|
|
42
42
|
res: ExpressResponse,
|
|
43
43
|
path: string,
|
|
44
44
|
): void {
|
|
45
|
+
Response.setNoCacheHeaders(res);
|
|
45
46
|
res.sendFile(path);
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -56,6 +57,7 @@ export default class Response {
|
|
|
56
57
|
const oneUptimeResponse: OneUptimeResponse = res as OneUptimeResponse;
|
|
57
58
|
|
|
58
59
|
if (headers) {
|
|
60
|
+
Response.setNoCacheHeaders(oneUptimeResponse);
|
|
59
61
|
for (const key in headers) {
|
|
60
62
|
oneUptimeResponse.set(key, headers[key]?.toString() || "");
|
|
61
63
|
}
|
|
@@ -322,4 +324,15 @@ export default class Response {
|
|
|
322
324
|
oneUptimeResponse.writeHead(200, { "Content-Type": "text/javascript" });
|
|
323
325
|
oneUptimeResponse.end(javascript);
|
|
324
326
|
}
|
|
327
|
+
|
|
328
|
+
public static setNoCacheHeaders(res: ExpressResponse): void {
|
|
329
|
+
const oneUptimeResponse: OneUptimeResponse = res as OneUptimeResponse;
|
|
330
|
+
|
|
331
|
+
oneUptimeResponse.setHeader(
|
|
332
|
+
"Cache-Control",
|
|
333
|
+
"no-store, no-cache, must-revalidate",
|
|
334
|
+
);
|
|
335
|
+
oneUptimeResponse.setHeader("Pragma", "no-cache");
|
|
336
|
+
oneUptimeResponse.setHeader("Expires", "0");
|
|
337
|
+
}
|
|
325
338
|
}
|
package/Types/File/MimeType.ts
CHANGED
|
@@ -6,6 +6,24 @@ enum MimeType {
|
|
|
6
6
|
jpg = "image/jpeg",
|
|
7
7
|
jpeg = "image/jpeg",
|
|
8
8
|
svg = "image/svg+xml",
|
|
9
|
+
gif = "image/gif",
|
|
10
|
+
webp = "image/webp",
|
|
11
|
+
pdf = "application/pdf",
|
|
12
|
+
doc = "application/msword",
|
|
13
|
+
docx = "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
14
|
+
txt = "text/plain",
|
|
15
|
+
md = "text/markdown",
|
|
16
|
+
csv = "text/csv",
|
|
17
|
+
rtf = "application/rtf",
|
|
18
|
+
odt = "application/vnd.oasis.opendocument.text",
|
|
19
|
+
json = "application/json",
|
|
20
|
+
zip = "application/zip",
|
|
21
|
+
xls = "application/vnd.ms-excel",
|
|
22
|
+
xlsx = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
23
|
+
ods = "application/vnd.oasis.opendocument.spreadsheet",
|
|
24
|
+
ppt = "application/vnd.ms-powerpoint",
|
|
25
|
+
pptx = "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
26
|
+
odp = "application/vnd.oasis.opendocument.presentation",
|
|
9
27
|
|
|
10
28
|
// TODO add more mime types.
|
|
11
29
|
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import React, { FunctionComponent, ReactElement } from "react";
|
|
2
|
+
|
|
3
|
+
export interface EventAttachment {
|
|
4
|
+
name: string;
|
|
5
|
+
downloadUrl: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface EventAttachmentListProps {
|
|
9
|
+
attachments: Array<EventAttachment>;
|
|
10
|
+
title?: string;
|
|
11
|
+
variant?: "section" | "inline";
|
|
12
|
+
showHeader?: boolean;
|
|
13
|
+
showCount?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getAttachmentExtensionLabel(fileName?: string | null): string | null {
|
|
18
|
+
if (!fileName) {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const trimmedName: string = fileName.trim();
|
|
23
|
+
const lastDotIndex: number = trimmedName.lastIndexOf(".");
|
|
24
|
+
|
|
25
|
+
if (lastDotIndex === -1 || lastDotIndex === trimmedName.length - 1) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return trimmedName.substring(lastDotIndex + 1).toUpperCase();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type AttachmentCardProps = {
|
|
33
|
+
attachment: EventAttachment;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const AttachmentCard: FunctionComponent<AttachmentCardProps> = (
|
|
37
|
+
props: AttachmentCardProps,
|
|
38
|
+
): ReactElement => {
|
|
39
|
+
const { attachment } = props;
|
|
40
|
+
const extensionLabel: string | null = getAttachmentExtensionLabel(
|
|
41
|
+
attachment.name,
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<li>
|
|
46
|
+
<a
|
|
47
|
+
href={attachment.downloadUrl}
|
|
48
|
+
target="_blank"
|
|
49
|
+
rel="noopener noreferrer"
|
|
50
|
+
title={attachment.name}
|
|
51
|
+
className="group flex items-center justify-between gap-3 rounded-lg border border-gray-200 bg-white px-3 py-2 text-sm text-gray-900 transition hover:border-gray-300 hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-gray-200"
|
|
52
|
+
>
|
|
53
|
+
<span className="flex items-center gap-3 min-w-0">
|
|
54
|
+
<span className="flex flex-col min-w-0">
|
|
55
|
+
<span className="text-sm font-medium text-gray-900 truncate">
|
|
56
|
+
{attachment.name || "Attachment"}
|
|
57
|
+
</span>
|
|
58
|
+
{extensionLabel && (
|
|
59
|
+
<span className="text-[11px] font-semibold uppercase tracking-wide text-gray-400">
|
|
60
|
+
{extensionLabel}
|
|
61
|
+
</span>
|
|
62
|
+
)}
|
|
63
|
+
</span>
|
|
64
|
+
</span>
|
|
65
|
+
|
|
66
|
+
<span className="flex flex-shrink-0 items-center text-gray-500 text-xs font-semibold uppercase tracking-wide">
|
|
67
|
+
Download
|
|
68
|
+
</span>
|
|
69
|
+
</a>
|
|
70
|
+
</li>
|
|
71
|
+
);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const EventAttachmentList: FunctionComponent<EventAttachmentListProps> = (
|
|
75
|
+
props: EventAttachmentListProps,
|
|
76
|
+
): ReactElement | null => {
|
|
77
|
+
const {
|
|
78
|
+
attachments,
|
|
79
|
+
title = "Attachments",
|
|
80
|
+
variant = "section",
|
|
81
|
+
showHeader = true,
|
|
82
|
+
showCount = true,
|
|
83
|
+
className = "",
|
|
84
|
+
} = props;
|
|
85
|
+
|
|
86
|
+
if (!attachments || attachments.length === 0) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const attachmentsList: ReactElement = (
|
|
91
|
+
<ul className="space-y-2">
|
|
92
|
+
{attachments.map((attachment: EventAttachment, index: number) => {
|
|
93
|
+
return <AttachmentCard attachment={attachment} key={index} />;
|
|
94
|
+
})}
|
|
95
|
+
</ul>
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (variant === "inline") {
|
|
99
|
+
return <div className={className}>{attachmentsList}</div>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<div
|
|
104
|
+
className={`mt-4 rounded-2xl border border-gray-100 bg-gray-50/80 p-4 ${className}`.trim()}
|
|
105
|
+
>
|
|
106
|
+
{showHeader && (
|
|
107
|
+
<div className="flex items-center justify-between text-xs font-semibold uppercase tracking-wide text-gray-500">
|
|
108
|
+
<span>{title}</span>
|
|
109
|
+
{showCount && (
|
|
110
|
+
<span className="rounded-full bg-white px-2 py-0.5 text-[11px] font-semibold text-gray-600">
|
|
111
|
+
{attachments.length}
|
|
112
|
+
</span>
|
|
113
|
+
)}
|
|
114
|
+
</div>
|
|
115
|
+
)}
|
|
116
|
+
<div className="mt-3">{attachmentsList}</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export default EventAttachmentList;
|
|
@@ -2,6 +2,7 @@ import Icon from "../Icon/Icon";
|
|
|
2
2
|
import Link from "../Link/Link";
|
|
3
3
|
import MarkdownViewer from "../Markdown.tsx/LazyMarkdownViewer";
|
|
4
4
|
import Pill from "../Pill/Pill";
|
|
5
|
+
import EventAttachmentList from "../AttachmentList/EventAttachmentList";
|
|
5
6
|
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
6
7
|
import Route from "../../../Types/API/Route";
|
|
7
8
|
import URL from "../../../Types/API/URL";
|
|
@@ -16,6 +17,11 @@ export enum TimelineItemType {
|
|
|
16
17
|
Note = "Note",
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
export interface TimelineAttachment {
|
|
21
|
+
name: string;
|
|
22
|
+
downloadUrl: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
19
25
|
export interface TimelineItem {
|
|
20
26
|
date: Date;
|
|
21
27
|
note?: string;
|
|
@@ -23,6 +29,7 @@ export interface TimelineItem {
|
|
|
23
29
|
state?: BaseModel;
|
|
24
30
|
icon: IconProp;
|
|
25
31
|
iconColor: Color;
|
|
32
|
+
attachments?: Array<TimelineAttachment>;
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
export interface EventItemLabel {
|
|
@@ -46,6 +53,7 @@ export interface ComponentProps {
|
|
|
46
53
|
anotherStatusColor?: Color | undefined;
|
|
47
54
|
eventSecondDescription: string;
|
|
48
55
|
labels?: Array<EventItemLabel> | undefined;
|
|
56
|
+
eventAttachments?: Array<TimelineAttachment> | undefined;
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
const EventItem: FunctionComponent<ComponentProps> = (
|
|
@@ -102,6 +110,10 @@ const EventItem: FunctionComponent<ComponentProps> = (
|
|
|
102
110
|
</div>
|
|
103
111
|
)}
|
|
104
112
|
|
|
113
|
+
{props.eventAttachments && props.eventAttachments.length > 0 && (
|
|
114
|
+
<EventAttachmentList attachments={props.eventAttachments} />
|
|
115
|
+
)}
|
|
116
|
+
|
|
105
117
|
{props.eventSecondDescription && (
|
|
106
118
|
<div className="mt-3 text-gray-500 text-sm active-event-box-body-second-description">
|
|
107
119
|
{props.eventSecondDescription}
|
|
@@ -278,6 +290,16 @@ const EventItem: FunctionComponent<ComponentProps> = (
|
|
|
278
290
|
<p>
|
|
279
291
|
<MarkdownViewer text={item.note || ""} />
|
|
280
292
|
</p>
|
|
293
|
+
{item.attachments &&
|
|
294
|
+
item.attachments.length > 0 && (
|
|
295
|
+
<EventAttachmentList
|
|
296
|
+
attachments={item.attachments}
|
|
297
|
+
variant="inline"
|
|
298
|
+
showHeader={false}
|
|
299
|
+
showCount={false}
|
|
300
|
+
className="mt-3"
|
|
301
|
+
/>
|
|
302
|
+
)}
|
|
281
303
|
</div>
|
|
282
304
|
</div>
|
|
283
305
|
</div>
|
|
@@ -4,7 +4,7 @@ import { GetReactElementFunction } from "../../Types/FunctionTypes";
|
|
|
4
4
|
import Image from "../Image/Image";
|
|
5
5
|
import Route from "../../../Types/API/Route";
|
|
6
6
|
import BlankProfilePic from "../../Images/users/blank-profile.svg";
|
|
7
|
-
import
|
|
7
|
+
import UserUtil from "../../Utils/User";
|
|
8
8
|
import ObjectID from "../../../Types/ObjectID";
|
|
9
9
|
import OneUptimeDate from "../../../Types/Date";
|
|
10
10
|
import Tooltip from "../Tooltip/Tooltip";
|
|
@@ -60,23 +60,16 @@ const FeedItem: FunctionComponent<ComponentProps> = (
|
|
|
60
60
|
};
|
|
61
61
|
|
|
62
62
|
const getUserIcon: GetReactElementFunction = (): ReactElement => {
|
|
63
|
+
const userImageRoute: Route = props.user?.id
|
|
64
|
+
? UserUtil.getProfilePictureRoute(props.user.id as ObjectID)
|
|
65
|
+
: Route.fromString(`${BlankProfilePic}`);
|
|
66
|
+
|
|
63
67
|
return (
|
|
64
68
|
<div>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
/>
|
|
70
|
-
)}
|
|
71
|
-
|
|
72
|
-
{props.user?.profilePictureId && (
|
|
73
|
-
<Image
|
|
74
|
-
className="flex size-10 items-center justify-center rounded-full bg-gray-400 ring-8 ring-white"
|
|
75
|
-
imageUrl={FileUtil.getFileRoute(
|
|
76
|
-
props.user!.profilePictureId as ObjectID,
|
|
77
|
-
)}
|
|
78
|
-
/>
|
|
79
|
-
)}
|
|
69
|
+
<Image
|
|
70
|
+
className="flex size-10 items-center justify-center rounded-full bg-gray-400 ring-8 ring-white"
|
|
71
|
+
imageUrl={userImageRoute}
|
|
72
|
+
/>
|
|
80
73
|
|
|
81
74
|
{props.icon && (
|
|
82
75
|
<span className="absolute -bottom-0.5 -right-1 rounded-tl bg-white px-0.5 py-px">
|