- add cv management

This commit is contained in:
2023-07-29 00:34:32 +02:00
parent f32f13604f
commit 1ccc934561
15 changed files with 741 additions and 4 deletions

View File

@@ -0,0 +1,37 @@
<script setup>
import { router } from '@inertiajs/vue3';
const props = defineProps({
cv: {
type: Object,
required: true,
},
});
function confirmDelete() {
router.delete(`/dashboard/cv/${props.cv.token}/delete`);
}
</script>
<template>
<InertiaHead title="Usuwanie CV" />
<div class="p-4">
<header class="pb-4">
<h1 class="text-3xl font-roboto font-light">Usuwanie CV</h1>
</header>
<div class="max-w-[600px]">
<p class="mb-4">Na pewno usunąć CV dla firmy {{ cv.recipient }}?</p>
<div class="grid grid-cols-3 gap-2">
<InertiaLink
as="button"
href="/dashboard/cv"
class="col-span-1 flex justify-center items-center gap-3 w-full px-2 py-1 border-t-4 border-b-4 border-transparent hover:border-b-black"
><FontAwesomeIcon :icon="['fas', 'backward']" />Anuluj</InertiaLink>
<button
@click.prevent="confirmDelete"
class="col-span-2 flex justify-center items-center gap-3 w-full px-2 py-1 rounded-md bg-red-600 border-4 border-red-600 text-white text-lg hover:bg-transparent hover:text-red-600"
><FontAwesomeIcon :icon="['fas', 'trash']" /><span class="whitespace-nowrap overflow-hidden overflow-ellipsis">Usuń CV dla firmy {{ cv.recipient }}</span></button>
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,113 @@
<script setup>
import { computed, ref } from 'vue';
import { useForm } from '@inertiajs/inertia-vue3';
import Input from '../../Share/Components/Input.vue';
const locations = ref([]);
const locationsToString = computed({
get: () => locations.value.join(', '),
set: (val) => {
val = val.replace(', ', ',').replace(' , ', ',').replace(' ,', ',');
val = val.split(',');
val.forEach((element, key) => {
val[key] = element.trim();
});
locations.value = val;
}
});
const mission = ref([]);
const missionToString = computed({
get: () => mission.value.join("\n"),
set: (value) => {
mission.value = value.split("\n");
}
});
const form = useForm({
recipient: null,
email: null,
phone_number: null,
locations: locations,
mission: missionToString,
rodo: null,
position: null,
});
function createCV() {
form.post('/dashboard/cv');
}
</script>
<template>
<InertiaHead title="Nowe dane do CV" />
<div class="p-4">
<header class="pb-4">
<div class="flex items-center gap-2">
<InertiaLink
as="button"
href="/dashboard/cv"
class="px-2 text-xl text-gray-700 hover:text-black"
title="Wróć do listy CV"><FontAwesomeIcon :icon="['fas', 'caret-left']" /></InertiaLink>
<h1 class="text-3xl font-roboto font-light">Nowe dane do CV</h1>
</div>
</header>
<div>
<form class="flex flex-col gap-4" @submit.prevent="createCV">
<Input
id="recipient"
label="Firma gdzie jest składane CV"
placeholder="Oki doki sp. z.o.o"
v-model="form.recipient"
:error="form.errors.recipient"
/>
<Input
id="email"
type="email"
label="E-mail"
placeholder="Adres e-mail wyświetlany na CV"
v-model="form.email"
:error="form.errors.email"
/>
<Input
id="phone"
label="Numer telefonu"
placeholder="+48 123 456 789"
v-model="form.phone_number"
:error="form.errors.phone_number"
/>
<Input
id="locations"
label="Lokalizacje"
placeholder="Miejsca pracy."
v-model="locationsToString"
:error="form.errors.locations"
/>
<Input
id="position"
label="Stanowisko"
placeholder="Stanowisko na jakie jest rekrutacja."
v-model="form.position"
:error="form.errors.position"
/>
<Input
id="mission"
type="textarea"
label="Misja - wstęp"
placeholder="Krótki opis, list motywacyjny."
v-model="form.mission"
:error="form.errors.mission"
/>
<Input
id="rodo"
type="textarea"
label="RODO"
placeholder="Klauzula informacyjna RODO"
v-model="form.rodo"
:error="form.errors.rodo"
/>
<button class="px-0.5 py-1 rounded-lg bg-[#436da7] border-4 border-[#436da7] text-white text-lg hover:bg-transparent hover:text-[#436da7]">Utwórz nowe CV</button>
</form>
</div>
</div>
</template>

View File

@@ -0,0 +1,126 @@
<script setup>
import { computed, ref } from 'vue';
import { useForm } from '@inertiajs/inertia-vue3';
import Input from '../../Share/Components/Input.vue';
const props = defineProps({
cv: {
type: Object,
required: true,
}
});
const locations = ref(props.cv.locations);
const locationsToString = computed({
get: () => locations.value.join(', '),
set: (val) => {
val = val.replace(', ', ',').replace(' , ', ',').replace(' ,', ',');
val = val.split(',');
val.forEach((element, key) => {
val[key] = element.trim();
});
locations.value = val;
}
});
const mission = ref(props.cv.mission);
const missionToString = computed({
get: () => mission.value.join("\n"),
set: (value) => {
mission.value = value.split("\n");
}
});
const form = useForm({
recipient: props.cv.recipient,
email: props.cv.email,
phone_number: props.cv.phone.formattedNumber,
locations: locations,
mission: missionToString,
rodo: props.cv.rodo,
position: props.cv.position,
});
function updateCV() {
form.put(`/dashboard/cv/${props.cv.token}`);
}
</script>
<template>
<InertiaHead title="Edycja danych do CV" />
<div class="p-4">
<header class="flex items-center justify-between pb-4">
<div class="flex items-center gap-2">
<InertiaLink
as="button"
href="/dashboard/cv"
class="px-2 text-xl text-gray-700 hover:text-black"
title="Wróć do listy CV"><FontAwesomeIcon :icon="['fas', 'caret-left']" /></InertiaLink>
<h1 class="text-3xl font-roboto font-light">Edycja danych do CV</h1>
</div>
<InertiaLink
as="button"
:href="`/dashboard/cv/${cv.token}/delete`"
class="flex items-center gap-2 px-2 py-1 text-red-600 hover:text-white hover:bg-red-600 rounded-md"
title="Usuń CV"
><FontAwesomeIcon :icon="['fas', 'trash']" />Usuń</InertiaLink>
</header>
<div>
<form class="flex flex-col gap-4" @submit.prevent="updateCV">
<Input
id="recipient"
label="Firma gdzie jest składane CV"
placeholder="Oki doki sp. z.o.o"
v-model="form.recipient"
:error="form.errors.recipient"
/>
<Input
id="email"
type="email"
label="E-mail"
placeholder="Adres e-mail wyświetlany na CV"
v-model="form.email"
:error="form.errors.email"
/>
<Input
id="phone"
label="Numer telefonu"
placeholder="+48 123 456 789"
v-model="form.phone_number"
:error="form.errors.phone_number"
/>
<Input
id="locations"
label="Lokalizacje"
placeholder="Miejsca pracy."
v-model="locationsToString"
:error="form.errors.locations"
/>
<Input
id="position"
label="Stanowisko"
placeholder="Stanowisko na jakie jest rekrutacja."
v-model="form.position"
:error="form.errors.position"
/>
<Input
id="mission"
type="textarea"
label="Misja - wstęp"
placeholder="Krótki opis, list motywacyjny."
v-model="form.mission"
:error="form.errors.mission"
/>
<Input
id="rodo"
type="textarea"
label="RODO"
placeholder="Klauzula informacyjna RODO"
v-model="form.rodo"
:error="form.errors.rodo"
/>
<button class="px-0.5 py-1 rounded-lg bg-[#436da7] border-4 border-[#436da7] text-white text-lg hover:bg-transparent hover:text-[#436da7]">Edytuj CV</button>
</form>
</div>
</div>
</template>

View File

@@ -0,0 +1,96 @@
<script setup>
defineProps({
cvs: {
type: Object,
required: true,
}
});
function copySlug(slug) {
const input = document.createElement('input');
input.value = slug;
input.select();
input.setSelectionRange(0, 99999);
navigator
.clipboard
.writeText(input.value)
.then((value, reason) => { });
}
</script>
<template>
<InertiaHead title="Lista CV" />
<div class="p-4">
<header class="flex justify-between items-center pb-4">
<div class="flex items-center gap-2">
<InertiaLink
as="button"
href="/dashboard"
class="px-2 text-xl text-gray-700 hover:text-black"
title="Wróc do dashboard"><FontAwesomeIcon :icon="['fas', 'caret-left']" /></InertiaLink>
<h1 class="text-3xl font-roboto font-light">Lista CV</h1>
</div>
<InertiaLink
as="button"
href="/dashboard/cv/create"
class="bg-blue-400 hover:bg-blue-500 text-white px-2.5 py-1 rounded-full"
title="Dodaj nowe dane dla CV"
><FontAwesomeIcon :icon="['fas', 'plus']" /></InertiaLink>
</header>
<div class="overflow-x-auto">
<table v-if="cvs.data.length" class="w-full min-w-[600px] border-separate border-spacing-y-2">
<colgroup>
<col class="w-min" />
</colgroup>
<thead class="text-left bg-gray-100">
<th class="p-2 text-center">ID</th>
<th class="p-2">Token</th>
<th class="w-[100px] p-2 whitespace-nowrap">Firma</th>
<th class="hidden md:table-cell p-2">Lokalizacje</th>
<th class="p-2">Telefon</th>
<th class="p-2"></th>
</thead>
<tbody>
<InertiaLink
as="tr"
v-for="(cv, key) in cvs.data"
:key="key"
class="px-3 py-2 bg-white hover:bg-neutral-200 rounded-md z-10"
:href="`/dashboard/cv/${cv.token}`">
<td class="p-2 text-center">#{{ cv.id }}</td>
<td class="p-2"
><button
class="bg-gray-50 p-1 rounded-md hover:bg-gray-100"
@click.prevent="copySlug(cv.token)"
:title="cv.token">{{ cv.token.slice(0, 7) }}</button></td>
<td class="max-w-[150px] md:max-w-[250px] p-2 whitespace-nowrap overflow-hidden overflow-ellipsis" :title="cv.recipient">{{ cv.recipient }}</td>
<td class="hidden md:table-cell p-2">{{ cv.locations.join(' / ') }}</td>
<td class="p-2">{{ cv.phone.formattedNumber }}</td>
<td class="flex items-center justify-center gap-2 p-3 z-50">
<a
class="px-3 py-3 text-blue-600 hover:text-blue-800"
:href="`https://cv.kamilcraft.com/show/${cv.token}`"
target="_blank"
rel="noopener noreferrer"
title="Przekieruj do CV"><FontAwesomeIcon :icon="['fas', 'arrow-up-right-from-square']" /></a>
<InertiaLink
as="button"
class="px-3 py-3 text-lime-600 hover:text-lime-800 border-t-2 border-b-2 border-transparent hover:border-b-lime-600"
:href="`/dashboard/cv/${cv.token}/edit`"
title="Edytuj projekt"><FontAwesomeIcon :icon="['fas', 'pen-to-square']" /></InertiaLink>
<InertiaLink
as="button"
class="px-3 py-3 text-red-600 hover:text-red-800"
:href="`/dashboard/cv/${cv.token}/delete`"
title="Usuń projekt z listy"><FontAwesomeIcon :icon="['fas', 'trash']" /></InertiaLink>
</td>
</InertiaLink>
</tbody>
</table>
<div v-else>
Pusta lista
</div>
</div>
</div>
</template>

View File

@@ -0,0 +1,124 @@
<script setup>
defineProps({
cv: {
type: Object,
required: true,
},
cvInfo: {
type: Object,
required: true,
},
});
</script>
<template>
<InertiaHead title="Szczegóły CV" />
<div class="p-4">
<header class="flex justify-between items-center pb-4">
<div class="flex items-center gap-2">
<InertiaLink
as="button"
href="/dashboard/cv"
class="px-2 text-xl text-gray-700 hover:text-black"
title="Wróć do listy CV"><FontAwesomeIcon :icon="['fas', 'caret-left']" /></InertiaLink>
<h1 class="text-3xl font-roboto font-light">Szczegóły CV</h1>
</div>
<div class="flex gap-2">
<a
class="px-2 py-1 text-blue-600 hover:text-blue-800"
:href="`https://cv.kamilcraft.com/show/${cv.token}`"
target="_blank"
rel="noopener noreferrer"
title="Przekieruj do CV"><FontAwesomeIcon :icon="['fas', 'arrow-up-right-from-square']" /></a>
<InertiaLink
as="button"
:href="`/dashboard/cv/${cv.token}/edit`"
class="flex items-center gap-2 px-2 py-1 text-lime-600 hover:text-white hover:bg-lime-600 rounded-md"
title="Usuń CV"
><FontAwesomeIcon :icon="['fas', 'pen-to-square']" />Edytuj</InertiaLink>
<InertiaLink
as="button"
:href="`/dashboard/cv/${cv.token}/delete`"
class="flex items-center gap-2 px-2 py-1 text-red-600 hover:text-white hover:bg-red-600 rounded-md"
title="Usuń CV"
><FontAwesomeIcon :icon="['fas', 'trash']" />Usuń</InertiaLink>
</div>
</header>
<div class="mb-4">
<header>
<h2 class="text-2xl font-roboto font-light pb-3">Podstawowe informacje</h2>
</header>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<div class="text-gray-500 pb-0.5">Token</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white whitespace-nowrap overflow-hidden overflow-ellipsis">{{ cv.token }}</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">Firma</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.recipient }}</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">Stanowisko</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.position }}</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">E-mail</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.email }}</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">Numer telefonu</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.phone.formattedNumber }}</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">Lokalizacje</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.locations.join(' / ') }}</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">Zachowane wyświetlenia</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.views }}</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">Łączne wyświetlenia</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.registeredViews }}</p>
</div>
</div>
</div>
<div class="mb-4">
<header>
<h2 class="text-2xl font-roboto font-light pb-3">Opisy</h2>
</header>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<div class="text-gray-500 pb-0.5">Misja</div>
<div v-if="cv.mission.length > 0 && cv.mission[0] !== ''" class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">
<p
v-for="(mission, key) in cv.mission"
:key="key">{{ mission }}</p>
</div>
<p v-else class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">[Wartość domyślna]</p>
</div>
<div>
<div class="text-gray-500 pb-0.5">RODO</div>
<p class="w-full min-w-full max-w-full px-2.5 py-2 border-b-2 rounded-md bg-white">{{ cv.rodo ?? '[Wartość domyślna]' }}</p>
</div>
</div>
</div>
<div>
<header>
<h2 class="text-2xl font-roboto font-light pb-3">Lista wejść</h2>
</header>
<ul v-if="cvInfo.data.length" class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
<li
v-for="(info, key) in cvInfo.data"
:key="key"
class="flex flex-col px-3 py-2 bg-white rounded-md">
<span>#{{ info.id }} {{ info.ip }}</span>
<span class="text-xs">{{ info.created_at }}</span>
</li>
</ul>
<div v-else>
Brak wyświetleń
</div>
</div>
</div>
</template>

View File

@@ -26,8 +26,9 @@ defineProps({
>Kamil<span class="logo-green">Craft</span></div>
</InertiaLink>
<nav>
<ul class="flex gap-3 items-center">
<li></li>
<ul class="flex gap-3 items-center font-bold">
<li><InertiaLink class="text-white active:text-kamilcraft-green hover:text-black hover:underline" href="/dashboard">Dashboard</InertiaLink></li>
<li><InertiaLink class="text-white active:text-kamilcraft-green hover:text-black hover:underline" href="/dashboard/cv">CV</InertiaLink></li>
</ul>
</nav>
</div>