Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions frontend/src/mothership.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,8 @@ exports.removeFromWorkspace = function removeFromWorkspace(params) {
return client.post('/removeFromWorkspace', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
};

exports.updateWorkspaceMember = function updateWorkspaceMember(params) {
return client.post('/updateWorkspaceMember', { workspaceId: window.MONGOOSE_STUDIO_CONFIG.workspace._id, ...params }).then(res => res.data);
};

exports.hasAPIKey = client.hasAPIKey;
67 changes: 63 additions & 4 deletions frontend/src/team/team.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,13 @@
<div class="hidden shrink-0 sm:flex sm:flex-col sm:items-end">
<p class="text-sm/6 text-gray-900 capitalize">{{getRolesForUser(user).join(', ')}}</p>
<div class="flex gap-3">
<p class="mt-1 text-xs/5 text-gray-500 cursor-pointer">
<button
type="button"
class="mt-1 text-xs/5 text-gray-500 cursor-pointer disabled:cursor-not-allowed disabled:text-gray-300"
:disabled="getRolesForUser(user).includes('owner')"
@click="openEditModal(user)">
Edit
</p>
</button>
<button
class="mt-1 text-xs/5 text-valencia-500 cursor-pointer disabled:cursor-not-allowed disabled:text-gray-300"
:disabled="getRolesForUser(user).includes('owner')"
Expand Down Expand Up @@ -152,15 +156,70 @@ <h3 class="mt-2 text-sm font-semibold text-gray-900">No invitations</h3>
</template>
</modal>

<modal v-if="showEditModal">
<template v-slot:body>
<div class="modal-exit" @click="closeEditModal">&times;</div>
<div class="p-1 space-y-4">
<div class="text-lg font-bold">
Edit Member
</div>

<div>
<div class="text-sm/6 font-semibold text-gray-900">
{{showEditModal.user.name || showEditModal.user.githubUsername}}
</div>
<div class="text-xs/5 text-gray-500">
{{showEditModal.user.email ?? 'No Email'}}
</div>
</div>

<div>
<label for="editRole" class="block text-sm/6 font-medium text-gray-900">Role</label>
<div class="mt-2 grid grid-cols-1">
<select
id="editRole"
name="editRole"
v-model="showEditModal.role"
class="col-start-1 row-start-1 w-full appearance-none rounded-md bg-white py-1.5 pl-3 pr-8 text-base text-gray-900 outline outline-1 -outline-offset-1 outline-gray-300 focus:outline focus:outline-2 focus:-outline-offset-2 focus:outline-ultramarine-600 sm:text-sm/6">
<option value="admin" :disabled="disableRoleOption('admin')">Admin</option>
<option value="member" :disabled="disableRoleOption('member')">Member</option>
<option value="readonly" :disabled="disableRoleOption('readonly')">Read-only</option>
<option value="dashboards" :disabled="disableRoleOption('dashboards')">Dashboards Only</option>
</select>
<svg class="pointer-events-none col-start-1 row-start-1 mr-2 size-5 self-center justify-self-end text-gray-500 sm:size-4" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" data-slot="icon">
<path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" />
</svg>
</div>
<div v-if="!workspace?.subscriptionTier" class="mt-2 text-sm text-gray-700">
You can only assign the "Dashboards Only" role until you activate a subscription.
</div>
</div>

<div class="mt-6 grid grid-cols-2 gap-4">
<async-button
@click="updateWorkspaceMember"
:disabled="showEditModal.role === showEditModal.originalRole"
class="border-0 mt-0 flex w-full items-center justify-center gap-3 rounded-md bg-ultramarine-600 hover:bg-ultramarine-500 px-3 py-1.5 text-white focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-ultramarine-500">
<span class="text-sm font-semibold leading-6">Save</span>
</async-button>

<span @click="closeEditModal" 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">
<span class="text-sm font-semibold leading-6">Cancel</span>
</span>
</div>
</div>
</template>
</modal>

<modal v-if="showRemoveModal">
<template v-slot:body>
<div class="modal-exit" @click="showRemoveModal = false">&times;</div>
<div class="modal-exit" @click="showRemoveModal = null">&times;</div>
<div>
Are you sure you want to remove user <span class="font-bold">{{showRemoveModal.githubUsername}}</span> from this workspace?
</div>
<div class="mt-6 grid grid-cols-2 gap-4">
<async-button
@click="removeFromWorkspace(showConfirmDeleteModal)"
@click="removeFromWorkspace"
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">
<span class="text-sm font-semibold leading-6">Yes, Remove</span>
</async-button>
Expand Down
48 changes: 47 additions & 1 deletion frontend/src/team/team.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = app => app.component('team', {
invitations: null,
showNewInvitationModal: false,
showRemoveModal: null,
showEditModal: null,
status: 'loading'
}),
async mounted() {
Expand All @@ -32,15 +33,60 @@ module.exports = app => app.component('team', {
getRolesForUser(user) {
return this.workspace.members.find(member => member.userId === user._id)?.roles ?? [];
},
openEditModal(user) {
if (this.getRolesForUser(user).includes('owner')) {
return;
}

const roles = this.getRolesForUser(user);
const nonOwnerRoles = roles.filter(role => role !== 'owner');
const currentRole = nonOwnerRoles[0] ?? null;
const editableRole = currentRole ?? (this.workspace?.subscriptionTier ? 'member' : 'dashboards');

this.showEditModal = {
user,
role: editableRole,
originalRole: currentRole
};
},
closeEditModal() {
this.showEditModal = null;
},
async updateWorkspaceMember() {
if (this.showEditModal.role === this.showEditModal.originalRole) {
this.closeEditModal();
return;
}

const { workspace, users } = await mothership.updateWorkspaceMember({
userId: this.showEditModal.user._id,
roles: [this.showEditModal.role]
});

this.workspace = workspace;
this.users = users;
this.closeEditModal();
},
async removeFromWorkspace() {
const { workspace, users } = await mothership.removeFromWorkspace({ userId: this.showRemoveModal._id });
this.workspace = workspace;
this.users = users;
this.showRemoveModal = false;
this.showRemoveModal = null;
},
async getWorkspaceCustomerPortalLink() {
const { url } = await mothership.getWorkspaceCustomerPortalLink();
window.open(url, '_self');
},
disableRoleOption(option) {
if (this.workspace?.subscriptionTier) {
return false;
}

if (this.showEditModal?.originalRole === option) {
return false;
}

return option !== 'dashboards';
}
}
});