@mongoosejs/studio 0.2.13 → 0.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/backend/actions/ChatMessage/executeScript.js +5 -1
- package/backend/actions/ChatThread/createChatMessage.js +4 -1
- package/backend/actions/ChatThread/streamChatMessage.js +4 -2
- package/backend/actions/Dashboard/updateDashboard.js +2 -2
- package/backend/actions/Task/getTaskOverview.js +102 -0
- package/backend/actions/Task/getTasks.js +85 -45
- package/backend/actions/Task/index.js +1 -0
- package/eslint.config.js +4 -1
- package/frontend/public/app.js +25025 -762
- package/frontend/public/dark-theme.css +365 -0
- package/frontend/public/images/mongoose-studio.svg +4 -0
- package/frontend/public/index.html +21 -1
- package/frontend/public/style.css +5 -7
- package/frontend/public/theme-variables.css +294 -0
- package/frontend/public/tw.css +348 -213
- package/frontend/src/_util/dateRange.js +82 -0
- package/frontend/src/ace-editor/ace-editor.html +4 -0
- package/frontend/src/ace-editor/ace-editor.js +95 -0
- package/frontend/src/aceEditor.js +69 -0
- package/frontend/src/api.js +6 -0
- package/frontend/src/chat/chat-message/chat-message.html +1 -1
- package/frontend/src/chat/chat-message/chat-message.js +1 -1
- package/frontend/src/chat/chat-message-script/chat-message-script.html +54 -42
- package/frontend/src/chat/chat-message-script/chat-message-script.js +6 -55
- package/frontend/src/chat/chat.html +68 -39
- package/frontend/src/chat/chat.js +26 -2
- package/frontend/src/clone-document/clone-document.html +7 -2
- package/frontend/src/clone-document/clone-document.js +1 -8
- package/frontend/src/create-dashboard/create-dashboard.html +11 -6
- package/frontend/src/create-dashboard/create-dashboard.js +0 -7
- package/frontend/src/create-document/create-document.html +15 -9
- package/frontend/src/create-document/create-document.js +5 -12
- package/frontend/src/dashboard/dashboard.html +14 -12
- package/frontend/src/dashboard/dashboard.js +21 -4
- package/frontend/src/dashboard/edit-dashboard/edit-dashboard.html +13 -7
- package/frontend/src/dashboard/edit-dashboard/edit-dashboard.js +16 -23
- package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.html +19 -17
- package/frontend/src/dashboard-result/dashboard-chart/dashboard-chart.js +97 -2
- package/frontend/src/dashboard-result/dashboard-map/dashboard-map.js +27 -3
- package/frontend/src/dashboard-result/dashboard-result.html +3 -3
- package/frontend/src/dashboard-result/dashboard-result.js +3 -0
- package/frontend/src/dashboard-result/dashboard-table/dashboard-table.html +34 -0
- package/frontend/src/dashboard-result/dashboard-table/dashboard-table.js +37 -0
- package/frontend/src/dashboards/dashboards.html +101 -109
- package/frontend/src/dashboards/dashboards.js +25 -1
- package/frontend/src/detail-default/detail-default.html +2 -2
- package/frontend/src/detail-default/detail-default.js +24 -3
- package/frontend/src/document/confirm-changes/confirm-changes.html +1 -1
- package/frontend/src/document/confirm-delete/confirm-delete.html +1 -1
- package/frontend/src/document/document.css +1 -1
- package/frontend/src/document/document.html +28 -28
- package/frontend/src/document/execute-script/execute-script.html +20 -21
- package/frontend/src/document/execute-script/execute-script.js +1 -43
- package/frontend/src/document-details/document-details.css +4 -9
- package/frontend/src/document-details/document-details.html +34 -33
- package/frontend/src/document-details/document-details.js +2 -53
- package/frontend/src/document-details/document-property/document-property.html +12 -12
- package/frontend/src/edit-array/edit-array.html +7 -6
- package/frontend/src/edit-array/edit-array.js +10 -50
- package/frontend/src/edit-boolean/edit-boolean.html +12 -12
- package/frontend/src/edit-date/edit-date.html +2 -2
- package/frontend/src/edit-default/edit-default.html +1 -1
- package/frontend/src/edit-string/edit-string.html +3 -3
- package/frontend/src/edit-subdocument/edit-subdocument.html +5 -3
- package/frontend/src/edit-subdocument/edit-subdocument.js +1 -15
- package/frontend/src/export-query-results/export-query-results.html +3 -3
- package/frontend/src/json-node/json-node.html +3 -3
- package/frontend/src/list-json/json-node.html +1 -1
- package/frontend/src/models/document-search/document-search.html +3 -3
- package/frontend/src/models/model-switcher/model-switcher.html +53 -0
- package/frontend/src/models/model-switcher/model-switcher.js +123 -0
- package/frontend/src/models/models.css +3 -10
- package/frontend/src/models/models.html +146 -80
- package/frontend/src/models/models.js +116 -7
- package/frontend/src/navbar/navbar.html +157 -97
- package/frontend/src/navbar/navbar.js +31 -12
- package/frontend/src/routes.js +1 -1
- package/frontend/src/splash/splash.html +5 -5
- package/frontend/src/task-by-name/task-by-name.html +77 -7
- package/frontend/src/task-by-name/task-by-name.js +84 -9
- package/frontend/src/task-single/task-single.html +29 -29
- package/frontend/src/task-single/task-single.js +10 -10
- package/frontend/src/tasks/task-details/task-details.html +43 -43
- package/frontend/src/tasks/task-details/task-details.js +9 -3
- package/frontend/src/tasks/tasks.html +36 -35
- package/frontend/src/tasks/tasks.js +27 -143
- package/frontend/src/team/new-invitation/new-invitation.html +8 -8
- package/frontend/src/team/team.html +27 -27
- package/frontend/src/update-document/update-document.html +7 -2
- package/frontend/src/update-document/update-document.js +2 -11
- package/package.json +3 -1
- package/tailwind.config.js +75 -11
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
<div class="flex" style="height: calc(100vh - 55px); height: calc(100dvh - 55px)">
|
|
2
2
|
<button
|
|
3
3
|
type="button"
|
|
4
|
-
class="fixed top-[65px] left-0 cursor-pointer bg-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
aria-label="
|
|
8
|
-
title="
|
|
9
|
-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0
|
|
4
|
+
class="fixed top-[65px] left-0 cursor-pointer bg-muted rounded-r-md p-1"
|
|
5
|
+
:class="hideSidebar === true ? 'z-[10001]' : hideSidebar === false ? 'z-10' : 'z-30 lg:hidden'"
|
|
6
|
+
@click="hideSidebar = hideSidebar === false ? true : false"
|
|
7
|
+
aria-label="Toggle chat history"
|
|
8
|
+
title="Toggle chat history">
|
|
9
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5 text-content-tertiary">
|
|
10
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m5.25 4.5 7.5 7.5-7.5 7.5m6-15 7.5 7.5-7.5 7.5" />
|
|
11
|
+
</svg>
|
|
10
12
|
</button>
|
|
11
13
|
<button
|
|
12
|
-
class="fixed top-[65px] right-4 z-10 p-2 rounded-md shadow bg-
|
|
13
|
-
:class="hasWorkspace ? 'text-
|
|
14
|
+
class="fixed top-[65px] right-4 z-10 p-2 rounded-md shadow bg-surface"
|
|
15
|
+
:class="hasWorkspace ? 'text-content-secondary hover:bg-muted' : 'text-gray-300 cursor-not-allowed bg-page'"
|
|
14
16
|
@click="toggleShareThread"
|
|
15
17
|
:disabled="!hasWorkspace || !chatThreadId || sharingThread"
|
|
16
18
|
aria-label="Share thread with workspace"
|
|
@@ -21,41 +23,68 @@
|
|
|
21
23
|
</button>
|
|
22
24
|
<!-- Sidebar: Chat Threads -->
|
|
23
25
|
<aside
|
|
24
|
-
class="bg-
|
|
25
|
-
:class="hideSidebar === false ? 'translate-x-0' : hideSidebar === true ? '-translate-x-full lg:
|
|
26
|
-
style="z-index:
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
class="bg-page border-r overflow-hidden h-full transition-all duration-300 ease-in-out z-20 w-64 fixed lg:relative shrink-0 flex flex-col"
|
|
27
|
+
:class="hideSidebar === false ? 'translate-x-0' : hideSidebar === true ? '-translate-x-full lg:translate-x-0 lg:!w-0 lg:!border-r-0' : '-translate-x-full lg:translate-x-0'"
|
|
28
|
+
style="z-index: 1000">
|
|
29
|
+
<!-- Search + New Thread -->
|
|
30
|
+
<div class="p-3 shrink-0">
|
|
31
|
+
<div class="relative flex items-center gap-2">
|
|
32
|
+
<div class="relative flex-1">
|
|
33
|
+
<svg class="absolute left-2.5 top-1/2 -translate-y-1/2 h-4 w-4 text-gray-400" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
|
|
34
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607Z" />
|
|
35
|
+
</svg>
|
|
36
|
+
<input
|
|
37
|
+
v-model="threadSearch"
|
|
38
|
+
type="text"
|
|
39
|
+
placeholder="Search threads..."
|
|
40
|
+
@keydown.esc="threadSearch = ''"
|
|
41
|
+
class="w-full rounded-md border border-edge bg-surface py-1.5 pl-8 pr-3 text-sm text-content placeholder:text-gray-400 focus:border-edge-strong focus:outline-none focus:ring-1 focus:ring-gray-300"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
<async-button
|
|
45
|
+
@click="createNewThread"
|
|
46
|
+
class="shrink-0 rounded-md border border-edge bg-surface p-1.5 text-content-tertiary hover:bg-muted hover:text-content-secondary"
|
|
47
|
+
title="New thread"
|
|
48
|
+
>
|
|
49
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-4 h-4">
|
|
50
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
|
|
51
|
+
</svg>
|
|
52
|
+
</async-button>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
<!-- Thread list (scrollable) -->
|
|
56
|
+
<nav class="flex-1 overflow-y-auto px-2 pb-2">
|
|
57
|
+
<div class="px-2 py-1 text-xs font-semibold text-gray-400 uppercase tracking-wider">Threads</div>
|
|
58
|
+
<div v-if="status === 'loaded' && filteredThreads.length === 0" class="px-2 py-2 text-sm text-content-tertiary">
|
|
59
|
+
<template v-if="threadSearch.trim()">No threads match "{{threadSearch}}"</template>
|
|
60
|
+
<template v-else>No threads yet</template>
|
|
61
|
+
</div>
|
|
62
|
+
<ul role="list">
|
|
63
|
+
<li
|
|
64
|
+
v-for="thread in filteredThreads"
|
|
65
|
+
:key="thread._id"
|
|
66
|
+
@click="selectThread(thread._id)"
|
|
67
|
+
class="rounded-md py-2 px-2 cursor-pointer mb-0.5"
|
|
68
|
+
:class="thread._id === chatThreadId ? 'bg-gray-200 text-content' : 'hover:bg-muted text-content-secondary'"
|
|
69
|
+
>
|
|
70
|
+
<div class="text-sm font-medium truncate">{{ thread.title || 'Untitled Thread' }}</div>
|
|
71
|
+
<div class="text-xs text-gray-400 mt-0.5">{{ formatThreadDate(thread.updatedAt || thread.createdAt) }}</div>
|
|
72
|
+
</li>
|
|
73
|
+
</ul>
|
|
74
|
+
</nav>
|
|
75
|
+
<!-- Bottom toolbar -->
|
|
76
|
+
<div class="shrink-0 border-t border-edge bg-page px-2 py-1.5 flex items-center gap-1">
|
|
29
77
|
<button
|
|
78
|
+
type="button"
|
|
30
79
|
@click="hideSidebar = true"
|
|
31
|
-
class="
|
|
32
|
-
|
|
80
|
+
class="rounded p-1.5 text-gray-400 hover:text-gray-600 hover:bg-muted"
|
|
81
|
+
title="Hide sidebar"
|
|
33
82
|
>
|
|
34
|
-
<svg xmlns="http://www.w3.org/2000/svg"
|
|
83
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4">
|
|
84
|
+
<path stroke-linecap="round" stroke-linejoin="round" d="m18.75 4.5-7.5 7.5 7.5 7.5m-6-15L5.25 12l7.5 7.5" />
|
|
85
|
+
</svg>
|
|
35
86
|
</button>
|
|
36
87
|
</div>
|
|
37
|
-
<div class="p-4 w-64">
|
|
38
|
-
<async-button
|
|
39
|
-
@click="createNewThread"
|
|
40
|
-
class="w-full bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
|
|
41
|
-
>
|
|
42
|
-
Create New Thread
|
|
43
|
-
</async-button>
|
|
44
|
-
</div>
|
|
45
|
-
<div v-if="status === 'loaded' && chatThreads.length === 0" class="p-4 text-sm text-gray-700">
|
|
46
|
-
No threads yet
|
|
47
|
-
</div>
|
|
48
|
-
<ul v-if="status === 'loaded'" class="w-full">
|
|
49
|
-
<li
|
|
50
|
-
v-for="thread in chatThreads"
|
|
51
|
-
:key="thread._id"
|
|
52
|
-
@click="selectThread(thread._id)"
|
|
53
|
-
class="w-full p-2 hover:bg-ultramarine-100 cursor-pointer text-sm text-gray-700"
|
|
54
|
-
:class="{ 'bg-ultramarine-200 text-gray-900': thread._id === chatThreadId }"
|
|
55
|
-
>
|
|
56
|
-
{{ thread.title || 'Untitled Thread' }}
|
|
57
|
-
</li>
|
|
58
|
-
</ul>
|
|
59
88
|
</aside>
|
|
60
89
|
|
|
61
90
|
<!-- Main Chat Area -->
|
|
@@ -63,7 +92,7 @@
|
|
|
63
92
|
<div class="flex-1 overflow-y-auto p-6 space-y-4" ref="messagesContainer">
|
|
64
93
|
<div v-if="chatMessages?.length === 0">
|
|
65
94
|
<div class="flex items-center w-full h-full justify-center py-3 mb-4">
|
|
66
|
-
<p class="mx-4 font-bold text-
|
|
95
|
+
<p class="mx-4 font-bold text-content">
|
|
67
96
|
Ask Mongoose Studio to analyze your data or generate a script
|
|
68
97
|
</p>
|
|
69
98
|
</div>
|
|
@@ -14,7 +14,8 @@ module.exports = {
|
|
|
14
14
|
chatThreads: [],
|
|
15
15
|
chatMessages: [],
|
|
16
16
|
hideSidebar: null,
|
|
17
|
-
sharingThread: false
|
|
17
|
+
sharingThread: false,
|
|
18
|
+
threadSearch: ''
|
|
18
19
|
}),
|
|
19
20
|
methods: {
|
|
20
21
|
async sendMessage() {
|
|
@@ -125,13 +126,29 @@ module.exports = {
|
|
|
125
126
|
this.$router.push('/chat/' + threadId);
|
|
126
127
|
},
|
|
127
128
|
styleForMessage(message) {
|
|
128
|
-
return message.role === 'user' ? 'bg-
|
|
129
|
+
return message.role === 'user' ? 'bg-muted' : '';
|
|
129
130
|
},
|
|
130
131
|
async createNewThread() {
|
|
131
132
|
const { chatThread } = await api.ChatThread.createChatThread();
|
|
132
133
|
this.$toast.success('Chat thread created!');
|
|
133
134
|
this.$router.push('/chat/' + chatThread._id);
|
|
134
135
|
},
|
|
136
|
+
formatThreadDate(dateStr) {
|
|
137
|
+
if (!dateStr) return '';
|
|
138
|
+
const date = new Date(dateStr);
|
|
139
|
+
const now = new Date();
|
|
140
|
+
const diff = now - date;
|
|
141
|
+
const oneDay = 24 * 60 * 60 * 1000;
|
|
142
|
+
const isToday = date.toDateString() === now.toDateString();
|
|
143
|
+
const isYesterday = new Date(now - oneDay).toDateString() === date.toDateString();
|
|
144
|
+
const timeStr = date.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' });
|
|
145
|
+
if (isToday) return 'Today, ' + timeStr;
|
|
146
|
+
if (isYesterday) return 'Yesterday, ' + timeStr;
|
|
147
|
+
if (diff < 7 * oneDay) {
|
|
148
|
+
return date.toLocaleDateString(undefined, { weekday: 'long' }) + ', ' + timeStr;
|
|
149
|
+
}
|
|
150
|
+
return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) + ', ' + timeStr;
|
|
151
|
+
},
|
|
135
152
|
async toggleShareThread() {
|
|
136
153
|
if (!this.chatThreadId || !this.hasWorkspace) {
|
|
137
154
|
return;
|
|
@@ -165,6 +182,13 @@ module.exports = {
|
|
|
165
182
|
},
|
|
166
183
|
sharedWithWorkspace() {
|
|
167
184
|
return !!this.currentThread?.sharingOptions?.sharedWithWorkspace;
|
|
185
|
+
},
|
|
186
|
+
filteredThreads() {
|
|
187
|
+
const search = this.threadSearch.trim().toLowerCase();
|
|
188
|
+
if (!search) {
|
|
189
|
+
return this.chatThreads;
|
|
190
|
+
}
|
|
191
|
+
return this.chatThreads.filter(t => (t.title || 'Untitled Thread').toLowerCase().includes(search));
|
|
168
192
|
}
|
|
169
193
|
},
|
|
170
194
|
async mounted() {
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
<div>
|
|
2
2
|
<div class="mb-2">
|
|
3
|
-
<
|
|
3
|
+
<ace-editor
|
|
4
|
+
v-model="documentData"
|
|
5
|
+
mode="javascript"
|
|
6
|
+
:line-numbers="true"
|
|
7
|
+
class="border border-edge h-[300px] w-full"
|
|
8
|
+
/>
|
|
4
9
|
</div>
|
|
5
|
-
<button @click="cloneDocument()" class="rounded-md bg-
|
|
10
|
+
<button @click="cloneDocument()" class="rounded-md bg-primary px-2.5 py-1.5 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600">Submit</button>
|
|
6
11
|
<div v-if="errors.length > 0" class="rounded-md bg-red-50 p-4 mt-1">
|
|
7
12
|
<div class="flex">
|
|
8
13
|
<div class="flex-shrink-0">
|
|
@@ -22,13 +22,12 @@ module.exports = app => app.component('clone-document', {
|
|
|
22
22
|
data: function() {
|
|
23
23
|
return {
|
|
24
24
|
documentData: '',
|
|
25
|
-
editor: null,
|
|
26
25
|
errors: []
|
|
27
26
|
};
|
|
28
27
|
},
|
|
29
28
|
methods: {
|
|
30
29
|
async cloneDocument() {
|
|
31
|
-
const data = EJSON.serialize(eval(`(${this.
|
|
30
|
+
const data = EJSON.serialize(eval(`(${this.documentData})`));
|
|
32
31
|
try {
|
|
33
32
|
const { doc } = await api.Model.createDocument({ model: this.currentModel, data });
|
|
34
33
|
this.errors.length = 0;
|
|
@@ -64,11 +63,5 @@ module.exports = app => app.component('clone-document', {
|
|
|
64
63
|
}
|
|
65
64
|
|
|
66
65
|
this.documentData = JSON.stringify(filteredDoc, null, 2);
|
|
67
|
-
this.$refs.codeEditor.value = this.documentData;
|
|
68
|
-
this.editor = CodeMirror.fromTextArea(this.$refs.codeEditor, {
|
|
69
|
-
mode: 'javascript',
|
|
70
|
-
lineNumbers: true,
|
|
71
|
-
smartIndent: false
|
|
72
|
-
});
|
|
73
66
|
}
|
|
74
67
|
});
|
|
@@ -1,19 +1,24 @@
|
|
|
1
1
|
<div>
|
|
2
|
-
<div class="mt-4 text-
|
|
2
|
+
<div class="mt-4 text-content font-semibold">
|
|
3
3
|
Create New Dashboard
|
|
4
4
|
</div>
|
|
5
5
|
<div class="mt-4">
|
|
6
|
-
<label class="block text-sm font-medium leading-6 text-
|
|
6
|
+
<label class="block text-sm font-medium leading-6 text-content">Title</label>
|
|
7
7
|
<div class="mt-2">
|
|
8
8
|
<div class="w-full flex rounded-md shadow-sm ring-1 ring-inset ring-gray-300 focus-within:ring-2 focus-within:ring-inset focus-within:ring-teal-600">
|
|
9
|
-
<input type="text" v-model="title" class="outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-
|
|
9
|
+
<input type="text" v-model="title" class="outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-content placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" placeholder="ACME-123">
|
|
10
10
|
</div>
|
|
11
11
|
</div>
|
|
12
12
|
</div>
|
|
13
13
|
<div class="my-4">
|
|
14
|
-
<label class="block text-sm font-medium leading-6 text-
|
|
15
|
-
<div class="border border-
|
|
16
|
-
<
|
|
14
|
+
<label class="block text-sm font-medium leading-6 text-content">Code</label>
|
|
15
|
+
<div class="border border-edge">
|
|
16
|
+
<ace-editor
|
|
17
|
+
v-model="code"
|
|
18
|
+
mode="javascript"
|
|
19
|
+
:line-numbers="true"
|
|
20
|
+
class="h-[300px] w-full"
|
|
21
|
+
/>
|
|
17
22
|
</div>
|
|
18
23
|
</div>
|
|
19
24
|
<async-button
|
|
@@ -15,7 +15,6 @@ module.exports = app => app.component('create-dashboard', {
|
|
|
15
15
|
},
|
|
16
16
|
methods: {
|
|
17
17
|
async createDashboard() {
|
|
18
|
-
this.code = this._editor.getValue();
|
|
19
18
|
const { dashboard } = await api.Dashboard.createDashboard({ code: this.code, title: this.title }).catch(err => {
|
|
20
19
|
if (err.response?.data?.message) {
|
|
21
20
|
console.log(err.response.data);
|
|
@@ -31,11 +30,5 @@ module.exports = app => app.component('create-dashboard', {
|
|
|
31
30
|
this.$toast.success('Dashboard created!');
|
|
32
31
|
this.$emit('close', dashboard);
|
|
33
32
|
}
|
|
34
|
-
},
|
|
35
|
-
mounted: function() {
|
|
36
|
-
this._editor = CodeMirror.fromTextArea(this.$refs.codeEditor, {
|
|
37
|
-
mode: 'javascript',
|
|
38
|
-
lineNumbers: true
|
|
39
|
-
});
|
|
40
33
|
}
|
|
41
34
|
});
|
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
<div>
|
|
1
|
+
<div class="px-1">
|
|
2
2
|
<div class="mb-4">
|
|
3
|
-
<label class="block text-sm font-bold text-
|
|
3
|
+
<label class="block text-sm font-bold text-content">AI Mode</label>
|
|
4
4
|
<div class="mt-2 flex flex-col gap-2 sm:flex-row sm:items-center">
|
|
5
5
|
<input
|
|
6
6
|
v-model="aiPrompt"
|
|
7
7
|
type="text"
|
|
8
8
|
placeholder="Describe the document you'd like to create..."
|
|
9
9
|
@keydown.enter.prevent="requestAiSuggestion()"
|
|
10
|
-
class="w-full rounded-md border border-
|
|
10
|
+
class="w-full rounded-md border border-edge-strong px-3 py-2 text-sm shadow-sm focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary"
|
|
11
11
|
/>
|
|
12
12
|
<button
|
|
13
13
|
@click="requestAiSuggestion()"
|
|
14
14
|
:disabled="aiStreaming || !aiPrompt.trim()"
|
|
15
|
-
class="inline-flex items-center justify-center rounded-md bg-
|
|
15
|
+
class="inline-flex items-center justify-center rounded-md bg-primary px-3 py-2 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover disabled:cursor-not-allowed disabled:opacity-50"
|
|
16
16
|
>
|
|
17
17
|
{{ aiStreaming ? 'Generating...' : 'Generate' }}
|
|
18
18
|
</button>
|
|
19
19
|
</div>
|
|
20
|
-
<p class="mt-2 text-xs text-
|
|
20
|
+
<p class="mt-2 text-xs text-content-tertiary">Use AI to draft the document. You can accept or reject the suggestion once it finishes.</p>
|
|
21
21
|
<div v-if="aiSuggestionReady" class="mt-3 flex flex-wrap gap-2">
|
|
22
22
|
<button
|
|
23
23
|
@click="acceptAiSuggestion()"
|
|
@@ -27,17 +27,23 @@
|
|
|
27
27
|
</button>
|
|
28
28
|
<button
|
|
29
29
|
@click="rejectAiSuggestion()"
|
|
30
|
-
class="rounded-md bg-
|
|
30
|
+
class="rounded-md bg-muted px-2.5 py-1.5 text-sm font-semibold text-content-secondary shadow-sm hover:bg-muted"
|
|
31
31
|
>
|
|
32
32
|
Reject suggestion
|
|
33
33
|
</button>
|
|
34
34
|
</div>
|
|
35
35
|
</div>
|
|
36
36
|
<div class="mb-2">
|
|
37
|
-
<label class="block text-sm font-bold text-
|
|
38
|
-
<
|
|
37
|
+
<label class="block text-sm font-bold text-content">Document to Create</label>
|
|
38
|
+
<ace-editor
|
|
39
|
+
ref="codeEditor"
|
|
40
|
+
v-model="documentData"
|
|
41
|
+
mode="javascript"
|
|
42
|
+
:line-numbers="true"
|
|
43
|
+
class="h-[300px] w-full mt-2"
|
|
44
|
+
/>
|
|
39
45
|
</div>
|
|
40
|
-
<button @click="createDocument()" class="rounded-md bg-
|
|
46
|
+
<button @click="createDocument()" class="rounded-md bg-primary px-2.5 py-1.5 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600">Submit</button>
|
|
41
47
|
<div v-if="errors.length > 0" class="rounded-md bg-red-50 p-4 mt-1">
|
|
42
48
|
<div class="flex">
|
|
43
49
|
<div class="flex-shrink-0">
|
|
@@ -22,7 +22,6 @@ module.exports = app => app.component('create-document', {
|
|
|
22
22
|
data: function() {
|
|
23
23
|
return {
|
|
24
24
|
documentData: '',
|
|
25
|
-
editor: null,
|
|
26
25
|
errors: [],
|
|
27
26
|
aiPrompt: '',
|
|
28
27
|
aiSuggestion: '',
|
|
@@ -41,7 +40,7 @@ module.exports = app => app.component('create-document', {
|
|
|
41
40
|
return;
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
this.aiOriginalDocument = this.
|
|
43
|
+
this.aiOriginalDocument = this.documentData;
|
|
45
44
|
this.aiSuggestion = '';
|
|
46
45
|
this.aiSuggestionReady = false;
|
|
47
46
|
this.aiStreaming = true;
|
|
@@ -56,10 +55,10 @@ module.exports = app => app.component('create-document', {
|
|
|
56
55
|
this.aiSuggestion += event.textPart;
|
|
57
56
|
}
|
|
58
57
|
}
|
|
59
|
-
this.
|
|
58
|
+
this.$refs.codeEditor.setValue(this.aiSuggestion);
|
|
60
59
|
this.aiSuggestionReady = true;
|
|
61
60
|
} catch (err) {
|
|
62
|
-
this.
|
|
61
|
+
this.$refs.codeEditor.setValue(this.aiOriginalDocument);
|
|
63
62
|
this.$toast.error('Failed to generate a document suggestion.');
|
|
64
63
|
throw err;
|
|
65
64
|
} finally {
|
|
@@ -72,13 +71,13 @@ module.exports = app => app.component('create-document', {
|
|
|
72
71
|
this.aiOriginalDocument = '';
|
|
73
72
|
},
|
|
74
73
|
rejectAiSuggestion() {
|
|
75
|
-
this.
|
|
74
|
+
this.$refs.codeEditor.setValue(this.aiOriginalDocument);
|
|
76
75
|
this.aiSuggestionReady = false;
|
|
77
76
|
this.aiSuggestion = '';
|
|
78
77
|
this.aiOriginalDocument = '';
|
|
79
78
|
},
|
|
80
79
|
async createDocument() {
|
|
81
|
-
const data = EJSON.serialize(eval(`(${this.
|
|
80
|
+
const data = EJSON.serialize(eval(`(${this.documentData})`));
|
|
82
81
|
try {
|
|
83
82
|
const { doc } = await api.Model.createDocument({ model: this.currentModel, data });
|
|
84
83
|
this.errors.length = 0;
|
|
@@ -104,11 +103,5 @@ module.exports = app => app.component('create-document', {
|
|
|
104
103
|
this.documentData += ` ${requiredPaths[i].path}: ${isLast ? '' : ','}\n`;
|
|
105
104
|
}
|
|
106
105
|
this.documentData += '}';
|
|
107
|
-
this.$refs.codeEditor.value = this.documentData;
|
|
108
|
-
this.editor = CodeMirror.fromTextArea(this.$refs.codeEditor, {
|
|
109
|
-
mode: 'javascript',
|
|
110
|
-
lineNumbers: true,
|
|
111
|
-
smartIndent: false
|
|
112
|
-
});
|
|
113
106
|
}
|
|
114
107
|
});
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
<div class="dashboard
|
|
1
|
+
<div class="dashboard-root">
|
|
2
|
+
<div class="dashboard px-1">
|
|
2
3
|
<div v-if="status === 'loading'" class="max-w-5xl mx-auto text-center">
|
|
3
4
|
<img src="images/loader.gif" class="inline mt-10">
|
|
4
5
|
</div>
|
|
5
6
|
<div v-if="dashboard && status !== 'loading'" class="max-w-5xl mx-auto">
|
|
6
7
|
<div class="flex items-center w-full" v-if="!showEditor">
|
|
7
|
-
<h2 class="mt-4 mb-4 text-
|
|
8
|
+
<h2 class="mt-4 mb-4 text-content font-semibold text-xl grow shrink">{{title}}</h2>
|
|
8
9
|
<div class="flex gap-2">
|
|
9
10
|
<button
|
|
10
11
|
@click="showEditor = true"
|
|
11
12
|
type="button"
|
|
12
13
|
:disabled="status === 'evaluating'"
|
|
13
|
-
class="flex items-center rounded-md bg-
|
|
14
|
+
class="flex items-center rounded-md bg-primary px-2.5 py-1.5 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:bg-gray-600">
|
|
14
15
|
<img src="images/edit.svg" class="inline h-[1.25em] mr-1" /> Edit
|
|
15
16
|
</button>
|
|
16
17
|
|
|
@@ -18,7 +19,7 @@
|
|
|
18
19
|
@click="evaluateDashboard"
|
|
19
20
|
type="button"
|
|
20
21
|
:disabled="status === 'evaluating'"
|
|
21
|
-
class="flex items-center rounded-md bg-
|
|
22
|
+
class="flex items-center rounded-md bg-primary px-4 py-1.5 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:bg-gray-600"
|
|
22
23
|
>
|
|
23
24
|
<svg class="inline h-[1.25em] mr-1" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960" fill="currentColor"><path d="m670-140 160-100-160-100v200ZM240-600h480v-80H240v80ZM720-40q-83 0-141.5-58.5T520-240q0-83 58.5-141.5T720-440q83 0 141.5 58.5T920-240q0 83-58.5 141.5T720-40ZM120-80v-680q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v267q-19-9-39-15t-41-9v-243H200v562h243q5 31 15.5 59T486-86l-6 6-60-60-60 60-60-60-60 60-60-60-60 60Zm120-200h203q3-21 9-41t15-39H240v80Zm0-160h284q38-37 88.5-58.5T720-520H240v80Zm-40 242v-562 562Z"/></svg>
|
|
24
25
|
Evaluate
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
<button
|
|
28
29
|
type="button"
|
|
29
30
|
@click.stop="toggleActionsMenu"
|
|
30
|
-
class="flex items-center rounded-md bg-
|
|
31
|
+
class="flex items-center rounded-md bg-muted px-3 py-1.5 text-sm font-semibold text-content-secondary shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-muted focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gray-500"
|
|
31
32
|
aria-haspopup="true"
|
|
32
33
|
:aria-expanded="showActionsMenu"
|
|
33
34
|
>
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
</button>
|
|
39
40
|
<div
|
|
40
41
|
v-if="showActionsMenu"
|
|
41
|
-
class="absolute right-0 mt-2 w-44 origin-top-right rounded-md bg-
|
|
42
|
+
class="absolute right-0 mt-2 w-44 origin-top-right rounded-md bg-surface shadow-lg ring-1 ring-black/5 z-20"
|
|
42
43
|
role="menu"
|
|
43
44
|
aria-label="Dashboard actions"
|
|
44
45
|
>
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
type="button"
|
|
47
48
|
:disabled="startingChat"
|
|
48
49
|
@click.stop="startChatWithDashboard"
|
|
49
|
-
class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-
|
|
50
|
+
class="flex w-full items-center gap-2 px-3 py-2 text-left text-sm text-content-secondary hover:bg-primary-subtle disabled:cursor-not-allowed disabled:text-gray-400"
|
|
50
51
|
role="menuitem"
|
|
51
52
|
>
|
|
52
53
|
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
@@ -61,12 +62,12 @@
|
|
|
61
62
|
<div v-if="!showEditor" class="mt-4 mb-4">
|
|
62
63
|
<div v-if="dashboardResults.length === 0">
|
|
63
64
|
<div class="flex flex-col items-center justify-center py-8">
|
|
64
|
-
<p class="text-
|
|
65
|
+
<p class="text-content-secondary text-base mb-4">This dashboard hasn't been evaluated yet.</p>
|
|
65
66
|
<async-button
|
|
66
67
|
@click="evaluateDashboard"
|
|
67
68
|
type="button"
|
|
68
69
|
:disabled="status === 'evaluating'"
|
|
69
|
-
class="rounded-md bg-
|
|
70
|
+
class="rounded-md bg-primary px-4 py-2 text-sm font-semibold text-primary-text shadow-sm hover:bg-primary-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary disabled:cursor-not-allowed disabled:bg-gray-600"
|
|
70
71
|
>
|
|
71
72
|
Evaluate Dashboard
|
|
72
73
|
</async-button>
|
|
@@ -74,7 +75,7 @@
|
|
|
74
75
|
</div>
|
|
75
76
|
<div v-else-if="dashboardResult">
|
|
76
77
|
<div class="relative">
|
|
77
|
-
<div v-if="status === 'evaluating'" class="absolute inset-0 flex items-center justify-center bg-
|
|
78
|
+
<div v-if="status === 'evaluating'" class="absolute inset-0 flex items-center justify-center bg-surface bg-opacity-60 z-10 flex flex-col gap-2">
|
|
78
79
|
<div>Evaluating Dashboard...</div>
|
|
79
80
|
<img src="images/loader.gif" class="h-12 w-12" alt="Loading..." />
|
|
80
81
|
</div>
|
|
@@ -116,9 +117,9 @@
|
|
|
116
117
|
<div v-if="!dashboard && status !== 'loading'">
|
|
117
118
|
No dashboard with the given id could be found.
|
|
118
119
|
</div>
|
|
119
|
-
</div>
|
|
120
|
+
</div>
|
|
120
121
|
|
|
121
|
-
<modal
|
|
122
|
+
<modal
|
|
122
123
|
v-if="showDetailModal"
|
|
123
124
|
containerClass="!h-[90vh] !w-[90vw]"
|
|
124
125
|
role="dialog"
|
|
@@ -138,3 +139,4 @@
|
|
|
138
139
|
</div>
|
|
139
140
|
</template>
|
|
140
141
|
</modal>
|
|
142
|
+
</div>
|
|
@@ -28,6 +28,13 @@ module.exports = {
|
|
|
28
28
|
this.showEditor = !this.showEditor;
|
|
29
29
|
},
|
|
30
30
|
async updateCode(update) {
|
|
31
|
+
if (!update?.doc) {
|
|
32
|
+
const message = update?.error?.message || 'Dashboard update failed';
|
|
33
|
+
console.error(update?.error || new Error(message));
|
|
34
|
+
this.$toast.error(message);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
31
38
|
this.code = update.doc.code;
|
|
32
39
|
this.title = update.doc.title;
|
|
33
40
|
this.description = update.doc.description;
|
|
@@ -37,14 +44,24 @@ module.exports = {
|
|
|
37
44
|
async evaluateDashboard() {
|
|
38
45
|
this.status = 'evaluating';
|
|
39
46
|
try {
|
|
40
|
-
const { dashboard, dashboardResult } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
|
|
47
|
+
const { dashboard, dashboardResult, result, error } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
|
|
41
48
|
this.dashboard = dashboard;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
49
|
+
if (dashboard) {
|
|
50
|
+
this.code = this.dashboard.code;
|
|
51
|
+
this.title = this.dashboard.title;
|
|
52
|
+
this.description = this.dashboard.description ?? '';
|
|
53
|
+
}
|
|
45
54
|
if (dashboardResult) {
|
|
46
55
|
this.dashboardResults.unshift(dashboardResult);
|
|
56
|
+
} else if (result !== undefined) {
|
|
57
|
+
this.dashboardResults.unshift({ result, finishedEvaluatingAt: new Date() });
|
|
58
|
+
} else if (error) {
|
|
59
|
+
this.dashboardResults.unshift({ error: { message: error.message || 'Evaluation failed' }, finishedEvaluatingAt: new Date() });
|
|
47
60
|
}
|
|
61
|
+
} catch (err) {
|
|
62
|
+
const message = err?.response?.data?.message || err?.message || 'Dashboard evaluation failed';
|
|
63
|
+
console.error(err || new Error(message));
|
|
64
|
+
this.$toast.error(message);
|
|
48
65
|
} finally {
|
|
49
66
|
this.status = 'loaded';
|
|
50
67
|
}
|
|
@@ -1,21 +1,27 @@
|
|
|
1
|
-
<div class="p-4 bg-
|
|
1
|
+
<div class="dashboard-editor p-4 bg-muted rounded-lg shadow-lg">
|
|
2
2
|
<div v-show="status === 'loading'" class="max-w-5xl mx-auto text-center">
|
|
3
3
|
<img src="images/loader.gif" class="inline mt-10">
|
|
4
4
|
</div>
|
|
5
5
|
<div v-show="status !== 'loading'" class="flex flex-col gap-4">
|
|
6
6
|
<div>
|
|
7
|
-
<input v-model="title" class="w-full p-2 border border-
|
|
7
|
+
<input v-model="title" class="w-full p-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Title"/>
|
|
8
8
|
</div>
|
|
9
9
|
<div>
|
|
10
|
-
<textarea v-model="description" class="w-full p-2 border border-
|
|
10
|
+
<textarea v-model="description" class="w-full p-2 border border-edge-strong rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="4" placeholder="Description">{{description}}</textarea>
|
|
11
11
|
</div>
|
|
12
|
-
<div class="border border-
|
|
13
|
-
<
|
|
14
|
-
|
|
12
|
+
<div class="border border-edge-strong rounded-md">
|
|
13
|
+
<ace-editor
|
|
14
|
+
ref="codeEditor"
|
|
15
|
+
v-model="editCode"
|
|
16
|
+
mode="javascript"
|
|
17
|
+
:line-numbers="true"
|
|
18
|
+
:wrap="true"
|
|
19
|
+
class="w-full min-h-[200px]"
|
|
20
|
+
/>
|
|
15
21
|
</div>
|
|
16
22
|
<div class="flex space-x-2">
|
|
17
23
|
<async-button @click="updateCode" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500">Submit</async-button>
|
|
18
|
-
<button @click="closeEditor" class="px-4 py-2 bg-
|
|
24
|
+
<button @click="closeEditor" class="px-4 py-2 bg-page0 text-white rounded-md hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-500">Cancel</button>
|
|
19
25
|
</div>
|
|
20
26
|
</div>
|
|
21
27
|
</div>
|