@mongoosejs/studio 0.0.61 → 0.0.62

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.
@@ -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 });
21
+ const context = vm.createContext({ db: this.constructor.db, setTimeout });
22
22
  let result = null;
23
23
  result = await vm.runInContext(formatFunction(this.code), context);
24
24
  if (result.$document?.constructor?.modelName) {
@@ -42,4 +42,4 @@ module.exports = dashboardSchema;
42
42
 
43
43
  const formatFunction = code => `(async function() {
44
44
  ${code}
45
- })();`
45
+ })();`
@@ -343,7 +343,7 @@ module.exports = app => app.component('dashboard-document', {
343
343
  return null;
344
344
  },
345
345
  schemaPaths() {
346
- return Object.keys(this.value.$document?.schemaPaths || {}).sort((k1, k2) => {
346
+ return Object.keys(this.value?.$document?.schemaPaths || {}).sort((k1, k2) => {
347
347
  if (k1 === '_id' && k2 !== '_id') {
348
348
  return -1;
349
349
  }
@@ -351,7 +351,7 @@ module.exports = app => app.component('dashboard-document', {
351
351
  return 1;
352
352
  }
353
353
  return 0;
354
- }).map(key => this.value.$document.schemaPaths[key]);
354
+ }).map(key => this.value?.$document.schemaPaths[key]);
355
355
  }
356
356
  }
357
357
  });
@@ -499,7 +499,8 @@ module.exports = app => app.component('dashboard', {
499
499
  }
500
500
  },
501
501
  mounted: async function() {
502
- const { dashboard, result, error } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
502
+ this.showEditor = this.$route.query.edit;
503
+ const { dashboard, result, error } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: !this.showEditor });
503
504
  if (!dashboard) {
504
505
  return;
505
506
  }
@@ -535,7 +536,7 @@ module.exports = app => app.component('edit-dashboard', {
535
536
  props: ['dashboardId', 'code', 'currentDescription', 'currentTitle'],
536
537
  data: function() {
537
538
  return {
538
- status: 'loading',
539
+ status: 'loaded',
539
540
  editor: null,
540
541
  title: '',
541
542
  description: ''
@@ -546,7 +547,7 @@ module.exports = app => app.component('edit-dashboard', {
546
547
  this.$emit('close')
547
548
  },
548
549
  async updateCode() {
549
- console.log('this.title', this.title, 'this.description', this.description)
550
+ this.status = 'loading';
550
551
  const { doc, result, error } = await api.Dashboard.updateDashboard({
551
552
  dashboardId: this.dashboardId,
552
553
  code: this.editor.getValue(),
@@ -555,6 +556,7 @@ module.exports = app => app.component('edit-dashboard', {
555
556
  });
556
557
  this.$emit('update', { doc, result, error });
557
558
  this.editor.setValue(doc.code);
559
+ this.status = 'loaded';
558
560
  this.closeEditor();
559
561
  }
560
562
  },
@@ -570,12 +572,7 @@ module.exports = app => app.component('edit-dashboard', {
570
572
  lineWrapping: true,
571
573
  showCursorWhenSelecting: true,
572
574
  });
573
- // this.editor.setValue(this.code);
574
- // this.editor.setSize(300, 300); // Ensure the editor has a fixed height
575
-
576
- // this.editor.setCursor(this.editor.lineCount() - 1, this.editor.getLine(this.editor.lineCount() - 1).length);
577
-
578
- this.editor.focus();
575
+ // this.editor.focus();
579
576
  // this.editor.refresh(); // if anything weird happens on load, this usually fixes it. However, this breaks it in this case.
580
577
  this.description = this.currentDescription;
581
578
  this.title = this.currentTitle;
@@ -1900,27 +1897,27 @@ exports.githubLogin = function githubLogin() {
1900
1897
  };
1901
1898
 
1902
1899
  exports.getWorkspaceTeam = function getWorkspaceTeam() {
1903
- return client.post('/getWorkspaceTeam', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id }).then(res => res.data);
1900
+ return client.post('/getWorkspaceTeam', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T22:07:14.883Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnClziXVYs4Kez","stripeSubscriptionId":"sub_1QtcLYIIV4Jx8vgxXFUfKRfo"}._id }).then(res => res.data);
1904
1901
  };
1905
1902
 
1906
1903
  exports.getWorkspaceCustomerPortalLink = function getWorkspaceCustomerPortalLink(params) {
1907
- return client.post('/getWorkspaceCustomerPortalLink', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id, ...params }).then(res => res.data);
1904
+ return client.post('/getWorkspaceCustomerPortalLink', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T22:07:14.883Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnClziXVYs4Kez","stripeSubscriptionId":"sub_1QtcLYIIV4Jx8vgxXFUfKRfo"}._id, ...params }).then(res => res.data);
1908
1905
  };
1909
1906
 
1910
1907
  exports.github = function github(code) {
1911
- return client.post('/github', { code, workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id }).then(res => res.data);
1908
+ return client.post('/github', { code, workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T22:07:14.883Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnClziXVYs4Kez","stripeSubscriptionId":"sub_1QtcLYIIV4Jx8vgxXFUfKRfo"}._id }).then(res => res.data);
1912
1909
  };
1913
1910
 
1914
1911
  exports.inviteToWorkspace = function inviteToWorkspace(params) {
1915
- return client.post('/inviteToWorkspace', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id, ...params }).then(res => res.data);
1912
+ return client.post('/inviteToWorkspace', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T22:07:14.883Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnClziXVYs4Kez","stripeSubscriptionId":"sub_1QtcLYIIV4Jx8vgxXFUfKRfo"}._id, ...params }).then(res => res.data);
1916
1913
  };
1917
1914
 
1918
1915
  exports.me = function me() {
1919
- return client.post('/me', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id }).then(res => res.data);
1916
+ return client.post('/me', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T22:07:14.883Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnClziXVYs4Kez","stripeSubscriptionId":"sub_1QtcLYIIV4Jx8vgxXFUfKRfo"}._id }).then(res => res.data);
1920
1917
  };
1921
1918
 
1922
1919
  exports.removeFromWorkspace = function removeFromWorkspace(params) {
1923
- return client.post('/removeFromWorkspace', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id, ...params }).then(res => res.data);
1920
+ return client.post('/removeFromWorkspace', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T22:07:14.883Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnClziXVYs4Kez","stripeSubscriptionId":"sub_1QtcLYIIV4Jx8vgxXFUfKRfo"}._id, ...params }).then(res => res.data);
1924
1921
  };
1925
1922
 
1926
1923
  exports.hasAPIKey = client.hasAPIKey;
@@ -1948,13 +1945,14 @@ appendCSS(__webpack_require__(/*! ./navbar.css */ "./frontend/src/navbar/navbar.
1948
1945
  module.exports = app => app.component('navbar', {
1949
1946
  template: template,
1950
1947
  props: ['user', 'roles'],
1951
- data: () => ({ nodeEnv: null, showFlyout: false }),
1948
+ inject: ['state'],
1949
+ data: () => ({ showFlyout: false }),
1952
1950
  computed: {
1953
1951
  routeName() {
1954
1952
  return this.$route.name;
1955
1953
  },
1956
1954
  warnEnv() {
1957
- return this.nodeEnv === 'prod' || this.nodeEnv === 'production';
1955
+ return this.state.nodeEnv === 'prod' || this.state.nodeEnv === 'production';
1958
1956
  },
1959
1957
  hasAPIKey() {
1960
1958
  return mothership.hasAPIKey;
@@ -1963,10 +1961,6 @@ module.exports = app => app.component('navbar', {
1963
1961
  return this.roles?.includes('owner') || this.roles?.includes('admin');
1964
1962
  }
1965
1963
  },
1966
- async mounted() {
1967
- const { nodeEnv } = await api.status();
1968
- this.nodeEnv = nodeEnv;
1969
- },
1970
1964
  methods: {
1971
1965
  async loginWithGithub() {
1972
1966
  const { url } = await mothership.githubLogin();
@@ -2064,23 +2058,11 @@ const template = __webpack_require__(/*! ./splash.html */ "./frontend/src/splash
2064
2058
  module.exports = app => app.component('splash', {
2065
2059
  template,
2066
2060
  inject: ['state'],
2061
+ props: ['loading'],
2067
2062
  data: () => ({ error: null }),
2068
2063
  computed: {
2069
2064
  workspaceName() {
2070
- return {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}.name;
2071
- }
2072
- },
2073
- async mounted() {
2074
- const href = window.location.href;
2075
- if (href.match(/\?code=([a-zA-Z0-9]+)$/)) {
2076
- const code = href.match(/\?code=([a-zA-Z0-9]+)$/)[1];
2077
- const { accessToken, user, roles } = await mothership.github(code);
2078
- if (roles == null) {
2079
- this.error = 'You are not authorized to access this workspace';
2080
- return;
2081
- }
2082
- this.state.user = user;
2083
- window.localStorage.setItem('_mongooseStudioAccessToken', accessToken._id);
2065
+ return {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T22:07:14.883Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnClziXVYs4Kez","stripeSubscriptionId":"sub_1QtcLYIIV4Jx8vgxXFUfKRfo"}.name;
2084
2066
  }
2085
2067
  },
2086
2068
  methods: {
@@ -2170,15 +2152,7 @@ module.exports = app => app.component('team', {
2170
2152
  },
2171
2153
  async getWorkspaceCustomerPortalLink() {
2172
2154
  const { url } = await mothership.getWorkspaceCustomerPortalLink();
2173
- window.open(url, '_blank');
2174
-
2175
- const interval = setInterval(async () => {
2176
- const { workspace } = await mothership.getWorkspaceTeam();
2177
- if (workspace.subscriptionTier) {
2178
- this.workspace = workspace;
2179
- clearInterval(interval);
2180
- }
2181
- }, 15000);
2155
+ window.open(url, '_self');
2182
2156
  }
2183
2157
  }
2184
2158
  });
@@ -2892,7 +2866,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
2892
2866
  /***/ ((module) => {
2893
2867
 
2894
2868
  "use strict";
2895
- 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 === 'loaded'\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{title}}</h2>\n <div>\n <button\n v-if=\"!showEditor\"\n @click=\"showEditor = true\"\n type=\"button\"\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 <img src=\"images/edit.svg\" class=\"inline h-[1em]\" /> Edit\n </button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <dashboard-result :result=\"result\"></dashboard-result>\n </div>\n <div v-if=\"showEditor\">\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 === 'loaded'\">\n No dashboard with the given id could be found.\n </div>\n</div>\n";
2869
+ 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 === 'loaded'\" class=\"max-w-5xl mx-auto\">\n <div class=\"flex items-center w-full\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{title}}</h2>\n <div>\n <button\n v-if=\"!showEditor\"\n @click=\"showEditor = true\"\n type=\"button\"\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 <img src=\"images/edit.svg\" class=\"inline h-[1em]\" /> Edit\n </button>\n </div>\n </div>\n <div v-if=\"!showEditor\" class=\"mt-4 mb-4\">\n <dashboard-result :result=\"result\"></dashboard-result>\n </div>\n <div v-if=\"showEditor\">\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 === 'loaded'\">\n No dashboard with the given id could be found.\n </div>\n</div>\n";
2896
2870
 
2897
2871
  /***/ }),
2898
2872
 
@@ -2903,7 +2877,7 @@ module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"status === 'load
2903
2877
  /***/ ((module) => {
2904
2878
 
2905
2879
  "use strict";
2906
- module.exports = "<div class=\"p-4 bg-gray-100 rounded-lg shadow-lg\">\n <div>\n <input v-model=\"title\" class=\"w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\" placeholder=\"Title\"/>\n </div>\n <div>\n <textarea v-model=\"description\" class=\"w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\" rows=\"4\" placeholder=\"Description\">{{description}}</textarea>\n </div>\n <div>\n <textarea ref=\"codeEditor\" class=\"w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\" rows=\"6\">{{code}}</textarea>\n </div>\n <div class=\"flex space-x-2\">\n <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</button>\n <button @click=\"closeEditor\" 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-500\">Cancel</button>\n </div>\n</div>";
2880
+ module.exports = "<div class=\"p-4 bg-gray-100 rounded-lg shadow-lg\">\n <div v-show=\"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-show=\"status !== 'loading'\">\n <div>\n <input v-model=\"title\" class=\"w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\" placeholder=\"Title\"/>\n </div>\n <div>\n <textarea v-model=\"description\" class=\"w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\" rows=\"4\" placeholder=\"Description\">{{description}}</textarea>\n </div>\n <div>\n <textarea ref=\"codeEditor\" class=\"w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\" rows=\"6\">{{code}}</textarea>\n </div>\n <div class=\"flex space-x-2\">\n <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>\n <button @click=\"closeEditor\" 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-500\">Cancel</button>\n </div>\n </div>\n</div>";
2907
2881
 
2908
2882
  /***/ }),
2909
2883
 
@@ -3310,7 +3284,7 @@ module.exports = ".navbar {\n width: 100%;\n background-color: #eee;\n}\n\n.ac
3310
3284
  /***/ ((module) => {
3311
3285
 
3312
3286
  "use strict";
3313
- module.exports = "<div class=\"navbar\">\n <div class=\"nav-left flex items-center gap-4 h-full\">\n <router-link to=\"/\">\n <img src=\"images/logo.svg\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <div v-if=\"!!nodeEnv\" class=\"inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900\" :class=\"warnEnv ? 'bg-red-300' : 'bg-yellow-300'\">\n {{nodeEnv}}\n </div>\n </div>\n <div class=\"nav-right h-full\">\n <div class=\"sm:ml-6 sm:flex sm:space-x-8 h-full\">\n <a\n href=\"#/\"\n class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\"\n :class=\"routeName === 'root' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Documents</a>\n <a\n href=\"#/dashboards\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"routeName === 'dashboards' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Dashboards</a>\n\n <div class=\"h-full flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 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 Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"h-full flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <div>\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800\" id=\"user-menu-button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <span class=\"absolute -inset-1.5\"></span>\n <span class=\"sr-only\">Open user menu</span>\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n </button>\n </div>\n\n <div v-if=\"showFlyout\" class=\"absolute right-0 z-10 top-[90%] w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\" role=\"menu\" aria-orientation=\"vertical\" aria-labelledby=\"user-menu-button\" tabindex=\"-1\">\n <router-link to=\"/team\" v-if=\"canViewTeam\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n\n </div>\n </div>\n <div style=\"clear: both\"></div>\n</div>\n";
3287
+ module.exports = "<div class=\"navbar\">\n <div class=\"nav-left flex items-center gap-4 h-full\">\n <router-link to=\"/\">\n <img src=\"images/logo.svg\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <div v-if=\"!!state.nodeEnv\" class=\"inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900\" :class=\"warnEnv ? 'bg-red-300' : 'bg-yellow-300'\">\n {{state.nodeEnv}}\n </div>\n </div>\n <div class=\"nav-right h-full\">\n <div class=\"sm:ml-6 sm:flex sm:space-x-8 h-full\">\n <a\n href=\"#/\"\n class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\"\n :class=\"routeName === 'root' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Documents</a>\n <a\n href=\"#/dashboards\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"routeName === 'dashboards' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Dashboards</a>\n\n <div class=\"h-full flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 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 Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"h-full flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <div>\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800\" id=\"user-menu-button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <span class=\"absolute -inset-1.5\"></span>\n <span class=\"sr-only\">Open user menu</span>\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n </button>\n </div>\n\n <div v-if=\"showFlyout\" class=\"absolute right-0 z-10 top-[90%] w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\" role=\"menu\" aria-orientation=\"vertical\" aria-labelledby=\"user-menu-button\" tabindex=\"-1\">\n <router-link to=\"/team\" v-if=\"canViewTeam\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n\n </div>\n </div>\n <div style=\"clear: both\"></div>\n</div>\n";
3314
3288
 
3315
3289
  /***/ }),
3316
3290
 
@@ -3321,7 +3295,7 @@ module.exports = "<div class=\"navbar\">\n <div class=\"nav-left flex items-cen
3321
3295
  /***/ ((module) => {
3322
3296
 
3323
3297
  "use strict";
3324
- module.exports = "<div class=\"w-full h-full flex items-center justify-center\">\n <div class=\"text-center\">\n <div class=\"rounded-full bg-gray-100 p-6 inline-block\">\n <img src=\"images/logo.svg\" class=\"w-48 h-48\">\n </div>\n <div class=\"text-lg mt-2 font-bold\">\n Mongoose Studio\n </div>\n <div class=\"mt-2 text-gray-700\">\n {{workspaceName}}\n </div>\n <div class=\"mt-4\">\n <async-button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 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 Login With GitHub\n </async-button>\n </div>\n <div class=\"mt-4\" v-if=\"error\">\n <div class=\"rounded-md bg-red-50 p-4\">\n <div class=\"flex\">\n <div class=\"shrink-0\">\n <svg class=\"size-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\">{{error}}</h3>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
3298
+ module.exports = "<div class=\"w-full h-full flex items-center justify-center\">\n <div class=\"text-center\">\n <div class=\"rounded-full bg-gray-100 p-6 inline-block\">\n <img src=\"images/logo.svg\" class=\"w-48 h-48\">\n </div>\n <div class=\"text-lg mt-2 font-bold\">\n Mongoose Studio\n </div>\n <div v-if=\"loading\" class=\"mt-2\">\n <img src=\"images/loader.gif\" class=\"inline w-16 h-16\">\n </div>\n <div class=\"mt-2 text-gray-700\" v-if=\"!loading\">\n {{workspaceName}}\n </div>\n <div class=\"mt-4\" v-if=\"!loading\">\n <async-button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 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 Login With GitHub\n </async-button>\n </div>\n <div class=\"mt-4\" v-if=\"state.authError\">\n <div class=\"rounded-md bg-red-50 p-4\">\n <div class=\"flex\">\n <div class=\"shrink-0\">\n <svg class=\"size-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\">{{state.authError}}</h3>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n";
3325
3299
 
3326
3300
  /***/ }),
3327
3301
 
@@ -11164,6 +11138,7 @@ if (typeof process === 'undefined') {
11164
11138
  __webpack_require__.g.process = { env: {} }; // To make `util` package work
11165
11139
  }
11166
11140
 
11141
+ const api = __webpack_require__(/*! ./api */ "./frontend/src/api.js");
11167
11142
  const mothership = __webpack_require__(/*! ./mothership */ "./frontend/src/mothership.js");
11168
11143
  const vanillatoasts = __webpack_require__(/*! vanillatoasts */ "./node_modules/vanillatoasts/vanillatoasts.js");
11169
11144
 
@@ -11211,8 +11186,8 @@ __webpack_require__(/*! ./team/new-invitation/new-invitation */ "./frontend/src/
11211
11186
  app.component('app-component', {
11212
11187
  template: `
11213
11188
  <div>
11214
- <div v-if="hasAPIKey && user == null">
11215
- <splash />
11189
+ <div v-if="hasAPIKey && (user == null || status === 'init')">
11190
+ <splash :loading="status === 'init'" />
11216
11191
  </div>
11217
11192
  <div v-else-if="!hasAPIKey || user">
11218
11193
  <navbar :user="user" :roles="roles" />
@@ -11239,19 +11214,45 @@ app.component('app-component', {
11239
11214
  window.$router = this.$router;
11240
11215
 
11241
11216
  if (mothership.hasAPIKey) {
11242
- const token = window.localStorage.getItem('_mongooseStudioAccessToken');
11243
- if (token) {
11244
- const { user, roles } = await mothership.me();
11217
+ const href = window.location.href;
11218
+ if (href.match(/\?code=([a-zA-Z0-9]+)$/)) {
11219
+ const code = href.match(/\?code=([a-zA-Z0-9]+)$/)[1];
11220
+ const { accessToken, user, roles } = await mothership.github(code);
11221
+ if (roles == null) {
11222
+ this.authError = 'You are not authorized to access this workspace';
11223
+ return;
11224
+ }
11245
11225
  this.user = user;
11246
11226
  this.roles = roles;
11227
+ window.localStorage.setItem('_mongooseStudioAccessToken', accessToken._id);
11228
+
11229
+ const { nodeEnv } = await api.status();
11230
+ this.nodeEnv = nodeEnv;
11231
+ } else {
11232
+ const token = window.localStorage.getItem('_mongooseStudioAccessToken');
11233
+ if (token) {
11234
+ const { user, roles } = await mothership.me();
11235
+ this.user = user;
11236
+ this.roles = roles;
11237
+
11238
+ const { nodeEnv } = await api.status();
11239
+ this.nodeEnv = nodeEnv;
11240
+ }
11247
11241
  }
11242
+ } else {
11243
+ const { nodeEnv } = await api.status();
11244
+ this.nodeEnv = nodeEnv;
11248
11245
  }
11246
+ this.status = 'loaded';
11249
11247
  },
11250
11248
  setup() {
11251
11249
  const user = Vue.ref(null);
11252
11250
  const roles = Vue.ref(null);
11251
+ const status = Vue.ref('init');
11252
+ const nodeEnv = Vue.ref(null);
11253
+ const authError = Vue.ref(null);
11253
11254
 
11254
- const state = Vue.reactive({ user, roles });
11255
+ const state = Vue.reactive({ user, roles, status, nodeEnv, authError });
11255
11256
  Vue.provide('state', state);
11256
11257
 
11257
11258
  return state;
@@ -594,10 +594,6 @@ video {
594
594
  pointer-events: none;
595
595
  }
596
596
 
597
- .fixed {
598
- position: fixed;
599
- }
600
-
601
597
  .absolute {
602
598
  position: absolute;
603
599
  }
@@ -789,6 +785,10 @@ video {
789
785
  height: 2rem;
790
786
  }
791
787
 
788
+ .h-16 {
789
+ height: 4rem;
790
+ }
791
+
792
792
  .h-4 {
793
793
  height: 1rem;
794
794
  }
@@ -829,6 +829,10 @@ video {
829
829
  max-height: 50vh;
830
830
  }
831
831
 
832
+ .w-16 {
833
+ width: 4rem;
834
+ }
835
+
832
836
  .w-4 {
833
837
  width: 1rem;
834
838
  }
@@ -15,7 +15,7 @@
15
15
  </button>
16
16
  </div>
17
17
  </div>
18
- <div v-if="!showEditor" class="mt-4 mb-4">
18
+ <div v-if="!showEditor" class="mt-4 mb-4">
19
19
  <dashboard-result :result="result"></dashboard-result>
20
20
  </div>
21
21
  <div v-if="showEditor">
@@ -39,7 +39,7 @@
39
39
  </div>
40
40
  </div>
41
41
  </div>
42
-
42
+
43
43
  </div>
44
44
  <div v-if="!dashboard && status === 'loaded'">
45
45
  No dashboard with the given id could be found.
@@ -34,7 +34,8 @@ module.exports = app => app.component('dashboard', {
34
34
  }
35
35
  },
36
36
  mounted: async function() {
37
- const { dashboard, result, error } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: true });
37
+ this.showEditor = this.$route.query.edit;
38
+ const { dashboard, result, error } = await api.Dashboard.getDashboard({ dashboardId: this.dashboardId, evaluate: !this.showEditor });
38
39
  if (!dashboard) {
39
40
  return;
40
41
  }
@@ -1,15 +1,20 @@
1
1
  <div class="p-4 bg-gray-100 rounded-lg shadow-lg">
2
- <div>
3
- <input v-model="title" class="w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Title"/>
2
+ <div v-show="status === 'loading'" class="max-w-5xl mx-auto text-center">
3
+ <img src="images/loader.gif" class="inline mt-10">
4
4
  </div>
5
- <div>
6
- <textarea v-model="description" class="w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="4" placeholder="Description">{{description}}</textarea>
7
- </div>
8
- <div>
9
- <textarea ref="codeEditor" class="w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="6">{{code}}</textarea>
10
- </div>
11
- <div class="flex space-x-2">
12
- <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</button>
13
- <button @click="closeEditor" 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-500">Cancel</button>
5
+ <div v-show="status !== 'loading'">
6
+ <div>
7
+ <input v-model="title" class="w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Title"/>
8
+ </div>
9
+ <div>
10
+ <textarea v-model="description" class="w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="4" placeholder="Description">{{description}}</textarea>
11
+ </div>
12
+ <div>
13
+ <textarea ref="codeEditor" class="w-full p-2 mb-4 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500" rows="6">{{code}}</textarea>
14
+ </div>
15
+ <div class="flex space-x-2">
16
+ <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>
17
+ <button @click="closeEditor" 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-500">Cancel</button>
18
+ </div>
14
19
  </div>
15
20
  </div>
@@ -8,7 +8,7 @@ module.exports = app => app.component('edit-dashboard', {
8
8
  props: ['dashboardId', 'code', 'currentDescription', 'currentTitle'],
9
9
  data: function() {
10
10
  return {
11
- status: 'loading',
11
+ status: 'loaded',
12
12
  editor: null,
13
13
  title: '',
14
14
  description: ''
@@ -19,7 +19,7 @@ module.exports = app => app.component('edit-dashboard', {
19
19
  this.$emit('close')
20
20
  },
21
21
  async updateCode() {
22
- console.log('this.title', this.title, 'this.description', this.description)
22
+ this.status = 'loading';
23
23
  const { doc, result, error } = await api.Dashboard.updateDashboard({
24
24
  dashboardId: this.dashboardId,
25
25
  code: this.editor.getValue(),
@@ -28,6 +28,7 @@ module.exports = app => app.component('edit-dashboard', {
28
28
  });
29
29
  this.$emit('update', { doc, result, error });
30
30
  this.editor.setValue(doc.code);
31
+ this.status = 'loaded';
31
32
  this.closeEditor();
32
33
  }
33
34
  },
@@ -43,12 +44,7 @@ module.exports = app => app.component('edit-dashboard', {
43
44
  lineWrapping: true,
44
45
  showCursorWhenSelecting: true,
45
46
  });
46
- // this.editor.setValue(this.code);
47
- // this.editor.setSize(300, 300); // Ensure the editor has a fixed height
48
-
49
- // this.editor.setCursor(this.editor.lineCount() - 1, this.editor.getLine(this.editor.lineCount() - 1).length);
50
-
51
- this.editor.focus();
47
+ // this.editor.focus();
52
48
  // this.editor.refresh(); // if anything weird happens on load, this usually fixes it. However, this breaks it in this case.
53
49
  this.description = this.currentDescription;
54
50
  this.title = this.currentTitle;
@@ -15,7 +15,7 @@ module.exports = app => app.component('dashboard-document', {
15
15
  return null;
16
16
  },
17
17
  schemaPaths() {
18
- return Object.keys(this.value.$document?.schemaPaths || {}).sort((k1, k2) => {
18
+ return Object.keys(this.value?.$document?.schemaPaths || {}).sort((k1, k2) => {
19
19
  if (k1 === '_id' && k2 !== '_id') {
20
20
  return -1;
21
21
  }
@@ -23,7 +23,7 @@ module.exports = app => app.component('dashboard-document', {
23
23
  return 1;
24
24
  }
25
25
  return 0;
26
- }).map(key => this.value.$document.schemaPaths[key]);
26
+ }).map(key => this.value?.$document.schemaPaths[key]);
27
27
  }
28
28
  }
29
29
  });
@@ -4,6 +4,7 @@ if (typeof process === 'undefined') {
4
4
  global.process = { env: {} }; // To make `util` package work
5
5
  }
6
6
 
7
+ const api = require('./api');
7
8
  const mothership = require('./mothership');
8
9
  const vanillatoasts = require('vanillatoasts');
9
10
 
@@ -51,8 +52,8 @@ require('./team/new-invitation/new-invitation')(app);
51
52
  app.component('app-component', {
52
53
  template: `
53
54
  <div>
54
- <div v-if="hasAPIKey && user == null">
55
- <splash />
55
+ <div v-if="hasAPIKey && (user == null || status === 'init')">
56
+ <splash :loading="status === 'init'" />
56
57
  </div>
57
58
  <div v-else-if="!hasAPIKey || user">
58
59
  <navbar :user="user" :roles="roles" />
@@ -79,19 +80,45 @@ app.component('app-component', {
79
80
  window.$router = this.$router;
80
81
 
81
82
  if (mothership.hasAPIKey) {
82
- const token = window.localStorage.getItem('_mongooseStudioAccessToken');
83
- if (token) {
84
- const { user, roles } = await mothership.me();
83
+ const href = window.location.href;
84
+ if (href.match(/\?code=([a-zA-Z0-9]+)$/)) {
85
+ const code = href.match(/\?code=([a-zA-Z0-9]+)$/)[1];
86
+ const { accessToken, user, roles } = await mothership.github(code);
87
+ if (roles == null) {
88
+ this.authError = 'You are not authorized to access this workspace';
89
+ return;
90
+ }
85
91
  this.user = user;
86
92
  this.roles = roles;
93
+ window.localStorage.setItem('_mongooseStudioAccessToken', accessToken._id);
94
+
95
+ const { nodeEnv } = await api.status();
96
+ this.nodeEnv = nodeEnv;
97
+ } else {
98
+ const token = window.localStorage.getItem('_mongooseStudioAccessToken');
99
+ if (token) {
100
+ const { user, roles } = await mothership.me();
101
+ this.user = user;
102
+ this.roles = roles;
103
+
104
+ const { nodeEnv } = await api.status();
105
+ this.nodeEnv = nodeEnv;
106
+ }
87
107
  }
108
+ } else {
109
+ const { nodeEnv } = await api.status();
110
+ this.nodeEnv = nodeEnv;
88
111
  }
112
+ this.status = 'loaded';
89
113
  },
90
114
  setup() {
91
115
  const user = Vue.ref(null);
92
116
  const roles = Vue.ref(null);
117
+ const status = Vue.ref('init');
118
+ const nodeEnv = Vue.ref(null);
119
+ const authError = Vue.ref(null);
93
120
 
94
- const state = Vue.reactive({ user, roles });
121
+ const state = Vue.reactive({ user, roles, status, nodeEnv, authError });
95
122
  Vue.provide('state', state);
96
123
 
97
124
  return state;
@@ -3,8 +3,8 @@
3
3
  <router-link to="/">
4
4
  <img src="images/logo.svg" alt="Mongoose Studio Logo" />
5
5
  </router-link>
6
- <div v-if="!!nodeEnv" class="inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900" :class="warnEnv ? 'bg-red-300' : 'bg-yellow-300'">
7
- {{nodeEnv}}
6
+ <div v-if="!!state.nodeEnv" class="inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900" :class="warnEnv ? 'bg-red-300' : 'bg-yellow-300'">
7
+ {{state.nodeEnv}}
8
8
  </div>
9
9
  </div>
10
10
  <div class="nav-right h-full">
@@ -11,13 +11,14 @@ appendCSS(require('./navbar.css'));
11
11
  module.exports = app => app.component('navbar', {
12
12
  template: template,
13
13
  props: ['user', 'roles'],
14
- data: () => ({ nodeEnv: null, showFlyout: false }),
14
+ inject: ['state'],
15
+ data: () => ({ showFlyout: false }),
15
16
  computed: {
16
17
  routeName() {
17
18
  return this.$route.name;
18
19
  },
19
20
  warnEnv() {
20
- return this.nodeEnv === 'prod' || this.nodeEnv === 'production';
21
+ return this.state.nodeEnv === 'prod' || this.state.nodeEnv === 'production';
21
22
  },
22
23
  hasAPIKey() {
23
24
  return mothership.hasAPIKey;
@@ -26,10 +27,6 @@ module.exports = app => app.component('navbar', {
26
27
  return this.roles?.includes('owner') || this.roles?.includes('admin');
27
28
  }
28
29
  },
29
- async mounted() {
30
- const { nodeEnv } = await api.status();
31
- this.nodeEnv = nodeEnv;
32
- },
33
30
  methods: {
34
31
  async loginWithGithub() {
35
32
  const { url } = await mothership.githubLogin();
@@ -6,10 +6,13 @@
6
6
  <div class="text-lg mt-2 font-bold">
7
7
  Mongoose Studio
8
8
  </div>
9
- <div class="mt-2 text-gray-700">
9
+ <div v-if="loading" class="mt-2">
10
+ <img src="images/loader.gif" class="inline w-16 h-16">
11
+ </div>
12
+ <div class="mt-2 text-gray-700" v-if="!loading">
10
13
  {{workspaceName}}
11
14
  </div>
12
- <div class="mt-4">
15
+ <div class="mt-4" v-if="!loading">
13
16
  <async-button
14
17
  type="button"
15
18
  @click="loginWithGithub"
@@ -17,7 +20,7 @@
17
20
  Login With GitHub
18
21
  </async-button>
19
22
  </div>
20
- <div class="mt-4" v-if="error">
23
+ <div class="mt-4" v-if="state.authError">
21
24
  <div class="rounded-md bg-red-50 p-4">
22
25
  <div class="flex">
23
26
  <div class="shrink-0">
@@ -26,7 +29,7 @@
26
29
  </svg>
27
30
  </div>
28
31
  <div class="ml-3">
29
- <h3 class="text-sm font-medium text-red-800">{{error}}</h3>
32
+ <h3 class="text-sm font-medium text-red-800">{{state.authError}}</h3>
30
33
  </div>
31
34
  </div>
32
35
  </div>
@@ -6,25 +6,13 @@ const template = require('./splash.html');
6
6
  module.exports = app => app.component('splash', {
7
7
  template,
8
8
  inject: ['state'],
9
+ props: ['loading'],
9
10
  data: () => ({ error: null }),
10
11
  computed: {
11
12
  workspaceName() {
12
13
  return config__workspace.name;
13
14
  }
14
15
  },
15
- async mounted() {
16
- const href = window.location.href;
17
- if (href.match(/\?code=([a-zA-Z0-9]+)$/)) {
18
- const code = href.match(/\?code=([a-zA-Z0-9]+)$/)[1];
19
- const { accessToken, user, roles } = await mothership.github(code);
20
- if (roles == null) {
21
- this.error = 'You are not authorized to access this workspace';
22
- return;
23
- }
24
- this.state.user = user;
25
- window.localStorage.setItem('_mongooseStudioAccessToken', accessToken._id);
26
- }
27
- },
28
16
  methods: {
29
17
  async loginWithGithub() {
30
18
  const { url } = await mothership.githubLogin();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.0.61",
3
+ "version": "0.0.62",
4
4
  "dependencies": {
5
5
  "archetype": "0.13.1",
6
6
  "csv-stringify": "6.3.0",