Added Team System
This commit is contained in:
180
web/components/team/Default.vue
Normal file
180
web/components/team/Default.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<Modal v-if="modalOpen" title="Invite team member" @close="modalOpen = false" @submit="modalOpen = false">
|
||||
<div>
|
||||
<h2 class="text-lg font-bold text-primary">Users</h2>
|
||||
<div class="h-48 space-y-3 overflow-y-auto">
|
||||
<div v-for="unaffiliatedUser in unaffilatedUsers" :key="unaffiliatedUser._id.toString()" class="flex items-center rounded bg-neutral-700 px-3 py-1 text-gray-200">
|
||||
{{ unaffiliatedUser.username }}
|
||||
<Button v-if="!unaffiliatedUser.teamInvites.includes(user.team.id)" class="ml-auto" @click="inviteUser(unaffiliatedUser)">Invite</Button>
|
||||
<Button v-else class="ml-auto" type="danger" @click="cancelInvite(unaffiliatedUser)">Cancel Invite</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal v-if="editTeamModal.open" title="Edit team" @close="editTeamModal.open = false" @submit="editTeam">
|
||||
<Input v-model:value="editTeamModal.name" background-class="bg-neutral-800" class="w-full max-w-sm">Naam / Prefix</Input>
|
||||
<Colorpicker v-model:value="editTeamModal.color" input-background-class="bg-neutral-800" class="w-full max-w-sm" />
|
||||
</Modal>
|
||||
<div class="mx-auto my-10 max-w-2xl">
|
||||
<h2 class="mb-2 text-xl font-bold text-primary">Team Information</h2>
|
||||
<div class="rounded border-[1px] border-primary px-5 py-2 text-primary">
|
||||
<table class="w-full table-auto">
|
||||
<tbody class="divide-y divide-gray-700">
|
||||
<tr>
|
||||
<td class="py-3">Name</td>
|
||||
<td class="font-bold">{{ team.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-3">Color</td>
|
||||
<td class="font-bold">{{ team.color }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="py-3">ID</td>
|
||||
<td class="font-bold">{{ team._id }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mb-10 mt-5 flex justify-center gap-x-3">
|
||||
<Button type="danger" @click="leaveTeam">Leave Team</Button>
|
||||
<Button @click="openTeamModal">Edit Team</Button>
|
||||
</div>
|
||||
<h2 class="mb-2 text-xl font-bold text-primary">Team Members</h2>
|
||||
<div class="space-y-5 rounded border-[1px] border-primary p-5 text-primary">
|
||||
<div v-for="teamMember in teamMembers" :key="teamMember._id" class="flex h-12 items-center rounded bg-neutral-800 px-5 font-bold text-gray-200">
|
||||
{{ teamMember.username }}
|
||||
<span v-if="teamMember.team.admin" class="ml-3 text-sm text-gray-400">Admin</span>
|
||||
<div v-if="user.team.admin" class="ml-auto">
|
||||
<Button v-if="!teamMember.team.admin && teamMember._id !== user._id" @click="promoteUser(teamMember)">Promote</Button>
|
||||
<Button v-if="teamMember.team.admin && teamMember._id !== user._id" @click="demoteUser(teamMember)">Demote</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="user.team.admin" class="rounded border-2 border-dashed border-neutral-500 bg-neutral-800 p-3 text-center font-bold text-gray-200 hover:cursor-pointer hover:bg-neutral-700" @click="modalOpen = true">
|
||||
Invite new member
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const user = useState('user');
|
||||
const { data: team } = await useFetch('/api/team');
|
||||
const { data: teamMembers } = await useFetch('/api/team/members');
|
||||
const { data: unaffilatedUsers } = await useFetch('/api/team/unaffiliated')
|
||||
|
||||
const modalOpen = ref(false)
|
||||
|
||||
const editTeamModal = ref({
|
||||
open: false,
|
||||
name: '',
|
||||
color: '',
|
||||
})
|
||||
|
||||
const openTeamModal = () => {
|
||||
editTeamModal.value.name = team.value.name
|
||||
editTeamModal.value.color = team.value.color
|
||||
editTeamModal.value.open = true
|
||||
}
|
||||
|
||||
const leaveTeam = async () => {
|
||||
try {
|
||||
await $fetch('/api/team/leave');
|
||||
|
||||
user.value.team = null;
|
||||
|
||||
useToast().success('Successfully left team')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const editTeam = async () => {
|
||||
try {
|
||||
await $fetch('/api/team/edit', {
|
||||
method: 'POST',
|
||||
body: {
|
||||
name: editTeamModal.value.name,
|
||||
color: editTeamModal.value.color,
|
||||
}
|
||||
});
|
||||
|
||||
team.value.name = editTeamModal.value.name
|
||||
team.value.color = editTeamModal.value.color
|
||||
|
||||
useToast().success('Successfully modified team')
|
||||
|
||||
editTeamModal.value.open = false
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const inviteUser = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/invite', {
|
||||
method: 'POST',
|
||||
body: { id: usr._id }
|
||||
});
|
||||
|
||||
usr.teamInvites.push(user.value.team.id);
|
||||
|
||||
useToast().success(`Successfully invited ${usr.username}`)
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const cancelInvite = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/cancelinvite', {
|
||||
method: 'POST',
|
||||
body: { id: usr._id }
|
||||
});
|
||||
|
||||
usr.teamInvites = usr.teamInvites.filter(a => a !== user.value.team.id);
|
||||
|
||||
useToast().success('Successfully cancelled invited')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const promoteUser = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/promote', {
|
||||
method: 'POST',
|
||||
body: { userId: usr._id }
|
||||
});
|
||||
|
||||
usr.team.admin = true
|
||||
|
||||
console.log(usr)
|
||||
console.log(teamMembers)
|
||||
|
||||
useToast().success('Successfully promted user')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const demoteUser = async (usr) => {
|
||||
try {
|
||||
await $fetch('/api/team/demote', {
|
||||
method: 'POST',
|
||||
body: { userId: usr._id }
|
||||
});
|
||||
|
||||
usr.team.admin = false
|
||||
|
||||
useToast().success('Successfully demoted user')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
@@ -4,28 +4,83 @@
|
||||
</p>
|
||||
<div class="mb-5 flex w-full justify-center gap-10">
|
||||
<span
|
||||
class="font-bold text-primary hover:cursor-pointer" :class="{ 'underline underline-offset-4': !createTeam }"
|
||||
@click="createTeam = false"
|
||||
class="font-bold text-primary hover:cursor-pointer" :class="{ 'underline underline-offset-4': !createTeam.show }"
|
||||
@click="createTeam.show = false"
|
||||
>
|
||||
Team Invites
|
||||
</span>
|
||||
<span
|
||||
class="font-bold text-primary hover:cursor-pointer" :class="{ 'underline underline-offset-4': createTeam }"
|
||||
@click="createTeam = true"
|
||||
class="font-bold text-primary hover:cursor-pointer" :class="{ 'underline underline-offset-4': createTeam.show }"
|
||||
@click="createTeam.show = true"
|
||||
>
|
||||
Create Team
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="!createTeam" class="text-center text-gray-300">
|
||||
You don't have any team invites
|
||||
<div v-if="!createTeam.show" class="text-center text-gray-300">
|
||||
<h2 v-if="!user.teamInvites.length">You don't have any team invites</h2>
|
||||
<div v-else class="mx-auto flex max-w-lg flex-col">
|
||||
<div v-for="team in filteredTeams" :key="team._id" class="flex items-center rounded bg-neutral-800 px-3 py-1 text-left" @click="acceptInvite(team)">
|
||||
<span>
|
||||
{{ team.name }}
|
||||
</span>
|
||||
<Button class="ml-auto">Accept</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="flex w-full flex-col items-center gap-5">
|
||||
<Input class="w-full max-w-sm">Naam / Prefix</Input>
|
||||
<Colorpicker class="w-full max-w-sm" />
|
||||
<Button>Create Team</Button>
|
||||
<Input v-model:value="createTeam.name" class="w-full max-w-sm">Naam / Prefix</Input>
|
||||
<Colorpicker v-model:value="createTeam.color" class="w-full max-w-sm" />
|
||||
<Button @click="handleCreateTeam">Create Team</Button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const createTeam = ref(false)
|
||||
const user = useState('user')
|
||||
|
||||
const teams = await $fetch('/api/team/all')
|
||||
|
||||
const filteredTeams = computed(() => {
|
||||
return teams.filter(a => user.value.teamInvites.includes(a._id));
|
||||
})
|
||||
|
||||
const createTeam = ref({
|
||||
show: false,
|
||||
name: '',
|
||||
color: '',
|
||||
})
|
||||
|
||||
const acceptInvite = async (team) => {
|
||||
try {
|
||||
const response = await $fetch('/api/team/acceptinvite', {
|
||||
method: 'POST',
|
||||
body: { teamId: team._id }
|
||||
})
|
||||
|
||||
console.log(response)
|
||||
|
||||
user.value.team = { id: response._id, admin: false }
|
||||
|
||||
useToast().success('Successfully joined team')
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreateTeam = async () => {
|
||||
try {
|
||||
const response = await $fetch('/api/team/create', {
|
||||
method: 'POST',
|
||||
body: { teamName: createTeam.value.name, teamColor: createTeam.value.color }
|
||||
})
|
||||
|
||||
user.value.team = { id: response.insertedId.toString(), admin: true }
|
||||
|
||||
useToast().success('Successfully created team')
|
||||
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
useToast().error(e.statusMessage)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
Reference in New Issue
Block a user