@mongoosejs/studio 0.0.60 → 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;
@@ -1794,8 +1791,25 @@ module.exports = app => app.component('models', {
1794
1791
  }).map(key => this.selectedPaths[key]);
1795
1792
  }
1796
1793
  },
1794
+ openFieldSelection() {
1795
+ if (this.$route.query?.fields) {
1796
+ this.selectedPaths.length = 0;
1797
+ console.log('there are fields in play', this.$route.query.fields)
1798
+ const fields = this.$route.query.fields.split(',');
1799
+ for (let i = 0; i < fields.length; i++) {
1800
+ this.selectedPaths.push({ path: fields[i] });
1801
+ }
1802
+ } else {
1803
+ this.selectedPaths = [{ path: '_id' }];
1804
+ }
1805
+ this.shouldShowFieldModal = true;
1806
+ },
1797
1807
  filterDocuments() {
1798
- this.filteredPaths = [...this.selectedPaths];
1808
+ if (this.selectedPaths.length > 0) {
1809
+ this.filteredPaths = [...this.selectedPaths];
1810
+ } else {
1811
+ this.filteredPaths.length = 0;
1812
+ }
1799
1813
  this.shouldShowFieldModal = false;
1800
1814
  const selectedParams = this.filteredPaths.map(x => x.path).join(',');
1801
1815
  this.query.fields = selectedParams;
@@ -1864,10 +1878,10 @@ module.exports = app => app.component('models', {
1864
1878
 
1865
1879
  const axios = __webpack_require__(/*! axios */ "./node_modules/axios/dist/browser/axios.cjs");
1866
1880
  const client = axios.create({
1867
- baseURL: 'http://localhost:8888/.netlify/functions'
1881
+ baseURL: 'https://mongoose-js.netlify.app/.netlify/functions'
1868
1882
  });
1869
1883
 
1870
- client.hasAPIKey = !!'http://localhost:8888/.netlify/functions';
1884
+ client.hasAPIKey = !!'https://mongoose-js.netlify.app/.netlify/functions';
1871
1885
 
1872
1886
  client.interceptors.request.use(req => {
1873
1887
  const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
@@ -1883,19 +1897,27 @@ exports.githubLogin = function githubLogin() {
1883
1897
  };
1884
1898
 
1885
1899
  exports.getWorkspaceTeam = function getWorkspaceTeam() {
1886
- return client.post('/getWorkspaceTeam', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]},{"userId":"67a91f3e5db099be8bd0f5c5","roles":["member"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-09T21:35:14.905Z","__v":1,"name":"Zevo DEV","subscriptionTier":"pro"}._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);
1887
1901
  };
1888
1902
 
1889
- exports.inviteToWorkspace = function inviteToWorkspace(params) {
1890
- return client.post('/inviteToWorkspace', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]},{"userId":"67a91f3e5db099be8bd0f5c5","roles":["member"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-09T21:35:14.905Z","__v":1,"name":"Zevo DEV","subscriptionTier":"pro"}._id, ...params }).then(res => res.data);
1903
+ exports.getWorkspaceCustomerPortalLink = function getWorkspaceCustomerPortalLink(params) {
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);
1891
1905
  };
1892
1906
 
1893
1907
  exports.github = function github(code) {
1894
- return client.post('/github', { code, workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]},{"userId":"67a91f3e5db099be8bd0f5c5","roles":["member"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-09T21:35:14.905Z","__v":1,"name":"Zevo DEV","subscriptionTier":"pro"}._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);
1909
+ };
1910
+
1911
+ exports.inviteToWorkspace = function inviteToWorkspace(params) {
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);
1895
1913
  };
1896
1914
 
1897
1915
  exports.me = function me() {
1898
- return client.post('/me', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]},{"userId":"67a91f3e5db099be8bd0f5c5","roles":["member"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-09T21:35:14.905Z","__v":1,"name":"Zevo DEV","subscriptionTier":"pro"}._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);
1917
+ };
1918
+
1919
+ exports.removeFromWorkspace = function removeFromWorkspace(params) {
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);
1899
1921
  };
1900
1922
 
1901
1923
  exports.hasAPIKey = client.hasAPIKey;
@@ -1923,13 +1945,14 @@ appendCSS(__webpack_require__(/*! ./navbar.css */ "./frontend/src/navbar/navbar.
1923
1945
  module.exports = app => app.component('navbar', {
1924
1946
  template: template,
1925
1947
  props: ['user', 'roles'],
1926
- data: () => ({ nodeEnv: null, showFlyout: false }),
1948
+ inject: ['state'],
1949
+ data: () => ({ showFlyout: false }),
1927
1950
  computed: {
1928
1951
  routeName() {
1929
1952
  return this.$route.name;
1930
1953
  },
1931
1954
  warnEnv() {
1932
- return this.nodeEnv === 'prod' || this.nodeEnv === 'production';
1955
+ return this.state.nodeEnv === 'prod' || this.state.nodeEnv === 'production';
1933
1956
  },
1934
1957
  hasAPIKey() {
1935
1958
  return mothership.hasAPIKey;
@@ -1938,10 +1961,6 @@ module.exports = app => app.component('navbar', {
1938
1961
  return this.roles?.includes('owner') || this.roles?.includes('admin');
1939
1962
  }
1940
1963
  },
1941
- async mounted() {
1942
- const { nodeEnv } = await api.status();
1943
- this.nodeEnv = nodeEnv;
1944
- },
1945
1964
  methods: {
1946
1965
  async loginWithGithub() {
1947
1966
  const { url } = await mothership.githubLogin();
@@ -2039,23 +2058,11 @@ const template = __webpack_require__(/*! ./splash.html */ "./frontend/src/splash
2039
2058
  module.exports = app => app.component('splash', {
2040
2059
  template,
2041
2060
  inject: ['state'],
2061
+ props: ['loading'],
2042
2062
  data: () => ({ error: null }),
2043
2063
  computed: {
2044
2064
  workspaceName() {
2045
- return {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]},{"userId":"67a91f3e5db099be8bd0f5c5","roles":["member"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-09T21:35:14.905Z","__v":1,"name":"Zevo DEV","subscriptionTier":"pro"}.name;
2046
- }
2047
- },
2048
- async mounted() {
2049
- const href = window.location.href;
2050
- if (href.match(/\?code=([a-zA-Z0-9]+)$/)) {
2051
- const code = href.match(/\?code=([a-zA-Z0-9]+)$/)[1];
2052
- const { accessToken, user, roles } = await mothership.github(code);
2053
- if (roles == null) {
2054
- this.error = 'You are not authorized to access this workspace';
2055
- return;
2056
- }
2057
- this.state.user = user;
2058
- 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;
2059
2066
  }
2060
2067
  },
2061
2068
  methods: {
@@ -2119,7 +2126,8 @@ module.exports = app => app.component('team', {
2119
2126
  workspace: null,
2120
2127
  users: null,
2121
2128
  invitations: null,
2122
- showNewInvitationModal: false
2129
+ showNewInvitationModal: false,
2130
+ showRemoveModal: null
2123
2131
  }),
2124
2132
  async mounted() {
2125
2133
  const { workspace, users, invitations } = await mothership.getWorkspaceTeam();
@@ -2127,9 +2135,24 @@ module.exports = app => app.component('team', {
2127
2135
  this.users = users;
2128
2136
  this.invitations = invitations;
2129
2137
  },
2138
+ computed: {
2139
+ paymentLink() {
2140
+ return 'https://buy.stripe.com/test_eVaeYa2jC7565Lq7ss?client_reference_id=' + this.workspace?._id;
2141
+ }
2142
+ },
2130
2143
  methods: {
2131
2144
  getRolesForUser(user) {
2132
2145
  return this.workspace.members.find(member => member.userId === user._id)?.roles ?? [];
2146
+ },
2147
+ async removeFromWorkspace() {
2148
+ const { workspace, users } = await mothership.removeFromWorkspace({ userId: this.showRemoveModal._id });
2149
+ this.workspace = workspace;
2150
+ this.users = users;
2151
+ this.showRemoveModal = false;
2152
+ },
2153
+ async getWorkspaceCustomerPortalLink() {
2154
+ const { url } = await mothership.getWorkspaceCustomerPortalLink();
2155
+ window.open(url, '_self');
2133
2156
  }
2134
2157
  }
2135
2158
  });
@@ -2843,7 +2866,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
2843
2866
  /***/ ((module) => {
2844
2867
 
2845
2868
  "use strict";
2846
- 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";
2847
2870
 
2848
2871
  /***/ }),
2849
2872
 
@@ -2854,7 +2877,7 @@ module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"status === 'load
2854
2877
  /***/ ((module) => {
2855
2878
 
2856
2879
  "use strict";
2857
- 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>";
2858
2881
 
2859
2882
  /***/ }),
2860
2883
 
@@ -3206,7 +3229,7 @@ module.exports = "<div class=\"list-subdocument tooltip\">\n <pre>\n <code r
3206
3229
  /***/ ((module) => {
3207
3230
 
3208
3231
  "use strict";
3209
- module.exports = "/** Vue modal */\n\n.modal-mask {\n position: fixed;\n z-index: 9998;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.5);\n display: table;\n transition: opacity 0.3s ease;\n}\n\n.modal-wrapper {\n display: table-cell;\n vertical-align: middle;\n}\n\n.modal-container {\n width: 600px;\n margin: 0px auto;\n padding: 20px 30px;\n padding-bottom: 40px;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);\n transition: all 0.3s ease;\n font-family: Helvetica, Arial, sans-serif;\n position: relative;\n}\n\n.modal-header {\n margin-top: 0;\n font-size: 18px;\n font-weight: bold;\n}\n\n.modal-header-success {\n color: #42b983;\n}\n\n.modal-header-error {\n color: #ff0000;\n}\n\n.modal-body {\n margin: 20px 0;\n max-height: calc(100vh - 40px - 60px - 10px);\n overflow: auto;\n}\n\n.modal__button--default {\n float: right;\n}\n\n/*\n * The following styles are auto-applied to elements with\n * transition=\"modal\" when their visibility is toggled\n * by Vue.js.\n *\n * You can easily play with the modal transition by editing\n * these styles.\n */\n\n.modal-enter {\n opacity: 0;\n}\n\n.modal-leave-active {\n opacity: 0;\n}\n\n.modal-enter .modal-container,\n.modal-leave-active .modal-container {\n -webkit-transform: scale(1.1);\n transform: scale(1.1);\n}\n\n.modal-container .modal-exit {\n position: absolute;\n right: 0.25em;\n top: 0.25em;\n cursor: pointer;\n font-size: 1.25em;\n height: 1.25em;\n width: 1.25em;\n border-radius: 100%;\n border: 1px solid #ddd;\n display: flex;\n align-items: center;\n justify-content: center;\n padding-bottom: 0.25em;\n}\n";
3232
+ module.exports = "/** Vue modal */\n\n.modal-mask {\n position: fixed;\n z-index: 9998;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.5);\n display: table;\n transition: opacity 0.3s ease;\n}\n\n.modal-wrapper {\n display: table-cell;\n vertical-align: middle;\n}\n\n.modal-container {\n width: 600px;\n margin: 0px auto;\n padding: 20px 30px;\n padding-bottom: 40px;\n background-color: #fff;\n border-radius: 2px;\n box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);\n transition: all 0.3s ease;\n font-family: Helvetica, Arial, sans-serif;\n position: relative;\n}\n\n.modal-header {\n margin-top: 0;\n font-size: 18px;\n font-weight: bold;\n}\n\n.modal-header-success {\n color: #42b983;\n}\n\n.modal-header-error {\n color: #ff0000;\n}\n\n.modal-body {\n margin: 20px 0;\n max-height: calc(100vh - 40px - 60px - 10px);\n overflow: auto;\n}\n\n.modal__button--default {\n float: right;\n}\n\n/*\n * The following styles are auto-applied to elements with\n * transition=\"modal\" when their visibility is toggled\n * by Vue.js.\n *\n * You can easily play with the modal transition by editing\n * these styles.\n */\n\n.modal-enter {\n opacity: 0;\n}\n\n.modal-leave-active {\n opacity: 0;\n}\n\n.modal-enter .modal-container,\n.modal-leave-active .modal-container {\n -webkit-transform: scale(1.1);\n transform: scale(1.1);\n}\n\n.modal-container .modal-exit {\n position: absolute;\n right: 0.25em;\n top: 0.25em;\n cursor: pointer;\n font-size: 1.25em;\n height: 1.25em;\n width: 1.25em;\n border-radius: 100%;\n border: 1px solid #ddd;\n display: flex;\n align-items: center;\n justify-content: center;\n padding-bottom: 0.25em;\n}\n\n.modal-container .modal-exit:hover {\n background-color: #f1f5ff;\n}\n";
3210
3233
 
3211
3234
  /***/ }),
3212
3235
 
@@ -3239,7 +3262,7 @@ module.exports = ".models {\n position: relative;\n display: flex;\n flex-dir
3239
3262
  /***/ ((module) => {
3240
3263
 
3241
3264
  "use strict";
3242
- module.exports = "<div class=\"models\">\n <div>\n <div class=\"flex grow flex-col gap-y-5 overflow-auto border-r border-gray-200 bg-white px-2 h-[calc(100vh-55px)] w-48\">\n <div class=\"flex font-bold font-xl mt-4 pl-2\">\n Models\n </div>\n <nav class=\"flex flex-1 flex-col\">\n <ul role=\"list\" class=\"flex flex-1 flex-col gap-y-7\">\n <li>\n <ul role=\"list\">\n <li v-for=\"model in models\">\n <router-link\n :to=\"'/model/' + model\"\n class=\"block truncate rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold' : 'hover:bg-ultramarine-100'\">\n {{model}}\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n </nav>\n </div>\n\n </div>\n <div class=\"documents\" ref=\"documentsList\">\n <div class=\"relative h-[42px]\">\n <div class=\"documents-menu\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <form @submit.prevent=\"search\" class=\"flex-grow m-0\">\n <input class=\"w-full rounded-md p-1 outline-gray-300 text-lg focus:ring-1 focus:ring-ultramarine-200 focus:ring-offset-0 focus:outline-none\" type=\"text\" placeholder=\"Filter or text\" v-model=\"searchText\" />\n </form>\n <div>\n <span v-if=\"status === 'loading'\">Loading ...</span>\n <span v-if=\"status === 'loaded'\">{{numDocuments === 1 ? numDocuments+ ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"shouldShowExportModal = true\"\n type=\"button\"\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 Export\n </button>\n <button\n @click=\"shouldShowCreateModal = true;\"\n type=\"button\"\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 Create\n </button>\n <button\n @click=\"shouldShowFieldModal = true\"\n type=\"button\"\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 Fields\n </button>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"outputType = 'table'\"\n type=\"button\"\n class=\"relative inline-flex items-center rounded-none rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'table' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/table.svg\">\n </button>\n <button\n @click=\"outputType = 'json'\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <table v-if=\"outputType === 'table'\">\n <thead>\n <th v-for=\"path in filteredPaths\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown')}})\n </span>\n <span class=\"sort-arrow\" @click=\"sortDocs(1, path.path)\">{{sortBy[path.path] == 1 ? 'X' : '↑'}}</span>\n <span class=\"sort-arrow\" @click=\"sortDocs(-1, path.path)\">{{sortBy[path.path] == -1 ? 'X' : '↓'}}</span>\n </th>\n </thead>\n <tbody>\n <tr v-for=\"document in documents\" @click=\"$router.push('/model/' + currentModel + '/document/' + document._id)\" :key=\"document._id\">\n <td v-for=\"schemaPath in filteredPaths\">\n <component\n :is=\"getComponentForPath(schemaPath)\"\n :value=\"getValueForPath(document, schemaPath.path)\"\n :allude=\"getReferenceModel(schemaPath)\">\n </component>\n </td>\n </tr>\n </tbody>\n </table>\n <div v-if=\"outputType === 'json'\">\n <div v-for=\"document in documents\" @click=\"$router.push('/model/' + currentModel + '/document/' + document._id)\" :key=\"document._id\">\n <list-json :value=\"filterDocument(document)\">\n </list-json>\n </div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"loader\">\n <img src=\"images/loader.gif\">\n </div>\n </div>\n <modal v-if=\"shouldShowExportModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowExportModal = false\">&times;</div>\n <export-query-results\n :schemaPaths=\"schemaPaths\"\n :filter=\"filter\"\n :currentModel=\"currentModel\"\n @done=\"shouldShowExportModal = false\">\n </export-query-results>\n </template>\n </modal>\n <modal v-if=\"shouldShowFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowFieldModal = false; selectedPaths = [...filteredPaths];\">&times;</div>\n <div v-for=\"(path, index) in schemaPaths\" :key=\"index\" style=\"margin-bottom: 0.5em\">\n <input type=\"checkbox\" :id=\"'path.path'+index\" @change=\"addOrRemove(path)\" :value=\"path.path\" :checked=\"isSelected(path.path)\" />\n <label :for=\"'path' + index\">{{path.path}}</label>\n </div>\n <div style=\"margin-top: 1em\">\n <button type=\"submit\" @click=\"filterDocuments()\" style=\"color: black;margin-right: 0.5em\">Filter Selection</button>\n <button type=\"submit\" @click=\"deselectAll()\" class=\"gray\" style=\"margin-right: 0.5em\">Deselect All</button>\n <button type=\"submit\" @click=\"resetDocuments()\" class=\"gray\">Cancel</button>\n\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCreateModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCreateModal = false;\">&times;</div>\n <create-document :currentModel=\"currentModel\" :paths=\"schemaPaths\" @close=\"closeCreationModal\"></create-document>\n </template>\n </modal>\n </div>\n</div>\n";
3265
+ module.exports = "<div class=\"models\">\n <div>\n <div class=\"flex grow flex-col gap-y-5 overflow-auto border-r border-gray-200 bg-white px-2 h-[calc(100vh-55px)] w-48\">\n <div class=\"flex font-bold font-xl mt-4 pl-2\">\n Models\n </div>\n <nav class=\"flex flex-1 flex-col\">\n <ul role=\"list\" class=\"flex flex-1 flex-col gap-y-7\">\n <li>\n <ul role=\"list\">\n <li v-for=\"model in models\">\n <router-link\n :to=\"'/model/' + model\"\n class=\"block truncate rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700\"\n :class=\"model === currentModel ? 'bg-ultramarine-100 font-bold' : 'hover:bg-ultramarine-100'\">\n {{model}}\n </router-link>\n </li>\n </ul>\n </li>\n </ul>\n </nav>\n </div>\n\n </div>\n <div class=\"documents\" ref=\"documentsList\">\n <div class=\"relative h-[42px]\">\n <div class=\"documents-menu\">\n <div class=\"flex flex-row items-center w-full gap-2\">\n <form @submit.prevent=\"search\" class=\"flex-grow m-0\">\n <input class=\"w-full rounded-md p-1 border border-gray-300 outline-gray-300 text-lg focus:ring-1 focus:ring-ultramarine-200 focus:ring-offset-0 focus:outline-none\" type=\"text\" placeholder=\"Filter or text\" v-model=\"searchText\" />\n </form>\n <div>\n <span v-if=\"status === 'loading'\">Loading ...</span>\n <span v-if=\"status === 'loaded'\">{{numDocuments === 1 ? numDocuments+ ' document' : numDocuments + ' documents'}}</span>\n </div>\n <button\n @click=\"shouldShowExportModal = true\"\n type=\"button\"\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 Export\n </button>\n <button\n @click=\"shouldShowCreateModal = true;\"\n type=\"button\"\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 Create\n </button>\n <button\n @click=\"openFieldSelection\"\n type=\"button\"\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 Fields\n </button>\n <span class=\"isolate inline-flex rounded-md shadow-sm\">\n <button\n @click=\"outputType = 'table'\"\n type=\"button\"\n class=\"relative inline-flex items-center rounded-none rounded-l-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'table' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/table.svg\">\n </button>\n <button\n @click=\"outputType = 'json'\"\n type=\"button\"\n class=\"relative -ml-px inline-flex items-center rounded-none rounded-r-md px-2 py-2 text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10\"\n :class=\"outputType === 'json' ? 'bg-gray-200' : 'bg-white'\">\n <img class=\"h-5 w-5\" src=\"images/json.svg\">\n </button>\n </span>\n </div>\n </div>\n </div>\n <div class=\"documents-container relative\">\n <table v-if=\"outputType === 'table'\">\n <thead>\n <th v-for=\"path in filteredPaths\">\n {{path.path}}\n <span class=\"path-type\">\n ({{(path.instance || 'unknown')}})\n </span>\n <span class=\"sort-arrow\" @click=\"sortDocs(1, path.path)\">{{sortBy[path.path] == 1 ? 'X' : '↑'}}</span>\n <span class=\"sort-arrow\" @click=\"sortDocs(-1, path.path)\">{{sortBy[path.path] == -1 ? 'X' : '↓'}}</span>\n </th>\n </thead>\n <tbody>\n <tr v-for=\"document in documents\" @click=\"$router.push('/model/' + currentModel + '/document/' + document._id)\" :key=\"document._id\">\n <td v-for=\"schemaPath in filteredPaths\">\n <component\n :is=\"getComponentForPath(schemaPath)\"\n :value=\"getValueForPath(document, schemaPath.path)\"\n :allude=\"getReferenceModel(schemaPath)\">\n </component>\n </td>\n </tr>\n </tbody>\n </table>\n <div v-if=\"outputType === 'json'\">\n <div v-for=\"document in documents\" @click=\"$router.push('/model/' + currentModel + '/document/' + document._id)\" :key=\"document._id\">\n <list-json :value=\"filterDocument(document)\">\n </list-json>\n </div>\n </div>\n <div v-if=\"status === 'loading'\" class=\"loader\">\n <img src=\"images/loader.gif\">\n </div>\n </div>\n </div>\n\n <modal v-if=\"shouldShowExportModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowExportModal = false\">&times;</div>\n <export-query-results\n :schemaPaths=\"schemaPaths\"\n :filter=\"filter\"\n :currentModel=\"currentModel\"\n @done=\"shouldShowExportModal = false\">\n </export-query-results>\n </template>\n </modal>\n <modal v-if=\"shouldShowFieldModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowFieldModal = false; selectedPaths = [...filteredPaths];\">&times;</div>\n <div v-for=\"(path, index) in schemaPaths\" :key=\"index\" class=\"w-5 flex items-center\">\n <input class=\"mt-0 h-4 w-4 rounded border-gray-300 text-sky-600 focus:ring-sky-600 accent-sky-600\" type=\"checkbox\" :id=\"'path.path'+index\" @change=\"addOrRemove(path)\" :value=\"path.path\" :checked=\"isSelected(path.path)\" />\n <div class=\"ml-2 text-gray-700 grow shrink text-left\">\n <label :for=\"'path' + index\">{{path.path}}</label>\n </div>\n </div>\n <div class=\"mt-4 flex gap-2\">\n <button type=\"submit\" @click=\"filterDocuments()\" class=\"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-teal-600\">Filter Selection</button>\n <button type=\"submit\" @click=\"deselectAll()\" class=\"rounded-md bg-valencia-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-valencia-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-600\">Deselect All</button>\n <button type=\"submit\" @click=\"resetDocuments()\" class=\"rounded-md bg-gray-600 px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm hover:bg-gray-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-green-600\" >Cancel</button>\n </div>\n </template>\n </modal>\n <modal v-if=\"shouldShowCreateModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"shouldShowCreateModal = false;\">&times;</div>\n <create-document :currentModel=\"currentModel\" :paths=\"schemaPaths\" @close=\"closeCreationModal\"></create-document>\n </template>\n </modal>\n</div>\n";
3243
3266
 
3244
3267
  /***/ }),
3245
3268
 
@@ -3261,7 +3284,7 @@ module.exports = ".navbar {\n width: 100%;\n background-color: #eee;\n}\n\n.ac
3261
3284
  /***/ ((module) => {
3262
3285
 
3263
3286
  "use strict";
3264
- 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";
3265
3288
 
3266
3289
  /***/ }),
3267
3290
 
@@ -3272,7 +3295,7 @@ module.exports = "<div class=\"navbar\">\n <div class=\"nav-left flex items-cen
3272
3295
  /***/ ((module) => {
3273
3296
 
3274
3297
  "use strict";
3275
- 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";
3276
3299
 
3277
3300
  /***/ }),
3278
3301
 
@@ -3294,7 +3317,7 @@ module.exports = "<div class=\"p-1\">\n <form class=\"space-y-4\">\n <div cl
3294
3317
  /***/ ((module) => {
3295
3318
 
3296
3319
  "use strict";
3297
- module.exports = "<div class=\"mx-auto max-w-5xl py-6 px-2\">\n <div class=\"text-xl font-bold\">\n Current Members\n </div>\n <ul role=\"list\" class=\"divide-y divide-gray-100\">\n <li class=\"flex justify-between gap-x-6 py-5\" v-for=\"user in users\">\n <div class=\"flex min-w-0 gap-x-4\">\n <img class=\"size-12 flex-none rounded-full bg-gray-50\" :src=\"user.picture ?? 'images/logo.svg'\" alt=\"\">\n <div class=\"min-w-0 flex-auto\">\n <p class=\"text-sm/6 font-semibold text-gray-900\">{{user.name}}</p>\n <p class=\"mt-1 truncate text-xs/5 text-gray-500\">{{user.email}}</p>\n </div>\n </div>\n <div class=\"hidden shrink-0 sm:flex sm:flex-col sm:items-end\">\n <p class=\"text-sm/6 text-gray-900 capitalize\">{{getRolesForUser(user).join(', ')}}</p>\n <p class=\"mt-1 text-xs/5 text-gray-500\">Last seen <time datetime=\"2023-01-23T13:23Z\">3h ago</time></p>\n </div>\n </li>\n </ul>\n <div class=\"mt-6\">\n <div class=\"flex items-center justify-between\">\n <div class=\"text-xl font-bold\">\n Invitations\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showNewInvitationModal = 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\">\n New Invitation\n </button>\n </div>\n </div>\n <div class=\"mt-8 flow-root\" v-if=\"invitations?.length > 0\">\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 sm:px-6 lg:px-8\">\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-0\">GitHub Username</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Email</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Status</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Role</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"invitation in invitations\">\n <td class=\"whitespace-nowrap py-5 pl-4 pr-3 text-sm sm:pl-0\">\n {{invitation.githubUsername}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.email}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n <span class=\"inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-600/20\">\n Pending\n </span>\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.roles.join(', ')}}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n\n <div v-if=\"invitations?.length === 0\" class=\"mt-4\">\n <div class=\"text-center\">\n <svg class=\"mx-auto size-12 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path vector-effect=\"non-scaling-stroke\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z\" />\n </svg>\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No invitations</h3>\n <p class=\"mt-1 text-sm text-gray-500\">You have no outstanding invitations</p>\n </div>\n </div>\n </div>\n\n <modal v-if=\"showNewInvitationModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showNewInvitationModal = false\">&times;</div>\n <new-invitation @close=\"showNewInvitationModal = false\" @invitationCreated=\"invitations.push($event.invitation)\"></new-invitation>\n </template>\n </modal>\n</div>\n";
3320
+ module.exports = "<div class=\"mx-auto max-w-5xl py-6 px-2 flex flex-col gap-8\">\n <div>\n <div class=\"text-xl font-bold\">\n Subscription Details\n </div>\n <div v-if=\"workspace && workspace.subscriptionTier\" class=\"mt-4 flex justify-between items-center\">\n <div>\n <span class=\"font-bold\">Tier:</span> {{workspace.subscriptionTier ?? 'No subscription'}}\n </div>\n <div>\n <async-button\n type=\"submit\"\n @click=\"getWorkspaceCustomerPortalLink\"\n class=\"inline-flex items-center justify-center rounded-md border border-transparent bg-ultramarine-600 py-1 px-2 text-sm font-medium text-white shadow-sm hover:bg-ultramarine-500 focus:outline-none focus:ring-2 focus:ring-forest-green-500 focus:ring-offset-2\">\n View in Stripe\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4 ml-1\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25\" />\n </svg>\n </async-button>\n </div>\n </div>\n <div v-if=\"workspace && !workspace.subscriptionTier\" class=\"mt-4 flex justify-between items-center\">\n <div>\n <span class=\"font-bold\">No active subscription</span>\n <div class=\"text-sm text-gray-700\">\n You won't be able to invite your team until you activate a subscription\n </div>\n </div>\n <div>\n <a\n :href=\"paymentLink\"\n target=\"_blank\"\n class=\"inline-flex items-center justify-center rounded-md border border-transparent bg-ultramarine-600 py-1 px-2 text-sm font-medium text-white shadow-sm hover:bg-ultramarine-500 focus:outline-none focus:ring-2 focus:ring-ultramarine-500 focus:ring-offset-2\">\n Subscribe With Stripe\n <svg xmlns=\"http://www.w3.org/2000/svg\" fill=\"none\" viewBox=\"0 0 24 24\" stroke-width=\"1.5\" stroke=\"currentColor\" class=\"w-4 h-4 ml-1\">\n <path stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M13.5 6H5.25A2.25 2.25 0 003 8.25v10.5A2.25 2.25 0 005.25 21h10.5A2.25 2.25 0 0018 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25\" />\n </svg>\n </a>\n </div>\n </div>\n </div>\n <div>\n <div class=\"text-xl font-bold\">\n Current Members\n </div>\n <ul role=\"list\" class=\"divide-y divide-gray-100\">\n <li class=\"flex justify-between gap-x-6 py-5\" v-for=\"user in users\">\n <div class=\"flex min-w-0 gap-x-4\">\n <img class=\"size-12 flex-none rounded-full bg-gray-50\" :src=\"user.picture ?? 'images/logo.svg'\" alt=\"\">\n <div class=\"min-w-0 flex-auto\">\n <p class=\"text-sm/6 font-semibold text-gray-900\">\n {{user.name}}\n <span v-if=\"user.isFreeUser\" class=\"inline-flex items-center rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20\">Free</span>\n </p>\n <p class=\"mt-1 truncate text-xs/5 text-gray-500\">{{user.email ?? 'No Email'}}</p>\n </div>\n </div>\n <div class=\"hidden shrink-0 sm:flex sm:flex-col sm:items-end\">\n <p class=\"text-sm/6 text-gray-900 capitalize\">{{getRolesForUser(user).join(', ')}}</p>\n <div class=\"flex gap-3\">\n <p class=\"mt-1 text-xs/5 text-gray-500 cursor-pointer\">\n Edit\n </p>\n <p class=\"mt-1 text-xs/5 text-valencia-500 cursor-pointer\" @click=\"showRemoveModal = user\">\n Remove\n </p>\n </div>\n </div>\n </li>\n </ul>\n </div>\n <div>\n <div class=\"flex items-center justify-between\">\n <div class=\"text-xl font-bold\">\n Invitations\n </div>\n <div class=\"mt-4 sm:ml-16 sm:mt-0 sm:flex-none\">\n <button\n type=\"button\"\n @click=\"showNewInvitationModal = true\"\n :disabled=\"workspace && !workspace.subscriptionTier\"\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 disabled:bg-gray-500 disabled:cursor-not-allowed focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n New Invitation\n <svg class=\"inline w-4 h-4 ml-1\" v-if=\"workspace && !workspace.subscriptionTier\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n <path fill-rule=\"evenodd\" d=\"M12 1.5a5.25 5.25 0 00-5.25 5.25v3a3 3 0 00-3 3v6.75a3 3 0 003 3h10.5a3 3 0 003-3v-6.75a3 3 0 00-3-3v-3c0-2.9-2.35-5.25-5.25-5.25zm3.75 8.25v-3a3.75 3.75 0 10-7.5 0v3h7.5z\" clip-rule=\"evenodd\" />\n </svg>\n </button>\n </div>\n </div>\n <div class=\"mt-8 flow-root\" v-if=\"invitations?.length > 0\">\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 sm:px-6 lg:px-8\">\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-0\">GitHub Username</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Email</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Status</th>\n <th scope=\"col\" class=\"px-3 py-3.5 text-left text-sm font-semibold text-gray-900\">Role</th>\n </tr>\n </thead>\n <tbody class=\"divide-y divide-gray-200 bg-white\">\n <tr v-for=\"invitation in invitations\">\n <td class=\"whitespace-nowrap py-5 pl-4 pr-3 text-sm sm:pl-0\">\n {{invitation.githubUsername}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.email}}\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n <span class=\"inline-flex items-center rounded-md bg-gray-50 px-2 py-1 text-xs font-medium text-gray-700 ring-1 ring-inset ring-gray-600/20\">\n Pending\n </span>\n </td>\n <td class=\"whitespace-nowrap px-3 py-5 text-sm text-gray-500\">\n {{invitation.roles.join(', ')}}\n </td>\n </tr>\n </tbody>\n </table>\n </div>\n </div>\n </div>\n\n <div v-if=\"invitations?.length === 0\" class=\"mt-4\">\n <div class=\"text-center\">\n <svg class=\"mx-auto size-12 text-gray-400\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" aria-hidden=\"true\">\n <path vector-effect=\"non-scaling-stroke\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M9 13h6m-3-3v6m-9 1V7a2 2 0 012-2h6l2 2h6a2 2 0 012 2v8a2 2 0 01-2 2H5a2 2 0 01-2-2z\" />\n </svg>\n <h3 class=\"mt-2 text-sm font-semibold text-gray-900\">No invitations</h3>\n <p class=\"mt-1 text-sm text-gray-500\">You have no outstanding invitations</p>\n </div>\n </div>\n </div>\n\n <modal v-if=\"showNewInvitationModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showNewInvitationModal = false\">&times;</div>\n <new-invitation @close=\"showNewInvitationModal = false\" @invitationCreated=\"invitations.push($event.invitation)\"></new-invitation>\n </template>\n </modal>\n\n <modal v-if=\"showRemoveModal\">\n <template v-slot:body>\n <div class=\"modal-exit\" @click=\"showRemoveModal = false\">&times;</div>\n <div>\n Are you sure you want to remove user <span class=\"font-bold\">{{showRemoveModal.githubUsername}}</span> from this workspace?\n </div>\n <div class=\"mt-6 grid grid-cols-2 gap-4\">\n <async-button\n @click=\"removeFromWorkspace(showConfirmDeleteModal)\"\n class=\"border-0 mt-0 flex w-full items-center justify-center gap-3 rounded-md bg-valencia-500 hover:bg-valencia-400 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-400\">\n <span class=\"text-sm font-semibold leading-6\">Yes, Remove</span>\n </async-button>\n\n <span @click=\"showRemoveModal = null\" class=\"cursor-pointer flex w-full items-center justify-center gap-3 rounded-md bg-slate-500 hover:bg-slate-400 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-slate-400\">\n <span class=\"text-sm font-semibold leading-6\">Cancel</span>\n </span>\n </div>\n </template>\n </modal>\n</div>\n";
3298
3321
 
3299
3322
  /***/ }),
3300
3323
 
@@ -11115,6 +11138,7 @@ if (typeof process === 'undefined') {
11115
11138
  __webpack_require__.g.process = { env: {} }; // To make `util` package work
11116
11139
  }
11117
11140
 
11141
+ const api = __webpack_require__(/*! ./api */ "./frontend/src/api.js");
11118
11142
  const mothership = __webpack_require__(/*! ./mothership */ "./frontend/src/mothership.js");
11119
11143
  const vanillatoasts = __webpack_require__(/*! vanillatoasts */ "./node_modules/vanillatoasts/vanillatoasts.js");
11120
11144
 
@@ -11162,8 +11186,8 @@ __webpack_require__(/*! ./team/new-invitation/new-invitation */ "./frontend/src/
11162
11186
  app.component('app-component', {
11163
11187
  template: `
11164
11188
  <div>
11165
- <div v-if="hasAPIKey && user == null">
11166
- <splash />
11189
+ <div v-if="hasAPIKey && (user == null || status === 'init')">
11190
+ <splash :loading="status === 'init'" />
11167
11191
  </div>
11168
11192
  <div v-else-if="!hasAPIKey || user">
11169
11193
  <navbar :user="user" :roles="roles" />
@@ -11190,19 +11214,45 @@ app.component('app-component', {
11190
11214
  window.$router = this.$router;
11191
11215
 
11192
11216
  if (mothership.hasAPIKey) {
11193
- const token = window.localStorage.getItem('_mongooseStudioAccessToken');
11194
- if (token) {
11195
- 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
+ }
11196
11225
  this.user = user;
11197
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
+ }
11198
11241
  }
11242
+ } else {
11243
+ const { nodeEnv } = await api.status();
11244
+ this.nodeEnv = nodeEnv;
11199
11245
  }
11246
+ this.status = 'loaded';
11200
11247
  },
11201
11248
  setup() {
11202
11249
  const user = Vue.ref(null);
11203
11250
  const roles = Vue.ref(null);
11251
+ const status = Vue.ref('init');
11252
+ const nodeEnv = Vue.ref(null);
11253
+ const authError = Vue.ref(null);
11204
11254
 
11205
- const state = Vue.reactive({ user, roles });
11255
+ const state = Vue.reactive({ user, roles, status, nodeEnv, authError });
11206
11256
  Vue.provide('state', state);
11207
11257
 
11208
11258
  return state;
@@ -14,10 +14,6 @@ a {
14
14
  font-weight: bold !important;
15
15
  }
16
16
 
17
- input {
18
- outline: #666 solid 1px;
19
- }
20
-
21
17
  .tooltip {
22
18
  position: relative;
23
19
  display: inline-block;