@mongoosejs/studio 0.0.59 → 0.0.61

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.
@@ -1794,8 +1794,25 @@ module.exports = app => app.component('models', {
1794
1794
  }).map(key => this.selectedPaths[key]);
1795
1795
  }
1796
1796
  },
1797
+ openFieldSelection() {
1798
+ if (this.$route.query?.fields) {
1799
+ this.selectedPaths.length = 0;
1800
+ console.log('there are fields in play', this.$route.query.fields)
1801
+ const fields = this.$route.query.fields.split(',');
1802
+ for (let i = 0; i < fields.length; i++) {
1803
+ this.selectedPaths.push({ path: fields[i] });
1804
+ }
1805
+ } else {
1806
+ this.selectedPaths = [{ path: '_id' }];
1807
+ }
1808
+ this.shouldShowFieldModal = true;
1809
+ },
1797
1810
  filterDocuments() {
1798
- this.filteredPaths = [...this.selectedPaths];
1811
+ if (this.selectedPaths.length > 0) {
1812
+ this.filteredPaths = [...this.selectedPaths];
1813
+ } else {
1814
+ this.filteredPaths.length = 0;
1815
+ }
1799
1816
  this.shouldShowFieldModal = false;
1800
1817
  const selectedParams = this.filteredPaths.map(x => x.path).join(',');
1801
1818
  this.query.fields = selectedParams;
@@ -1864,10 +1881,10 @@ module.exports = app => app.component('models', {
1864
1881
 
1865
1882
  const axios = __webpack_require__(/*! axios */ "./node_modules/axios/dist/browser/axios.cjs");
1866
1883
  const client = axios.create({
1867
- baseURL: 'http://localhost:8888/.netlify/functions'
1884
+ baseURL: 'https://mongoose-js.netlify.app/.netlify/functions'
1868
1885
  });
1869
1886
 
1870
- client.hasAPIKey = !!'http://localhost:8888/.netlify/functions';
1887
+ client.hasAPIKey = !!'https://mongoose-js.netlify.app/.netlify/functions';
1871
1888
 
1872
1889
  client.interceptors.request.use(req => {
1873
1890
  const accessToken = window.localStorage.getItem('_mongooseStudioAccessToken') || null;
@@ -1883,19 +1900,27 @@ exports.githubLogin = function githubLogin() {
1883
1900
  };
1884
1901
 
1885
1902
  exports.getWorkspaceTeam = function getWorkspaceTeam() {
1886
- 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-06T22:24:05.260Z","__v":0,"name":"Zevo DEV"}._id }).then(res => res.data);
1903
+ return client.post('/getWorkspaceTeam', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id }).then(res => res.data);
1887
1904
  };
1888
1905
 
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"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-06T22:24:05.260Z","__v":0,"name":"Zevo DEV"}._id, ...params }).then(res => res.data);
1906
+ exports.getWorkspaceCustomerPortalLink = function getWorkspaceCustomerPortalLink(params) {
1907
+ return client.post('/getWorkspaceCustomerPortalLink', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id, ...params }).then(res => res.data);
1891
1908
  };
1892
1909
 
1893
1910
  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"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-06T22:24:05.260Z","__v":0,"name":"Zevo DEV"}._id }).then(res => res.data);
1911
+ return client.post('/github', { code, workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id }).then(res => res.data);
1912
+ };
1913
+
1914
+ exports.inviteToWorkspace = function inviteToWorkspace(params) {
1915
+ return client.post('/inviteToWorkspace', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id, ...params }).then(res => res.data);
1895
1916
  };
1896
1917
 
1897
1918
  exports.me = function me() {
1898
- 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-06T22:24:05.260Z","__v":0,"name":"Zevo DEV"}._id }).then(res => res.data);
1919
+ return client.post('/me', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id }).then(res => res.data);
1920
+ };
1921
+
1922
+ exports.removeFromWorkspace = function removeFromWorkspace(params) {
1923
+ return client.post('/removeFromWorkspace', { workspaceId: {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}._id, ...params }).then(res => res.data);
1899
1924
  };
1900
1925
 
1901
1926
  exports.hasAPIKey = client.hasAPIKey;
@@ -2042,7 +2067,7 @@ module.exports = app => app.component('splash', {
2042
2067
  data: () => ({ error: null }),
2043
2068
  computed: {
2044
2069
  workspaceName() {
2045
- 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-06T22:24:05.260Z","__v":0,"name":"Zevo DEV"}.name;
2070
+ return {"_id":"67a5366c745bb0e6735950dc","ownerId":"679ba73e4cc3ddc28f6ef6af","baseUrl":"https://web.zevo.io","members":[{"userId":"679ba73e4cc3ddc28f6ef6af","roles":["owner"]}],"createdAt":"2025-02-06T22:23:40.903Z","updatedAt":"2025-02-17T21:53:20.548Z","__v":2,"name":"Zevo DEV","subscriptionTier":"pro","stripeCustomerId":"cus_RnCdSdRYLJQl4Z","stripeSubscriptionId":"sub_1QtcEgIIV4Jx8vgxY7z8cKeC"}.name;
2046
2071
  }
2047
2072
  },
2048
2073
  async mounted() {
@@ -2119,7 +2144,8 @@ module.exports = app => app.component('team', {
2119
2144
  workspace: null,
2120
2145
  users: null,
2121
2146
  invitations: null,
2122
- showNewInvitationModal: false
2147
+ showNewInvitationModal: false,
2148
+ showRemoveModal: null
2123
2149
  }),
2124
2150
  async mounted() {
2125
2151
  const { workspace, users, invitations } = await mothership.getWorkspaceTeam();
@@ -2127,9 +2153,32 @@ module.exports = app => app.component('team', {
2127
2153
  this.users = users;
2128
2154
  this.invitations = invitations;
2129
2155
  },
2156
+ computed: {
2157
+ paymentLink() {
2158
+ return 'https://buy.stripe.com/test_eVaeYa2jC7565Lq7ss?client_reference_id=' + this.workspace?._id;
2159
+ }
2160
+ },
2130
2161
  methods: {
2131
2162
  getRolesForUser(user) {
2132
2163
  return this.workspace.members.find(member => member.userId === user._id)?.roles ?? [];
2164
+ },
2165
+ async removeFromWorkspace() {
2166
+ const { workspace, users } = await mothership.removeFromWorkspace({ userId: this.showRemoveModal._id });
2167
+ this.workspace = workspace;
2168
+ this.users = users;
2169
+ this.showRemoveModal = false;
2170
+ },
2171
+ async getWorkspaceCustomerPortalLink() {
2172
+ const { url } = await mothership.getWorkspaceCustomerPortalLink();
2173
+ window.open(url, '_blank');
2174
+
2175
+ const interval = setInterval(async () => {
2176
+ const { workspace } = await mothership.getWorkspaceTeam();
2177
+ if (workspace.subscriptionTier) {
2178
+ this.workspace = workspace;
2179
+ clearInterval(interval);
2180
+ }
2181
+ }, 15000);
2133
2182
  }
2134
2183
  }
2135
2184
  });
@@ -3206,7 +3255,7 @@ module.exports = "<div class=\"list-subdocument tooltip\">\n <pre>\n <code r
3206
3255
  /***/ ((module) => {
3207
3256
 
3208
3257
  "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";
3258
+ 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
3259
 
3211
3260
  /***/ }),
3212
3261
 
@@ -3228,7 +3277,7 @@ module.exports = "<transition name=\"modal\">\n <div class=\"modal-mask\">\n
3228
3277
  /***/ ((module) => {
3229
3278
 
3230
3279
  "use strict";
3231
- module.exports = ".models {\n position: relative;\n display: flex;\n flex-direction: row;\n min-height: calc(100% - 56px);\n}\n\n.models button.gray {\n color: black;\n background-color: #eee;\n}\n\n.models .model-selector {\n background-color: #eee;\n flex-grow: 0;\n padding: 15px;\n padding-top: 0px;\n}\n\n.models h1 {\n margin-top: 0px;\n}\n\n.models .documents {\n flex-grow: 1;\n overflow: scroll;\n max-height: calc(100vh - 56px);\n}\n\n.models .documents table {\n /* max-width: -moz-fit-content;\n max-width: fit-content; */\n width: 100%;\n table-layout: auto;\n font-size: small;\n padding: 0;\n margin-right: 1em;\n white-space: nowrap;\n z-index: -1;\n border-collapse: collapse;\n line-height: 1.5em;\n}\n\n.models .documents table th {\n position: sticky;\n top: 0px;\n background-color: white;\n z-index: 1;\n}\n\n.models .documents table th:after {\n content: \"\";\n position: absolute;\n left: 0;\n width: 100%;\n bottom: -1px;\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n}\n\n.models .documents table tr {\n color: black;\n border-spacing: 0px 0px;\n background-color: white;\n cursor: pointer;\n}\n\n.models .documents table tr:nth-child(even) {\n background-color: #f5f5f5;\n}\n\n.models .documents table tr:hover {\n background-color: #a7b9ff;\n}\n\n.models .documents table th,\ntd {\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n text-align: left;\n padding: 0 16px;\n height: 48px;\n}\n\n.models textarea {\n font-size: 1.2em;\n}\n\n.models .path-type {\n color: rgba(0, 0, 0, 0.36);\n font-size: 0.8em;\n}\n\n.models .documents-menu {\n display: flex;\n margin: 0.25em;\n width: calc(100vw - 220px);\n}\n\n.models .documents-menu .search-input {\n flex-grow: 1;\n align-items: center;\n}\n\n.models .search-input input {\n padding: 0.25em 0.5em;\n font-size: 1.1em;\n border: 1px solid #ddd;\n border-radius: 3px;\n width: calc(100% - 1em);\n}\n\n.models .sort-arrow {\n padding-left: 10px;\n padding-right: 10px;\n}\n\n.models .loader {\n width: 100%;\n text-align: center;\n}\n\n.models .loader img {\n height: 4em;\n}\n\n.models .documents .buttons {\n display: inline-flex;\n justify-content: space-around;\n align-items: center;\n}\n";
3280
+ module.exports = ".models {\n position: relative;\n display: flex;\n flex-direction: row;\n min-height: calc(100% - 56px);\n}\n\n.models button.gray {\n color: black;\n background-color: #eee;\n}\n\n.models .model-selector {\n background-color: #eee;\n flex-grow: 0;\n padding: 15px;\n padding-top: 0px;\n}\n\n.models h1 {\n margin-top: 0px;\n}\n\n.models .documents {\n flex-grow: 1;\n overflow: scroll;\n max-height: calc(100vh - 56px);\n}\n\n.models .documents table {\n /* max-width: -moz-fit-content;\n max-width: fit-content; */\n width: 100%;\n table-layout: auto;\n font-size: small;\n padding: 0;\n margin-right: 1em;\n white-space: nowrap;\n z-index: -1;\n border-collapse: collapse;\n line-height: 1.5em;\n}\n\n.models .documents table th {\n position: sticky;\n top: 42px;\n background-color: white;\n z-index: 1;\n}\n\n.models .documents table th:after {\n content: \"\";\n position: absolute;\n left: 0;\n width: 100%;\n bottom: -1px;\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n}\n\n.models .documents table tr {\n color: black;\n border-spacing: 0px 0px;\n background-color: white;\n cursor: pointer;\n}\n\n.models .documents table tr:nth-child(even) {\n background-color: #f5f5f5;\n}\n\n.models .documents table tr:hover {\n background-color: #a7b9ff;\n}\n\n.models .documents table th,\ntd {\n border-bottom: thin solid rgba(0, 0, 0, 0.12);\n text-align: left;\n padding: 0 16px;\n height: 48px;\n}\n\n.models textarea {\n font-size: 1.2em;\n}\n\n.models .path-type {\n color: rgba(0, 0, 0, 0.36);\n font-size: 0.8em;\n}\n\n.models .documents-menu {\n position: fixed;\n background-color: white;\n z-index: 1;\n padding: 4px;\n display: flex;\n width: calc(100vw - 12rem);\n}\n\n.models .documents-menu .search-input {\n flex-grow: 1;\n align-items: center;\n}\n\n.models .search-input input {\n padding: 0.25em 0.5em;\n font-size: 1.1em;\n border: 1px solid #ddd;\n border-radius: 3px;\n width: calc(100% - 1em);\n}\n\n.models .sort-arrow {\n padding-left: 10px;\n padding-right: 10px;\n}\n\n.models .loader {\n width: 100%;\n text-align: center;\n}\n\n.models .loader img {\n height: 4em;\n}\n\n.models .documents .buttons {\n display: inline-flex;\n justify-content: space-around;\n align-items: center;\n}\n";
3232
3281
 
3233
3282
  /***/ }),
3234
3283
 
@@ -3239,7 +3288,7 @@ module.exports = ".models {\n position: relative;\n display: flex;\n flex-dir
3239
3288
  /***/ ((module) => {
3240
3289
 
3241
3290
  "use strict";
3242
- module.exports = "<div class=\"models\">\n <div>\n <div class=\"flex grow flex-col gap-y-5 overflow-y-auto border-r border-gray-200 bg-white px-2 h-[calc(100vh-55px)]\">\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 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>\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\" 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\">\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";
3291
+ 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
3292
 
3244
3293
  /***/ }),
3245
3294
 
@@ -3261,7 +3310,7 @@ module.exports = ".navbar {\n width: 100%;\n background-color: #eee;\n}\n\n.ac
3261
3310
  /***/ ((module) => {
3262
3311
 
3263
3312
  "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";
3313
+ module.exports = "<div class=\"navbar\">\n <div class=\"nav-left flex items-center gap-4 h-full\">\n <router-link to=\"/\">\n <img src=\"images/logo.svg\" alt=\"Mongoose Studio Logo\" />\n </router-link>\n <div v-if=\"!!nodeEnv\" class=\"inline-flex items-center rounded-md px-2 py-1 text-sm font-medium text-gray-900\" :class=\"warnEnv ? 'bg-red-300' : 'bg-yellow-300'\">\n {{nodeEnv}}\n </div>\n </div>\n <div class=\"nav-right h-full\">\n <div class=\"sm:ml-6 sm:flex sm:space-x-8 h-full\">\n <a\n href=\"#/\"\n class=\"inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium\"\n :class=\"routeName === 'root' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Documents</a>\n <a\n href=\"#/dashboards\"\n class=\"inline-flex items-center border-b-2 px-1 pt-1 text-sm font-medium\"\n :class=\"routeName === 'dashboards' ? 'text-gray-900 border-ultramarine-500' : 'border-transparent text-gray-500 hover:border-gray-300 hover:text-gray-700'\">Dashboards</a>\n\n <div class=\"h-full flex items-center\" v-if=\"!user && hasAPIKey\">\n <button\n type=\"button\"\n @click=\"loginWithGithub\"\n class=\"rounded bg-ultramarine-600 px-2 py-2 text-sm font-semibold text-white shadow-sm hover:bg-ultramarine-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-600\">\n Login\n </button>\n </div>\n <div v-if=\"user && hasAPIKey\" class=\"h-full flex items-center relative\" v-clickOutside=\"hideFlyout\">\n <div>\n <button type=\"button\" @click=\"showFlyout = !showFlyout\" class=\"relative flex rounded-full bg-gray-800 text-sm focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800\" id=\"user-menu-button\" aria-expanded=\"false\" aria-haspopup=\"true\">\n <span class=\"absolute -inset-1.5\"></span>\n <span class=\"sr-only\">Open user menu</span>\n <img class=\"size-8 rounded-full\" :src=\"user.picture\" alt=\"\">\n </button>\n </div>\n\n <div v-if=\"showFlyout\" class=\"absolute right-0 z-10 top-[90%] w-48 origin-top-right rounded-md bg-white py-1 shadow-lg ring-1 ring-black/5 focus:outline-none\" role=\"menu\" aria-orientation=\"vertical\" aria-labelledby=\"user-menu-button\" tabindex=\"-1\">\n <router-link to=\"/team\" v-if=\"canViewTeam\" @click=\"showFlyout = false\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Team</router-link>\n <span @click=\"logout\" class=\"cursor-pointer block px-4 py-2 text-sm text-gray-700 hover:bg-ultramarine-200\" role=\"menuitem\" tabindex=\"-1\" id=\"user-menu-item-2\">Sign out</span>\n </div>\n </div>\n\n </div>\n </div>\n <div style=\"clear: both\"></div>\n</div>\n";
3265
3314
 
3266
3315
  /***/ }),
3267
3316
 
@@ -3294,7 +3343,7 @@ module.exports = "<div class=\"p-1\">\n <form class=\"space-y-4\">\n <div cl
3294
3343
  /***/ ((module) => {
3295
3344
 
3296
3345
  "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";
3346
+ 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
3347
 
3299
3348
  /***/ }),
3300
3349
 
@@ -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;
@@ -686,6 +686,14 @@ video {
686
686
  margin-bottom: 1rem;
687
687
  }
688
688
 
689
+ .ml-1 {
690
+ margin-left: 0.25rem;
691
+ }
692
+
693
+ .ml-2 {
694
+ margin-left: 0.5rem;
695
+ }
696
+
689
697
  .ml-3 {
690
698
  margin-left: 0.75rem;
691
699
  }
@@ -702,6 +710,10 @@ video {
702
710
  margin-right: 0.5rem;
703
711
  }
704
712
 
713
+ .mt-0 {
714
+ margin-top: 0px;
715
+ }
716
+
705
717
  .mt-1 {
706
718
  margin-top: 0.25rem;
707
719
  }
@@ -777,6 +789,10 @@ video {
777
789
  height: 2rem;
778
790
  }
779
791
 
792
+ .h-4 {
793
+ height: 1rem;
794
+ }
795
+
780
796
  .h-48 {
781
797
  height: 12rem;
782
798
  }
@@ -797,6 +813,10 @@ video {
797
813
  height: 300px;
798
814
  }
799
815
 
816
+ .h-\[42px\] {
817
+ height: 42px;
818
+ }
819
+
800
820
  .h-\[calc\(100vh-55px\)\] {
801
821
  height: calc(100vh - 55px);
802
822
  }
@@ -809,6 +829,10 @@ video {
809
829
  max-height: 50vh;
810
830
  }
811
831
 
832
+ .w-4 {
833
+ width: 1rem;
834
+ }
835
+
812
836
  .w-48 {
813
837
  width: 12rem;
814
838
  }
@@ -895,6 +919,10 @@ video {
895
919
  grid-template-columns: repeat(1, minmax(0, 1fr));
896
920
  }
897
921
 
922
+ .grid-cols-2 {
923
+ grid-template-columns: repeat(2, minmax(0, 1fr));
924
+ }
925
+
898
926
  .flex-row {
899
927
  flex-direction: row;
900
928
  }
@@ -927,10 +955,18 @@ video {
927
955
  gap: 0.5rem;
928
956
  }
929
957
 
958
+ .gap-3 {
959
+ gap: 0.75rem;
960
+ }
961
+
930
962
  .gap-4 {
931
963
  gap: 1rem;
932
964
  }
933
965
 
966
+ .gap-8 {
967
+ gap: 2rem;
968
+ }
969
+
934
970
  .gap-x-4 {
935
971
  -moz-column-gap: 1rem;
936
972
  column-gap: 1rem;
@@ -1008,10 +1044,6 @@ video {
1008
1044
  overflow-x: auto;
1009
1045
  }
1010
1046
 
1011
- .overflow-y-auto {
1012
- overflow-y: auto;
1013
- }
1014
-
1015
1047
  .truncate {
1016
1048
  overflow: hidden;
1017
1049
  text-overflow: ellipsis;
@@ -1135,6 +1167,11 @@ video {
1135
1167
  background-color: rgb(107 114 128 / var(--tw-bg-opacity));
1136
1168
  }
1137
1169
 
1170
+ .bg-gray-600 {
1171
+ --tw-bg-opacity: 1;
1172
+ background-color: rgb(75 85 99 / var(--tw-bg-opacity));
1173
+ }
1174
+
1138
1175
  .bg-gray-800 {
1139
1176
  --tw-bg-opacity: 1;
1140
1177
  background-color: rgb(31 41 55 / var(--tw-bg-opacity));
@@ -1170,6 +1207,11 @@ video {
1170
1207
  background-color: rgb(241 245 249 / var(--tw-bg-opacity));
1171
1208
  }
1172
1209
 
1210
+ .bg-slate-500 {
1211
+ --tw-bg-opacity: 1;
1212
+ background-color: rgb(100 116 139 / var(--tw-bg-opacity));
1213
+ }
1214
+
1173
1215
  .bg-slate-600 {
1174
1216
  --tw-bg-opacity: 1;
1175
1217
  background-color: rgb(71 85 105 / var(--tw-bg-opacity));
@@ -1194,6 +1236,11 @@ video {
1194
1236
  background-color: rgb(24 35 255 / var(--tw-bg-opacity));
1195
1237
  }
1196
1238
 
1239
+ .bg-valencia-500 {
1240
+ --tw-bg-opacity: 1;
1241
+ background-color: rgb(220 73 73 / var(--tw-bg-opacity));
1242
+ }
1243
+
1197
1244
  .bg-valencia-600 {
1198
1245
  --tw-bg-opacity: 1;
1199
1246
  background-color: rgb(202 56 56 / var(--tw-bg-opacity));
@@ -1458,6 +1505,11 @@ video {
1458
1505
  color: rgb(153 27 27 / var(--tw-text-opacity));
1459
1506
  }
1460
1507
 
1508
+ .text-sky-600 {
1509
+ --tw-text-opacity: 1;
1510
+ color: rgb(2 132 199 / var(--tw-text-opacity));
1511
+ }
1512
+
1461
1513
  .text-sky-800 {
1462
1514
  --tw-text-opacity: 1;
1463
1515
  color: rgb(7 89 133 / var(--tw-text-opacity));
@@ -1468,11 +1520,20 @@ video {
1468
1520
  color: rgb(0 168 165 / var(--tw-text-opacity));
1469
1521
  }
1470
1522
 
1523
+ .text-valencia-500 {
1524
+ --tw-text-opacity: 1;
1525
+ color: rgb(220 73 73 / var(--tw-text-opacity));
1526
+ }
1527
+
1471
1528
  .text-white {
1472
1529
  --tw-text-opacity: 1;
1473
1530
  color: rgb(255 255 255 / var(--tw-text-opacity));
1474
1531
  }
1475
1532
 
1533
+ .accent-sky-600 {
1534
+ accent-color: #0284c7;
1535
+ }
1536
+
1476
1537
  .shadow-lg {
1477
1538
  --tw-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
1478
1539
  --tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);
@@ -1529,6 +1590,10 @@ video {
1529
1590
  --tw-ring-color: rgb(209 213 219 / var(--tw-ring-opacity));
1530
1591
  }
1531
1592
 
1593
+ .ring-gray-600\/20 {
1594
+ --tw-ring-color: rgb(75 85 99 / 0.2);
1595
+ }
1596
+
1532
1597
  .ring-gray-900\/5 {
1533
1598
  --tw-ring-color: rgb(17 24 39 / 0.05);
1534
1599
  }
@@ -1537,10 +1602,6 @@ video {
1537
1602
  --tw-ring-color: rgb(22 163 74 / 0.2);
1538
1603
  }
1539
1604
 
1540
- .ring-gray-600\/20 {
1541
- --tw-ring-color: rgb(75 85 99 / 0.2);
1542
- }
1543
-
1544
1605
  .filter {
1545
1606
  filter: var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow);
1546
1607
  }
@@ -1603,6 +1664,11 @@ video {
1603
1664
  background-color: rgb(249 250 251 / var(--tw-bg-opacity));
1604
1665
  }
1605
1666
 
1667
+ .hover\:bg-gray-500:hover {
1668
+ --tw-bg-opacity: 1;
1669
+ background-color: rgb(107 114 128 / var(--tw-bg-opacity));
1670
+ }
1671
+
1606
1672
  .hover\:bg-gray-600:hover {
1607
1673
  --tw-bg-opacity: 1;
1608
1674
  background-color: rgb(75 85 99 / var(--tw-bg-opacity));
@@ -1618,6 +1684,11 @@ video {
1618
1684
  background-color: rgb(220 38 38 / var(--tw-bg-opacity));
1619
1685
  }
1620
1686
 
1687
+ .hover\:bg-slate-400:hover {
1688
+ --tw-bg-opacity: 1;
1689
+ background-color: rgb(148 163 184 / var(--tw-bg-opacity));
1690
+ }
1691
+
1621
1692
  .hover\:bg-slate-500:hover {
1622
1693
  --tw-bg-opacity: 1;
1623
1694
  background-color: rgb(100 116 139 / var(--tw-bg-opacity));
@@ -1643,6 +1714,11 @@ video {
1643
1714
  background-color: rgb(63 83 255 / var(--tw-bg-opacity));
1644
1715
  }
1645
1716
 
1717
+ .hover\:bg-valencia-400:hover {
1718
+ --tw-bg-opacity: 1;
1719
+ background-color: rgb(235 126 126 / var(--tw-bg-opacity));
1720
+ }
1721
+
1646
1722
  .hover\:bg-valencia-500:hover {
1647
1723
  --tw-bg-opacity: 1;
1648
1724
  background-color: rgb(220 73 73 / var(--tw-bg-opacity));
@@ -1693,6 +1769,12 @@ video {
1693
1769
  box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1694
1770
  }
1695
1771
 
1772
+ .focus\:ring-1:focus {
1773
+ --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1774
+ --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);
1775
+ box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000);
1776
+ }
1777
+
1696
1778
  .focus\:ring-2:focus {
1697
1779
  --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);
1698
1780
  --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);
@@ -1724,11 +1806,30 @@ video {
1724
1806
  --tw-ring-color: rgb(239 68 68 / var(--tw-ring-opacity));
1725
1807
  }
1726
1808
 
1809
+ .focus\:ring-sky-600:focus {
1810
+ --tw-ring-opacity: 1;
1811
+ --tw-ring-color: rgb(2 132 199 / var(--tw-ring-opacity));
1812
+ }
1813
+
1814
+ .focus\:ring-ultramarine-200:focus {
1815
+ --tw-ring-opacity: 1;
1816
+ --tw-ring-color: rgb(206 218 255 / var(--tw-ring-opacity));
1817
+ }
1818
+
1819
+ .focus\:ring-ultramarine-500:focus {
1820
+ --tw-ring-opacity: 1;
1821
+ --tw-ring-color: rgb(63 83 255 / var(--tw-ring-opacity));
1822
+ }
1823
+
1727
1824
  .focus\:ring-white:focus {
1728
1825
  --tw-ring-opacity: 1;
1729
1826
  --tw-ring-color: rgb(255 255 255 / var(--tw-ring-opacity));
1730
1827
  }
1731
1828
 
1829
+ .focus\:ring-offset-0:focus {
1830
+ --tw-ring-offset-width: 0px;
1831
+ }
1832
+
1732
1833
  .focus\:ring-offset-2:focus {
1733
1834
  --tw-ring-offset-width: 2px;
1734
1835
  }
@@ -1757,10 +1858,18 @@ video {
1757
1858
  outline-color: #16a34a;
1758
1859
  }
1759
1860
 
1861
+ .focus-visible\:outline-orange-400:focus-visible {
1862
+ outline-color: #fb923c;
1863
+ }
1864
+
1760
1865
  .focus-visible\:outline-red-600:focus-visible {
1761
1866
  outline-color: #dc2626;
1762
1867
  }
1763
1868
 
1869
+ .focus-visible\:outline-slate-400:focus-visible {
1870
+ outline-color: #94a3b8;
1871
+ }
1872
+
1764
1873
  .focus-visible\:outline-slate-600:focus-visible {
1765
1874
  outline-color: #475569;
1766
1875
  }
@@ -1773,6 +1882,15 @@ video {
1773
1882
  outline-color: #1823ff;
1774
1883
  }
1775
1884
 
1885
+ .disabled\:cursor-not-allowed:disabled {
1886
+ cursor: not-allowed;
1887
+ }
1888
+
1889
+ .disabled\:bg-gray-500:disabled {
1890
+ --tw-bg-opacity: 1;
1891
+ background-color: rgb(107 114 128 / var(--tw-bg-opacity));
1892
+ }
1893
+
1776
1894
  @media (min-width: 640px) {
1777
1895
  .sm\:-mx-6 {
1778
1896
  margin-left: -1.5rem;
@@ -92,3 +92,7 @@
92
92
  justify-content: center;
93
93
  padding-bottom: 0.25em;
94
94
  }
95
+
96
+ .modal-container .modal-exit:hover {
97
+ background-color: #f1f5ff;
98
+ }
@@ -43,7 +43,7 @@
43
43
 
44
44
  .models .documents table th {
45
45
  position: sticky;
46
- top: 0px;
46
+ top: 42px;
47
47
  background-color: white;
48
48
  z-index: 1;
49
49
  }
@@ -90,9 +90,12 @@ td {
90
90
  }
91
91
 
92
92
  .models .documents-menu {
93
+ position: fixed;
94
+ background-color: white;
95
+ z-index: 1;
96
+ padding: 4px;
93
97
  display: flex;
94
- margin: 0.25em;
95
- width: calc(100vw - 220px);
98
+ width: calc(100vw - 12rem);
96
99
  }
97
100
 
98
101
  .models .documents-menu .search-input {
@@ -1,6 +1,6 @@
1
1
  <div class="models">
2
2
  <div>
3
- <div class="flex grow flex-col gap-y-5 overflow-y-auto border-r border-gray-200 bg-white px-2 h-[calc(100vh-55px)]">
3
+ <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">
4
4
  <div class="flex font-bold font-xl mt-4 pl-2">
5
5
  Models
6
6
  </div>
@@ -11,7 +11,7 @@
11
11
  <li v-for="model in models">
12
12
  <router-link
13
13
  :to="'/model/' + model"
14
- class="block rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700"
14
+ class="block truncate rounded-md py-2 pr-2 pl-2 text-sm font-semibold text-gray-700"
15
15
  :class="model === currentModel ? 'bg-ultramarine-100 font-bold' : 'hover:bg-ultramarine-100'">
16
16
  {{model}}
17
17
  </router-link>
@@ -24,11 +24,11 @@
24
24
 
25
25
  </div>
26
26
  <div class="documents" ref="documentsList">
27
- <div>
27
+ <div class="relative h-[42px]">
28
28
  <div class="documents-menu">
29
29
  <div class="flex flex-row items-center w-full gap-2">
30
30
  <form @submit.prevent="search" class="flex-grow m-0">
31
- <input class="w-full rounded-md p-1 outline-gray-300 text-lg" type="text" placeholder="Filter or text" v-model="searchText" />
31
+ <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" />
32
32
  </form>
33
33
  <div>
34
34
  <span v-if="status === 'loading'">Loading ...</span>
@@ -47,7 +47,7 @@
47
47
  Create
48
48
  </button>
49
49
  <button
50
- @click="shouldShowFieldModal = true"
50
+ @click="openFieldSelection"
51
51
  type="button"
52
52
  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">
53
53
  Fields
@@ -71,7 +71,7 @@
71
71
  </div>
72
72
  </div>
73
73
  </div>
74
- <div class="documents-container">
74
+ <div class="documents-container relative">
75
75
  <table v-if="outputType === 'table'">
76
76
  <thead>
77
77
  <th v-for="path in filteredPaths">
@@ -105,37 +105,39 @@
105
105
  <img src="images/loader.gif">
106
106
  </div>
107
107
  </div>
108
- <modal v-if="shouldShowExportModal">
109
- <template v-slot:body>
110
- <div class="modal-exit" @click="shouldShowExportModal = false">&times;</div>
111
- <export-query-results
112
- :schemaPaths="schemaPaths"
113
- :filter="filter"
114
- :currentModel="currentModel"
115
- @done="shouldShowExportModal = false">
116
- </export-query-results>
117
- </template>
118
- </modal>
119
- <modal v-if="shouldShowFieldModal">
120
- <template v-slot:body>
121
- <div class="modal-exit" @click="shouldShowFieldModal = false; selectedPaths = [...filteredPaths];">&times;</div>
122
- <div v-for="(path, index) in schemaPaths" :key="index" style="margin-bottom: 0.5em">
123
- <input type="checkbox" :id="'path.path'+index" @change="addOrRemove(path)" :value="path.path" :checked="isSelected(path.path)" />
124
- <label :for="'path' + index">{{path.path}}</label>
125
- </div>
126
- <div style="margin-top: 1em">
127
- <button type="submit" @click="filterDocuments()" style="color: black;margin-right: 0.5em">Filter Selection</button>
128
- <button type="submit" @click="deselectAll()" class="gray" style="margin-right: 0.5em">Deselect All</button>
129
- <button type="submit" @click="resetDocuments()" class="gray">Cancel</button>
108
+ </div>
130
109
 
110
+ <modal v-if="shouldShowExportModal">
111
+ <template v-slot:body>
112
+ <div class="modal-exit" @click="shouldShowExportModal = false">&times;</div>
113
+ <export-query-results
114
+ :schemaPaths="schemaPaths"
115
+ :filter="filter"
116
+ :currentModel="currentModel"
117
+ @done="shouldShowExportModal = false">
118
+ </export-query-results>
119
+ </template>
120
+ </modal>
121
+ <modal v-if="shouldShowFieldModal">
122
+ <template v-slot:body>
123
+ <div class="modal-exit" @click="shouldShowFieldModal = false; selectedPaths = [...filteredPaths];">&times;</div>
124
+ <div v-for="(path, index) in schemaPaths" :key="index" class="w-5 flex items-center">
125
+ <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)" />
126
+ <div class="ml-2 text-gray-700 grow shrink text-left">
127
+ <label :for="'path' + index">{{path.path}}</label>
131
128
  </div>
132
- </template>
133
- </modal>
134
- <modal v-if="shouldShowCreateModal">
135
- <template v-slot:body>
136
- <div class="modal-exit" @click="shouldShowCreateModal = false;">&times;</div>
137
- <create-document :currentModel="currentModel" :paths="schemaPaths" @close="closeCreationModal"></create-document>
138
- </template>
139
- </modal>
140
- </div>
129
+ </div>
130
+ <div class="mt-4 flex gap-2">
131
+ <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>
132
+ <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>
133
+ <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>
134
+ </div>
135
+ </template>
136
+ </modal>
137
+ <modal v-if="shouldShowCreateModal">
138
+ <template v-slot:body>
139
+ <div class="modal-exit" @click="shouldShowCreateModal = false;">&times;</div>
140
+ <create-document :currentModel="currentModel" :paths="schemaPaths" @close="closeCreationModal"></create-document>
141
+ </template>
142
+ </modal>
141
143
  </div>
@@ -211,8 +211,25 @@ module.exports = app => app.component('models', {
211
211
  }).map(key => this.selectedPaths[key]);
212
212
  }
213
213
  },
214
+ openFieldSelection() {
215
+ if (this.$route.query?.fields) {
216
+ this.selectedPaths.length = 0;
217
+ console.log('there are fields in play', this.$route.query.fields)
218
+ const fields = this.$route.query.fields.split(',');
219
+ for (let i = 0; i < fields.length; i++) {
220
+ this.selectedPaths.push({ path: fields[i] });
221
+ }
222
+ } else {
223
+ this.selectedPaths = [{ path: '_id' }];
224
+ }
225
+ this.shouldShowFieldModal = true;
226
+ },
214
227
  filterDocuments() {
215
- this.filteredPaths = [...this.selectedPaths];
228
+ if (this.selectedPaths.length > 0) {
229
+ this.filteredPaths = [...this.selectedPaths];
230
+ } else {
231
+ this.filteredPaths.length = 0;
232
+ }
216
233
  this.shouldShowFieldModal = false;
217
234
  const selectedParams = this.filteredPaths.map(x => x.path).join(',');
218
235
  this.query.fields = selectedParams;
@@ -24,16 +24,24 @@ exports.getWorkspaceTeam = function getWorkspaceTeam() {
24
24
  return client.post('/getWorkspaceTeam', { workspaceId: config__workspace._id }).then(res => res.data);
25
25
  };
26
26
 
27
- exports.inviteToWorkspace = function inviteToWorkspace(params) {
28
- return client.post('/inviteToWorkspace', { workspaceId: config__workspace._id, ...params }).then(res => res.data);
27
+ exports.getWorkspaceCustomerPortalLink = function getWorkspaceCustomerPortalLink(params) {
28
+ return client.post('/getWorkspaceCustomerPortalLink', { workspaceId: config__workspace._id, ...params }).then(res => res.data);
29
29
  };
30
30
 
31
31
  exports.github = function github(code) {
32
32
  return client.post('/github', { code, workspaceId: config__workspace._id }).then(res => res.data);
33
33
  };
34
34
 
35
+ exports.inviteToWorkspace = function inviteToWorkspace(params) {
36
+ return client.post('/inviteToWorkspace', { workspaceId: config__workspace._id, ...params }).then(res => res.data);
37
+ };
38
+
35
39
  exports.me = function me() {
36
40
  return client.post('/me', { workspaceId: config__workspace._id }).then(res => res.data);
37
41
  };
38
42
 
43
+ exports.removeFromWorkspace = function removeFromWorkspace(params) {
44
+ return client.post('/removeFromWorkspace', { workspaceId: config__workspace._id, ...params }).then(res => res.data);
45
+ };
46
+
39
47
  exports.hasAPIKey = client.hasAPIKey;
@@ -36,7 +36,7 @@
36
36
  </div>
37
37
 
38
38
  <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">
39
- <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>
39
+ <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>
40
40
  <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>
41
41
  </div>
42
42
  </div>
@@ -1,23 +1,75 @@
1
- <div class="mx-auto max-w-5xl py-6 px-2">
2
- <div class="text-xl font-bold">
3
- Current Members
4
- </div>
5
- <ul role="list" class="divide-y divide-gray-100">
6
- <li class="flex justify-between gap-x-6 py-5" v-for="user in users">
7
- <div class="flex min-w-0 gap-x-4">
8
- <img class="size-12 flex-none rounded-full bg-gray-50" :src="user.picture ?? 'images/logo.svg'" alt="">
9
- <div class="min-w-0 flex-auto">
10
- <p class="text-sm/6 font-semibold text-gray-900">{{user.name}}</p>
11
- <p class="mt-1 truncate text-xs/5 text-gray-500">{{user.email}}</p>
1
+ <div class="mx-auto max-w-5xl py-6 px-2 flex flex-col gap-8">
2
+ <div>
3
+ <div class="text-xl font-bold">
4
+ Subscription Details
5
+ </div>
6
+ <div v-if="workspace && workspace.subscriptionTier" class="mt-4 flex justify-between items-center">
7
+ <div>
8
+ <span class="font-bold">Tier:</span> {{workspace.subscriptionTier ?? 'No subscription'}}
9
+ </div>
10
+ <div>
11
+ <async-button
12
+ type="submit"
13
+ @click="getWorkspaceCustomerPortalLink"
14
+ 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">
15
+ View in Stripe
16
+ <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">
17
+ <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" />
18
+ </svg>
19
+ </async-button>
20
+ </div>
21
+ </div>
22
+ <div v-if="workspace && !workspace.subscriptionTier" class="mt-4 flex justify-between items-center">
23
+ <div>
24
+ <span class="font-bold">No active subscription</span>
25
+ <div class="text-sm text-gray-700">
26
+ You won't be able to invite your team until you activate a subscription
12
27
  </div>
13
28
  </div>
14
- <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
15
- <p class="text-sm/6 text-gray-900 capitalize">{{getRolesForUser(user).join(', ')}}</p>
16
- <p class="mt-1 text-xs/5 text-gray-500">Last seen <time datetime="2023-01-23T13:23Z">3h ago</time></p>
29
+ <div>
30
+ <a
31
+ :href="paymentLink"
32
+ target="_blank"
33
+ 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">
34
+ Subscribe With Stripe
35
+ <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">
36
+ <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" />
37
+ </svg>
38
+ </a>
17
39
  </div>
18
- </li>
19
- </ul>
20
- <div class="mt-6">
40
+ </div>
41
+ </div>
42
+ <div>
43
+ <div class="text-xl font-bold">
44
+ Current Members
45
+ </div>
46
+ <ul role="list" class="divide-y divide-gray-100">
47
+ <li class="flex justify-between gap-x-6 py-5" v-for="user in users">
48
+ <div class="flex min-w-0 gap-x-4">
49
+ <img class="size-12 flex-none rounded-full bg-gray-50" :src="user.picture ?? 'images/logo.svg'" alt="">
50
+ <div class="min-w-0 flex-auto">
51
+ <p class="text-sm/6 font-semibold text-gray-900">
52
+ {{user.name}}
53
+ <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>
54
+ </p>
55
+ <p class="mt-1 truncate text-xs/5 text-gray-500">{{user.email ?? 'No Email'}}</p>
56
+ </div>
57
+ </div>
58
+ <div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
59
+ <p class="text-sm/6 text-gray-900 capitalize">{{getRolesForUser(user).join(', ')}}</p>
60
+ <div class="flex gap-3">
61
+ <p class="mt-1 text-xs/5 text-gray-500 cursor-pointer">
62
+ Edit
63
+ </p>
64
+ <p class="mt-1 text-xs/5 text-valencia-500 cursor-pointer" @click="showRemoveModal = user">
65
+ Remove
66
+ </p>
67
+ </div>
68
+ </div>
69
+ </li>
70
+ </ul>
71
+ </div>
72
+ <div>
21
73
  <div class="flex items-center justify-between">
22
74
  <div class="text-xl font-bold">
23
75
  Invitations
@@ -26,8 +78,12 @@
26
78
  <button
27
79
  type="button"
28
80
  @click="showNewInvitationModal = true"
29
- 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">
81
+ :disabled="workspace && !workspace.subscriptionTier"
82
+ 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">
30
83
  New Invitation
84
+ <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">
85
+ <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" />
86
+ </svg>
31
87
  </button>
32
88
  </div>
33
89
  </div>
@@ -83,4 +139,24 @@
83
139
  <new-invitation @close="showNewInvitationModal = false" @invitationCreated="invitations.push($event.invitation)"></new-invitation>
84
140
  </template>
85
141
  </modal>
142
+
143
+ <modal v-if="showRemoveModal">
144
+ <template v-slot:body>
145
+ <div class="modal-exit" @click="showRemoveModal = false">&times;</div>
146
+ <div>
147
+ Are you sure you want to remove user <span class="font-bold">{{showRemoveModal.githubUsername}}</span> from this workspace?
148
+ </div>
149
+ <div class="mt-6 grid grid-cols-2 gap-4">
150
+ <async-button
151
+ @click="removeFromWorkspace(showConfirmDeleteModal)"
152
+ 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">
153
+ <span class="text-sm font-semibold leading-6">Yes, Remove</span>
154
+ </async-button>
155
+
156
+ <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">
157
+ <span class="text-sm font-semibold leading-6">Cancel</span>
158
+ </span>
159
+ </div>
160
+ </template>
161
+ </modal>
86
162
  </div>
@@ -9,7 +9,8 @@ module.exports = app => app.component('team', {
9
9
  workspace: null,
10
10
  users: null,
11
11
  invitations: null,
12
- showNewInvitationModal: false
12
+ showNewInvitationModal: false,
13
+ showRemoveModal: null
13
14
  }),
14
15
  async mounted() {
15
16
  const { workspace, users, invitations } = await mothership.getWorkspaceTeam();
@@ -17,9 +18,24 @@ module.exports = app => app.component('team', {
17
18
  this.users = users;
18
19
  this.invitations = invitations;
19
20
  },
21
+ computed: {
22
+ paymentLink() {
23
+ return 'https://buy.stripe.com/test_eVaeYa2jC7565Lq7ss?client_reference_id=' + this.workspace?._id;
24
+ }
25
+ },
20
26
  methods: {
21
27
  getRolesForUser(user) {
22
28
  return this.workspace.members.find(member => member.userId === user._id)?.roles ?? [];
29
+ },
30
+ async removeFromWorkspace() {
31
+ const { workspace, users } = await mothership.removeFromWorkspace({ userId: this.showRemoveModal._id });
32
+ this.workspace = workspace;
33
+ this.users = users;
34
+ this.showRemoveModal = false;
35
+ },
36
+ async getWorkspaceCustomerPortalLink() {
37
+ const { url } = await mothership.getWorkspaceCustomerPortalLink();
38
+ window.open(url, '_self');
23
39
  }
24
40
  }
25
41
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mongoosejs/studio",
3
- "version": "0.0.59",
3
+ "version": "0.0.61",
4
4
  "dependencies": {
5
5
  "archetype": "0.13.1",
6
6
  "csv-stringify": "6.3.0",
package/test.html ADDED
@@ -0,0 +1,57 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Fixed Navbars with Scrollable Table</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ </head>
9
+ <body class="h-screen flex flex-col">
10
+
11
+ <!-- Top-Level Navbar (Fixed) -->
12
+ <nav class="bg-blue-600 text-white p-4 fixed top-0 left-0 right-0 z-20">
13
+ Top-Level Navbar
14
+ </nav>
15
+
16
+ <!-- Side Navbar (Fixed) -->
17
+ <aside class="bg-gray-800 text-white w-64 p-4 fixed top-16 left-0 bottom-0 z-10">
18
+ Side Navbar
19
+ </aside>
20
+
21
+ <!-- Stacked Navigation Bar (Fixed Below Top Navbar) -->
22
+ <nav class="bg-gray-300 p-4 fixed top-16 left-64 right-0 z-20">
23
+ Stacked Navigation Bar
24
+ </nav>
25
+
26
+ <!-- Scrollable Table Container -->
27
+ <div class="flex-grow overflow-auto p-4 pl-64 pt-32 bg-gray-50" style="height: calc(100vh - 8rem);">
28
+ <div class="overflow-auto border border-gray-300" style="max-height: calc(100vh - 12rem);">
29
+ <table class="min-w-max border border-gray-300">
30
+ <thead class="bg-gray-300 sticky top-0 z-10">
31
+ <tr>
32
+ <!-- Generate many columns -->
33
+ <script>
34
+ for (let j = 0; j < 20; j++) {
35
+ document.write(`<th class="border p-2 whitespace-nowrap">Header ${j + 1}</th>`);
36
+ }
37
+ </script>
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ <!-- Generate many rows -->
42
+ <script>
43
+ for (let i = 0; i < 100; i++) {
44
+ document.write(`<tr class="border">`);
45
+ for (let j = 0; j < 20; j++) {
46
+ document.write(`<td class="border p-2 whitespace-nowrap">Row ${i + 1} Col ${j + 1}</td>`);
47
+ }
48
+ document.write(`</tr>`);
49
+ }
50
+ </script>
51
+ </tbody>
52
+ </table>
53
+ </div>
54
+ </div>
55
+
56
+ </body>
57
+ </html>