Skip to content

Commit

Permalink
Added user managment for workspaces
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianFun123 committed Nov 10, 2024
1 parent 7e20baa commit 5da760a
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 3 deletions.
20 changes: 20 additions & 0 deletions src/PunyshortAPI.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,4 +87,24 @@ export default class PunyshortAPI extends Cajax {
removeWorkspaceDomain(id, domainId) {
return this.delete(`/v1/workspaces/${id}/domains/${domainId}`)
}

getWorkspaceUsers(id, params = {}) {
return this.get(`/v1/workspaces/${id}/users`, params)
}

inviteUserToWorkspace(id, email) {
return this.post(`/v1/workspaces/${id}/users`, {email})
}

acceptWorkspaceInvitation(id, userId) {
return this.post(`/v1/workspaces/${id}/users/${userId}/accept`)
}

removeWorkspaceUser(id, userId) {
return this.delete(`/v1/workspaces/${id}/users/${userId}`)
}

updateWorkspaceUser(id, userId, body) {
return this.put(`/v1/workspaces/${id}/users/${userId}`, body)
}
}
5 changes: 5 additions & 0 deletions src/assets/css/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ input, select {
display: inline-block;
transition: 0.3s;

&.icon-button-small {
font-size: 18px;
padding: 3px;
}

&:hover {
background: #00000011;
transform: scale(1.15);
Expand Down
6 changes: 5 additions & 1 deletion src/components/LinksList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,11 @@ onMounted(() => {
<QRCodeModal v-model:visible="qrCodeModalOpened[link.id]" :text="link.full_link" />
</div>
<h2>{{ link.long_link }}</h2>
<div class="flex w-full gap-2 align-items-center">
<h2>{{ link.long_link }}</h2>
<img class="border-1 border-200 border-round-xl" style="height: 16px; width: 16px;" v-if="link.user" :src="link.user.avatar" :title="`${link.user.name} (${link.user.email})`" alt="">
</div>
</div>
</div>
</div>
Expand Down
38 changes: 37 additions & 1 deletion src/components/WorkspaceList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,33 @@
import CreateWorkspaceModal from "@/components/CreateWorkspaceModal.vue";
import {onMounted, ref} from "vue";
import {apiClient} from "@/main";
import {storeToRefs} from "pinia";
import {useUserStore} from "@/store/userStore";
const { user } = storeToRefs(useUserStore())
const createWorkspaceModalVisible = ref(false)
const workspaces = ref([])
const invitations = ref([])
onMounted(async () => {
const load = async () => {
workspaces.value = (await apiClient.getWorkspaces()).data
invitations.value = (await apiClient.getWorkspaces({invitations: true})).data
}
const acceptInvitation = async workspace => {
await apiClient.acceptWorkspaceInvitation(workspace.id, user.value.id)
load()
}
const declineInvitation = async workspace => {
await apiClient.removeWorkspaceUser(workspace.id, user.value.id)
load()
}
onMounted(async () => {
load()
})
</script>
<template>
Expand All @@ -26,6 +45,23 @@ onMounted(async () => {
add workspace
</div>
</div>

<div v-if="invitations.length" class="mt-5">
<h3 class="mb-3">Invitations</h3>
<div style="display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px">
<div v-for="workspace of invitations" :to="{name: 'workspace-home', params: {workspace: workspace.slug}}" class="block no-underline">
<div class="border-1 border-300 p-2 px-3 transition-duration-300 border-round-md text-900 flex justify-content-between">
<span>
{{ workspace.name }}
</span>
<div class="flex gap-2 align-items-center">
<i @click="acceptInvitation(workspace)" class="ti ti-check icon-button icon-button-small" />
<i @click="declineInvitation(workspace)" class="ti ti-x icon-button icon-button-small" />
</div>
</div>
</div>
</div>
</div>
<CreateWorkspaceModal v-model:visible="createWorkspaceModalVisible" />
</div>
</template>
96 changes: 96 additions & 0 deletions src/components/workspaces/WorkspaceUsers.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<script setup>
import DataTable from "@/components/DataTable.vue";
import {onMounted, ref} from "vue";
import {apiClient} from "@/main";
import {storeToRefs} from "pinia";
import {useUserStore} from "@/store/userStore";
import ConfirmationModal from "@/components/ConfirmationModal.vue";
import Modal from "@/components/Modal.vue";
const props = defineProps(["workspace"])
const { user } = storeToRefs(useUserStore())
const users = ref([])
const load = async () => {
users.value = (await apiClient.getWorkspaceUsers(props.workspace.id, {
page_limit: 10000
})).data
}
const inviteModalShown = ref(false)
const inviteEmail = ref("")
const userToRemove = ref(null)
const removeUserModal = ref(false)
const updateUser = async user => {
await apiClient.updateWorkspaceUser(props.workspace.id, user.id, user)
load()
}
const removeUser = async user => {
await apiClient.removeWorkspaceUser(props.workspace.id, user.id, user)
userToRemove.value = null
removeUserModal.value = false
load()
}
const inviteUser = async user => {
await apiClient.inviteUserToWorkspace(props.workspace.id, inviteEmail.value)
inviteModalShown.value = false
inviteEmail.value = ""
load()
}
const emit = defineEmits(['change'])
onMounted(async () => {
load()
})
</script>
<template>
<div class="mt-4 mb-5">
<div v-for="workspaceUser of users" class="p-1 mb-2 flex w-full justify-content-between" :class="user.id === workspaceUser.id && 'opacity-70 pointer-events-none'">
<span class="flex gap-3 align-items-center">
<img v-if="workspaceUser.state === 'ACCEPTED'" class="border-1 border-200 border-round-xl" style="height: 24px; width: 24px;" :src="workspaceUser.avatar">
<span v-if="workspaceUser.state === 'ACCEPTED'">
{{ workspaceUser.name }}
</span>
<span class="opacity-40">
{{workspaceUser.email}}
</span>
<span v-if="user.id === workspaceUser.id" class="opacity-80 text-primary">
(you)
</span>
<span v-if="workspaceUser.state === 'INVITED'" class="opacity-80 text-primary">
(invited)
</span>
</span>

<div class="flex align-items-center gap-2">
<select class="border-1 border-200 p-2 py-1 border-round-md text-sm" v-model="workspaceUser.role" @change="updateUser(workspaceUser)">
<option value="ADMIN">Admin</option>
<option value="MEMBER">Member</option>
</select>

<i @click="userToRemove = workspaceUser; removeUserModal = true" class="ti ti-x icon-button" :class="user.id === workspaceUser.id && 'opacity-0'" />
</div>
</div>

<button class="btn" @click="inviteModalShown = true">Invite user</button>
</div>

<ConfirmationModal @confirm="removeUser(userToRemove)" v-model:visible="removeUserModal">Do you really want to delete this user?</ConfirmationModal>

<Modal title="Invite" v-model:visible="inviteModalShown" @submit="inviteUser">
<div class="mb-4">
<label class="mb-1">E-Mail</label>
<input v-model="inviteEmail" type="text" class="input">
</div>

<button type="submit" class="btn right mt-4">Confirm</button>
</Modal>

</template>
11 changes: 10 additions & 1 deletion src/views/workspace/WorkspaceLinks.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {useUserStore} from "@/store/userStore";
import {useTemplateRef} from "vue";
import Tabs from "@/components/Tabs.vue";
import LinkedDomains from "@/components/workspaces/LinkedDomains.vue";
import WorkspaceUsers from "@/components/workspaces/WorkspaceUsers.vue";
const { user } = storeToRefs(useUserStore())
defineProps(["workspace", "workspaceUser"])
Expand All @@ -31,13 +32,21 @@ const shortenLinkRef = useTemplateRef('shortenLinkRef')
<div class="site-width mt-5 relative">
<Tabs :tabs="{links: 'Shorten Links', ...(workspaceUser?.role === 'ADMIN' ? {domains: 'Linked Domains'} : {})}">
<Tabs :tabs="{
links: 'Shorten Links', ...(workspaceUser?.role === 'ADMIN' ? {
domains: 'Linked Domains',
users: 'Users'
} : {})
}">
<template v-slot:[`tab-links`]>
<LinksList :workspace="workspace" ref="linksList" class="mt-4" />
</template>
<template v-slot:[`tab-domains`]>
<LinkedDomains @change="shortenLinkRef.loadDomains()" :workspace="workspace" />
</template>
<template v-slot:[`tab-users`]>
<WorkspaceUsers @change="shortenLinkRef.loadDomains()" :workspace="workspace" />
</template>
</Tabs>
</div>
</div>
Expand Down

0 comments on commit 5da760a

Please sign in to comment.