Merge branch 'dev'
# Conflicts: # package.json # yarn.lock
@@ -1 +1,2 @@
|
||||
node_modules
|
||||
build
|
||||
|
||||
0
.editorconfig
Executable file → Normal file
15
Dockerfile
@@ -1,18 +1,15 @@
|
||||
FROM node:15.8.0-alpine3.10
|
||||
FROM node:16-alpine3.11
|
||||
|
||||
RUN mkdir -p /usr/src/artsite
|
||||
WORKDIR /usr/src/artsite
|
||||
RUN mkdir -p /usr/src/ares
|
||||
|
||||
COPY . /usr/src/artsite
|
||||
WORKDIR /usr/src/ares
|
||||
|
||||
RUN yarn install
|
||||
COPY . /usr/src/ares
|
||||
|
||||
RUN yarn install --production
|
||||
|
||||
RUN yarn build
|
||||
|
||||
RUN cp .env build
|
||||
|
||||
EXPOSE 3333
|
||||
|
||||
COPY . .
|
||||
|
||||
CMD ["yarn", "start"]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ArtSite 🌍
|
||||
# Arès 🌍
|
||||
|
||||
This is my personnal website.
|
||||
Hosted version : [arthurdanjou.fr](https://arthurdanjou.fr)
|
||||
@@ -13,7 +13,7 @@ Hosted version : [arthurdanjou.fr](https://arthurdanjou.fr)
|
||||
- Automatic working state
|
||||
- Wonderfull Mobile-Header
|
||||
- Multi-Language site
|
||||
- Connected to ArtApi (my personal api)
|
||||
- Connected to [Athena](https://github.com/arthurdanjou/athena) (my personal api)
|
||||
|
||||
## Tech used ⚙
|
||||
|
||||
@@ -30,4 +30,4 @@ Hosted version : [arthurdanjou.fr](https://arthurdanjou.fr)
|
||||
|
||||
## License 📑
|
||||
Copyright © 2020 - [@ArthurDanj](https://arthurdanjou.fr) \
|
||||
This project is [MIT](https://github.com/ArthurDanjou/artsite/blob/master/License) Licensed.
|
||||
This project is [MIT](https://github.com/ArthurDanjou/ares/blob/master/License) Licensed.
|
||||
|
||||
@@ -6,6 +6,7 @@ import arch from './settings/Arch'
|
||||
import plugins from './settings/Plugins'
|
||||
import css from './settings/Style'
|
||||
import configs from './settings/RuntimeConfig'
|
||||
import {NuxtConfig} from "@nuxt/types";
|
||||
|
||||
const config = {
|
||||
head,
|
||||
@@ -16,6 +17,6 @@ const config = {
|
||||
css,
|
||||
buildModules,
|
||||
...configs,
|
||||
}
|
||||
} as NuxtConfig
|
||||
|
||||
export default config
|
||||
|
||||
27
package.json
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "artsite",
|
||||
"name": "ares",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -10,26 +10,27 @@
|
||||
"dependencies": {
|
||||
"@nuxt/content": "^1.14.0",
|
||||
"@nuxtjs/axios": "^5.13.6",
|
||||
"@nuxtjs/composition-api": "0.23.4",
|
||||
"@nuxtjs/composition-api": "^0.27.0",
|
||||
"@nuxtjs/dotenv": "^1.4.1",
|
||||
"@nuxtjs/i18n": "^7.0.2",
|
||||
"@nuxtjs/proxy": "^2.1.0",
|
||||
"@nuxtjs/redirect-module": "^0.3.1",
|
||||
"@nuxtjs/robots": "^2.5.0",
|
||||
"@nuxtjs/sentry": "^5.1.0",
|
||||
"@nuxtjs/sentry": "^5.1.2",
|
||||
"@nuxtjs/sitemap": "^2.4.0",
|
||||
"@nuxtjs/universal-storage": "^0.5.9",
|
||||
"core-js": "^3.14.0",
|
||||
"nuxt": "^2.15.7",
|
||||
"nuxt-i18n": "^6.27.1",
|
||||
"prism-themes": "^1.7.0",
|
||||
"sass": "^1.35.2",
|
||||
"windicss": "^3.1.3"
|
||||
"axios": "^0.21.1",
|
||||
"core-js": "^3.16.2",
|
||||
"nuxt": "^2.15.8",
|
||||
"prism-themes": "^1.8.0",
|
||||
"sass": "^1.38.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@nuxt/types": "^2.15.7",
|
||||
"@nuxt/types": "^2.15.8",
|
||||
"@nuxt/typescript-build": "^2.1.0",
|
||||
"@nuxtjs/color-mode": "^2.0.10",
|
||||
"markdown-it-prism": "^2.1.6",
|
||||
"nuxt-windicss": "1.0.3",
|
||||
"@nuxtjs/color-mode": "^2.1.1",
|
||||
"markdown-it-prism": "^2.1.8",
|
||||
"nuxt-windicss": "^1.2.3",
|
||||
"sass-loader": "10.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,4 +34,15 @@ const buildDir = 'build'
|
||||
|
||||
const ssr = true
|
||||
|
||||
export default { srcDir, dir, build, pageTransition, target, server, buildDir, components, ssr }
|
||||
const proxy = {
|
||||
'/api': {
|
||||
target: 'https://athena.arthurdanjou.fr',
|
||||
pathRewrite: { "^/api": "" }
|
||||
}
|
||||
}
|
||||
|
||||
const router = {
|
||||
middleware: 'maintenance'
|
||||
}
|
||||
|
||||
export default { router, srcDir, dir, build, pageTransition, target, server, buildDir, components, ssr, proxy }
|
||||
|
||||
@@ -1,6 +1,15 @@
|
||||
// Build Configuration: https://go.nuxtjs.dev/config-build
|
||||
export default {
|
||||
babel: {
|
||||
plugins: [['@babel/plugin-proposal-private-methods', { loose: true }]],
|
||||
plugins: [
|
||||
['@babel/plugin-proposal-private-methods', { loose: true }]
|
||||
],
|
||||
},
|
||||
postcss: {
|
||||
preset: {
|
||||
features: {
|
||||
"focus-within-pseudo-class": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {NuxtOptionsHead} from "@nuxt/types/config/head";
|
||||
|
||||
const params = {
|
||||
title: 'artsite',
|
||||
title: 'arthurdanjou.fr - Web & Software Developer',
|
||||
color: '#00a0ff',
|
||||
image: '/images/memojies/Hey.png',
|
||||
url: 'https://arthurdanjou.fr',
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import {NuxtOptionsModule} from "@nuxt/types/config/module";
|
||||
|
||||
const axios = {
|
||||
proxy: true,
|
||||
credentials: true,
|
||||
baseURL: 'https://api.arthurdanjou.fr'
|
||||
proxyHeaders: true
|
||||
}
|
||||
|
||||
const i18n = {
|
||||
@@ -31,7 +32,6 @@ const i18n = {
|
||||
}
|
||||
|
||||
const content = {
|
||||
apiPrefix: 'api',
|
||||
nestedProperties: ['skills.slug'],
|
||||
markdown: {
|
||||
prism: {
|
||||
@@ -49,7 +49,7 @@ const content = {
|
||||
|
||||
const storage = {
|
||||
cookie: {
|
||||
prefix: 'artsite/',
|
||||
prefix: 'arthurdanjou.fr/',
|
||||
options: {
|
||||
path: '/'
|
||||
}
|
||||
@@ -71,9 +71,7 @@ const robots = {
|
||||
}
|
||||
|
||||
const redirect = [
|
||||
{ from: '/source', to: 'https://github.com/arthurdanjou/artsite' },
|
||||
{ from: '/twitter', to: 'https://twitter.com/arthurdanj' },
|
||||
{ from: '/github', to: 'https://github.com/arthurdanjou/' },
|
||||
{ from: '/source', to: 'https://github.com/arthurdanjou/ares' },
|
||||
{ from: '/shelf', to: '/blog' },
|
||||
{ from: '/posts', to: '/blog' },
|
||||
{ from: '/resume', to: '/cv' }
|
||||
@@ -84,15 +82,12 @@ const env = {
|
||||
}
|
||||
|
||||
const sentry = {
|
||||
dsn: process.env.SENTRY_DSN,
|
||||
config: {
|
||||
|
||||
}
|
||||
dsn: process.env.SENTRY_DSN
|
||||
}
|
||||
|
||||
export default [
|
||||
['@nuxtjs/axios', axios],
|
||||
['nuxt-i18n', i18n],
|
||||
['@nuxtjs/i18n', i18n],
|
||||
['@nuxt/content', content],
|
||||
['@nuxtjs/universal-storage', storage],
|
||||
['@nuxtjs/robots', robots],
|
||||
|
||||
@@ -2,8 +2,5 @@
|
||||
export default [
|
||||
{
|
||||
src: '~/plugins/i18n.ts'
|
||||
},
|
||||
{
|
||||
src: '~/plugins/main.ts',
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
const publicRuntimeConfig = {
|
||||
|
||||
}
|
||||
|
||||
const privateRuntimeConfig = {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@500&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto:wght@900&display=swap');
|
||||
|
||||
.icon {
|
||||
transform: translate(3px, -3px);
|
||||
|
||||
|
Before Width: | Height: | Size: 21 KiB |
BIN
src/assets/images/maintenance.png
Normal file
|
After Width: | Height: | Size: 340 KiB |
BIN
src/assets/images/photo-rounded.png
Normal file
|
After Width: | Height: | Size: 125 KiB |
|
Before Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 457 KiB |
|
Before Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 2.8 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.3 KiB |
49
src/components/About.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<section v-if="age" class="w-full flex items-center justify-center my-12">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="mb-12 md:mb-0 w-full md:w-1/2 flex justify-center">
|
||||
<img src="@/assets/images/memojies/Hat.png" alt="It's me !" class="xl:w-1/2" />
|
||||
</div>
|
||||
<div class="md:w-1/2 text-justify">
|
||||
<h2 class="text-4xl font-bold text-center md:text-justify">
|
||||
{{ $t('home.about.title') }}
|
||||
</h2>
|
||||
<p class="text-xl my-6 text-gray-700 dark:text-gray-400">
|
||||
{{ $t('home.about.description', {age: age}) }}
|
||||
</p>
|
||||
<div class="flex justify-center md:justify-start">
|
||||
<Button content="home.about.about" link="about" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
name: "About",
|
||||
setup() {
|
||||
const {$axios, $sentry, app} = useContext()
|
||||
|
||||
const age = useAsync(async () => {
|
||||
const response = await $axios.get('/api/informations', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
return response.data.informations.age
|
||||
} else {
|
||||
$sentry.captureEvent(response.data)
|
||||
app.error({statusCode: 500})
|
||||
}
|
||||
}, 'infos')
|
||||
|
||||
return {
|
||||
age
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,43 +0,0 @@
|
||||
<template>
|
||||
<section v-if="info && info.age" class="w-full flex items-center justify-center my-12">
|
||||
<div class="flex flex-col md:flex-row justify-between items-center">
|
||||
<div class="mb-12 md:mb-0 w-full md:w-1/2 flex justify-center">
|
||||
<img src="~/assets/images/memojies/Hat.png" alt="It's me !" class="xl:w-1/2" />
|
||||
</div>
|
||||
<div class="md:w-1/2 text-justify">
|
||||
<h2 class="text-4xl font-bold text-center md:text-justify">
|
||||
{{ $t('home.about.title') }}
|
||||
</h2>
|
||||
<p class="text-xl my-6 text-gray-700 dark:text-gray-400">
|
||||
{{ $t('home.about.description', {age: info.age}) }}
|
||||
</p>
|
||||
<div class="flex justify-center md:justify-start">
|
||||
<Button content="home.about.about" link="about"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
|
||||
import {InfoData} from "~/types/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "AboutHome",
|
||||
setup() {
|
||||
const {$content, $sentry} = useContext()
|
||||
const info = useAsync(() => {
|
||||
return $content('infos')
|
||||
.fetch<InfoData>()
|
||||
.catch((error) => {
|
||||
$sentry.captureEvent(error)
|
||||
});
|
||||
})
|
||||
|
||||
return {
|
||||
info
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -37,6 +37,6 @@
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: "AdPreview"
|
||||
name: "AdHome"
|
||||
}
|
||||
</script>
|
||||
|
||||
64
src/components/Announce.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="announce"
|
||||
class="p-4 duration-300 flex justify-center items-center text-center"
|
||||
:class="[getHoverColor, getBackgroundColor]"
|
||||
>
|
||||
{{ $t(announce.message.code) }}
|
||||
<img
|
||||
v-if="announce.file"
|
||||
:src="`https://athena.arthurdanjou.fr/files/${announce.file}`"
|
||||
alt="Announce Cover File"
|
||||
>
|
||||
</div>
|
||||
<div v-else class="p-4 duration-300 flex justify-center items-center text-center bg-black dark:bg-white text-white dark:text-black">
|
||||
{{ $t('loading') }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Announce",
|
||||
setup() {
|
||||
const {$axios, $sentry, app} = useContext()
|
||||
|
||||
const announce = useAsync(async () => {
|
||||
const response = await $axios.get('/api/announce', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
return response.data.announce
|
||||
} else {
|
||||
$sentry.captureEvent(response.data)
|
||||
app.error({statusCode: 500})
|
||||
}
|
||||
}, 'announce')
|
||||
|
||||
const getBackgroundColor = computed(() => {
|
||||
switch (announce.value.color) {
|
||||
case 'black': {
|
||||
return 'bg-black text-white dark:(bg-white text-black)'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const getHoverColor = computed(() => {
|
||||
switch (announce.value.hover_color) {
|
||||
case 'gray': {
|
||||
return 'hover:bg-gray-800 dark:hover:bg-gray-300'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
announce,
|
||||
getBackgroundColor,
|
||||
getHoverColor,
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<section class="my-16 md:my-32 w-full flex items-center justify-center">
|
||||
<section class="my-16 w-full flex items-center justify-center">
|
||||
<div class="text-center flex flex-col items-center">
|
||||
<h1 class="text-6xl font-bold text-gray-700 dark:text-gray-400">
|
||||
{{ $t('home.banner.hello') }} <span class="text-black dark:text-white">Arthur Danjou</span> 👋,
|
||||
@@ -8,26 +8,66 @@
|
||||
<strong>{{ $t('home.banner.role') }}</strong>. <br />
|
||||
{{ $t('home.banner.student.main') }} <strong>{{ $t('home.banner.student.strong') }}</strong>.
|
||||
</h2>
|
||||
<p class="mt-4 text-lg text-gray-800 mb-10 dark:text-gray-300">{{ $t('home.banner.contact.follow') }}
|
||||
<a href="https://twitter.com/arthurdanj" target="_blank" rel="noreferrer noopener" class="link">Twitter</a>,
|
||||
<a href="https://twitch.tv/arthurdanjou" target="_blank" rel="noreferrer noopener" class="link">Twitch</a>
|
||||
{{ $t('home.banner.contact.and') }}
|
||||
<a href="https://github.com/arthurdanjou" target="_blank" rel="noreferrer noopener" class="link">Github</a>
|
||||
{{ $t('home.banner.contact.spacer') }}
|
||||
<nuxt-link to="contact" class="link">{{ $t('home.banner.contact.contact') }}</nuxt-link> {{ $t('home.banner.contact.me') }}
|
||||
</p>
|
||||
<div class="select-none mt-4 text-5xl text-gray-800 mb-10 dark:text-gray-300">
|
||||
<span id="develop" class="color blue">{{ $t('home.banner.color.develop') }}</span>
|
||||
<span id="publish" class="color">{{ $t('home.banner.color.publish') }}</span>
|
||||
<span id="improve" class="color">{{ $t('home.banner.color.improve') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: "Banner"
|
||||
import {defineComponent, onMounted, onUnmounted} from "@nuxtjs/composition-api";
|
||||
|
||||
interface Color {
|
||||
name: string,
|
||||
color: string
|
||||
}
|
||||
|
||||
const COLORS: Color[] = [
|
||||
{ name: 'develop', color: 'blue' },
|
||||
{ name: 'publish', color: 'red' },
|
||||
{ name: 'improve', color: 'green' }
|
||||
]
|
||||
|
||||
export default defineComponent({
|
||||
name: "Banner",
|
||||
setup() {
|
||||
let task
|
||||
|
||||
onMounted(() => {
|
||||
let CURRENT = 0
|
||||
task = setInterval(() => {
|
||||
const color = COLORS[CURRENT]
|
||||
const new_color = COLORS[CURRENT === COLORS.length -1 ? 0 : CURRENT + 1]
|
||||
document.getElementById(new_color.name)!.classList.toggle(new_color.color)
|
||||
setTimeout(() => document.getElementById(color.name)!.classList.toggle(color.color), 0)
|
||||
CURRENT === COLORS.length - 1 ? CURRENT = 0 : CURRENT++
|
||||
}, 2000)
|
||||
})
|
||||
|
||||
onUnmounted(() => clearInterval(task))
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.link {
|
||||
@apply font-medium cursor-pointer border-b-2 border-gray-200 text-indigo-600 hover:border-indigo-600 duration-200 dark:(font-white border-gray-700) hover:dark:border-indigo-600
|
||||
}
|
||||
|
||||
.color {
|
||||
@apply duration-500 text-white dark:text-black font-color;
|
||||
|
||||
&.red {
|
||||
@apply select-text bg-clip-text text-transparent bg-gradient-to-r from-red-600 to-rose-300
|
||||
}
|
||||
&.blue {
|
||||
@apply select-text bg-clip-text text-transparent bg-gradient-to-r from-blue-600 to-cyan-400
|
||||
}
|
||||
&.green {
|
||||
@apply select-text bg-clip-text text-transparent bg-gradient-to-r from-emerald-600 to-lime-500
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<section class="w-full flex items-center justify-center flex-col">
|
||||
<h2 class="text-center mb-10 font-bold text-gray-700 text-2xl md:text-3xl my-4 dark:text-gray-400">
|
||||
<section class="w-full flex justify-center flex-col">
|
||||
<h2 class="mb-10 font-bold text-gray-700 text-2xl md:text-3xl my-4 dark:text-gray-400">
|
||||
{{ $t('contact.form.title.main') }} <br class="lg:hidden"/><a class="email text-black dark:text-white" href="mailto:me@arthurdanjou.fr" target="_blank">{{ $t('contact.form.title.email') }}</a> 📬
|
||||
</h2>
|
||||
<form class="w-full lg:w-1/2">
|
||||
<form class="w-full">
|
||||
<div class="w-full lg:flex justify-center mb-8 lg:mb-12">
|
||||
<div class="form-div lg:w-1/2 mb-8 lg:mb-0 lg:mr-4">
|
||||
<input
|
||||
@@ -11,7 +11,6 @@
|
||||
v-model="form.name"
|
||||
required
|
||||
type="text"
|
||||
placeholder=" "
|
||||
class="first-input w-full"
|
||||
/>
|
||||
<label for="name" class="form-label">{{ $t('contact.form.name') }}</label>
|
||||
@@ -22,7 +21,6 @@
|
||||
v-model="form.email"
|
||||
required
|
||||
type="email"
|
||||
placeholder=" "
|
||||
class="second-input w-full"
|
||||
/>
|
||||
<label for="email" class="form-label">{{ $t('contact.form.email') }}</label>
|
||||
@@ -34,7 +32,6 @@
|
||||
v-model="form.subject"
|
||||
required
|
||||
type="text"
|
||||
placeholder=" "
|
||||
class="form-input w-full"
|
||||
/>
|
||||
<label for="subject" class="form-label">{{ $t('contact.form.subject') }}</label>
|
||||
@@ -44,7 +41,6 @@
|
||||
id="content"
|
||||
v-model="form.content"
|
||||
required
|
||||
placeholder=" "
|
||||
class="form-input w-full"
|
||||
minlength="30"
|
||||
rows="4"
|
||||
@@ -52,14 +48,19 @@
|
||||
<label for="content" class="form-label">{{ $t('contact.form.content') }}</label>
|
||||
</div>
|
||||
</form>
|
||||
<div v-if="error" class="mt-4 px-3 py-1 rounded-full bg-red-300 font-bold text-black">
|
||||
<div v-if="error" class="mt-2 py-1 text-red-400 text-sm">
|
||||
{{ $t('contact.form.error') }}
|
||||
</div>
|
||||
<div v-if="success" class="mt-4 px-3 py-1 rounded-full bg-green-300 font-bold text-black">
|
||||
<div v-if="success" class="mt-2 py-1 text-green-400 text-sm">
|
||||
{{ $t('contact.form.success') }}
|
||||
</div>
|
||||
<div class="my-12">
|
||||
<button :disabled="!isSendable" :class="{'disabled': !isSendable}" @click.prevent="handleForm" class="font-bold px-6 py-3 border-2 rounded-full border-indigo-600 text-indigo-600 hover:(bg-indigo-600 text-white) hover:dark:text-black duration-300 cursor-pointer">
|
||||
<div class="my-12 flex justify-center">
|
||||
<button
|
||||
:disabled="!isSendable"
|
||||
:class="{'disabled': !isSendable}"
|
||||
@click.prevent="handleForm"
|
||||
class="font-bold px-8 py-4 border-2 rounded-xl border-indigo-600 text-indigo-600 hover:(bg-indigo-600 text-white) hover:dark:text-black duration-300 cursor-pointer"
|
||||
>
|
||||
{{ $t('contact.form.submit') }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -79,7 +80,7 @@ export default defineComponent({
|
||||
const {$axios, $sentry} = useContext()
|
||||
const form = ref<Form>({} as Form)
|
||||
const handleForm = async () => {
|
||||
const response = await $axios.post('form',
|
||||
const response = await $axios.post('/api/form',
|
||||
{
|
||||
email: form.value.email,
|
||||
name: form.value.name,
|
||||
@@ -155,7 +156,7 @@ textarea:focus-within ~ label, textarea:not(:placeholder-shown) ~ label {
|
||||
}
|
||||
|
||||
.email {
|
||||
@apply duration-300 border-b-2 border-gray-200 dark:border-gray-800 hover:(border-black dark:border-white)
|
||||
@apply duration-300 border-b-3 border-gray-200 dark:border-gray-800 hover:border-indigo-600
|
||||
}
|
||||
|
||||
.disabled {
|
||||
|
||||
@@ -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"
|
||||
@@ -17,21 +17,27 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
|
||||
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.sort((a, b) => {
|
||||
return a.end_date === 'Today' ? -1 : a.end_date.split('-')[1] > b.end_date.split('-')[1] ? -1 : a.end_date.split('-')[0] > b.end_date.split('-')[0] ? 0 : 1
|
||||
})
|
||||
})
|
||||
} else {
|
||||
app.error({statusCode: 500})
|
||||
$sentry.captureEvent(response.data)
|
||||
}
|
||||
}, 'experiences')
|
||||
|
||||
return {
|
||||
experiences
|
||||
|
||||
@@ -1,69 +1,153 @@
|
||||
<template>
|
||||
<footer class="footer w-full border-t-2 border-solid border-gray-200 dark:border-gray-800 mb-20 md:mb-0">
|
||||
<div>
|
||||
<div class="flex flex-col items-center py-4 text-center ">
|
||||
<div class="mb-3">
|
||||
<p class="inline">{{ $t('footer.find_me') }}
|
||||
<br class="md:hidden"/>
|
||||
<a class="font-semibold" href="https://twitch.com/ArthurDanjou" target="_blank" rel="noopener noreferrer">
|
||||
<TwitchIcon />
|
||||
<span class="link">Twitch</span>
|
||||
</a>,
|
||||
<a class="font-semibold" href="https://github.com/ArthurDanjou" target="_blank" rel="noopener noreferrer">
|
||||
<GithubIcon />
|
||||
<span class="link">Github</span>
|
||||
</a> &
|
||||
<a class="font-semibold" href="https://twitter.com/ArthurDanj" target="_blank" rel="noopener noreferrer">
|
||||
<footer class="footer w-full mt-20">
|
||||
<div class="p-8 pb-0">
|
||||
<div class="lg:flex justify-evenly items-center">
|
||||
<div class="lg:w-1/3">
|
||||
<div class="flex">
|
||||
<Logo />
|
||||
</div>
|
||||
<div class="my-8">
|
||||
<p class="text-justify">
|
||||
{{ $t('footer.description') }}
|
||||
</p>
|
||||
<div class="mt-4">
|
||||
<nuxt-link to="/contact" class="text-red-400 border-b-2 border-gray-200 dark:border-gray-700 hover:border-red-400 duration-300">
|
||||
{{ $t(hiring_message) }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
<div class="social-links flex space-x-4 mb-8">
|
||||
<a target="_blank" href="https://twitter.com/ArthurDanj" rel="noopener noreferrer">
|
||||
<TwitterIcon />
|
||||
<span class="link">Twitter</span>
|
||||
</a>
|
||||
<br class="md:hidden"/>
|
||||
{{ $t('footer.separator') }}
|
||||
<a class="font-semibold" href="mailto:contact@arthurdanjou.fr" target="_blank" rel="noopener noreferrer">
|
||||
<a target="_blank" href="https://github.com/ArthurDanjou" rel="noopener noreferrer">
|
||||
<GithubIcon />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.polywork.com/arthurdanjou" rel="noopener noreferrer">
|
||||
<PolyworkIcon />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.twitch.tv/arthurdanjou" rel="noopener noreferrer">
|
||||
<TwitchIcon />
|
||||
</a>
|
||||
<a target="_blank" href="https://discord.gg/ENG6cFQhPS" rel="noopener noreferrer">
|
||||
<DiscordIcon />
|
||||
</a>
|
||||
<a target="_blank" href="mailto:contact@arthurdanjou.fr" rel="noopener noreferrer">
|
||||
<MailIcon />
|
||||
<span class="link">Mail</span>
|
||||
</a>
|
||||
</p>
|
||||
<br class="md:hidden"/>
|
||||
<span class="inline dark:text-gray-400 text-gray-600 text-xs">
|
||||
{{ $t('footer.links_click') }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
<div class="lg:ml-32 lg:w-1/3">
|
||||
<h1 class="font-bold mb-4 text-lg underline">
|
||||
{{ $t('footer.links') }}
|
||||
</h1>
|
||||
<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') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/about" :class="{'link-active': isWindow('about')}">
|
||||
{{ $t('header.about') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/blog" :class="{'link-active': isWindow('blog')}">
|
||||
{{ $t('header.blog') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/projects" :class="{'link-active': isWindow('projects')}">
|
||||
{{ $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') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/env" :class="{'link-active': isWindow('env')}">
|
||||
{{ $t('header.env') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/newsletter" :class="{'link-active': isWindow('newsletter')}">
|
||||
{{ $t('header.newsletter') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="link">
|
||||
<nuxt-link to="/contact" :class="{'link-active': isWindow('contact')}">
|
||||
{{ $t('header.contact') }}
|
||||
</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">
|
||||
<div>
|
||||
{{ $t('footer.credits') }}
|
||||
<a class="font-semibold" target="_blank" href="https://nuxtjs.org" rel="noopener noreferrer">
|
||||
<a class="social font-semibold" target="_blank" href="https://nuxtjs.org" rel="noopener noreferrer">
|
||||
<NuxtIcon />
|
||||
<span class="link">NuxtJS</span>
|
||||
<span>NuxtJS</span>
|
||||
</a>
|
||||
{{ $t('footer.credits_separator_and') }}
|
||||
<a class="font-semibold" target="_blank" href="https://adonisjs.com" rel="noopener noreferrer">
|
||||
{{ $t('footer.credits_separator') }}
|
||||
<a class="social font-semibold" target="_blank" href="https://adonisjs.com" rel="noopener noreferrer">
|
||||
<AdonisIcon />
|
||||
<span class="link">AdonisJS</span>
|
||||
<span>AdonisJS</span>
|
||||
</a>
|
||||
{{ $t('footer.credits_separator') }} <span>Arthur DANJOU</span>
|
||||
</p>
|
||||
<p>{{ $t('footer.copyrights', { date: getDate }) }}</p>
|
||||
</div>
|
||||
<p class="mt-4 lg:mt-0">{{ $t('footer.copyrights', { date: getDate }) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, defineComponent, ref, useContext} from "@nuxtjs/composition-api";
|
||||
import {computed, defineComponent, ref, useAsync, useContext, useRouter, useStore} from "@nuxtjs/composition-api";
|
||||
import {State} from "~/types/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Footer",
|
||||
setup() {
|
||||
const {$colorMode} = useContext()
|
||||
const {$colorMode, $axios, $sentry, app} = useContext()
|
||||
const isDarkMode = computed(() => {
|
||||
return $colorMode.preference === 'dark'
|
||||
})
|
||||
|
||||
const getDate = ref(new Date().getFullYear())
|
||||
|
||||
const hiring_message = useAsync(async () => {
|
||||
const request = await $axios.get('/api/informations', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (request.status === 200) {
|
||||
return request.data.informations.translation.code
|
||||
} else {
|
||||
app.error({statusCode: 500})
|
||||
$sentry.captureEvent(request.data)
|
||||
}
|
||||
}, 'hiring_message')
|
||||
|
||||
const store = useStore<State>()
|
||||
const route = computed(() => store.state.route)
|
||||
const isWindow = (loc: string) => {
|
||||
if (loc === '') return route.value === "/"
|
||||
else return route.value.includes(loc)
|
||||
}
|
||||
|
||||
return {
|
||||
getDate,
|
||||
isDarkMode
|
||||
isDarkMode,
|
||||
hiring_message,
|
||||
isWindow
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -71,8 +155,38 @@ export default defineComponent({
|
||||
|
||||
<style scoped lang="scss">
|
||||
.footer {
|
||||
.link {
|
||||
@apply border-b-2 border-gray-200 hover:border-black dark:border-gray-700 dark:hover:border-white duration-300;
|
||||
.social {
|
||||
span {
|
||||
@apply border-b-2 border-gray-200 dark:border-gray-700 duration-300;
|
||||
}
|
||||
&:hover span {
|
||||
@apply border-indigo-600
|
||||
}
|
||||
}
|
||||
.link-active a {
|
||||
@apply text-indigo-600;
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
svg {
|
||||
@apply h-6 w-6 duration-300
|
||||
}
|
||||
|
||||
&:hover svg {
|
||||
@apply transform hover:scale-120
|
||||
}
|
||||
}
|
||||
|
||||
.link a {
|
||||
@apply duration-500 border-b-2 border-transparent;
|
||||
|
||||
&.link-active {
|
||||
@apply text-indigo-600;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@apply border-indigo-600
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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" />
|
||||
@@ -17,21 +17,27 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
|
||||
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.sort((a, b) => {
|
||||
return a.end_date === 'Today' ? -1 : a.end_date.split('-')[1] > b.end_date.split('-')[1] ? -1 : a.end_date.split('-')[0] > b.end_date.split('-')[0] ? 0 : 1
|
||||
})
|
||||
})
|
||||
} else {
|
||||
app.error({statusCode: 500})
|
||||
$sentry.captureEvent(response.data)
|
||||
}
|
||||
}, 'formations')
|
||||
|
||||
return {
|
||||
formations
|
||||
|
||||
@@ -1,83 +1,37 @@
|
||||
<template>
|
||||
<header class="dark:bg-black dark:text-white fixed z-50 top-0 left-0 bg-white w-full duration-400" :class="scrollPosition > 50 ? ' shadow-md dark:shadow-white h-16 lg:h-20' : 'h-20 lg:h-24'">
|
||||
<header class="hidden xl:block dark:bg-black dark:text-white z-50 sticky top-0 left-0 bg-white w-full duration-400"
|
||||
:class="scrollPosition > 65 ? 'shadow-md dark:shadow-white h-16 lg:h-20' : 'h-20 lg:h-24'">
|
||||
<div class="header-container z-index-50 flex justify-between items-center h-full px-5 xl:px-32">
|
||||
<nuxt-link to="/">
|
||||
<img src="~/assets/images/logo-header.png" alt="Logo Circle" class="h-10 left cursor-pointer duration-500" />
|
||||
</nuxt-link>
|
||||
<nav class="right">
|
||||
<div class="flex flex-col md:flex-row items-center">
|
||||
<ul class="flex text-lg">
|
||||
<nuxt-link class="hidden md:inline-block" to="/about">
|
||||
<li class="nav-link">
|
||||
{{ $t('header.about') }}
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link class="hidden md:inline-block" to="/blog">
|
||||
<li class="nav-link">
|
||||
{{ $t('header.blog') }}
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link class="hidden md:inline-block" to="/env">
|
||||
<li class="nav-link">
|
||||
{{ $t('header.env') }}
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link class="hidden md:inline-block" to="/projects">
|
||||
<li class="nav-link">
|
||||
{{ $t('header.projects') }}
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link class="hidden md:inline-block" to="/contact">
|
||||
<li class="nav-link">
|
||||
{{ $t('header.contact') }}
|
||||
</li>
|
||||
</nuxt-link>
|
||||
</ul>
|
||||
<Logo />
|
||||
<nav class="right flex flex-col md:flex-row items-center hidden md:inline-block">
|
||||
<div class="flex text-lg">
|
||||
<nuxt-link class="nav-link" to="/about" :class="{ 'link-active': isWindow('about') }">
|
||||
{{ $t('header.about') }}
|
||||
</nuxt-link>
|
||||
<nuxt-link class="nav-link" to="/blog" :class="{ 'link-active': isWindow('blog') }">
|
||||
{{ $t('header.blog') }}
|
||||
</nuxt-link>
|
||||
<nuxt-link class="nav-link" to="/projects" :class="{ 'link-active': isWindow('projects') }">
|
||||
{{ $t('header.projects') }}
|
||||
</nuxt-link>
|
||||
<nuxt-link class="nav-link" to="/contact" :class="{ 'link-active': isWindow('contact') }">
|
||||
{{ $t('header.contact') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<ul class="dark:text-white dark:bg-black text-sm z-50 bg-white md:hidden fixed bottom-0 left-0 w-full flex items-center justify-around h-20 border-t border-gray-200 border-solid navbar-bottom-items">
|
||||
<nuxt-link to="/" class="w-1/6 nav-link-mobile">
|
||||
<li class="h-full w-full font-medium flex flex-col items-center justify-center">
|
||||
<HomeIcon :active="true" />
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/about" class="w-1/6 nav-link-mobile">
|
||||
<li class="font-medium flex flex-col items-center justify-center">
|
||||
<UserIcon :active="false" />
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/blog" class="w-1/6 nav-link-mobile blog">
|
||||
<li class="font-medium flex flex-col items-center justify-center">
|
||||
<BookIcon />
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/env" class="w-1/6 nav-link-mobile">
|
||||
<li class="font-medium flex flex-col items-center justify-center">
|
||||
<ComputerIcon />
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/projects" class="w-1/6 nav-link-mobile">
|
||||
<li class="font-medium flex flex-col items-center justify-center">
|
||||
<LightbulbIcon />
|
||||
</li>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/contact" class="w-1/6 nav-link-mobile">
|
||||
<li class="font-medium flex flex-col items-center justify-center">
|
||||
<ContactIcon />
|
||||
</li>
|
||||
</nuxt-link>
|
||||
</ul>
|
||||
</nav>
|
||||
<div>
|
||||
<ul class="flex items-center">
|
||||
<li @click="changeLanguage()" class="mx-1 h-9 w-9 cursor-pointer flex items-center justify-center p-1.5 rounded-xl hover:bg-gray-300 duration-200 dark:hover:bg-dark-400">
|
||||
<TranslateIcon />
|
||||
<li @click="changeLanguage()"
|
||||
class="mx-1 h-9 w-9 cursor-pointer flex items-center justify-center p-1.5 rounded-xl hover:bg-gray-300 duration-200 dark:hover:bg-dark-400">
|
||||
<TranslateIcon :french="isFrench"/>
|
||||
</li>
|
||||
<li @click="changeColorMode()" class="mx-1 h-9 w-9 cursor-pointer flex items-center p-1.5 rounded-xl hover:bg-gray-300 dark:hover:bg-dark-400 duration-200">
|
||||
<li @click="changeColorMode()"
|
||||
class="mx-1 h-9 w-9 cursor-pointer flex items-center p-1.5 rounded-xl hover:bg-gray-300 dark:hover:bg-dark-400 duration-200">
|
||||
<div v-if="this.$colorMode.preference === 'light'">
|
||||
<MoonIcon />
|
||||
<MoonIcon/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<SunIcon />
|
||||
<SunIcon/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
@@ -87,7 +41,18 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, onMounted, onUnmounted, ref, useAsync, useContext, useRouter} from "@nuxtjs/composition-api";
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
onMounted,
|
||||
onUnmounted,
|
||||
ref,
|
||||
useAsync,
|
||||
useContext,
|
||||
useRouter,
|
||||
useStore
|
||||
} from "@nuxtjs/composition-api";
|
||||
import {State} from "~/types/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Header",
|
||||
@@ -110,7 +75,7 @@ export default defineComponent({
|
||||
window.removeEventListener('scroll', updateScroll)
|
||||
})
|
||||
|
||||
const {i18n} = useContext()
|
||||
const {i18n, } = useContext()
|
||||
const $router = useRouter()
|
||||
const changeLanguage = () => useAsync(() => {
|
||||
i18n.setLocale(i18n.locale === 'fr' ? 'en' : 'fr')
|
||||
@@ -118,12 +83,23 @@ export default defineComponent({
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
const isFrench = computed(() => i18n.locale === 'fr')
|
||||
|
||||
const store = useStore<State>()
|
||||
const route = computed(() => store.state.route)
|
||||
|
||||
const isWindow = (loc: string) => {
|
||||
if (loc === '') return route.value === "/"
|
||||
else return route.value.includes(loc)
|
||||
}
|
||||
|
||||
return {
|
||||
scrollPosition,
|
||||
changeColorMode,
|
||||
updateScroll,
|
||||
changeLanguage
|
||||
changeLanguage,
|
||||
isWindow,
|
||||
isFrench
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -131,18 +107,14 @@ export default defineComponent({
|
||||
|
||||
<style scoped lang="scss">
|
||||
.nav-link {
|
||||
@apply text-gray-500 dark:text-gray-400 hover:dark:text-white font-medium hover:text-black mx-4 cursor-pointer duration-300
|
||||
}
|
||||
|
||||
.nav-link-mobile {
|
||||
@apply text-gray-500 dark:text-gray-400 hover:dark:text-white hover:text-black duration-300
|
||||
}
|
||||
|
||||
.nuxt-link-exact-active {
|
||||
@apply text-black dark:text-white;
|
||||
@apply font-medium cursor-pointer duration-500 mx-4 border-b-2 border-transparent hover:(border-indigo-600);
|
||||
}
|
||||
|
||||
.navbar-bottom-items li {
|
||||
transition: all .2s ease-in-out;
|
||||
}
|
||||
|
||||
.link-active {
|
||||
@apply text-indigo-600
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<section class="w-full mb-10">
|
||||
<h3 class="font-bold text-2xl md:text-4xl">
|
||||
{{ $t('about.title.languages') }}
|
||||
<TranslateIcon />
|
||||
<LanguageIcon />
|
||||
</h3>
|
||||
<div>
|
||||
<table class="text-base text-xl text-gray-700 dark:text-gray-400">
|
||||
@@ -21,6 +21,6 @@
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "LanguagesAbout"
|
||||
name: "LanguagesAbout",
|
||||
}
|
||||
</script>
|
||||
|
||||
18
src/components/Logo.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<nuxt-link class="profile ml-4 flex items-center" to="/">
|
||||
<img class="h-12 w-12 duration-500" src="@/assets/images/photo-rounded.png" alt="Photo of me" />
|
||||
<h1 class="ml-4 font-bold text-lg">Arthur Danjou</h1>
|
||||
</nuxt-link>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Logo"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.profile:hover img {
|
||||
@apply transform rotate-360;
|
||||
}
|
||||
</style>
|
||||
80
src/components/MobileNavbar.vue
Normal file
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div
|
||||
class="xl:hidden fixed z-50 top-auto bottom-0 w-full md:w-2/3 md:left-1/2 p-4 transition-all duration-500 transform md:-translate-x-1/2"
|
||||
:class="{'-translate-y-8 translate-x-10/12 sm:translate-x-1/2 xl:translate-x-0': opened}"
|
||||
>
|
||||
<nav class="flex justify-evenly py-4 bg-gray-200 dark:bg-gray-700 rounded-3xl dark:text-white text-sm overflow-hidden">
|
||||
<nuxt-link to="/" class="relative font-medium">
|
||||
<HomeIcon :active="isWindow('')"/>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/about" class="relative font-medium">
|
||||
<UserIcon :active="isWindow('/about')"/>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/blog" class="relative font-medium">
|
||||
<BookIcon :active="isWindow('/blog')"/>
|
||||
</nuxt-link>
|
||||
<nuxt-link to="/projects" class="relative font-medium">
|
||||
<LightbulbIcon :active="isWindow('/projects')"/>
|
||||
</nuxt-link>
|
||||
<button @click='toggleMenu' class="font-medium cursor-pointer">
|
||||
<CrossIcon v-if="opened" />
|
||||
<MenuIcon v-else :type="getMenuIconType"/>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, defineComponent, ref, useRouter, useStore, watch} from "@nuxtjs/composition-api";
|
||||
import {State} from "~/types/types";
|
||||
|
||||
const PAGE_TYPE = {
|
||||
projects: 1,
|
||||
services: 2,
|
||||
env: 3,
|
||||
about: 4,
|
||||
newsletter: 5
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: "MobileNavbar",
|
||||
setup () {
|
||||
const store = useStore<State>()
|
||||
const route = computed(() => store.state.route)
|
||||
const isWindow = (loc: string) => {
|
||||
if (loc === '') return route.value === "/"
|
||||
else return route.value.includes(loc)
|
||||
}
|
||||
|
||||
const getMenuIconType = computed(() => PAGE_TYPE[route.value.split('/')[1]] || 0)
|
||||
|
||||
const toggleMenu = () => {
|
||||
store.commit('TOGGLE_OPENED', !store.state.opened)
|
||||
if (store.state.opened) {
|
||||
document.getElementById('slider')!.style.maxHeight = window.screen.height + 'px'
|
||||
setTimeout(() => document.getElementById('nav')!.classList.add('z-50'), 300)
|
||||
} else {
|
||||
document.getElementById('nav')!.classList.remove('z-50')
|
||||
setTimeout(() => {
|
||||
document.getElementById('slider')!.style.maxHeight = 'none'
|
||||
}, 500)
|
||||
}
|
||||
}
|
||||
|
||||
const $router = useRouter()
|
||||
$router.afterEach(() => {
|
||||
store.commit('TOGGLE_OPENED', false)
|
||||
setTimeout(() => {
|
||||
document.getElementById('slider')!.style.maxHeight = 'none'
|
||||
}, 600)
|
||||
})
|
||||
|
||||
return {
|
||||
isWindow,
|
||||
toggleMenu,
|
||||
opened: computed(() => store.state.opened),
|
||||
getMenuIconType
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<h1 class="mt-16 md:mt-32 font-bold text-3xl md:text-5xl mr-2 inline mb-4 border-b-3 border-solid border-indigo-600">
|
||||
<h1 class="mt-16 font-bold text-3xl md:text-5xl mr-2 inline mb-4 border-b-3 border-solid border-indigo-600">
|
||||
{{ this.$t(title) }}
|
||||
</h1>
|
||||
</template>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<nuxt-link :to="`/blog/${slug}`" rel="noreferrer noopener">
|
||||
<div class="rounded-lg shadow-xl h-116 w-full lg:w-100 text-left bg-gray-100 dark:bg-gray-800 transform hover:scale-103 duration-300 mb-8 lg:mb-0">
|
||||
<nuxt-link :to="`/blog/${slug}`">
|
||||
<div class="rounded-lg dark:shadow-white shadow-xl h-116 w-full lg:w-100 text-left bg-gray-100 dark:bg-gray-800 transform hover:scale-103 duration-300 mb-8 lg:mb-0">
|
||||
<div class="h-2/5 post rounded-t-lg"
|
||||
:style="{ backgroundImage: `url(${getCover})` }">
|
||||
:style="{ backgroundImage: `url(https://athena.arthurdanjou.fr/files/${cover})` }">
|
||||
</div>
|
||||
<div class="h-3/5 p-4 flex flex-col justify-between">
|
||||
<div>
|
||||
@@ -11,8 +11,8 @@
|
||||
<Tag :content="tag" :pill="true"/>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold">{{ title }}</h1>
|
||||
<p class="text-base mt-3 text-gray-700 dark:text-gray-400 text-justify">{{ description }}</p>
|
||||
<h1 class="text-2xl font-bold">{{ $t(title) }}</h1>
|
||||
<p class="text-base mt-3 text-gray-700 dark:text-gray-400 text-justify">{{ $t(description) }}</p>
|
||||
</div>
|
||||
<div class="flex justify-between">
|
||||
<h5 class="text-base text-gray-700 dark:text-gray-400">{{ formatDate }}</h5>
|
||||
@@ -69,8 +69,6 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props: PostProps) {
|
||||
const getCover = computed(() => require(`~/assets/images/posts/${props.cover}`))
|
||||
|
||||
const { i18n } = useContext()
|
||||
const formatDate = computed(() => {
|
||||
const [first, second, third]: any = props.date.split('-')
|
||||
@@ -78,7 +76,6 @@ export default defineComponent({
|
||||
})
|
||||
|
||||
return {
|
||||
getCover,
|
||||
formatDate
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
<div class="my-8 lg:flex w-full lg:space-x-6">
|
||||
<div v-for="post in posts">
|
||||
<Post
|
||||
:title="post.title"
|
||||
:cover="post.cover"
|
||||
:description="post.description"
|
||||
:title="post.title.code"
|
||||
:cover="post.cover.file_name"
|
||||
:description="post.description.code"
|
||||
:date="post.date"
|
||||
:slug="post.slug"
|
||||
:tags="post.tags"
|
||||
@@ -36,17 +36,21 @@ import {Post} from "~/types/types";
|
||||
export default defineComponent({
|
||||
name: "PostsHome",
|
||||
setup() {
|
||||
const { $content, i18n, $sentry } = useContext()
|
||||
const { $axios, app, $sentry } = useContext()
|
||||
|
||||
const posts = useAsync(() => {
|
||||
return $content(`articles/${i18n.locale}`)
|
||||
.sortBy('date', 'asc')
|
||||
.limit(3)
|
||||
.fetch<Post>()
|
||||
.catch((error) => {
|
||||
$sentry.captureEvent(error)
|
||||
})
|
||||
}, 'posts')
|
||||
const posts = useAsync(async () => {
|
||||
const response = await $axios.get('/api/posts', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
return response.data.posts
|
||||
} else {
|
||||
app.error({statusCode: 500})
|
||||
$sentry.captureEvent(response.data)
|
||||
}
|
||||
}, 'posts_home')
|
||||
|
||||
return {
|
||||
posts
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
<template>
|
||||
<section class="flex flex-col 2xl:flex-row justify-center items-center py-8">
|
||||
<div class="lg:mr-12">
|
||||
<img class="logo-img rounded-full my-5" src="~/assets/images/memojies/Hey.png" alt="It's me !" />
|
||||
<section class="flex flex-col justify-center items-center py-8">
|
||||
<div>
|
||||
<img class="rounded-full my-5" src="~/assets/images/memojies/Hey.png" alt="A picture of myself" />
|
||||
</div>
|
||||
<div class="ml-2 text-lg leading-6 xl:w-2/3 text-justify dark:text-gray-400 text-gray-700">
|
||||
<p>{{ $t('about.banner.hello') }} <span class="text-indigo-600 font-bold">Arthur DANJOU</span> 👋.</p> <br/>
|
||||
<div class="text-lg leading-6 text-justify dark:text-gray-400 text-gray-700">
|
||||
<div>{{ $t('about.banner.hello') }} <span class="text-indigo-600 font-bold">Arthur DANJOU</span> 👋.</div> <br/>
|
||||
<p>{{ $t('about.banner.1')}}</p> <br/>
|
||||
<p>{{ $t('about.banner.2') }}</p> <br/>
|
||||
<p>{{ $t('about.banner.3') }}</p>
|
||||
<p>{{ $t('about.banner.3') }}</p> <br />
|
||||
<p>{{ $t('about.banner.4') }}</p>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SkillsAbout"
|
||||
name: "PresentationAbout"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.logo-img {
|
||||
height: 25rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<a :href="url" target="_blank" rel="noopener noreferrer">
|
||||
<div class="rounded-lg shadow-xl h-92 w-full lg:w-84 text-left bg-gray-100 dark:bg-gray-800 transform hover:scale-103 duration-300 mb-8 lg:mb-0">
|
||||
<div class="h-1/2 w-full h-2/5 project rounded-t-lg"
|
||||
:style="{ backgroundImage: `url(${getCover})` }">
|
||||
<a :href="url" target="_blank">
|
||||
<div class="rounded-lg dark:shadow-white shadow-xl lg:h-92 w-full lg:w-84 text-left bg-gray-100 dark:bg-gray-800 transform hover:scale-103 duration-300 mb-8 lg:mb-0">
|
||||
<div class="h-64 lg:h-1/2 w-full project rounded-t-lg"
|
||||
:style="{ backgroundImage: `url(https://athena.arthurdanjou.fr/files/${cover})` }">
|
||||
</div>
|
||||
<div class="h-1/2 p-4 flex flex-col justify-between">
|
||||
<div class="lg:h-1/2 py-8 px-4 lg:p-4 flex flex-col justify-between">
|
||||
<div>
|
||||
<div class="flex space-x-2 mb-2">
|
||||
<div v-for="tag in tags">
|
||||
<Tag :content="tag" :pill="false"/>
|
||||
<Tag :content="tag.label.code" :pill="false"/>
|
||||
</div>
|
||||
</div>
|
||||
<h1 class="text-2xl font-bold">{{ title }}</h1>
|
||||
@@ -20,15 +20,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, defineComponent} from "@nuxtjs/composition-api";
|
||||
|
||||
interface ProjectProp {
|
||||
title: string,
|
||||
cover: string,
|
||||
tags: Array<String>,
|
||||
description: string,
|
||||
url: string,
|
||||
}
|
||||
import {defineComponent} from "@nuxtjs/composition-api";
|
||||
|
||||
export default defineComponent({
|
||||
name: "Project",
|
||||
@@ -39,7 +31,7 @@ export default defineComponent({
|
||||
},
|
||||
cover: {
|
||||
type: String,
|
||||
default: "artapi.png"
|
||||
default: "athena.png"
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
@@ -53,13 +45,6 @@ export default defineComponent({
|
||||
type: String,
|
||||
default: 'https://arthurdanjou.fr'
|
||||
}
|
||||
},
|
||||
setup(props: ProjectProp) {
|
||||
const getCover = computed(() => require(`@/assets/images/projects/${props.cover}`))
|
||||
|
||||
return {
|
||||
getCover
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
{{ $t('projects.description') }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="my-8 lg:flex w-full lg:space-x-8 flex flex-wrap justify-center">
|
||||
<div v-for="project in projects">
|
||||
<div class="my-8 w-full lg:space-x-8 lg:flex justify-center">
|
||||
<div v-for="project in projects" class="mb-4">
|
||||
<Project
|
||||
:title="project.title"
|
||||
:cover="project.cover"
|
||||
:description="project.description"
|
||||
:title="project.name"
|
||||
:cover="project.cover.file_name"
|
||||
:description="project.description.code"
|
||||
:tags="project.tags"
|
||||
:url="project.url"
|
||||
/>
|
||||
@@ -29,21 +29,25 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
|
||||
import {Project} from "~/types/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "ProjectsHome",
|
||||
setup() {
|
||||
const { $content, $sentry } = useContext()
|
||||
const { $axios, app, $sentry } = useContext()
|
||||
|
||||
const projects = useAsync(() => {
|
||||
return $content(`projects`)
|
||||
.limit(3)
|
||||
.fetch<Project>()
|
||||
.catch((error) => {
|
||||
$sentry.captureEvent(error)
|
||||
})
|
||||
}, 'projects')
|
||||
const projects = useAsync(async () => {
|
||||
const response = await $axios.get('/api/projects', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
return response.data.projects.slice(0, 3)
|
||||
} else {
|
||||
$sentry.captureEvent(response.data)
|
||||
app.error({statusCode: 500})
|
||||
}
|
||||
}, 'projects_home')
|
||||
|
||||
return {
|
||||
projects
|
||||
|
||||
31
src/components/ServicePart.vue
Normal file
@@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<div class="relative lg:(w-1/2 m-8) mt-8">
|
||||
<div class="absolute right-0 top-0 bg-indigo-600 text-white rounded-xl p-3">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="rounded-lg mt-8 md:mr-8 pt-4 md:p-8">
|
||||
<h1 class="font-bold capitalize text-4xl mb-4">
|
||||
{{ $t(title) }}
|
||||
</h1>
|
||||
<p class="text-gray-600 text-lg text-justify leading-5 ">
|
||||
{{ $t(content) }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "ServicePart",
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
default: 'services.1.title'
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: 'services.1.content'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
188
src/components/SideMenu.vue
Normal file
@@ -0,0 +1,188 @@
|
||||
<template>
|
||||
<div class="relative w-full min-w-screen xl:overflow-auto">
|
||||
<div
|
||||
class="min-h-screen bg-gray-100 dark:bg-gray-900 xl:hidden pl-4 pr-20 py-4 transition-all duration-500 duration-500 absolute top-0 left-0 right-0 flex items-center"
|
||||
>
|
||||
<nav
|
||||
id="nav"
|
||||
class="w-auto"
|
||||
>
|
||||
<div class="mb-8">
|
||||
<div class="flex justify-between mb-4">
|
||||
<div @click="closeMenu" class="flex justify-center items-center cursor-pointer cross text-sm">
|
||||
<CrossIcon class="duration-300" />
|
||||
<div class="ml-4">{{ $t('sidebar.close') }}</div>
|
||||
</div>
|
||||
<div class="ml-6">
|
||||
<ul class="flex items-center">
|
||||
<li @click="changeLanguage()"
|
||||
class="mx-1 h-9 w-9 cursor-pointer flex items-center justify-center p-1.5 rounded-xl hover:bg-gray-300 duration-200 dark:hover:bg-dark-400">
|
||||
<TranslateIcon :french="isFrench" />
|
||||
</li>
|
||||
<li @click="changeColorMode()"
|
||||
class="mx-1 h-9 w-9 cursor-pointer flex items-center p-1.5 rounded-xl hover:bg-gray-300 dark:hover:bg-dark-400 duration-200">
|
||||
<div v-if="this.$colorMode.preference === 'light'">
|
||||
<MoonIcon/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<SunIcon/>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<Logo />
|
||||
</div>
|
||||
<div class="w-auto flex">
|
||||
<div class="flex flex-col ml-4 mb-8 space-y-1.5 font-bold text-lg">
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('') }">
|
||||
<nuxt-link to="/">
|
||||
{{ $t('header.home') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('about') }">
|
||||
<nuxt-link to="/about">
|
||||
{{ $t('header.about') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('blog') }">
|
||||
<nuxt-link to="/blog">
|
||||
{{ $t('header.blog') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('projects') }">
|
||||
<nuxt-link to="/projects">
|
||||
{{ $t('header.projects') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('services') }">
|
||||
<nuxt-link to="/services">
|
||||
{{ $t('header.services') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('env') }">
|
||||
<nuxt-link to="/env">
|
||||
{{ $t('header.env') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('newsletter') }">
|
||||
<nuxt-link to="/newsletter">
|
||||
{{ $t('header.newsletter') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
<div class="nav-link" :class="{ 'link-active': isWindow('contact') }">
|
||||
<nuxt-link to="/contact">
|
||||
{{ $t('header.contact') }}
|
||||
</nuxt-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="social-links flex justify-between space-x-0.5">
|
||||
<a target="_blank" href="https://twitter.com/ArthurDanj" rel="noopener noreferrer">
|
||||
<TwitterIcon />
|
||||
</a>
|
||||
<a target="_blank" href="https://github.com/ArthurDanjou" rel="noopener noreferrer">
|
||||
<GithubIcon />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.polywork.com/arthurdanjou" rel="noopener noreferrer">
|
||||
<PolyworkIcon />
|
||||
</a>
|
||||
<a target="_blank" href="https://www.twitch.tv/arthurdanjou" rel="noopener noreferrer">
|
||||
<TwitchIcon />
|
||||
</a>
|
||||
<a target="_blank" href="https://discord.gg/ENG6cFQhPS" rel="noopener noreferrer">
|
||||
<DiscordIcon />
|
||||
</a>
|
||||
<a target="_blank" href="mailto:contact@arthurdanjou.fr" rel="noopener noreferrer">
|
||||
<MailIcon />
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {
|
||||
computed,
|
||||
defineComponent,
|
||||
useAsync,
|
||||
useContext,
|
||||
useRouter,
|
||||
useStore
|
||||
} from "@nuxtjs/composition-api";
|
||||
import {State} from "~/types/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "SideMenu",
|
||||
setup() {
|
||||
const {$colorMode} = useContext()
|
||||
const changeColorMode = () => {
|
||||
$colorMode.preference = $colorMode.value === 'light' ? 'dark' : 'light'
|
||||
}
|
||||
|
||||
const {i18n} = useContext()
|
||||
const $router = useRouter()
|
||||
const changeLanguage = () => useAsync(() => {
|
||||
i18n.setLocale(i18n.locale === 'fr' ? 'en' : 'fr')
|
||||
if ($router.currentRoute.fullPath.includes('blog') || $router.currentRoute.fullPath === '/') {
|
||||
window.location.reload()
|
||||
}
|
||||
})
|
||||
|
||||
const isFrench = computed(() => i18n.locale === 'fr')
|
||||
|
||||
const store = useStore<State>()
|
||||
const closeMenu = () => {
|
||||
store.commit('TOGGLE_OPENED', false)
|
||||
document.getElementById('nav')!.classList.remove('z-50')
|
||||
setTimeout(() => {
|
||||
document.getElementById('slider')!.style.maxHeight = 'none'
|
||||
}, 500)
|
||||
}
|
||||
|
||||
const route = computed(() => store.state.route)
|
||||
const isWindow = (loc: string) => {
|
||||
if (loc === '') return route.value === "/"
|
||||
else return route.value.includes(loc)
|
||||
}
|
||||
|
||||
return {
|
||||
changeColorMode,
|
||||
changeLanguage,
|
||||
closeMenu,
|
||||
opened: computed(() => store.state.opened),
|
||||
isWindow,
|
||||
isFrench
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.cross:hover svg {
|
||||
@apply transform scale-140;
|
||||
}
|
||||
|
||||
.nav-link a {
|
||||
@apply duration-300 border-b-2 border-transparent;
|
||||
|
||||
&:hover {
|
||||
@apply border-indigo-600;
|
||||
}
|
||||
}
|
||||
|
||||
.link-active a {
|
||||
@apply text-indigo-600;
|
||||
}
|
||||
|
||||
.social-links a {
|
||||
svg {
|
||||
@apply h-6 w-6 duration-300
|
||||
}
|
||||
|
||||
&:hover svg {
|
||||
@apply transform hover:scale-120
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,9 +2,9 @@
|
||||
<div class="mb-3 mr-2 p-1 md:p-2 h-32 w-32 border-gray-900 dark:border-dark-200 border-2 duration-300 rounded-3xl hover:bg-opacity-25" :class="getColor">
|
||||
<div class="w-full h-full flex flex-col justify-center items-center">
|
||||
<div>
|
||||
<img class="rounded-sm" alt="Skill Img" :src="getCoverLink" />
|
||||
<img class="rounded-sm" :alt="`Skill ${name} Image`" :src="`https://athena.arthurdanjou.fr/files/${cover}`" />
|
||||
</div>
|
||||
<h1 class="md:text-lg text-md font-bold text-center text-gray-700 dark:text-gray-400">{{ skill }}</h1>
|
||||
<h1 class="md:text-lg text-md font-bold text-center text-gray-700 dark:text-gray-400">{{ name }}</h1>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -13,7 +13,7 @@
|
||||
import {computed, defineComponent} from '@nuxtjs/composition-api'
|
||||
|
||||
interface SkillProps {
|
||||
skill: string,
|
||||
name: string,
|
||||
color: string,
|
||||
cover: string
|
||||
}
|
||||
@@ -21,7 +21,7 @@ interface SkillProps {
|
||||
export default defineComponent({
|
||||
name: "Skill",
|
||||
props: {
|
||||
skill: {
|
||||
name: {
|
||||
type: String,
|
||||
default: "Rien"
|
||||
},
|
||||
@@ -65,11 +65,9 @@ export default defineComponent({
|
||||
return 'hover:bg-amber-400'
|
||||
}
|
||||
})
|
||||
const getCoverLink = computed(() => require(`@/assets/images/skills/${props.cover}`))
|
||||
|
||||
return {
|
||||
getColor,
|
||||
getCoverLink
|
||||
getColor
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
<div class="flex flex-row w-full overflow-x-auto md:overflow-x-hidden lg:flex-wrap space-x-4 lg:space-x-0 lg:justify-center">
|
||||
<div v-if="skills" v-for="skill in skills">
|
||||
<Skill
|
||||
:skill="skill.title"
|
||||
:name="skill.name"
|
||||
:color="skill.color"
|
||||
:cover="skill.cover"
|
||||
:cover="skill.file.file_name"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -18,20 +18,25 @@
|
||||
|
||||
<script lang="ts">
|
||||
import {defineComponent, useAsync, useContext} from "@nuxtjs/composition-api";
|
||||
import {Skill} from "~/types/types";
|
||||
|
||||
export default defineComponent({
|
||||
name: "SkillsAbout",
|
||||
setup() {
|
||||
const {$content, $sentry} = useContext()
|
||||
const {$axios, $sentry, app} = useContext()
|
||||
|
||||
const skills = useAsync(() => {
|
||||
return $content('skills')
|
||||
.fetch<Skill>()
|
||||
.catch((error) => {
|
||||
$sentry.captureEvent(error)
|
||||
})
|
||||
})
|
||||
const skills = useAsync(async () => {
|
||||
const response = await $axios.get('/api/skills', {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${process.env.API_TOKEN}`
|
||||
}
|
||||
})
|
||||
if (response.status === 200) {
|
||||
return response.data.skills
|
||||
} else {
|
||||
app.error({statusCode: 500})
|
||||
$sentry.captureEvent(response.data)
|
||||
}
|
||||
}, 'skills')
|
||||
|
||||
return {
|
||||
skills
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
<template>
|
||||
<svg class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<svg v-if="active" class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M21 4H7a2 2 0 1 0 0 4h14v13a1 1 0 0 1-1 1H7a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h13a1 1 0 0 1 1 1v1zm-1 3H7a1 1 0 1 1 0-2h13v2z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<svg v-else class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M21 4H7a2 2 0 1 0 0 4h14v13a1 1 0 0 1-1 1H7a4 4 0 0 1-4-4V6a4 4 0 0 1 4-4h13a1 1 0 0 1 1 1v1zM5 18a2 2 0 0 0 2 2h12V10H7a3.982 3.982 0 0 1-2-.535V18zM20 7H7a1 1 0 1 1 0-2h13v2z"
|
||||
fill="currentColor"
|
||||
|
||||
17
src/components/icons/CodeIcon.vue
Normal file
@@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<svg class="inline" width="2rem" height="2rem" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M20 3H4c-1.103 0-2 .897-2 2v14c0 1.103.897 2 2 2h16c1.103 0 2-.897 2-2V5c0-1.103-.897-2-2-2zM4 19V7h16l.002 12H4z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
<path
|
||||
d="M9.293 9.293L5.586 13l3.707 3.707l1.414-1.414L8.414 13l2.293-2.293zm5.414 0l-1.414 1.414L15.586 13l-2.293 2.293l1.414 1.414L18.414 13z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CodeIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -1,20 +0,0 @@
|
||||
<template>
|
||||
<svg class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M20 18c1.1 0 1.99-.9 1.99-2L22 6c0-1.1-.9-2-2-2H4c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2H0v2h24v-2h-4zM4 6h16v10H4V6z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: "ComputerIcon",
|
||||
props: {
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,8 +1,16 @@
|
||||
<template>
|
||||
<svg class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<svg v-if="active" class="inline" width="2.5em" height="2.5em" viewBox="0 0 20 20" focusable="false">
|
||||
<path
|
||||
d="M20 4H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm-1 14H5c-.55 0-1-.45-1-1V8l6.94 4.34c.65.41 1.47.41 2.12 0L20 8v9c0 .55-.45 1-1 1zm-7-7L4 6h16l-8 5z"
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
d="M7.172 11.334l2.83 1.935l2.728-1.882l6.115 6.033c-.161.052-.333.08-.512.08H1.667c-.22 0-.43-.043-.623-.12l6.128-6.046zM20 6.376v9.457c0 .247-.054.481-.15.692l-5.994-5.914L20 6.376zM0 6.429l6.042 4.132l-5.936 5.858A1.663 1.663 0 0 1 0 15.833V6.43zM18.333 2.5c.92 0 1.667.746 1.667 1.667v.586L9.998 11.648L0 4.81v-.643C0 3.247.746 2.5 1.667 2.5h16.666z"
|
||||
/>
|
||||
</svg>
|
||||
<svg v-else class="inline" width="2.5em" height="2.5em" viewBox="0 0 20 20" focusable="false">
|
||||
<path
|
||||
fill="currentColor"
|
||||
fill-rule="evenodd"
|
||||
d="M18.333 2.5c.92 0 1.667.746 1.667 1.667v11.666c0 .92-.746 1.667-1.667 1.667H1.667C.747 17.5 0 16.754 0 15.833V4.167C0 3.247.746 2.5 1.667 2.5h16.666zM7.168 11.328l-4.91 4.852h15.325l-4.857-4.802L10 13.265l-2.832-1.937zM18.64 7.292l-4.796 3.316l4.796 4.736V7.292zm-17.279.061v7.836l4.686-4.631l-4.686-3.205zm16.956-3.532H1.698a.358.358 0 0 0-.25.086a.26.26 0 0 0-.085.222v1.62L10 11.656l8.644-5.965V4.199c.001-.134-.03-.231-.092-.292a.306.306 0 0 0-.234-.086z"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
18
src/components/icons/CrossIcon.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg class="inline" width="1.5em" height="1.5em" viewBox="0 0 15 15" focusable="false">
|
||||
<g fill="none">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M11.782 4.032a.575.575 0 1 0-.813-.814L7.5 6.687L4.032 3.218a.575.575 0 0 0-.814.814L6.687 7.5l-3.469 3.468a.575.575 0 0 0 .814.814L7.5 8.313l3.469 3.469a.575.575 0 0 0 .813-.814L8.313 7.5l3.469-3.468z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "CrossIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -5,7 +5,8 @@
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<path d="M6 17.6l-2-1.1V14"/>
|
||||
<path d="M4 10V7.5l2-1.1"/>
|
||||
<path d="M10 4.1L12 3l2 1.1"/>
|
||||
|
||||
13
src/components/icons/DiscordIcon.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<svg class="inline" width="1.5em" height="1.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M20.317 4.37a19.791 19.791 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.27 18.27 0 0 0-5.487 0a12.64 12.64 0 0 0-.617-1.25a.077.077 0 0 0-.079-.037A19.736 19.736 0 0 0 3.677 4.37a.07.07 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.082.082 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.078.078 0 0 0 .084-.028c.462-.63.874-1.295 1.226-1.994a.076.076 0 0 0-.041-.106a13.107 13.107 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10.2 10.2 0 0 0 .372-.292a.074.074 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.074.074 0 0 1 .078.01c.12.098.246.198.373.292a.077.077 0 0 1-.006.127a12.299 12.299 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.076.076 0 0 0 .084.028a19.839 19.839 0 0 0 6.002-3.03a.077.077 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.061.061 0 0 0-.031-.03zM8.02 15.33c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418zm7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "DiscordIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -13,8 +13,8 @@
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round">
|
||||
</path>
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
13
src/components/icons/GlassIcon.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<svg class="inline" width="2rem" height="2rem" viewBox="0 0 512 512" focusable="false">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M479.6 399.716l-81.084-81.084l-62.368-25.767A175.014 175.014 0 0 0 368 192c0-97.047-78.953-176-176-176S16 94.953 16 192s78.953 176 176 176a175.034 175.034 0 0 0 101.619-32.377l25.7 62.2l81.081 81.088a56 56 0 1 0 79.2-79.195zM48 192c0-79.4 64.6-144 144-144s144 64.6 144 144s-64.6 144-144 144S48 271.4 48 192zm408.971 264.284a24.028 24.028 0 0 1-33.942 0l-76.572-76.572l-23.894-57.835l57.837 23.894l76.573 76.572a24.028 24.028 0 0 1-.002 33.941z" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "GlassIcon"
|
||||
}
|
||||
</script>
|
||||
14
src/components/icons/GoogleIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg class="inline" width="1.5em" height="1.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M21.35 11.1h-9.17v2.73h6.51c-.33 3.81-3.5 5.44-6.5 5.44C8.36 19.27 5 16.25 5 12c0-4.1 3.2-7.27 7.2-7.27c3.09 0 4.9 1.97 4.9 1.97L19 4.72S16.56 2 12.1 2C6.42 2 2.03 6.8 2.03 12c0 5.05 4.13 10 10.22 10c5.35 0 9.25-3.67 9.25-9.09c0-1.15-.15-1.81-.15-1.81z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "GoogleIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -1,8 +1,14 @@
|
||||
<template>
|
||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 24 24" focusable="false">
|
||||
<svg v-if="liked" class="icon inline" width="1em" height="1em" viewBox="0 0 256 256" focusable="false">
|
||||
<path
|
||||
d="M12.1 18.55l-.1.1l-.11-.1C7.14 14.24 4 11.39 4 8.5C4 6.5 5.5 5 7.5 5c1.54 0 3.04 1 3.57 2.36h1.86C13.46 6 14.96 5 16.5 5c2 0 3.5 1.5 3.5 3.5c0 2.89-3.14 5.74-7.9 10.05M16.5 3c-1.74 0-3.41.81-4.5 2.08C10.91 3.81 9.24 3 7.5 3C4.42 3 2 5.41 2 8.5c0 3.77 3.4 6.86 8.55 11.53L12 21.35l1.45-1.32C18.6 15.36 22 12.27 22 8.5C22 5.41 19.58 3 16.5 3z"
|
||||
:class="liked ? 'fill-heart': 'fill-current'"
|
||||
d="M220.346 136.508l-81.032 81.031a16.013 16.013 0 0 1-22.625 0L33.58 134.43a59.974 59.974 0 0 1 2.344-87.07c23.281-21.015 61.25-19.054 84.578 4.297l7.5 7.492l9.578-9.578a60.698 60.698 0 0 1 43.984-17.554a59.55 59.55 0 0 1 43.063 19.89c20.984 23.297 19.062 61.25-4.281 84.602z"
|
||||
class="fill-heart"
|
||||
/>
|
||||
</svg>
|
||||
<svg v-else class="icon inline" width="1em" height="1em" viewBox="0 0 256 256" focusable="false">
|
||||
<path
|
||||
d="M128 220.218a13.957 13.957 0 0 1-9.9-4.093l-83.112-83.113a58 58 0 0 1 2.257-84.166A56.049 56.049 0 0 1 78.826 34.79a62.72 62.72 0 0 1 40.266 18.28L128 61.975l10.988-10.988a58.003 58.003 0 0 1 84.166 2.257a56.05 56.05 0 0 1 14.058 41.581a62.725 62.725 0 0 1-18.28 40.267L137.9 216.125a13.96 13.96 0 0 1-9.899 4.093zm5.657-8.336zM74.985 46.734a44.094 44.094 0 0 0-29.71 11.03a46.003 46.003 0 0 0-1.802 66.763l83.112 83.113a2.005 2.005 0 0 0 2.83 0l81.032-81.033c18.168-18.168 19.868-47.476 3.79-65.332a46.003 46.003 0 0 0-66.764-1.802l-15.23 15.23a5.999 5.999 0 0 1-8.485 0l-13.15-13.15a50.6 50.6 0 0 0-35.623-14.82z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
@@ -18,3 +24,9 @@ export default {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.icon {
|
||||
transform: translate(0);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<svg class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<svg v-if="active" class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M15 22.065v-5a3 3 0 0 0-6 0v5H4a2 2 0 0 1-2-2V9.197a2 2 0 0 1 .971-1.715l8-4.8a2 2 0 0 1 2.058 0l8 4.8A2 2 0 0 1 22 9.197v10.868a2 2 0 0 1-2 2h-5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<svg v-else class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M20 20V9.132l-8-4.8l-8 4.8V20h4v-2.75a4 4 0 1 1 8 0V20h4zm-6 2v-4.75a2 2 0 1 0-4 0V22H4a2 2 0 0 1-2-2V9.132a2 2 0 0 1 .971-1.715l8-4.8a2 2 0 0 1 2.058 0l8 4.8A2 2 0 0 1 22 9.132V20a2 2 0 0 1-2 2h-6z"
|
||||
stroke="currentColor"
|
||||
:class="{active: 'fill-black dark:fill-white'}"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
14
src/components/icons/LanguageIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M12.87 15.07l-2.54-2.51l.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35C8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5l3.11 3.11l.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "LanguageIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -1,9 +1,15 @@
|
||||
<template>
|
||||
<svg class="inline" width="1.5em" height="2.5em" viewBox="0 0 352 512" focusable="false">
|
||||
<svg v-if="active" class="inline" width="2.5em" height="2.5em" viewBox="0 0 256 256" focusable="false">
|
||||
<path
|
||||
d="M176 80c-52.94 0-96 43.06-96 96c0 8.84 7.16 16 16 16s16-7.16 16-16c0-35.3 28.72-64 64-64c8.84 0 16-7.16 16-16s-7.16-16-16-16zM96.06 459.17c0 3.15.93 6.22 2.68 8.84l24.51 36.84c2.97 4.46 7.97 7.14 13.32 7.14h78.85c5.36 0 10.36-2.68 13.32-7.14l24.51-36.84c1.74-2.62 2.67-5.7 2.68-8.84l.05-43.18H96.02l.04 43.18zM176 0C73.72 0 0 82.97 0 176c0 44.37 16.45 84.85 43.56 115.78c16.64 18.99 42.74 58.8 52.42 92.16v.06h48v-.12c-.01-4.77-.72-9.51-2.15-14.07c-5.59-17.81-22.82-64.77-62.17-109.67c-20.54-23.43-31.52-53.15-31.61-84.14c-.2-73.64 59.67-128 127.95-128c70.58 0 128 57.42 128 128c0 30.97-11.24 60.85-31.65 84.14c-39.11 44.61-56.42 91.47-62.1 109.46a47.507 47.507 0 0 0-2.22 14.3v.1h48v-.05c9.68-33.37 35.78-73.18 52.42-92.16C335.55 260.85 352 220.37 352 176C352 78.8 273.2 0 176 0z"
|
||||
d="M176 232a8 8 0 0 1-8 8H88a8 8 0 0 1 0-16h80a8 8 0 0 1 8 8zm40-128a87.544 87.544 0 0 1-33.642 69.208A16.235 16.235 0 0 0 176 185.977V192a16.018 16.018 0 0 1-16 16H96a16.018 16.018 0 0 1-16-16v-6.031a16.02 16.02 0 0 0-6.23-12.66a87.575 87.575 0 0 1-33.769-68.814c-.263-47.662 38.263-87.35 85.881-88.47A88.002 88.002 0 0 1 216 104zm-50.343 2.343a8.002 8.002 0 0 0-11.314 0L128 132.687l-26.343-26.344a8 8 0 0 0-11.314 11.314L120 147.314V184a8 8 0 0 0 16 0v-36.686l29.657-29.657a8 8 0 0 0 0-11.314z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
/>
|
||||
</svg>
|
||||
<svg v-else class="inline" width="2.5em" height="2.5em" viewBox="0 0 256 256" focusable="false">
|
||||
<path
|
||||
d="M176 232a8 8 0 0 1-8 8H88a8 8 0 0 1 0-16h80a8 8 0 0 1 8 8zm40-128a87.544 87.544 0 0 1-33.642 69.208A16.235 16.235 0 0 0 176 185.977V192a16.018 16.018 0 0 1-16 16H96a16.018 16.018 0 0 1-16-16v-6.031a16.02 16.02 0 0 0-6.23-12.66a87.575 87.575 0 0 1-33.769-68.814c-.263-47.662 38.263-87.35 85.881-88.47A88.002 88.002 0 0 1 216 104zm-16 0a72 72 0 0 0-73.741-71.98C87.303 32.939 55.786 65.41 56 104.408a71.657 71.657 0 0 0 27.637 56.307A31.922 31.922 0 0 1 96 185.968V192h24v-44.686l-29.657-29.657a8 8 0 0 1 11.314-11.314L128 132.687l26.343-26.344a8 8 0 1 1 11.314 11.314L136 147.314V192h24v-6.024a32.135 32.135 0 0 1 12.467-25.344A71.638 71.638 0 0 0 200 104z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
|
||||
63
src/components/icons/MenuIcon.vue
Normal file
@@ -0,0 +1,63 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg v-if="type === 0" class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 12h18"/>
|
||||
<path d="M3 6h18"/>
|
||||
<path d="M3 18h18"/>
|
||||
</g>
|
||||
</svg>
|
||||
<svg v-else-if="type === 1" class="inline" width="2.5em" height="2.5em" viewBox="0 0 48 48" focusable="false">
|
||||
<g fill="none" stroke="currentColor" stroke-width="4" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path
|
||||
clip-rule="evenodd"
|
||||
d="M44 22c0-9.941-8.954-18-20-18S4 12.059 4 22h40z" />
|
||||
<path d="M4 38h40v6H4z" />
|
||||
<path d="M4 28l5.455 4l7.272-4L24 32l7.273-4l7.272 4L44 28" />
|
||||
</g>
|
||||
</svg>
|
||||
<svg v-else-if="type === 2" class="inline" width="0.75em" height="2.5em" viewBox="0 0 3 16" focusable="false">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M0 2.5a1.5 1.5 0 1 0 3 0a1.5 1.5 0 0 0-3 0zm0 5a1.5 1.5 0 1 0 3 0a1.5 1.5 0 0 0-3 0zM1.5 14a1.5 1.5 0 1 1 0-3a1.5 1.5 0 0 1 0 3z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
<svg v-else-if="type === 3" class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<g fill="none">
|
||||
<path
|
||||
d="M7 6a3 3 0 0 0-3 3h16a3 3 0 0 0-3-3H7z"
|
||||
fill="currentColor" />
|
||||
<path
|
||||
d="M7 18a3 3 0 0 1-3-3h16a3 3 0 0 1-3 3H7z"
|
||||
fill="currentColor" />
|
||||
<path
|
||||
d="M3 11a1 1 0 1 0 0 2h18a1 1 0 1 0 0-2H3z"
|
||||
fill="currentColor" />
|
||||
</g>
|
||||
</svg>
|
||||
<svg v-else-if="type === 4" class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<g fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M7.05 11.293l4.243-4.243a2 2 0 0 1 2.828 0l2.829 2.83a2 2 0 0 1 0 2.828l-4.243 4.243a2 2 0 0 1-2.828 0L7.05 14.12a2 2 0 0 1 0-2.828z" />
|
||||
<path d="M16.243 9.172l3.086-.772a1.5 1.5 0 0 0 .697-2.516L17.81 3.667a1.5 1.5 0 0 0-2.44.47L14.122 7.05" />
|
||||
<path d="M9.172 16.243L8.4 19.329a1.5 1.5 0 0 1-2.516.697L3.667 17.81a1.5 1.5 0 0 1 .47-2.44l2.913-1.248" />
|
||||
</g>
|
||||
</svg>
|
||||
<svg v-else-if="type === 5" class="inline" width="2.5em" height="2.5em" viewBox="0 0 20 20" focusable="false">
|
||||
<path
|
||||
d="M9.584 6.036c1.952 0 2.591-1.381 1.839-2.843c-.871-1.693 1.895-3.155.521-3.155c-1.301 0-3.736 1.418-4.19 3.183c-.339 1.324.296 2.815 1.83 2.815zm5.212 8.951l-.444-.383a1.355 1.355 0 0 0-1.735 0l-.442.382a3.326 3.326 0 0 1-2.174.801a3.325 3.325 0 0 1-2.173-.8l-.444-.384a1.353 1.353 0 0 0-1.734.001l-.444.383c-1.193 1.028-2.967 1.056-4.204.1V19a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1v-3.912c-1.237.954-3.011.929-4.206-.101zM10 7c-7.574 0-9 3.361-9 5v.469l1.164 1.003a1.355 1.355 0 0 0 1.735 0l.444-.383a3.353 3.353 0 0 1 4.345 0l.444.384c.484.417 1.245.42 1.735-.001l.442-.382a3.352 3.352 0 0 1 4.346-.001l.444.383c.487.421 1.25.417 1.735 0L19 12.469V12c0-1.639-1.426-5-9-5z"
|
||||
fill="currentColor"/>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MenuIcon",
|
||||
props: {
|
||||
type: {
|
||||
type: Number,
|
||||
default: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
14
src/components/icons/NetworkIcon.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<svg class="inline" width="2rem" height="2rem" viewBox="0 0 32 32" focusable="false">
|
||||
<path
|
||||
d="M17 17h5.142a4 4 0 1 0 0-2H17V7h5.142a4 4 0 1 0 0-2H17a2.002 2.002 0 0 0-2 2v8H9.858a4 4 0 1 0 0 2H15v8a2.002 2.002 0 0 0 2 2h5.142a4 4 0 1 0 0-2H17zm9-3a2 2 0 1 1-2 2a2.002 2.002 0 0 1 2-2zm0-10a2 2 0 1 1-2 2a2.002 2.002 0 0 1 2-2zM6 18a2 2 0 1 1 2-2a2.002 2.002 0 0 1-2 2zm20 6a2 2 0 1 1-2 2a2.002 2.002 0 0 1 2-2z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "NetworkIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -10,7 +10,8 @@
|
||||
/>
|
||||
<path
|
||||
d="M25.233 25.7l.026-.052l.07-.139a1.278 1.278 0 0 0 .061-.7a2.11 2.11 0 0 0-.27-.724l-6.286-10.9l-.95-1.656h-.017l-.959 1.648l-6.277 10.9a2.18 2.18 0 0 0-.244.715a1.438 1.438 0 0 0 .148.942a1.563 1.563 0 0 0 1.482.7h11.708a1.79 1.79 0 0 0 1.508-.741zm-7.367-10.864L23.62 24.8H12.112z"
|
||||
fill="#2f495e"/>
|
||||
fill="#2f495e"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
|
||||
13
src/components/icons/PolyworkIcon.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<svg class="inline" width="1.5em" height="1.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M19.125 0H4.875A4.865 4.865 0 0 0 0 4.875v14.25C0 21.825 2.175 24 4.875 24h6.6c2.7 0 4.875-2.175 4.875-4.875V16.65h2.775c2.7 0 4.875-2.175 4.875-4.875v-6.9C24 2.175 21.825 0 19.125 0zM16.5 1.275h2.625a3.6 3.6 0 0 1 3.6 3.6v2.7H16.5v-6.3zM15.075 9v6.45H8.85V9h6.225zM8.85 1.2h6.225v6.375H8.85V1.2zM1.275 4.8a3.6 3.6 0 0 1 3.6-3.6H7.5v6.375H1.275V4.8zM7.5 9v6.45H1.2V9h6.3zm0 13.725H4.8a3.6 3.6 0 0 1-3.6-3.6V16.8h6.3v5.925zm7.575-3.525a3.6 3.6 0 0 1-3.6 3.6H8.85v-5.925h6.225V19.2zm7.65-7.35a3.6 3.6 0 0 1-3.6 3.6H16.5V9h6.225v2.85z"
|
||||
fill="currentColor" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "PolyworkIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<svg class="inline" width="1em" height="1 em" viewBox="0 0 24 24" focusable="false">
|
||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 24 24" focusable="false">
|
||||
<g
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
|
||||
18
src/components/icons/SupportIcon.vue
Normal file
@@ -0,0 +1,18 @@
|
||||
<template>
|
||||
<svg class="inline" width="2 rem" height="2rem" viewBox="0 0 24 24" focusable="false">
|
||||
<g fill="none">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M12.26 21.997a10.276 10.276 0 0 1-.52 0a10.004 10.004 0 0 1-8.983-6.173a10.034 10.034 0 0 1 .017-7.69A10.015 10.015 0 0 1 4.908 4.95l.042-.042a10.015 10.015 0 0 1 3.167-2.126a10.034 10.034 0 0 1 7.753-.006a10.015 10.015 0 0 1 3.186 2.138l.03.03c.913.917 1.65 2.01 2.153 3.223a10.012 10.012 0 0 1 .76 3.985a10.004 10.004 0 0 1-6.226 9.112a10.013 10.013 0 0 1-3.512.733zm1.772-6.55l2.874 2.873a8.004 8.004 0 0 1-9.812 0l2.874-2.874a4.007 4.007 0 0 0 4.064 0zm-5.478-1.415L5.68 16.906a8.004 8.004 0 0 1 0-9.812l2.874 2.874a4.007 4.007 0 0 0 0 4.064zm1.528-1.463a2.01 2.01 0 0 1-.014-1.087a1.99 1.99 0 0 1 .518-.896a1.99 1.99 0 0 1 1.932-.518c.328.088.639.26.896.518a1.99 1.99 0 0 1 .518 1.932c-.088.328-.26.639-.518.896a1.99 1.99 0 0 1-1.932.518a1.991 1.991 0 0 1-.896-.518a1.99 1.99 0 0 1-.504-.845zm3.95-4.015a4.007 4.007 0 0 0-4.064 0L7.094 5.68a8.004 8.004 0 0 1 9.812 0l-2.874 2.874zm4.288 8.352a8.004 8.004 0 0 0 0-9.812l-2.874 2.874a4.007 4.007 0 0 1 0 4.064l2.874 2.874z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "SupportIcon"
|
||||
}
|
||||
</script>
|
||||
@@ -1,14 +1,40 @@
|
||||
<template>
|
||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 24 24" focusable="false">
|
||||
<svg v-if="french" class="inline" width="1.5em" height="1.5em" viewBox="0 0 36 36" focusable="false">
|
||||
<path
|
||||
d="M12.87 15.07l-2.54-2.51l.03-.03A17.52 17.52 0 0 0 14.07 6H17V4h-7V2H8v2H1v1.99h11.17C11.5 7.92 10.44 9.75 9 11.35C8.07 10.32 7.3 9.19 6.69 8h-2c.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5l3.11 3.11l.76-2.04zM18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12zm-2.62 7l1.62-4.33L19.12 17h-3.24z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
fill="#00247D"
|
||||
d="M0 9.059V13h5.628zM4.664 31H13v-5.837zM23 25.164V31h8.335zM0 23v3.941L5.63 23zM31.337 5H23v5.837zM36 26.942V23h-5.631zM36 13V9.059L30.371 13zM13 5H4.664L13 10.837z" />
|
||||
<path
|
||||
fill="#CF1B2B"
|
||||
d="M25.14 23l9.712 6.801a3.977 3.977 0 0 0 .99-1.749L28.627 23H25.14zM13 23h-2.141l-9.711 6.8c.521.53 1.189.909 1.938 1.085L13 23.943V23zm10-10h2.141l9.711-6.8a3.988 3.988 0 0 0-1.937-1.085L23 12.057V13zm-12.141 0L1.148 6.2a3.994 3.994 0 0 0-.991 1.749L7.372 13h3.487z" />
|
||||
<path
|
||||
fill="#EEE"
|
||||
d="M36 21H21v10h2v-5.836L31.335 31H32a3.99 3.99 0 0 0 2.852-1.199L25.14 23h3.487l7.215 5.052c.093-.337.158-.686.158-1.052v-.058L30.369 23H36v-2zM0 21v2h5.63L0 26.941V27c0 1.091.439 2.078 1.148 2.8l9.711-6.8H13v.943l-9.914 6.941c.294.07.598.116.914.116h.664L13 25.163V31h2V21H0zM36 9a3.983 3.983 0 0 0-1.148-2.8L25.141 13H23v-.943l9.915-6.942A4.001 4.001 0 0 0 32 5h-.663L23 10.837V5h-2v10h15v-2h-5.629L36 9.059V9zM13 5v5.837L4.664 5H4a3.985 3.985 0 0 0-2.852 1.2l9.711 6.8H7.372L.157 7.949A3.968 3.968 0 0 0 0 9v.059L5.628 13H0v2h15V5h-2z" />
|
||||
<path
|
||||
fill="#CF1B2B"
|
||||
d="M21 15V5h-6v10H0v6h15v10h6V21h15v-6z" />
|
||||
</svg>
|
||||
<svg v-else class="inline" width="1.5em" height="1.5em" viewBox="0 0 36 36" focusable="false">
|
||||
<path
|
||||
fill="#ED2939"
|
||||
d="M36 27a4 4 0 0 1-4 4h-8V5h8a4 4 0 0 1 4 4v18z" />
|
||||
<path
|
||||
fill="#002495"
|
||||
d="M4 5a4 4 0 0 0-4 4v18a4 4 0 0 0 4 4h8V5H4z" />
|
||||
<path
|
||||
fill="#EEE"
|
||||
d="M12 5h12v26H12z" />
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "TranslateIcon"
|
||||
name: "TranslateIcon",
|
||||
props: {
|
||||
french: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
<template>
|
||||
<svg class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<svg v-if="active" class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M15.71 12.71a6 6 0 1 0-7.42 0a10 10 0 0 0-6.22 8.18a1 1 0 0 0 2 .22a8 8 0 0 1 15.9 0a1 1 0 0 0 1 .89h.11a1 1 0 0 0 .88-1.1a10 10 0 0 0-6.25-8.19zM12 12a4 4 0 1 1 4-4a4 4 0 0 1-4 4z"
|
||||
stroke="currentColor"
|
||||
:class="{active: 'fill-black dark:fill-white'}"
|
||||
d="M20 22H4v-2a5 5 0 0 1 5-5h6a5 5 0 0 1 5 5v2zm-8-9a6 6 0 1 1 0-12a6 6 0 0 1 0 12z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<svg v-else class="inline" width="2.5em" height="2.5em" viewBox="0 0 24 24" focusable="false">
|
||||
<path
|
||||
d="M20 22h-2v-2a3 3 0 0 0-3-3H9a3 3 0 0 0-3 3v2H4v-2a5 5 0 0 1 5-5h6a5 5 0 0 1 5 5v2zm-8-9a6 6 0 1 1 0-12a6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8a4 4 0 0 0 0 8z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
slug: how-i-start
|
||||
title: How did I start development?
|
||||
description: How did I start development? What am I capable of mastering? What will my future be?
|
||||
reading_time: 5
|
||||
tags: ["tags.life"]
|
||||
cover: post-2.png
|
||||
date: 07-01-2021
|
||||
---
|
||||
|
||||
## Presentation
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
slug: new-version-new-website
|
||||
title: Opening of the new version of my site!
|
||||
description: After long months of development, here is version 2 of my site!
|
||||
reading_time: 2
|
||||
tags: ["tags.dev"]
|
||||
cover: post-1.png
|
||||
date: 13-12-2020
|
||||
---
|
||||
|
||||
## Presentation
|
||||
@@ -1,34 +0,0 @@
|
||||
[
|
||||
{
|
||||
"slug": "erisium",
|
||||
"title": "experiences.erisium",
|
||||
"company": "Erisium",
|
||||
"location": "Remote",
|
||||
"begin_date": "02-2019",
|
||||
"end_date": "11-2020"
|
||||
},
|
||||
{
|
||||
"slug": "freelance",
|
||||
"title": "experiences.freelance",
|
||||
"company": "ArtDanjProduction",
|
||||
"location": "France",
|
||||
"begin_date": "09-2015",
|
||||
"end_date": "Today"
|
||||
},
|
||||
{
|
||||
"slug": "idemia",
|
||||
"title": "experiences.idemia",
|
||||
"company": "Idemia, Courbevoie",
|
||||
"location": "France",
|
||||
"begin_date": "06-2019",
|
||||
"end_date": "06-2019"
|
||||
},
|
||||
{
|
||||
"slug": "la-salle-a-manger",
|
||||
"title": "experiences.lsam",
|
||||
"company": "La Salle à manger, Boulogne Billancourt",
|
||||
"location": "France",
|
||||
"begin_date": "04-2018",
|
||||
"end_date": "04-2018"
|
||||
}
|
||||
]
|
||||
@@ -1,26 +0,0 @@
|
||||
[
|
||||
{
|
||||
"slug": "freelance",
|
||||
"title": "formations.freelance.title",
|
||||
"description": "formations.freelance.description",
|
||||
"location": "Remote",
|
||||
"begin_date": "04-2015",
|
||||
"end_date": "Today"
|
||||
},
|
||||
{
|
||||
"slug": "dnb",
|
||||
"title": "formations.dnb.title",
|
||||
"description": "formations.dnb.description",
|
||||
"location": "France",
|
||||
"begin_date": "09-2017",
|
||||
"end_date": "09-2018"
|
||||
},
|
||||
{
|
||||
"slug": "bac",
|
||||
"title": "formations.bac.title",
|
||||
"description": "formations.bac.description",
|
||||
"location": "France",
|
||||
"begin_date": "09-2020",
|
||||
"end_date": "Today"
|
||||
}
|
||||
]
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
slug: how-i-start
|
||||
title: Comment ai-je commencé le développement ?
|
||||
description: Comment ai-je commencé le développement ? Que suis-je capable de maitriser ? Quel sera mon futur ?
|
||||
reading_time: 5
|
||||
tags: ["tags.life"]
|
||||
cover: post-2.png
|
||||
date: 07-01-2021
|
||||
---
|
||||
|
||||
## Présentation
|
||||
@@ -1,11 +1,5 @@
|
||||
---
|
||||
slug: new-version-new-website
|
||||
title: Ouverture de la nouvelle version de mon site !
|
||||
description: Après de long mois de développement, voici la version 2 de mon site !
|
||||
reading_time: 2
|
||||
tags: ["tags.dev"]
|
||||
cover: post-1.png
|
||||
date: 13-12-2020
|
||||
---
|
||||
|
||||
## Présentation
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"age": 18,
|
||||
"hiring": {
|
||||
"status": "available",
|
||||
"color": "green"
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
[
|
||||
{
|
||||
"slug": "erisium",
|
||||
"title": "Erisium",
|
||||
"description": "works.erisium",
|
||||
"url": "https://erisium.com",
|
||||
"cover": "erisium.png",
|
||||
"tags": ["tags.mc"]
|
||||
},
|
||||
{
|
||||
"slug": "artsite",
|
||||
"title": "ArtSite",
|
||||
"description": "works.artsite",
|
||||
"url": "https://arthurdanjou.fr",
|
||||
"cover": "artsite.png",
|
||||
"tags": ["tags.web"]
|
||||
},
|
||||
{
|
||||
"slug": "artapi",
|
||||
"title": "ArtApi",
|
||||
"description": "works.artapi",
|
||||
"url": "https://api.arthurdanjou.fr",
|
||||
"cover": "artapi.png",
|
||||
"tags": ["tags.api"]
|
||||
},
|
||||
{
|
||||
"slug": "linkyjs",
|
||||
"title": "LinkyJs",
|
||||
"description": "works.linkyjs",
|
||||
"url": "https://github.com/linkyjs/",
|
||||
"cover": "linkyjs.png",
|
||||
"tags": ["tags.software", "tags.opensource"]
|
||||
}
|
||||
]
|
||||
@@ -1,122 +0,0 @@
|
||||
[
|
||||
{
|
||||
"title": "Java",
|
||||
"color": "blueGray",
|
||||
"cover": "Java.png",
|
||||
"slug": "java"
|
||||
},
|
||||
{
|
||||
"title": "TypeScript",
|
||||
"color": "cyan",
|
||||
"cover": "TypeScript.png",
|
||||
"slug": "typescript"
|
||||
},
|
||||
{
|
||||
"title": "JavaScript",
|
||||
"color": "yellow",
|
||||
"cover": "JavaScript.png",
|
||||
"slug": "javascript"
|
||||
},
|
||||
{
|
||||
"title": "Go",
|
||||
"color": "cyan",
|
||||
"cover": "GoLang.png",
|
||||
"slug": "golang"
|
||||
},
|
||||
{
|
||||
"title": "NuxtJs",
|
||||
"color": "emerald",
|
||||
"cover": "NuxtJs.png",
|
||||
"slug": "nuxtjs"
|
||||
},
|
||||
{
|
||||
"title": "VueJs",
|
||||
"color": "emerald",
|
||||
"cover": "VueJs.png",
|
||||
"slug": "vuejs"
|
||||
},
|
||||
{
|
||||
"title": "AdonisJs",
|
||||
"color": "purple",
|
||||
"cover": "AdonisJs.png",
|
||||
"slug": "adonisjs"
|
||||
},
|
||||
{
|
||||
"title": "ViteJs",
|
||||
"color": "amber",
|
||||
"cover": "Vitejs.png",
|
||||
"slug": "vitejs"
|
||||
},
|
||||
{
|
||||
"title": "WindiCss",
|
||||
"color": "lightBlue",
|
||||
"cover": "Windicss.png",
|
||||
"slug": "windicss"
|
||||
},
|
||||
{
|
||||
"title": "Html",
|
||||
"color": "orange",
|
||||
"cover": "Html.png",
|
||||
"slug": "html"
|
||||
},
|
||||
{
|
||||
"title": "Css",
|
||||
"color": "lightBlue",
|
||||
"cover": "Css.png",
|
||||
"slug": "css"
|
||||
},
|
||||
{
|
||||
"title": "NodeJs",
|
||||
"color": "lime",
|
||||
"cover": "NodeJs.png",
|
||||
"slug": "nodejs"
|
||||
},
|
||||
{
|
||||
"title": "ElectronJs",
|
||||
"color": "teal",
|
||||
"cover": "ElectronJs.png",
|
||||
"slug": "electronjs"
|
||||
},
|
||||
{
|
||||
"title": "Git",
|
||||
"color": "orange",
|
||||
"cover": "Git.png",
|
||||
"slug": "git"
|
||||
},
|
||||
{
|
||||
"title": "MariaDB",
|
||||
"color": "blue",
|
||||
"cover": "MariaDB.png",
|
||||
"slug": "mariadb"
|
||||
},
|
||||
{
|
||||
"title": "RabbitMQ",
|
||||
"color": "orange",
|
||||
"cover": "RabbitMQ.png",
|
||||
"slug": "rabbitmq"
|
||||
},
|
||||
{
|
||||
"title": "Redis",
|
||||
"color": "red",
|
||||
"cover": "Redis.png",
|
||||
"slug": "redis"
|
||||
},
|
||||
{
|
||||
"title": "Sass",
|
||||
"color": "rose",
|
||||
"cover": "Sass.png",
|
||||
"slug": "sass"
|
||||
},
|
||||
{
|
||||
"title": "TailwindCss",
|
||||
"color": "teal",
|
||||
"cover": "TailwindCss.png",
|
||||
"slug": "tailwindcss"
|
||||
},
|
||||
{
|
||||
"title": "Docker",
|
||||
"color": "cyan",
|
||||
"cover": "Docker.png",
|
||||
"slug": "docker"
|
||||
}
|
||||
]
|
||||
@@ -1,11 +0,0 @@
|
||||
[
|
||||
{
|
||||
"slug": "tags.dev"
|
||||
},
|
||||
{
|
||||
"slug": "tags.tech"
|
||||
},
|
||||
{
|
||||
"slug": "tags.life"
|
||||
}
|
||||
]
|
||||
@@ -1,7 +1,54 @@
|
||||
<template>
|
||||
<div>
|
||||
<Header />
|
||||
<Nuxt class="z-10 pt-16 lg:pt-24 content"/>
|
||||
<Footer />
|
||||
<SideMenu />
|
||||
<div class="w-full overflow-hidden xl:overflow-visible">
|
||||
<div
|
||||
id="slider"
|
||||
class="xl:static shadow-2xl transition-all duration-500 overflow-hidden xl:overflow-visible"
|
||||
:class="{'transform scale-90 md:scale-70 lg:scale-60 xl:scale-100 rounded-lg xl:rounded-none translate-x-10/12 sm:translate-x-4/12 lg:translate-x-3/12 xl:translate-x-0': opened}"
|
||||
:tabindex="opened ? -1 : 0"
|
||||
>
|
||||
<div
|
||||
@click="closeMenu"
|
||||
class="min-h-screen relative z-50 bg-white dark:bg-black w-full min-w-screen pb-20 xl:pb-0 duration-300"
|
||||
:class="{'cursor-pointer': opened}"
|
||||
>
|
||||
<Announcement />
|
||||
<Header />
|
||||
<Nuxt class="z-10 pt-16 content"/>
|
||||
<Footer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<MobileNavbar />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import {computed, useRouter, useStore} from "@nuxtjs/composition-api";
|
||||
import {State} from "~/types/types";
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const store = useStore<State>()
|
||||
const $router = useRouter()
|
||||
|
||||
$router.afterEach(() => {
|
||||
store.commit('SET_ROUTE', $router.currentRoute.path)
|
||||
})
|
||||
|
||||
const closeMenu = () => {
|
||||
store.commit('TOGGLE_OPENED', false)
|
||||
document.getElementById('nav')!.classList.remove('z-50')
|
||||
setTimeout(() => {
|
||||
document.getElementById('slider')!.style.maxHeight = 'none'
|
||||
}, 500)
|
||||
}
|
||||
|
||||
return {
|
||||
opened: computed(() => store.state.opened),
|
||||
closeMenu,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||