feat: introducing team (HBE-86) (#36)

This commit is contained in:
Anwarul Islam
2023-03-21 16:35:01 +06:00
committed by GitHub
parent 2244fb0523
commit 8b1d8e6a90
30 changed files with 1733 additions and 439 deletions

View File

@@ -4,8 +4,8 @@
<div>
<div>
<div class="px-6 rounded-md">
<form>
<div class="flex mt-4 ml-10">
<div>
<div class="flex mt-4">
<div>
<label class="text-gray-200 mr-5 text-lg" for="username"
>Name:
@@ -13,15 +13,16 @@
<input
class="w-96 p-2 mt-2 bg-zinc-800 border-gray-600 rounded-md focus:border-emerald-600 focus:ring focus:ring-opacity-40 focus:ring-emerald-500"
type="text"
v-model="name"
placeholder="Enter Name"
/>
</div>
</div>
</form>
</div>
<div class="mt-6"></div>
<div class="mx-10">
<TeamsAddMembers />
<div>
<HoppButtonPrimary :loading="loading" label="Save" @click="addTeam" />
</div>
<div class="mt-8"></div>
@@ -30,4 +31,50 @@
</div>
</template>
<script setup lang="ts"></script>
<script setup lang="ts">
import { useMutation } from '@urql/vue';
import { ref } from 'vue';
import { useRouter } from 'vue-router';
import { useReadonlyStream } from '~/composables/stream';
import { auth } from '~/helpers/auth';
import { CreateTeamDocument } from '../../helpers/backend/graphql';
const router = useRouter();
const name = ref('');
const loading = ref(false);
const addTeamMutation = useMutation(CreateTeamDocument);
const currentUser = useReadonlyStream(
auth.getProbableUserStream(),
auth.getProbableUser()
);
const addTeam = async () => {
loading.value = true;
let payload = {
name: name.value.trim(),
userUid: '',
};
if (currentUser.value) {
payload['userUid'] = currentUser.value.uid;
}
const { data, error } = await addTeamMutation.executeMutation(payload);
loading.value = false;
if (data) {
name.value = '';
goToTeamDetailsPage(data.createATeamByAdmin.id);
}
if (error) {
console.log(error);
}
};
const goToTeamDetailsPage = (teamId: string) => {
router.push('/teams/' + teamId);
};
</script>

View File

@@ -0,0 +1,3 @@
<template>
<TeamsDetails />
</template>

View File

@@ -1,120 +0,0 @@
<template>
<div class="sm:px-6 p-4">
<h3 class="sm:px-6 p-4 text-3xl font-medium text-gray-200">Team Details</h3>
<div class="mt-5">
<div class="flex flex-wrap justify-center mx-6">
<div class="w-full px-4 sm:w-1/2 xl:w-1/3">
<div class="h-80 px-6 py-6 bg-zinc-800 rounded-md shadow-sm">
<div class="flex ml-3 mt-2">
<icon-lucide-user class="text-emerald-400 text-3xl" />
<h4 class="text-3xl ml-2 font-semibold text-gray-200">
Team Info
</h4>
</div>
<div class="flex mt-5 ml-5 text-xl">
<h4 class="font-semibold text-gray-400">Team ID:</h4>
<div class="text-gray-200 ml-2">
{{ team.id }}
</div>
</div>
<div class="flex mt-2 ml-5 text-xl">
<h4 class="font-semibold text-gray-400">Team Name:</h4>
<div class="text-gray-200 ml-2">
{{ team.name }}
</div>
</div>
<div class="flex mt-2 ml-5 text-xl">
<h4 class="font-semibold text-gray-400">Creation Date:</h4>
<div class="text-gray-200 ml-2">
{{ team.date }}
</div>
</div>
<div class="flex mt-2 ml-5 text-xl">
<h4 class="font-semibold text-gray-400">Number of members:</h4>
<div class="text-gray-200 ml-2">
{{ team.members }}
</div>
</div>
</div>
</div>
<div class="w-full px-4 sm:w-1/2 xl:w-1/3">
<div class="h-80 px-5 py-6 bg-zinc-800 rounded-md shadow-sm">
<div class="flex ml-3 mt-2">
<icon-lucide-line-chart class="text-yellow-300 text-3xl" />
<h4 class="text-3xl ml-2 font-semibold text-gray-200">Stats</h4>
</div>
<div class="flex mt-5 ml-5 text-xl">
<h4 class="font-semibold text-gray-400">
Number of Collections:
</h4>
<div class="text-gray-200 ml-2">
{{ stats.collections }}
</div>
</div>
<div class="flex mt-2 ml-5 text-xl">
<h4 class="font-semibold text-gray-400">Number of Requests:</h4>
<div class="text-gray-200 ml-2">
{{ stats.requests }}
</div>
</div>
<div class="flex mt-2 ml-5 text-xl">
<h4 class="font-semibold text-gray-400">
Number of Environments:
</h4>
<div class="text-gray-200 ml-2">
{{ stats.environments }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="mt-6"></div>
<div class="mx-10">
<TeamsAddMembers />
</div>
<div class="mt-8"></div>
</div>
</template>
<script setup lang="ts">
import { reactive } from 'vue';
interface Team {
id: string;
name: string;
date: string;
members: number;
}
const team = reactive<Team>({
id: 'absd123',
name: 'SelfHosters',
date: '30-01-2023',
members: 20,
});
interface Stats {
collections: number;
requests: number;
environments: number;
}
const stats = reactive<Stats>({
collections: 10,
requests: 20,
environments: 30,
});
</script>

View File

@@ -2,11 +2,11 @@
<div>
<h3 class="sm:px-6 p-4 text-3xl font-medium text-gray-200">Teams</h3>
<div class="flex flex-col">
<div class="py-2 -my-2 overflow-x-auto sm:-mx-6 sm:px-4 lg:-mx-8 lg:px-8">
<div class="flex flex-col" v-if="!fetching">
<div class="py-2 overflow-x-auto">
<div class="inline-block min-w-full overflow-hidden align-middle">
<div class="sm:px-7 p-4">
<div v-if="showOptions" class="flex w-full items-center mb-7">
<div class="flex w-full items-center mb-7">
<button
class="inline-flex mr-3 items-center h-8 pl-2.5 pr-2 rounded-md shadow text-gray-400 border-gray-800 border-2 leading-none py-0"
>
@@ -42,27 +42,26 @@
</button>
</div>
</div>
<div>
<table class="w-full text-left">
<thead>
<tr class="text-gray-200 border-b border-gray-600 text-sm">
<th class="font-normal px-3 pt-0 pb-3"></th>
<th class="font-normal px-3 pt-0 pb-3">Team ID</th>
<th class="font-normal px-3 pt-0 pb-3">Team Name</th>
<th class="font-normal px-3 pt-0 pb-3 hidden md:table-cell">
Members
<th class="font-normal px-3 pt-0 pb-3">Team ID</th>
<th class="font-normal px-3 pt-0 pb-3 md:table-cell">
Number of Members
</th>
<!-- <th class="font-normal px-3 pt-0 pb-3">Status</th> -->
<th class="font-normal px-3 pt-0 pb-3">Date</th>
<th class="font-normal px-3 pt-0 pb-3">Action</th>
</tr>
</thead>
<tbody class="text-gray-300">
<!-- <router-link :custom="true" class="" :to="'/team/detail'"> -->
<tr
v-for="team in teams"
id="team.id"
class="border-b border-gray-600 hover:bg-zinc-800 rounded-xl"
@click="goToTeam"
v-for="(team, index) in teamList"
:key="team.id"
class="border-b border-gray-300 dark:border-gray-600 hover:bg-zinc-800 rounded-xl"
>
<td>
<label>
@@ -73,91 +72,159 @@
/>
</label>
</td>
<td class="sm:p-3 py-2 px-1">
<div class="flex">
<span class="ml-3">
{{ team.id }}
</span>
</div>
</td>
<td
class="sm:p-3 py-2 px-1 md:table-cell hidden text-sky-300"
@click="goToTeam(team.id)"
>
{{ team.name }}
<span class="hover:underline cursor-pointer">
{{ team.name }}
</span>
</td>
<td @click="goToTeam(team.id)" class="sm:p-3 py-2 px-1">
<span class="hover:underline cursor-pointer">
{{ team.id }}
</span>
</td>
<td class="sm:p-3 py-2 px-1 justify-center">
{{ team.members }}
</td>
<td class="sm:p-3 py-2 px-1">
<div class="flex items-center">
<div class="sm:flex hidden flex-col">
{{ team.date }}
<div class="text-gray-400 text-xs">11:16 AM</div>
</div>
</div>
{{ team.members?.length }}
</td>
<td>
<button
class="w-8 h-8 inline-flex items-center justify-right text-gray-400"
<tippy
interactive
trigger="click"
theme="popover"
:on-shown="() => tippyActions![index].focus()"
>
<icon-lucide-more-horizontal />
</button>
<span class="cursor-pointer">
<icon-lucide-more-horizontal />
</span>
<template #content="{ hide }">
<div
ref="tippyActions"
class="flex flex-col focus:outline-none"
tabindex="0"
@keyup.escape="hide()"
>
<HoppSmartItem
label="Delete"
@click="
() => {
deleteTeam(team.id);
hide();
}
"
/>
</div>
</template>
</tippy>
</td>
</tr>
<!-- </router-link> -->
</tbody>
</table>
</div>
<div v-if="teamList.length >= 20" class="text-center">
<button
@click="fetchNextTeams"
class="mt-5 p-2 rounded-3xl bg-gray-700"
>
<icon-lucide-chevron-down class="text-xl" />
</button>
</div>
</div>
</div>
</div>
</div>
</div>
<HoppSmartConfirmModal
:show="confirmDeletion"
:title="`Confirm Deletion of the team?`"
@hide-modal="confirmDeletion = false"
@resolve="deleteTeamMutation(deleteTeamID)"
/>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
import { defineProps } from 'vue';
defineProps({
showOptions: {
type: Boolean,
default: true,
},
});
import {
RemoveTeamDocument,
TeamListDocument,
} from '../../helpers/backend/graphql';
import { usePagedQuery } from '../../composables/usePagedQuery';
interface Team {
id: string;
name: string;
members: number;
date: string;
}
import { onMounted, onBeforeUnmount, ref } from 'vue';
import { useMutation } from '@urql/vue';
import { useToast } from '../../composables/toast';
const teams: Array<Team> = [
{
id: '123e4',
name: 'HoppMain',
members: 100,
date: '15-01-2023',
},
{
id: '12vbe',
name: 'Hopp',
members: 10,
date: '19-05-2023',
},
{
id: 'bg1d2',
name: 'Kratos',
members: 59,
date: '15-03-2023',
},
];
const toast = useToast();
const router = useRouter();
const goToTeam = () => {
router.push('/teams/details');
const {
fetching,
goToNextPage: fetchNextTeams,
refetch,
list: teamList,
} = usePagedQuery(
TeamListDocument,
(x) => x.admin.allTeams,
(x) => x.id,
{ cursor: undefined }
);
const goToTeam = (teamId: string) => {
router.push('/teams/' + teamId);
};
// Open the side menu dropdown of only the selected user
const activeTeamId = ref<null | String>(null);
// Template refs
const tippyActions = ref<any | null>(null);
// Hide dropdown when user clicks elsewhere
const close = (e: any) => {
if (!e.target.closest('.dropdown')) {
activeTeamId.value = null;
}
};
onMounted(() => document.addEventListener('click', close));
onBeforeUnmount(() => document.removeEventListener('click', close));
// User Deletion
const teamDeletion = useMutation(RemoveTeamDocument);
const confirmDeletion = ref(false);
const deleteTeamID = ref<string | null>(null);
const deleteTeam = (id: string) => {
confirmDeletion.value = true;
deleteTeamID.value = id;
};
const deleteTeamMutation = async (id: string | null) => {
if (!id) {
confirmDeletion.value = false;
toast.error('Team Deletion Failed');
return;
}
const variables = { uid: id };
await teamDeletion.executeMutation(variables).then((result) => {
if (result.error) {
toast.error('Team Deletion Failed');
} else {
refetch();
toast.success('Team Deleted Successfully');
}
});
confirmDeletion.value = false;
deleteTeamID.value = null;
router.push('/teams');
};
</script>