@wopr-network/platform-ui-core 1.6.0 → 1.7.0

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wopr-network/platform-ui-core",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Brand-agnostic AI agent platform UI — deploy as any brand via env vars",
5
5
  "repository": {
6
6
  "type": "git",
@@ -4,6 +4,7 @@ import { ArrowDownToLine, Loader2, MoreHorizontal, Pencil } from "lucide-react";
4
4
  import Link from "next/link";
5
5
  import { useMemo, useState } from "react";
6
6
  import { toast } from "sonner";
7
+ import { UpdateAvailableBadge } from "@/components/instances/update-available-badge";
7
8
  import { StatusBadge } from "@/components/status-badge";
8
9
  import {
9
10
  AlertDialog,
@@ -285,7 +286,14 @@ export function InstanceListClient() {
285
286
  </Link>
286
287
  </TableCell>
287
288
  <TableCell>
288
- <StatusBadge status={inst.status} />
289
+ <div className="flex items-center gap-2">
290
+ <StatusBadge status={inst.status} />
291
+ <InstanceUpdateBadge
292
+ instanceId={inst.id}
293
+ instanceName={inst.name}
294
+ onUpdated={refetch}
295
+ />
296
+ </div>
289
297
  </TableCell>
290
298
  <TableCell className="text-muted-foreground">{inst.provider}</TableCell>
291
299
  <TableCell className="text-muted-foreground">
@@ -538,3 +546,32 @@ export function InstanceRowActions({
538
546
  </>
539
547
  );
540
548
  }
549
+
550
+ /* --- Instance Update Badge — inline "Update" pill next to status --- */
551
+
552
+ function InstanceUpdateBadge({
553
+ instanceId,
554
+ instanceName,
555
+ onUpdated,
556
+ }: {
557
+ instanceId: string;
558
+ instanceName: string;
559
+ onUpdated?: () => void;
560
+ }) {
561
+ const { updateAvailable } = useImageStatus(instanceId);
562
+ const { data: changelog } = trpc.fleet.getChangelog.useQuery(
563
+ { instanceId },
564
+ { enabled: updateAvailable },
565
+ );
566
+
567
+ if (!updateAvailable) return null;
568
+
569
+ return (
570
+ <UpdateAvailableBadge
571
+ instanceId={instanceId}
572
+ instanceName={instanceName}
573
+ changelog={changelog ?? null}
574
+ onUpdated={onUpdated}
575
+ />
576
+ );
577
+ }
@@ -0,0 +1,117 @@
1
+ "use client";
2
+
3
+ import { ArrowUpCircle, Loader2 } from "lucide-react";
4
+ import { useState } from "react";
5
+ import { toast } from "sonner";
6
+ import {
7
+ AlertDialog,
8
+ AlertDialogAction,
9
+ AlertDialogCancel,
10
+ AlertDialogContent,
11
+ AlertDialogDescription,
12
+ AlertDialogFooter,
13
+ AlertDialogHeader,
14
+ AlertDialogTitle,
15
+ AlertDialogTrigger,
16
+ } from "@/components/ui/alert-dialog";
17
+ import { Badge } from "@/components/ui/badge";
18
+ import { pullImageUpdate } from "@/lib/api";
19
+ import { toUserMessage } from "@/lib/errors";
20
+
21
+ interface ChangelogSection {
22
+ title: string;
23
+ items: string[];
24
+ }
25
+
26
+ interface Changelog {
27
+ version: string;
28
+ date: string;
29
+ sections: ChangelogSection[];
30
+ }
31
+
32
+ interface UpdateAvailableBadgeProps {
33
+ instanceId: string;
34
+ instanceName: string;
35
+ changelog?: Changelog | null;
36
+ onUpdated?: () => void;
37
+ }
38
+
39
+ export function UpdateAvailableBadge({
40
+ instanceId,
41
+ instanceName,
42
+ changelog,
43
+ onUpdated,
44
+ }: UpdateAvailableBadgeProps) {
45
+ const [pulling, setPulling] = useState(false);
46
+
47
+ async function handleUpdate() {
48
+ setPulling(true);
49
+ try {
50
+ await pullImageUpdate(instanceId);
51
+ toast.success(`${instanceName} is updating...`);
52
+ onUpdated?.();
53
+ } catch (err) {
54
+ toast.error(toUserMessage(err));
55
+ } finally {
56
+ setPulling(false);
57
+ }
58
+ }
59
+
60
+ return (
61
+ <AlertDialog>
62
+ <AlertDialogTrigger asChild>
63
+ <Badge
64
+ variant="outline"
65
+ className="cursor-pointer gap-1 border-amber-500/30 bg-amber-500/10 text-amber-500 hover:bg-amber-500/20 transition-colors"
66
+ >
67
+ <ArrowUpCircle className="h-3 w-3" />
68
+ Update
69
+ </Badge>
70
+ </AlertDialogTrigger>
71
+ <AlertDialogContent>
72
+ <AlertDialogHeader>
73
+ <AlertDialogTitle>Update Available for {instanceName}</AlertDialogTitle>
74
+ <AlertDialogDescription>
75
+ A new version is ready. This will pull the latest image and restart the instance.
76
+ </AlertDialogDescription>
77
+ </AlertDialogHeader>
78
+
79
+ {changelog && changelog.sections.length > 0 && (
80
+ <div className="max-h-64 overflow-y-auto rounded-md border border-border bg-muted/30 p-4 text-sm">
81
+ <p className="mb-2 font-medium text-foreground">What&apos;s new ({changelog.date})</p>
82
+ {changelog.sections.map((section) => (
83
+ <div key={section.title} className="mb-3 last:mb-0">
84
+ <p className="mb-1 text-xs font-semibold uppercase tracking-wider text-muted-foreground">
85
+ {section.title}
86
+ </p>
87
+ <ul className="list-inside list-disc space-y-0.5 text-muted-foreground">
88
+ {section.items.map((item) => (
89
+ <li key={item}>{item}</li>
90
+ ))}
91
+ </ul>
92
+ </div>
93
+ ))}
94
+ </div>
95
+ )}
96
+
97
+ <AlertDialogFooter>
98
+ <AlertDialogCancel>Later</AlertDialogCancel>
99
+ <AlertDialogAction
100
+ onClick={handleUpdate}
101
+ disabled={pulling}
102
+ className="bg-amber-600 hover:bg-amber-700"
103
+ >
104
+ {pulling ? (
105
+ <>
106
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
107
+ Updating...
108
+ </>
109
+ ) : (
110
+ "Update Now"
111
+ )}
112
+ </AlertDialogAction>
113
+ </AlertDialogFooter>
114
+ </AlertDialogContent>
115
+ </AlertDialog>
116
+ );
117
+ }
@@ -133,6 +133,7 @@ type AppRouterRecord = {
133
133
  getInstanceLogs: AnyTRPCQueryProcedure;
134
134
  getInstanceMetrics: AnyTRPCQueryProcedure;
135
135
  listTemplates: AnyTRPCQueryProcedure;
136
+ getChangelog: AnyTRPCQueryProcedure;
136
137
  };
137
138
  settings: {
138
139
  notificationPreferences: AnyTRPCQueryProcedure;