@mongoosejs/studio 0.0.66 → 0.0.68

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/express.js CHANGED
@@ -30,7 +30,7 @@ module.exports = async function(apiUrl, conn, options) {
30
30
  .then(res => res.json()));
31
31
  }
32
32
 
33
- apiUrl = apiUrl || '/admin/api';
33
+ apiUrl = apiUrl || 'api';
34
34
  const backend = Backend(conn);
35
35
 
36
36
  router.use(
@@ -73,7 +73,11 @@ module.exports = async function(apiUrl, conn, options) {
73
73
  );
74
74
 
75
75
  console.log('Workspace', workspace);
76
- frontend(apiUrl, false, options, workspace);
76
+ const { config } = await frontend(apiUrl, false, options, workspace);
77
+ router.get('/config.js', function (req, res) {
78
+ res.setHeader('Content-Type', 'application/javascript');
79
+ res.end(`window.MONGOOSE_STUDIO_CONFIG = ${JSON.stringify(config, null, 2)};`);
80
+ });
77
81
 
78
82
  router.use(express.static(`${__dirname}/frontend/public`));
79
83
 
package/frontend/index.js CHANGED
@@ -1,7 +1,10 @@
1
1
  'use strict';
2
2
 
3
3
  const { execSync, exec } = require('child_process');
4
+ const fs = require('fs');
5
+ const path = require('path');
4
6
  const webpack = require('webpack');
7
+ const webpackConfig = require('./webpack.config');
5
8
 
6
9
  module.exports = async function frontend(apiUrl, isLambda, options, workspace) {
7
10
  const mothershipUrl = options?._mothershipUrl || 'https://mongoose-js.netlify.app/.netlify/functions';
@@ -26,35 +29,20 @@ module.exports = async function frontend(apiUrl, isLambda, options, workspace) {
26
29
  .then(res => res.json()));
27
30
  }
28
31
 
29
- const config = { ...require('./webpack.config'), plugins: [] };
30
- if (apiUrl != null) {
31
- config.plugins = [
32
- new webpack.DefinePlugin({
33
- config__baseURL: `'${apiUrl}'`,
34
- config__isLambda: `${!!isLambda}`
35
- })
36
- ]
37
- }
38
- if (options?.setAuthorizationHeaderFrom) {
39
- config.plugins.push(new webpack.DefinePlugin({
40
- config__setAuthorizationHeaderFrom: `'${options.setAuthorizationHeaderFrom}'`
41
- }));
42
- }
43
- if (options?.apiKey) {
44
- config.plugins.push(new webpack.DefinePlugin({
45
- config__mothershipUrl: options?._mothershipUrl ? `'${options?._mothershipUrl}'` : '\'https://mongoose-js.netlify.app/.netlify/functions\''
46
- }));
47
- } else {
48
- config.plugins.push(new webpack.DefinePlugin({
49
- config__mothershipUrl: '\'\''
50
- }));
32
+ const { apiKey, ...workspaceData } = workspace || {};
33
+ const config = {
34
+ baseURL: apiUrl,
35
+ isLambda,
36
+ mothershipUrl: mothershipUrl ?? '',
37
+ workspace: workspaceData
38
+ };
39
+
40
+ if (isLambda) {
41
+ const configPath = path.join(__dirname, './public/config.js');
42
+ fs.writeFileSync(configPath, `window.MONGOOSE_STUDIO_CONFIG = ${JSON.stringify(config, null, 2)};`);
51
43
  }
52
44
 
53
- const { apiKey, ...workspaceData } = workspace || {};
54
- config.plugins.push(new webpack.DefinePlugin({
55
- config__workspace: JSON.stringify(workspaceData)
56
- }));
57
- const compiler = webpack(config);
45
+ const compiler = webpack(webpackConfig);
58
46
 
59
47
  if (options && options.watch) {
60
48
  compiler.watch({}, (err) => {
@@ -68,7 +56,7 @@ module.exports = async function frontend(apiUrl, isLambda, options, workspace) {
68
56
  childProcess.stdout.on('data', data => console.log('[TAILWIND]', data));
69
57
  childProcess.stderr.on('data', data => console.log('[TAILWIND]', data));
70
58
  } else {
71
- return new Promise((resolve, reject) => {
59
+ await new Promise((resolve, reject) => {
72
60
  compiler.run((err) => {
73
61
  if (err) {
74
62
  reject(err);
@@ -79,4 +67,6 @@ module.exports = async function frontend(apiUrl, isLambda, options, workspace) {
79
67
  });
80
68
  });
81
69
  }
70
+
71
+ return { config };
82
72
  };
@@ -13,9 +13,11 @@
13
13
  const axios = __webpack_require__(/*! axios */ "./node_modules/axios/dist/browser/axios.cjs");
14
14
 
15
15
  const client = axios.create({
16
- baseURL: '/studio/api'
16
+ baseURL: window.MONGOOSE_STUDIO_CONFIG.baseURL
17
17
  });
18
18
 
19
+ console.log('API baseURL', window.MONGOOSE_STUDIO_CONFIG.baseURL);
20
+
19
21
  window.apiClient = client;
20
22
  client.interceptors.request.use(req => {
21
23
  const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
@@ -36,7 +38,51 @@ client.interceptors.response.use(
36
38
  }
37
39
  );
38
40
 
39
- if (false) {} else {
41
+ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
42
+ exports.status = function status() {
43
+ return client.post('', { action: 'status' }).then(res => res.data);
44
+ };
45
+ exports.Dashboard = {
46
+ createDashboard(params) {
47
+ return client.post('', { action: 'Dashboard.createDashboard', ...params }).then(res => res.data);
48
+ },
49
+ getDashboard(params) {
50
+ return client.post('', { action: 'Dashboard.getDashboard', ...params }).then(res => res.data);
51
+ },
52
+ getDashboards(params) {
53
+ return client.post('', { action: 'Dashboard.getDashboards', ...params }).then(res => res.data);
54
+ },
55
+ updateDashboard(params) {
56
+ return client.post('', { action: 'Dashboard.updateDashboard', ...params}).then(res => res.data);
57
+ }
58
+ }
59
+ exports.Model = {
60
+ createChart(params) {
61
+ return client.post('', { action: 'Model.createChart', ...params}).then(res => res.data);
62
+ },
63
+ createDocument(params) {
64
+ return client.post('', { action: 'Model.createDocument', ...params}).then(res => res.data);
65
+ },
66
+ deleteDocument(params) {
67
+ return client.post('', { action: 'Model.deleteDocument', ...params}).then(res => res.data);
68
+ },
69
+ exportQueryResults(params) {
70
+ return client.post('', { action: 'Model.exportQueryResults', ...params }).then(res => res.data);
71
+ },
72
+ getDocument: function getDocument(params) {
73
+ return client.post('', { action: 'Model.getDocument', ...params }).then(res => res.data);
74
+ },
75
+ getDocuments: function getDocuments(params) {
76
+ return client.post('', { action: 'Model.getDocuments', ...params }).then(res => res.data);
77
+ },
78
+ listModels: function listModels() {
79
+ return client.post('', { action: 'Model.listModels' }).then(res => res.data);
80
+ },
81
+ updateDocument: function updateDocument(params) {
82
+ return client.post('', { action: 'Model.updateDocument', ...params }).then(res => res.data);
83
+ }
84
+ };
85
+ } else {
40
86
  exports.status = function status() {
41
87
  return client.get('/status').then(res => res.data);
42
88
  };
@@ -69,7 +115,7 @@ if (false) {} else {
69
115
  },
70
116
  exportQueryResults(params) {
71
117
  const anchor = document.createElement('a');
72
- anchor.href = '/studio/api' + '/Model/exportQueryResults?' + (new URLSearchParams(params)).toString();
118
+ anchor.href = window.MONGOOSE_STUDIO_CONFIG.baseURL + '/Model/exportQueryResults?' + (new URLSearchParams(params)).toString();
73
119
  anchor.target = '_blank';
74
120
  anchor.download = 'export.csv';
75
121
  anchor.click();
@@ -1878,10 +1924,10 @@ module.exports = app => app.component('models', {
1878
1924
 
1879
1925
  const axios = __webpack_require__(/*! axios */ "./node_modules/axios/dist/browser/axios.cjs");
1880
1926
  const client = axios.create({
1881
- baseURL: 'https://mongoose-js.netlify.app/.netlify/functions'
1927
+ baseURL: window.MONGOOSE_STUDIO_CONFIG.mothershipUrl
1882
1928
  });
1883
1929
 
1884
- client.hasAPIKey = !!'https://mongoose-js.netlify.app/.netlify/functions';
1930
+ client.hasAPIKey = !!window.MONGOOSE_STUDIO_CONFIG.mothershipUrl;
1885
1931
 
1886
1932
  client.interceptors.request.use(req => {
1887
1933
  const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
@@ -1897,27 +1943,27 @@ exports.githubLogin = function githubLogin() {
1897
1943
  };
1898
1944
 
1899
1945
  exports.getWorkspaceTeam = function getWorkspaceTeam() {
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);
1946
+ return client.post('/getWorkspaceTeam', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id }).then(res => res.data);
1901
1947
  };
1902
1948
 
1903
1949
  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);
1950
+ return client.post('/getWorkspaceCustomerPortalLink', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
1905
1951
  };
1906
1952
 
1907
1953
  exports.github = function github(code) {
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);
1954
+ return client.post('/github', { code, workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id }).then(res => res.data);
1909
1955
  };
1910
1956
 
1911
1957
  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);
1958
+ return client.post('/inviteToWorkspace', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
1913
1959
  };
1914
1960
 
1915
1961
  exports.me = function me() {
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);
1962
+ return client.post('/me', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id }).then(res => res.data);
1917
1963
  };
1918
1964
 
1919
1965
  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);
1966
+ return client.post('/removeFromWorkspace', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
1921
1967
  };
1922
1968
 
1923
1969
  exports.hasAPIKey = client.hasAPIKey;
@@ -1937,6 +1983,7 @@ exports.hasAPIKey = client.hasAPIKey;
1937
1983
  const api = __webpack_require__(/*! ../api */ "./frontend/src/api.js");
1938
1984
  const mothership = __webpack_require__(/*! ../mothership */ "./frontend/src/mothership.js");
1939
1985
  const template = __webpack_require__(/*! ./navbar.html */ "./frontend/src/navbar/navbar.html");
1986
+ const routes = __webpack_require__(/*! ../routes */ "./frontend/src/routes.js");
1940
1987
 
1941
1988
  const appendCSS = __webpack_require__(/*! ../appendCSS */ "./frontend/src/appendCSS.js");
1942
1989
 
@@ -1948,6 +1995,12 @@ module.exports = app => app.component('navbar', {
1948
1995
  inject: ['state'],
1949
1996
  data: () => ({ showFlyout: false }),
1950
1997
  computed: {
1998
+ dashboardView() {
1999
+ return routes.filter(x => x.name.startsWith('dashboard')).map(x => x.name).includes(this.$route.name)
2000
+ },
2001
+ documentView() {
2002
+ return ['root', 'model', 'document'].includes(this.$route.name);
2003
+ },
1951
2004
  routeName() {
1952
2005
  return this.$route.name;
1953
2006
  },
@@ -2062,7 +2115,7 @@ module.exports = app => app.component('splash', {
2062
2115
  data: () => ({ error: null }),
2063
2116
  computed: {
2064
2117
  workspaceName() {
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;
2118
+ return window.MONGOOSE_STUDIO_CONFIG.workspace.name;
2066
2119
  }
2067
2120
  },
2068
2121
  methods: {
@@ -2131,8 +2184,6 @@ module.exports = app => app.component('team', {
2131
2184
  status: 'loading'
2132
2185
  }),
2133
2186
  async mounted() {
2134
- window.pageState = this;
2135
-
2136
2187
  const { workspace, users, invitations } = await mothership.getWorkspaceTeam();
2137
2188
  this.workspace = workspace;
2138
2189
  this.users = users;
@@ -2141,8 +2192,7 @@ module.exports = app => app.component('team', {
2141
2192
  },
2142
2193
  computed: {
2143
2194
  paymentLink() {
2144
- return 'https://buy.stripe.com/3csaFg8XTdd0d6U7sy?client_reference_id=' + this.workspace?._id;
2145
- // return 'https://buy.stripe.com/test_eVaeYa2jC7565Lq7ss?client_reference_id=' + this.workspace?._id;
2195
+ return 'https://buy.stripe.com/test_eVaeYa2jC7565Lq7ss?client_reference_id=' + this.workspace?._id;
2146
2196
  }
2147
2197
  },
2148
2198
  methods: {
@@ -2871,7 +2921,7 @@ module.exports = "<div class=\"py-2\">\n <div v-if=\"header\" class=\"border-b
2871
2921
  /***/ ((module) => {
2872
2922
 
2873
2923
  "use strict";
2874
- 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\" v-if=\"!showEditor\">\n <h2 class=\"mt-4 mb-4 text-gray-900 font-semibold text-xl grow shrink\">{{title}}</h2>\n <div>\n <button\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\" class=\"mt-4\">\n <edit-dashboard\n :dashboardId=\"dashboard._id\"\n :code=\"code\"\n :currentDescription=\"description\"\n :currentTitle=\"title\"\n @close=\"showEditor=false;\"\n @update=\"updateCode\"></edit-dashboard>\n </div>\n <div v-if=\"errorMessage\" class=\"rounded-md bg-red-50 p-4 mt-4\">\n <div class=\"flex\">\n <div class=\"flex-shrink-0\">\n <svg class=\"h-5 w-5 text-red-400\" viewBox=\"0 0 20 20\" fill=\"currentColor\" aria-hidden=\"true\" data-slot=\"icon\">\n <path fill-rule=\"evenodd\" d=\"M10 18a8 8 0 1 0 0-16 8 8 0 0 0 0 16ZM8.28 7.22a.75.75 0 0 0-1.06 1.06L8.94 10l-1.72 1.72a.75.75 0 1 0 1.06 1.06L10 11.06l1.72 1.72a.75.75 0 1 0 1.06-1.06L11.06 10l1.72-1.72a.75.75 0 0 0-1.06-1.06L10 8.94 8.28 7.22Z\" clip-rule=\"evenodd\" />\n </svg>\n </div>\n <div class=\"ml-3\">\n <h3 class=\"text-sm font-medium text-red-800\">{{errorMessage}}</h3>\n </div>\n </div>\n </div>\n\n </div>\n <div v-if=\"!dashboard && status === 'loaded'\">\n No dashboard with the given id could be found.\n </div>\n</div>\n";
2924
+ 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";
2875
2925
 
2876
2926
  /***/ }),
2877
2927
 
@@ -2882,7 +2932,7 @@ module.exports = "<div class=\"dashboard px-1\">\n <div v-if=\"status === 'load
2882
2932
  /***/ ((module) => {
2883
2933
 
2884
2934
  "use strict";
2885
- 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'\" class=\"flex flex-col gap-4\">\n <div>\n <input v-model=\"title\" class=\"w-full p-2 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 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 class=\"border border-gray-300 rounded-md\">\n <textarea ref=\"codeEditor\" class=\"w-full p-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500\" rows=\"6\">{{code}}</textarea>\n <textarea v-model=\"chatMessage\" class=\"w-full p-2\"></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>\n";
2935
+ 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>";
2886
2936
 
2887
2937
  /***/ }),
2888
2938
 
@@ -3289,7 +3339,7 @@ module.exports = ".navbar {\n width: 100%;\n background-color: #eee;\n}\n\n.ac
3289
3339
  /***/ ((module) => {
3290
3340
 
3291
3341
  "use strict";
3292
- 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";
3342
+ 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=\"documentView ? '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=\"dashboardView ? '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";
3293
3343
 
3294
3344
  /***/ }),
3295
3345
 
@@ -3322,7 +3372,7 @@ module.exports = "<div class=\"p-1\">\n <form class=\"space-y-4\">\n <div cl
3322
3372
  /***/ ((module) => {
3323
3373
 
3324
3374
  "use strict";
3325
- 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=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-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-else-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 <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <ul v-else 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=\"ml-1 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=\"status === 'loading' || (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 v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-if=\"invitations?.length > 0\" 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 <div v-else-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";
3375
+ 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=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-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-else-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 <div v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <ul v-else 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=\"status === 'loading' || (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 v-if=\"status === 'loading'\" class=\"mt-4\">\n <img src=\"images/loader.gif\" class=\"inline w-8 h-8\">\n </div>\n <div v-else-if=\"invitations?.length > 0\" 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 <div v-else-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";
3326
3376
 
3327
3377
  /***/ }),
3328
3378
 
@@ -11222,24 +11272,14 @@ app.component('app-component', {
11222
11272
  const href = window.location.href;
11223
11273
  if (href.match(/\?code=([a-zA-Z0-9]+)$/)) {
11224
11274
  const code = href.match(/\?code=([a-zA-Z0-9]+)$/)[1];
11225
- try {
11226
- const { accessToken, user, roles } = await mothership.github(code);
11227
- if (roles == null) {
11228
- this.authError = 'You are not authorized to access this workspace';
11229
- return;
11230
- }
11231
- this.user = user;
11232
- this.roles = roles;
11233
- window.localStorage.setItem('_mongooseStudioAccessToken', accessToken._id);
11234
- } catch (err) {
11235
- this.authError = 'An error occurred while logging in. Please try again.';
11236
- this.status = 'loaded';
11275
+ const { accessToken, user, roles } = await mothership.github(code);
11276
+ if (roles == null) {
11277
+ this.authError = 'You are not authorized to access this workspace';
11237
11278
  return;
11238
- } finally {
11239
- setTimeout(() => {
11240
- this.$router.replace(this.$router.currentRoute.value.path);
11241
- }, 0);
11242
11279
  }
11280
+ this.user = user;
11281
+ this.roles = roles;
11282
+ window.localStorage.setItem('_mongooseStudioAccessToken', accessToken._id);
11243
11283
 
11244
11284
  const { nodeEnv } = await api.status();
11245
11285
  this.nodeEnv = nodeEnv;
@@ -12,6 +12,7 @@
12
12
  <link rel="stylesheet" href="vanillatoasts/vanillatoasts.css">
13
13
  <link rel="stylesheet" href="https://unpkg.com/codemirror@5.65.16/lib/codemirror.css">
14
14
  <link rel="icon" href="images/logo.svg" type="image/svg+xml">
15
+ <script src="config.js"></script>
15
16
  <script src="https://unpkg.com/vue@3.x"></script>
16
17
  <script src="https://unpkg.com/vue-router@4.0.10"></script>
17
18
  <script src="https://unpkg.com/chart.js@4.2.0/dist/chart.umd.js"></script>
@@ -3,9 +3,11 @@
3
3
  const axios = require('axios');
4
4
 
5
5
  const client = axios.create({
6
- baseURL: config__baseURL
6
+ baseURL: window.MONGOOSE_STUDIO_CONFIG.baseURL
7
7
  });
8
8
 
9
+ console.log('API baseURL:', window.MONGOOSE_STUDIO_CONFIG.baseURL);
10
+
9
11
  window.apiClient = client;
10
12
  client.interceptors.request.use(req => {
11
13
  const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
@@ -26,7 +28,7 @@ client.interceptors.response.use(
26
28
  }
27
29
  );
28
30
 
29
- if (config__isLambda) {
31
+ if (window.MONGOOSE_STUDIO_CONFIG.isLambda) {
30
32
  exports.status = function status() {
31
33
  return client.post('', { action: 'status' }).then(res => res.data);
32
34
  };
@@ -103,7 +105,7 @@ if (config__isLambda) {
103
105
  },
104
106
  exportQueryResults(params) {
105
107
  const anchor = document.createElement('a');
106
- anchor.href = config__baseURL + '/Model/exportQueryResults?' + (new URLSearchParams(params)).toString();
108
+ anchor.href = window.MONGOOSE_STUDIO_CONFIG.baseURL + '/Model/exportQueryResults?' + (new URLSearchParams(params)).toString();
107
109
  anchor.target = '_blank';
108
110
  anchor.download = 'export.csv';
109
111
  anchor.click();
@@ -2,10 +2,12 @@
2
2
 
3
3
  const axios = require('axios');
4
4
  const client = axios.create({
5
- baseURL: config__mothershipUrl
5
+ baseURL: window.MONGOOSE_STUDIO_CONFIG.mothershipUrl
6
6
  });
7
7
 
8
- client.hasAPIKey = !!config__mothershipUrl;
8
+ console.log('Mothership baseURL:', window.MONGOOSE_STUDIO_CONFIG.baseURL);
9
+
10
+ client.hasAPIKey = !!window.MONGOOSE_STUDIO_CONFIG.mothershipUrl;
9
11
 
10
12
  client.interceptors.request.use(req => {
11
13
  const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
@@ -21,27 +23,27 @@ exports.githubLogin = function githubLogin() {
21
23
  };
22
24
 
23
25
  exports.getWorkspaceTeam = function getWorkspaceTeam() {
24
- return client.post('/getWorkspaceTeam', { workspaceId: config__workspace._id }).then(res => res.data);
26
+ return client.post('/getWorkspaceTeam', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id }).then(res => res.data);
25
27
  };
26
28
 
27
29
  exports.getWorkspaceCustomerPortalLink = function getWorkspaceCustomerPortalLink(params) {
28
- return client.post('/getWorkspaceCustomerPortalLink', { workspaceId: config__workspace._id, ...params }).then(res => res.data);
30
+ return client.post('/getWorkspaceCustomerPortalLink', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
29
31
  };
30
32
 
31
33
  exports.github = function github(code) {
32
- return client.post('/github', { code, workspaceId: config__workspace._id }).then(res => res.data);
34
+ return client.post('/github', { code, workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id }).then(res => res.data);
33
35
  };
34
36
 
35
37
  exports.inviteToWorkspace = function inviteToWorkspace(params) {
36
- return client.post('/inviteToWorkspace', { workspaceId: config__workspace._id, ...params }).then(res => res.data);
38
+ return client.post('/inviteToWorkspace', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
37
39
  };
38
40
 
39
41
  exports.me = function me() {
40
- return client.post('/me', { workspaceId: config__workspace._id }).then(res => res.data);
42
+ return client.post('/me', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id }).then(res => res.data);
41
43
  };
42
44
 
43
45
  exports.removeFromWorkspace = function removeFromWorkspace(params) {
44
- return client.post('/removeFromWorkspace', { workspaceId: config__workspace._id, ...params }).then(res => res.data);
46
+ return client.post('/removeFromWorkspace', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
45
47
  };
46
48
 
47
49
  exports.hasAPIKey = client.hasAPIKey;
@@ -12,11 +12,11 @@
12
12
  <a
13
13
  href="#/"
14
14
  class="inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium"
15
- :class="routeName === 'root' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'">Documents</a>
15
+ :class="documentView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'">Documents</a>
16
16
  <a
17
17
  href="#/dashboards"
18
18
  class="inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium"
19
- :class="routeName === 'dashboards' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'">Dashboards</a>
19
+ :class="dashboardView ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'">Dashboards</a>
20
20
 
21
21
  <div class="h-full flex items-center" v-if="!user && hasAPIKey">
22
22
  <button
@@ -3,6 +3,7 @@
3
3
  const api = require('../api');
4
4
  const mothership = require('../mothership');
5
5
  const template = require('./navbar.html');
6
+ const routes = require('../routes');
6
7
 
7
8
  const appendCSS = require('../appendCSS');
8
9
 
@@ -14,6 +15,12 @@ module.exports = app => app.component('navbar', {
14
15
  inject: ['state'],
15
16
  data: () => ({ showFlyout: false }),
16
17
  computed: {
18
+ dashboardView() {
19
+ return routes.filter(x => x.name.startsWith('dashboard')).map(x => x.name).includes(this.$route.name)
20
+ },
21
+ documentView() {
22
+ return ['root', 'model', 'document'].includes(this.$route.name);
23
+ },
17
24
  routeName() {
18
25
  return this.$route.name;
19
26
  },
@@ -10,7 +10,7 @@ module.exports = app => app.component('splash', {
10
10
  data: () => ({ error: null }),
11
11
  computed: {
12
12
  workspaceName() {
13
- return config__workspace.name;
13
+ return window.MONGOOSE_STUDIO_CONFIG.workspace.name;
14
14
  }
15
15
  },
16
16
  methods: {
@@ -55,7 +55,7 @@
55
55
  <img class="size-12 flex-none rounded-full bg-gray-50" :src="user.picture ?? 'images/logo.svg'" alt="">
56
56
  <div class="min-w-0 flex-auto">
57
57
  <p class="text-sm/6 font-semibold text-gray-900">
58
- {{user.name}}
58
+ {{user.name || user.githubUsername}}
59
59
  <span v-if="user.isFreeUser" class="ml-1 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>
60
60
  </p>
61
61
  <p class="mt-1 truncate text-xs/5 text-gray-500">{{user.email ?? 'No Email'}}</p>
@@ -16,11 +16,6 @@ module.exports = {
16
16
  path: `${__dirname}/public`,
17
17
  filename: '[name].js'
18
18
  },
19
- plugins: [
20
- new webpack.DefinePlugin({
21
- config__baseURL: '\'/.netlify/functions/admin\''
22
- })
23
- ],
24
19
  module: {
25
20
  rules: [
26
21
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.0.66",
3
+ "version": "0.0.68",
4
4
  "dependencies": {
5
5
  "archetype": "0.13.1",
6
6
  "csv-stringify": "6.3.0",