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