Ngiler SH3LL 360
Home
Information
Create File
Create Folder
:
/
home
/
tbf
/
tbfguest.tbf.ro
/
src
/
views
/
public
/
positions
/
Information Server
MySQL :
OFF
Perl :
OFF
CURL :
ON
WGET :
OFF
PKEXEC :
OFF
Directive
Local Value
IP Address
89.40.16.97
System
Linux server.atelieruldeit.ro 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64
User
tbf
PHP Version
7.3.33
Software
Apache
Doc root
Writable
close
Edit File :
Index.vue
| Size :
17.64
KB
Copy
<template> <NavbarPositions :noPositions="positions.length" :isResponsible="isResponsible" /> <!-- Content --> <div class="relative bg-white"> <TransitionRoot :show="loaded" enter="ease-out duration-500" enter-from="opacity-0" enter-to="opacity-100" leave="ease-in duration-200" leave-from="opacity-100" leave-to="opacity-0"> <div class="py-10 px-16 max-w-none mx-auto"> <ul role="list" class="grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-2 xl:grid-cols-3 min-[2000px]:grid-cols-4 xl:gap-x-8"> <li class="overflow-hidden rounded-xl border border-gray-200 bg-white" v-for="position in positions"> <!-- Header --> <div class="group flex items-center justify-between border-b border-gray-900/5 bg-gray-50 p-6"> <div class="flex items-center gap-x-4"> <div class="h-12 w-12 flex items-center justify-center rounded-lg bg-white object-cover ring-1 ring-gray-900/10"> {{ position.emoji }} </div> <div class="text-sm font-medium leading-6 text-gray-900"> {{ position.name }} <div class="flex items-center gap-x-2" v-if="$auth.impersonating()"> <button v-if="position.status == 'completed'" type="button" :disabled="loadingResetStatusUser[position.id]" @click.stop="resetPositionStatus(position.id)" class="ml-auto flex items-center gap-x-1 rounded-md bg-transparent text-sm underline text-blue-500 hover:text-blue-400 disabled:text-blue-400 disabled:hover:text-blue-400 disabled:cursor-not-allowed"> <LoaderTbf color="text-blue-400" class="w-3 h-3 inline-flex items-center justify-center" v-if="loadingResetStatusUser[position.id]" /> Resetează </button> <button type="button" @click="deletePosition(position.id)" class="flex items-center gap-x-1 rounded-md bg-transparent text-sm underline text-red-700 hover:text-red-500 disabled:text-red-500 disabled:hover:text-red-500 disabled:cursor-not-allowed"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="inline-block w-4 h-4"> <path vector-effect="non-scaling-stroke" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" /> </svg> Șterge </button> </div> </div> </div> <div class="relative ml-auto flex items-center group-hover:translate-x-0 transition duration-300 delay-75" :class="[['processing', 'completed'].includes(position.status) ? 'translate-x-[103px]' : 'translate-x-[78px]']" v-if="['admin', 'super_admin'].includes($auth.user().rights.role)"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-6 h-6 text-gray-400 delay-75 transition duration-300 group-hover:opacity-0 inline"> <path vector-effect="non-scaling-stroke" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" d="M6.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM12.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0zM18.75 12a.75.75 0 11-1.5 0 .75.75 0 011.5 0z" /> </svg> <button type="button" @click="$router.push({ name: ['processing', 'completed'].includes(position.status) ? 'positions_show' : 'positions_edit', params: { id: position.id } })" class="opacity-0 delay-75 transition duration-300 group-hover:opacity-100 rounded-md bg-white px-2.5 py-1.5 text-sm font-medium text-gray-900 ring-1 ring-inset ring-gray-300 ml-2 hover:bg-gray-50"> {{ ["processing", "completed"].includes(position.status) ? "Vizualizează" : "Modifică" }} </button> </div> </div> <!-- Listă oameni --> <dl class="-my-3 divide-y divide-gray-100 px-6 py-4 text-sm leading-6"> <div class="py-3 group"> <dd class="text-sm text-yellow-700" v-if="position.status == 'in_progress'"> <div class="rounded-lg ring-1 px-3 py-2 flex items-center gap-1 justify-between bg-yellow-50 ring-yellow-600/10"> <div class="flex gap-1 items-center"> Documentare activități ({{ position.users.filter((el) => el.status == "completed").length }}/{{ position.users_count }}) </div> <div> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-5 h-5"> <path stroke-width="1.5" vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" /> </svg> </div> </div> </dd> <dd class="text-sm text-yellow-700" v-else-if="position.status == 'processing'"> <div class="rounded-lg ring-1 px-3 py-2 flex items-center gap-1 justify-between bg-yellow-50 ring-yellow-600/10"> <div class="flex gap-1 items-center">Optimizare activități prin AI</div> <div> <LoaderTbf color="text-yellow-700" /> </div> </div> </dd> <dd class="text-sm" :class="[position.evaluation_completed ? 'text-green-700' : 'text-blue-700']" v-else-if="position.status == 'completed'"> <div class="rounded-lg ring-1 px-3 py-2 flex items-center gap-1 justify-between group" :class="[ position.evaluation_completed ? 'bg-green-50 ring-green-600/10' : 'bg-blue-50 ring-blue-600/10', { 'hover:cursor-pointer hover:bg-blue-100 transition duration-150 delay-75': position.rights.can_evaluate, 'hover:cursor-pointer hover:bg-green-100 transition duration-150 delay-75': position.evaluation_completed && position.rights.can_evaluate, }, ]" @click="openVerification(position)"> <template v-if="position.evaluation_completed"> <div class="flex gap-1 items-center">Activități verificate</div> <div> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512" class="w-4 h-4"> <path fill="currentColor" d="M256 32a224 224 0 1 1 0 448 224 224 0 1 1 0-448zm0 480A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM363.3 203.3c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0L224 297.4l-52.7-52.7c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6l64 64c6.2 6.2 16.4 6.2 22.6 0l128-128z" /> </svg> </div> </template> <template v-else> <div>Verificare activități</div> <div class="group-hover:scale-110 transition-all duration-150 delay-75"> <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"> <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" /> </svg> </div> </template> </div> </dd> </div> <a v-for="user in position.users.sort((a, b) => { return b.is_responsible - a.is_responsible; })" @click=" user.rights.can_view ? $router.push({ name: 'positions_add_activity', params: { userId: user.id, positionId: position.id }, query: { is_completed: user.status == 'completed' }, }) : '' " class="flex justify-between items-center gap-x-4 py-3 group" :class="[{ 'hover:cursor-pointer': user.rights.can_view }]"> <div class="group-hover:text-gray-600 transition duration-150 flex gap-3 items-center" :class="[$auth.user().id == user.id ? 'text-blue-500' : 'text-gray-500']"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-7 h-7" v-if="user.is_responsible"> <path stroke-width="1.2" vector-effect="non-scaling-stroke" stroke-linecap="round" stroke-linejoin="round" d="M4.26 10.147a60.436 60.436 0 00-.491 6.347A48.627 48.627 0 0112 20.904a48.627 48.627 0 018.232-4.41 60.46 60.46 0 00-.491-6.347m-15.482 0a50.57 50.57 0 00-2.658-.813A59.905 59.905 0 0112 3.493a59.902 59.902 0 0110.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.697 50.697 0 0112 13.489a50.702 50.702 0 017.74-3.342M6.75 15a.75.75 0 100-1.5.75.75 0 000 1.5zm0 0v-3.675A55.378 55.378 0 0112 8.443m-7.007 11.55A5.981 5.981 0 006.75 15.75v-1.5" /> </svg> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-7 h-7" v-else> <path vector-effect="non-scaling-stroke" stroke-width="1.1" stroke-linecap="round" stroke-linejoin="round" d="M17.982 18.725A7.488 7.488 0 0012 15.75a7.488 7.488 0 00-5.982 2.975m11.963 0a9 9 0 10-11.963 0m11.963 0A8.966 8.966 0 0112 21a8.966 8.966 0 01-5.982-2.275M15 9.75a3 3 0 11-6 0 3 3 0 016 0z" /> </svg> {{ user.first_name }} </div> <button v-if="$auth.impersonating()" type="button" :disabled="loadingResetStatusUser[`${position.id}_${user.id}`]" @click.stop="resetUsersStatus(position.id, user.id)" class="ml-auto flex items-center gap-x-1 rounded-md bg-transparent text-sm underline text-blue-500 hover:text-blue-400 disabled:text-blue-400 disabled:hover:text-blue-400 disabled:cursor-not-allowed"> <LoaderTbf color="text-blue-400" class="w-3 h-3 inline-flex items-center justify-center" v-if="loadingResetStatusUser[`${position.id}_${user.id}`]" /> Resetează </button> <div class="text-gray-700 flex items-center translate-x-6 transition delay-75 duration-150" :class="[{ 'group-hover:translate-x-0': user.rights.can_view }]"> <div class="transition delay-75 duration-150 opacity-100 rounded-md py-1 px-2 text-xs font-medium ring-1 ring-inset" :class="[ user.status == 'completed' ? 'text-green-700 bg-green-50 ring-green-600/10' : $auth.user().id == user.id ? 'text-blue-700 bg-blue-50 ring-blue-600/10' : 'text-yellow-700 bg-yellow-50 ring-yellow-600/10', { 'group-hover:opacity-0': user.rights.can_view }, ]"> <template v-if="user.activity_count == 1">1 activitate</template> <template v-else>{{ user.activity_count }} activități</template> </div> <div class="ml-2 opacity-0 transition delay-75 duration-150 text-gray-400" :class="[{ 'group-hover:opacity-100': user.rights.can_view }]"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4"> <path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M16.28 11.47a.75.75 0 010 1.06l-7.5 7.5a.75.75 0 01-1.06-1.06L14.69 12 7.72 5.03a.75.75 0 011.06-1.06l7.5 7.5z" clip-rule="evenodd" /> </svg> </div> </div> </a> </dl> </li> <template v-if="positions.length < 6"> <CardEmptyPosition v-for="index in 6 - positions.length" :withPulse="false" class="cursor-pointer" @click="['admin', 'super_admin'].includes($auth.user().rights.role) ? $router.push({ name: 'positions_add' }) : ''" /> </template> </ul> </div> </TransitionRoot> <TransitionRoot :show="!loaded" enter="ease-out duration-500" enter-from="opacity-0" enter-to="opacity-100" leave="ease-in duration-200" leave-from="opacity-100" leave-to="opacity-0"> <div class="absolute inset-0 pt-10 pb-20 px-16 max-w-none mx-auto"> <ul role="list" class="grid grid-cols-1 gap-x-6 gap-y-8 lg:grid-cols-3 xl:gap-x-8"> <CardLoaderPosition v-for="index in 6" :withPulse="true" /> </ul> </div> </TransitionRoot> </div> </template> <script> // Import the required components import { TransitionRoot } from "@headlessui/vue"; import NavbarPositions from "@/components/public/positions/NavbarPositions.vue"; import LoaderTbf from "@/components/public/LoadingTbf.vue"; import CardLoaderPosition from "@/components/public/positions/CardLoaderPosition.vue"; import CardEmptyPosition from "@/components/public/positions/CardEmptyPosition.vue"; import { userNotificationsStore } from "@/stores/notifications.js"; import { useModalsStore } from "@/stores/modals.js"; export default { components: { TransitionRoot, NavbarPositions, LoaderTbf, CardLoaderPosition, CardEmptyPosition, }, data() { return { loaded: false, isResponsible: false, positions: [], realTimeModals: useModalsStore(), realTimeNotifications: userNotificationsStore(), loadingResetStatusUser: {}, loadingResetStatus: {}, }; }, async mounted() { await this.getPositions(); }, methods: { async getPositions() { await axios .get(`instances/${this.$auth.user().instance.id}/positions`) .then(({ data }) => { this.positions = data.data; this.isResponsible = data.data?.filter((el) => el.users?.find((user) => user.is_responsible == 1 && user.id == this.$auth.user().id)).length ? true : false; if (this.positions.filter((el) => el.status == "processing").length) { this.positions .filter((el) => el.status == "processing") .forEach((el) => { this.checkStatusUntilCompleted(el); }); } }) .catch((error) => { var responseError = error.response.data; this.realTimeNotifications.addNotification({ type: "error", title: "TBF ERROR", description: `"${responseError.message}"<br>Vă rugăm să ne contactați și să ne transmiteți eroare. În interesul remedierii acestei probleme cât mai curând posibil.`, }); }) .finally(() => { this.loaded = true; }); }, openVerification(position) { if (position.evaluation_completed && position.rights.can_evaluate) { this.$router.push({ name: "position_activities", params: { id: position.id } }); } else if (position.status == "completed" && position.rights.can_evaluate) { this.$router.push({ name: "positions_check_activity", params: { id: position.id } }); } }, async checkStatusUntilCompleted(position) { let interval = setInterval(async () => { try { let response = await axios.get(`/instances/${this.$auth.user().instance.id}/positions/${position.id}/check-status`); if (response.data.status === "completed") { clearInterval(interval); this.positions.find((el) => el.id == position.id).status = "completed"; } } catch (error) { console.error("Error checking position status:", error); } }, 5000); }, deletePosition(positionId) { this.realTimeModals.toggleShowModal("modal_confirm_alert", { modal_title: "Ești sigur că dorești să ștergi această funcție?", modal_description: "Această acțiune este ireversibilă.", call_back: () => { axios.delete(`instances/${this.$auth.user().instance.id}/positions/${positionId}`).then(({ data }) => { this.realTimeNotifications.addNotification({ type: "success", title: "Ștergere reușită", description: `Funcția a fost ștearsă cu succes.`, }); let positionIndex = this.positions.findIndex((el) => el.id == positionId); if (positionIndex != -1) { this.positions.splice(positionIndex, 1); } }); }, }); }, resetUsersStatus(positionId, userId) { this.loadingResetStatusUser[`${positionId}_${userId}`] = true; this.realTimeModals.toggleShowModal("modal_confirm_alert", { modal_title: "Ești sigur că dorești să resetezi aceast angajat?", modal_description: "Această acțiune este ireversibilă.", theme_color: "yellow", confirm_btn_text: "Resetează", call_back: () => { axios.patch(`instances/${this.$auth.user().instance.id}/positions/${positionId}/users/${userId}/update-status`, { status: 'ongoing' }).then(({ data }) => { this.realTimeNotifications.addNotification({ type: "success", title: "Resetare reușită", description: `Userul poate adauga activitati in continuare.`, }); this.getPositions(); }) .finally(() => { this.loadingResetStatusUser[`${positionId}_${userId}`] = false; }); }, }); }, resetPositionStatus(positionId) { this.loadingResetStatusUser[positionId] = true; this.realTimeModals.toggleShowModal("modal_confirm_alert", { modal_title: "Ești sigur că dorești să resetezi această funcție?", modal_description: "Această acțiune este ireversibilă.", theme_color: "yellow", confirm_btn_text: "Resetează", call_back: () => { axios.post(`instances/${this.$auth.user().instance.id}/positions/${positionId}/regenerate`).then(({ data }) => { this.realTimeNotifications.addNotification({ type: "success", title: "Resetare reușită", description: `Funcția a fost resetată cu succes.`, }); this.getPositions(); }) .finally(() => { this.loadingResetStatusUser[positionId] = false; }); }, }); } }, }; </script>
Back