@siteboon/claude-code-ui 1.8.2 → 1.8.3
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/dist/assets/index-BNGSzSdr.css +32 -0
- package/dist/assets/index-BZctMHnE.js +900 -0
- package/{index.html → dist/index.html} +4 -3
- package/package.json +6 -1
- package/server/database/auth.db +0 -0
- package/.env.example +0 -12
- package/.nvmrc +0 -1
- package/postcss.config.js +0 -6
- package/src/App.jsx +0 -751
- package/src/components/ChatInterface.jsx +0 -3485
- package/src/components/ClaudeLogo.jsx +0 -11
- package/src/components/ClaudeStatus.jsx +0 -107
- package/src/components/CodeEditor.jsx +0 -422
- package/src/components/CreateTaskModal.jsx +0 -88
- package/src/components/CursorLogo.jsx +0 -9
- package/src/components/DarkModeToggle.jsx +0 -35
- package/src/components/DiffViewer.jsx +0 -41
- package/src/components/ErrorBoundary.jsx +0 -73
- package/src/components/FileTree.jsx +0 -480
- package/src/components/GitPanel.jsx +0 -1283
- package/src/components/ImageViewer.jsx +0 -54
- package/src/components/LoginForm.jsx +0 -110
- package/src/components/MainContent.jsx +0 -577
- package/src/components/MicButton.jsx +0 -272
- package/src/components/MobileNav.jsx +0 -88
- package/src/components/NextTaskBanner.jsx +0 -695
- package/src/components/PRDEditor.jsx +0 -871
- package/src/components/ProtectedRoute.jsx +0 -44
- package/src/components/QuickSettingsPanel.jsx +0 -262
- package/src/components/Settings.jsx +0 -2023
- package/src/components/SetupForm.jsx +0 -135
- package/src/components/Shell.jsx +0 -663
- package/src/components/Sidebar.jsx +0 -1665
- package/src/components/StandaloneShell.jsx +0 -106
- package/src/components/TaskCard.jsx +0 -210
- package/src/components/TaskDetail.jsx +0 -406
- package/src/components/TaskIndicator.jsx +0 -108
- package/src/components/TaskList.jsx +0 -1054
- package/src/components/TaskMasterSetupWizard.jsx +0 -603
- package/src/components/TaskMasterStatus.jsx +0 -86
- package/src/components/TodoList.jsx +0 -91
- package/src/components/Tooltip.jsx +0 -91
- package/src/components/ui/badge.jsx +0 -31
- package/src/components/ui/button.jsx +0 -46
- package/src/components/ui/input.jsx +0 -19
- package/src/components/ui/scroll-area.jsx +0 -23
- package/src/contexts/AuthContext.jsx +0 -158
- package/src/contexts/TaskMasterContext.jsx +0 -324
- package/src/contexts/TasksSettingsContext.jsx +0 -95
- package/src/contexts/ThemeContext.jsx +0 -94
- package/src/contexts/WebSocketContext.jsx +0 -29
- package/src/hooks/useAudioRecorder.js +0 -109
- package/src/hooks/useVersionCheck.js +0 -39
- package/src/index.css +0 -822
- package/src/lib/utils.js +0 -6
- package/src/main.jsx +0 -10
- package/src/utils/api.js +0 -141
- package/src/utils/websocket.js +0 -109
- package/src/utils/whisper.js +0 -37
- package/tailwind.config.js +0 -63
- package/vite.config.js +0 -29
- /package/{public → dist}/convert-icons.md +0 -0
- /package/{public → dist}/favicon.png +0 -0
- /package/{public → dist}/favicon.svg +0 -0
- /package/{public → dist}/generate-icons.js +0 -0
- /package/{public → dist}/icons/claude-ai-icon.svg +0 -0
- /package/{public → dist}/icons/cursor.svg +0 -0
- /package/{public → dist}/icons/generate-icons.md +0 -0
- /package/{public → dist}/icons/icon-128x128.png +0 -0
- /package/{public → dist}/icons/icon-128x128.svg +0 -0
- /package/{public → dist}/icons/icon-144x144.png +0 -0
- /package/{public → dist}/icons/icon-144x144.svg +0 -0
- /package/{public → dist}/icons/icon-152x152.png +0 -0
- /package/{public → dist}/icons/icon-152x152.svg +0 -0
- /package/{public → dist}/icons/icon-192x192.png +0 -0
- /package/{public → dist}/icons/icon-192x192.svg +0 -0
- /package/{public → dist}/icons/icon-384x384.png +0 -0
- /package/{public → dist}/icons/icon-384x384.svg +0 -0
- /package/{public → dist}/icons/icon-512x512.png +0 -0
- /package/{public → dist}/icons/icon-512x512.svg +0 -0
- /package/{public → dist}/icons/icon-72x72.png +0 -0
- /package/{public → dist}/icons/icon-72x72.svg +0 -0
- /package/{public → dist}/icons/icon-96x96.png +0 -0
- /package/{public → dist}/icons/icon-96x96.svg +0 -0
- /package/{public → dist}/icons/icon-template.svg +0 -0
- /package/{public → dist}/logo.svg +0 -0
- /package/{public → dist}/manifest.json +0 -0
- /package/{public → dist}/screenshots/cli-selection.png +0 -0
- /package/{public → dist}/screenshots/desktop-main.png +0 -0
- /package/{public → dist}/screenshots/mobile-chat.png +0 -0
- /package/{public → dist}/screenshots/tools-modal.png +0 -0
- /package/{public → dist}/sw.js +0 -0
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Badge } from './ui/badge';
|
|
3
|
-
import { CheckCircle2, Clock, Circle } from 'lucide-react';
|
|
4
|
-
|
|
5
|
-
const TodoList = ({ todos, isResult = false }) => {
|
|
6
|
-
if (!todos || !Array.isArray(todos)) {
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const getStatusIcon = (status) => {
|
|
11
|
-
switch (status) {
|
|
12
|
-
case 'completed':
|
|
13
|
-
return <CheckCircle2 className="w-4 h-4 text-green-500 dark:text-green-400" />;
|
|
14
|
-
case 'in_progress':
|
|
15
|
-
return <Clock className="w-4 h-4 text-blue-500 dark:text-blue-400" />;
|
|
16
|
-
case 'pending':
|
|
17
|
-
default:
|
|
18
|
-
return <Circle className="w-4 h-4 text-gray-400 dark:text-gray-500" />;
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
const getStatusColor = (status) => {
|
|
23
|
-
switch (status) {
|
|
24
|
-
case 'completed':
|
|
25
|
-
return 'bg-green-100 dark:bg-green-900/30 text-green-800 dark:text-green-200 border-green-200 dark:border-green-800';
|
|
26
|
-
case 'in_progress':
|
|
27
|
-
return 'bg-blue-100 dark:bg-blue-900/30 text-blue-800 dark:text-blue-200 border-blue-200 dark:border-blue-800';
|
|
28
|
-
case 'pending':
|
|
29
|
-
default:
|
|
30
|
-
return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700';
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const getPriorityColor = (priority) => {
|
|
35
|
-
switch (priority) {
|
|
36
|
-
case 'high':
|
|
37
|
-
return 'bg-red-100 dark:bg-red-900/30 text-red-700 dark:text-red-300 border-red-200 dark:border-red-800';
|
|
38
|
-
case 'medium':
|
|
39
|
-
return 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-700 dark:text-yellow-300 border-yellow-200 dark:border-yellow-800';
|
|
40
|
-
case 'low':
|
|
41
|
-
default:
|
|
42
|
-
return 'bg-gray-100 dark:bg-gray-800 text-gray-600 dark:text-gray-400 border-gray-200 dark:border-gray-700';
|
|
43
|
-
}
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
return (
|
|
47
|
-
<div className="space-y-3">
|
|
48
|
-
{isResult && (
|
|
49
|
-
<div className="text-sm font-medium text-gray-700 dark:text-gray-300 mb-3">
|
|
50
|
-
Todo List ({todos.length} {todos.length === 1 ? 'item' : 'items'})
|
|
51
|
-
</div>
|
|
52
|
-
)}
|
|
53
|
-
|
|
54
|
-
{todos.map((todo, index) => (
|
|
55
|
-
<div
|
|
56
|
-
key={todo.id || `todo-${index}`}
|
|
57
|
-
className="flex items-start gap-3 p-3 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm hover:shadow-md dark:shadow-gray-900/50 transition-shadow"
|
|
58
|
-
>
|
|
59
|
-
<div className="flex-shrink-0 mt-0.5">
|
|
60
|
-
{getStatusIcon(todo.status)}
|
|
61
|
-
</div>
|
|
62
|
-
|
|
63
|
-
<div className="flex-1 min-w-0">
|
|
64
|
-
<div className="flex items-start justify-between gap-2 mb-2">
|
|
65
|
-
<p className={`text-sm font-medium ${todo.status === 'completed' ? 'line-through text-gray-500 dark:text-gray-400' : 'text-gray-900 dark:text-gray-100'}`}>
|
|
66
|
-
{todo.content}
|
|
67
|
-
</p>
|
|
68
|
-
|
|
69
|
-
<div className="flex gap-1 flex-shrink-0">
|
|
70
|
-
<Badge
|
|
71
|
-
variant="outline"
|
|
72
|
-
className={`text-xs px-2 py-0.5 ${getPriorityColor(todo.priority)}`}
|
|
73
|
-
>
|
|
74
|
-
{todo.priority}
|
|
75
|
-
</Badge>
|
|
76
|
-
<Badge
|
|
77
|
-
variant="outline"
|
|
78
|
-
className={`text-xs px-2 py-0.5 ${getStatusColor(todo.status)}`}
|
|
79
|
-
>
|
|
80
|
-
{todo.status.replace('_', ' ')}
|
|
81
|
-
</Badge>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
))}
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
export default TodoList;
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { cn } from '../lib/utils';
|
|
3
|
-
|
|
4
|
-
const Tooltip = ({
|
|
5
|
-
children,
|
|
6
|
-
content,
|
|
7
|
-
position = 'top',
|
|
8
|
-
className = '',
|
|
9
|
-
delay = 500
|
|
10
|
-
}) => {
|
|
11
|
-
const [isVisible, setIsVisible] = useState(false);
|
|
12
|
-
const [timeoutId, setTimeoutId] = useState(null);
|
|
13
|
-
|
|
14
|
-
const handleMouseEnter = () => {
|
|
15
|
-
const id = setTimeout(() => {
|
|
16
|
-
setIsVisible(true);
|
|
17
|
-
}, delay);
|
|
18
|
-
setTimeoutId(id);
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
const handleMouseLeave = () => {
|
|
22
|
-
if (timeoutId) {
|
|
23
|
-
clearTimeout(timeoutId);
|
|
24
|
-
setTimeoutId(null);
|
|
25
|
-
}
|
|
26
|
-
setIsVisible(false);
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
const getPositionClasses = () => {
|
|
30
|
-
switch (position) {
|
|
31
|
-
case 'top':
|
|
32
|
-
return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
|
|
33
|
-
case 'bottom':
|
|
34
|
-
return 'top-full left-1/2 transform -translate-x-1/2 mt-2';
|
|
35
|
-
case 'left':
|
|
36
|
-
return 'right-full top-1/2 transform -translate-y-1/2 mr-2';
|
|
37
|
-
case 'right':
|
|
38
|
-
return 'left-full top-1/2 transform -translate-y-1/2 ml-2';
|
|
39
|
-
default:
|
|
40
|
-
return 'bottom-full left-1/2 transform -translate-x-1/2 mb-2';
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const getArrowClasses = () => {
|
|
45
|
-
switch (position) {
|
|
46
|
-
case 'top':
|
|
47
|
-
return 'top-full left-1/2 transform -translate-x-1/2 border-t-gray-900 dark:border-t-gray-100';
|
|
48
|
-
case 'bottom':
|
|
49
|
-
return 'bottom-full left-1/2 transform -translate-x-1/2 border-b-gray-900 dark:border-b-gray-100';
|
|
50
|
-
case 'left':
|
|
51
|
-
return 'left-full top-1/2 transform -translate-y-1/2 border-l-gray-900 dark:border-l-gray-100';
|
|
52
|
-
case 'right':
|
|
53
|
-
return 'right-full top-1/2 transform -translate-y-1/2 border-r-gray-900 dark:border-r-gray-100';
|
|
54
|
-
default:
|
|
55
|
-
return 'top-full left-1/2 transform -translate-x-1/2 border-t-gray-900 dark:border-t-gray-100';
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
if (!content) {
|
|
60
|
-
return children;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<div
|
|
65
|
-
className="relative inline-block"
|
|
66
|
-
onMouseEnter={handleMouseEnter}
|
|
67
|
-
onMouseLeave={handleMouseLeave}
|
|
68
|
-
>
|
|
69
|
-
{children}
|
|
70
|
-
|
|
71
|
-
{isVisible && (
|
|
72
|
-
<div className={cn(
|
|
73
|
-
'absolute z-50 px-2 py-1 text-xs font-medium text-white bg-gray-900 dark:bg-gray-100 dark:text-gray-900 rounded shadow-lg whitespace-nowrap pointer-events-none',
|
|
74
|
-
'animate-in fade-in-0 zoom-in-95 duration-200',
|
|
75
|
-
getPositionClasses(),
|
|
76
|
-
className
|
|
77
|
-
)}>
|
|
78
|
-
{content}
|
|
79
|
-
|
|
80
|
-
{/* Arrow */}
|
|
81
|
-
<div className={cn(
|
|
82
|
-
'absolute w-0 h-0 border-4 border-transparent',
|
|
83
|
-
getArrowClasses()
|
|
84
|
-
)} />
|
|
85
|
-
</div>
|
|
86
|
-
)}
|
|
87
|
-
</div>
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
export default Tooltip;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva } from "class-variance-authority"
|
|
3
|
-
import { cn } from "../../lib/utils"
|
|
4
|
-
|
|
5
|
-
const badgeVariants = cva(
|
|
6
|
-
"inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
|
|
7
|
-
{
|
|
8
|
-
variants: {
|
|
9
|
-
variant: {
|
|
10
|
-
default:
|
|
11
|
-
"border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80",
|
|
12
|
-
secondary:
|
|
13
|
-
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
|
14
|
-
destructive:
|
|
15
|
-
"border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80",
|
|
16
|
-
outline: "text-foreground",
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
defaultVariants: {
|
|
20
|
-
variant: "default",
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
function Badge({ className, variant, ...props }) {
|
|
26
|
-
return (
|
|
27
|
-
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export { Badge, badgeVariants }
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cva } from "class-variance-authority"
|
|
3
|
-
import { cn } from "../../lib/utils"
|
|
4
|
-
|
|
5
|
-
const buttonVariants = cva(
|
|
6
|
-
"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",
|
|
7
|
-
{
|
|
8
|
-
variants: {
|
|
9
|
-
variant: {
|
|
10
|
-
default:
|
|
11
|
-
"bg-primary text-primary-foreground shadow hover:bg-primary/90",
|
|
12
|
-
destructive:
|
|
13
|
-
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90",
|
|
14
|
-
outline:
|
|
15
|
-
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground",
|
|
16
|
-
secondary:
|
|
17
|
-
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80",
|
|
18
|
-
ghost: "hover:bg-accent hover:text-accent-foreground",
|
|
19
|
-
link: "text-primary underline-offset-4 hover:underline",
|
|
20
|
-
},
|
|
21
|
-
size: {
|
|
22
|
-
default: "h-9 px-4 py-2",
|
|
23
|
-
sm: "h-8 rounded-md px-3 text-xs",
|
|
24
|
-
lg: "h-10 rounded-md px-8",
|
|
25
|
-
icon: "h-9 w-9",
|
|
26
|
-
},
|
|
27
|
-
},
|
|
28
|
-
defaultVariants: {
|
|
29
|
-
variant: "default",
|
|
30
|
-
size: "default",
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
const Button = React.forwardRef(({ className, variant, size, ...props }, ref) => {
|
|
36
|
-
return (
|
|
37
|
-
<button
|
|
38
|
-
className={cn(buttonVariants({ variant, size, className }))}
|
|
39
|
-
ref={ref}
|
|
40
|
-
{...props}
|
|
41
|
-
/>
|
|
42
|
-
)
|
|
43
|
-
})
|
|
44
|
-
Button.displayName = "Button"
|
|
45
|
-
|
|
46
|
-
export { Button, buttonVariants }
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cn } from "../../lib/utils"
|
|
3
|
-
|
|
4
|
-
const Input = React.forwardRef(({ className, type, ...props }, ref) => {
|
|
5
|
-
return (
|
|
6
|
-
<input
|
|
7
|
-
type={type}
|
|
8
|
-
className={cn(
|
|
9
|
-
"flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
|
10
|
-
className
|
|
11
|
-
)}
|
|
12
|
-
ref={ref}
|
|
13
|
-
{...props}
|
|
14
|
-
/>
|
|
15
|
-
)
|
|
16
|
-
})
|
|
17
|
-
Input.displayName = "Input"
|
|
18
|
-
|
|
19
|
-
export { Input }
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import * as React from "react"
|
|
2
|
-
import { cn } from "../../lib/utils"
|
|
3
|
-
|
|
4
|
-
const ScrollArea = React.forwardRef(({ className, children, ...props }, ref) => (
|
|
5
|
-
<div
|
|
6
|
-
ref={ref}
|
|
7
|
-
className={cn("relative overflow-hidden", className)}
|
|
8
|
-
{...props}
|
|
9
|
-
>
|
|
10
|
-
<div
|
|
11
|
-
className="h-full w-full rounded-[inherit] overflow-auto"
|
|
12
|
-
style={{
|
|
13
|
-
WebkitOverflowScrolling: 'touch',
|
|
14
|
-
touchAction: 'pan-y'
|
|
15
|
-
}}
|
|
16
|
-
>
|
|
17
|
-
{children}
|
|
18
|
-
</div>
|
|
19
|
-
</div>
|
|
20
|
-
))
|
|
21
|
-
ScrollArea.displayName = "ScrollArea"
|
|
22
|
-
|
|
23
|
-
export { ScrollArea }
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import React, { createContext, useContext, useEffect, useState } from 'react';
|
|
2
|
-
import { api } from '../utils/api';
|
|
3
|
-
|
|
4
|
-
const AuthContext = createContext({
|
|
5
|
-
user: null,
|
|
6
|
-
token: null,
|
|
7
|
-
login: () => {},
|
|
8
|
-
register: () => {},
|
|
9
|
-
logout: () => {},
|
|
10
|
-
isLoading: true,
|
|
11
|
-
needsSetup: false,
|
|
12
|
-
error: null
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
export const useAuth = () => {
|
|
16
|
-
const context = useContext(AuthContext);
|
|
17
|
-
if (!context) {
|
|
18
|
-
throw new Error('useAuth must be used within an AuthProvider');
|
|
19
|
-
}
|
|
20
|
-
return context;
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
export const AuthProvider = ({ children }) => {
|
|
24
|
-
const [user, setUser] = useState(null);
|
|
25
|
-
const [token, setToken] = useState(localStorage.getItem('auth-token'));
|
|
26
|
-
const [isLoading, setIsLoading] = useState(true);
|
|
27
|
-
const [needsSetup, setNeedsSetup] = useState(false);
|
|
28
|
-
const [error, setError] = useState(null);
|
|
29
|
-
|
|
30
|
-
// Check authentication status on mount
|
|
31
|
-
useEffect(() => {
|
|
32
|
-
checkAuthStatus();
|
|
33
|
-
}, []);
|
|
34
|
-
|
|
35
|
-
const checkAuthStatus = async () => {
|
|
36
|
-
try {
|
|
37
|
-
setIsLoading(true);
|
|
38
|
-
setError(null);
|
|
39
|
-
|
|
40
|
-
// Check if system needs setup
|
|
41
|
-
const statusResponse = await api.auth.status();
|
|
42
|
-
const statusData = await statusResponse.json();
|
|
43
|
-
|
|
44
|
-
if (statusData.needsSetup) {
|
|
45
|
-
setNeedsSetup(true);
|
|
46
|
-
setIsLoading(false);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// If we have a token, verify it
|
|
51
|
-
if (token) {
|
|
52
|
-
try {
|
|
53
|
-
const userResponse = await api.auth.user();
|
|
54
|
-
|
|
55
|
-
if (userResponse.ok) {
|
|
56
|
-
const userData = await userResponse.json();
|
|
57
|
-
setUser(userData.user);
|
|
58
|
-
setNeedsSetup(false);
|
|
59
|
-
} else {
|
|
60
|
-
// Token is invalid
|
|
61
|
-
localStorage.removeItem('auth-token');
|
|
62
|
-
setToken(null);
|
|
63
|
-
setUser(null);
|
|
64
|
-
}
|
|
65
|
-
} catch (error) {
|
|
66
|
-
console.error('Token verification failed:', error);
|
|
67
|
-
localStorage.removeItem('auth-token');
|
|
68
|
-
setToken(null);
|
|
69
|
-
setUser(null);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} catch (error) {
|
|
73
|
-
console.error('Auth status check failed:', error);
|
|
74
|
-
setError('Failed to check authentication status');
|
|
75
|
-
} finally {
|
|
76
|
-
setIsLoading(false);
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
const login = async (username, password) => {
|
|
81
|
-
try {
|
|
82
|
-
setError(null);
|
|
83
|
-
const response = await api.auth.login(username, password);
|
|
84
|
-
|
|
85
|
-
const data = await response.json();
|
|
86
|
-
|
|
87
|
-
if (response.ok) {
|
|
88
|
-
setToken(data.token);
|
|
89
|
-
setUser(data.user);
|
|
90
|
-
localStorage.setItem('auth-token', data.token);
|
|
91
|
-
return { success: true };
|
|
92
|
-
} else {
|
|
93
|
-
setError(data.error || 'Login failed');
|
|
94
|
-
return { success: false, error: data.error || 'Login failed' };
|
|
95
|
-
}
|
|
96
|
-
} catch (error) {
|
|
97
|
-
console.error('Login error:', error);
|
|
98
|
-
const errorMessage = 'Network error. Please try again.';
|
|
99
|
-
setError(errorMessage);
|
|
100
|
-
return { success: false, error: errorMessage };
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
const register = async (username, password) => {
|
|
105
|
-
try {
|
|
106
|
-
setError(null);
|
|
107
|
-
const response = await api.auth.register(username, password);
|
|
108
|
-
|
|
109
|
-
const data = await response.json();
|
|
110
|
-
|
|
111
|
-
if (response.ok) {
|
|
112
|
-
setToken(data.token);
|
|
113
|
-
setUser(data.user);
|
|
114
|
-
setNeedsSetup(false);
|
|
115
|
-
localStorage.setItem('auth-token', data.token);
|
|
116
|
-
return { success: true };
|
|
117
|
-
} else {
|
|
118
|
-
setError(data.error || 'Registration failed');
|
|
119
|
-
return { success: false, error: data.error || 'Registration failed' };
|
|
120
|
-
}
|
|
121
|
-
} catch (error) {
|
|
122
|
-
console.error('Registration error:', error);
|
|
123
|
-
const errorMessage = 'Network error. Please try again.';
|
|
124
|
-
setError(errorMessage);
|
|
125
|
-
return { success: false, error: errorMessage };
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
const logout = () => {
|
|
130
|
-
setToken(null);
|
|
131
|
-
setUser(null);
|
|
132
|
-
localStorage.removeItem('auth-token');
|
|
133
|
-
|
|
134
|
-
// Optional: Call logout endpoint for logging
|
|
135
|
-
if (token) {
|
|
136
|
-
api.auth.logout().catch(error => {
|
|
137
|
-
console.error('Logout endpoint error:', error);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const value = {
|
|
143
|
-
user,
|
|
144
|
-
token,
|
|
145
|
-
login,
|
|
146
|
-
register,
|
|
147
|
-
logout,
|
|
148
|
-
isLoading,
|
|
149
|
-
needsSetup,
|
|
150
|
-
error
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
return (
|
|
154
|
-
<AuthContext.Provider value={value}>
|
|
155
|
-
{children}
|
|
156
|
-
</AuthContext.Provider>
|
|
157
|
-
);
|
|
158
|
-
};
|