kyd-shared-badge 0.3.71 → 0.3.73

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": "kyd-shared-badge",
3
- "version": "0.3.71",
3
+ "version": "0.3.73",
4
4
  "private": false,
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -21,7 +21,9 @@
21
21
  "@aws-sdk/lib-dynamodb": "^3.893.0",
22
22
  "@chatscope/chat-ui-kit-react": "^2.1.1",
23
23
  "@chatscope/chat-ui-kit-styles": "^1.4.0",
24
+ "@radix-ui/react-slot": "^1.2.3",
24
25
  "ai": "5.0.47",
26
+ "class-variance-authority": "^0.7.1",
25
27
  "framer-motion": "^12.23.24",
26
28
  "i18n-iso-countries": "^7.14.0",
27
29
  "lucide-react": "^0.545.0",
@@ -29,6 +31,7 @@
29
31
  "react-hot-toast": "^2.6.0",
30
32
  "react-icons": "^5.5.0",
31
33
  "recharts": "^2.15.4",
34
+ "tailwind-merge": "^3.3.1",
32
35
  "ulid": "^3.0.1"
33
36
  },
34
37
  "publishConfig": {
@@ -5,7 +5,7 @@ import { normalizeLinkedInInput } from './linkedin';
5
5
  import type { ConnectAccountsProps } from './types';
6
6
  import { CheckCircle, Link2, LinkIcon, Unlink, ArrowLeft, ExternalLink } from 'lucide-react';
7
7
  import { AnimatePresence, motion } from 'framer-motion';
8
- import { Button, Input, Spinner } from '../ui';
8
+ import { Button, Input, Spinner, Card, CardHeader, CardContent, CardFooter, CardTitle } from '../ui';
9
9
  import Link from 'next/link';
10
10
 
11
11
  function byPriority(a: string, b: string) {
@@ -32,6 +32,9 @@ export function ConnectAccounts(props: ConnectAccountsProps) {
32
32
  className,
33
33
  oauthClientIds,
34
34
  providerPickCallback,
35
+ handleBackButton,
36
+ headerTitle,
37
+ headerDescription,
35
38
  } = props;
36
39
 
37
40
  const router = useRouter();
@@ -148,11 +151,11 @@ export function ConnectAccounts(props: ConnectAccountsProps) {
148
151
  setLinkUrl('');
149
152
  };
150
153
 
151
- const fadeIn = {
152
- hidden: { opacity: 0, y: 6 },
153
- visible: { opacity: 1, y: 0, transition: { duration: 0.15 } },
154
- exit: { opacity: 0, y: 6, transition: { duration: 0.1 } },
155
- } as const;
154
+ const cardVariants = {
155
+ initial: { opacity: 0, y: 20 },
156
+ animate: { opacity: 1, y: 0 },
157
+ exit: { opacity: 0, y: -20 },
158
+ };
156
159
 
157
160
  return (
158
161
  <>
@@ -162,7 +165,7 @@ export function ConnectAccounts(props: ConnectAccountsProps) {
162
165
  key="connect-card"
163
166
  className="rounded-xl border max-w-xl w-full"
164
167
  style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)'}}
165
- initial="hidden" animate="visible" exit="exit" variants={fadeIn}
168
+ initial="initial" animate="animate" exit="exit" variants={cardVariants}
166
169
  >
167
170
  <div className="sm:p-6 p-4">
168
171
  <button onClick={handleConnectBack} className="flex items-center gap-2 text-sm text-[var(--text-secondary)] hover:text-[var(--text-main)] transition-colors mb-4">
@@ -189,7 +192,7 @@ export function ConnectAccounts(props: ConnectAccountsProps) {
189
192
  <motion.form
190
193
  onSubmit={(e) => { e.preventDefault(); onSubmitLink(selectedProvider.id); }}
191
194
  className="mt-6 space-y-4"
192
- initial="hidden" animate="visible" variants={fadeIn}
195
+ initial="initial" animate="animate" exit="exit" variants={cardVariants}
193
196
  >
194
197
  {selectedProvider.id === 'linkedin' && (
195
198
  <p className="sm:text-xs items-center text-[10px] text-[var(--text-secondary)] leading-relaxed max-w-xs mx-auto -mt-2">
@@ -246,128 +249,133 @@ export function ConnectAccounts(props: ConnectAccountsProps) {
246
249
  </motion.div>
247
250
  </AnimatePresence>
248
251
  ) : (
249
- <motion.div className="rounded-xl border max-w-lg" style={{ backgroundColor: 'var(--content-card-background)', borderColor: 'var(--icon-button-secondary)'}} initial="hidden" animate="visible" variants={fadeIn}>
250
- <div className="sm:p-6 p-4">
251
- <h2 className="sm:text-xl text-base font-semibold" style={{ color: 'var(--text-main)'}}>Connect accounts</h2>
252
- <p className="mt-2 sm:text-sm text-xs" style={{ color: 'var(--text-secondary)'}}>
253
- Each connected account adds verified signals to your report. Connect more accounts to strengthen your score—then disconnect anytime.
254
- </p>
255
- </div>
256
- <motion.div className="sm:px-6 px-4 sm:pb-6 pb-4 space-y-2" initial="hidden" animate="visible" variants={fadeIn}>
257
- <div className="space-y-2">
258
- {(() => {
259
- const oauthList = list.filter(p => p.connectionType === 'oauth');
260
- const urlList = list.filter(p => (p.connectionType || 'url') === 'url' || p.connectionType === 'link');
252
+ <Card className="border-[var(--icon-button-secondary)] pt-2" style={{ backgroundColor: 'var(--content-card-background)'}}>
253
+ <AnimatePresence mode="wait">
254
+ <motion.div key="platform-list-shared" variants={cardVariants} initial="initial" animate="animate" exit="exit" transition={{ duration: 0.3 }}>
255
+ <CardHeader className="pb-4">
256
+ {handleBackButton && (
257
+ <button onClick={() => handleBackButton()} className="flex items-center gap-2 text-sm sm:mb-1 mb-4 text-[var(--text-secondary)] hover:text-[var(--text-main)] transition-colors">
258
+ <ArrowLeft className="w-4 h-4" /> Back
259
+ </button>
260
+ )}
261
+ <CardTitle className="sm:text-xl text-base font-semibold text-[var(--text-main)] mb-2">{headerTitle}</CardTitle>
262
+ <p className="sm:text-sm text-xs text-[var(--text-secondary)] leading-relaxed">{headerDescription}</p>
263
+ </CardHeader>
264
+ <CardContent className="space-y-2">
265
+ {(() => {
266
+ const oauthList = list.filter(p => p.connectionType === 'oauth');
267
+ const urlList = list.filter(p => (p.connectionType || 'url') === 'url' || p.connectionType === 'link');
261
268
 
262
- const Row = (provider: typeof list[number]) => {
263
- const providerId = provider.id;
264
- const isConnected = connectedIds.has(providerId.toLowerCase());
265
- const needsReconnect = reconnectIds.has(providerId.toLowerCase());
266
- const isOauth = provider.connectionType === 'oauth'
267
- const connectedUrl = connected.find(c => c.id.toLowerCase() === providerId.toLowerCase())?.url;
268
- const betaFlag = () => (
269
- <span
270
- className="ml-2 inline-block rounded-full px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 border border-blue-200"
271
- style={{ verticalAlign: 'middle', letterSpacing: '0.05em' }}
272
- >
273
- Beta
274
- </span>
275
- );
269
+ const Row = (provider: typeof list[number]) => {
270
+ const providerId = provider.id;
271
+ const isConnected = connectedIds.has(providerId.toLowerCase());
272
+ const needsReconnect = reconnectIds.has(providerId.toLowerCase());
273
+ const isOauth = provider.connectionType === 'oauth'
274
+ const connectedUrl = connected.find(c => c.id.toLowerCase() === providerId.toLowerCase())?.url;
275
+ const betaFlag = () => (
276
+ <span
277
+ className="ml-2 inline-block rounded-full px-2 py-0.5 text-xs font-semibold bg-blue-100 text-blue-700 border border-blue-200"
278
+ style={{ verticalAlign: 'middle', letterSpacing: '0.05em' }}
279
+ >
280
+ Beta
281
+ </span>
282
+ );
276
283
 
277
- return (
278
- <div key={providerId} className="group flex items-center justify-between p-2 rounded-lg transition-colors hover:bg-[var(--icon-button-secondary)]/10 transform transition-transform hover:-translate-y-px">
279
- <div className="flex items-center gap-3">
284
+ return (
285
+ <div key={providerId} className="group flex items-center justify-between p-2 rounded-lg transition-colors hover:bg-[var(--icon-button-secondary)]/10 transform transition-transform hover:-translate-y-px">
286
+ <div className="flex items-center gap-3">
280
287
 
281
- {isConnected && connectedUrl ? (
282
- <Link href={connectedUrl} target="_blank" rel="noopener noreferrer" className="flex items-center gap-3 hover:underline">
283
- <ProviderIcon name={provider.id} className={`sm:size-7 size-5 ${provider.iconColor || 'text-gray-500'}`} />
284
- <span className="font-medium sm:text-base text-sm" style={{ color: 'var(--text-main)'}}>{provider.name}</span>
285
- </Link>
288
+ {isConnected && connectedUrl ? (
289
+ <Link href={connectedUrl} target="_blank" rel="noopener noreferrer" className="flex items-center gap-3 hover:underline">
290
+ <ProviderIcon name={provider.id} className={`sm:size-7 size-5 ${provider.iconColor || 'text-gray-500'}`} />
291
+ <span className="font-medium sm:text-base text-sm" style={{ color: 'var(--text-main)'}}>{provider.name}</span>
292
+ </Link>
293
+ ) : (
294
+ <div className="flex items-center gap-3">
295
+ <ProviderIcon name={provider.id} className={`sm:size-7 size-5 ${provider.iconColor || 'text-gray-500'}`} />
296
+ <span className="font-medium sm:text-base text-sm" style={{ color: 'var(--text-main)'}}>{provider.name}</span>
297
+ </div>
298
+ )}
299
+ {provider.beta ? betaFlag() : null}
300
+ </div>
301
+
302
+ {isConnected ? (
303
+ <div className="relative flex items-center">
304
+ <div className="flex items-center gap-2 transition-opacity group-hover:opacity-0" style={{ color: 'var(--success-green)'}}>
305
+ <CheckCircle className="size-3 sm:size-4" />
306
+ <span className="text-sm font-medium">Connected</span>
307
+ </div>
308
+ <div className="absolute right-0 opacity-0 transition-opacity group-hover:opacity-100">
309
+ <button
310
+ onClick={() => onDisconnect(providerId)}
311
+ className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm text-red-600 hover:text-red-700 hover:underline"
312
+ >
313
+ <Unlink className="size-3 sm:size-4" />
314
+ <span>Disconnect</span>
315
+ </button>
316
+ </div>
317
+ </div>
286
318
  ) : (
287
- <div className="flex items-center gap-3">
288
- <ProviderIcon name={provider.id} className={`sm:size-7 size-5 ${provider.iconColor || 'text-gray-500'}`} />
289
- <span className="font-medium sm:text-base text-sm" style={{ color: 'var(--text-main)'}}>{provider.name}</span>
319
+ <div className="flex items-center gap-2">
320
+ <>
321
+ <Button
322
+ onClick={() => setSelectedProviderIdAndCallback(providerId)}
323
+ className="bg-[var(--icon-button-secondary)] text-[var(--text-main)] hover:bg-[var(--icon-accent)] hover:text-white border-0 sm:px-4 px-3 py-1 sm:py-2 rounded-lg flex items-center gap-2"
324
+ >
325
+ {isOauth ? (
326
+ <Link2 className="size-3 sm:size-4" />
327
+ ) : (
328
+ <LinkIcon className="size-3 sm:size-4" />
329
+ )}
330
+ <span className="sm:text-base text-sm">Connect</span>
331
+ </Button>
332
+ {needsReconnect && (
333
+ <Button
334
+ onClick={() => onDisconnect(providerId)}
335
+ className="inline-flex items-center justify-center gap-1.5 px-4 py-2 text-sm rounded border"
336
+ style={{ color: 'var(--text-main)', borderColor: 'var(--icon-button-secondary)'}}
337
+ >
338
+ <Unlink className="size-3 sm:size-4" />
339
+ <span>Remove</span>
340
+ </Button>
341
+ )}
342
+ </>
290
343
  </div>
291
344
  )}
292
- {provider.beta ? betaFlag() : null}
293
345
  </div>
346
+ );
347
+ };
294
348
 
295
- {isConnected ? (
296
- <div className="relative flex items-center">
297
- <div className="flex items-center gap-2 transition-opacity group-hover:opacity-0" style={{ color: 'var(--success-green)'}}>
298
- <CheckCircle className="size-3 sm:size-4" />
299
- <span className="text-sm font-medium">Connected</span>
349
+ return (
350
+ <>
351
+ {oauthList.length ? (
352
+ <>
353
+ <div className="flex items-center my-2">
354
+ <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
355
+ <span className="mx-3 text-[10px] sm:text-xs uppercase tracking-wide" style={{ color: 'var(--text-secondary)'}}>OAuth</span>
356
+ <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
300
357
  </div>
301
- <div className="absolute right-0 opacity-0 transition-opacity group-hover:opacity-100">
302
- <button
303
- onClick={() => onDisconnect(providerId)}
304
- className="inline-flex items-center gap-1.5 px-3 py-1.5 text-sm text-red-600 hover:text-red-700 hover:underline"
305
- >
306
- <Unlink className="size-3 sm:size-4" />
307
- <span>Disconnect</span>
308
- </button>
358
+ {oauthList.map(p => Row(p))}
359
+ </>
360
+ ) : null}
361
+
362
+ {urlList.length ? (
363
+ <>
364
+ <div className="flex items-center my-2">
365
+ <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
366
+ <span className="mx-3 text-[10px] sm:text-xs uppercase tracking-wide" style={{ color: 'var(--text-secondary)'}}>Public Urls</span>
367
+ <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
309
368
  </div>
310
- </div>
311
- ) : (
312
- <div className="flex items-center gap-2">
313
- <>
314
- <Button
315
- onClick={() => setSelectedProviderIdAndCallback(providerId)}
316
- className="bg-[var(--icon-button-secondary)] text-[var(--text-main)] hover:bg-[var(--icon-accent)] hover:text-white border-0 sm:px-4 px-3 py-1 sm:py-2 rounded-lg flex items-center gap-2"
317
- >
318
- {isOauth ? (
319
- <Link2 className="size-3 sm:size-4" />
320
- ) : (
321
- <LinkIcon className="size-3 sm:size-4" />
322
- )}
323
- <span className="sm:text-base text-sm">Connect</span>
324
- </Button>
325
- {needsReconnect && (
326
- <Button
327
- onClick={() => onDisconnect(providerId)}
328
- className="inline-flex items-center justify-center gap-1.5 px-4 py-2 text-sm rounded border"
329
- style={{ color: 'var(--text-main)', borderColor: 'var(--icon-button-secondary)'}}
330
- >
331
- <Unlink className="size-3 sm:size-4" />
332
- <span>Remove</span>
333
- </Button>
334
- )}
335
- </>
336
- </div>
337
- )}
338
- </div>
369
+ {urlList.map(p => Row(p))}
370
+ </>
371
+ ) : null}
372
+ </>
339
373
  );
340
- };
341
-
342
- return (
343
- <>
344
- {oauthList.length ? (
345
- <>
346
- <div className="flex items-center my-2">
347
- <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
348
- <span className="mx-3 text-[10px] sm:text-xs uppercase tracking-wide" style={{ color: 'var(--text-secondary)'}}>OAuth</span>
349
- <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
350
- </div>
351
- {oauthList.map(p => Row(p))}
352
- </>
353
- ) : null}
354
-
355
- {urlList.length ? (
356
- <>
357
- <div className="flex items-center my-2">
358
- <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
359
- <span className="mx-3 text-[10px] sm:text-xs uppercase tracking-wide" style={{ color: 'var(--text-secondary)'}}>Public Urls</span>
360
- <div className="flex-1 border-t" style={{ borderColor: 'var(--icon-button-secondary)', opacity: 0.3 }} />
361
- </div>
362
- {urlList.map(p => Row(p))}
363
- </>
364
- ) : null}
365
- </>
366
- );
367
- })()}
368
- </div>
369
- </motion.div>
370
- </motion.div>
374
+ })()}
375
+ </CardContent>
376
+ </motion.div>
377
+ </AnimatePresence>
378
+ </Card>
371
379
  )}
372
380
  </>
373
381
  );
@@ -33,6 +33,9 @@ export interface ConnectAccountsProps {
33
33
  onError?: (message: string) => void;
34
34
  className?: string;
35
35
  providerPickCallback: (providerId: string | null) => void;
36
+ handleBackButton?: () => void;
37
+ headerTitle: string;
38
+ headerDescription: string;
36
39
  }
37
40
 
38
41
 
package/src/ui/button.tsx CHANGED
@@ -2,7 +2,7 @@ import * as React from "react"
2
2
  import { Slot } from "@radix-ui/react-slot"
3
3
  import { cva, type VariantProps } from "class-variance-authority"
4
4
 
5
- import { cn } from "@/lib/utils"
5
+ import { cn } from "../utils"
6
6
 
7
7
  const buttonVariants = cva(
8
8
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
package/src/ui/card.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from "react"
2
2
 
3
- import { cn } from "@/lib/utils"
3
+ import { cn } from "../utils"
4
4
 
5
5
  const Card = React.forwardRef<
6
6
  HTMLDivElement,
package/src/ui/input.tsx CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as React from "react"
2
2
 
3
- import { cn } from "@/lib/utils"
3
+ import { cn } from "../utils"
4
4
 
5
5
  const Input = React.forwardRef<HTMLInputElement, React.ComponentProps<"input">>(
6
6
  ({ className, type, ...props }, ref) => {
@@ -0,0 +1,2 @@
1
+ export * from './date';
2
+ export * from './utils';
@@ -0,0 +1,9 @@
1
+ import { clsx, type ClassValue } from "clsx"
2
+ import { twMerge } from "tailwind-merge"
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
7
+
8
+ export const ROLE_NAME_MAX_LENGTH = 500;
9
+ export const ROLE_DESCRIPTION_MAX_LENGTH = 5500;