@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.
Files changed (137) hide show
  1. package/Models/DatabaseModels/AlertInternalNote.ts +58 -1
  2. package/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.ts +1 -0
  3. package/Models/DatabaseModels/File.ts +1 -1
  4. package/Models/DatabaseModels/IncidentInternalNote.ts +58 -1
  5. package/Models/DatabaseModels/IncidentPublicNote.ts +58 -1
  6. package/Models/DatabaseModels/ScheduledMaintenanceInternalNote.ts +58 -1
  7. package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +58 -1
  8. package/Models/DatabaseModels/StatusPageAnnouncement.ts +49 -0
  9. package/Server/API/AlertInternalNoteAPI.ts +96 -0
  10. package/Server/API/IncidentInternalNoteAPI.ts +96 -0
  11. package/Server/API/IncidentPublicNoteAPI.ts +96 -0
  12. package/Server/API/ScheduledMaintenanceInternalNoteAPI.ts +100 -0
  13. package/Server/API/ScheduledMaintenancePublicNoteAPI.ts +100 -0
  14. package/Server/API/StatusPageAPI.ts +585 -59
  15. package/Server/API/StatusPageAnnouncementAPI.ts +98 -0
  16. package/Server/API/UserAPI.ts +95 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.ts +79 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.ts +81 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.ts +79 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  21. package/Server/Middleware/ProjectAuthorization.ts +3 -1
  22. package/Server/Services/AlertInternalNoteService.ts +75 -2
  23. package/Server/Services/IncidentInternalNoteService.ts +76 -2
  24. package/Server/Services/IncidentPublicNoteService.ts +76 -2
  25. package/Server/Services/ScheduledMaintenanceInternalNoteService.ts +76 -2
  26. package/Server/Services/ScheduledMaintenancePublicNoteService.ts +76 -2
  27. package/Server/Services/ScheduledMaintenanceService.ts +10 -7
  28. package/Server/Services/StatusPagePrivateUserService.ts +10 -7
  29. package/Server/Services/StatusPageService.ts +12 -7
  30. package/Server/Services/StatusPageSubscriberService.ts +19 -13
  31. package/Server/Utils/FileAttachmentMarkdownUtil.ts +98 -0
  32. package/Server/Utils/Response.ts +13 -0
  33. package/Types/File/MimeType.ts +18 -0
  34. package/UI/Components/AttachmentList/EventAttachmentList.tsx +121 -0
  35. package/UI/Components/EventItem/EventItem.tsx +22 -0
  36. package/UI/Components/Feed/FeedItem.tsx +9 -16
  37. package/UI/Components/FilePicker/FilePicker.tsx +441 -145
  38. package/UI/Components/Forms/Fields/FormField.tsx +32 -15
  39. package/UI/Components/Forms/FormSummary.tsx +168 -1
  40. package/UI/Components/Forms/ModelForm.tsx +46 -24
  41. package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
  42. package/UI/Components/Icon/Icon.tsx +1 -1
  43. package/UI/Utils/API/RequestOptions.ts +2 -0
  44. package/UI/Utils/ModelAPI/ModelAPI.ts +18 -0
  45. package/UI/Utils/User.ts +8 -0
  46. package/Utils/API.ts +11 -1
  47. package/build/dist/Models/DatabaseModels/AlertInternalNote.js +49 -1
  48. package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
  49. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js +1 -0
  50. package/build/dist/Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel.js.map +1 -1
  51. package/build/dist/Models/DatabaseModels/File.js +1 -1
  52. package/build/dist/Models/DatabaseModels/File.js.map +1 -1
  53. package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +49 -1
  54. package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
  55. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +49 -1
  56. package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
  57. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js +49 -1
  58. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceInternalNote.js.map +1 -1
  59. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +49 -1
  60. package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
  61. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +48 -0
  62. package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
  63. package/build/dist/Server/API/AlertInternalNoteAPI.js +68 -0
  64. package/build/dist/Server/API/AlertInternalNoteAPI.js.map +1 -0
  65. package/build/dist/Server/API/IncidentInternalNoteAPI.js +68 -0
  66. package/build/dist/Server/API/IncidentInternalNoteAPI.js.map +1 -0
  67. package/build/dist/Server/API/IncidentPublicNoteAPI.js +68 -0
  68. package/build/dist/Server/API/IncidentPublicNoteAPI.js.map +1 -0
  69. package/build/dist/Server/API/ScheduledMaintenanceInternalNoteAPI.js +68 -0
  70. package/build/dist/Server/API/ScheduledMaintenanceInternalNoteAPI.js.map +1 -0
  71. package/build/dist/Server/API/ScheduledMaintenancePublicNoteAPI.js +68 -0
  72. package/build/dist/Server/API/ScheduledMaintenancePublicNoteAPI.js.map +1 -0
  73. package/build/dist/Server/API/StatusPageAPI.js +488 -85
  74. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  75. package/build/dist/Server/API/StatusPageAnnouncementAPI.js +68 -0
  76. package/build/dist/Server/API/StatusPageAnnouncementAPI.js.map +1 -0
  77. package/build/dist/Server/API/UserAPI.js +66 -0
  78. package/build/dist/Server/API/UserAPI.js.map +1 -0
  79. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.js +34 -0
  80. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763471659817-MigrationName.js.map +1 -0
  81. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.js +34 -0
  82. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763477560906-MigrationName.js.map +1 -0
  83. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.js +34 -0
  84. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1763480947474-MigrationName.js.map +1 -0
  85. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  86. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  87. package/build/dist/Server/Middleware/ProjectAuthorization.js +4 -1
  88. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  89. package/build/dist/Server/Services/AlertInternalNoteService.js +54 -2
  90. package/build/dist/Server/Services/AlertInternalNoteService.js.map +1 -1
  91. package/build/dist/Server/Services/IncidentInternalNoteService.js +54 -2
  92. package/build/dist/Server/Services/IncidentInternalNoteService.js.map +1 -1
  93. package/build/dist/Server/Services/IncidentPublicNoteService.js +54 -2
  94. package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
  95. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js +54 -2
  96. package/build/dist/Server/Services/ScheduledMaintenanceInternalNoteService.js.map +1 -1
  97. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +54 -2
  98. package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
  99. package/build/dist/Server/Services/ScheduledMaintenanceService.js +6 -5
  100. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  101. package/build/dist/Server/Services/StatusPagePrivateUserService.js +6 -4
  102. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  103. package/build/dist/Server/Services/StatusPageService.js +7 -4
  104. package/build/dist/Server/Services/StatusPageService.js.map +1 -1
  105. package/build/dist/Server/Services/StatusPageSubscriberService.js +11 -7
  106. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  107. package/build/dist/Server/Utils/FileAttachmentMarkdownUtil.js +67 -0
  108. package/build/dist/Server/Utils/FileAttachmentMarkdownUtil.js.map +1 -0
  109. package/build/dist/Server/Utils/Response.js +8 -0
  110. package/build/dist/Server/Utils/Response.js.map +1 -1
  111. package/build/dist/Types/File/MimeType.js +18 -0
  112. package/build/dist/Types/File/MimeType.js.map +1 -1
  113. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js +42 -0
  114. package/build/dist/UI/Components/AttachmentList/EventAttachmentList.js.map +1 -0
  115. package/build/dist/UI/Components/EventItem/EventItem.js +5 -1
  116. package/build/dist/UI/Components/EventItem/EventItem.js.map +1 -1
  117. package/build/dist/UI/Components/Feed/FeedItem.js +6 -4
  118. package/build/dist/UI/Components/Feed/FeedItem.js.map +1 -1
  119. package/build/dist/UI/Components/FilePicker/FilePicker.js +262 -77
  120. package/build/dist/UI/Components/FilePicker/FilePicker.js.map +1 -1
  121. package/build/dist/UI/Components/Forms/Fields/FormField.js +24 -12
  122. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  123. package/build/dist/UI/Components/Forms/FormSummary.js +77 -1
  124. package/build/dist/UI/Components/Forms/FormSummary.js.map +1 -1
  125. package/build/dist/UI/Components/Forms/ModelForm.js +32 -18
  126. package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
  127. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
  128. package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
  129. package/build/dist/UI/Components/Icon/Icon.js +1 -1
  130. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  131. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +30 -45
  132. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  133. package/build/dist/UI/Utils/User.js +7 -0
  134. package/build/dist/UI/Utils/User.js.map +1 -1
  135. package/build/dist/Utils/API.js +3 -0
  136. package/build/dist/Utils/API.js.map +1 -1
  137. 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 { FileRoute } from "../../ServiceRoute";
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: statusPage.logoFileId
551
- ? new URL(httpProtocol, host)
552
- .addRoute(FileRoute)
553
- .addRoute("/image/" + statusPage.logoFileId)
554
- .toString()
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: statusPage.logoFileId
679
- ? new URL(httpProtocol, host)
680
- .addRoute(FileRoute)
681
- .addRoute("/image/" + statusPage.logoFileId)
682
- .toString()
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
+ }
@@ -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
  }
@@ -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 FileUtil from "../../Utils/File";
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
- {!props.user?.profilePictureId && (
66
- <Image
67
- className="h-10 w-10 rounded-full"
68
- imageUrl={Route.fromString(`${BlankProfilePic}`)}
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">