@makemore/agent-frontend 2.4.0 → 2.7.1
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/LICENSE +82 -21
- package/README.md +10 -1
- package/dist/chat-widget.css +442 -1
- package/dist/chat-widget.js +357 -160
- package/package.json +1 -2
- package/src/components/ChatWidget.js +73 -27
- package/src/components/Message.js +156 -4
- package/src/components/MessageList.js +10 -4
- package/src/components/TaskList.js +183 -0
- package/src/hooks/useChat.js +92 -4
- package/src/hooks/useTasks.js +164 -0
- package/src/utils/api.js +24 -9
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useTasks hook - manages task list state and API interactions
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { useState, useCallback, useEffect } from 'preact/hooks';
|
|
6
|
+
|
|
7
|
+
export function useTasks(config, api) {
|
|
8
|
+
const [taskList, setTaskList] = useState(null);
|
|
9
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
10
|
+
const [error, setError] = useState(null);
|
|
11
|
+
|
|
12
|
+
// Build tasks API path
|
|
13
|
+
const tasksPath = config.apiPaths?.tasks || '/api/agent/tasks/';
|
|
14
|
+
|
|
15
|
+
// Load the user's task list
|
|
16
|
+
const loadTaskList = useCallback(async () => {
|
|
17
|
+
setIsLoading(true);
|
|
18
|
+
setError(null);
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const url = `${config.backendUrl}${tasksPath}`;
|
|
22
|
+
const response = await fetch(url, api.getFetchOptions({ method: 'GET' }));
|
|
23
|
+
|
|
24
|
+
if (response.ok) {
|
|
25
|
+
const data = await response.json();
|
|
26
|
+
setTaskList(data);
|
|
27
|
+
} else {
|
|
28
|
+
const errorData = await response.json().catch(() => ({}));
|
|
29
|
+
setError(errorData.error || 'Failed to load tasks');
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error('[useTasks] Failed to load task list:', err);
|
|
33
|
+
setError('Failed to load tasks');
|
|
34
|
+
} finally {
|
|
35
|
+
setIsLoading(false);
|
|
36
|
+
}
|
|
37
|
+
}, [config.backendUrl, tasksPath, api]);
|
|
38
|
+
|
|
39
|
+
// Add a task
|
|
40
|
+
const addTask = useCallback(async (taskData) => {
|
|
41
|
+
if (!taskList) return null;
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const url = `${config.backendUrl}${tasksPath}${taskList.id}/add_task/`;
|
|
45
|
+
const response = await fetch(url, api.getFetchOptions({
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: { 'Content-Type': 'application/json' },
|
|
48
|
+
body: JSON.stringify(taskData),
|
|
49
|
+
}));
|
|
50
|
+
|
|
51
|
+
if (response.ok) {
|
|
52
|
+
const newTask = await response.json();
|
|
53
|
+
// Refresh the task list to get updated state
|
|
54
|
+
await loadTaskList();
|
|
55
|
+
return newTask;
|
|
56
|
+
} else {
|
|
57
|
+
const errorData = await response.json().catch(() => ({}));
|
|
58
|
+
setError(errorData.error || 'Failed to add task');
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error('[useTasks] Failed to add task:', err);
|
|
63
|
+
setError('Failed to add task');
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}, [config.backendUrl, tasksPath, taskList, api, loadTaskList]);
|
|
67
|
+
|
|
68
|
+
// Update a task
|
|
69
|
+
const updateTask = useCallback(async (taskId, updates) => {
|
|
70
|
+
if (!taskList) return null;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
const url = `${config.backendUrl}${tasksPath}${taskList.id}/update_task/${taskId}/`;
|
|
74
|
+
const response = await fetch(url, api.getFetchOptions({
|
|
75
|
+
method: 'PUT',
|
|
76
|
+
headers: { 'Content-Type': 'application/json' },
|
|
77
|
+
body: JSON.stringify(updates),
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
if (response.ok) {
|
|
81
|
+
const updatedTask = await response.json();
|
|
82
|
+
// Refresh the task list
|
|
83
|
+
await loadTaskList();
|
|
84
|
+
return updatedTask;
|
|
85
|
+
} else {
|
|
86
|
+
const errorData = await response.json().catch(() => ({}));
|
|
87
|
+
setError(errorData.error || 'Failed to update task');
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
} catch (err) {
|
|
91
|
+
console.error('[useTasks] Failed to update task:', err);
|
|
92
|
+
setError('Failed to update task');
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
}, [config.backendUrl, tasksPath, taskList, api, loadTaskList]);
|
|
96
|
+
|
|
97
|
+
// Remove a task
|
|
98
|
+
const removeTask = useCallback(async (taskId) => {
|
|
99
|
+
if (!taskList) return false;
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const url = `${config.backendUrl}${tasksPath}${taskList.id}/remove_task/${taskId}/`;
|
|
103
|
+
const response = await fetch(url, api.getFetchOptions({
|
|
104
|
+
method: 'DELETE',
|
|
105
|
+
}));
|
|
106
|
+
|
|
107
|
+
if (response.ok) {
|
|
108
|
+
await loadTaskList();
|
|
109
|
+
return true;
|
|
110
|
+
} else {
|
|
111
|
+
const errorData = await response.json().catch(() => ({}));
|
|
112
|
+
setError(errorData.error || 'Failed to remove task');
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
} catch (err) {
|
|
116
|
+
console.error('[useTasks] Failed to remove task:', err);
|
|
117
|
+
setError('Failed to remove task');
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
}, [config.backendUrl, tasksPath, taskList, api, loadTaskList]);
|
|
121
|
+
|
|
122
|
+
// Clear all tasks
|
|
123
|
+
const clearTasks = useCallback(async () => {
|
|
124
|
+
if (!taskList) return false;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const url = `${config.backendUrl}${tasksPath}${taskList.id}/clear/`;
|
|
128
|
+
const response = await fetch(url, api.getFetchOptions({
|
|
129
|
+
method: 'POST',
|
|
130
|
+
}));
|
|
131
|
+
|
|
132
|
+
if (response.ok) {
|
|
133
|
+
await loadTaskList();
|
|
134
|
+
return true;
|
|
135
|
+
} else {
|
|
136
|
+
const errorData = await response.json().catch(() => ({}));
|
|
137
|
+
setError(errorData.error || 'Failed to clear tasks');
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
} catch (err) {
|
|
141
|
+
console.error('[useTasks] Failed to clear tasks:', err);
|
|
142
|
+
setError('Failed to clear tasks');
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
}, [config.backendUrl, tasksPath, taskList, api, loadTaskList]);
|
|
146
|
+
|
|
147
|
+
// Clear error
|
|
148
|
+
const clearError = useCallback(() => setError(null), []);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
taskList,
|
|
152
|
+
tasks: taskList?.tasks || [],
|
|
153
|
+
progress: taskList?.progress || { total: 0, completed: 0, percent_complete: 0 },
|
|
154
|
+
isLoading,
|
|
155
|
+
error,
|
|
156
|
+
loadTaskList,
|
|
157
|
+
addTask,
|
|
158
|
+
updateTask,
|
|
159
|
+
removeTask,
|
|
160
|
+
clearTasks,
|
|
161
|
+
clearError,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
package/src/utils/api.js
CHANGED
|
@@ -66,25 +66,31 @@ export function createApiClient(config, getState, setState) {
|
|
|
66
66
|
const getFetchOptions = (options = {}, overrideToken = null) => {
|
|
67
67
|
const strategy = getAuthStrategy();
|
|
68
68
|
const fetchOptions = { ...options };
|
|
69
|
-
|
|
69
|
+
const authHeaders = getAuthHeaders(overrideToken);
|
|
70
|
+
console.log('[ChatWidget] getFetchOptions - strategy:', strategy, 'overrideToken:', overrideToken, 'authHeaders:', authHeaders);
|
|
71
|
+
fetchOptions.headers = { ...fetchOptions.headers, ...authHeaders };
|
|
70
72
|
if (strategy === 'session') fetchOptions.credentials = 'include';
|
|
71
73
|
return fetchOptions;
|
|
72
74
|
};
|
|
73
75
|
|
|
74
|
-
const getOrCreateSession = async () => {
|
|
76
|
+
const getOrCreateSession = async (forceRefresh = false) => {
|
|
75
77
|
const strategy = getAuthStrategy();
|
|
76
78
|
const state = getState();
|
|
77
|
-
|
|
79
|
+
const storageKey = config.anonymousTokenKey || config.sessionTokenKey;
|
|
80
|
+
|
|
78
81
|
if (strategy !== 'anonymous') return config.authToken || state.authToken;
|
|
79
|
-
if (state.authToken) return state.authToken;
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
// If not forcing refresh, try existing tokens
|
|
84
|
+
if (!forceRefresh) {
|
|
85
|
+
if (state.authToken) return state.authToken;
|
|
86
|
+
const stored = state.storage?.get(storageKey);
|
|
87
|
+
if (stored) {
|
|
88
|
+
setState(s => ({ ...s, authToken: stored }));
|
|
89
|
+
return stored;
|
|
90
|
+
}
|
|
86
91
|
}
|
|
87
92
|
|
|
93
|
+
// Fetch new token (either first time or forced refresh)
|
|
88
94
|
try {
|
|
89
95
|
const endpoint = config.anonymousSessionEndpoint || config.apiPaths.anonymousSession;
|
|
90
96
|
const response = await fetch(`${config.backendUrl}${endpoint}`, {
|
|
@@ -103,11 +109,20 @@ export function createApiClient(config, getState, setState) {
|
|
|
103
109
|
return null;
|
|
104
110
|
};
|
|
105
111
|
|
|
112
|
+
// Clear stored token (call on 401 to force refresh)
|
|
113
|
+
const clearSession = () => {
|
|
114
|
+
const storageKey = config.anonymousTokenKey || config.sessionTokenKey;
|
|
115
|
+
const state = getState();
|
|
116
|
+
setState(s => ({ ...s, authToken: null }));
|
|
117
|
+
state.storage?.set(storageKey, null);
|
|
118
|
+
};
|
|
119
|
+
|
|
106
120
|
return {
|
|
107
121
|
getAuthStrategy,
|
|
108
122
|
getAuthHeaders,
|
|
109
123
|
getFetchOptions,
|
|
110
124
|
getOrCreateSession,
|
|
125
|
+
clearSession,
|
|
111
126
|
transformRequest,
|
|
112
127
|
transformResponse,
|
|
113
128
|
};
|