create-bluecopa-react-app 1.0.19 → 1.0.21
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/bin/create-bluecopa-react-app.js +6 -7
- package/package.json +1 -1
- package/templates/latest/app/components/app-sidebar.tsx +5 -0
- package/templates/latest/app/routes/apitest.tsx +1083 -0
- package/templates/latest/app/routes/comments.tsx +335 -60
- package/templates/latest/app/routes.tsx +2 -0
- package/templates/latest/dist/assets/{__federation_expose_App-CcOhEUCE.js → __federation_expose_App-rkiN5ftu.js} +1 -1
- package/templates/latest/dist/assets/{client-uh-HfYnI.js → client-CjZD2orr.js} +5762 -4345
- package/templates/latest/dist/assets/{index-C1IOBHtM.js → index-BIZxzud9.js} +1 -1
- package/templates/latest/dist/assets/remoteEntry.css +130 -0
- package/templates/latest/dist/assets/remoteEntry.js +1 -1
- package/templates/latest/dist/index.html +2 -2
- package/templates/latest/package-lock.json +82 -8
- package/templates/latest/package.json +2 -1
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
import React, { useState } from
|
|
1
|
+
import React, { useState } from "react";
|
|
2
2
|
import { AppSidebar } from "~/components/app-sidebar";
|
|
3
3
|
import { SiteHeader } from "~/components/site-header";
|
|
4
4
|
import { SidebarInset, SidebarProvider } from "~/components/ui/sidebar";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
Card,
|
|
7
|
+
CardContent,
|
|
8
|
+
CardDescription,
|
|
9
|
+
CardHeader,
|
|
10
|
+
CardTitle,
|
|
11
|
+
} from "~/components/ui/card";
|
|
6
12
|
import { Button } from "~/components/ui/button";
|
|
7
13
|
import { Input } from "~/components/ui/input";
|
|
8
14
|
import { Label } from "~/components/ui/label";
|
|
9
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
Select,
|
|
17
|
+
SelectContent,
|
|
18
|
+
SelectItem,
|
|
19
|
+
SelectTrigger,
|
|
20
|
+
SelectValue,
|
|
21
|
+
} from "~/components/ui/select";
|
|
10
22
|
import { Badge } from "~/components/ui/badge";
|
|
11
23
|
import { Separator } from "~/components/ui/separator";
|
|
12
24
|
import { Avatar, AvatarFallback } from "~/components/ui/avatar";
|
|
@@ -21,17 +33,260 @@ import {
|
|
|
21
33
|
useSubscribeUser,
|
|
22
34
|
useUnsubscribeUser,
|
|
23
35
|
useCheckSubscriptionStatus,
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
36
|
+
useGetAuditLogs,
|
|
37
|
+
useCreateAuditLog,
|
|
38
|
+
} from "@bluecopa/react";
|
|
39
|
+
import {
|
|
40
|
+
MessageSquare,
|
|
41
|
+
Plus,
|
|
42
|
+
Send,
|
|
43
|
+
Edit,
|
|
44
|
+
Trash2,
|
|
45
|
+
Bell,
|
|
46
|
+
BellOff,
|
|
47
|
+
Users,
|
|
48
|
+
} from "lucide-react";
|
|
27
49
|
|
|
28
50
|
// Mock user data - in real app this would come from auth context
|
|
29
51
|
const MOCK_USER = {
|
|
30
|
-
id:
|
|
31
|
-
name:
|
|
32
|
-
email:
|
|
52
|
+
id: "user-123",
|
|
53
|
+
name: "John Doe",
|
|
54
|
+
email: "john@example.com",
|
|
33
55
|
};
|
|
34
56
|
|
|
57
|
+
// Default values for audit log examples
|
|
58
|
+
const DEFAULT_AUDIT_LOG_REQUEST = {
|
|
59
|
+
entityId: "0TOlmUqx0TKseTn8W6gW",
|
|
60
|
+
source: "UI",
|
|
61
|
+
limit: 20,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const DEFAULT_AUDIT_RECORD = [
|
|
65
|
+
{
|
|
66
|
+
"source": "UI",
|
|
67
|
+
"userId": "0TOlmUqx0TKseTn8W6gW",
|
|
68
|
+
"entityId": "0TOlmUqx0TKseTn8W6gW",
|
|
69
|
+
"entityType": "Dataset",
|
|
70
|
+
"action": "CREATE",
|
|
71
|
+
"eventType": "API"
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
|
|
75
|
+
// Get Audit Logs Test Component
|
|
76
|
+
function GetAuditLogsTestSection() {
|
|
77
|
+
const [auditLogRequest, setAuditLogRequest] = useState<string>("");
|
|
78
|
+
const [auditLogsResult, setAuditLogsResult] = useState<any>(null);
|
|
79
|
+
|
|
80
|
+
// Get Audit Logs Hook
|
|
81
|
+
const getAuditLogsMutation = useGetAuditLogs();
|
|
82
|
+
|
|
83
|
+
// Handle Load Example
|
|
84
|
+
const handleLoadExample = () => {
|
|
85
|
+
setAuditLogRequest(JSON.stringify(DEFAULT_AUDIT_LOG_REQUEST, null, 2));
|
|
86
|
+
toast.info(
|
|
87
|
+
'Example audit log request loaded. Click "Get Audit Logs" to fetch.'
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Handle Get Audit Logs
|
|
92
|
+
const handleGetAuditLogs = async () => {
|
|
93
|
+
if (!auditLogRequest) {
|
|
94
|
+
toast.error("Please enter audit log request (JSON)");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
let parsedRequest;
|
|
99
|
+
try {
|
|
100
|
+
parsedRequest = JSON.parse(auditLogRequest);
|
|
101
|
+
} catch (e) {
|
|
102
|
+
toast.error("Invalid JSON format");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const result = await getAuditLogsMutation.mutateAsync(parsedRequest);
|
|
107
|
+
setAuditLogsResult(result);
|
|
108
|
+
toast.success("Audit logs loaded! Check console for data.");
|
|
109
|
+
} catch (error: any) {
|
|
110
|
+
console.error("Get audit logs error:", error);
|
|
111
|
+
toast.error(`Get audit logs failed: ${error.message}`);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Card className="mb-6">
|
|
117
|
+
<CardHeader>
|
|
118
|
+
<CardTitle>Get Audit Logs Operations Test</CardTitle>
|
|
119
|
+
<CardDescription>Get audit logs with filter criteria</CardDescription>
|
|
120
|
+
</CardHeader>
|
|
121
|
+
<CardContent className="space-y-4">
|
|
122
|
+
<div className="space-y-2">
|
|
123
|
+
<Label htmlFor="auditLogRequest">Audit Log Request (JSON)</Label>
|
|
124
|
+
<div className="flex gap-2 mb-2">
|
|
125
|
+
<Button onClick={handleLoadExample} variant="outline" type="button">
|
|
126
|
+
Load Example
|
|
127
|
+
</Button>
|
|
128
|
+
</div>
|
|
129
|
+
<textarea
|
|
130
|
+
id="auditLogRequest"
|
|
131
|
+
value={auditLogRequest}
|
|
132
|
+
onChange={(e) => setAuditLogRequest(e.target.value)}
|
|
133
|
+
placeholder="Enter audit log request JSON"
|
|
134
|
+
className="w-full min-h-[150px] p-2 border rounded-md font-mono text-xs"
|
|
135
|
+
/>
|
|
136
|
+
<p className="text-xs text-muted-foreground">
|
|
137
|
+
Example: Click "Load Example" button above to load sample request
|
|
138
|
+
</p>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div className="flex gap-4">
|
|
142
|
+
<Button
|
|
143
|
+
onClick={handleGetAuditLogs}
|
|
144
|
+
disabled={getAuditLogsMutation.isPending || !auditLogRequest}
|
|
145
|
+
variant="default"
|
|
146
|
+
>
|
|
147
|
+
{getAuditLogsMutation.isPending ? "Loading..." : "Get Audit Logs"}
|
|
148
|
+
</Button>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
{/* Status Messages */}
|
|
152
|
+
{getAuditLogsMutation.isError && (
|
|
153
|
+
<p className="text-red-500">
|
|
154
|
+
Get Error: {getAuditLogsMutation.error?.message}
|
|
155
|
+
</p>
|
|
156
|
+
)}
|
|
157
|
+
{getAuditLogsMutation.isSuccess && auditLogsResult && (
|
|
158
|
+
<p className="text-green-500">
|
|
159
|
+
✓ Audit logs loaded successfully (Count:{" "}
|
|
160
|
+
{auditLogsResult.count || 0})
|
|
161
|
+
</p>
|
|
162
|
+
)}
|
|
163
|
+
|
|
164
|
+
{/* Audit Logs Result Preview */}
|
|
165
|
+
{auditLogsResult && (
|
|
166
|
+
<div className="mt-4 p-4 bg-muted rounded-lg">
|
|
167
|
+
<p className="text-sm font-semibold mb-2">Audit Logs Result:</p>
|
|
168
|
+
<pre className="text-xs overflow-auto max-h-60">
|
|
169
|
+
{JSON.stringify(auditLogsResult, null, 2)}
|
|
170
|
+
</pre>
|
|
171
|
+
</div>
|
|
172
|
+
)}
|
|
173
|
+
</CardContent>
|
|
174
|
+
</Card>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Create Audit Log Test Component
|
|
179
|
+
function CreateAuditLogTestSection() {
|
|
180
|
+
const [auditRecords, setAuditRecords] = useState<string>("");
|
|
181
|
+
const [createResult, setCreateResult] = useState<any>(null);
|
|
182
|
+
|
|
183
|
+
// Create Audit Log Hook
|
|
184
|
+
const createAuditLogMutation = useCreateAuditLog();
|
|
185
|
+
|
|
186
|
+
// Handle Load Example
|
|
187
|
+
const handleLoadExample = () => {
|
|
188
|
+
const exampleRecords = DEFAULT_AUDIT_RECORD;
|
|
189
|
+
setAuditRecords(JSON.stringify(exampleRecords, null, 2));
|
|
190
|
+
toast.info(
|
|
191
|
+
'Example audit record loaded. Click "Create Audit Log" to create.'
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Handle Create Audit Log
|
|
196
|
+
const handleCreateAuditLog = async () => {
|
|
197
|
+
if (!auditRecords) {
|
|
198
|
+
toast.error("Please enter audit records (JSON array)");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
try {
|
|
202
|
+
let parsedRecords;
|
|
203
|
+
try {
|
|
204
|
+
parsedRecords = JSON.parse(auditRecords);
|
|
205
|
+
} catch (e) {
|
|
206
|
+
toast.error("Invalid JSON format");
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
if (!Array.isArray(parsedRecords)) {
|
|
211
|
+
toast.error("Audit records must be an array");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const result = await createAuditLogMutation.mutateAsync({
|
|
216
|
+
auditRecords: parsedRecords,
|
|
217
|
+
});
|
|
218
|
+
console.log("Audit log created:", result);
|
|
219
|
+
setCreateResult(result);
|
|
220
|
+
toast.success("Audit log created successfully! Check console for data.");
|
|
221
|
+
} catch (error: any) {
|
|
222
|
+
console.error("Create audit log error:", error);
|
|
223
|
+
toast.error(`Create audit log failed: ${error.message}`);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<Card className="mb-6">
|
|
229
|
+
<CardHeader>
|
|
230
|
+
<CardTitle>Create Audit Log Operations Test</CardTitle>
|
|
231
|
+
<CardDescription>Create audit log records</CardDescription>
|
|
232
|
+
</CardHeader>
|
|
233
|
+
<CardContent className="space-y-4">
|
|
234
|
+
<div className="space-y-2">
|
|
235
|
+
<Label htmlFor="auditRecords">Audit Records (JSON Array)</Label>
|
|
236
|
+
<div className="flex gap-2 mb-2">
|
|
237
|
+
<Button onClick={handleLoadExample} variant="outline" type="button">
|
|
238
|
+
Load Example
|
|
239
|
+
</Button>
|
|
240
|
+
</div>
|
|
241
|
+
<textarea
|
|
242
|
+
id="auditRecords"
|
|
243
|
+
value={auditRecords}
|
|
244
|
+
onChange={(e) => setAuditRecords(e.target.value)}
|
|
245
|
+
placeholder="Enter audit records as JSON array"
|
|
246
|
+
className="w-full min-h-[200px] p-2 border rounded-md font-mono text-xs"
|
|
247
|
+
/>
|
|
248
|
+
<p className="text-xs text-muted-foreground">
|
|
249
|
+
Example: Click "Load Example" button above to load sample audit
|
|
250
|
+
record
|
|
251
|
+
</p>
|
|
252
|
+
</div>
|
|
253
|
+
|
|
254
|
+
<div className="flex gap-4">
|
|
255
|
+
<Button
|
|
256
|
+
onClick={handleCreateAuditLog}
|
|
257
|
+
disabled={createAuditLogMutation.isPending || !auditRecords}
|
|
258
|
+
variant="default"
|
|
259
|
+
>
|
|
260
|
+
{createAuditLogMutation.isPending
|
|
261
|
+
? "Creating..."
|
|
262
|
+
: "Create Audit Log"}
|
|
263
|
+
</Button>
|
|
264
|
+
</div>
|
|
265
|
+
|
|
266
|
+
{/* Status Messages */}
|
|
267
|
+
{createAuditLogMutation.isError && (
|
|
268
|
+
<p className="text-red-500">
|
|
269
|
+
Create Error: {createAuditLogMutation.error?.message}
|
|
270
|
+
</p>
|
|
271
|
+
)}
|
|
272
|
+
{createAuditLogMutation.isSuccess && createResult && (
|
|
273
|
+
<p className="text-green-500">✓ Audit log created successfully</p>
|
|
274
|
+
)}
|
|
275
|
+
|
|
276
|
+
{/* Create Result Preview */}
|
|
277
|
+
{createResult && (
|
|
278
|
+
<div className="mt-4 p-4 bg-muted rounded-lg">
|
|
279
|
+
<p className="text-sm font-semibold mb-2">Create Result:</p>
|
|
280
|
+
<pre className="text-xs overflow-auto max-h-60">
|
|
281
|
+
{JSON.stringify(createResult, null, 2)}
|
|
282
|
+
</pre>
|
|
283
|
+
</div>
|
|
284
|
+
)}
|
|
285
|
+
</CardContent>
|
|
286
|
+
</Card>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
35
290
|
// Component types for better organization
|
|
36
291
|
interface ThreadFormProps {
|
|
37
292
|
onThreadCreated: (threadId: string) => void;
|
|
@@ -55,15 +310,15 @@ interface SubscriptionControlsProps {
|
|
|
55
310
|
|
|
56
311
|
// Thread Creation Form Component
|
|
57
312
|
function ThreadForm({ onThreadCreated }: ThreadFormProps) {
|
|
58
|
-
const [componentId, setComponentId] = useState(
|
|
59
|
-
const [componentType, setComponentType] = useState<string>(
|
|
313
|
+
const [componentId, setComponentId] = useState("");
|
|
314
|
+
const [componentType, setComponentType] = useState<string>("");
|
|
60
315
|
|
|
61
316
|
const createThreadMutation = useCreateThread({
|
|
62
317
|
onSuccess: (data) => {
|
|
63
|
-
toast.success(
|
|
64
|
-
onThreadCreated(data.data.id ||
|
|
65
|
-
setComponentId(
|
|
66
|
-
setComponentType(
|
|
318
|
+
toast.success("Thread created successfully!");
|
|
319
|
+
onThreadCreated(data.data.id || "");
|
|
320
|
+
setComponentId("");
|
|
321
|
+
setComponentType("");
|
|
67
322
|
},
|
|
68
323
|
onError: (error) => {
|
|
69
324
|
toast.error(`Failed to create thread: ${error.message}`);
|
|
@@ -73,7 +328,7 @@ function ThreadForm({ onThreadCreated }: ThreadFormProps) {
|
|
|
73
328
|
const handleSubmit = (e: React.FormEvent) => {
|
|
74
329
|
e.preventDefault();
|
|
75
330
|
if (!componentId || !componentType) {
|
|
76
|
-
toast.error(
|
|
331
|
+
toast.error("Please fill in all fields");
|
|
77
332
|
return;
|
|
78
333
|
}
|
|
79
334
|
|
|
@@ -123,12 +378,12 @@ function ThreadForm({ onThreadCreated }: ThreadFormProps) {
|
|
|
123
378
|
</SelectContent>
|
|
124
379
|
</Select>
|
|
125
380
|
</div>
|
|
126
|
-
<Button
|
|
127
|
-
type="submit"
|
|
381
|
+
<Button
|
|
382
|
+
type="submit"
|
|
128
383
|
disabled={createThreadMutation.isPending}
|
|
129
384
|
className="w-full"
|
|
130
385
|
>
|
|
131
|
-
{createThreadMutation.isPending ?
|
|
386
|
+
{createThreadMutation.isPending ? "Creating..." : "Create Thread"}
|
|
132
387
|
</Button>
|
|
133
388
|
</form>
|
|
134
389
|
</CardContent>
|
|
@@ -138,12 +393,12 @@ function ThreadForm({ onThreadCreated }: ThreadFormProps) {
|
|
|
138
393
|
|
|
139
394
|
// Comment Form Component
|
|
140
395
|
function CommentForm({ threadId, onCommentPosted }: CommentFormProps) {
|
|
141
|
-
const [comment, setComment] = useState(
|
|
396
|
+
const [comment, setComment] = useState("");
|
|
142
397
|
|
|
143
398
|
const postCommentMutation = usePostComment({
|
|
144
399
|
onSuccess: () => {
|
|
145
|
-
toast.success(
|
|
146
|
-
setComment(
|
|
400
|
+
toast.success("Comment posted successfully!");
|
|
401
|
+
setComment("");
|
|
147
402
|
onCommentPosted();
|
|
148
403
|
},
|
|
149
404
|
onError: (error) => {
|
|
@@ -154,7 +409,7 @@ function CommentForm({ threadId, onCommentPosted }: CommentFormProps) {
|
|
|
154
409
|
const handleSubmit = (e: React.FormEvent) => {
|
|
155
410
|
e.preventDefault();
|
|
156
411
|
if (!comment.trim()) {
|
|
157
|
-
toast.error(
|
|
412
|
+
toast.error("Please enter a comment");
|
|
158
413
|
return;
|
|
159
414
|
}
|
|
160
415
|
|
|
@@ -162,7 +417,7 @@ function CommentForm({ threadId, onCommentPosted }: CommentFormProps) {
|
|
|
162
417
|
data: {
|
|
163
418
|
chatThreadId: threadId,
|
|
164
419
|
comment: comment.trim(),
|
|
165
|
-
parentId: threadId
|
|
420
|
+
parentId: threadId,
|
|
166
421
|
},
|
|
167
422
|
});
|
|
168
423
|
};
|
|
@@ -187,12 +442,12 @@ function CommentForm({ threadId, onCommentPosted }: CommentFormProps) {
|
|
|
187
442
|
className="min-h-[80px]"
|
|
188
443
|
/>
|
|
189
444
|
</div>
|
|
190
|
-
<Button
|
|
191
|
-
type="submit"
|
|
445
|
+
<Button
|
|
446
|
+
type="submit"
|
|
192
447
|
disabled={postCommentMutation.isPending}
|
|
193
448
|
className="w-full"
|
|
194
449
|
>
|
|
195
|
-
{postCommentMutation.isPending ?
|
|
450
|
+
{postCommentMutation.isPending ? "Posting..." : "Post Comment"}
|
|
196
451
|
</Button>
|
|
197
452
|
</form>
|
|
198
453
|
</CardContent>
|
|
@@ -201,13 +456,17 @@ function CommentForm({ threadId, onCommentPosted }: CommentFormProps) {
|
|
|
201
456
|
}
|
|
202
457
|
|
|
203
458
|
// Individual Comment Component
|
|
204
|
-
function CommentItem({
|
|
459
|
+
function CommentItem({
|
|
460
|
+
comment,
|
|
461
|
+
onCommentUpdated,
|
|
462
|
+
onCommentDeleted,
|
|
463
|
+
}: CommentItemProps) {
|
|
205
464
|
const [isEditing, setIsEditing] = useState(false);
|
|
206
|
-
const [editText, setEditText] = useState(comment.comment ||
|
|
465
|
+
const [editText, setEditText] = useState(comment.comment || "");
|
|
207
466
|
|
|
208
467
|
const updateCommentMutation = useUpdateComment({
|
|
209
468
|
onSuccess: () => {
|
|
210
|
-
toast.success(
|
|
469
|
+
toast.success("Comment updated successfully!");
|
|
211
470
|
setIsEditing(false);
|
|
212
471
|
onCommentUpdated();
|
|
213
472
|
},
|
|
@@ -218,7 +477,7 @@ function CommentItem({ comment, onCommentUpdated, onCommentDeleted }: CommentIte
|
|
|
218
477
|
|
|
219
478
|
const deleteCommentMutation = useDeleteComment({
|
|
220
479
|
onSuccess: () => {
|
|
221
|
-
toast.success(
|
|
480
|
+
toast.success("Comment deleted successfully!");
|
|
222
481
|
onCommentDeleted();
|
|
223
482
|
},
|
|
224
483
|
onError: (error) => {
|
|
@@ -228,7 +487,7 @@ function CommentItem({ comment, onCommentUpdated, onCommentDeleted }: CommentIte
|
|
|
228
487
|
|
|
229
488
|
const handleUpdate = () => {
|
|
230
489
|
if (!editText.trim()) {
|
|
231
|
-
toast.error(
|
|
490
|
+
toast.error("Comment cannot be empty");
|
|
232
491
|
return;
|
|
233
492
|
}
|
|
234
493
|
|
|
@@ -242,7 +501,7 @@ function CommentItem({ comment, onCommentUpdated, onCommentDeleted }: CommentIte
|
|
|
242
501
|
};
|
|
243
502
|
|
|
244
503
|
const handleDelete = () => {
|
|
245
|
-
if (window.confirm(
|
|
504
|
+
if (window.confirm("Are you sure you want to delete this comment?")) {
|
|
246
505
|
deleteCommentMutation.mutate(comment.id);
|
|
247
506
|
}
|
|
248
507
|
};
|
|
@@ -253,13 +512,17 @@ function CommentItem({ comment, onCommentUpdated, onCommentDeleted }: CommentIte
|
|
|
253
512
|
<div className="flex items-center gap-3">
|
|
254
513
|
<Avatar className="h-8 w-8">
|
|
255
514
|
<AvatarFallback>
|
|
256
|
-
{comment.createdBy?.charAt(0)?.toUpperCase() ||
|
|
515
|
+
{comment.createdBy?.charAt(0)?.toUpperCase() || "U"}
|
|
257
516
|
</AvatarFallback>
|
|
258
517
|
</Avatar>
|
|
259
518
|
<div>
|
|
260
|
-
<p className="font-medium text-sm">
|
|
519
|
+
<p className="font-medium text-sm">
|
|
520
|
+
{comment.createdBy || "Unknown User"}
|
|
521
|
+
</p>
|
|
261
522
|
<p className="text-xs text-muted-foreground">
|
|
262
|
-
{comment.createdDate
|
|
523
|
+
{comment.createdDate
|
|
524
|
+
? new Date(comment.createdDate).toLocaleString()
|
|
525
|
+
: "Just now"}
|
|
263
526
|
</p>
|
|
264
527
|
</div>
|
|
265
528
|
</div>
|
|
@@ -296,14 +559,14 @@ function CommentItem({ comment, onCommentUpdated, onCommentDeleted }: CommentIte
|
|
|
296
559
|
onClick={handleUpdate}
|
|
297
560
|
disabled={updateCommentMutation.isPending}
|
|
298
561
|
>
|
|
299
|
-
{updateCommentMutation.isPending ?
|
|
562
|
+
{updateCommentMutation.isPending ? "Saving..." : "Save"}
|
|
300
563
|
</Button>
|
|
301
564
|
<Button
|
|
302
565
|
size="sm"
|
|
303
566
|
variant="outline"
|
|
304
567
|
onClick={() => {
|
|
305
568
|
setIsEditing(false);
|
|
306
|
-
setEditText(comment.comment ||
|
|
569
|
+
setEditText(comment.comment || "");
|
|
307
570
|
}}
|
|
308
571
|
>
|
|
309
572
|
Cancel
|
|
@@ -319,11 +582,14 @@ function CommentItem({ comment, onCommentUpdated, onCommentDeleted }: CommentIte
|
|
|
319
582
|
|
|
320
583
|
// Subscription Controls Component
|
|
321
584
|
function SubscriptionControls({ userId, threadId }: SubscriptionControlsProps) {
|
|
322
|
-
const { data: subscriptionStatus, isLoading } = useCheckSubscriptionStatus(
|
|
585
|
+
const { data: subscriptionStatus, isLoading } = useCheckSubscriptionStatus(
|
|
586
|
+
userId,
|
|
587
|
+
threadId
|
|
588
|
+
);
|
|
323
589
|
|
|
324
590
|
const subscribeUserMutation = useSubscribeUser({
|
|
325
591
|
onSuccess: () => {
|
|
326
|
-
toast.success(
|
|
592
|
+
toast.success("Successfully subscribed to thread!");
|
|
327
593
|
},
|
|
328
594
|
onError: (error) => {
|
|
329
595
|
toast.error(`Failed to subscribe: ${error.message}`);
|
|
@@ -332,7 +598,7 @@ function SubscriptionControls({ userId, threadId }: SubscriptionControlsProps) {
|
|
|
332
598
|
|
|
333
599
|
const unsubscribeUserMutation = useUnsubscribeUser({
|
|
334
600
|
onSuccess: () => {
|
|
335
|
-
toast.success(
|
|
601
|
+
toast.success("Successfully unsubscribed from thread!");
|
|
336
602
|
},
|
|
337
603
|
onError: (error) => {
|
|
338
604
|
toast.error(`Failed to unsubscribe: ${error.message}`);
|
|
@@ -363,7 +629,9 @@ function SubscriptionControls({ userId, threadId }: SubscriptionControlsProps) {
|
|
|
363
629
|
variant={isSubscribed ? "outline" : "default"}
|
|
364
630
|
size="sm"
|
|
365
631
|
onClick={isSubscribed ? handleUnsubscribe : handleSubscribe}
|
|
366
|
-
disabled={
|
|
632
|
+
disabled={
|
|
633
|
+
subscribeUserMutation.isPending || unsubscribeUserMutation.isPending
|
|
634
|
+
}
|
|
367
635
|
>
|
|
368
636
|
{isSubscribed ? (
|
|
369
637
|
<>
|
|
@@ -386,12 +654,12 @@ function SubscriptionControls({ userId, threadId }: SubscriptionControlsProps) {
|
|
|
386
654
|
|
|
387
655
|
// Main Comments Page Component
|
|
388
656
|
export default function CommentsPage() {
|
|
389
|
-
const [selectedThreadId, setSelectedThreadId] = useState<string>(
|
|
657
|
+
const [selectedThreadId, setSelectedThreadId] = useState<string>("");
|
|
390
658
|
|
|
391
|
-
const {
|
|
392
|
-
data: commentsData,
|
|
393
|
-
isLoading: commentsLoading,
|
|
394
|
-
refetch: refetchComments
|
|
659
|
+
const {
|
|
660
|
+
data: commentsData,
|
|
661
|
+
isLoading: commentsLoading,
|
|
662
|
+
refetch: refetchComments,
|
|
395
663
|
} = useGetCommentsByThreadId(selectedThreadId, {
|
|
396
664
|
enabled: !!selectedThreadId,
|
|
397
665
|
});
|
|
@@ -418,6 +686,10 @@ export default function CommentsPage() {
|
|
|
418
686
|
<SiteHeader />
|
|
419
687
|
<div className="flex flex-1 flex-col">
|
|
420
688
|
<div className="@container/main flex flex-1 flex-col gap-6 p-6">
|
|
689
|
+
{/* Audit Log Test Sections */}
|
|
690
|
+
<GetAuditLogsTestSection />
|
|
691
|
+
<CreateAuditLogTestSection />
|
|
692
|
+
|
|
421
693
|
{/* Page Header */}
|
|
422
694
|
<div className="flex items-center justify-between">
|
|
423
695
|
<div>
|
|
@@ -440,14 +712,14 @@ export default function CommentsPage() {
|
|
|
440
712
|
{/* Left Column - Thread Creation */}
|
|
441
713
|
<div className="space-y-6">
|
|
442
714
|
<ThreadForm onThreadCreated={handleThreadCreated} />
|
|
443
|
-
|
|
715
|
+
|
|
444
716
|
{selectedThreadId && (
|
|
445
717
|
<>
|
|
446
|
-
<CommentForm
|
|
447
|
-
threadId={selectedThreadId}
|
|
448
|
-
onCommentPosted={handleCommentAction}
|
|
718
|
+
<CommentForm
|
|
719
|
+
threadId={selectedThreadId}
|
|
720
|
+
onCommentPosted={handleCommentAction}
|
|
449
721
|
/>
|
|
450
|
-
|
|
722
|
+
|
|
451
723
|
<Card>
|
|
452
724
|
<CardHeader>
|
|
453
725
|
<CardTitle className="flex items-center gap-2">
|
|
@@ -459,9 +731,9 @@ export default function CommentsPage() {
|
|
|
459
731
|
</CardDescription>
|
|
460
732
|
</CardHeader>
|
|
461
733
|
<CardContent>
|
|
462
|
-
<SubscriptionControls
|
|
463
|
-
userId={MOCK_USER.id}
|
|
464
|
-
threadId={selectedThreadId}
|
|
734
|
+
<SubscriptionControls
|
|
735
|
+
userId={MOCK_USER.id}
|
|
736
|
+
threadId={selectedThreadId}
|
|
465
737
|
/>
|
|
466
738
|
</CardContent>
|
|
467
739
|
</Card>
|
|
@@ -475,10 +747,9 @@ export default function CommentsPage() {
|
|
|
475
747
|
<CardHeader>
|
|
476
748
|
<CardTitle>Comments</CardTitle>
|
|
477
749
|
<CardDescription>
|
|
478
|
-
{selectedThreadId
|
|
750
|
+
{selectedThreadId
|
|
479
751
|
? `Comments for thread ${selectedThreadId.slice(0, 8)}...`
|
|
480
|
-
:
|
|
481
|
-
}
|
|
752
|
+
: "Create a thread to see comments"}
|
|
482
753
|
</CardDescription>
|
|
483
754
|
</CardHeader>
|
|
484
755
|
<CardContent>
|
|
@@ -524,13 +795,17 @@ export default function CommentsPage() {
|
|
|
524
795
|
<CardContent className="space-y-2">
|
|
525
796
|
<div className="flex justify-between items-center">
|
|
526
797
|
<span className="text-sm">Thread Selected:</span>
|
|
527
|
-
<Badge
|
|
798
|
+
<Badge
|
|
799
|
+
variant={selectedThreadId ? "default" : "secondary"}
|
|
800
|
+
>
|
|
528
801
|
{selectedThreadId ? "Yes" : "No"}
|
|
529
802
|
</Badge>
|
|
530
803
|
</div>
|
|
531
804
|
<div className="flex justify-between items-center">
|
|
532
805
|
<span className="text-sm">Comments Loading:</span>
|
|
533
|
-
<Badge
|
|
806
|
+
<Badge
|
|
807
|
+
variant={commentsLoading ? "default" : "secondary"}
|
|
808
|
+
>
|
|
534
809
|
{commentsLoading ? "Yes" : "No"}
|
|
535
810
|
</Badge>
|
|
536
811
|
</div>
|
|
@@ -5,6 +5,7 @@ import Comments from "./routes/comments";
|
|
|
5
5
|
import Websocket from "./routes/websocket";
|
|
6
6
|
import Payments from "./routes/payments";
|
|
7
7
|
import Statements from "./routes/statements";
|
|
8
|
+
import ApiTest from "./routes/apitest";
|
|
8
9
|
|
|
9
10
|
export default function RouteConfig() {
|
|
10
11
|
return (
|
|
@@ -14,6 +15,7 @@ export default function RouteConfig() {
|
|
|
14
15
|
<Route path="/websocket" element={<Websocket />} />
|
|
15
16
|
<Route path="/payments" element={<Payments />} />
|
|
16
17
|
<Route path="/statements" element={<Statements />} />
|
|
18
|
+
<Route path="/apitest" element={<ApiTest />} />
|
|
17
19
|
<Route path="*" element={<Navigate to="/" replace />} />
|
|
18
20
|
</Routes>
|
|
19
21
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
System.register(['./__federation_fn_import-CzfA7kmP.js', './client-
|
|
1
|
+
System.register(['./__federation_fn_import-CzfA7kmP.js', './client-CjZD2orr.js'], (function (exports, module) {
|
|
2
2
|
'use strict';
|
|
3
3
|
var importShared, clientExports, jsxRuntimeExports, MemoryRouter, BrowserRouter, App;
|
|
4
4
|
return {
|