@aiready/components 0.1.31 → 0.1.34

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.
Files changed (73) hide show
  1. package/README.md +2 -2
  2. package/dist/charts/ForceDirectedGraph.js +49 -13
  3. package/dist/charts/ForceDirectedGraph.js.map +1 -1
  4. package/dist/components/badge.js.map +1 -1
  5. package/dist/components/button.js.map +1 -1
  6. package/dist/components/card.js.map +1 -1
  7. package/dist/components/checkbox.js.map +1 -1
  8. package/dist/components/container.js.map +1 -1
  9. package/dist/components/grid.js.map +1 -1
  10. package/dist/components/input.d.ts +1 -2
  11. package/dist/components/input.js.map +1 -1
  12. package/dist/components/label.js +1 -8
  13. package/dist/components/label.js.map +1 -1
  14. package/dist/components/radio-group.js.map +1 -1
  15. package/dist/components/select.js.map +1 -1
  16. package/dist/components/separator.js.map +1 -1
  17. package/dist/components/stack.js.map +1 -1
  18. package/dist/components/switch.js +29 -22
  19. package/dist/components/switch.js.map +1 -1
  20. package/dist/components/textarea.d.ts +1 -2
  21. package/dist/components/textarea.js.map +1 -1
  22. package/dist/hooks/useD3.js.map +1 -1
  23. package/dist/hooks/useDebounce.js.map +1 -1
  24. package/dist/hooks/useForceSimulation.d.ts +1 -0
  25. package/dist/hooks/useForceSimulation.js +37 -14
  26. package/dist/hooks/useForceSimulation.js.map +1 -1
  27. package/dist/index.d.ts +5 -5
  28. package/dist/index.js +337 -141
  29. package/dist/index.js.map +1 -1
  30. package/dist/utils/cn.js.map +1 -1
  31. package/dist/utils/colors.js.map +1 -1
  32. package/dist/utils/formatters.js.map +1 -1
  33. package/package.json +3 -2
  34. package/src/__tests__/smoke.test.js +1 -1
  35. package/src/__tests__/smoke.test.ts +3 -3
  36. package/src/charts/ForceDirectedGraph.tsx +583 -517
  37. package/src/charts/GraphControls.tsx +5 -2
  38. package/src/charts/LinkItem.tsx +17 -5
  39. package/src/charts/NodeItem.tsx +17 -2
  40. package/src/code-block/CodeBlock.tsx +53 -16
  41. package/src/code-block/index.ts +1 -1
  42. package/src/components/badge.tsx +3 -2
  43. package/src/components/button.tsx +3 -2
  44. package/src/components/card.tsx +8 -1
  45. package/src/components/checkbox.tsx +6 -4
  46. package/src/components/container.tsx +1 -1
  47. package/src/components/grid.tsx +1 -1
  48. package/src/components/input.tsx +2 -3
  49. package/src/components/label.tsx +4 -7
  50. package/src/components/radio-group.tsx +5 -3
  51. package/src/components/select.tsx +5 -3
  52. package/src/components/separator.tsx +1 -1
  53. package/src/components/stack.tsx +1 -1
  54. package/src/components/switch.tsx +15 -7
  55. package/src/components/textarea.tsx +2 -3
  56. package/src/data-display/ScoreBar.tsx +52 -15
  57. package/src/data-display/index.ts +7 -1
  58. package/src/feedback/ErrorDisplay.tsx +17 -4
  59. package/src/feedback/LoadingSpinner.tsx +8 -3
  60. package/src/feedback/index.ts +12 -2
  61. package/src/hooks/useD3.ts +1 -3
  62. package/src/hooks/useDebounce.ts +1 -1
  63. package/src/hooks/useForceSimulation.ts +142 -44
  64. package/src/index.ts +29 -9
  65. package/src/navigation/Breadcrumb.tsx +17 -8
  66. package/src/navigation/index.ts +5 -1
  67. package/src/theme/ThemeProvider.tsx +11 -3
  68. package/src/theme/index.ts +6 -1
  69. package/src/utils/cn.ts +1 -1
  70. package/src/utils/colors.ts +1 -1
  71. package/src/utils/formatters.ts +1 -1
  72. package/src/utils/score.ts +3 -1
  73. package/tailwind.config.js +1 -1
@@ -89,7 +89,6 @@ export const GraphControls: React.FC<GraphControlsProps> = ({
89
89
  position = 'top-left',
90
90
  className,
91
91
  }) => {
92
-
93
92
  if (!visible) return null;
94
93
 
95
94
  const positionClasses: Record<string, string> = {
@@ -150,7 +149,11 @@ export const GraphControls: React.FC<GraphControlsProps> = ({
150
149
  onClick={() => onManualLayoutToggle?.(!manualLayout)}
151
150
  active={manualLayout}
152
151
  icon="🔧"
153
- label={manualLayout ? 'Manual layout: ON (drag freely)' : 'Manual layout: OFF (forces active)'}
152
+ label={
153
+ manualLayout
154
+ ? 'Manual layout: ON (drag freely)'
155
+ : 'Manual layout: OFF (forces active)'
156
+ }
154
157
  />
155
158
 
156
159
  {/* Divider */}
@@ -9,19 +9,31 @@ export interface LinkItemProps {
9
9
  nodes?: GraphNode[]; // Optional nodes array to resolve string IDs to node objects
10
10
  }
11
11
 
12
- export const LinkItem: React.FC<LinkItemProps> = ({ link, onClick, defaultWidth, showLabel = true, nodes = [] }) => {
13
- const src = (link.source as any)?.id ?? (typeof link.source === 'string' ? link.source : undefined);
14
- const tgt = (link.target as any)?.id ?? (typeof link.target === 'string' ? link.target : undefined);
12
+ export const LinkItem: React.FC<LinkItemProps> = ({
13
+ link,
14
+ onClick,
15
+ defaultWidth,
16
+ showLabel = true,
17
+ nodes = [],
18
+ }) => {
19
+ const src =
20
+ (link.source as any)?.id ??
21
+ (typeof link.source === 'string' ? link.source : undefined);
22
+ const tgt =
23
+ (link.target as any)?.id ??
24
+ (typeof link.target === 'string' ? link.target : undefined);
15
25
 
16
26
  // Helper to get node position from source/target (which could be node object or string ID)
17
- const getNodePosition = (nodeOrId: string | GraphNode): { x: number; y: number } | null => {
27
+ const getNodePosition = (
28
+ nodeOrId: string | GraphNode
29
+ ): { x: number; y: number } | null => {
18
30
  if (typeof nodeOrId === 'object' && nodeOrId !== null) {
19
31
  // It's a node object
20
32
  const node = nodeOrId as GraphNode;
21
33
  return { x: node.x ?? 0, y: node.y ?? 0 };
22
34
  } else if (typeof nodeOrId === 'string') {
23
35
  // It's a string ID, try to find in nodes array
24
- const found = nodes.find(n => n.id === nodeOrId);
36
+ const found = nodes.find((n) => n.id === nodeOrId);
25
37
  if (found) return { x: found.x ?? 0, y: found.y ?? 0 };
26
38
  }
27
39
  return null;
@@ -56,10 +56,25 @@ export const NodeItem: React.FC<NodeItemProps> = ({
56
56
  opacity={isHovered || isSelected ? 1 : 0.9}
57
57
  />
58
58
  {pinned && (
59
- <circle r={nodeSize + 4} fill="none" stroke="#ff6b6b" strokeWidth={1} opacity={0.5} className="pointer-events-none" />
59
+ <circle
60
+ r={nodeSize + 4}
61
+ fill="none"
62
+ stroke="#ff6b6b"
63
+ strokeWidth={1}
64
+ opacity={0.5}
65
+ className="pointer-events-none"
66
+ />
60
67
  )}
61
68
  {showLabel && node.label && (
62
- <text y={nodeSize + 15} fill="#333" fontSize="12" textAnchor="middle" dominantBaseline="middle" pointerEvents="none" className="select-none">
69
+ <text
70
+ y={nodeSize + 15}
71
+ fill="#333"
72
+ fontSize="12"
73
+ textAnchor="middle"
74
+ dominantBaseline="middle"
75
+ pointerEvents="none"
76
+ className="select-none"
77
+ >
63
78
  {node.label}
64
79
  </text>
65
80
  )}
@@ -13,8 +13,8 @@ export interface CodeBlockProps {
13
13
  // Dedent helper - removes common leading indentation
14
14
  function dedentCode(code: string): string {
15
15
  // Normalize tabs to two spaces
16
- let normalized = code.replace(/\t/g, ' ').replace(/[ \t]+$/gm, '');
17
-
16
+ const normalized = code.replace(/\t/g, ' ').replace(/[ \t]+$/gm, '');
17
+
18
18
  const lines = normalized.split('\n');
19
19
  if (lines.length <= 1) return normalized.trim();
20
20
 
@@ -23,7 +23,7 @@ function dedentCode(code: string): string {
23
23
  while (start < lines.length && lines[start].trim() === '') start++;
24
24
  let end = lines.length - 1;
25
25
  while (end >= 0 && lines[end].trim() === '') end--;
26
-
26
+
27
27
  if (start > end) return '';
28
28
  const relevantLines = lines.slice(start, end + 1);
29
29
 
@@ -35,10 +35,15 @@ function dedentCode(code: string): string {
35
35
  }, Infinity);
36
36
 
37
37
  // Remove common indentation
38
- const dedented = minIndent === Infinity || minIndent === 0
39
- ? relevantLines.join('\n')
40
- : relevantLines.map((l) => (l.startsWith(' '.repeat(minIndent)) ? l.slice(minIndent) : l)).join('\n');
41
-
38
+ const dedented =
39
+ minIndent === Infinity || minIndent === 0
40
+ ? relevantLines.join('\n')
41
+ : relevantLines
42
+ .map((l) =>
43
+ l.startsWith(' '.repeat(minIndent)) ? l.slice(minIndent) : l
44
+ )
45
+ .join('\n');
46
+
42
47
  return dedented;
43
48
  }
44
49
 
@@ -73,12 +78,32 @@ function CopyButton({ code }: { code: string }) {
73
78
  title={copied ? 'Copied!' : 'Copy code'}
74
79
  >
75
80
  {copied ? (
76
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
77
- <path strokeLinecap="round" strokeLinejoin="round" d="M4.5 12.75l6 6 9-13.5" />
81
+ <svg
82
+ className="h-4 w-4"
83
+ fill="none"
84
+ viewBox="0 0 24 24"
85
+ strokeWidth="1.5"
86
+ stroke="currentColor"
87
+ >
88
+ <path
89
+ strokeLinecap="round"
90
+ strokeLinejoin="round"
91
+ d="M4.5 12.75l6 6 9-13.5"
92
+ />
78
93
  </svg>
79
94
  ) : (
80
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
81
- <path strokeLinecap="round" strokeLinejoin="round" d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184" />
95
+ <svg
96
+ className="h-4 w-4"
97
+ fill="none"
98
+ viewBox="0 0 24 24"
99
+ strokeWidth="1.5"
100
+ stroke="currentColor"
101
+ >
102
+ <path
103
+ strokeLinecap="round"
104
+ strokeLinejoin="round"
105
+ d="M15.666 3.888A2.25 2.25 0 0013.5 2.25h-3c-1.03 0-1.9.693-2.166 1.638m7.332 0c.055.194.084.4.084.612v0a.75.75 0 01-.75.75H9a.75.75 0 01-.75-.75v0c0-.212.03-.418.084-.612m7.332 0c.646.049 1.288.11 1.927.184 1.1.128 1.907 1.077 1.907 2.185V19.5a2.25 2.25 0 01-2.25 2.25H6.75A2.25 2.25 0 014.5 19.5V6.257c0-1.108.806-2.057 1.907-2.185a48.208 48.208 0 011.927-.184"
106
+ />
82
107
  </svg>
83
108
  )}
84
109
  </button>
@@ -100,7 +125,9 @@ export function CodeBlock({
100
125
  // Handle template literal children
101
126
  try {
102
127
  const raw = React.Children.toArray(children)
103
- .map((c) => (typeof c === 'string' ? c : typeof c === 'number' ? String(c) : ''))
128
+ .map((c) =>
129
+ typeof c === 'string' ? c : typeof c === 'number' ? String(c) : ''
130
+ )
104
131
  .join('');
105
132
  return dedentCode(raw);
106
133
  } catch {
@@ -109,7 +136,9 @@ export function CodeBlock({
109
136
  }, [children]);
110
137
 
111
138
  return (
112
- <div className={`group relative my-4 overflow-hidden rounded-xl border border-slate-700 bg-slate-900 shadow-lg ${className}`}>
139
+ <div
140
+ className={`group relative my-4 overflow-hidden rounded-xl border border-slate-700 bg-slate-900 shadow-lg ${className}`}
141
+ >
113
142
  {/* Header bar */}
114
143
  {showHeader && (
115
144
  <div className="flex items-center justify-between border-b border-slate-700 bg-slate-800/50 px-4 py-2">
@@ -138,10 +167,18 @@ export function CodeBlock({
138
167
  }
139
168
 
140
169
  // Inline code component
141
- export function InlineCode({ children, className = '' }: { children: React.ReactNode; className?: string }) {
170
+ export function InlineCode({
171
+ children,
172
+ className = '',
173
+ }: {
174
+ children: React.ReactNode;
175
+ className?: string;
176
+ }) {
142
177
  return (
143
- <code className={`rounded-md bg-slate-100 px-1.5 py-0.5 text-sm font-mono text-slate-800 ${className}`}>
178
+ <code
179
+ className={`rounded-md bg-slate-100 px-1.5 py-0.5 text-sm font-mono text-slate-800 ${className}`}
180
+ >
144
181
  {children}
145
182
  </code>
146
183
  );
147
- }
184
+ }
@@ -1 +1 @@
1
- export { CodeBlock, InlineCode, type CodeBlockProps } from './CodeBlock';
1
+ export { CodeBlock, InlineCode, type CodeBlockProps } from './CodeBlock';
@@ -23,7 +23,8 @@ const badgeVariants = cva(
23
23
  );
24
24
 
25
25
  export interface BadgeProps
26
- extends React.HTMLAttributes<HTMLDivElement>,
26
+ extends
27
+ React.HTMLAttributes<HTMLDivElement>,
27
28
  VariantProps<typeof badgeVariants> {}
28
29
 
29
30
  function Badge({ className, variant, ...props }: BadgeProps) {
@@ -32,4 +33,4 @@ function Badge({ className, variant, ...props }: BadgeProps) {
32
33
  );
33
34
  }
34
35
 
35
- export { Badge, badgeVariants };
36
+ export { Badge, badgeVariants };
@@ -32,7 +32,8 @@ const buttonVariants = cva(
32
32
  );
33
33
 
34
34
  export interface ButtonProps
35
- extends React.ButtonHTMLAttributes<HTMLButtonElement>,
35
+ extends
36
+ React.ButtonHTMLAttributes<HTMLButtonElement>,
36
37
  VariantProps<typeof buttonVariants> {
37
38
  asChild?: boolean;
38
39
  }
@@ -50,4 +51,4 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
50
51
  );
51
52
  Button.displayName = 'Button';
52
53
 
53
- export { Button, buttonVariants };
54
+ export { Button, buttonVariants };
@@ -75,4 +75,11 @@ const CardFooter = React.forwardRef<
75
75
  ));
76
76
  CardFooter.displayName = 'CardFooter';
77
77
 
78
- export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent };
78
+ export {
79
+ Card,
80
+ CardHeader,
81
+ CardFooter,
82
+ CardTitle,
83
+ CardDescription,
84
+ CardContent,
85
+ };
@@ -1,15 +1,17 @@
1
1
  import * as React from 'react';
2
2
  import { cn } from '../utils/cn';
3
3
 
4
- export interface CheckboxProps
5
- extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
4
+ export interface CheckboxProps extends Omit<
5
+ React.InputHTMLAttributes<HTMLInputElement>,
6
+ 'type'
7
+ > {
6
8
  label?: string;
7
9
  }
8
10
 
9
11
  const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
10
12
  ({ className, label, id, ...props }, ref) => {
11
13
  const checkboxId = id || React.useId();
12
-
14
+
13
15
  return (
14
16
  <div className="flex items-center">
15
17
  <input
@@ -36,4 +38,4 @@ const Checkbox = React.forwardRef<HTMLInputElement, CheckboxProps>(
36
38
  );
37
39
  Checkbox.displayName = 'Checkbox';
38
40
 
39
- export { Checkbox };
41
+ export { Checkbox };
@@ -28,4 +28,4 @@ const Container = React.forwardRef<HTMLDivElement, ContainerProps>(
28
28
  );
29
29
  Container.displayName = 'Container';
30
30
 
31
- export { Container };
31
+ export { Container };
@@ -37,4 +37,4 @@ const Grid = React.forwardRef<HTMLDivElement, GridProps>(
37
37
  );
38
38
  Grid.displayName = 'Grid';
39
39
 
40
- export { Grid };
40
+ export { Grid };
@@ -1,8 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { cn } from '../utils/cn';
3
3
 
4
- export interface InputProps
5
- extends React.InputHTMLAttributes<HTMLInputElement> {}
4
+ export type InputProps = React.InputHTMLAttributes<HTMLInputElement>;
6
5
 
7
6
  const Input = React.forwardRef<HTMLInputElement, InputProps>(
8
7
  ({ className, type, ...props }, ref) => {
@@ -21,4 +20,4 @@ const Input = React.forwardRef<HTMLInputElement, InputProps>(
21
20
  );
22
21
  Input.displayName = 'Input';
23
22
 
24
- export { Input };
23
+ export { Input };
@@ -7,18 +7,15 @@ const labelVariants = cva(
7
7
  );
8
8
 
9
9
  export interface LabelProps
10
- extends React.LabelHTMLAttributes<HTMLLabelElement>,
10
+ extends
11
+ React.LabelHTMLAttributes<HTMLLabelElement>,
11
12
  VariantProps<typeof labelVariants> {}
12
13
 
13
14
  const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
14
15
  ({ className, ...props }, ref) => (
15
- <label
16
- ref={ref}
17
- className={cn(labelVariants(), className)}
18
- {...props}
19
- />
16
+ <label ref={ref} className={cn(labelVariants(), className)} {...props} />
20
17
  )
21
18
  );
22
19
  Label.displayName = 'Label';
23
20
 
24
- export { Label };
21
+ export { Label };
@@ -7,8 +7,10 @@ export interface RadioOption {
7
7
  disabled?: boolean;
8
8
  }
9
9
 
10
- export interface RadioGroupProps
11
- extends Omit<React.HTMLAttributes<HTMLDivElement>, 'onChange'> {
10
+ export interface RadioGroupProps extends Omit<
11
+ React.HTMLAttributes<HTMLDivElement>,
12
+ 'onChange'
13
+ > {
12
14
  options: RadioOption[];
13
15
  value?: string;
14
16
  onChange?: (value: string) => void;
@@ -68,4 +70,4 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>(
68
70
  );
69
71
  RadioGroup.displayName = 'RadioGroup';
70
72
 
71
- export { RadioGroup };
73
+ export { RadioGroup };
@@ -7,8 +7,10 @@ export interface SelectOption {
7
7
  disabled?: boolean;
8
8
  }
9
9
 
10
- export interface SelectProps
11
- extends Omit<React.SelectHTMLAttributes<HTMLSelectElement>, 'onChange'> {
10
+ export interface SelectProps extends Omit<
11
+ React.SelectHTMLAttributes<HTMLSelectElement>,
12
+ 'onChange'
13
+ > {
12
14
  options: SelectOption[];
13
15
  placeholder?: string;
14
16
  onChange?: (value: string) => void;
@@ -50,4 +52,4 @@ const Select = React.forwardRef<HTMLSelectElement, SelectProps>(
50
52
  );
51
53
  Select.displayName = 'Select';
52
54
 
53
- export { Select };
55
+ export { Select };
@@ -26,4 +26,4 @@ const Separator = React.forwardRef<HTMLDivElement, SeparatorProps>(
26
26
  );
27
27
  Separator.displayName = 'Separator';
28
28
 
29
- export { Separator };
29
+ export { Separator };
@@ -58,4 +58,4 @@ const Stack = React.forwardRef<HTMLDivElement, StackProps>(
58
58
  );
59
59
  Stack.displayName = 'Stack';
60
60
 
61
- export { Stack };
61
+ export { Stack };
@@ -1,24 +1,32 @@
1
1
  import * as React from 'react';
2
2
  import { cn } from '../utils/cn';
3
3
 
4
- export interface SwitchProps
5
- extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'type'> {
4
+ export interface SwitchProps extends Omit<
5
+ React.InputHTMLAttributes<HTMLInputElement>,
6
+ 'type'
7
+ > {
6
8
  label?: string;
7
9
  onCheckedChange?: (checked: boolean) => void;
8
10
  }
9
11
 
10
12
  const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
11
- ({ className, label, id, checked, onCheckedChange, onChange, ...props }, ref) => {
13
+ (
14
+ { className, label, id, checked, onCheckedChange, onChange, ...props },
15
+ ref
16
+ ) => {
12
17
  const switchId = id || React.useId();
13
-
18
+
14
19
  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
15
20
  onChange?.(e);
16
21
  onCheckedChange?.(e.target.checked);
17
22
  };
18
-
23
+
19
24
  return (
20
25
  <div className="flex items-center">
21
- <label htmlFor={switchId} className="relative inline-flex cursor-pointer items-center">
26
+ <label
27
+ htmlFor={switchId}
28
+ className="relative inline-flex cursor-pointer items-center"
29
+ >
22
30
  <input
23
31
  type="checkbox"
24
32
  id={switchId}
@@ -46,4 +54,4 @@ const Switch = React.forwardRef<HTMLInputElement, SwitchProps>(
46
54
  );
47
55
  Switch.displayName = 'Switch';
48
56
 
49
- export { Switch };
57
+ export { Switch };
@@ -1,8 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { cn } from '../utils/cn';
3
3
 
4
- export interface TextareaProps
5
- extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
4
+ export type TextareaProps = React.TextareaHTMLAttributes<HTMLTextAreaElement>;
6
5
 
7
6
  const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
8
7
  ({ className, ...props }, ref) => {
@@ -20,4 +19,4 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
20
19
  );
21
20
  Textarea.displayName = 'Textarea';
22
21
 
23
- export { Textarea };
22
+ export { Textarea };
@@ -3,7 +3,12 @@
3
3
  import { getRating as getCoreRating } from '@aiready/core/client';
4
4
  import { cn } from '../utils/cn';
5
5
 
6
- export type ScoreRating = 'excellent' | 'good' | 'fair' | 'needs-work' | 'critical';
6
+ export type ScoreRating =
7
+ | 'excellent'
8
+ | 'good'
9
+ | 'fair'
10
+ | 'needs-work'
11
+ | 'critical';
7
12
 
8
13
  export interface ScoreBarProps {
9
14
  score: number;
@@ -14,11 +19,22 @@ export interface ScoreBarProps {
14
19
  className?: string;
15
20
  }
16
21
 
17
- const ratingConfig: Record<ScoreRating, { color: string; bgColor: string; label: string }> = {
18
- excellent: { color: 'bg-green-500', bgColor: 'bg-green-100', label: 'Excellent' },
22
+ const ratingConfig: Record<
23
+ ScoreRating,
24
+ { color: string; bgColor: string; label: string }
25
+ > = {
26
+ excellent: {
27
+ color: 'bg-green-500',
28
+ bgColor: 'bg-green-100',
29
+ label: 'Excellent',
30
+ },
19
31
  good: { color: 'bg-emerald-500', bgColor: 'bg-emerald-100', label: 'Good' },
20
32
  fair: { color: 'bg-amber-500', bgColor: 'bg-amber-100', label: 'Fair' },
21
- 'needs-work': { color: 'bg-orange-500', bgColor: 'bg-orange-100', label: 'Needs Work' },
33
+ 'needs-work': {
34
+ color: 'bg-orange-500',
35
+ bgColor: 'bg-orange-100',
36
+ label: 'Needs Work',
37
+ },
22
38
  critical: { color: 'bg-red-500', bgColor: 'bg-red-100', label: 'Critical' },
23
39
  };
24
40
 
@@ -26,11 +42,11 @@ const ratingConfig: Record<ScoreRating, { color: string; bgColor: string; label:
26
42
  function getRating(score: number): ScoreRating {
27
43
  const coreRating = getCoreRating(score);
28
44
  const ratingMap: Record<string, ScoreRating> = {
29
- 'Excellent': 'excellent',
30
- 'Good': 'good',
31
- 'Fair': 'fair',
45
+ Excellent: 'excellent',
46
+ Good: 'good',
47
+ Fair: 'fair',
32
48
  'Needs Work': 'needs-work',
33
- 'Critical': 'critical',
49
+ Critical: 'critical',
34
50
  };
35
51
  return ratingMap[coreRating] || 'critical';
36
52
  }
@@ -66,7 +82,11 @@ export function ScoreBar({
66
82
  </div>
67
83
  <div className={cn('w-full rounded-full bg-slate-200', sizes.height)}>
68
84
  <div
69
- className={cn('rounded-full transition-all duration-500', config.color, sizes.height)}
85
+ className={cn(
86
+ 'rounded-full transition-all duration-500',
87
+ config.color,
88
+ sizes.height
89
+ )}
70
90
  style={{ width: `${percentage}%` }}
71
91
  />
72
92
  </div>
@@ -85,15 +105,30 @@ export interface ScoreCardProps {
85
105
  className?: string;
86
106
  }
87
107
 
88
- export function ScoreCard({ score, title, breakdown, className }: ScoreCardProps) {
108
+ export function ScoreCard({
109
+ score,
110
+ title,
111
+ breakdown,
112
+ className,
113
+ }: ScoreCardProps) {
89
114
  const rating = getRating(score);
90
115
  const config = ratingConfig[rating];
91
116
 
92
117
  return (
93
- <div className={cn('rounded-xl border-2 border-slate-200 bg-white p-6 shadow-lg', className)}>
118
+ <div
119
+ className={cn(
120
+ 'rounded-xl border-2 border-slate-200 bg-white p-6 shadow-lg',
121
+ className
122
+ )}
123
+ >
94
124
  <div className="mb-4">
95
125
  <div className="text-4xl font-black text-slate-900">{score}/100</div>
96
- <div className={cn('text-lg font-bold', `text-${rating === 'excellent' ? 'green' : rating === 'good' ? 'emerald' : rating === 'fair' ? 'amber' : rating === 'needs-work' ? 'orange' : 'red'}-600`)}>
126
+ <div
127
+ className={cn(
128
+ 'text-lg font-bold',
129
+ `text-${rating === 'excellent' ? 'green' : rating === 'good' ? 'emerald' : rating === 'fair' ? 'amber' : rating === 'needs-work' ? 'orange' : 'red'}-600`
130
+ )}
131
+ >
97
132
  {config.label} Rating
98
133
  </div>
99
134
  {title && <p className="text-sm text-slate-600 mt-1">{title}</p>}
@@ -114,9 +149,11 @@ export function ScoreCard({ score, title, breakdown, className }: ScoreCardProps
114
149
 
115
150
  {breakdown && breakdown.length > 0 && (
116
151
  <div className="mt-4 text-xs text-slate-600 bg-slate-50 p-3 rounded-lg">
117
- <strong>Formula:</strong> {breakdown.map(item =>
118
- `${item.score}×${item.weight || 1}`
119
- ).join(' + ')} / 100 = {score}
152
+ <strong>Formula:</strong>{' '}
153
+ {breakdown
154
+ .map((item) => `${item.score}×${item.weight || 1}`)
155
+ .join(' + ')}{' '}
156
+ / 100 = {score}
120
157
  </div>
121
158
  )}
122
159
  </div>
@@ -1 +1,7 @@
1
- export { ScoreBar, ScoreCard, type ScoreBarProps, type ScoreCardProps, type ScoreRating } from './ScoreBar';
1
+ export {
2
+ ScoreBar,
3
+ ScoreCard,
4
+ type ScoreBarProps,
5
+ type ScoreCardProps,
6
+ type ScoreRating,
7
+ } from './ScoreBar';
@@ -41,7 +41,13 @@ export function ErrorDisplay({
41
41
  onClick={retry}
42
42
  className="mt-2 inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 transition-colors"
43
43
  >
44
- <svg className="h-4 w-4" fill="none" viewBox="0 0 24 24" strokeWidth="1.5" stroke="currentColor">
44
+ <svg
45
+ className="h-4 w-4"
46
+ fill="none"
47
+ viewBox="0 0 24 24"
48
+ strokeWidth="1.5"
49
+ stroke="currentColor"
50
+ >
45
51
  <path
46
52
  strokeLinecap="round"
47
53
  strokeLinejoin="round"
@@ -65,13 +71,20 @@ export interface EmptyStateProps {
65
71
  };
66
72
  }
67
73
 
68
- export function EmptyState({ title, description, icon, action }: EmptyStateProps) {
74
+ export function EmptyState({
75
+ title,
76
+ description,
77
+ icon,
78
+ action,
79
+ }: EmptyStateProps) {
69
80
  return (
70
81
  <div className="flex flex-col items-center justify-center min-h-[200px] gap-4 p-8">
71
82
  {icon && <div className="rounded-full bg-slate-100 p-3">{icon}</div>}
72
83
  <div className="text-center">
73
84
  <h3 className="text-lg font-semibold text-slate-900">{title}</h3>
74
- {description && <p className="mt-2 text-sm text-slate-500">{description}</p>}
85
+ {description && (
86
+ <p className="mt-2 text-sm text-slate-500">{description}</p>
87
+ )}
75
88
  </div>
76
89
  {action && (
77
90
  <button
@@ -83,4 +96,4 @@ export function EmptyState({ title, description, icon, action }: EmptyStateProps
83
96
  )}
84
97
  </div>
85
98
  );
86
- }
99
+ }
@@ -11,7 +11,10 @@ const sizeClasses = {
11
11
  lg: 'h-12 w-12',
12
12
  };
13
13
 
14
- export function LoadingSpinner({ size = 'md', className = '' }: LoadingSpinnerProps) {
14
+ export function LoadingSpinner({
15
+ size = 'md',
16
+ className = '',
17
+ }: LoadingSpinnerProps) {
15
18
  return (
16
19
  <div className={`flex items-center justify-center ${className}`}>
17
20
  <div
@@ -25,11 +28,13 @@ export interface LoadingOverlayProps {
25
28
  message?: string;
26
29
  }
27
30
 
28
- export function LoadingOverlay({ message = 'Loading...' }: LoadingOverlayProps) {
31
+ export function LoadingOverlay({
32
+ message = 'Loading...',
33
+ }: LoadingOverlayProps) {
29
34
  return (
30
35
  <div className="flex flex-col items-center justify-center min-h-[200px] gap-4">
31
36
  <LoadingSpinner size="lg" />
32
37
  <p className="text-sm text-slate-500 animate-pulse">{message}</p>
33
38
  </div>
34
39
  );
35
- }
40
+ }