@mongoosejs/studio 0.0.103 → 0.0.105

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.
@@ -330,7 +330,17 @@ const vanillatoasts = __webpack_require__(/*! vanillatoasts */ "./node_modules/v
330
330
  module.exports = app => app.component('chat-message-script', {
331
331
  template,
332
332
  props: ['message', 'script', 'language'],
333
- data: () => ({ activeTab: 'code', showDetailModal: false }),
333
+ data() {
334
+ return {
335
+ activeTab: 'code',
336
+ showDetailModal: false,
337
+ showCreateDashboardModal: false,
338
+ newDashboardTitle: '',
339
+ dashboardCode: '',
340
+ createError: null,
341
+ dashboardEditor: null
342
+ };
343
+ },
334
344
  computed: {
335
345
  styleForMessage() {
336
346
  return this.message.role === 'user' ? 'bg-gray-100' : '';
@@ -348,6 +358,42 @@ module.exports = app => app.component('chat-message-script', {
348
358
  openDetailModal() {
349
359
  this.showDetailModal = true;
350
360
  },
361
+ openCreateDashboardModal() {
362
+ this.newDashboardTitle = '';
363
+ this.dashboardCode = this.script;
364
+ this.createErrors = [];
365
+ this.showCreateDashboardModal = true;
366
+ this.$nextTick(() => {
367
+ if (this.dashboardEditor) {
368
+ this.dashboardEditor.toTextArea();
369
+ }
370
+ this.$refs.dashboardCodeEditor.value = this.dashboardCode;
371
+ this.dashboardEditor = CodeMirror.fromTextArea(this.$refs.dashboardCodeEditor, {
372
+ mode: 'javascript',
373
+ lineNumbers: true
374
+ });
375
+ this.dashboardEditor.on('change', () => {
376
+ this.dashboardCode = this.dashboardEditor.getValue();
377
+ });
378
+ });
379
+ },
380
+ async createDashboardFromScript() {
381
+ this.dashboardCode = this.dashboardEditor.getValue();
382
+ const { dashboard } = await api.Dashboard.createDashboard({
383
+ code: this.dashboardCode,
384
+ title: this.newDashboardTitle
385
+ }).catch(err => {
386
+ if (err.response?.data?.message) {
387
+ const message = err.response.data.message.split(': ').slice(1).join(': ');
388
+ this.createError = message;
389
+ throw new Error(err.response?.data?.message);
390
+ }
391
+ throw err;
392
+ });
393
+ this.createError = null;
394
+ this.showCreateDashboardModal = false;
395
+ this.$router.push('/dashboard/' + dashboard._id);
396
+ },
351
397
  async copyOutput() {
352
398
  await navigator.clipboard.writeText(this.message.executionResult.output);
353
399
  vanillatoasts.create({
@@ -359,6 +405,14 @@ module.exports = app => app.component('chat-message-script', {
359
405
  });
360
406
  }
361
407
  },
408
+ watch: {
409
+ showCreateDashboardModal(val) {
410
+ if (!val && this.dashboardEditor) {
411
+ this.dashboardEditor.toTextArea();
412
+ this.dashboardEditor = null;
413
+ }
414
+ }
415
+ },
362
416
  mounted() {
363
417
  Prism.highlightElement(this.$refs.code);
364
418
  if (this.message.executionResult?.output) {
@@ -3962,7 +4016,7 @@ module.exports = "<button v-bind=\"attrsToBind\" :disabled=\"isDisabled\" @click
3962
4016
  /***/ ((module) => {
3963
4017
 
3964
4018
  "use strict";
3965
- module.exports = "<div class=\"relative border rounded bg-gray-100 text-black text-sm overflow-hidden\">\n <div class=\"flex border-b pt-[1px] text-xs font-medium bg-gray-200\">\n <button\n class=\"px-3 py-1 border-r border-gray-300 hover:bg-green-300\"\n :class=\"{'bg-gray-300': activeTab === 'code', 'bg-green-300': activeTab === 'code'}\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-3 py-1 hover:bg-green-300\"\n :class=\"{'bg-green-300': activeTab === 'output'}\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex\">\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click=\"copyOutput\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-green-500 text-white border-none rounded cursor-pointer hover:bg-green-600 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript(message, script)\">\n Execute\n </async-button>\n </div>\n </div>\n\n <pre class=\"p-3 whitespace-pre-wrap max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto\" v-show=\"activeTab === 'code'\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n\n <div class=\"p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-white border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative\" v-show=\"activeTab === 'output'\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" />\n <pre v-else>{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" :responsive=\"true\" />\n <pre v-else class=\"whitespace-pre-wrap\">{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n </template>\n </modal>\n</div>\n";
4019
+ module.exports = "<div class=\"relative border rounded bg-gray-100 text-black text-sm overflow-hidden\">\n <div class=\"flex border-b pt-[1px] text-xs font-medium bg-gray-200\">\n <button\n class=\"px-3 py-1 border-r border-gray-300 hover:bg-green-300\"\n :class=\"{'bg-gray-300': activeTab === 'code', 'bg-green-300': activeTab === 'code'}\"\n @click=\"activeTab = 'code'\">\n Code\n </button>\n <button\n class=\"px-3 py-1 hover:bg-green-300\"\n :class=\"{'bg-green-300': activeTab === 'output'}\"\n @click=\"activeTab = 'output'\">\n Output\n </button>\n <div class=\"ml-auto mr-1 flex\">\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-gray-500 text-white border-none rounded cursor-pointer hover:bg-gray-600 transition-colors flex items-center\"\n @click=\"copyOutput\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M8 5H6a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2v-1M8 5a2 2 0 002 2h2a2 2 0 002-2M8 5a2 2 0 012-2h2a2 2 0 012 2m0 0h2a2 2 0 012 2v3m2 4H10m0 0l3-3m-3 3l3 3\" />\n </svg>\n </button>\n <button\n v-if=\"activeTab === 'output'\"\n class=\"px-2 py-1 mr-1 text-xs bg-blue-500 text-white border-none rounded cursor-pointer hover:bg-blue-600 transition-colors flex items-center\"\n @click=\"openDetailModal\">\n <svg xmlns=\"http://www.w3.org/2000/svg\" class=\"h-3 w-3\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5\" />\n </svg>\n </button>\n <button\n class=\"px-2 py-1 mr-1 text-xs bg-ultramarine-500 text-white border-none rounded cursor-pointer hover:bg-ultramarine-600 transition-colors flex items-center\"\n @click=\"openCreateDashboardModal\">\n Create Dashboard\n </button>\n <async-button\n class=\"px-2 py-1 text-xs bg-green-500 text-white border-none rounded cursor-pointer hover:bg-green-600 transition-colors disabled:bg-gray-400\"\n @click=\"executeScript(message, script)\">\n Execute\n </async-button>\n </div>\n </div>\n\n <pre class=\"p-3 whitespace-pre-wrap max-h-[50vh] max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] overflow-y-auto\" v-show=\"activeTab === 'code'\"><code v-text=\"script\" ref=\"code\" :class=\"'language-' + language\"></code></pre>\n\n <div class=\"p-3 whitespace-pre-wrap max-h-[50vh] overflow-y-auto bg-white border-t max-w-[calc(100vw-4rem)] lg:max-w-[calc(100vw-20rem)] relative\" v-show=\"activeTab === 'output'\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" />\n <pre v-else>{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n\n <modal ref=\"outputModal\" v-if=\"showDetailModal\" containerClass=\"!h-[90vh] !w-[90vw]\">\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-chart v-if=\"message.executionResult?.output?.$chart\" :value=\"message.executionResult?.output\" :responsive=\"true\" />\n <pre v-else class=\"whitespace-pre-wrap\">{{ message.executionResult?.output || 'No output' }}</pre>\n </div>\n </template>\n </modal>\n <modal v-if=\"showCreateDashboardModal\">\n <template #body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false\">&times;</div>\n <div>\n <div class=\"mt-4 text-gray-900 font-semibold\">Create Dashboard</div>\n <div class=\"mt-4\">\n <label class=\"block text-sm font-medium leading-6 text-gray-900\">Title</label>\n <div class=\"mt-2\">\n <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\">\n <input type=\"text\" v-model=\"newDashboardTitle\" class=\"outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6\" placeholder=\"My Dashboard\">\n </div>\n </div>\n </div>\n <div class=\"my-4\">\n <label class=\"block text-sm font-medium leading-6 text-gray-900\">Code</label>\n <div class=\"border border-gray-200\">\n <textarea class=\"p-2 h-[300px] w-full\" ref=\"dashboardCodeEditor\"></textarea>\n </div>\n </div>\n <async-button\n @click=\"createDashboardFromScript\"\n class=\"rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600\">\n Submit\n </async-button>\n <div v-if=\"createErrors.length > 0\" class=\"rounded-md bg-red-50 p-4 mt-1\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">Error</h3>\n <div class=\"mt-2 text-sm text-red-700\">\n {{createError}}\n </div>\n </div>\n </div>\n </div>\n </div>\n </template>\n </modal>\n</div>\n";
3966
4020
 
3967
4021
  /***/ }),
3968
4022
 
@@ -4193,7 +4247,7 @@ module.exports = ".document-details {\n width: 100%;\n }\n \n .document-de
4193
4247
  /***/ ((module) => {
4194
4248
 
4195
4249
  "use strict";
4196
- module.exports = "<div>\n <div class=\"relative path-key p-1 flex\">\n <div class=\"grow\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown').toLowerCase()}})\n </span>\n </div>\n <div v-if=\"editting && path.instance === 'Date'\" class=\"flex gap-1.5\">\n <div\n @click=\"dateType = 'picker'\"\n :class=\"dateType === 'picker' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'picker' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n Date Picker\n </div>\n </div>\n <div\n @click=\"dateType = 'iso'\"\n :class=\"dateType === 'iso' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'iso' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n ISO String\n </div>\n </div>\n </div>\n </div>\n <div v-if=\"editting && path.path !== '_id'\" class=\"pl-1\">\n <component\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :format=\"dateType\"\n @input=\"changes[path.path] = $event; delete invalid[path.path];\"\n @error=\"invalid[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else class=\"pl-1\">\n <component :is=\"getComponentForPath(path)\" :value=\"getValueForPath(path.path)\"></component>\n </div>\n</div>";
4250
+ module.exports = "<div>\n <div class=\"relative path-key p-1 flex\">\n <div class=\"grow flex justify-between items-center\">\n <div>\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown').toLowerCase()}})\n </span>\n </div>\n <div>\n <router-link\n v-if=\"path.ref && getValueForPath(path.path)\"\n :to=\"`/model/${path.ref}/document/${getValueForPath(path.path)}`\"\n class=\"bg-ultramarine-600 hover:bg-ultramarine-500 text-white px-2 py-1 text-sm mr-1 rounded-md\"\n >View Document\n </router-link>\n </div>\n </div>\n <div v-if=\"editting && path.instance === 'Date'\" class=\"flex gap-1.5\">\n <div\n @click=\"dateType = 'picker'\"\n :class=\"dateType === 'picker' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'picker' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n Date Picker\n </div>\n </div>\n <div\n @click=\"dateType = 'iso'\"\n :class=\"dateType === 'iso' ? 'bg-teal-600' : ''\"\n class=\"self-stretch px-2 py-1 rounded-sm justify-center items-center gap-1.5 flex cursor-pointer\">\n <div\n :class=\"dateType === 'iso' ? 'text-white' : ''\"\n class=\"text-xs font-medium font-['Lato'] capitalize leading-tight\">\n ISO String\n </div>\n </div>\n </div>\n </div>\n <div v-if=\"editting && path.path !== '_id'\" class=\"pl-1\">\n <component\n :is=\"getEditComponentForPath(path)\"\n :value=\"getEditValueForPath(path)\"\n :format=\"dateType\"\n @input=\"changes[path.path] = $event; delete invalid[path.path];\"\n @error=\"invalid[path.path] = $event;\"\n >\n </component>\n </div>\n <div v-else class=\"pl-1\">\n <component :is=\"getComponentForPath(path)\" :value=\"getValueForPath(path.path)\"></component>\n </div>\n</div>\n";
4197
4251
 
4198
4252
  /***/ }),
4199
4253
 
@@ -14619,7 +14673,7 @@ var bson = /*#__PURE__*/Object.freeze({
14619
14673
  /***/ ((module) => {
14620
14674
 
14621
14675
  "use strict";
14622
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.102","description":"A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.","homepage":"https://studio.mongoosejs.io/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"dependencies":{"archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"0.0.26","marked":"15.0.12","node-inspect-extracted":"3.x","tailwindcss":"3.4.0","vanillatoasts":"^1.6.0","vue":"3.x","webpack":"5.x"},"peerDependencies":{"bson":"^5.5.1 || 6.x","express":"4.x","mongoose":"7.x || 8.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongoose":"8.x"},"scripts":{"lint":"eslint .","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js"}}');
14676
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.105","description":"A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.","homepage":"https://studio.mongoosejs.io/","repository":{"type":"git","url":"https://github.com/mongoosejs/studio"},"dependencies":{"archetype":"0.13.1","csv-stringify":"6.3.0","ejson":"^2.2.3","extrovert":"0.0.26","marked":"15.0.12","node-inspect-extracted":"3.x","tailwindcss":"3.4.0","vanillatoasts":"^1.6.0","vue":"3.x","webpack":"5.x"},"peerDependencies":{"bson":"^5.5.1 || 6.x","express":"4.x","mongoose":"7.x || 8.x"},"devDependencies":{"@masteringjs/eslint-config":"0.1.1","axios":"1.2.2","eslint":"9.30.0","express":"4.x","mocha":"10.2.0","mongoose":"8.x"},"scripts":{"lint":"eslint .","tailwind":"tailwindcss -o ./frontend/public/tw.css","tailwind:watch":"tailwindcss -o ./frontend/public/tw.css --watch","test":"mocha test/*.test.js"}}');
14623
14677
 
14624
14678
  /***/ })
14625
14679
 
@@ -29,6 +29,11 @@
29
29
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 1v4m0 0h-4m4 0l-5-5" />
30
30
  </svg>
31
31
  </button>
32
+ <button
33
+ class="px-2 py-1 mr-1 text-xs bg-ultramarine-500 text-white border-none rounded cursor-pointer hover:bg-ultramarine-600 transition-colors flex items-center"
34
+ @click="openCreateDashboardModal">
35
+ Create Dashboard
36
+ </button>
32
37
  <async-button
33
38
  class="px-2 py-1 text-xs bg-green-500 text-white border-none rounded cursor-pointer hover:bg-green-600 transition-colors disabled:bg-gray-400"
34
39
  @click="executeScript(message, script)">
@@ -53,4 +58,46 @@
53
58
  </div>
54
59
  </template>
55
60
  </modal>
61
+ <modal v-if="showCreateDashboardModal">
62
+ <template #body>
63
+ <div class="modal-exit" @click="showCreateDashboardModal = false">&times;</div>
64
+ <div>
65
+ <div class="mt-4 text-gray-900 font-semibold">Create Dashboard</div>
66
+ <div class="mt-4">
67
+ <label class="block text-sm font-medium leading-6 text-gray-900">Title</label>
68
+ <div class="mt-2">
69
+ <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">
70
+ <input type="text" v-model="newDashboardTitle" class="outline-none block flex-1 border-0 bg-transparent py-1.5 pl-1 text-gray-900 placeholder:text-gray-400 focus:ring-0 sm:text-sm sm:leading-6" placeholder="My Dashboard">
71
+ </div>
72
+ </div>
73
+ </div>
74
+ <div class="my-4">
75
+ <label class="block text-sm font-medium leading-6 text-gray-900">Code</label>
76
+ <div class="border border-gray-200">
77
+ <textarea class="p-2 h-[300px] w-full" ref="dashboardCodeEditor"></textarea>
78
+ </div>
79
+ </div>
80
+ <async-button
81
+ @click="createDashboardFromScript"
82
+ class="rounded-md bg-teal-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-teal-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-teal-600">
83
+ Submit
84
+ </async-button>
85
+ <div v-if="createErrors.length > 0" class="rounded-md bg-red-50 p-4 mt-1">
86
+ <div class="flex">
87
+ <div class="flex-shrink-0">
88
+ <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
89
+ <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.28 7.22a.75.75 0 00-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 101.06 1.06L10 11.06l1.72 1.72a.75.75 0 101.06-1.06L11.06 10l1.72-1.72a.75.75 0 00-1.06-1.06L10 8.94 8.28 7.22z" clip-rule="evenodd" />
90
+ </svg>
91
+ </div>
92
+ <div class="ml-3">
93
+ <h3 class="text-sm font-medium text-red-800">Error</h3>
94
+ <div class="mt-2 text-sm text-red-700">
95
+ {{createError}}
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ </template>
102
+ </modal>
56
103
  </div>
@@ -7,7 +7,17 @@ const vanillatoasts = require('vanillatoasts');
7
7
  module.exports = app => app.component('chat-message-script', {
8
8
  template,
9
9
  props: ['message', 'script', 'language'],
10
- data: () => ({ activeTab: 'code', showDetailModal: false }),
10
+ data() {
11
+ return {
12
+ activeTab: 'code',
13
+ showDetailModal: false,
14
+ showCreateDashboardModal: false,
15
+ newDashboardTitle: '',
16
+ dashboardCode: '',
17
+ createError: null,
18
+ dashboardEditor: null
19
+ };
20
+ },
11
21
  computed: {
12
22
  styleForMessage() {
13
23
  return this.message.role === 'user' ? 'bg-gray-100' : '';
@@ -25,6 +35,42 @@ module.exports = app => app.component('chat-message-script', {
25
35
  openDetailModal() {
26
36
  this.showDetailModal = true;
27
37
  },
38
+ openCreateDashboardModal() {
39
+ this.newDashboardTitle = '';
40
+ this.dashboardCode = this.script;
41
+ this.createErrors = [];
42
+ this.showCreateDashboardModal = true;
43
+ this.$nextTick(() => {
44
+ if (this.dashboardEditor) {
45
+ this.dashboardEditor.toTextArea();
46
+ }
47
+ this.$refs.dashboardCodeEditor.value = this.dashboardCode;
48
+ this.dashboardEditor = CodeMirror.fromTextArea(this.$refs.dashboardCodeEditor, {
49
+ mode: 'javascript',
50
+ lineNumbers: true
51
+ });
52
+ this.dashboardEditor.on('change', () => {
53
+ this.dashboardCode = this.dashboardEditor.getValue();
54
+ });
55
+ });
56
+ },
57
+ async createDashboardFromScript() {
58
+ this.dashboardCode = this.dashboardEditor.getValue();
59
+ const { dashboard } = await api.Dashboard.createDashboard({
60
+ code: this.dashboardCode,
61
+ title: this.newDashboardTitle
62
+ }).catch(err => {
63
+ if (err.response?.data?.message) {
64
+ const message = err.response.data.message.split(': ').slice(1).join(': ');
65
+ this.createError = message;
66
+ throw new Error(err.response?.data?.message);
67
+ }
68
+ throw err;
69
+ });
70
+ this.createError = null;
71
+ this.showCreateDashboardModal = false;
72
+ this.$router.push('/dashboard/' + dashboard._id);
73
+ },
28
74
  async copyOutput() {
29
75
  await navigator.clipboard.writeText(this.message.executionResult.output);
30
76
  vanillatoasts.create({
@@ -36,6 +82,14 @@ module.exports = app => app.component('chat-message-script', {
36
82
  });
37
83
  }
38
84
  },
85
+ watch: {
86
+ showCreateDashboardModal(val) {
87
+ if (!val && this.dashboardEditor) {
88
+ this.dashboardEditor.toTextArea();
89
+ this.dashboardEditor = null;
90
+ }
91
+ }
92
+ },
39
93
  mounted() {
40
94
  Prism.highlightElement(this.$refs.code);
41
95
  if (this.message.executionResult?.output) {
@@ -1,10 +1,20 @@
1
1
  <div>
2
2
  <div class="relative path-key p-1 flex">
3
- <div class="grow">
4
- {{path.path}}
5
- <span class="path-type">
6
- ({{(path.instance || 'unknown').toLowerCase()}})
7
- </span>
3
+ <div class="grow flex justify-between items-center">
4
+ <div>
5
+ {{path.path}}
6
+ <span class="path-type">
7
+ ({{(path.instance || 'unknown').toLowerCase()}})
8
+ </span>
9
+ </div>
10
+ <div>
11
+ <router-link
12
+ v-if="path.ref && getValueForPath(path.path)"
13
+ :to="`/model/${path.ref}/document/${getValueForPath(path.path)}`"
14
+ class="bg-ultramarine-600 hover:bg-ultramarine-500 text-white px-2 py-1 text-sm mr-1 rounded-md"
15
+ >View Document
16
+ </router-link>
17
+ </div>
8
18
  </div>
9
19
  <div v-if="editting && path.instance === 'Date'" class="flex gap-1.5">
10
20
  <div
@@ -42,4 +52,4 @@
42
52
  <div v-else class="pl-1">
43
53
  <component :is="getComponentForPath(path)" :value="getValueForPath(path.path)"></component>
44
54
  </div>
45
- </div>
55
+ </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.0.103",
3
+ "version": "0.0.105",
4
4
  "description": "A sleek, powerful MongoDB UI with built-in dashboarding and auth, seamlessly integrated with your Express, Vercel, or Netlify app.",
5
5
  "homepage": "https://studio.mongoosejs.io/",
6
6
  "repository": {