kyd-shared-badge 0.3.119 → 0.3.121
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
|
@@ -5,7 +5,7 @@ import { normalizeLinkedInInput } from './linkedin';
|
|
|
5
5
|
import type { ConnectAccountsProps } from './types';
|
|
6
6
|
import { CheckCircle, Link2, LinkIcon, Unlink, ArrowLeft, ExternalLink, Settings, Shield, InfoIcon } from 'lucide-react';
|
|
7
7
|
import { AnimatePresence, motion } from 'framer-motion';
|
|
8
|
-
import { Button, Input, Spinner, Card, CardHeader, CardContent, CardFooter, CardTitle
|
|
8
|
+
import { Button, Input, Spinner, Card, CardHeader, CardContent, CardFooter, CardTitle } from '../ui';
|
|
9
9
|
import Link from 'next/link';
|
|
10
10
|
import { Tooltip, TooltipTrigger, TooltipProvider, TooltipContent } from '../ui/';
|
|
11
11
|
|
|
@@ -222,32 +222,11 @@ export function ConnectAccounts(props: ConnectAccountsProps) {
|
|
|
222
222
|
return setupAction === 'install' || setupAction === 'created';
|
|
223
223
|
}, [githubConnectedAccount]);
|
|
224
224
|
|
|
225
|
-
// Progress
|
|
226
|
-
const targetProviderIds = useMemo(() => {
|
|
227
|
-
const targets = (requiredProviders && requiredProviders.length
|
|
228
|
-
? requiredProviders
|
|
229
|
-
: providers.map(p => p.id))
|
|
230
|
-
.map(id => id.toLowerCase());
|
|
231
|
-
return new Set(targets);
|
|
232
|
-
}, [requiredProviders, providers]);
|
|
233
|
-
const numConnectedTargets = useMemo(() => {
|
|
234
|
-
let count = 0;
|
|
235
|
-
targetProviderIds.forEach(id => { if (connectedIds.has(id)) count += 1; });
|
|
236
|
-
return count;
|
|
237
|
-
}, [targetProviderIds, connectedIds]);
|
|
238
|
-
const progressPercent = useMemo(() => {
|
|
239
|
-
const total = targetProviderIds.size;
|
|
240
|
-
if (!total) return 0;
|
|
241
|
-
return Math.round((numConnectedTargets / total) * 100);
|
|
242
|
-
}, [numConnectedTargets, targetProviderIds]);
|
|
225
|
+
// Progress UI is outsourced to ConnectProgress
|
|
243
226
|
|
|
244
227
|
return (
|
|
245
228
|
<>
|
|
246
|
-
|
|
247
|
-
<div className="fixed sm:bottom-6 sm:right-6 bottom-4 right-4 z-50 pointer-events-none">
|
|
248
|
-
<ProgressCircle value={progressPercent} size={68} thickness={6} />
|
|
249
|
-
</div>
|
|
250
|
-
) : null}
|
|
229
|
+
|
|
251
230
|
<AnimatePresence initial={false} mode="wait">
|
|
252
231
|
{showDataHandling ? (
|
|
253
232
|
<motion.div
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useMemo } from 'react'
|
|
3
|
+
import { Card, CardContent } from './card'
|
|
4
|
+
import { ProgressCircle } from './progress-circle'
|
|
5
|
+
|
|
6
|
+
export type ConnectProgressProps = {
|
|
7
|
+
providers: Array<{ id: string; name: string }>
|
|
8
|
+
connectedIds: Set<string>
|
|
9
|
+
className?: string
|
|
10
|
+
layout?: 'fixed-desktop' | 'inline'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function ConnectProgress(props: ConnectProgressProps) {
|
|
14
|
+
const { providers, connectedIds, className, layout = 'fixed-desktop' } = props
|
|
15
|
+
|
|
16
|
+
const hasGithub = connectedIds.has('github')
|
|
17
|
+
const hasLinkedIn = connectedIds.has('linkedin')
|
|
18
|
+
const otherConnectedCount = useMemo(() => {
|
|
19
|
+
let count = 0
|
|
20
|
+
connectedIds.forEach(id => { if (id !== 'github' && id !== 'linkedin') count += 1 })
|
|
21
|
+
return count
|
|
22
|
+
}, [connectedIds])
|
|
23
|
+
|
|
24
|
+
const progressPercent = useMemo(() => {
|
|
25
|
+
const githubWeight = hasGithub ? 65 : 0
|
|
26
|
+
const linkedinWeight = hasLinkedIn ? 15 : 0
|
|
27
|
+
const othersWeight = Math.min(otherConnectedCount, 2) * 10
|
|
28
|
+
return Math.max(0, Math.min(100, githubWeight + linkedinWeight + othersWeight))
|
|
29
|
+
}, [hasGithub, hasLinkedIn, otherConnectedCount])
|
|
30
|
+
|
|
31
|
+
const progressCopy = useMemo(() => {
|
|
32
|
+
const score = progressPercent
|
|
33
|
+
if (score === 0) {
|
|
34
|
+
return {
|
|
35
|
+
headline: 'Get started — connect GitHub',
|
|
36
|
+
body: 'GitHub contributes ~65% of your profile strength. Add LinkedIn (+15%) and 1–2 other platforms (+10% each). 70%+ is strong; 100% is optional.'
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (hasGithub && score < 70) {
|
|
40
|
+
return {
|
|
41
|
+
headline: 'Good base — add more signals',
|
|
42
|
+
body: 'Add LinkedIn (+15%) and at least one other platform (+10%) to reach 70%+, which is considered strong. 100% is not mandatory.'
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (!hasGithub && score < 70) {
|
|
46
|
+
return {
|
|
47
|
+
headline: 'Add GitHub for a big boost',
|
|
48
|
+
body: 'Current signals are light. Connecting GitHub adds ~65% weight and significantly improves your profile.'
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (score >= 70 && score < 90) {
|
|
52
|
+
return {
|
|
53
|
+
headline: 'Strong — recommended coverage achieved',
|
|
54
|
+
body: '70%+ indicates a strong profile. Adding more platforms can further improve verification. 100% is optional but even better.'
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
if (score >= 90) {
|
|
58
|
+
return {
|
|
59
|
+
headline: 'Excellent — high confidence',
|
|
60
|
+
body: 'You are near complete coverage. 100% is not required, but additional platforms can further increase confidence.'
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (score === 100) {
|
|
64
|
+
return {
|
|
65
|
+
headline: 'Complete — high confidence',
|
|
66
|
+
body: 'You\'ve achieved the recommended coverage for a very strong profile! Connecting even more accounts can further strengthen your verification.'
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
headline: 'Keep going',
|
|
71
|
+
body: 'Connect more platforms to strengthen your profile. 70%+ is strong; 100% is optional.'
|
|
72
|
+
}
|
|
73
|
+
}, [progressPercent, hasGithub])
|
|
74
|
+
|
|
75
|
+
const content = (
|
|
76
|
+
<Card className={`border-[var(--icon-button-secondary)] ${className || ''}`} style={{ backgroundColor: 'var(--content-card-background)'}}>
|
|
77
|
+
<CardContent className="py-3 px-3">
|
|
78
|
+
<div className="flex items-center gap-3">
|
|
79
|
+
<ProgressCircle value={progressPercent} size={64} thickness={6} />
|
|
80
|
+
<div className="min-w-0 max-w-xs">
|
|
81
|
+
<div className="text-base font-semibold truncate" style={{ color: 'var(--text-main)'}}>{progressCopy.headline}</div>
|
|
82
|
+
<div className="text-sm mt-0.5" style={{ color: 'var(--text-secondary)'}}>
|
|
83
|
+
{progressCopy.body}
|
|
84
|
+
</div>
|
|
85
|
+
<div className="text-sm mt-1" style={{ color: 'var(--text-secondary)'}}>
|
|
86
|
+
Weighted score • {progressPercent}%
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</CardContent>
|
|
91
|
+
</Card>
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if (layout === 'inline') return content
|
|
95
|
+
|
|
96
|
+
return (
|
|
97
|
+
<div className="hidden sm:block fixed top-6 right-6 z-50">{content}</div>
|
|
98
|
+
)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default ConnectProgress
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
package/src/ui/index.ts
CHANGED
|
@@ -13,6 +13,9 @@ export type ProgressCircleProps = {
|
|
|
13
13
|
export function ProgressCircle(props: ProgressCircleProps) {
|
|
14
14
|
const { value, size = 72, thickness = 6, className, showLabel = true, label } = props
|
|
15
15
|
|
|
16
|
+
const rawId = React.useId()
|
|
17
|
+
const gradientId = React.useMemo(() => `kyd-pc-${String(rawId).replace(/:/g, '')}` , [rawId])
|
|
18
|
+
|
|
16
19
|
const clamped = Math.max(0, Math.min(100, Number.isFinite(value) ? value : 0))
|
|
17
20
|
const radius = (size - thickness) / 2
|
|
18
21
|
const circumference = 2 * Math.PI * radius
|
|
@@ -22,7 +25,7 @@ export function ProgressCircle(props: ProgressCircleProps) {
|
|
|
22
25
|
<div className={`relative inline-flex items-center justify-center ${className || ''}`} style={{ width: size, height: size }}>
|
|
23
26
|
<svg width={size} height={size} viewBox={`0 0 ${size} ${size}`} className="block">
|
|
24
27
|
<defs>
|
|
25
|
-
<linearGradient id=
|
|
28
|
+
<linearGradient id={gradientId} x1="0%" y1="0%" x2="100%" y2="0%">
|
|
26
29
|
<stop offset="0%" stopColor="var(--icon-accent)" />
|
|
27
30
|
<stop offset="100%" stopColor="var(--icon-accent-hover)" />
|
|
28
31
|
</linearGradient>
|
|
@@ -41,7 +44,7 @@ export function ProgressCircle(props: ProgressCircleProps) {
|
|
|
41
44
|
cy={size / 2}
|
|
42
45
|
r={radius}
|
|
43
46
|
fill="none"
|
|
44
|
-
stroke=
|
|
47
|
+
stroke={`url(#${gradientId})`}
|
|
45
48
|
strokeWidth={thickness}
|
|
46
49
|
strokeLinecap="round"
|
|
47
50
|
strokeDasharray={circumference}
|
|
@@ -76,3 +79,4 @@ export function ProgressCircle(props: ProgressCircleProps) {
|
|
|
76
79
|
export default ProgressCircle
|
|
77
80
|
|
|
78
81
|
|
|
82
|
+
|