mirror of
https://github.com/ArthurDanjou/website-old.git
synced 2026-01-23 16:30:42 +01:00
Working and clean fucking guestbook
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
</h3>
|
||||
<div v-if="experiences" v-for="experience in experiences">
|
||||
<Experience
|
||||
:title="experience.title"
|
||||
:title="experience.title.code"
|
||||
:company="experience.company"
|
||||
:location="experience.location"
|
||||
:begin="experience.begin_date"
|
||||
@@ -22,16 +22,21 @@ import {Experience} from "~/types/types";
|
||||
export default defineComponent({
|
||||
name: "ExperiencesAbout",
|
||||
setup() {
|
||||
const {$content, $sentry} = useContext()
|
||||
const {$axios, $sentry, app} = useContext()
|
||||
|
||||
const experiences = useAsync(() => {
|
||||
return $content('experiences')
|
||||
.sortBy('end_date', 'desc')
|
||||
.fetch<Experience>()
|
||||
.catch((error) => {
|
||||
$sentry.captureEvent(error)
|
||||
})
|
||||
})
|
||||
const experiences = useAsync(async () => {
|
||||
const response = await $axios.get('/api/experiences', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
return response.data.experiences
|
||||
} else {
|
||||
app.error({statusCode: 500})
|
||||
$sentry.captureEvent(response.data)
|
||||
}
|
||||
}, 'experiences')
|
||||
|
||||
return {
|
||||
experiences
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<footer class="footer w-full mt-20">
|
||||
<div class="p-8 pb-0">
|
||||
<div class="lg:flex justify-evenly">
|
||||
<div class="lg:flex justify-evenly items-center">
|
||||
<div class="lg:w-1/3">
|
||||
<div class="flex">
|
||||
<Logo />
|
||||
@@ -41,7 +41,8 @@
|
||||
<h1 class="font-bold mb-4 text-lg underline">
|
||||
{{ $t('footer.links') }}
|
||||
</h1>
|
||||
<div class="flex flex-col mb-8 space-y-2 lg:space-y-4 font-medium">
|
||||
<div class="flex flex-col lg:flex-row mb-8 font-medium">
|
||||
<div class="space-y-2 lg:space-y-4 mr-16">
|
||||
<div class="link">
|
||||
<nuxt-link to="/" :class="{'link-active': isWindow('')}">
|
||||
{{ $t('header.home') }}
|
||||
@@ -62,6 +63,8 @@
|
||||
{{ $t('header.projects') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2 lg:space-y-4 ">
|
||||
<div class="link">
|
||||
<nuxt-link to="/services" :class="{'link-active': isWindow('services')}">
|
||||
{{ $t('header.services') }}
|
||||
@@ -72,11 +75,6 @@
|
||||
{{ $t('header.env') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/guestbook" :class="{'link-active': isWindow('guestbook')}">
|
||||
{{ $t('header.guestbook') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/newsletter" :class="{'link-active': isWindow('newsletter')}">
|
||||
{{ $t('header.newsletter') }}
|
||||
@@ -88,6 +86,7 @@
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center border-t-2 border-gray-200 dark:border-gray-800 py-8 lg:flex lg:flex-row-reverse justify-between">
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</h3>
|
||||
<div v-if="formations" v-for="formation in formations">
|
||||
<Formation
|
||||
:title="formation.title"
|
||||
:description="formation.description"
|
||||
:title="formation.title.code"
|
||||
:description="formation.description.code"
|
||||
:location="formation.location"
|
||||
:begin="formation.begin_date"
|
||||
:end="formation.end_date" />
|
||||
@@ -22,15 +22,20 @@ import {Formation} from "~/types/types";
|
||||
export default defineComponent({
|
||||
name: "FormationsHome",
|
||||
setup() {
|
||||
const {$content, $sentry} = useContext()
|
||||
const {$axios, $sentry, app} = useContext()
|
||||
|
||||
const formations = useAsync(() => {
|
||||
return $content('formations')
|
||||
.sortBy('end_date', 'desc')
|
||||
.fetch<Formation>()
|
||||
.catch((error) => {
|
||||
$sentry.captureEvent(error)
|
||||
})
|
||||
const formations = useAsync(async () => {
|
||||
const response = await $axios.get('/api/formations', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
return response.data.formations
|
||||
} else {
|
||||
app.error({statusCode: 500})
|
||||
$sentry.captureEvent(response.data)
|
||||
}
|
||||
}, 'formations')
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
<template>
|
||||
<section class="p-6 border border-indigo-600 dark:border-indigo-700 rounded-lg text-justify">
|
||||
<h1 class="text-black font-bold dark:text-white text-2xl">{{ $t('guestbook.signin') }}</h1>
|
||||
<h3 class="text-gray-500 dark:text-gray-400">{{ $t('guestbook.share') }}</h3>
|
||||
<div v-if="!loginRef" class="flex space-x-4 my-3">
|
||||
<div @click="login('google')" class="icon-parent flex justify-center items-center p-2 border border-black dark:border-white duration-300 cursor-pointer">
|
||||
<GoogleIcon />
|
||||
</div>
|
||||
<div @click="login('github')" class="icon-parent flex justify-center items-center p-2 border border-black dark:border-white duration-300 cursor-pointer">
|
||||
<GithubIcon />
|
||||
</div>
|
||||
<div @click="login('twitter')" class="icon-parent flex justify-center items-center p-2 border border-black dark:border-white duration-300 cursor-pointer">
|
||||
<TwitterIcon />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="my-3">
|
||||
<form class="relative">
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
:placeholder="$t('guestbook.placeholder')"
|
||||
v-model="form.message"
|
||||
class="pl-4 pr-32 py-2 mt-1 block w-full border-gray-300 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100"
|
||||
>
|
||||
<button
|
||||
@click.prevent="handleForm"
|
||||
v-if="form.message && form.message.length > 0"
|
||||
class="button flex items-center justify-center px-8 py-1 font-bold bg-gray-100 dark:bg-gray-700 hover:dark:bg-gray-800 text-gray-900 dark:text-gray-100 rounded hover:bg-gray-300 duration-300"
|
||||
>
|
||||
{{ $t('guestbook.sign') }}
|
||||
</button>
|
||||
</form>
|
||||
<div class="flex">
|
||||
<div v-if="error" class="py-1 text-red-400 text-sm">
|
||||
{{ $t('guestbook.error') }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex">
|
||||
<div v-if="success" class="py-1 text-green-400 text-sm">
|
||||
{{ $t('guestbook.success') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-gray-400 dark:text-gray-600">{{ $t('guestbook.infos') }}</p>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, ref, useContext} from "@nuxtjs/composition-api";
|
||||
import {GuestbookForm} from "~/types/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "GuestBookForm",
|
||||
setup() {
|
||||
const { $axios, $sentry, app } = useContext()
|
||||
|
||||
const loginRef = ref(false)
|
||||
const login = async (driver: 'github' | 'google' | 'twitter') => {
|
||||
const response = await $axios.get(`/api/auth/${driver}`, {
|
||||
headers: {
|
||||
'Accept': '*/*',
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
loginRef.value = true
|
||||
await hasAlreadySignMessage(response.data.user.id)
|
||||
} else {
|
||||
$sentry.captureEvent(response.data)
|
||||
app.error({statusCode: 500})
|
||||
}
|
||||
}
|
||||
|
||||
const error = ref(false)
|
||||
const success = ref(false)
|
||||
const form = ref<GuestbookForm>({} as GuestbookForm)
|
||||
|
||||
const handleForm = async () => {
|
||||
const response = await $axios.post('/api/guestbook', {
|
||||
message: form.value.message,
|
||||
email: 'contact@arthurdanjou.fr'
|
||||
}, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
form.value.message = ''
|
||||
success.value = true
|
||||
} else {
|
||||
$sentry.captureEvent(response.data)
|
||||
error.value = true
|
||||
setTimeout(() => {
|
||||
error.value = false
|
||||
}, 5000)
|
||||
}
|
||||
}
|
||||
|
||||
const alreadySent = ref(false)
|
||||
const hasAlreadySignMessage = async (id: number) => {
|
||||
const response = await $axios.get(`/api/guestbook/${id}`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
switch (response.data.signed) {
|
||||
case 0:
|
||||
alreadySent.value = false
|
||||
break
|
||||
case 1:
|
||||
alreadySent.value = true
|
||||
break
|
||||
}
|
||||
} else {
|
||||
$sentry.captureEvent(response.data)
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
login,
|
||||
form,
|
||||
success,
|
||||
error,
|
||||
alreadySent,
|
||||
loginRef,
|
||||
handleForm,
|
||||
hasAlreadySignMessage
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.icon-parent svg {
|
||||
@apply duration-300;
|
||||
}
|
||||
.icon-parent:hover svg {
|
||||
@apply transform scale-110;
|
||||
}
|
||||
.button {
|
||||
position: absolute;
|
||||
top: 0.25rem;
|
||||
right: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
<template>
|
||||
<div class="my-6">
|
||||
<div class="text-justify leading-6 text-black dark:text-white">
|
||||
{{ message }}
|
||||
</div>
|
||||
<div class="flex mt-3">
|
||||
<div class="text-gray-600 dark:text-gray-300">
|
||||
{{ author }}
|
||||
</div>
|
||||
<div class="text-gray-200 px-3 dark:text-gray-700">/</div>
|
||||
<div class="text-gray-400 dark:text-gray-500 lining-nums">
|
||||
{{ formatDateAndTime }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, defineComponent, useContext} from "@nuxtjs/composition-api";
|
||||
|
||||
interface GuestBookMessageProps {
|
||||
author: string,
|
||||
message: string,
|
||||
date: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: "GuestbookMessage",
|
||||
props: {
|
||||
message: {
|
||||
type: String,
|
||||
default: 'Guestbook Message'
|
||||
},
|
||||
author: {
|
||||
type: String,
|
||||
default: "Guestbook Author"
|
||||
},
|
||||
date: {
|
||||
type: String,
|
||||
default: "Guestbook Date"
|
||||
}
|
||||
},
|
||||
setup(props: GuestBookMessageProps) {
|
||||
const {i18n} = useContext()
|
||||
|
||||
const formatDateAndTime = computed(() => {
|
||||
const date = new Date(props.date)
|
||||
const realMonth = date.getMonth()+1
|
||||
const month = realMonth.toString().length == 2 ? realMonth.toString() : `0${realMonth.toString()}`
|
||||
const minutes = date.getMinutes().toString().length == 2 ? date.getMinutes() : `0${date.getMinutes()}`
|
||||
const hours = date.getHours().toString().length == 2 ? date.getHours() : `0${date.getHours()}`
|
||||
return `${date.getDate()} ${i18n.t(`month.${month}`)} ${date.getFullYear()} ${i18n.t('guestbook.at')} ${hours}:${minutes}`
|
||||
})
|
||||
|
||||
return {
|
||||
formatDateAndTime
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -33,7 +33,7 @@ const PAGE_TYPE = {
|
||||
services: 2,
|
||||
env: 3,
|
||||
about: 4,
|
||||
guestbook: 5
|
||||
newsletter: 5
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
|
||||
@@ -65,11 +65,6 @@
|
||||
{{ $t('header.env') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('guestbook') }">
|
||||
<nuxt-link to="/guestbook">
|
||||
{{ $t('header.guestbook') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('newsletter') }">
|
||||
<nuxt-link to="/newsletter">
|
||||
{{ $t('header.newsletter') }}
|
||||
|
||||
Reference in New Issue
Block a user