@mongoosejs/studio 0.0.116 → 0.0.118

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.
@@ -38,24 +38,45 @@ module.exports = ({ db }) => async function getDashboard(params) {
38
38
  try {
39
39
  result = await dashboard.evaluate();
40
40
  } catch (error) {
41
- return { dashboard, error: { message: error.message } };
41
+ const { dashboardResult } = await startExec.then(({ dashboardResult }) => {
42
+ if (!dashboardResult) {
43
+ return {};
44
+ }
45
+ return completeDashboardEvaluate(
46
+ dashboardResult._id,
47
+ $workspaceId,
48
+ authorization,
49
+ null,
50
+ { message: error.message }
51
+ );
52
+ });
53
+ return { dashboard, dashboardResult, error: { message: error.message } };
42
54
  }
43
55
 
44
- const { dashboardResult } = await startExec.then(({ dashboardResult }) => {
45
- if (!dashboardResult) {
46
- return;
47
- }
48
- return completeDashboardEvaluate(dashboardResult._id, $workspaceId, authorization, result);
49
- });
56
+ try {
57
+ const { dashboardResult } = await startExec.then(({ dashboardResult }) => {
58
+ if (!dashboardResult) {
59
+ return {};
60
+ }
61
+ return completeDashboardEvaluate(
62
+ dashboardResult._id,
63
+ $workspaceId,
64
+ authorization,
65
+ result
66
+ );
67
+ });
50
68
 
51
- return { dashboard, dashboardResult };
69
+ return { dashboard, dashboardResult };
70
+ } catch (error) {
71
+ return { dashboard, error: { message: error.message } };
72
+ }
52
73
  } else {
53
74
  const { dashboardResults } = await getDashboardResults(dashboardId, $workspaceId, authorization);
54
75
  return { dashboard, dashboardResults };
55
76
  }
56
77
  };
57
78
 
58
- async function completeDashboardEvaluate(dashboardResultId, workspaceId, authorization, result) {
79
+ async function completeDashboardEvaluate(dashboardResultId, workspaceId, authorization, result, error) {
59
80
  if (!workspaceId) {
60
81
  return {};
61
82
  }
@@ -70,7 +91,8 @@ async function completeDashboardEvaluate(dashboardResultId, workspaceId, authori
70
91
  dashboardResultId,
71
92
  workspaceId,
72
93
  finishedEvaluatingAt: new Date(),
73
- result
94
+ result,
95
+ error
74
96
  })
75
97
  }).then(response => {
76
98
  if (response.status < 200 || response.status >= 400) {
@@ -24,7 +24,7 @@ const UpdateDashboardParams = new Archetype({
24
24
  }).compile('UpdateDashboardParams');
25
25
 
26
26
  module.exports = ({ db }) => async function updateDashboard(params) {
27
- const { dashboardId, code, title, description, roles } = new UpdateDashboardParams(params);
27
+ const { dashboardId, code, title, description, roles, evaluate } = new UpdateDashboardParams(params);
28
28
 
29
29
  const Dashboard = db.models['__Studio_Dashboard'];
30
30
 
@@ -43,12 +43,5 @@ module.exports = ({ db }) => async function updateDashboard(params) {
43
43
  const doc = await Dashboard.
44
44
  findByIdAndUpdate(dashboardId, updateObj, { sanitizeFilter: true, returnDocument: 'after', overwriteImmutable: true });
45
45
 
46
- let result = null;
47
- try {
48
- result = await doc.evaluate();
49
- } catch (error) {
50
- return { doc, error: { message: error.message } };
51
- }
52
-
53
- return { doc, result };
46
+ return { doc };
54
47
  };
@@ -18,7 +18,7 @@ const dashboardSchema = new mongoose.Schema({
18
18
  });
19
19
 
20
20
  dashboardSchema.methods.evaluate = async function evaluate() {
21
- const context = vm.createContext({ db: this.constructor.db, setTimeout });
21
+ const context = vm.createContext({ db: this.constructor.db, setTimeout, ObjectId: mongoose.Types.ObjectId });
22
22
  let result = null;
23
23
  result = await vm.runInContext(formatFunction(this.code), context);
24
24
  if (result.$document?.constructor?.modelName) {
@@ -1201,24 +1201,20 @@ module.exports = app => app.component('dashboard', {
1201
1201
  this.code = update.doc.code;
1202
1202
  this.title = update.doc.title;
1203
1203
  this.description = update.doc.description;
1204
- if (update.result) {
1205
- this.result = update.result;
1206
- } else {
1207
- this.errorMessage = update.error.message;
1208
- }
1204
+
1205
+ await this.evaluateDashboard();
1209
1206
  },
1210
1207
  async evaluateDashboard() {
1211
1208
  this.status = 'evaluating';
1212
1209
  try {
1213
- const { dashboard, dashboardResult, error } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
1210
+ const { dashboard, dashboardResult } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
1214
1211
  this.dashboard = dashboard;
1215
- if (error) {
1216
- this.errorMessage = error.message;
1217
- }
1218
1212
  this.code = this.dashboard.code;
1219
1213
  this.title = this.dashboard.title;
1220
1214
  this.description = this.dashboard.description ?? '';
1221
- this.dashboardResults.unshift(dashboardResult);
1215
+ if (dashboardResult) {
1216
+ this.dashboardResults.unshift(dashboardResult);
1217
+ }
1222
1218
  } finally {
1223
1219
  this.status = 'loaded';
1224
1220
  }
@@ -1236,9 +1232,6 @@ module.exports = app => app.component('dashboard', {
1236
1232
  return;
1237
1233
  }
1238
1234
  this.dashboard = dashboard;
1239
- if (error) {
1240
- this.errorMessage = error.message;
1241
- }
1242
1235
  this.code = this.dashboard.code;
1243
1236
  this.title = this.dashboard.title;
1244
1237
  this.description = this.dashboard.description ?? '';
@@ -1265,6 +1258,7 @@ const template = __webpack_require__(/*! ./edit-dashboard.html */ "./frontend/sr
1265
1258
  module.exports = app => app.component('edit-dashboard', {
1266
1259
  template: template,
1267
1260
  props: ['dashboardId', 'code', 'currentDescription', 'currentTitle'],
1261
+ emits: ['close'],
1268
1262
  data: function() {
1269
1263
  return {
1270
1264
  status: 'loaded',
@@ -1280,13 +1274,14 @@ module.exports = app => app.component('edit-dashboard', {
1280
1274
  async updateCode() {
1281
1275
  this.status = 'loading';
1282
1276
  try {
1283
- const { doc, result, error } = await api.Dashboard.updateDashboard({
1277
+ const { doc } = await api.Dashboard.updateDashboard({
1284
1278
  dashboardId: this.dashboardId,
1285
1279
  code: this.editor.getValue(),
1286
1280
  title: this.title,
1287
- description: this.description
1281
+ description: this.description,
1282
+ evaluate: false
1288
1283
  });
1289
- this.$emit('update', { doc, result, error });
1284
+ this.$emit('update', { doc });
1290
1285
  this.editor.setValue(doc.code);
1291
1286
  this.closeEditor();
1292
1287
  } catch (err) {
@@ -4362,7 +4357,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
4362
4357
  /***/ ((module) => {
4363
4358
 
4364
4359
  "use strict";
4365
- module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"status === 'loading'\" class=\"max-w-5xl mx-auto text-center\">\n <img src=\"images/loader.gif\" class=\"inline mt-10\">\n </div>\n <div v-if=\"dashboard && status !== 'loading'\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\" v-if=\"!showEditor\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{title}}</h2>\n <div class=\"flex gap-2\">\n <button\n @click=\"showEditor = true\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\">\n <img src=\"images/edit.svg\" class=\"inline h-[1.25em] mr-1\" /> Edit\n </button>\n\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center rounded-md bg-ultramarine-600 px-4 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n <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>\n Evaluate\n </async-button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <div v-if=\"dashboardResults.length === 0\">\n <div class=\"flex flex-col items-center justify-center py-8\">\n <p class=\"text-gray-700 text-base mb-4\">This dashboard hasn't been evaluated yet.</p>\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"rounded-md bg-ultramarine-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n Evaluate Dashboard\n </async-button>\n </div>\n </div>\n <div v-else>\n <div class=\"relative\">\n <dashboard-result\n :key=\"dashboardResult.finishedEvaluatingAt\"\n :result=\"dashboardResult.result\"\n :finishedEvaluatingAt=\"dashboardResult.finishedEvaluatingAt\"\n @fullscreen=\"showDetailModal = true\"\n class=\"h-[40vh]\"\n >\n </dashboard-result>\n </div>\n </div>\n </div>\n <div v-if=\"showEditor\" class=\"mt-4\">\n <edit-dashboard\n :dashboardId=\"dashboard._id\"\n :code=\"code\"\n :currentDescription=\"description\"\n :currentTitle=\"title\"\n @close=\"showEditor=false;\"\n @update=\"updateCode\"></edit-dashboard>\n </div>\n <div v-if=\"errorMessage\" class=\"rounded-md bg-red-50 p-4 mt-4\">\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\" data-slot=\"icon\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-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\">{{errorMessage}}</h3>\n </div>\n </div>\n </div>\n\n </div>\n <div v-if=\"!dashboard && status !== 'loading'\">\n No dashboard with the given id could be found.\n </div>\n</div>\n\n<modal\n v-if=\"showDetailModal\"\n containerClass=\"!h-[90vh] !w-[90vw]\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Dashboard Details\"\n>\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\" role=\"button\" aria-label=\"Close modal\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-result\n v-if=\"dashboardResult\"\n :result=\"dashboardResult.result\"\n :finishedEvaluatingAt=\"dashboardResult.finishedEvaluatingAt\"\n :fullscreen=\"true\"\n :responsive=\"true\">\n </dashboard-result>\n </div>\n </template>\n</modal>\n";
4360
+ module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"status === 'loading'\" class=\"max-w-5xl mx-auto text-center\">\n <img src=\"images/loader.gif\" class=\"inline mt-10\">\n </div>\n <div v-if=\"dashboard && status !== 'loading'\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\" v-if=\"!showEditor\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{title}}</h2>\n <div class=\"flex gap-2\">\n <button\n @click=\"showEditor = true\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center rounded-md bg-ultramarine-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\">\n <img src=\"images/edit.svg\" class=\"inline h-[1.25em] mr-1\" /> Edit\n </button>\n\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"flex items-center rounded-md bg-ultramarine-600 px-4 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n <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>\n Evaluate\n </async-button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <div v-if=\"dashboardResults.length === 0\">\n <div class=\"flex flex-col items-center justify-center py-8\">\n <p class=\"text-gray-700 text-base mb-4\">This dashboard hasn't been evaluated yet.</p>\n <async-button\n @click=\"evaluateDashboard\"\n type=\"button\"\n :disabled=\"status === 'evaluating'\"\n class=\"rounded-md bg-ultramarine-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600 disabled:cursor-not-allowed disabled:bg-gray-600\"\n >\n Evaluate Dashboard\n </async-button>\n </div>\n </div>\n <div v-else-if=\"dashboardResult\">\n <div class=\"relative\">\n <div v-if=\"status === 'evaluating'\" class=\"absolute inset-0 flex items-center justify-center bg-white bg-opacity-60 z-10 flex flex-col gap-2\">\n <div>Evaluating Dashboard...</div>\n <img src=\"images/loader.gif\" class=\"h-12 w-12\" alt=\"Loading...\" />\n </div>\n <div v-else-if=\"dashboardResult.error\" class=\"rounded-md bg-red-50 p-4 mt-4\">\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\" data-slot=\"icon\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-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\">{{dashboardResult.error.message || 'Unknown Error'}}</h3>\n </div>\n </div>\n </div>\n <dashboard-result\n v-else\n :key=\"dashboardResult.finishedEvaluatingAt\"\n :result=\"dashboardResult.result\"\n :finishedEvaluatingAt=\"dashboardResult.finishedEvaluatingAt\"\n @fullscreen=\"showDetailModal = true\"\n class=\"h-[40vh]\"\n >\n </dashboard-result>\n </div>\n </div>\n </div>\n <div v-if=\"showEditor\" class=\"mt-4\">\n <edit-dashboard\n :dashboardId=\"dashboard._id\"\n :code=\"code\"\n :currentDescription=\"description\"\n :currentTitle=\"title\"\n @close=\"showEditor=false;\"\n @update=\"updateCode\"></edit-dashboard>\n </div>\n\n </div>\n <div v-if=\"!dashboard && status !== 'loading'\">\n No dashboard with the given id could be found.\n </div>\n</div>\n\n<modal\n v-if=\"showDetailModal\"\n containerClass=\"!h-[90vh] !w-[90vw]\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-label=\"Dashboard Details\"\n>\n <template #body>\n <div class=\"absolute font-mono right-1 top-1 cursor-pointer text-xl\" @click=\"showDetailModal = false;\" role=\"button\" aria-label=\"Close modal\">&times;</div>\n <div class=\"h-full overflow-auto\">\n <dashboard-result\n v-if=\"dashboardResult\"\n :result=\"dashboardResult.result\"\n :finishedEvaluatingAt=\"dashboardResult.finishedEvaluatingAt\"\n :fullscreen=\"true\"\n :responsive=\"true\">\n </dashboard-result>\n </div>\n </template>\n</modal>\n";
4366
4361
 
4367
4362
  /***/ }),
4368
4363
 
@@ -4384,7 +4379,7 @@ module.exports = "<div class=\"p-4 bg-gray-100 rounded-lg shadow-lg\">\n <div
4384
4379
  /***/ ((module) => {
4385
4380
 
4386
4381
  "use strict";
4387
- module.exports = "<div class=\"dashboards max-w-5xl mx-auto mt-8\">\n <div v-if=\"status === 'loaded' && dashboards.length === 0\">\n <div class=\"text-center\">\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No dashboards yet</h3>\n <p class=\"mt-1 text-sm text-gray-500\">Get started by creating a new dashboard.</p>\n <div class=\"mt-6\">\n <button type=\"button\" class=\"inline-flex items-center rounded-md bg-teal-600 px-3 py-2 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 <svg class=\"-ml-0.5 mr-1.5 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z\" />\n </svg>\n New Dashboard\n </button>\n </div>\n </div>\n </div>\n\n\n <div class=\"px-4 sm:px-6 lg:px-8\">\n <div class=\"sm:flex sm:items-center\">\n <div class=\"sm:flex-auto\">\n <h1 class=\"text-base font-semibold leading-6 text-gray-900\">Dashboards</h1>\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showCreateDashboardModal = true\"\n class=\"block rounded-md bg-teal-600 px-3 py-2 text-center 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\">Create New Dashboard</button>\n </div>\n </div>\n <div class=\"mt-8 flow-root\">\n <div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\">\n <div class=\"inline-block min-w-full py-2 align-middle\">\n <table class=\"min-w-full divide-y divide-gray-300\">\n <thead>\n <tr>\n <th scope=\"col\" class=\"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8\">Title</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900 w-[50%]\">Description</th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"dashboard in dashboards\">\n <td class=\"whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8\">{{dashboard.title}}</td>\n <td class=\"whitespace-nowrap px-3 py-4 text-sm text-gray-500 truncate w-[50%]\">{{dashboard.description}}</td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <router-link\n :to=\"'/dashboard/' + dashboard._id + '?edit=true'\"\n class=\"text-teal-600 hover:text-teal-900\">\n Edit\n </router-link>\n </td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <router-link\n :to=\"'/dashboard/' + dashboard._id\"\n class=\"text-teal-600 hover:text-teal-900\">\n View\n </router-link>\n </td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <button\n @click=\"showDeleteDashboardModal=dashboard\"\n class=\"text-teal-600 hover:text-teal-900\">\n Delete\n </button>\n </td>\n </tr>\n \n <!-- More people... -->\n </tbody>\n </table>\n </div>\n </div>\n </div>\n </div>\n\n <modal v-if=\"showCreateDashboardModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false;\">&times;</div>\n \n <create-dashboard @close=\"insertNewDashboard\"></create-dashboard>\n </template>\n </modal>\n\n <modal v-if=\"showDeleteDashboardModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showDeleteDashboardModal = null;\">&times;</div>\n <h2>Are you sure you want to delete this dashboard titled {{showDeleteDashboardModal.title}}?</h2>\n <div class=\"flex space-x-2\">\n <button class=\"px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500\" @click=\"deleteDashboard(showDeleteDashboardModal)\">Yes, delete</button>\n <button class=\"px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-600\" @click=\"showDeleteDashboardModal=null;\">Cancel</button>\n </div>\n </template>\n </modal>\n</div>";
4382
+ module.exports = "<div class=\"dashboards max-w-5xl mx-auto mt-8\">\n <div v-if=\"status === 'loading'\" class=\"text-center mt-4\">\n <svg\n class=\"inline w-8 h-8 animate-spin text-ultramarine-600\"\n xmlns=\"http://www.w3.org/2000/svg\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n >\n <circle\n class=\"opacity-25\"\n cx=\"12\"\n cy=\"12\"\n r=\"10\"\n stroke=\"currentColor\"\n stroke-width=\"4\"\n ></circle>\n <path\n class=\"opacity-75\"\n fill=\"currentColor\"\n d=\"M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z\"\n ></path>\n </svg>\n </div>\n <div v-if=\"status === 'loaded' && dashboards.length === 0\">\n <div class=\"text-center\">\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No dashboards yet</h3>\n <p class=\"mt-1 text-sm text-gray-500\">Get started by creating a new dashboard.</p>\n <div class=\"mt-6\">\n <button type=\"button\" class=\"inline-flex items-center rounded-md bg-ultramarine-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n <svg class=\"-ml-0.5 mr-1.5 h-5 w-5\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z\" />\n </svg>\n New Dashboard\n </button>\n </div>\n </div>\n </div>\n\n <div class=\"px-4 sm:px-6 lg:px-8\">\n <div class=\"sm:flex sm:items-center\">\n <div class=\"sm:flex-auto\">\n <h1 class=\"text-base font-semibold leading-6 text-gray-900\">Dashboards</h1>\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showCreateDashboardModal = true\"\n class=\"block rounded-md bg-ultramarine-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">Create New Dashboard</button>\n </div>\n </div>\n <div class=\"mt-8 flow-root\">\n <div class=\"-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8\">\n <div class=\"inline-block min-w-full py-2 align-middle\">\n <table class=\"min-w-full divide-y divide-gray-300\">\n <thead>\n <tr>\n <th scope=\"col\" class=\"py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8\">Title</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900 w-[50%]\">Description</th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n <th scope=\"col\" class=\"relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8\">\n </th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"dashboard in dashboards\">\n <td class=\"whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8\">{{dashboard.title}}</td>\n <td class=\"whitespace-nowrap px-3 py-4 text-sm text-gray-500 truncate w-[50%]\">{{dashboard.description}}</td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <router-link\n :to=\"'/dashboard/' + dashboard._id + '?edit=true'\"\n class=\"text-ultramarine-600 hover:text-ultramarine-900\">\n Edit\n </router-link>\n </td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <router-link\n :to=\"'/dashboard/' + dashboard._id\"\n class=\"text-ultramarine-600 hover:text-ultramarine-900\">\n View\n </router-link>\n </td>\n <td class=\"relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8\">\n <button\n @click=\"showDeleteDashboardModal=dashboard\"\n class=\"text-ultramarine-600 hover:text-ultramarine-900\">\n Delete\n </button>\n </td>\n </tr>\n\n <!-- More people... -->\n </tbody>\n </table>\n </div>\n </div>\n </div>\n </div>\n\n <modal v-if=\"showCreateDashboardModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showCreateDashboardModal = false;\">&times;</div>\n\n <create-dashboard @close=\"insertNewDashboard\"></create-dashboard>\n </template>\n </modal>\n\n <modal v-if=\"showDeleteDashboardModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showDeleteDashboardModal = null;\">&times;</div>\n <h2>Are you sure you want to delete this dashboard titled {{showDeleteDashboardModal.title}}?</h2>\n <div class=\"flex space-x-2\">\n <button class=\"px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500\" @click=\"deleteDashboard(showDeleteDashboardModal)\">Yes, delete</button>\n <button class=\"px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-600\" @click=\"showDeleteDashboardModal=null;\">Cancel</button>\n </div>\n </template>\n </modal>\n </div>\n</div>";
4388
4383
 
4389
4384
  /***/ }),
4390
4385
 
@@ -14876,7 +14871,7 @@ var bson = /*#__PURE__*/Object.freeze({
14876
14871
  /***/ ((module) => {
14877
14872
 
14878
14873
  "use strict";
14879
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.116","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","dedent":"^1.6.0","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"}}');
14874
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@mongoosejs/studio","version":"0.0.118","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","dedent":"^1.6.0","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"}}');
14880
14875
 
14881
14876
  /***/ })
14882
14877
 
@@ -642,10 +642,6 @@ video {
642
642
  top: 90%;
643
643
  }
644
644
 
645
- .bottom-0 {
646
- bottom: 0px;
647
- }
648
-
649
645
  .isolate {
650
646
  isolation: isolate;
651
647
  }
@@ -846,6 +842,10 @@ video {
846
842
  height: 90vh !important;
847
843
  }
848
844
 
845
+ .h-12 {
846
+ height: 3rem;
847
+ }
848
+
849
849
  .h-16 {
850
850
  height: 4rem;
851
851
  }
@@ -930,6 +930,10 @@ video {
930
930
  width: 0px;
931
931
  }
932
932
 
933
+ .w-12 {
934
+ width: 3rem;
935
+ }
936
+
933
937
  .w-16 {
934
938
  width: 4rem;
935
939
  }
@@ -982,6 +986,10 @@ video {
982
986
  max-width: 64rem;
983
987
  }
984
988
 
989
+ .max-w-\[calc\(100vw-3rem\)\] {
990
+ max-width: calc(100vw - 3rem);
991
+ }
992
+
985
993
  .max-w-\[calc\(100vw-4rem\)\] {
986
994
  max-width: calc(100vw - 4rem);
987
995
  }
@@ -990,10 +998,6 @@ video {
990
998
  max-width: 20rem;
991
999
  }
992
1000
 
993
- .max-w-\[calc\(100vw-3rem\)\] {
994
- max-width: calc(100vw - 3rem);
995
- }
996
-
997
1001
  .flex-1 {
998
1002
  flex: 1 1 0%;
999
1003
  }
@@ -1044,6 +1048,16 @@ video {
1044
1048
  transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
1045
1049
  }
1046
1050
 
1051
+ @keyframes spin {
1052
+ to {
1053
+ transform: rotate(360deg);
1054
+ }
1055
+ }
1056
+
1057
+ .animate-spin {
1058
+ animation: spin 1s linear infinite;
1059
+ }
1060
+
1047
1061
  .cursor-not-allowed {
1048
1062
  cursor: not-allowed;
1049
1063
  }
@@ -1494,6 +1508,10 @@ video {
1494
1508
  --tw-bg-opacity: 0.4;
1495
1509
  }
1496
1510
 
1511
+ .bg-opacity-60 {
1512
+ --tw-bg-opacity: 0.6;
1513
+ }
1514
+
1497
1515
  .p-1 {
1498
1516
  padding: 0.25rem;
1499
1517
  }
@@ -1780,9 +1798,9 @@ video {
1780
1798
  color: rgb(7 89 133 / var(--tw-text-opacity));
1781
1799
  }
1782
1800
 
1783
- .text-teal-600 {
1801
+ .text-ultramarine-600 {
1784
1802
  --tw-text-opacity: 1;
1785
- color: rgb(0 168 165 / var(--tw-text-opacity));
1803
+ color: rgb(24 35 255 / var(--tw-text-opacity));
1786
1804
  }
1787
1805
 
1788
1806
  .text-ultramarine-700 {
@@ -1804,10 +1822,18 @@ video {
1804
1822
  accent-color: #0284c7;
1805
1823
  }
1806
1824
 
1825
+ .opacity-25 {
1826
+ opacity: 0.25;
1827
+ }
1828
+
1807
1829
  .opacity-50 {
1808
1830
  opacity: 0.5;
1809
1831
  }
1810
1832
 
1833
+ .opacity-75 {
1834
+ opacity: 0.75;
1835
+ }
1836
+
1811
1837
  .shadow {
1812
1838
  --tw-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
1813
1839
  --tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);
@@ -2080,14 +2106,9 @@ video {
2080
2106
  color: rgb(55 65 81 / var(--tw-text-opacity));
2081
2107
  }
2082
2108
 
2083
- .hover\:text-teal-900:hover {
2084
- --tw-text-opacity: 1;
2085
- color: rgb(10 87 87 / var(--tw-text-opacity));
2086
- }
2087
-
2088
- .hover\:text-gray-600:hover {
2109
+ .hover\:text-ultramarine-900:hover {
2089
2110
  --tw-text-opacity: 1;
2090
- color: rgb(75 85 99 / var(--tw-text-opacity));
2111
+ color: rgb(6 14 172 / var(--tw-text-opacity));
2091
2112
  }
2092
2113
 
2093
2114
  .focus\:z-10:focus {
@@ -2367,10 +2388,6 @@ video {
2367
2388
  .md\:hidden {
2368
2389
  display: none;
2369
2390
  }
2370
-
2371
- .md\:p-3 {
2372
- padding: 0.75rem;
2373
- }
2374
2391
  }
2375
2392
 
2376
2393
  @media (min-width: 1024px) {
@@ -2391,14 +2408,14 @@ video {
2391
2408
  width: 16rem;
2392
2409
  }
2393
2410
 
2394
- .lg\:max-w-\[calc\(100vw-20rem\)\] {
2395
- max-width: calc(100vw - 20rem);
2396
- }
2397
-
2398
2411
  .lg\:max-w-\[calc\(100vw-15rem\)\] {
2399
2412
  max-width: calc(100vw - 15rem);
2400
2413
  }
2401
2414
 
2415
+ .lg\:max-w-\[calc\(100vw-20rem\)\] {
2416
+ max-width: calc(100vw - 20rem);
2417
+ }
2418
+
2402
2419
  .lg\:px-8 {
2403
2420
  padding-left: 2rem;
2404
2421
  padding-right: 2rem;
@@ -39,9 +39,26 @@
39
39
  </async-button>
40
40
  </div>
41
41
  </div>
42
- <div v-else>
42
+ <div v-else-if="dashboardResult">
43
43
  <div class="relative">
44
+ <div v-if="status === 'evaluating'" class="absolute inset-0 flex items-center justify-center bg-white bg-opacity-60 z-10 flex flex-col gap-2">
45
+ <div>Evaluating Dashboard...</div>
46
+ <img src="images/loader.gif" class="h-12 w-12" alt="Loading..." />
47
+ </div>
48
+ <div v-else-if="dashboardResult.error" class="rounded-md bg-red-50 p-4 mt-4">
49
+ <div class="flex">
50
+ <div class="flex-shrink-0">
51
+ <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
52
+ <path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd" />
53
+ </svg>
54
+ </div>
55
+ <div class="ml-3">
56
+ <h3 class="text-sm font-medium text-red-800">{{dashboardResult.error.message || 'Unknown Error'}}</h3>
57
+ </div>
58
+ </div>
59
+ </div>
44
60
  <dashboard-result
61
+ v-else
45
62
  :key="dashboardResult.finishedEvaluatingAt"
46
63
  :result="dashboardResult.result"
47
64
  :finishedEvaluatingAt="dashboardResult.finishedEvaluatingAt"
@@ -61,18 +78,6 @@
61
78
  @close="showEditor=false;"
62
79
  @update="updateCode"></edit-dashboard>
63
80
  </div>
64
- <div v-if="errorMessage" class="rounded-md bg-red-50 p-4 mt-4">
65
- <div class="flex">
66
- <div class="flex-shrink-0">
67
- <svg class="h-5 w-5 text-red-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true" data-slot="icon">
68
- <path fill-rule="evenodd" d="M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z" clip-rule="evenodd" />
69
- </svg>
70
- </div>
71
- <div class="ml-3">
72
- <h3 class="text-sm font-medium text-red-800">{{errorMessage}}</h3>
73
- </div>
74
- </div>
75
- </div>
76
81
 
77
82
  </div>
78
83
  <div v-if="!dashboard && status !== 'loading'">
@@ -27,24 +27,20 @@ module.exports = app => app.component('dashboard', {
27
27
  this.code = update.doc.code;
28
28
  this.title = update.doc.title;
29
29
  this.description = update.doc.description;
30
- if (update.result) {
31
- this.result = update.result;
32
- } else {
33
- this.errorMessage = update.error.message;
34
- }
30
+
31
+ await this.evaluateDashboard();
35
32
  },
36
33
  async evaluateDashboard() {
37
34
  this.status = 'evaluating';
38
35
  try {
39
- const { dashboard, dashboardResult, error } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
36
+ const { dashboard, dashboardResult } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
40
37
  this.dashboard = dashboard;
41
- if (error) {
42
- this.errorMessage = error.message;
43
- }
44
38
  this.code = this.dashboard.code;
45
39
  this.title = this.dashboard.title;
46
40
  this.description = this.dashboard.description ?? '';
47
- this.dashboardResults.unshift(dashboardResult);
41
+ if (dashboardResult) {
42
+ this.dashboardResults.unshift(dashboardResult);
43
+ }
48
44
  } finally {
49
45
  this.status = 'loaded';
50
46
  }
@@ -62,9 +58,6 @@ module.exports = app => app.component('dashboard', {
62
58
  return;
63
59
  }
64
60
  this.dashboard = dashboard;
65
- if (error) {
66
- this.errorMessage = error.message;
67
- }
68
61
  this.code = this.dashboard.code;
69
62
  this.title = this.dashboard.title;
70
63
  this.description = this.dashboard.description ?? '';
@@ -6,6 +6,7 @@ const template = require('./edit-dashboard.html');
6
6
  module.exports = app => app.component('edit-dashboard', {
7
7
  template: template,
8
8
  props: ['dashboardId', 'code', 'currentDescription', 'currentTitle'],
9
+ emits: ['close'],
9
10
  data: function() {
10
11
  return {
11
12
  status: 'loaded',
@@ -21,13 +22,14 @@ module.exports = app => app.component('edit-dashboard', {
21
22
  async updateCode() {
22
23
  this.status = 'loading';
23
24
  try {
24
- const { doc, result, error } = await api.Dashboard.updateDashboard({
25
+ const { doc } = await api.Dashboard.updateDashboard({
25
26
  dashboardId: this.dashboardId,
26
27
  code: this.editor.getValue(),
27
28
  title: this.title,
28
- description: this.description
29
+ description: this.description,
30
+ evaluate: false
29
31
  });
30
- this.$emit('update', { doc, result, error });
32
+ this.$emit('update', { doc });
31
33
  this.editor.setValue(doc.code);
32
34
  this.closeEditor();
33
35
  } catch (err) {
@@ -1,97 +1,119 @@
1
1
  <div class="dashboards max-w-5xl mx-auto mt-8">
2
- <div v-if="status === 'loaded' && dashboards.length === 0">
3
- <div class="text-center">
4
- <h3 class="mt-2 text-sm font-semibold text-gray-900">No dashboards yet</h3>
5
- <p class="mt-1 text-sm text-gray-500">Get started by creating a new dashboard.</p>
6
- <div class="mt-6">
7
- <button type="button" class="inline-flex items-center rounded-md bg-teal-600 px-3 py-2 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">
8
- <svg class="-ml-0.5 mr-1.5 h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
9
- <path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
10
- </svg>
11
- New Dashboard
12
- </button>
2
+ <div v-if="status === 'loading'" class="text-center mt-4">
3
+ <svg
4
+ class="inline w-8 h-8 animate-spin text-ultramarine-600"
5
+ xmlns="http://www.w3.org/2000/svg"
6
+ fill="none"
7
+ viewBox="0 0 24 24"
8
+ >
9
+ <circle
10
+ class="opacity-25"
11
+ cx="12"
12
+ cy="12"
13
+ r="10"
14
+ stroke="currentColor"
15
+ stroke-width="4"
16
+ ></circle>
17
+ <path
18
+ class="opacity-75"
19
+ fill="currentColor"
20
+ d="M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
21
+ ></path>
22
+ </svg>
23
+ </div>
24
+ <div v-if="status === 'loaded' && dashboards.length === 0">
25
+ <div class="text-center">
26
+ <h3 class="mt-2 text-sm font-semibold text-gray-900">No dashboards yet</h3>
27
+ <p class="mt-1 text-sm text-gray-500">Get started by creating a new dashboard.</p>
28
+ <div class="mt-6">
29
+ <button type="button" class="inline-flex items-center rounded-md bg-ultramarine-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600">
30
+ <svg class="-ml-0.5 mr-1.5 h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
31
+ <path d="M10.75 4.75a.75.75 0 00-1.5 0v4.5h-4.5a.75.75 0 000 1.5h4.5v4.5a.75.75 0 001.5 0v-4.5h4.5a.75.75 0 000-1.5h-4.5v-4.5z" />
32
+ </svg>
33
+ New Dashboard
34
+ </button>
35
+ </div>
13
36
  </div>
14
37
  </div>
15
- </div>
16
-
17
38
 
18
- <div class="px-4 sm:px-6 lg:px-8">
19
- <div class="sm:flex sm:items-center">
20
- <div class="sm:flex-auto">
21
- <h1 class="text-base font-semibold leading-6 text-gray-900">Dashboards</h1>
22
- </div>
23
- <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
24
- <button
25
- type="button"
26
- @click="showCreateDashboardModal = true"
27
- class="block rounded-md bg-teal-600 px-3 py-2 text-center 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">Create New Dashboard</button>
39
+ <div class="px-4 sm:px-6 lg:px-8">
40
+ <div class="sm:flex sm:items-center">
41
+ <div class="sm:flex-auto">
42
+ <h1 class="text-base font-semibold leading-6 text-gray-900">Dashboards</h1>
43
+ </div>
44
+ <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
45
+ <button
46
+ type="button"
47
+ @click="showCreateDashboardModal = true"
48
+ class="block rounded-md bg-ultramarine-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600">Create New Dashboard</button>
49
+ </div>
28
50
  </div>
29
- </div>
30
- <div class="mt-8 flow-root">
31
- <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
32
- <div class="inline-block min-w-full py-2 align-middle">
33
- <table class="min-w-full divide-y divide-gray-300">
34
- <thead>
35
- <tr>
36
- <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8">Title</th>
37
- <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 w-[50%]">Description</th>
38
- <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8">
39
- </th>
40
- <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8">
41
- </th>
42
- </tr>
43
- </thead>
44
- <tbody class="divide-y divide-gray-200 bg-white">
45
- <tr v-for="dashboard in dashboards">
46
- <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8">{{dashboard.title}}</td>
47
- <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 truncate w-[50%]">{{dashboard.description}}</td>
48
- <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
49
- <router-link
50
- :to="'/dashboard/' + dashboard._id + '?edit=true'"
51
- class="text-teal-600 hover:text-teal-900">
52
- Edit
53
- </router-link>
54
- </td>
55
- <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
56
- <router-link
57
- :to="'/dashboard/' + dashboard._id"
58
- class="text-teal-600 hover:text-teal-900">
59
- View
60
- </router-link>
61
- </td>
62
- <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
63
- <button
64
- @click="showDeleteDashboardModal=dashboard"
65
- class="text-teal-600 hover:text-teal-900">
66
- Delete
67
- </button>
68
- </td>
69
- </tr>
70
-
71
- <!-- More people... -->
72
- </tbody>
73
- </table>
51
+ <div class="mt-8 flow-root">
52
+ <div class="-mx-4 -my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
53
+ <div class="inline-block min-w-full py-2 align-middle">
54
+ <table class="min-w-full divide-y divide-gray-300">
55
+ <thead>
56
+ <tr>
57
+ <th scope="col" class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6 lg:pl-8">Title</th>
58
+ <th scope="col" class="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 w-[50%]">Description</th>
59
+ <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8">
60
+ </th>
61
+ <th scope="col" class="relative py-3.5 pl-3 pr-4 sm:pr-6 lg:pr-8">
62
+ </th>
63
+ </tr>
64
+ </thead>
65
+ <tbody class="divide-y divide-gray-200 bg-white">
66
+ <tr v-for="dashboard in dashboards">
67
+ <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 lg:pl-8">{{dashboard.title}}</td>
68
+ <td class="whitespace-nowrap px-3 py-4 text-sm text-gray-500 truncate w-[50%]">{{dashboard.description}}</td>
69
+ <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
70
+ <router-link
71
+ :to="'/dashboard/' + dashboard._id + '?edit=true'"
72
+ class="text-ultramarine-600 hover:text-ultramarine-900">
73
+ Edit
74
+ </router-link>
75
+ </td>
76
+ <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
77
+ <router-link
78
+ :to="'/dashboard/' + dashboard._id"
79
+ class="text-ultramarine-600 hover:text-ultramarine-900">
80
+ View
81
+ </router-link>
82
+ </td>
83
+ <td class="relative whitespace-nowrap py-4 pl-3 pr-4 text-right text-sm font-medium sm:pr-6 lg:pr-8">
84
+ <button
85
+ @click="showDeleteDashboardModal=dashboard"
86
+ class="text-ultramarine-600 hover:text-ultramarine-900">
87
+ Delete
88
+ </button>
89
+ </td>
90
+ </tr>
91
+
92
+ <!-- More people... -->
93
+ </tbody>
94
+ </table>
95
+ </div>
74
96
  </div>
75
97
  </div>
76
98
  </div>
77
- </div>
78
99
 
79
- <modal v-if="showCreateDashboardModal">
80
- <template v-slot:body>
81
- <div class="modal-exit" @click="showCreateDashboardModal = false;">&times;</div>
82
-
83
- <create-dashboard @close="insertNewDashboard"></create-dashboard>
84
- </template>
85
- </modal>
100
+ <modal v-if="showCreateDashboardModal">
101
+ <template v-slot:body>
102
+ <div class="modal-exit" @click="showCreateDashboardModal = false;">&times;</div>
86
103
 
87
- <modal v-if="showDeleteDashboardModal">
88
- <template v-slot:body>
89
- <div class="modal-exit" @click="showDeleteDashboardModal = null;">&times;</div>
90
- <h2>Are you sure you want to delete this dashboard titled {{showDeleteDashboardModal.title}}?</h2>
91
- <div class="flex space-x-2">
92
- <button class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500" @click="deleteDashboard(showDeleteDashboardModal)">Yes, delete</button>
93
- <button class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-600" @click="showDeleteDashboardModal=null;">Cancel</button>
94
- </div>
95
- </template>
96
- </modal>
104
+ <create-dashboard @close="insertNewDashboard"></create-dashboard>
105
+ </template>
106
+ </modal>
107
+
108
+ <modal v-if="showDeleteDashboardModal">
109
+ <template v-slot:body>
110
+ <div class="modal-exit" @click="showDeleteDashboardModal = null;">&times;</div>
111
+ <h2>Are you sure you want to delete this dashboard titled {{showDeleteDashboardModal.title}}?</h2>
112
+ <div class="flex space-x-2">
113
+ <button class="px-4 py-2 bg-red-500 text-white rounded-md hover:bg-red-600 focus:outline-none focus:ring-2 focus:ring-red-500" @click="deleteDashboard(showDeleteDashboardModal)">Yes, delete</button>
114
+ <button class="px-4 py-2 bg-gray-500 text-white rounded-md hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-gray-600" @click="showDeleteDashboardModal=null;">Cancel</button>
115
+ </div>
116
+ </template>
117
+ </modal>
118
+ </div>
97
119
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.0.116",
3
+ "version": "0.0.118",
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": {