@salesforcedevs/docs-components 1.17.0-hack-alpha4 → 1.17.0-hack-alpha5
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/package.json +1 -1
- package/src/modules/doc/commentPopup/README.md +212 -0
- package/src/modules/doc/commentPopup/commentDevHelper.ts +152 -0
- package/src/modules/doc/commentPopup/commentPopup.css +91 -0
- package/src/modules/doc/commentPopup/commentPopup.html +20 -1
- package/src/modules/doc/commentPopup/commentPopup.ts +230 -29
- package/src/modules/doc/commentPopup/commentUtils.ts +250 -0
- package/src/modules/doc/heading/heading.css +12 -382
- package/src/modules/doc/heading/heading.html +28 -150
- package/src/modules/doc/heading/heading.ts +4 -204
|
@@ -3,20 +3,34 @@ import { normalizeBoolean } from "dxUtils/normalizers";
|
|
|
3
3
|
|
|
4
4
|
interface Comment {
|
|
5
5
|
email: string;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
timestamp: Date;
|
|
6
|
+
comment_text: string;
|
|
7
|
+
timestamp: string;
|
|
9
8
|
}
|
|
10
9
|
|
|
10
|
+
interface ApiCommentPayload {
|
|
11
|
+
branch: string;
|
|
12
|
+
file_path: string;
|
|
13
|
+
heading_title: string;
|
|
14
|
+
start_line: string;
|
|
15
|
+
end_line: string;
|
|
16
|
+
comment: {
|
|
17
|
+
comment_text: string;
|
|
18
|
+
email: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Local storage key for comments
|
|
24
|
+
const COMMENTS_STORAGE_KEY = "dsc_comments";
|
|
25
|
+
|
|
11
26
|
export default class CommentPopup extends LightningElement {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@api contentType?: string;
|
|
27
|
+
iconSymbol = "chat";
|
|
28
|
+
iconSize = "medium";
|
|
29
|
+
popupTitle = "Leave a Comment";
|
|
30
|
+
submitButtonLabel = "Post Comment";
|
|
31
|
+
emailPlaceholder = "Enter your email";
|
|
32
|
+
commentPlaceholder = "Enter your comment";
|
|
33
|
+
|
|
20
34
|
@api headingTitle?: string;
|
|
21
35
|
@api filePath?: string;
|
|
22
36
|
@api startLine?: string;
|
|
@@ -37,10 +51,11 @@ export default class CommentPopup extends LightningElement {
|
|
|
37
51
|
@track private emailError = "";
|
|
38
52
|
@track private commentError = "";
|
|
39
53
|
@track private isSubmitting = false;
|
|
54
|
+
@track private isLoading = false;
|
|
55
|
+
@track private apiError = "";
|
|
56
|
+
@track private submitSuccess = false;
|
|
40
57
|
|
|
41
58
|
get showComments() {
|
|
42
|
-
const unwrapped = JSON.parse(JSON.stringify(this.comments));
|
|
43
|
-
console.log("Comments (JSON unwrapped):", unwrapped);
|
|
44
59
|
return this.comments.length > 0;
|
|
45
60
|
}
|
|
46
61
|
|
|
@@ -54,7 +69,9 @@ export default class CommentPopup extends LightningElement {
|
|
|
54
69
|
|
|
55
70
|
get sortedComments() {
|
|
56
71
|
return [...this.comments].sort(
|
|
57
|
-
(a, b) =>
|
|
72
|
+
(a, b) =>
|
|
73
|
+
new Date(b.timestamp).getTime() -
|
|
74
|
+
new Date(a.timestamp).getTime()
|
|
58
75
|
);
|
|
59
76
|
}
|
|
60
77
|
|
|
@@ -68,31 +85,49 @@ export default class CommentPopup extends LightningElement {
|
|
|
68
85
|
|
|
69
86
|
handleIconClick() {
|
|
70
87
|
this._open = !this._open;
|
|
88
|
+
if (this._open) {
|
|
89
|
+
this.fetchComments();
|
|
90
|
+
}
|
|
71
91
|
}
|
|
72
92
|
|
|
73
93
|
handleEmailChange(event: Event) {
|
|
74
94
|
this.email = (event.target as HTMLInputElement).value;
|
|
75
95
|
this.emailError = "";
|
|
96
|
+
this.apiError = "";
|
|
97
|
+
this.submitSuccess = false;
|
|
76
98
|
}
|
|
77
99
|
|
|
78
100
|
handleCommentChange(event: Event) {
|
|
79
101
|
this.comment = (event.target as HTMLTextAreaElement).value;
|
|
80
102
|
this.commentError = "";
|
|
103
|
+
this.apiError = "";
|
|
104
|
+
this.submitSuccess = false;
|
|
81
105
|
}
|
|
82
106
|
|
|
83
|
-
handleSubmit() {
|
|
107
|
+
async handleSubmit() {
|
|
84
108
|
if (!this.validateForm()) {
|
|
85
109
|
return;
|
|
86
110
|
}
|
|
87
111
|
|
|
88
112
|
this.isSubmitting = true;
|
|
113
|
+
this.apiError = "";
|
|
114
|
+
this.submitSuccess = false;
|
|
89
115
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.addComment();
|
|
116
|
+
try {
|
|
117
|
+
await this.addComment();
|
|
93
118
|
this.resetForm();
|
|
119
|
+
this.submitSuccess = true;
|
|
120
|
+
|
|
121
|
+
// Auto-hide success message after 3 seconds
|
|
122
|
+
setTimeout(() => {
|
|
123
|
+
this.submitSuccess = false;
|
|
124
|
+
}, 3000);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error("Error submitting comment:", error);
|
|
127
|
+
this.apiError = "Failed to post comment. Please try again.";
|
|
128
|
+
} finally {
|
|
94
129
|
this.isSubmitting = false;
|
|
95
|
-
}
|
|
130
|
+
}
|
|
96
131
|
}
|
|
97
132
|
|
|
98
133
|
private validateForm(): boolean {
|
|
@@ -119,22 +154,115 @@ export default class CommentPopup extends LightningElement {
|
|
|
119
154
|
return emailRegex.test(email);
|
|
120
155
|
}
|
|
121
156
|
|
|
122
|
-
private addComment() {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
157
|
+
private async addComment() {
|
|
158
|
+
// Validate required fields before creating payload
|
|
159
|
+
if (!this.currentBranch || !this.filePath || !this.headingTitle) {
|
|
160
|
+
throw new Error(
|
|
161
|
+
"Missing required fields: branch, file_path, or heading_title"
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const payload: ApiCommentPayload = {
|
|
166
|
+
branch: this.currentBranch,
|
|
167
|
+
file_path: this.filePath,
|
|
168
|
+
heading_title: this.headingTitle,
|
|
169
|
+
start_line: this.startLine!,
|
|
170
|
+
end_line: this.endLine!,
|
|
171
|
+
comment: {
|
|
172
|
+
comment_text: this.comment.trim(),
|
|
173
|
+
email: this.email.trim(),
|
|
174
|
+
timestamp: new Date().toISOString()
|
|
175
|
+
}
|
|
128
176
|
};
|
|
129
177
|
|
|
130
|
-
|
|
178
|
+
console.log(
|
|
179
|
+
"Posting comment with payload:",
|
|
180
|
+
JSON.stringify(payload, null, 2)
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
// LOCAL STORAGE IMPLEMENTATION (Temporary until backend is ready)
|
|
184
|
+
try {
|
|
185
|
+
await this.saveCommentToLocalStorage(payload);
|
|
186
|
+
console.log("Comment saved to local storage successfully");
|
|
187
|
+
|
|
188
|
+
// Refresh comments after successful save
|
|
189
|
+
await this.fetchComments();
|
|
190
|
+
|
|
191
|
+
// Dispatch custom event
|
|
192
|
+
this.dispatchEvent(
|
|
193
|
+
new CustomEvent("commentadded", {
|
|
194
|
+
detail: {
|
|
195
|
+
...payload.comment,
|
|
196
|
+
id: Date.now().toString() // Generate temporary ID
|
|
197
|
+
}
|
|
198
|
+
})
|
|
199
|
+
);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error("Error saving comment to local storage:", error);
|
|
202
|
+
throw error;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// API IMPLEMENTATION (Commented until backend is ready)
|
|
206
|
+
/*
|
|
207
|
+
const response = await fetch('/post-comment', {
|
|
208
|
+
method: 'POST',
|
|
209
|
+
headers: {
|
|
210
|
+
'Content-Type': 'application/json'
|
|
211
|
+
},
|
|
212
|
+
body: JSON.stringify(payload)
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
if (!response.ok) {
|
|
216
|
+
const errorText = await response.text();
|
|
217
|
+
throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const responseData = await response.json();
|
|
221
|
+
console.log('Comment posted successfully:', responseData);
|
|
222
|
+
|
|
223
|
+
// Refresh comments after successful post
|
|
224
|
+
await this.fetchComments();
|
|
131
225
|
|
|
132
226
|
// Dispatch custom event
|
|
133
227
|
this.dispatchEvent(
|
|
134
228
|
new CustomEvent("commentadded", {
|
|
135
|
-
detail:
|
|
229
|
+
detail: {
|
|
230
|
+
...payload.comment,
|
|
231
|
+
id: responseData.id // Include any ID returned by the API
|
|
232
|
+
}
|
|
136
233
|
})
|
|
137
234
|
);
|
|
235
|
+
*/
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private async saveCommentToLocalStorage(payload: ApiCommentPayload) {
|
|
239
|
+
try {
|
|
240
|
+
// Get existing comments from localStorage
|
|
241
|
+
const existingData = localStorage.getItem(COMMENTS_STORAGE_KEY);
|
|
242
|
+
const allComments = existingData ? JSON.parse(existingData) : {};
|
|
243
|
+
|
|
244
|
+
// Create a unique key for this specific comment location
|
|
245
|
+
const commentKey = `${payload.branch}_${payload.file_path}_${payload.heading_title}`;
|
|
246
|
+
|
|
247
|
+
// Initialize array for this location if it doesn't exist
|
|
248
|
+
if (!allComments[commentKey]) {
|
|
249
|
+
allComments[commentKey] = [];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Add the new comment
|
|
253
|
+
allComments[commentKey].push(payload.comment);
|
|
254
|
+
|
|
255
|
+
// Save back to localStorage
|
|
256
|
+
localStorage.setItem(
|
|
257
|
+
COMMENTS_STORAGE_KEY,
|
|
258
|
+
JSON.stringify(allComments)
|
|
259
|
+
);
|
|
260
|
+
|
|
261
|
+
console.log("Comments saved to localStorage:", allComments);
|
|
262
|
+
} catch (error) {
|
|
263
|
+
console.error("Error saving to localStorage:", error);
|
|
264
|
+
throw new Error("Failed to save comment to local storage");
|
|
265
|
+
}
|
|
138
266
|
}
|
|
139
267
|
|
|
140
268
|
private resetForm() {
|
|
@@ -162,6 +290,8 @@ export default class CommentPopup extends LightningElement {
|
|
|
162
290
|
handleClose() {
|
|
163
291
|
this._open = false;
|
|
164
292
|
this.resetForm();
|
|
293
|
+
this.apiError = "";
|
|
294
|
+
this.submitSuccess = false;
|
|
165
295
|
}
|
|
166
296
|
|
|
167
297
|
handleOverlayClick(event: Event) {
|
|
@@ -180,13 +310,83 @@ export default class CommentPopup extends LightningElement {
|
|
|
180
310
|
document.addEventListener("keydown", this.handleKeyDown.bind(this));
|
|
181
311
|
}
|
|
182
312
|
|
|
313
|
+
private async fetchComments() {
|
|
314
|
+
if (!this.currentBranch || !this.filePath || !this.headingTitle) {
|
|
315
|
+
console.warn("Cannot fetch comments: missing required parameters");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
this.isLoading = true;
|
|
320
|
+
this.apiError = "";
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
// LOCAL STORAGE IMPLEMENTATION (Temporary until backend is ready)
|
|
324
|
+
const comments = await this.getCommentsFromLocalStorage();
|
|
325
|
+
console.log("Fetched comments from localStorage:", comments);
|
|
326
|
+
this.comments = comments;
|
|
327
|
+
|
|
328
|
+
// API IMPLEMENTATION (Commented until backend is ready)
|
|
329
|
+
/*
|
|
330
|
+
const params = new URLSearchParams({
|
|
331
|
+
branch: this.currentBranch,
|
|
332
|
+
file_path: this.filePath,
|
|
333
|
+
heading_title: this.headingTitle
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
console.log('Fetching comments with params:', params.toString());
|
|
337
|
+
|
|
338
|
+
const response = await fetch(`/api/comments?${params.toString()}`);
|
|
339
|
+
|
|
340
|
+
if (!response.ok) {
|
|
341
|
+
throw new Error(`Failed to fetch comments: ${response.status} ${response.statusText}`);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const data = await response.json();
|
|
345
|
+
console.log('Fetched comments:', data);
|
|
346
|
+
|
|
347
|
+
// Handle different response formats
|
|
348
|
+
if (Array.isArray(data)) {
|
|
349
|
+
this.comments = data;
|
|
350
|
+
} else if (data && Array.isArray(data.comments)) {
|
|
351
|
+
this.comments = data.comments;
|
|
352
|
+
} else {
|
|
353
|
+
this.comments = [];
|
|
354
|
+
}
|
|
355
|
+
*/
|
|
356
|
+
} catch (error) {
|
|
357
|
+
console.error("Error fetching comments:", error);
|
|
358
|
+
this.apiError = "Failed to load comments. Please try again later.";
|
|
359
|
+
this.comments = [];
|
|
360
|
+
} finally {
|
|
361
|
+
this.isLoading = false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private async getCommentsFromLocalStorage(): Promise<Comment[]> {
|
|
366
|
+
try {
|
|
367
|
+
const existingData = localStorage.getItem(COMMENTS_STORAGE_KEY);
|
|
368
|
+
if (!existingData) {
|
|
369
|
+
return [];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const allComments = JSON.parse(existingData);
|
|
373
|
+
const commentKey = `${this.currentBranch}_${this.filePath}_${this.headingTitle}`;
|
|
374
|
+
|
|
375
|
+
return allComments[commentKey] || [];
|
|
376
|
+
} catch (error) {
|
|
377
|
+
console.error("Error reading from localStorage:", error);
|
|
378
|
+
return [];
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
183
382
|
disconnectedCallback() {
|
|
184
383
|
document.removeEventListener("keydown", this.handleKeyDown.bind(this));
|
|
185
384
|
}
|
|
186
385
|
|
|
187
|
-
private formatTimestamp(timestamp:
|
|
386
|
+
private formatTimestamp(timestamp: string): string {
|
|
188
387
|
const now = new Date();
|
|
189
|
-
const
|
|
388
|
+
const commentDate = new Date(timestamp);
|
|
389
|
+
const diff = now.getTime() - commentDate.getTime();
|
|
190
390
|
const minutes = Math.floor(diff / 60000);
|
|
191
391
|
const hours = Math.floor(diff / 3600000);
|
|
192
392
|
const days = Math.floor(diff / 86400000);
|
|
@@ -204,6 +404,7 @@ export default class CommentPopup extends LightningElement {
|
|
|
204
404
|
get formattedComments() {
|
|
205
405
|
return this.sortedComments.map((comment) => ({
|
|
206
406
|
...comment,
|
|
407
|
+
comment: comment.comment_text, // Map API field to template expectation
|
|
207
408
|
formattedTimestamp: this.formatTimestamp(comment.timestamp),
|
|
208
409
|
maskedEmail: this.maskEmail(comment.email)
|
|
209
410
|
}));
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
// Utility functions for managing comments in localStorage
|
|
2
|
+
// This file provides helper functions for comment management
|
|
3
|
+
|
|
4
|
+
export interface Comment {
|
|
5
|
+
email: string;
|
|
6
|
+
comment_text: string;
|
|
7
|
+
timestamp: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ApiCommentPayload {
|
|
11
|
+
branch: string;
|
|
12
|
+
file_path: string;
|
|
13
|
+
heading_title: string;
|
|
14
|
+
start_line: string;
|
|
15
|
+
end_line: string;
|
|
16
|
+
comment: {
|
|
17
|
+
comment_text: string;
|
|
18
|
+
email: string;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const COMMENTS_STORAGE_KEY = "dsc_comments";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Export all comments from localStorage to a JSON file
|
|
27
|
+
*/
|
|
28
|
+
export function exportCommentsToFile(): void {
|
|
29
|
+
try {
|
|
30
|
+
const commentsData = localStorage.getItem(COMMENTS_STORAGE_KEY);
|
|
31
|
+
if (!commentsData) {
|
|
32
|
+
console.log("No comments found in localStorage");
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const comments = JSON.parse(commentsData);
|
|
37
|
+
const dataStr = JSON.stringify(comments, null, 2);
|
|
38
|
+
const dataBlob = new Blob([dataStr], { type: "application/json" });
|
|
39
|
+
|
|
40
|
+
const link = document.createElement("a");
|
|
41
|
+
link.href = URL.createObjectURL(dataBlob);
|
|
42
|
+
link.download = `dsc-comments-${
|
|
43
|
+
new Date().toISOString().split("T")[0]
|
|
44
|
+
}.json`;
|
|
45
|
+
link.click();
|
|
46
|
+
|
|
47
|
+
console.log("Comments exported successfully");
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error("Error exporting comments:", error);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Import comments from a JSON file to localStorage
|
|
55
|
+
*/
|
|
56
|
+
export function importCommentsFromFile(file: File): Promise<void> {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const reader = new FileReader();
|
|
59
|
+
|
|
60
|
+
reader.onload = (event) => {
|
|
61
|
+
try {
|
|
62
|
+
const content = event.target?.result as string;
|
|
63
|
+
const comments = JSON.parse(content);
|
|
64
|
+
|
|
65
|
+
// Validate the structure
|
|
66
|
+
if (typeof comments === "object" && comments !== null) {
|
|
67
|
+
localStorage.setItem(
|
|
68
|
+
COMMENTS_STORAGE_KEY,
|
|
69
|
+
JSON.stringify(comments)
|
|
70
|
+
);
|
|
71
|
+
console.log("Comments imported successfully");
|
|
72
|
+
resolve();
|
|
73
|
+
} else {
|
|
74
|
+
reject(new Error("Invalid comments file format"));
|
|
75
|
+
}
|
|
76
|
+
} catch (error) {
|
|
77
|
+
reject(new Error("Failed to parse comments file"));
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
reader.onerror = () => {
|
|
82
|
+
reject(new Error("Failed to read file"));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
reader.readAsText(file);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get all comments from localStorage
|
|
91
|
+
*/
|
|
92
|
+
export function getAllComments(): Record<string, Comment[]> {
|
|
93
|
+
try {
|
|
94
|
+
const commentsData = localStorage.getItem(COMMENTS_STORAGE_KEY);
|
|
95
|
+
return commentsData ? JSON.parse(commentsData) : {};
|
|
96
|
+
} catch (error) {
|
|
97
|
+
console.error("Error reading comments from localStorage:", error);
|
|
98
|
+
return {};
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get comments for a specific location
|
|
104
|
+
*/
|
|
105
|
+
export function getCommentsForLocation(
|
|
106
|
+
branch: string,
|
|
107
|
+
filePath: string,
|
|
108
|
+
headingTitle: string
|
|
109
|
+
): Comment[] {
|
|
110
|
+
try {
|
|
111
|
+
const allComments = getAllComments();
|
|
112
|
+
const commentKey = `${branch}_${filePath}_${headingTitle}`;
|
|
113
|
+
return allComments[commentKey] || [];
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error("Error getting comments for location:", error);
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Clear all comments from localStorage
|
|
122
|
+
*/
|
|
123
|
+
export function clearAllComments(): void {
|
|
124
|
+
try {
|
|
125
|
+
localStorage.removeItem(COMMENTS_STORAGE_KEY);
|
|
126
|
+
console.log("All comments cleared from localStorage");
|
|
127
|
+
} catch (error) {
|
|
128
|
+
console.error("Error clearing comments:", error);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get statistics about stored comments
|
|
134
|
+
*/
|
|
135
|
+
export function getCommentsStats(): {
|
|
136
|
+
totalLocations: number;
|
|
137
|
+
totalComments: number;
|
|
138
|
+
locations: Array<{
|
|
139
|
+
key: string;
|
|
140
|
+
commentCount: number;
|
|
141
|
+
lastComment?: string;
|
|
142
|
+
}>;
|
|
143
|
+
} {
|
|
144
|
+
try {
|
|
145
|
+
const allComments = getAllComments();
|
|
146
|
+
const locations = Object.keys(allComments);
|
|
147
|
+
let totalComments = 0;
|
|
148
|
+
|
|
149
|
+
const locationStats = locations.map((key) => {
|
|
150
|
+
const comments = allComments[key];
|
|
151
|
+
totalComments += comments.length;
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
key,
|
|
155
|
+
commentCount: comments.length,
|
|
156
|
+
lastComment:
|
|
157
|
+
comments.length > 0
|
|
158
|
+
? comments[comments.length - 1].timestamp
|
|
159
|
+
: undefined
|
|
160
|
+
};
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
totalLocations: locations.length,
|
|
165
|
+
totalComments,
|
|
166
|
+
locations: locationStats
|
|
167
|
+
};
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error("Error getting comments stats:", error);
|
|
170
|
+
return {
|
|
171
|
+
totalLocations: 0,
|
|
172
|
+
totalComments: 0,
|
|
173
|
+
locations: []
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Convert localStorage comments to API format for backend migration
|
|
180
|
+
*/
|
|
181
|
+
export function convertToApiFormat(): Array<{
|
|
182
|
+
branch: string;
|
|
183
|
+
file_path: string;
|
|
184
|
+
heading_title: string;
|
|
185
|
+
start_line: string;
|
|
186
|
+
end_line: string;
|
|
187
|
+
comments: Comment[];
|
|
188
|
+
}> {
|
|
189
|
+
try {
|
|
190
|
+
const allComments = getAllComments();
|
|
191
|
+
const apiFormat: Array<{
|
|
192
|
+
branch: string;
|
|
193
|
+
file_path: string;
|
|
194
|
+
heading_title: string;
|
|
195
|
+
start_line: string;
|
|
196
|
+
end_line: string;
|
|
197
|
+
comments: Comment[];
|
|
198
|
+
}> = [];
|
|
199
|
+
|
|
200
|
+
Object.keys(allComments).forEach((key) => {
|
|
201
|
+
const [branch, file_path, heading_title, start_line, end_line] =
|
|
202
|
+
key.split("_", 5);
|
|
203
|
+
if (
|
|
204
|
+
branch &&
|
|
205
|
+
file_path &&
|
|
206
|
+
heading_title &&
|
|
207
|
+
start_line &&
|
|
208
|
+
end_line
|
|
209
|
+
) {
|
|
210
|
+
apiFormat.push({
|
|
211
|
+
branch,
|
|
212
|
+
file_path,
|
|
213
|
+
heading_title,
|
|
214
|
+
start_line,
|
|
215
|
+
end_line,
|
|
216
|
+
comments: allComments[key]
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
return apiFormat;
|
|
222
|
+
} catch (error) {
|
|
223
|
+
console.error("Error converting to API format:", error);
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Add a comment to localStorage
|
|
230
|
+
*/
|
|
231
|
+
export function addComment(payload: ApiCommentPayload): void {
|
|
232
|
+
try {
|
|
233
|
+
const existingData = localStorage.getItem(COMMENTS_STORAGE_KEY);
|
|
234
|
+
const allComments = existingData ? JSON.parse(existingData) : {};
|
|
235
|
+
|
|
236
|
+
const commentKey = `${payload.branch}_${payload.file_path}_${payload.heading_title}`;
|
|
237
|
+
|
|
238
|
+
if (!allComments[commentKey]) {
|
|
239
|
+
allComments[commentKey] = [];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
allComments[commentKey].push(payload.comment);
|
|
243
|
+
localStorage.setItem(COMMENTS_STORAGE_KEY, JSON.stringify(allComments));
|
|
244
|
+
|
|
245
|
+
console.log("Comment added successfully");
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error("Error adding comment:", error);
|
|
248
|
+
throw error;
|
|
249
|
+
}
|
|
250
|
+
}
|