@auxiora/dashboard 1.0.0 → 1.3.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/dist/router.d.ts.map +1 -1
- package/dist/router.js +195 -49
- package/dist/router.js.map +1 -1
- package/dist/types.d.ts +66 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +10 -4
- package/dist-ui/assets/index-BfY0i5jw.css +0 -1
- package/dist-ui/assets/index-CXpk9mvw.js +0 -60
- package/dist-ui/icon.svg +0 -59
- package/dist-ui/index.html +0 -20
- package/src/auth.ts +0 -83
- package/src/cloud-types.ts +0 -63
- package/src/index.ts +0 -5
- package/src/router.ts +0 -2494
- package/src/types.ts +0 -269
- package/tests/auth.test.ts +0 -51
- package/tests/cloud-router.test.ts +0 -249
- package/tests/desktop-router.test.ts +0 -151
- package/tests/router.test.ts +0 -388
- package/tests/trust-router.test.ts +0 -170
- package/tsconfig.json +0 -12
- package/tsconfig.tsbuildinfo +0 -1
- package/ui/index.html +0 -19
- package/ui/node_modules/.bin/browserslist +0 -17
- package/ui/node_modules/.bin/tsc +0 -17
- package/ui/node_modules/.bin/tsserver +0 -17
- package/ui/node_modules/.bin/vite +0 -17
- package/ui/package.json +0 -23
- package/ui/public/icon.svg +0 -59
- package/ui/src/App.tsx +0 -63
- package/ui/src/api.ts +0 -238
- package/ui/src/components/ActivityFeed.tsx +0 -123
- package/ui/src/components/BehaviorHealth.tsx +0 -105
- package/ui/src/components/DataTable.tsx +0 -39
- package/ui/src/components/Layout.tsx +0 -160
- package/ui/src/components/PasswordStrength.tsx +0 -31
- package/ui/src/components/SetupProgress.tsx +0 -26
- package/ui/src/components/StatusBadge.tsx +0 -12
- package/ui/src/components/ThemeSelector.tsx +0 -39
- package/ui/src/contexts/ThemeContext.tsx +0 -58
- package/ui/src/hooks/useApi.ts +0 -19
- package/ui/src/hooks/usePolling.ts +0 -8
- package/ui/src/main.tsx +0 -16
- package/ui/src/pages/AuditLog.tsx +0 -36
- package/ui/src/pages/Behaviors.tsx +0 -426
- package/ui/src/pages/Chat.tsx +0 -688
- package/ui/src/pages/Login.tsx +0 -64
- package/ui/src/pages/Overview.tsx +0 -56
- package/ui/src/pages/Sessions.tsx +0 -26
- package/ui/src/pages/SettingsAmbient.tsx +0 -185
- package/ui/src/pages/SettingsConnections.tsx +0 -201
- package/ui/src/pages/SettingsNotifications.tsx +0 -241
- package/ui/src/pages/SetupAppearance.tsx +0 -45
- package/ui/src/pages/SetupChannels.tsx +0 -143
- package/ui/src/pages/SetupComplete.tsx +0 -31
- package/ui/src/pages/SetupConnections.tsx +0 -80
- package/ui/src/pages/SetupDashboardPassword.tsx +0 -50
- package/ui/src/pages/SetupIdentity.tsx +0 -68
- package/ui/src/pages/SetupPersonality.tsx +0 -78
- package/ui/src/pages/SetupProvider.tsx +0 -65
- package/ui/src/pages/SetupVault.tsx +0 -50
- package/ui/src/pages/SetupWelcome.tsx +0 -19
- package/ui/src/pages/UnlockVault.tsx +0 -56
- package/ui/src/pages/Webhooks.tsx +0 -158
- package/ui/src/pages/settings/Appearance.tsx +0 -63
- package/ui/src/pages/settings/Channels.tsx +0 -138
- package/ui/src/pages/settings/Identity.tsx +0 -61
- package/ui/src/pages/settings/Personality.tsx +0 -54
- package/ui/src/pages/settings/PersonalityEditor.tsx +0 -577
- package/ui/src/pages/settings/Provider.tsx +0 -537
- package/ui/src/pages/settings/Security.tsx +0 -111
- package/ui/src/styles/global.css +0 -2308
- package/ui/src/styles/themes/index.css +0 -7
- package/ui/src/styles/themes/monolith.css +0 -125
- package/ui/src/styles/themes/nebula.css +0 -90
- package/ui/src/styles/themes/neon.css +0 -149
- package/ui/src/styles/themes/polar.css +0 -151
- package/ui/src/styles/themes/signal.css +0 -163
- package/ui/src/styles/themes/terra.css +0 -146
- package/ui/tsconfig.json +0 -14
- package/ui/vite.config.ts +0 -20
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import { useApi } from '../hooks/useApi.js';
|
|
3
|
-
import { api } from '../api.js';
|
|
4
|
-
|
|
5
|
-
interface NotificationPreferences {
|
|
6
|
-
dnd: {
|
|
7
|
-
enabled: boolean;
|
|
8
|
-
schedule: { start: string; end: string };
|
|
9
|
-
};
|
|
10
|
-
urgencyKeywords: string[];
|
|
11
|
-
sources: {
|
|
12
|
-
email: boolean;
|
|
13
|
-
calendar: boolean;
|
|
14
|
-
github: boolean;
|
|
15
|
-
};
|
|
16
|
-
soundEnabled: boolean;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
interface Notification {
|
|
20
|
-
id: string;
|
|
21
|
-
title: string;
|
|
22
|
-
body: string;
|
|
23
|
-
source: string;
|
|
24
|
-
timestamp: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const DEFAULT_PREFS: NotificationPreferences = {
|
|
28
|
-
dnd: {
|
|
29
|
-
enabled: false,
|
|
30
|
-
schedule: { start: '22:00', end: '08:00' },
|
|
31
|
-
},
|
|
32
|
-
urgencyKeywords: [],
|
|
33
|
-
sources: {
|
|
34
|
-
email: true,
|
|
35
|
-
calendar: true,
|
|
36
|
-
github: true,
|
|
37
|
-
},
|
|
38
|
-
soundEnabled: true,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
export function SettingsNotifications() {
|
|
42
|
-
const { data: prefsData, loading: fetchingPrefs } = useApi(() => api.getNotificationPreferences(), []);
|
|
43
|
-
const { data: notifsData, loading: fetchingNotifs } = useApi(() => api.getNotifications(), []);
|
|
44
|
-
const [prefs, setPrefs] = useState<NotificationPreferences>(DEFAULT_PREFS);
|
|
45
|
-
const [notifications, setNotifications] = useState<Notification[]>([]);
|
|
46
|
-
const [saving, setSaving] = useState(false);
|
|
47
|
-
const [success, setSuccess] = useState('');
|
|
48
|
-
const [error, setError] = useState('');
|
|
49
|
-
const [keywordsText, setKeywordsText] = useState('');
|
|
50
|
-
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
if (prefsData?.data && Object.keys(prefsData.data).length > 0) {
|
|
53
|
-
const d = prefsData.data;
|
|
54
|
-
const merged: NotificationPreferences = {
|
|
55
|
-
dnd: {
|
|
56
|
-
enabled: d.dnd?.enabled ?? DEFAULT_PREFS.dnd.enabled,
|
|
57
|
-
schedule: {
|
|
58
|
-
start: d.dnd?.schedule?.start ?? DEFAULT_PREFS.dnd.schedule.start,
|
|
59
|
-
end: d.dnd?.schedule?.end ?? DEFAULT_PREFS.dnd.schedule.end,
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
urgencyKeywords: d.urgencyKeywords ?? DEFAULT_PREFS.urgencyKeywords,
|
|
63
|
-
sources: {
|
|
64
|
-
email: d.sources?.email ?? DEFAULT_PREFS.sources.email,
|
|
65
|
-
calendar: d.sources?.calendar ?? DEFAULT_PREFS.sources.calendar,
|
|
66
|
-
github: d.sources?.github ?? DEFAULT_PREFS.sources.github,
|
|
67
|
-
},
|
|
68
|
-
soundEnabled: d.soundEnabled ?? DEFAULT_PREFS.soundEnabled,
|
|
69
|
-
};
|
|
70
|
-
setPrefs(merged);
|
|
71
|
-
setKeywordsText((merged.urgencyKeywords || []).join(', '));
|
|
72
|
-
}
|
|
73
|
-
}, [prefsData]);
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
if (notifsData?.data) {
|
|
77
|
-
setNotifications(notifsData.data);
|
|
78
|
-
}
|
|
79
|
-
}, [notifsData]);
|
|
80
|
-
|
|
81
|
-
const handleSave = async () => {
|
|
82
|
-
setSaving(true);
|
|
83
|
-
setError('');
|
|
84
|
-
setSuccess('');
|
|
85
|
-
try {
|
|
86
|
-
const keywords = keywordsText
|
|
87
|
-
.split(',')
|
|
88
|
-
.map(k => k.trim())
|
|
89
|
-
.filter(Boolean);
|
|
90
|
-
await api.updateNotificationPreferences({ ...prefs, urgencyKeywords: keywords });
|
|
91
|
-
setSuccess('Notification settings saved successfully');
|
|
92
|
-
} catch (err: unknown) {
|
|
93
|
-
setError(err instanceof Error ? err.message : 'Failed to save settings');
|
|
94
|
-
} finally {
|
|
95
|
-
setSaving(false);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
const handleDismiss = async (id: string) => {
|
|
100
|
-
try {
|
|
101
|
-
await api.dismissNotification(id);
|
|
102
|
-
setNotifications(prev => prev.filter(n => n.id !== id));
|
|
103
|
-
} catch {
|
|
104
|
-
// ignore
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
if (fetchingPrefs || fetchingNotifs) return null;
|
|
109
|
-
|
|
110
|
-
return (
|
|
111
|
-
<div className="page">
|
|
112
|
-
<h2>Notification Settings</h2>
|
|
113
|
-
<div className="settings-form">
|
|
114
|
-
<div className="settings-section">
|
|
115
|
-
<h3>Do Not Disturb</h3>
|
|
116
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '1rem' }}>
|
|
117
|
-
<div
|
|
118
|
-
className={`toggle${prefs.dnd.enabled ? ' active' : ''}`}
|
|
119
|
-
onClick={() => setPrefs(prev => ({
|
|
120
|
-
...prev,
|
|
121
|
-
dnd: { ...prev.dnd, enabled: !prev.dnd.enabled },
|
|
122
|
-
}))}
|
|
123
|
-
/>
|
|
124
|
-
<span style={{ fontSize: '0.85rem', color: 'var(--text-secondary)' }}>
|
|
125
|
-
{prefs.dnd.enabled ? 'Enabled' : 'Disabled'}
|
|
126
|
-
</span>
|
|
127
|
-
</div>
|
|
128
|
-
{prefs.dnd.enabled && (
|
|
129
|
-
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center', marginBottom: '1rem' }}>
|
|
130
|
-
<div>
|
|
131
|
-
<label>Start</label>
|
|
132
|
-
<input
|
|
133
|
-
type="time"
|
|
134
|
-
value={prefs.dnd.schedule.start}
|
|
135
|
-
onChange={(e) => setPrefs(prev => ({
|
|
136
|
-
...prev,
|
|
137
|
-
dnd: { ...prev.dnd, schedule: { ...prev.dnd.schedule, start: e.target.value } },
|
|
138
|
-
}))}
|
|
139
|
-
/>
|
|
140
|
-
</div>
|
|
141
|
-
<div>
|
|
142
|
-
<label>End</label>
|
|
143
|
-
<input
|
|
144
|
-
type="time"
|
|
145
|
-
value={prefs.dnd.schedule.end}
|
|
146
|
-
onChange={(e) => setPrefs(prev => ({
|
|
147
|
-
...prev,
|
|
148
|
-
dnd: { ...prev.dnd, schedule: { ...prev.dnd.schedule, end: e.target.value } },
|
|
149
|
-
}))}
|
|
150
|
-
/>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
)}
|
|
154
|
-
</div>
|
|
155
|
-
|
|
156
|
-
<div className="settings-section">
|
|
157
|
-
<h3>Urgency Keywords</h3>
|
|
158
|
-
<label>Comma-separated keywords that mark a notification as urgent</label>
|
|
159
|
-
<textarea
|
|
160
|
-
rows={3}
|
|
161
|
-
value={keywordsText}
|
|
162
|
-
onChange={(e) => setKeywordsText(e.target.value)}
|
|
163
|
-
placeholder="urgent, critical, deadline, asap"
|
|
164
|
-
style={{ width: '100%', resize: 'vertical' }}
|
|
165
|
-
/>
|
|
166
|
-
</div>
|
|
167
|
-
|
|
168
|
-
<div className="settings-section">
|
|
169
|
-
<h3>Sources</h3>
|
|
170
|
-
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '0.5rem', marginBottom: '1rem' }}>
|
|
171
|
-
{(['email', 'calendar', 'github'] as const).map(source => (
|
|
172
|
-
<label key={source} style={{ display: 'flex', alignItems: 'center', gap: '0.4rem', cursor: 'pointer', fontSize: '0.85rem' }}>
|
|
173
|
-
<input
|
|
174
|
-
type="checkbox"
|
|
175
|
-
checked={prefs.sources[source]}
|
|
176
|
-
onChange={() => setPrefs(prev => ({
|
|
177
|
-
...prev,
|
|
178
|
-
sources: { ...prev.sources, [source]: !prev.sources[source] },
|
|
179
|
-
}))}
|
|
180
|
-
style={{ width: 'auto', marginBottom: 0 }}
|
|
181
|
-
/>
|
|
182
|
-
{source.charAt(0).toUpperCase() + source.slice(1)}
|
|
183
|
-
</label>
|
|
184
|
-
))}
|
|
185
|
-
</div>
|
|
186
|
-
</div>
|
|
187
|
-
|
|
188
|
-
<div className="settings-section">
|
|
189
|
-
<h3>Sound</h3>
|
|
190
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '0.75rem', marginBottom: '1rem' }}>
|
|
191
|
-
<div
|
|
192
|
-
className={`toggle${prefs.soundEnabled ? ' active' : ''}`}
|
|
193
|
-
onClick={() => setPrefs(prev => ({ ...prev, soundEnabled: !prev.soundEnabled }))}
|
|
194
|
-
/>
|
|
195
|
-
<span style={{ fontSize: '0.85rem', color: 'var(--text-secondary)' }}>
|
|
196
|
-
{prefs.soundEnabled ? 'Enabled' : 'Disabled'}
|
|
197
|
-
</span>
|
|
198
|
-
</div>
|
|
199
|
-
</div>
|
|
200
|
-
|
|
201
|
-
<button className="settings-btn" onClick={handleSave} disabled={saving}>
|
|
202
|
-
{saving ? 'Saving...' : 'Save Settings'}
|
|
203
|
-
</button>
|
|
204
|
-
{success && <div className="settings-success">{success}</div>}
|
|
205
|
-
{error && <div className="error">{error}</div>}
|
|
206
|
-
|
|
207
|
-
{notifications.length > 0 && (
|
|
208
|
-
<div className="settings-section" style={{ marginTop: '2rem' }}>
|
|
209
|
-
<h3>Recent Notifications</h3>
|
|
210
|
-
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}>
|
|
211
|
-
{notifications.map(n => (
|
|
212
|
-
<div key={n.id} style={{
|
|
213
|
-
display: 'flex',
|
|
214
|
-
justifyContent: 'space-between',
|
|
215
|
-
alignItems: 'center',
|
|
216
|
-
padding: '0.75rem',
|
|
217
|
-
background: 'var(--bg-secondary, #1a1a2e)',
|
|
218
|
-
borderRadius: '6px',
|
|
219
|
-
}}>
|
|
220
|
-
<div>
|
|
221
|
-
<div style={{ fontWeight: 500 }}>{n.title}</div>
|
|
222
|
-
<div style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>
|
|
223
|
-
{n.source} · {new Date(n.timestamp).toLocaleString()}
|
|
224
|
-
</div>
|
|
225
|
-
</div>
|
|
226
|
-
<button
|
|
227
|
-
className="settings-btn"
|
|
228
|
-
style={{ padding: '0.25rem 0.75rem', fontSize: '0.8rem' }}
|
|
229
|
-
onClick={() => handleDismiss(n.id)}
|
|
230
|
-
>
|
|
231
|
-
Dismiss
|
|
232
|
-
</button>
|
|
233
|
-
</div>
|
|
234
|
-
))}
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
)}
|
|
238
|
-
</div>
|
|
239
|
-
</div>
|
|
240
|
-
);
|
|
241
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
|
-
import { SetupProgress } from '../components/SetupProgress';
|
|
4
|
-
import { ThemeSelector } from '../components/ThemeSelector';
|
|
5
|
-
import { useTheme } from '../contexts/ThemeContext';
|
|
6
|
-
import { api } from '../api';
|
|
7
|
-
|
|
8
|
-
export function SetupAppearance() {
|
|
9
|
-
const { theme } = useTheme();
|
|
10
|
-
const [error, setError] = useState('');
|
|
11
|
-
const [loading, setLoading] = useState(false);
|
|
12
|
-
const navigate = useNavigate();
|
|
13
|
-
|
|
14
|
-
const handleContinue = async () => {
|
|
15
|
-
setLoading(true);
|
|
16
|
-
setError('');
|
|
17
|
-
try {
|
|
18
|
-
await api.updateAppearance(theme);
|
|
19
|
-
navigate('/setup/provider');
|
|
20
|
-
} catch (err: unknown) {
|
|
21
|
-
setError(err instanceof Error ? err.message : 'Failed to save appearance');
|
|
22
|
-
setLoading(false);
|
|
23
|
-
}
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div className="setup-page">
|
|
28
|
-
<SetupProgress currentStep={5} />
|
|
29
|
-
<div className="setup-card" style={{ maxWidth: 720 }}>
|
|
30
|
-
<h1>Appearance</h1>
|
|
31
|
-
<p className="subtitle">Choose a visual theme for Mission Control. You can change this later in settings.</p>
|
|
32
|
-
<ThemeSelector />
|
|
33
|
-
{error && <p className="error">{error}</p>}
|
|
34
|
-
<button
|
|
35
|
-
className="btn-primary"
|
|
36
|
-
onClick={handleContinue}
|
|
37
|
-
disabled={loading}
|
|
38
|
-
style={{ marginTop: '1.5rem', width: '100%' }}
|
|
39
|
-
>
|
|
40
|
-
{loading ? 'Saving...' : 'Continue'}
|
|
41
|
-
</button>
|
|
42
|
-
</div>
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
|
-
import { api } from '../api';
|
|
4
|
-
import { SetupProgress } from '../components/SetupProgress';
|
|
5
|
-
|
|
6
|
-
interface ChannelDef {
|
|
7
|
-
type: string;
|
|
8
|
-
name: string;
|
|
9
|
-
description: string;
|
|
10
|
-
fields: Array<{ key: string; label: string; type: string }>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const CHANNELS: ChannelDef[] = [
|
|
14
|
-
{ type: 'webchat', name: 'Webchat', description: 'Built-in, no setup needed', fields: [] },
|
|
15
|
-
{ type: 'discord', name: 'Discord', description: 'Connect a Discord bot', fields: [
|
|
16
|
-
{ key: 'botToken', label: 'Bot Token', type: 'password' },
|
|
17
|
-
]},
|
|
18
|
-
{ type: 'telegram', name: 'Telegram', description: 'Connect a Telegram bot', fields: [
|
|
19
|
-
{ key: 'botToken', label: 'Bot Token', type: 'password' },
|
|
20
|
-
]},
|
|
21
|
-
{ type: 'slack', name: 'Slack', description: 'Connect to a Slack workspace', fields: [
|
|
22
|
-
{ key: 'botToken', label: 'Bot Token', type: 'password' },
|
|
23
|
-
{ key: 'appToken', label: 'App Token', type: 'password' },
|
|
24
|
-
]},
|
|
25
|
-
{ type: 'matrix', name: 'Matrix', description: 'Connect to a Matrix homeserver', fields: [
|
|
26
|
-
{ key: 'homeserverUrl', label: 'Homeserver URL', type: 'text' },
|
|
27
|
-
{ key: 'userId', label: 'User ID', type: 'text' },
|
|
28
|
-
{ key: 'accessToken', label: 'Access Token', type: 'password' },
|
|
29
|
-
]},
|
|
30
|
-
{ type: 'signal', name: 'Signal', description: 'Connect via Signal CLI', fields: [
|
|
31
|
-
{ key: 'cliEndpoint', label: 'CLI Endpoint', type: 'text' },
|
|
32
|
-
{ key: 'phoneNumber', label: 'Phone Number', type: 'text' },
|
|
33
|
-
]},
|
|
34
|
-
{ type: 'teams', name: 'Teams', description: 'Connect to Microsoft Teams', fields: [
|
|
35
|
-
{ key: 'appId', label: 'App ID', type: 'text' },
|
|
36
|
-
{ key: 'appPassword', label: 'App Password', type: 'password' },
|
|
37
|
-
]},
|
|
38
|
-
{ type: 'whatsapp', name: 'WhatsApp', description: 'Connect via WhatsApp Business API', fields: [
|
|
39
|
-
{ key: 'phoneNumberId', label: 'Phone Number ID', type: 'text' },
|
|
40
|
-
{ key: 'accessToken', label: 'Access Token', type: 'password' },
|
|
41
|
-
{ key: 'verifyToken', label: 'Verify Token', type: 'text' },
|
|
42
|
-
]},
|
|
43
|
-
{ type: 'twilio', name: 'Twilio', description: 'Connect via Twilio SMS', fields: [
|
|
44
|
-
{ key: 'accountSid', label: 'Account SID', type: 'text' },
|
|
45
|
-
{ key: 'authToken', label: 'Auth Token', type: 'password' },
|
|
46
|
-
{ key: 'phoneNumber', label: 'Phone Number', type: 'text' },
|
|
47
|
-
]},
|
|
48
|
-
{ type: 'email', name: 'Email', description: 'Connect via IMAP/SMTP', fields: [
|
|
49
|
-
{ key: 'imapHost', label: 'IMAP Host', type: 'text' },
|
|
50
|
-
{ key: 'imapPort', label: 'IMAP Port', type: 'text' },
|
|
51
|
-
{ key: 'smtpHost', label: 'SMTP Host', type: 'text' },
|
|
52
|
-
{ key: 'smtpPort', label: 'SMTP Port', type: 'text' },
|
|
53
|
-
{ key: 'email', label: 'Email', type: 'text' },
|
|
54
|
-
{ key: 'password', label: 'Password', type: 'password' },
|
|
55
|
-
]},
|
|
56
|
-
];
|
|
57
|
-
|
|
58
|
-
export function SetupChannels() {
|
|
59
|
-
const [enabled, setEnabled] = useState<Record<string, boolean>>({ webchat: true });
|
|
60
|
-
const [credentials, setCredentials] = useState<Record<string, Record<string, string>>>({});
|
|
61
|
-
const [error, setError] = useState('');
|
|
62
|
-
const [loading, setLoading] = useState(false);
|
|
63
|
-
const navigate = useNavigate();
|
|
64
|
-
|
|
65
|
-
const toggleChannel = (type: string) => {
|
|
66
|
-
if (type === 'webchat') return;
|
|
67
|
-
setEnabled((prev) => ({ ...prev, [type]: !prev[type] }));
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const setField = (type: string, key: string, value: string) => {
|
|
71
|
-
setCredentials((prev) => ({
|
|
72
|
-
...prev,
|
|
73
|
-
[type]: { ...prev[type], [key]: value },
|
|
74
|
-
}));
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const handleSubmit = async () => {
|
|
78
|
-
setLoading(true);
|
|
79
|
-
setError('');
|
|
80
|
-
try {
|
|
81
|
-
const channels = CHANNELS
|
|
82
|
-
.filter((ch) => enabled[ch.type])
|
|
83
|
-
.map((ch) => ({
|
|
84
|
-
type: ch.type,
|
|
85
|
-
enabled: true,
|
|
86
|
-
credentials: credentials[ch.type],
|
|
87
|
-
}));
|
|
88
|
-
await api.setupChannels(channels);
|
|
89
|
-
navigate('/setup/connections');
|
|
90
|
-
} catch (err: unknown) {
|
|
91
|
-
setError(err instanceof Error ? err.message : 'Failed to save channels');
|
|
92
|
-
} finally {
|
|
93
|
-
setLoading(false);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
return (
|
|
98
|
-
<div className="setup-page">
|
|
99
|
-
<SetupProgress currentStep={7} />
|
|
100
|
-
<div className="setup-card" style={{ maxWidth: 720 }}>
|
|
101
|
-
<h1>Channels</h1>
|
|
102
|
-
<p className="subtitle">Enable the messaging channels you want to connect.</p>
|
|
103
|
-
<div className="channel-grid">
|
|
104
|
-
{CHANNELS.map((ch) => (
|
|
105
|
-
<div key={ch.type} className="channel-card">
|
|
106
|
-
<div className="channel-card-header">
|
|
107
|
-
<h3>{ch.name}</h3>
|
|
108
|
-
{ch.type === 'webchat' ? (
|
|
109
|
-
<span style={{ fontSize: '0.75rem', color: 'var(--success)' }}>Always on</span>
|
|
110
|
-
) : (
|
|
111
|
-
<div
|
|
112
|
-
className={`toggle${enabled[ch.type] ? ' active' : ''}`}
|
|
113
|
-
onClick={() => toggleChannel(ch.type)}
|
|
114
|
-
/>
|
|
115
|
-
)}
|
|
116
|
-
</div>
|
|
117
|
-
<p style={{ fontSize: '0.8rem', color: 'var(--text-secondary)', marginBottom: '0.5rem' }}>{ch.description}</p>
|
|
118
|
-
{enabled[ch.type] && ch.fields.length > 0 && (
|
|
119
|
-
<div className="channel-card-fields">
|
|
120
|
-
{ch.fields.map((f) => (
|
|
121
|
-
<div key={f.key}>
|
|
122
|
-
<label>{f.label}</label>
|
|
123
|
-
<input
|
|
124
|
-
type={f.type}
|
|
125
|
-
value={credentials[ch.type]?.[f.key] || ''}
|
|
126
|
-
onChange={(e) => setField(ch.type, f.key, e.target.value)}
|
|
127
|
-
/>
|
|
128
|
-
</div>
|
|
129
|
-
))}
|
|
130
|
-
</div>
|
|
131
|
-
)}
|
|
132
|
-
</div>
|
|
133
|
-
))}
|
|
134
|
-
</div>
|
|
135
|
-
<button className="setup-btn-primary" onClick={handleSubmit} disabled={loading}>
|
|
136
|
-
{loading ? 'Saving...' : 'Save & Continue'}
|
|
137
|
-
</button>
|
|
138
|
-
{error && <p className="error">{error}</p>}
|
|
139
|
-
<span className="skip-link" onClick={() => navigate('/setup/connections')}>Skip for now</span>
|
|
140
|
-
</div>
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect } from 'react';
|
|
2
|
-
import { api } from '../api';
|
|
3
|
-
|
|
4
|
-
export function SetupComplete() {
|
|
5
|
-
const [error, setError] = useState('');
|
|
6
|
-
|
|
7
|
-
useEffect(() => {
|
|
8
|
-
api.completeSetup().catch((err: unknown) => {
|
|
9
|
-
setError(err instanceof Error ? err.message : 'Failed to finalize setup');
|
|
10
|
-
});
|
|
11
|
-
}, []);
|
|
12
|
-
|
|
13
|
-
return (
|
|
14
|
-
<div className="setup-page">
|
|
15
|
-
<div className="setup-card" style={{ textAlign: 'center' }}>
|
|
16
|
-
<div className="setup-complete-check">{'\u2713'}</div>
|
|
17
|
-
<h1>Setup Complete!</h1>
|
|
18
|
-
<p className="subtitle">Your assistant is ready to go.</p>
|
|
19
|
-
{error && <p className="error">{error}</p>}
|
|
20
|
-
<div className="setup-complete-buttons">
|
|
21
|
-
<button className="setup-btn-primary" onClick={() => { window.location.href = '/'; }}>
|
|
22
|
-
Open Chat
|
|
23
|
-
</button>
|
|
24
|
-
<button className="setup-btn-secondary" onClick={() => { window.location.href = '/dashboard'; }}>
|
|
25
|
-
Go to Mission Control
|
|
26
|
-
</button>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
</div>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
|
-
import { api } from '../api';
|
|
4
|
-
import { SetupProgress } from '../components/SetupProgress';
|
|
5
|
-
|
|
6
|
-
export function SetupConnections() {
|
|
7
|
-
const [clientId, setClientId] = useState('');
|
|
8
|
-
const [clientSecret, setClientSecret] = useState('');
|
|
9
|
-
const [error, setError] = useState('');
|
|
10
|
-
const [loading, setLoading] = useState(false);
|
|
11
|
-
const navigate = useNavigate();
|
|
12
|
-
|
|
13
|
-
const handleConnect = async () => {
|
|
14
|
-
if (!clientId || !clientSecret) {
|
|
15
|
-
setError('Both Client ID and Client Secret are required');
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
setLoading(true);
|
|
19
|
-
setError('');
|
|
20
|
-
try {
|
|
21
|
-
const result = await api.saveConnectorCredentials('google-workspace', clientId, clientSecret);
|
|
22
|
-
if (result.oauthUrl) {
|
|
23
|
-
window.location.href = result.oauthUrl;
|
|
24
|
-
} else {
|
|
25
|
-
navigate('/setup/complete');
|
|
26
|
-
}
|
|
27
|
-
} catch (err: unknown) {
|
|
28
|
-
setError(err instanceof Error ? err.message : 'Failed to save credentials');
|
|
29
|
-
} finally {
|
|
30
|
-
setLoading(false);
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
return (
|
|
35
|
-
<div className="setup-page">
|
|
36
|
-
<SetupProgress currentStep={8} />
|
|
37
|
-
<div className="setup-card">
|
|
38
|
-
<h1>Connections</h1>
|
|
39
|
-
<p className="subtitle">Connect external services for calendar, email, and task intelligence.</p>
|
|
40
|
-
<div className="settings-section">
|
|
41
|
-
<h3>Google Workspace</h3>
|
|
42
|
-
<p style={{ fontSize: '0.8rem', color: 'var(--text-secondary)', marginBottom: '1rem' }}>
|
|
43
|
-
Connect your Google account to enable calendar awareness, email summaries, and task sync.
|
|
44
|
-
</p>
|
|
45
|
-
<label>Client ID</label>
|
|
46
|
-
<input
|
|
47
|
-
type="text"
|
|
48
|
-
value={clientId}
|
|
49
|
-
onChange={(e) => setClientId(e.target.value)}
|
|
50
|
-
placeholder="your-client-id.apps.googleusercontent.com"
|
|
51
|
-
/>
|
|
52
|
-
<label>Client Secret</label>
|
|
53
|
-
<input
|
|
54
|
-
type="password"
|
|
55
|
-
value={clientSecret}
|
|
56
|
-
onChange={(e) => setClientSecret(e.target.value)}
|
|
57
|
-
placeholder="GOCSPX-..."
|
|
58
|
-
/>
|
|
59
|
-
<button
|
|
60
|
-
type="button"
|
|
61
|
-
className="setup-btn-primary"
|
|
62
|
-
onClick={handleConnect}
|
|
63
|
-
disabled={loading}
|
|
64
|
-
>
|
|
65
|
-
{loading ? 'Connecting...' : 'Connect Google Account'}
|
|
66
|
-
</button>
|
|
67
|
-
{error && <p className="error">{error}</p>}
|
|
68
|
-
</div>
|
|
69
|
-
<button
|
|
70
|
-
type="button"
|
|
71
|
-
className="setup-btn-secondary"
|
|
72
|
-
onClick={() => navigate('/setup/complete')}
|
|
73
|
-
>
|
|
74
|
-
Continue without connecting
|
|
75
|
-
</button>
|
|
76
|
-
<span className="skip-link" onClick={() => navigate('/setup/complete')}>Skip for now</span>
|
|
77
|
-
</div>
|
|
78
|
-
</div>
|
|
79
|
-
);
|
|
80
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
|
-
import { api } from '../api';
|
|
4
|
-
import { SetupProgress } from '../components/SetupProgress';
|
|
5
|
-
import { PasswordStrength } from '../components/PasswordStrength';
|
|
6
|
-
|
|
7
|
-
export function SetupDashboardPassword() {
|
|
8
|
-
const [password, setPassword] = useState('');
|
|
9
|
-
const [confirm, setConfirm] = useState('');
|
|
10
|
-
const [error, setError] = useState('');
|
|
11
|
-
const [loading, setLoading] = useState(false);
|
|
12
|
-
const navigate = useNavigate();
|
|
13
|
-
|
|
14
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
15
|
-
e.preventDefault();
|
|
16
|
-
if (password.length < 8) { setError('Password must be at least 8 characters'); return; }
|
|
17
|
-
if (password !== confirm) { setError('Passwords do not match'); return; }
|
|
18
|
-
setLoading(true);
|
|
19
|
-
setError('');
|
|
20
|
-
try {
|
|
21
|
-
await api.setupDashboardPassword(password);
|
|
22
|
-
navigate('/setup/identity');
|
|
23
|
-
} catch (err: unknown) {
|
|
24
|
-
setError(err instanceof Error ? err.message : 'Failed to set password');
|
|
25
|
-
} finally {
|
|
26
|
-
setLoading(false);
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
return (
|
|
31
|
-
<div className="setup-page">
|
|
32
|
-
<SetupProgress currentStep={2} />
|
|
33
|
-
<div className="setup-card">
|
|
34
|
-
<h1>Mission Control Password</h1>
|
|
35
|
-
<p className="subtitle">This password protects Mission Control. You'll use it to log in.</p>
|
|
36
|
-
<form onSubmit={handleSubmit}>
|
|
37
|
-
<label>Password</label>
|
|
38
|
-
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} autoFocus />
|
|
39
|
-
<PasswordStrength password={password} />
|
|
40
|
-
<label>Confirm password</label>
|
|
41
|
-
<input type="password" value={confirm} onChange={(e) => setConfirm(e.target.value)} />
|
|
42
|
-
<button type="submit" className="setup-btn-primary" disabled={loading}>
|
|
43
|
-
{loading ? 'Saving...' : 'Set Password'}
|
|
44
|
-
</button>
|
|
45
|
-
{error && <p className="error">{error}</p>}
|
|
46
|
-
</form>
|
|
47
|
-
</div>
|
|
48
|
-
</div>
|
|
49
|
-
);
|
|
50
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { useState, useMemo } from 'react';
|
|
2
|
-
import { useNavigate } from 'react-router-dom';
|
|
3
|
-
import { api } from '../api';
|
|
4
|
-
import { SetupProgress } from '../components/SetupProgress';
|
|
5
|
-
|
|
6
|
-
const AI_NAMES = [
|
|
7
|
-
'Nova', 'Atlas', 'Jasper', 'Echo', 'Sage', 'Onyx', 'Iris', 'Phoenix',
|
|
8
|
-
'Kai', 'Luna', 'Orion', 'Zephyr', 'Rune', 'Pixel', 'Nyx', 'Sol',
|
|
9
|
-
'Cleo', 'Juno', 'Vex', 'Aura',
|
|
10
|
-
];
|
|
11
|
-
|
|
12
|
-
export function SetupIdentity() {
|
|
13
|
-
const randomName = useMemo(() => AI_NAMES[Math.floor(Math.random() * AI_NAMES.length)], []);
|
|
14
|
-
const [name, setName] = useState('');
|
|
15
|
-
const [pronouns, setPronouns] = useState('she/her');
|
|
16
|
-
const [vibe, setVibe] = useState('');
|
|
17
|
-
const [error, setError] = useState('');
|
|
18
|
-
const [loading, setLoading] = useState(false);
|
|
19
|
-
const navigate = useNavigate();
|
|
20
|
-
|
|
21
|
-
const handleSubmit = async (e: React.FormEvent) => {
|
|
22
|
-
e.preventDefault();
|
|
23
|
-
setLoading(true);
|
|
24
|
-
setError('');
|
|
25
|
-
try {
|
|
26
|
-
await api.setupIdentity(name || randomName, pronouns, vibe || undefined);
|
|
27
|
-
if (vibe) localStorage.setItem('auxiora_setup_vibe', vibe);
|
|
28
|
-
navigate('/setup/personality');
|
|
29
|
-
} catch (err: unknown) {
|
|
30
|
-
setError(err instanceof Error ? err.message : 'Failed to save identity');
|
|
31
|
-
} finally {
|
|
32
|
-
setLoading(false);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<div className="setup-page">
|
|
38
|
-
<SetupProgress currentStep={3} />
|
|
39
|
-
<div className="setup-card">
|
|
40
|
-
<h1>Agent Identity</h1>
|
|
41
|
-
<p className="subtitle">Give your AI assistant a name, pronouns, and vibe.</p>
|
|
42
|
-
<form onSubmit={handleSubmit}>
|
|
43
|
-
<label>Agent name</label>
|
|
44
|
-
<input type="text" value={name} onChange={(e) => setName(e.target.value)} placeholder={randomName} autoFocus />
|
|
45
|
-
<label>Pronouns</label>
|
|
46
|
-
<select value={pronouns} onChange={(e) => setPronouns(e.target.value)}>
|
|
47
|
-
<option value="she/her">she/her</option>
|
|
48
|
-
<option value="he/him">he/him</option>
|
|
49
|
-
<option value="they/them">they/them</option>
|
|
50
|
-
<option value="it/its">it/its</option>
|
|
51
|
-
</select>
|
|
52
|
-
<label>Describe the vibe</label>
|
|
53
|
-
<textarea
|
|
54
|
-
value={vibe}
|
|
55
|
-
onChange={(e) => setVibe(e.target.value)}
|
|
56
|
-
placeholder="e.g. chill and witty, professional and sharp, warm like a best friend"
|
|
57
|
-
rows={3}
|
|
58
|
-
style={{ width: '100%', resize: 'vertical' }}
|
|
59
|
-
/>
|
|
60
|
-
<button type="submit" className="setup-btn-primary" disabled={loading}>
|
|
61
|
-
{loading ? 'Saving...' : 'Continue'}
|
|
62
|
-
</button>
|
|
63
|
-
{error && <p className="error">{error}</p>}
|
|
64
|
-
</form>
|
|
65
|
-
</div>
|
|
66
|
-
</div>
|
|
67
|
-
);
|
|
68
|
-
}
|