create-supyagent-app 0.1.35 → 0.1.37

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.
@@ -0,0 +1,218 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect, useRef } from "react";
4
+ import {
5
+ User,
6
+ ExternalLink,
7
+ Mail,
8
+ Calendar,
9
+ HardDrive,
10
+ MessageSquare,
11
+ Github,
12
+ type LucideIcon,
13
+ FileText,
14
+ CreditCard,
15
+ SquareKanban,
16
+ Cloud,
17
+ CalendarClock,
18
+ Phone,
19
+ MessageCircle,
20
+ UserCircle,
21
+ Monitor,
22
+ Send,
23
+ CircleDot,
24
+ Briefcase,
25
+ Users,
26
+ Bell,
27
+ } from "lucide-react";
28
+
29
+ interface UserInfo {
30
+ email: string | null;
31
+ tier: string;
32
+ usage: { current: number; limit: number };
33
+ integrations: Array<{ provider: string; status: string }>;
34
+ dashboardUrl: string;
35
+ }
36
+
37
+ const PROVIDER_ICONS: Record<string, LucideIcon> = {
38
+ google: Mail,
39
+ slack: MessageSquare,
40
+ github: Github,
41
+ discord: MessageSquare,
42
+ microsoft: Monitor,
43
+ twitter: MessageCircle,
44
+ linkedin: UserCircle,
45
+ notion: FileText,
46
+ telegram: Send,
47
+ hubspot: Users,
48
+ whatsapp: MessageCircle,
49
+ stripe: CreditCard,
50
+ jira: SquareKanban,
51
+ salesforce: Cloud,
52
+ calendly: CalendarClock,
53
+ twilio: Phone,
54
+ linear: CircleDot,
55
+ pipedrive: Briefcase,
56
+ resend: Send,
57
+ inbox: Bell,
58
+ };
59
+
60
+ const PROVIDER_LABELS: Record<string, string> = {
61
+ google: "Google",
62
+ slack: "Slack",
63
+ github: "GitHub",
64
+ discord: "Discord",
65
+ microsoft: "Microsoft",
66
+ twitter: "Twitter / X",
67
+ linkedin: "LinkedIn",
68
+ notion: "Notion",
69
+ telegram: "Telegram",
70
+ hubspot: "HubSpot",
71
+ whatsapp: "WhatsApp",
72
+ stripe: "Stripe",
73
+ jira: "Jira",
74
+ salesforce: "Salesforce",
75
+ calendly: "Calendly",
76
+ twilio: "Twilio",
77
+ linear: "Linear",
78
+ pipedrive: "Pipedrive",
79
+ resend: "Resend",
80
+ inbox: "Inbox",
81
+ };
82
+
83
+ export function UserButton() {
84
+ const [user, setUser] = useState<UserInfo | null>(null);
85
+ const [open, setOpen] = useState(false);
86
+ const ref = useRef<HTMLDivElement>(null);
87
+
88
+ useEffect(() => {
89
+ fetch("/api/me")
90
+ .then((res) => (res.ok ? res.json() : null))
91
+ .then((data) => {
92
+ if (data) setUser(data);
93
+ })
94
+ .catch(() => {});
95
+ }, []);
96
+
97
+ // Click outside to close
98
+ useEffect(() => {
99
+ if (!open) return;
100
+ const handler = (e: MouseEvent) => {
101
+ if (ref.current && !ref.current.contains(e.target as Node)) {
102
+ setOpen(false);
103
+ }
104
+ };
105
+ document.addEventListener("mousedown", handler);
106
+ return () => document.removeEventListener("mousedown", handler);
107
+ }, [open]);
108
+
109
+ if (!user) {
110
+ return (
111
+ <div className="h-7 w-7 animate-pulse rounded-full bg-muted" />
112
+ );
113
+ }
114
+
115
+ const initial = (user.email?.[0] || "?").toUpperCase();
116
+ const usagePercent =
117
+ user.usage.limit > 0
118
+ ? Math.min(100, Math.round((user.usage.current / user.usage.limit) * 100))
119
+ : 0;
120
+ const isUnlimited = user.usage.limit === -1;
121
+
122
+ return (
123
+ <div ref={ref} className="relative">
124
+ <button
125
+ type="button"
126
+ onClick={() => setOpen(!open)}
127
+ className="flex items-center gap-2 rounded-lg px-2 py-1 text-sm text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
128
+ >
129
+ <div className="flex h-6 w-6 items-center justify-center rounded-full bg-muted text-xs font-medium text-foreground">
130
+ {initial}
131
+ </div>
132
+ <span className="hidden sm:inline text-xs">{user.email}</span>
133
+ </button>
134
+
135
+ {open && (
136
+ <div className="absolute right-0 top-full mt-1 z-50 w-72 rounded-xl border border-border bg-card shadow-lg">
137
+ {/* Profile */}
138
+ <div className="border-b border-border px-4 py-3">
139
+ <div className="flex items-center gap-3">
140
+ <div className="flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-muted text-sm font-medium text-foreground">
141
+ {initial}
142
+ </div>
143
+ <div className="min-w-0">
144
+ <p className="truncate text-sm font-medium text-foreground">
145
+ {user.email}
146
+ </p>
147
+ <span className="inline-block mt-0.5 rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
148
+ {user.tier}
149
+ </span>
150
+ </div>
151
+ </div>
152
+ </div>
153
+
154
+ {/* Usage */}
155
+ <div className="border-b border-border px-4 py-3">
156
+ <div className="flex items-center justify-between text-xs text-muted-foreground">
157
+ <span>API usage this month</span>
158
+ <span>
159
+ {user.usage.current.toLocaleString()}
160
+ {isUnlimited ? "" : ` / ${user.usage.limit.toLocaleString()}`}
161
+ </span>
162
+ </div>
163
+ {!isUnlimited && (
164
+ <div className="mt-1.5 h-1 w-full rounded-full bg-muted">
165
+ <div
166
+ className="h-1 rounded-full bg-foreground/40 transition-all"
167
+ style={{ width: `${usagePercent}%` }}
168
+ />
169
+ </div>
170
+ )}
171
+ </div>
172
+
173
+ {/* Integrations */}
174
+ {user.integrations.length > 0 && (
175
+ <div className="border-b border-border px-4 py-3">
176
+ <p className="mb-2 text-xs font-medium text-muted-foreground">
177
+ Connected integrations
178
+ </p>
179
+ <div className="flex flex-wrap gap-1.5">
180
+ {user.integrations.map((integration) => {
181
+ const Icon = PROVIDER_ICONS[integration.provider] || User;
182
+ const label =
183
+ PROVIDER_LABELS[integration.provider] ||
184
+ integration.provider;
185
+ return (
186
+ <div
187
+ key={integration.provider}
188
+ className="flex items-center gap-1.5 rounded-md bg-muted px-2 py-1"
189
+ title={label}
190
+ >
191
+ <Icon className="h-3 w-3 text-muted-foreground" />
192
+ <span className="text-[11px] text-muted-foreground">
193
+ {label}
194
+ </span>
195
+ </div>
196
+ );
197
+ })}
198
+ </div>
199
+ </div>
200
+ )}
201
+
202
+ {/* Dashboard link */}
203
+ <div className="px-4 py-2.5">
204
+ <a
205
+ href={user.dashboardUrl}
206
+ target="_blank"
207
+ rel="noopener noreferrer"
208
+ className="flex items-center gap-2 rounded-lg px-2 py-1.5 text-xs text-muted-foreground hover:bg-muted hover:text-foreground transition-colors"
209
+ >
210
+ <ExternalLink className="h-3.5 w-3.5" />
211
+ Open Dashboard
212
+ </a>
213
+ </div>
214
+ </div>
215
+ )}
216
+ </div>
217
+ );
218
+ }
@@ -17,7 +17,7 @@
17
17
  "{{aiProviderPackage}}": "{{aiProviderVersion}}",
18
18
  "@prisma/client": "^6.2.0",
19
19
  "@radix-ui/react-collapsible": "^1.1.0",
20
- "@supyagent/sdk": "^0.1.26",
20
+ "@supyagent/sdk": "^0.1.36",
21
21
  "class-variance-authority": "^0.7.0",
22
22
  "ai": "^6.0.0",
23
23
  "clsx": "^2.1.0",