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 :
PositionCreate.vue
| Size :
60.70
KB
Copy
<template> <!-- Content --> <div class="relative"> <div class="bg-gray-50 px-16 pt-14 py-10 border-b border-gray-200"> <div class="flex justify-between items-center"> <div class="flex items-center gap-x-3 relative"> <!-- Back button --> <router-link :to="{ name: 'positions' }" class="inset-y-0 -left-10 flex items-center absolute transition duration-300 hover:-translate-x-1 hover:text-gray-500"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"> <path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M7.72 12.53a.75.75 0 010-1.06l7.5-7.5a.75.75 0 111.06 1.06L9.31 12l6.97 6.97a.75.75 0 11-1.06 1.06l-7.5-7.5z" clip-rule="evenodd" /> </svg> </router-link> <!-- Titlu funcție --> <h3 class="text-base font-semibold leading-6 text-gray-900">{{ editMode ? "Modifică funcția" : "Adaugă funcție" }}</h3> </div> <Transition enter-active-class="ease-out duration-500" enter-from-class="opacity-0" enter-to-class="opacity-100" leave-active-class="ease-in duration-200" leave-from-class="opacity-100" leave-to-class="opacity-0"> <button v-if="loaded" type="button" @click="savePosition(true)" :disabled="loadingSubmit" class="ml-auto flex rounded-md bg-blue-500 px-3 py-2 text-sm font-semibold text-white hover:bg-blue-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"> <LoaderTbf color="text-white mr-2" v-if="loadingSubmit" /> Salvează Funcție </button> </Transition> <div class="relative ml-5"> <button @click="openTutorial" class="p-2 bg-blue-500 hover:bg-blue-400 rounded-full text-white"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6"> <path 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> </button> <transition enter-active-class="transition-all ease-out duration-150 origin-top" enter-from-class="opacity-0 scale-75" enter-to-class="opacity-100 scale-105" leave-active-class="transition-all ease-in duration-75 origin-top" leave-from-class="opacity-100 scale-105" leave-to-class="opacity-0 scale-75"> <div v-if="tutorialPopover" class="absolute top-[100%] scal translate-y-3 -right-5 w-[15rem] p-4 rounded-lg text-sm text-center text-white bg-blue-400 shadow-sm transition-all duration-150 z-20"> <div class="relative"> <div class="w-3 h-3 bg-blue-400 absolute -top-5 right-[1.1rem] rotate-45"></div> </div> <div class="p-3 bg-white/20 text-white rounded-full inline-block"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-6 h-6"> <path fill-rule="evenodd" d="M9.401 3.003c1.155-2 4.043-2 5.197 0l7.355 12.748c1.154 2-.29 4.5-2.599 4.5H4.645c-2.309 0-3.752-2.5-2.598-4.5L9.4 3.003zM12 8.25a.75.75 0 01.75.75v3.75a.75.75 0 01-1.5 0V9a.75.75 0 01.75-.75zm0 8.25a.75.75 0 100-1.5.75.75 0 000 1.5z" clip-rule="evenodd" /> </svg> </div> <h3 class="mt-2 font-semibold text-base text-center">Atenție</h3> <p class="mt-2 text-center">Urmărește acest video cu instrucțiuni înainte să completezi pagina</p> <button @click="openTutorial" type="button" class="mt-5 mb-0.5 w-full text-center rounded-full px-7 py-2 text-sm font-semibold focus-visible:outline-none bg-white text-blue-500 hover:text-blue-400"> Vezi video </button> </div> </transition> </div> </div> </div> <Transition enter-active-class="ease-out duration-500" enter-from-class="opacity-0" enter-to-class="opacity-100" leave-active-class="ease-in duration-200" leave-from-class="opacity-100" leave-to-class="opacity-0"> <div v-if="loaded" class="pt-10 pb-0 px-16 max-w-none mx-auto"> <div class="space-y-12"> <div class="grid grid-cols-1 gap-x-8 gap-y-10 border-b border-gray-900/10 pb-12 md:grid-cols-3"> <div> <h2 class="text-base font-semibold leading-7 text-gray-900">Despre Funcție</h2> <p class="mt-1 text-sm leading-6 text-gray-600">Completează numele și descrierea funcției</p> </div> <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 md:col-span-2"> <div class="sm:col-span-4"> <label for="name" class="block text-sm font-semibold leading-6 text-gray-900"> Nume funcție <Popper hover :openDelay="200" offsetDistance="10" placement="right"> <span class="text-gray-400">(?)</span> <template #content> <div role="tooltip" class="bg-gray-900 shadow-sm ring-1 ring-gray-900/5 rounded-lg px-4 py-3 text-white text-xs relative max-w-[15rem]"> <div class="w-3 h-3 bg-gray-900 absolute top-[50%] -translate-y-[50%] -left-1 rotate-45"></div> <p>Exemple: Director de vânzări, Specialist de marketing, Contabil, Expert contabil, etc.</p> </div> </template> </Popper> </label> <div class="mt-2 relative"> <input type="text" v-model="name" id="name" autocomplete="given-name" class="block w-full rounded-md border-0 py-1.5 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6" :class="[ v$.name.$errors.length ? 'text-red-900 ring-red-300 placeholder:text-red-300 focus:ring-red-500' : 'text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-blue-500', ]" placeholder="Ex: Vânzător de teren" /> <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3" v-if="v$.name.$errors.length"> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512" class="h-5 w-5 text-red-500"> <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 512zm0-384c-8.8 0-16 7.2-16 16V272c0 8.8 7.2 16 16 16s16-7.2 16-16V144c0-8.8-7.2-16-16-16zm24 224a24 24 0 1 0 -48 0 24 24 0 1 0 48 0z" /> </svg> </div> </div> <p class="mt-2 text-sm text-red-600" v-if="v$.name.$errors.length">Numele funcției este obligatoriu.</p> </div> <div class="col-span-full"> <div class="flex justify-between"> <label for="description" class="block text-sm font-semibold leading-6 text-gray-900"> Descriere funcție <Popper hover :openDelay="200" offsetDistance="10" placement="right"> <span class="text-gray-400">(?)</span> <template #content> <div role="tooltip" class="bg-gray-900 shadow-sm ring-1 ring-gray-900/5 rounded-lg px-4 py-3 text-white text-xs relative max-w-[15rem]"> <div class="w-3 h-3 bg-gray-900 absolute top-[50%] -translate-y-[50%] -left-1 rotate-45"></div> <p> Exemplu: Specialistul de marketing se ocupă cu tot ceea ce ține de promovarea magazinului nostru online. Crează AD-uri, Landing Page-uri și Newslettere săptămânale. </p> </div> </template> </Popper> </label> <div class="text-sm text-gray-500" :class="[{ 'text-green-500': descriptionLength > 150 }, { 'text-red-500': v$.description.$errors.length }]"> {{ descriptionLength }}/150 </div> </div> <div class="mt-2 relative"> <resize-textarea :key="`textareaDescription${keyInputs}`" id="description" v-model="description" :rows="3" class="block w-full rounded-md border-0 py-1.5 ring-1 ring-inset focus:ring-2 focus:ring-inset sm:text-sm sm:leading-6" :class="[ v$.description.$errors.length ? 'text-red-900 ring-red-300 placeholder:text-red-300 focus:ring-red-500' : 'text-gray-900 ring-gray-300 placeholder:text-gray-400 focus:ring-blue-500', ]" /> <p class="mt-2 text-sm text-red-600" v-if="v$.description.$errors.length">Descrierea funcției este obligatorie, minim 150 de caractere.</p> <div class="absolute top-0 left-0 w-full rounded-lg h-full flex items-center justify-center bg-white/50 z-50 backdrop-blur-sm" v-if="loadedGenerateAi"> <LoaderTbf color="text-blue-500" /> </div> </div> <div class="mt-3 text-sm font-medium leading-6 text-blue-500 hover:text-blue-600 flex items-center"> <span class="text-gray-600">Descrie funcția pe scurt</span> <button v-if="false" :disabled="loadedGenerateAi" class="cursor-pointer text-blue-500 hover:text-blue-600 disabled:cursor-not-allowed disabled:text-blue-300 disabled:hover:text-blue-300 inline-flex items-center" @click="generateDescWithAi"> <LoaderTbf color="text-blue-500" class="mx-2 flex items-center justify-center" v-if="loadedGenerateAi" /> <div class="w-[20px] h-[19px] mx-2 flex items-center justify-center" v-else> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 512 512" class="inline-block"> <path fill="currentColor" d="M327.5 85.2c-4.5 1.7-7.5 6-7.5 10.8s3 9.1 7.5 10.8L384 128l21.2 56.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L448 128l56.5-21.2c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L448 64 426.8 7.5C425.1 3 420.8 0 416 0s-9.1 3-10.8 7.5L384 64 327.5 85.2zM205.1 73.3c-2.6-5.7-8.3-9.3-14.5-9.3s-11.9 3.6-14.5 9.3L123.3 187.3 9.3 240C3.6 242.6 0 248.3 0 254.6s3.6 11.9 9.3 14.5l114.1 52.7L176 435.8c2.6 5.7 8.3 9.3 14.5 9.3s11.9-3.6 14.5-9.3l52.7-114.1 114.1-52.7c5.7-2.6 9.3-8.3 9.3-14.5s-3.6-11.9-9.3-14.5L257.8 187.4 205.1 73.3zM384 384l-56.5 21.2c-4.5 1.7-7.5 6-7.5 10.8s3 9.1 7.5 10.8L384 448l21.2 56.5c1.7 4.5 6 7.5 10.8 7.5s9.1-3 10.8-7.5L448 448l56.5-21.2c4.5-1.7 7.5-6 7.5-10.8s-3-9.1-7.5-10.8L448 384l-21.2-56.5c-1.7-4.5-6-7.5-10.8-7.5s-9.1 3-10.8 7.5L384 384z" /> </svg> </div> Generează cu AI </button> </div> </div> </div> </div> <div class="grid grid-cols-1 gap-x-8 gap-y-10 border-b border-gray-900/10 pb-12 md:grid-cols-3"> <div> <h2 class="text-base font-semibold leading-7 text-gray-900">Detalii Funcție</h2> <p class="mt-1 text-sm leading-6 text-gray-600">Bifează ce este adevărat în acest moment</p> </div> <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 md:col-span-2"> <fieldset class="col-span-2"> <legend class="text-sm font-semibold leading-6 text-gray-900" :class="[v$.optionsSelected.$errors.length ? 'text-red-500' : 'text-gray-900']"> Locație <Popper hover :openDelay="200" offsetDistance="10" placement="right"> <span class="text-gray-400">(?)</span> <template #content> <div role="tooltip" class="bg-gray-900 shadow-sm ring-1 ring-gray-900/5 rounded-lg px-4 py-3 text-white text-xs relative max-w-[15rem]"> <div class="w-3 h-3 bg-gray-900 absolute top-[50%] -translate-y-[50%] -left-1 rotate-45"></div> <p> Bifează tot ceea ce se aplică pentru oamenii care dețin această funcție. Dacă lucrează atât remote cât și de la birou atunci bifează ambele opțiuni. </p> </div> </template> </Popper> </legend> <div class="mt-4 space-y-3"> <div class="relative flex items-start" v-for="option in options.filter((el) => el.category == 'location')"> <div class="flex h-6 items-center"> <input :id="`optionLocations${option.code}`" v-model="optionsSelected" :value="option.id" type="checkbox" class="h-4 w-4 rounded" :class="[ v$.optionsSelected.$errors.length ? 'text-red-900 border-red-400 placeholder:text-red-300 focus:ring-red-500' : 'border-gray-300 text-blue-500 focus:ring-blue-500', ]" /> </div> <div class="ml-3 text-sm leading-6"> <label :for="`optionLocations${option.code}`" :class="[optionsSelected.find((el) => el == option.id) ? 'text-gray-900' : 'text-gray-500']">{{ option.name }}</label> </div> </div> </div> <p class="mt-2 text-sm text-red-600" v-if="v$.optionsSelected.$errors.length">Selectează o opțiune.</p> </fieldset> <fieldset class="col-span-2"> <legend class="text-sm font-semibold leading-6 text-gray-900"> Softuri Folosite <Popper hover :openDelay="200" offsetDistance="10" placement="right"> <span class="text-gray-400">(?)</span> <template #content> <div role="tooltip" class="bg-gray-900 shadow-sm ring-1 ring-gray-900/5 rounded-lg px-4 py-3 text-white text-xs relative max-w-[15rem]"> <div class="w-3 h-3 bg-gray-900 absolute top-[50%] -translate-y-[50%] -left-1 rotate-45"></div> <p>Bifează softurile pe care oamenii care dețin această funcție le folosesc în mod curent</p> </div> </template> </Popper> </legend> <div class="mt-4 space-y-3"> <div class="relative flex items-start" v-for="option in options.filter((el) => el.category == 'software')"> <div class="flex h-6 items-center"> <input :id="`optionSoftwares${option.code}`" v-model="optionsSelected" :value="option.id" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-blue-500 focus:ring-blue-500" /> </div> <div class="ml-3 text-sm leading-6"> <label :for="`optionSoftwares${option.code}`" :class="[optionsSelected.find((el) => el == option.id) ? 'text-gray-900' : 'text-gray-500']">{{ option.name }}</label> </div> </div> </div> </fieldset> <fieldset class="col-span-2"> <legend class="text-sm font-semibold leading-6 text-gray-900"> Altele <Popper hover :openDelay="200" offsetDistance="10" placement="right"> <span class="text-gray-400">(?)</span> <template #content> <div role="tooltip" class="bg-gray-900 shadow-sm ring-1 ring-gray-900/5 rounded-lg px-4 py-3 text-white text-xs relative max-w-[15rem]"> <div class="w-3 h-3 bg-gray-900 absolute top-[50%] -translate-y-[50%] -left-1 rotate-45"></div> <p>Bifează dacă oamenii de pe această funcție folosesc computerul sau conduc mașină în scop de serviciu</p> </div> </template> </Popper> </legend> <div class="mt-4 space-y-3"> <div class="relative flex items-start" v-for="option in options.filter((el) => el.category == 'detail')"> <div class="flex h-6 items-center"> <input :id="`optionDetails${option.code}`" v-model="optionsSelected" :value="option.id" type="checkbox" class="h-4 w-4 rounded border-gray-300 text-blue-500 focus:ring-blue-500" /> </div> <div class="ml-3 text-sm leading-6"> <label :for="`optionDetails${option.code}`" :class="[optionsSelected.find((el) => el == option.id) ? 'text-gray-900' : 'text-gray-500']">{{ option.name }}</label> </div> </div> </div> </fieldset> </div> </div> <div class="grid grid-cols-1 gap-x-8 gap-y-10 border-b border-gray-900/10 pb-12 md:grid-cols-3"> <div> <h2 class="text-base font-semibold leading-7 text-gray-900">Angajați Funcție</h2> <p class="mt-1 text-sm leading-6 text-gray-600">Adaugă echipa de documentare a activităților pentru funcție</p> </div> <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 md:col-span-2"> <div class="col-span-full"> <div> <div class="max-w-lg"> <h2 class="block text-sm font-semibold leading-6 text-gray-900">Adaugă responsabil de verificare</h2> <p class="mt-1 text-sm text-gray-600"> Această persoană este de cele mai multe ori managerul departamentului din care face parte funcția. Va fi responsabilă să verifice activitățile documentate de angajați. </p> </div> <div class="max-w-lg mt-6 grid grid-cols-2 gap-x-5"> <form class="space-y-3" @submit.prevent="addUser(true)" autocomplete="off"> <Combobox class="col-span-5 relative" as="div" v-model="selectedResponsible" nullable> <ComboboxInput class="w-full rounded-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6" @change="querySearchResponsible = $event.target.value" :display-value="(person) => person?.email" @blur="setCurrentOptionResponsible" placeholder="Adresa de email" autocomplete="off" /> <ComboboxOptions class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> <ComboboxOption v-if="queryResponsible" as="template" :value="queryResponsible"> <li :class="['relative cursor-default select-none py-2 pl-3 pr-9']"> <div class="flex"> <span :class="['truncate text-gray-500']"> Adauga "{{ querySearchResponsible }}" </span> </div> </li> </ComboboxOption> <ComboboxOption v-for="person in filteredUsers(true)" :key="person.first_name" :value="person" as="template" v-slot="{ active, selected }"> <li :class="['relative cursor-default select-none py-2 pl-3 pr-9', active ? 'bg-blue-600 text-white' : 'text-gray-900']"> <div class="flex flex-col"> <span :class="['truncate', selected && 'font-semibold']"> {{ person.first_name }} </span> <span :class="['truncate text-gray-500', active ? 'text-indigo-200' : 'text-gray-500']"> {{ person.email }} </span> </div> <span v-if="selected" :class="['absolute inset-y-0 right-0 flex items-center pr-4', active ? 'text-white' : 'text-blue-600']"> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512" class="h-5 w-5"> <path fill="currentColor" d="M443.3 100.7c6.2 6.2 6.2 16.4 0 22.6l-272 272c-6.2 6.2-16.4 6.2-22.6 0l-144-144c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L160 361.4 420.7 100.7c6.2-6.2 16.4-6.2 22.6 0z" /> </svg> </span> </li> </ComboboxOption> </ComboboxOptions> </Combobox> <label for="new-user_name_resp" class="sr-only">Nume</label> <input v-model="responsible_name" id="new-user_name_resp" class="col-span-4 block w-full rounded-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6" placeholder="Nume" autocomplete="off" /> <button type="submit" class="w-full col-span-2 rounded-md bg-blue-500 px-3 py-2 text-sm font-semibold text-white hover:bg-blue-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600"> Adaugă </button> </form> <div v-if="responsible"> <div class="relative h-full rounded-lg border-2 border-dashed border-gray-300 text-center flex flex-col items-center justify-center p-3"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-7 h-7 text-gray-900 mx-auto"> <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> <span class="max-w-full mt-2 block text-sm font-medium text-gray-900 truncate">{{ responsible.first_name }}</span> <p class="max-w-full text-sm text-gray-500 truncate">{{ responsible.email }}</p> <div class="absolute top-3 right-3 flex items-center content-center cursor-pointer hover:text-gray-500 w-4 h-4" @click="clearResponsible"> <svg xmlns="http://www.w3.org/2000/svg" class="w-4 h-4" viewBox="0 0 384 512"> <!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --> <path fill="currentColor" d="M345 137c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-119 119L73 103c-9.4-9.4-24.6-9.4-33.9 0s-9.4 24.6 0 33.9l119 119L39 375c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l119-119L311 409c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-119-119L345 137z" /> </svg> </div> </div> </div> <div v-else> <div class="relative h-full rounded-lg border-2 border-dashed text-center flex items-center justify-center" :class="[v$.responsible.$errors.length ? 'border-red-400' : 'border-gray-300']"> <div> <div class="w-7 h-7 bg-gray-200 mx-auto rounded-full flex items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 text-white"> <path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z" clip-rule="evenodd" /> </svg> </div> <div class="mt-3 h-2 w-32 bg-gray-200 rounded-md mx-auto"></div> <div class="mt-2 h-2 w-24 bg-gray-100 rounded-md mx-auto"></div> </div> <div class="absolute top-3 right-3 flex-shrink-0 flex items-center content-center" style="height: 20px"> <div class="bg-gray-100 rounded-md w-5 h-5 flex items-center justify-center"></div> </div> </div> <p class="mt-2 text-sm text-red-600" v-if="v$.responsible.$errors.length">Responsabilul este obligatoriu.</p> </div> </div> <div class="mt-10 max-w-lg"> <h2 class="block text-sm font-semibold leading-6 text-gray-900">Adaugă responsabili de documentare</h2> <p class="mt-1 text-sm text-gray-600"> Acești angajați vor documenta activitățile pe care le fac lună de lună. Folosind aceste activități vom genera responsabilități măsurabile și corecte. </p> </div> <form class="max-w-2xl mt-6 grid grid-cols-10 gap-x-2" :class="[{ 'opacity-50': selectedUsers.length >= 4 }]" @submit.prevent="addUser(false)" autocomplete="off"> <Combobox class="col-span-5 relative" as="div" v-model="selectedUser" nullable :disabled="selectedUsers.length >= 4"> <ComboboxInput class="w-full rounded-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6 disabled:cursor-not-allowed" @change="queryUser = $event.target.value" :display-value="(person) => person?.email" @blur="setCurrentOptionUser" placeholder="Adresa de email..." autocomplete="off" /> <ComboboxOptions class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> <ComboboxOption v-if="queryPerson" as="template" :value="queryPerson"> <li :class="['relative cursor-default select-none py-2 pl-3 pr-9']"> <div class="flex"> <span :class="['truncate text-gray-500']"> Adauga "{{ queryUser }}" </span> </div> </li> </ComboboxOption> <ComboboxOption v-for="person in filteredUsers(false)" :key="person.first_name" :value="person" as="template" v-slot="{ active, selected }"> <li :class="['relative cursor-default select-none py-2 pl-3 pr-9', active ? 'bg-blue-600 text-white' : 'text-gray-900']"> <div class="flex flex-col"> <span :class="['truncate', selected && 'font-semibold']"> {{ person.first_name }} </span> <span :class="['truncate text-gray-500', active ? 'text-indigo-200' : 'text-gray-500']"> {{ person.email }} </span> </div> <span v-if="selected" :class="['absolute inset-y-0 right-0 flex items-center pr-4', active ? 'text-white' : 'text-blue-600']"> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512" class="h-5 w-5"> <path fill="currentColor" d="M443.3 100.7c6.2 6.2 6.2 16.4 0 22.6l-272 272c-6.2 6.2-16.4 6.2-22.6 0l-144-144c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L160 361.4 420.7 100.7c6.2-6.2 16.4-6.2 22.6 0z" /> </svg> </span> </li> </ComboboxOption> </ComboboxOptions> </Combobox> <label for="new-user_name" class="sr-only">Nume</label> <input v-model="user_name" id="new-user_name" class="col-span-3 block w-full rounded-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6 disabled:cursor-not-allowed" placeholder="Nume" autocomplete="off" :disabled="selectedUsers.length >= 4" /> <button type="submit" :disabled="selectedUsers.length >= 4" class="col-span-2 flex-shrink-0 rounded-md bg-blue-500 px-3 py-2 text-sm font-semibold text-white hover:bg-blue-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:cursor-not-allowed"> Adaugă </button> </form> </div> <div class="mt-6 max-w-lg"> <ul role="list" class="mt-4 divide-y divide-gray-200"> <li v-for="(person, personIdx) in selectedUsers" :key="personIdx" class="flex items-center justify-between space-x-3 py-3"> <div class="flex min-w-0 flex-1 items-center space-x-3"> <div class="flex-shrink-0"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-7 h-7"> <path stroke-width="1.1" vector-effect="non-scaling-stroke" 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> </div> <div class="flex items-center gap-5"> <p class="truncate text-sm font-medium text-gray-900 w-40">{{ person.first_name }}</p> <p class="truncate text-sm text-gray-500">{{ person.email }}</p> </div> </div> <div class="flex-shrink-0"> <button @click="selectedUsers.splice(personIdx, 1)" class="inline-flex items-center gap-x-1.5 font-semibold leading-6 text-gray-500 hover:text-gray-900 hover:bg-gray-50 rounded-lg p-2"> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512"> <!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --> <path fill="currentColor" d="M324.5 411.1c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6L214.6 256 347.1 123.5c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0L192 233.4 59.5 100.9c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6L169.4 256 36.9 388.5c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0L192 278.6 324.5 411.1z" /> </svg> </button> </div> </li> <template v-if="selectedUsers.length < 4"> <li v-for="item in 4 - selectedUsers.length" class="flex items-center justify-between space-x-3 py-3"> <div class="flex min-w-0 flex-1 items-center space-x-3"> <div class="flex-shrink-0"> <div class="w-7 h-7 bg-gray-200 rounded-full flex items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 text-white"> <path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z" clip-rule="evenodd" /> </svg> </div> </div> <div class="flex items-center gap-5"> <div class="h-5 w-40 rounded-md bg-gray-200"></div> <div class="h-5 w-52 rounded-md bg-gray-100"></div> </div> </div> <div class="flex-shrink-0 flex items-center content-center" style="height: 32px"> <div class="bg-gray-200 rounded-md w-5 h-5 flex items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-4 h-4 text-white"> <path vector-effect="non-scaling-stroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" /> </svg> </div> </div> </li> </template> </ul> </div> </div> </div> </div> <template v-if="$auth.impersonating()"> <div class="grid grid-cols-1 gap-x-8 gap-y-10 border-b border-gray-900/10 pb-12 md:grid-cols-3"> <div> <h2 class="text-base font-semibold leading-7 text-gray-900">Administratori Funcție</h2> <p class="mt-1 text-sm leading-6 text-gray-600"> Acești angajați vor verifica activitățile introduse de responsabilii de documentare și vor avea acces la responsabilități, proceduri și procese de recrutare. </p> </div> <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 md:col-span-2"> <div class="col-span-full"> <div> <form class="max-w-2xl mt-0 grid grid-cols-10 gap-x-2" @submit.prevent="addUserAdmin()" autocomplete="off"> <Combobox class="col-span-5 relative" as="div" v-model="selectedAdmin" nullable> <ComboboxInput class="w-full rounded-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6 disabled:cursor-not-allowed" @change="querySearchAdmin = $event.target.value" :display-value="(person) => person?.email" @blur="setCurrentOptionAdmin" placeholder="Adresa de email..." autocomplete="off" /> <ComboboxOptions class="absolute z-10 mt-1 max-h-60 w-full overflow-auto rounded-md bg-white py-1 text-base shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none sm:text-sm"> <ComboboxOption v-if="queryAdmin" as="template" :value="queryAdmin"> <li :class="['relative cursor-default select-none py-2 pl-3 pr-9']"> <div class="flex"> <span :class="['truncate text-gray-500']"> Adauga "{{ querySearchAdmin }}" </span> </div> </li> </ComboboxOption> <ComboboxOption v-for="person in filteredUsers(false)" :key="person.first_name" :value="person" as="template" v-slot="{ active, selected }"> <li :class="['relative cursor-default select-none py-2 pl-3 pr-9', active ? 'bg-blue-600 text-white' : 'text-gray-900']"> <div class="flex flex-col"> <span :class="['truncate', selected && 'font-semibold']"> {{ person.first_name }} </span> <span :class="['truncate text-gray-500', active ? 'text-indigo-200' : 'text-gray-500']"> {{ person.email }} </span> </div> <span v-if="selected" :class="['absolute inset-y-0 right-0 flex items-center pr-4', active ? 'text-white' : 'text-blue-600']"> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512" class="h-5 w-5"> <path fill="currentColor" d="M443.3 100.7c6.2 6.2 6.2 16.4 0 22.6l-272 272c-6.2 6.2-16.4 6.2-22.6 0l-144-144c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L160 361.4 420.7 100.7c6.2-6.2 16.4-6.2 22.6 0z" /> </svg> </span> </li> </ComboboxOption> </ComboboxOptions> </Combobox> <label for="new-user_name" class="sr-only">Nume</label> <input v-model="admin_name" id="new-user_name" class="col-span-3 block w-full rounded-md border-0 py-1.5 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-blue-500 sm:text-sm sm:leading-6 disabled:cursor-not-allowed" placeholder="Nume" autocomplete="off" /> <button type="submit" class="col-span-2 flex-shrink-0 rounded-md bg-blue-500 px-3 py-2 text-sm font-semibold text-white hover:bg-blue-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:cursor-not-allowed"> Adaugă </button> </form> </div> <div class="mt-6 max-w-lg"> <ul role="list" class="mt-4 divide-y divide-gray-200"> <li v-for="(person, personIdx) in selectedAdmins" :key="personIdx" class="flex items-center justify-between space-x-3 py-3"> <div class="flex min-w-0 flex-1 items-center space-x-3"> <div class="flex-shrink-0"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-7 h-7"> <path stroke-width="1.1" vector-effect="non-scaling-stroke" 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> </div> <div class="flex items-center gap-5"> <p class="truncate text-sm font-medium text-gray-900 w-40">{{ person.first_name }}</p> <p class="truncate text-sm text-gray-500">{{ person.email }}</p> </div> </div> <div class="flex-shrink-0"> <button @click="selectedAdmins.splice(personIdx, 1)" class="inline-flex items-center gap-x-1.5 font-semibold leading-6 text-gray-500 hover:text-gray-900 hover:bg-gray-50 rounded-lg p-2"> <svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 384 512"> <!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --> <path fill="currentColor" d="M324.5 411.1c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6L214.6 256 347.1 123.5c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0L192 233.4 59.5 100.9c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6L169.4 256 36.9 388.5c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0L192 278.6 324.5 411.1z" /> </svg> </button> </div> </li> <template v-if="selectedAdmins.length < 4"> <li v-for="item in 4 - selectedAdmins.length" class="flex items-center justify-between space-x-3 py-3"> <div class="flex min-w-0 flex-1 items-center space-x-3"> <div class="flex-shrink-0"> <div class="w-7 h-7 bg-gray-200 rounded-full flex items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-4 h-4 text-white"> <path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M7.5 6a4.5 4.5 0 119 0 4.5 4.5 0 01-9 0zM3.751 20.105a8.25 8.25 0 0116.498 0 .75.75 0 01-.437.695A18.683 18.683 0 0112 22.5c-2.786 0-5.433-.608-7.812-1.7a.75.75 0 01-.437-.695z" clip-rule="evenodd" /> </svg> </div> </div> <div class="flex items-center gap-5"> <div class="h-5 w-40 rounded-md bg-gray-200"></div> <div class="h-5 w-52 rounded-md bg-gray-100"></div> </div> </div> <div class="flex-shrink-0 flex items-center content-center" style="height: 32px"> <div class="bg-gray-200 rounded-md w-5 h-5 flex items-center justify-center"> <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" class="w-4 h-4 text-white"> <path vector-effect="non-scaling-stroke" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5" /> </svg> </div> </div> </li> </template> </ul> </div> </div> </div> </div> <div class="grid grid-cols-1 gap-x-8 gap-y-10 border-b border-gray-900/10 pb-12 md:grid-cols-3"> <div> <h2 class="text-base font-semibold leading-7 text-gray-900">Setări Funcție</h2> <p class="mt-1 text-sm leading-6 text-gray-600">Bifează ceea ce ai nevoie</p> </div> <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6 md:col-span-2"> <div class="col-span-full"> <SwitchGroup as="div" class="flex items-center justify-between col-span-1"> <span class="flex flex-grow flex-col"> <SwitchLabel as="span" class="text-sm font-medium leading-6 text-gray-900" passive>Anunț de recrutare</SwitchLabel> <SwitchDescription as="span" class="text-sm text-gray-500 mt-1" >Dacă ai nevoie de anunț de recrutare, te rugăm să bifezi această opțiune.</SwitchDescription > </span> <Switch v-model="need_for_recruitment" :class="[ need_for_recruitment ? 'bg-blue-500' : 'bg-gray-200', 'relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2', ]"> <span aria-hidden="true" :class="[ need_for_recruitment ? 'translate-x-5' : 'translate-x-0', 'pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out', ]" /> </Switch> </SwitchGroup> </div> </div> </div> </template> <div class="grid grid-cols-1 gap-x-8 pb-14 md:grid-cols-3"> <div class="flex justify-end"> <button type="button" class="text-sm font-semibold leading-6 text-gray-900" @click="checkCanClose">Anulează</button> </div> <div class="col-span-2 flex items-center gap-x-4"> <button type="submit" :disabled="loadingSubmit" class="flex items-center gap-1 rounded-md bg-blue-500 px-3 py-2 text-sm font-semibold text-white hover:bg-blue-400 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-blue-600 disabled:bg-blue-400 disabled:cursor-not-allowed" @click="savePosition(false)"> <LoaderTbf color="text-white" v-if="loadingSubmit" /> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="w-5 h-5 -mt-px" v-else> <path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M6.32 2.577a49.255 49.255 0 0111.36 0c1.497.174 2.57 1.46 2.57 2.93V21a.75.75 0 01-1.085.67L12 18.089l-7.165 3.583A.75.75 0 013.75 21V5.507c0-1.47 1.073-2.756 2.57-2.93z" clip-rule="evenodd" /> </svg> Salvează Funcție </button> <p class="text-gray-500 text-sm mt-1">Angajații implicați vor fi <span class="font-medium text-gray-900">notificați imediat</span> după adăugare</p> </div> </div> </div> </div> <LoaderCreatePage v-else /> </Transition> </div> </template> <script> // Import the required components import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions, Switch, SwitchDescription, SwitchGroup, SwitchLabel } from "@headlessui/vue"; import { useVuelidate } from "@vuelidate/core"; import { required, helpers } from "@vuelidate/validators"; import { userNotificationsStore } from "@/stores/notifications.js"; import LoaderTbf from "@/components/public/LoadingTbf.vue"; import LoaderCreatePage from "@/components/public/positions/loaders/CreatePage.vue"; const requiredAndMinim150 = (value) => helpers.req(value) && value.replace(/ /g, "").length > 150; import { useModalsStore } from "@/stores/modals.js"; import { useCookies } from "vue3-cookies"; export default { setup() { const v$ = useVuelidate(); const { cookies } = useCookies(); return { v$, cookies }; }, components: { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOptions, LoaderTbf, LoaderCreatePage, Switch, SwitchDescription, SwitchGroup, SwitchLabel, }, data() { return { loaded: false, loadingSubmit: false, loadedGenerateAi: false, name: "", description: "", options: [], optionsSelected: [], optionsUsers: [], selectedUsers: [], selectedUser: "", selectedAdmins: [], selectedAdmin: "", admin_name: "", querySearchAdmin: "", need_for_recruitment: true, selectedResponsible: "", user_name: "", queryUser: "", responsible_name: "", querySearchResponsible: "", responsible: "", realTimeNotifications: userNotificationsStore(), editMode: false, keyInputs: 1, tutorialPopover: false, realTimeModals: useModalsStore(), emailRegex: /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/, }; }, computed: { queryPerson() { return this.queryUser === "" || this.optionsUsers.find((el) => el.email.toLowerCase() == this.queryUser.toLowerCase()) ? null : { first_name: this.user_name ? this.user_name : "", email: this.queryUser.toLowerCase() }; }, queryResponsible() { return this.querySearchResponsible === "" || this.optionsUsers.find((el) => el.email.toLowerCase() == this.querySearchResponsible.toLowerCase()) ? null : { first_name: this.responsible_name ? this.responsible_name : "", email: this.querySearchResponsible.toLowerCase() }; }, queryAdmin() { return this.querySearchAdmin === "" || this.optionsUsers.find((el) => el.email.toLowerCase() == this.querySearchAdmin.toLowerCase()) ? null : { first_name: this.admin_name ? this.admin_name : "", email: this.querySearchAdmin.toLowerCase() }; }, descriptionLength() { return this.description ? this.description.replace(/ /g, "").length : 0; }, }, watch: { selectedUser(newVal, oldVal) { this.user_name = newVal?.first_name ? newVal.first_name : ""; this.queryUser = newVal?.email ? newVal.email.toLowerCase() : this.queryUser.toLowerCase(); }, selectedResponsible(newVal, oldVal) { this.responsible_name = newVal?.first_name ? newVal.first_name : ""; this.querySearchResponsible = newVal?.email ? newVal.email.toLowerCase() : this.querySearchResponsible.toLowerCase(); }, selectedAdmin(newVal, oldVal) { this.admin_name = newVal?.first_name ? newVal.first_name : ""; this.querySearchAdmin = newVal?.email ? newVal.email.toLowerCase() : this.querySearchAdmin.toLowerCase(); }, }, validations() { return { name: { required }, description: { requiredAndMinim150 }, responsible: { required }, optionsSelected: { required }, }; }, async mounted() { if (this.$route.name == "positions_edit") { this.editMode = true; await this.getPositionData(); } await this.getOptions(); await this.getUsers(); var cookieModalVimeoData = this.cookies.get("modal_vimeo"); if (!(cookieModalVimeoData && JSON.parse(cookieModalVimeoData).includes("position_create"))) { setTimeout(() => { this.tutorialPopover = true; }, 1000); } }, methods: { filteredUsers(isResponsible) { var querySearch = isResponsible ? this.querySearchResponsible : this.queryUser; const selectedUserIDs = this.selectedUsers.map((user) => user.id); const selectedAdminIDs = this.selectedAdmins.map((user) => user.id); // const responsibleUserID = this.responsible ? this.responsible.id : ""; // const filtered = this.optionsUsers.filter((user) => user.id !== responsibleUserID && !selectedUserIDs.includes(user.id)); const filtered = this.optionsUsers.filter((user) => ![...selectedUserIDs, ...selectedAdminIDs].includes(user.id)); const remainingUsers = [...filtered]; return querySearch === "" ? remainingUsers.filter((el) => !el.unavailable) : remainingUsers.filter((person) => { const lowerCaseQuery = querySearch.toLowerCase(); return !person.unavailable && (person.first_name.toLowerCase().includes(lowerCaseQuery) || (person.email && person.email.toLowerCase().includes(lowerCaseQuery))); }); }, async getPositionData() { await axios.get(`instances/${this.$auth.user().instance.id}/positions/${this.$route.params.id}`).then(({ data }) => { var positionData = data.data; this.name = positionData.name; this.optionsSelected = positionData.options.map((el) => { return el.id; }); this.responsible = positionData.users.find((el) => el.is_responsible); this.selectedUsers = positionData.users.filter((el) => !el.is_responsible); this.selectedAdmins = positionData.admins; this.description = positionData.description; this.need_for_recruitment = positionData.need_for_recruitment; }); }, async getOptions() { await axios.get(`options?type=position`).then(({ data }) => { this.options = data.data; }); }, async getUsers() { await axios .get(`instances/${this.$auth.user().instance.id}/users`) .then(({ data }) => { this.optionsUsers = data.data; }) .finally(() => { this.loaded = true; }); }, async addUser(isResponsible = false) { if (isResponsible) { if (this.selectedResponsible && this.selectedResponsible.email && this.responsible_name && this.emailRegex.test(this.selectedResponsible.email.toLowerCase())) { var userData = { first_name: this.responsible_name, email: this.selectedResponsible.email.toLowerCase(), is_responsible: 1 }; if (this.selectedUsers.length && this.selectedUsers.find((el) => el.email.toLowerCase() == this.selectedResponsible.email.toLowerCase())) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "responsible_exist_on_users" }); this.clearUserInputs("responsible"); return; } if (this.selectedAdmins.length && this.selectedAdmins.find((el) => el.email.toLowerCase() == this.selectedResponsible.email.toLowerCase())) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "user_exist_on_admins" }); this.clearUserInputs("responsible"); return; } if (this.selectedResponsible.id) { userData.id = this.selectedResponsible.id; } else { try { const { data } = await axios.post(`/users/check/email`, { email: this.selectedResponsible.email.toLowerCase() }); if (data.exists) { this.realTimeNotifications.addNotification({ type: "error", title: "Adresă de email invalidă.", description: "Această adresă de email este deja înregistrată.", }); return; } } catch (error) { var responseError = error.response.data; this.realTimeNotifications.addNotification({ type: "error", title: "TBF ERROR", description: `"${responseError.message}".`, }); return; // This ensures we stop the function execution in case of an error } } this.responsible = userData; this.clearUserInputs("responsible"); } else { this.realTimeNotifications.addNotification({ type: "error", title: "Verifica formularul.", description: "Adresa de email și numele sunt obligatorii. Verifică ca adresa de email să fie validă.", }); } } else { if (this.selectedUser && this.selectedUser.email && this.user_name && this.emailRegex.test(this.selectedUser.email)) { if (this.responsible && this.responsible.email.toLowerCase() == this.selectedUser.email.toLowerCase()) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "exist_on_responsible" }); this.clearUserInputs("user"); return; } if (this.selectedUsers.length && this.selectedUsers.find((el) => el.email.toLowerCase() == this.selectedUser.email.toLowerCase())) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "user_exist_on_users" }); this.clearUserInputs("user"); return; } if (this.selectedAdmins.length && this.selectedAdmins.find((el) => el.email.toLowerCase() == this.selectedUser.email.toLowerCase())) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "user_exist_on_admins" }); this.clearUserInputs("user"); return; } var userData = { first_name: this.user_name, email: this.selectedUser.email.toLowerCase() }; if (this.selectedUser.id) { userData.id = this.selectedUser.id; } else { try { const { data } = await axios.post(`/users/check/email`, { email: this.selectedUser.email.toLowerCase() }); if (data.exists) { this.realTimeNotifications.addNotification({ type: "error", title: "Adresă de email invalidă.", description: "Această adresă de email este deja înregistrată.", }); return; } } catch (error) { var responseError = error.response.data; this.realTimeNotifications.addNotification({ type: "error", title: "TBF ERROR", description: `"${responseError.message}".`, }); return; // This ensures we stop the function execution in case of an error } } this.selectedUsers.push(userData); this.clearUserInputs("user"); } else { this.realTimeNotifications.addNotification({ type: "error", title: "Verifica formularul.", description: "Adresa de email și numele sunt obligatorii. Verifică ca adresa de email să fie validă.", }); } } }, async addUserAdmin() { if (this.selectedAdmin && this.selectedAdmin.email && this.admin_name && this.emailRegex.test(this.selectedAdmin.email)) { if (this.responsible && this.responsible.email.toLowerCase() == this.selectedAdmin.email.toLowerCase()) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "exist_on_responsible" }); this.clearUserInputs("admin"); return; } if (this.selectedUsers.length && this.selectedUsers.find((el) => el.email.toLowerCase() == this.selectedAdmin.email.toLowerCase())) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "user_exist_on_users" }); this.clearUserInputs("admin"); return; } if (this.selectedAdmins.length && this.selectedAdmins.find((el) => el.email.toLowerCase() == this.selectedAdmin.email.toLowerCase())) { this.realTimeModals.toggleShowModal("modal_error_add_users", { error: "user_exist_on_admins" }); this.clearUserInputs("admin"); return; } var userData = { first_name: this.admin_name, email: this.selectedAdmin.email.toLowerCase() }; if (this.selectedAdmin.id) { userData.id = this.selectedAdmin.id; } else { try { const { data } = await axios.post(`/users/check/email`, { email: this.selectedAdmin.email.toLowerCase() }); if (data.exists) { this.realTimeNotifications.addNotification({ type: "error", title: "Adresă de email invalidă.", description: "Această adresă de email este deja înregistrată.", }); return; } } catch (error) { var responseError = error.response.data; this.realTimeNotifications.addNotification({ type: "error", title: "TBF ERROR", description: `"${responseError.message}".`, }); return; // This ensures we stop the function execution in case of an error } } this.selectedAdmins.push(userData); this.clearUserInputs("admin"); } else { this.realTimeNotifications.addNotification({ type: "error", title: "Verifica formularul.", description: "Adresa de email și numele sunt obligatorii. Verifică ca adresa de email să fie validă.", }); } }, clearUserInputs(type) { if (type == "responsible") { this.selectedResponsible = ""; this.responsible_name = ""; this.querySearchResponsible = ""; } else if (type == "admin") { this.selectedAdmin = ""; this.admin_name = ""; this.querySearchAdmin = ""; } else { this.selectedUser = ""; this.user_name = ""; this.queryUser = ""; } }, async savePosition(withClose) { const isFormCorrect = await this.v$.$validate(); this.loadingSubmit = true; if (!isFormCorrect) { this.realTimeNotifications.addNotification({ type: "error", title: "Verifica formularul.", description: "Completeaza datele obligatorii pentru a putea continua.", }); this.loadingSubmit = false; return; } var usedUsers = []; if (this.responsible) { usedUsers.push(this.responsible); } usedUsers = usedUsers.concat([...this.selectedUsers]); var formData = { name: this.name, need_for_recruitment: this.need_for_recruitment, description: this.description, options: this.optionsSelected, users: usedUsers, admins: this.selectedAdmins, }; if (this.editMode) { this.updatePosition(formData); } else { this.storePosition(formData); } }, storePosition(formData) { axios .post(`/positions`, formData) .then(({ data }) => { this.v$.$reset(); // Reset form this.name = ""; this.description = ""; this.need_for_recruitment = ""; this.responsible = ""; this.optionsSelected = []; this.selectedUsers = []; this.selectedAdmins = []; this.keyInputs++; this.realTimeNotifications.addNotification({ type: "success", title: "Adăugat cu success.", description: "Funcția a fost salvata cu success.", }); window.scrollTo({ top: 0, behavior: "smooth" }); }) .catch((error) => { var responseError = error.response.data; this.realTimeNotifications.addNotification({ type: "error", title: "TBF ERROR", description: `"${responseError.message}". 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.loadingSubmit = false; }); }, updatePosition(formData) { axios .patch(`/instances/${this.$auth.user().instance.id}/positions/${this.$route.params.id}`, formData) .then(({ data }) => { this.v$.$reset(); var positionData = data.data; this.responsible = positionData.users.find((el) => el.is_responsible); this.selectedUsers = positionData.users.filter((el) => !el.is_responsible); this.selectedAdmins = positionData.admins; this.realTimeNotifications.addNotification({ type: "success", title: "Actualizat cu success.", description: "Funcția a fost actualizata cu success.", }); }) .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.loadingSubmit = false; }); }, checkDisabledGenerateDesc() { if (!this.name) { return true; } var stringDesc = this.description.replace(/ /g, ""); if (stringDesc.length < 30) { return true; } else { return false; } }, generateDescWithAi() { if (this.name && this.description) { this.loadedGenerateAi = true; axios .post(`/generate/description`, { name: this.name, description: this.description }) .then(({ data }) => { this.description = data.message; }) .finally(() => { this.keyInputs++; this.loadedGenerateAi = false; }); } }, checkCanClose() { this.$router.push({ name: "positions" }); }, openTutorial() { var arrayPages = this.cookies.get("modal_vimeo") ? JSON.parse(this.cookies.get("modal_vimeo")) : []; if (!arrayPages.includes("position_create")) { arrayPages.push("position_create"); this.cookies.set("modal_vimeo", JSON.stringify(arrayPages), "30d"); } this.tutorialPopover = false; this.realTimeModals.toggleShowModal("modal_video_vimeo", { videoId: "870588271" }); }, clearResponsible() { this.selectedResponsible = ""; this.responsible_name = ""; this.responsible = ""; }, setCurrentOptionResponsible(event) { if (this.optionsUsers.find((el) => el.email.toLowerCase() == this.querySearchResponsible.toLowerCase())) { this.selectedResponsible = this.optionsUsers.find((el) => el.email.toLowerCase() == this.querySearchResponsible.toLowerCase()); } else { this.selectedResponsible = { first_name: this.responsible_name ? this.responsible_name : "", email: this.querySearchResponsible.toLowerCase() }; } }, setCurrentOptionUser(event) { if (this.optionsUsers.find((el) => el.email.toLowerCase() == this.queryUser.toLowerCase())) { this.selectedUser = this.optionsUsers.find((el) => el.email.toLowerCase() == this.queryUser.toLowerCase()); } else { this.selectedUser = { first_name: this.user_name ? this.user_name : "", email: this.queryUser.toLowerCase() }; } }, setCurrentOptionAdmin(event) { if (this.optionsUsers.find((el) => el.email.toLowerCase() == this.querySearchAdmin.toLowerCase())) { this.selectedAdmin = this.optionsUsers.find((el) => el.email.toLowerCase() == this.querySearchAdmin.toLowerCase()); } else { this.selectedAdmin = { first_name: this.admin_name ? this.admin_name : "", email: this.querySearchAdmin.toLowerCase() }; } }, async checkEmail(email) { var isValid = false; await axios.post(`/users/check/email`, { email: email }).then(({ data }) => { if (data.user) { isValid = false; } else { isValid = true; } }); return isValid; }, }, }; </script>
Back