feat: add contact form, links and project page

This commit is contained in:
Matthieu Bessat 2022-07-07 00:03:47 +02:00
parent 998f3f9127
commit 03414ebfc6
80 changed files with 4074 additions and 363 deletions

0
.phpactor.yml Normal file
View file

View file

@ -1,4 +1,15 @@
## Structure
- Tech wall
- Pro projects
- Side projects
- Find me on
- Volunteering
- Contact form
- Reading list
## Technology walls
Mosaic of squared technologies logos

43
assets/locales/en.yaml Normal file
View file

@ -0,0 +1,43 @@
external-website: External website
last-update: "Last update :"
about-website: "About this website"
page:
title: Matthieu Bessat - Web developer
profile:
intro:
main: Hi! I'm Matthieu
secondary: I like to program stuff
technologies:
title: Some of the technologies that I'm currently working with
projects:
pro:
title: Highlighted professional projects
side:
title: Highlighted side projects
links:
title: Find me on...
contact:
no-script: If you want to use the contact form, you will need to enable javascript to run free-ish software
title: Contact me
use-email: "You can contact me via e-mail:"
use-form: "Or you can use the form bellow if you prefer not opening your email client."
form:
name: Your name
email: Your email
subject: The subject
message: Your message
submit: Submit
sucess: Thanks, I will try to read pursly your message
fail: Failure
locales:
choose: "Choose your language:"

47
assets/locales/fr.yaml Normal file
View file

@ -0,0 +1,47 @@
go-back-to-main: Revenir à la page principale
external-website: Site web externe
last-update: Dernière mise à jour le
about-website: A propos de ce site
page:
title: Matthieu Bessat - Développeur web
profile:
intro:
main: Salut, je m'appelle Matthieu
secondary: J'aime bien programmer des trucs
technologies:
title: Quelques des technologies avec lesquelles je travaille en ce moment
projects:
title: Projets
project: Projet
pro:
title: Projets professionels
side:
title: Autres projets
background: Contexte
solution: Solutions
technologies: Technologies utilisées
links:
title: Retrouvez moi sur...
contact:
no-script: Si vous voulez utiliser le formulaire de contact, vous devez activer le javascript (le site propose un code libre)
title: Me contacter
use-email: "Vous pouvez me contacter par e-mail :"
use-form: "Ou vous pouvez utiliser le formulaire ci-dessous"
form:
name: Votre nom
email: Votre email
subject: Sujet du message
message: Votre message
submit: Soumettre
locales:
choose: "Choisissez votre langue : "

52
assets/scripts/contact.js Normal file
View file

@ -0,0 +1,52 @@
import axios from 'axios'
function contactFormSetup() {
/**
* Contact form javascript implementation
*/
const namespace = "lefuturiste"
const postURL = "https://contact-form.thingmill.fr"
const form = document.getElementById('contact-form')
const formName = document.getElementById('contact-form-name')
const formEmail = document.getElementById('contact-form-email')
const formSubject = document.getElementById('contact-form-subject')
const formMessage = document.getElementById('contact-form-message')
const formSubmit = document.getElementById('contact-form-submit')
const formButtonLoading = document.getElementById('contact-form-loading')
const successAlert = document.getElementById('contact-alert-success')
const errorAlert = document.getElementById('contact-alert-error')
form.onsubmit = (e) => {
e.preventDefault()
console.log('> Attempting to send a message', formName.value, formEmail.value, formMessage.value)
let savedButtonText = formSubmit.textContent;
formButtonLoading.style.display = 'block'
formSubmit.style.display = 'none'
axios.post(postURL + '/' + namespace, {
name: formName.value,
email: formEmail.value,
subject: formSubject.value,
message: formMessage.value
}).then(() => {
console.log('> Message sent')
successAlert.style.display = "block";
formButtonLoading.style.display = 'none'
formSubmit.style.display = 'block'
formSubmit.textContent = savedButtonText;
// reset the form?
// formName.value = ''
// formEmail.value = ''
// formSubject.value = ''
// formMessage.value = ''
}).catch((err) => {
errorAlert.style.display = "block";
console.error(err)
})
}
}
export { contactFormSetup }

View file

@ -0,0 +1,6 @@
import axios from 'axios'
import { contactFormSetup } from './contact'
contactFormSetup()

View file

@ -0,0 +1,8 @@
.btn {
border-radius: 2px;
box-shadow: 1px 2px 4px 0 rgba(0,0,0,.08);
padding: 1em 1.5em;
margin-top: 1em;
border: 1px solid #ccc;
font-size: 16px;
}

View file

@ -0,0 +1,13 @@
.chips {
display: flex;
flex-wrap: wrap;
.chip {
margin-right: .5em;
padding: 0.5em 0.9em;
background: $primary;
color: white;
font-size: .9em;
border-radius: 10rem;
}
}

View file

@ -0,0 +1,125 @@
.means-of-contact {
p {
margin-bottom: 1em;
}
}
.contact-form-container {
form {
border-radius: 3px;
border: 1px dashed #80808054;
padding: 1em;
}
.contact-form-first-group {
display: flex;
.contact-form-input:first-of-type {
margin-right: .4em;
}
.contact-form-input:nth-of-type(2) {
margin-left: .4em;
}
}
.contact-form-input {
width: 100%;
margin-top: 1em;
}
input, textarea {
width: 100%;
margin-top: .6em;
border-radius: 3px;
padding: .6em;
border: 1px solid #dbdbdb;
box-shadow: 1px 2px 4px 0 rgba(0,0,0,.1);
}
.contact-form-message textarea {
min-height: 8em;
}
label {
display: block;
}
@keyframes rotating {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.loading-button {
display: none;
svg {
animation: rotating 2s linear infinite;
}
}
.contact-form-submit {
}
.contact-form-container {
display: flex;
justify-content: end;
}
}
.alert {
padding: 1em;
margin-bottom: 1em;
border-radius: 3px;
.alert-content {
display: flex;
align-items: center;
}
}
.alert.alert-success {
background: green;
color: white;
}
.alert.alert-error {
background: red;
color: white;
}
.alert .alert-icon {
padding-right: 0.6em;
}
@media (min-width: $sm-breakpoint) {
.contact-form-first-group {
.contact-form-input {
margin-top: 0;
}
}
}
@media (max-width: $sm-breakpoint) {
.contact-form {
.contact-form-first-group {
display: block;
.contact-form-input {
margin-left: 0 !important;
margin-right: 0 !important;
}
}
}
.contact-form-input:first-of-type {
margin-top: 0;
}
}

View file

@ -0,0 +1,46 @@
.footer-buffer {
height: 2em;
}
footer {
margin-top: auto;
border-top: 1px solid gray;
border-color: rgb(84, 91, 94) !important;
}
.footer-container {
padding: 2em 0;
display: flex;
justify-content: space-between;
}
.footer-left div {
margin-bottom: .5em;
}
.locale-switch-large {
display: flex;
align-items: center;
.l {
margin-right: 1em;
}
.r {
a {
margin-right: .2em;
}
}
}
.locale-switch-short {
display: none;
}
@media (max-width: $sm-breakpoint) {
.locale-switch-large {
display: none;
}
.locale-switch-short {
display: block;
}
}

View file

@ -0,0 +1,57 @@
.links-container {
display: flex;
justify-content: center;
}
.links {
width: 80%;
display: flex;
justify-content: space-around;
flex-wrap: wrap;
.link-card {
margin: 1em;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
min-width: 10em;
}
.link-card-title {
margin-top: .9em;
}
.link-card-logo {
padding: .5em;
width: 4em;
height: 4em;
overflow: hidden;
display: flex;
justify-content: center;
align-content: center;
img {
max-width: 4em;
max-height: 4em;
}
}
}
@media (max-width: $sm-breakpoint) {
.links {
width: 100%;
.link-card {
justify-content: center;
}
}
}
@media (max-width: $xs-breakpoint) {
.links {
width: 100%;
.link-card {
min-width: 6em;
}
}
}

View file

@ -95,16 +95,66 @@ img.main-profile {
.profile-content .title-1 {
text-align: center;
font-size: 3.5em;
font-size: 3em;
margin-bottom: 0.4em;
line-height: 1.5em;
}
.profile-content .title-2 {
text-align: center;
font-size: 2em;
font-size: 1.5em;
margin-bottom: 1em;
}
.profile-content .subtitle {
font-size: 1.5em;
}
@media (max-width: $sm-breakpoint) {
.profile-container {
flex-direction: column;
}
.profile-imgs-container {
margin-right: 0;
}
.profile-content {
margin-left: 0;
margin-top: -1em;
.title-1 {
font-size: 2.5em;
}
.title-2 {
font-size: 1.2em;
}
}
.profile-imgs-container {
transform: scale(0.7);
margin-top: -4em;
}
}
@media (max-width: $xs-breakpoint) {
.landing-section {
padding-top: 0;
}
.profile-content {
margin-top: -4em;
padding: 0 1em;
.title-1 {
font-size: 2.5em;
}
.title-2 {
font-size: 1.2em;
}
}
.profile-imgs-container {
transform: scale(0.6);
margin-top: -4em;
}
}

View file

@ -1,14 +1,25 @@
.projects {
display: flex;
flex-wrap: wrap;
display: grid;
grid-template-columns: 1fr 1fr;
grid-gap: 1em;
.project-card {
width: 20em;
margin-right: .5em;
margin-bottom: .5em;
border: 1px solid gray;
padding: 1.5em;
border-radius: 5px;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.project-top {
display: flex;
}
.project-left {
width: 100%;
}
.project-title {
@ -19,4 +30,40 @@
font-size: .9em;
color: gray;
}
}
.project-img {
width: 5em;
}
.project-right {
padding-left: .5em;
}
.project-bottom {
padding-top: 1em;
}
}
@media(min-width: $lg-breakpoint) {
.projects {
grid-template-columns: 1fr 1fr 1fr;
}
}
@media(max-width: $md-breakpoint) {
.projects {
grid-template-columns: 1fr 1fr;
}
}
@media (max-width: $sm-breakpoint) {
.projects {
grid-template-columns: 1fr;
}
.project-right {
padding-left: 1em !important;
}
}

View file

@ -37,6 +37,7 @@
img {
width: 100%;
}
padding: 2em;
}
.item-bg img.img-stretch {
width: auto;
@ -70,7 +71,13 @@
}
@media (max-width: 1300px) {
@media (min-width: 400px) {
.item-bg {
padding: 1em !important;
}
}
@media (max-width: 1000px) {
.tech-mosaic {
.item {
flex-basis: percentage(math.div(1, 5));
@ -78,14 +85,14 @@
}
}
@media (max-width: 900px) {
@media (max-width: 800px) {
.tech-mosaic {
.item {
flex-basis: percentage(math.div(1, 3));
flex-basis: percentage(math.div(1, 4));
}
}
}
@media (max-width: 500px) {
@media (max-width: 400px) {
.tech-mosaic {
.item {
flex-basis: percentage(math.div(1, 2));

View file

@ -1,3 +1,23 @@
p {
line-height: 1.45em;
margin-bottom: 1em;
}
h1 {
font-size: xx-large;
margin-bottom: 1em;
}
h2 {
font-size: x-large;
margin-bottom: .5em;
}
h3 {
font-size: large;
}
.section-title {
display: grid;
font-size: 1.2em;

View file

@ -1,5 +1,10 @@
@import url('https://fonts.googleapis.com/css2?family=Libre+Baskerville&display=swap');
$lg-breakpoint: 1500px;
$md-breakpoint: 1000px;
$sm-breakpoint: 900px;
$xs-breakpoint: 400px;
$primary: #4ba05f;
$secondary: #a04b8c;
@ -7,10 +12,21 @@ $secondary: #a04b8c;
@import './components/profile.scss';
@import './components/projects.scss';
@import './components/technologies.scss';
@import './components/links.scss';
@import './components/typography.scss';
@import './components/footer.scss';
@import './components/contact.scss';
@import './components/button.scss';
@import './components/chips.scss';
p {
line-height: 1.2em;
body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
.hidden {
display: none;
}
.container {
@ -25,7 +41,45 @@ p {
font-family: 'Libre Baskerville', serif;
}
section {
border-bottom: 1px solid gray;
padding-bottom: 1em;
}
section:last-of-type {
border-bottom: 0;
}
.about-header {
margin: 2em 0;
}
@media (min-width: $lg-breakpoint) {
.container {
width: 58%;
}
}
@media (min-width: $md-breakpoint) {
.container {
width: 65%;
}
}
@media (max-width: $md-breakpoint) {
.container {
width: 75%;
}
}
@media (max-width: $sm-breakpoint) {
.container {
width: 90%;
}
}
@media (max-width: $xs-breakpoint) {
}

View file

@ -5,7 +5,9 @@
"slim/twig-view": "^3.3",
"symfony/yaml": "^6.0",
"php-di/slim-bridge": "^3.2",
"symfony/var-dumper": "^6.0"
"symfony/var-dumper": "^6.0",
"adbario/php-dot-notation": "^3.1",
"boronczyk/localization-middleware": "^2.0"
},
"autoload": {
"psr-4": {

376
composer.lock generated
View file

@ -4,8 +4,114 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "5b64e03d031244562c5d143e11e9aa3e",
"content-hash": "cec518bea90ee444e31994b065bc4f79",
"packages": [
{
"name": "adbario/php-dot-notation",
"version": "3.1.1",
"source": {
"type": "git",
"url": "https://github.com/adbario/php-dot-notation.git",
"reference": "aca06bf775809808232b8c21ce1ca3a4fca70ace"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/adbario/php-dot-notation/zipball/aca06bf775809808232b8c21ce1ca3a4fca70ace",
"reference": "aca06bf775809808232b8c21ce1ca3a4fca70ace",
"shasum": ""
},
"require": {
"ext-json": "*",
"php": "^7.4 || ^8.0"
},
"require-dev": {
"phpstan/phpstan": "^1.5",
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.6"
},
"type": "library",
"autoload": {
"files": [
"src/helpers.php"
],
"psr-4": {
"Adbar\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Riku Särkinen",
"email": "riku@adbar.io"
}
],
"description": "PHP dot notation access to arrays",
"homepage": "https://github.com/adbario/php-dot-notation",
"keywords": [
"ArrayAccess",
"dotnotation"
],
"support": {
"issues": "https://github.com/adbario/php-dot-notation/issues",
"source": "https://github.com/adbario/php-dot-notation/tree/3.1.1"
},
"time": "2022-03-28T01:09:30+00:00"
},
{
"name": "boronczyk/localization-middleware",
"version": "2.0.1",
"source": {
"type": "git",
"url": "https://github.com/tboronczyk/localization-middleware.git",
"reference": "c2370e2631351ee35e21095db258a939fdb61368"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/tboronczyk/localization-middleware/zipball/c2370e2631351ee35e21095db258a939fdb61368",
"reference": "c2370e2631351ee35e21095db258a939fdb61368",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0",
"psr/http-message": "^1.0",
"psr/http-server-middleware": "^1.0"
},
"require-dev": {
"phpunit/phpunit": "^9.4",
"slim/psr7": "^1.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Boronczyk\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Timothy Boronczyk",
"email": "tboronczyk@gmail.com"
}
],
"description": "PSR-15 middleware to assist primarily with language-based content negotiation and various other localization tasks.",
"homepage": "https://github.com/tboronczyk/localization-middleware",
"keywords": [
"localization",
"psr-15",
"slim"
],
"support": {
"issues": "https://github.com/tboronczyk/localization-middleware/issues",
"source": "https://github.com/tboronczyk/localization-middleware/tree/2.0.1"
},
"time": "2021-04-15T02:46:25+00:00"
},
{
"name": "fig/http-message-util",
"version": "1.1.5",
@ -62,6 +168,65 @@
},
"time": "2020-11-24T22:02:12+00:00"
},
{
"name": "laravel/serializable-closure",
"version": "v1.2.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "09f0e9fb61829f628205b7c94906c28740ff9540"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/09f0e9fb61829f628205b7c94906c28740ff9540",
"reference": "09f0e9fb61829f628205b7c94906c28740ff9540",
"shasum": ""
},
"require": {
"php": "^7.3|^8.0"
},
"require-dev": {
"pestphp/pest": "^1.18",
"phpstan/phpstan": "^0.12.98",
"symfony/var-dumper": "^5.3"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.x-dev"
}
},
"autoload": {
"psr-4": {
"Laravel\\SerializableClosure\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Taylor Otwell",
"email": "taylor@laravel.com"
},
{
"name": "Nuno Maduro",
"email": "nuno@laravel.com"
}
],
"description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
"keywords": [
"closure",
"laravel",
"serializable"
],
"support": {
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2022-05-16T17:09:47+00:00"
},
{
"name": "nikic/fast-route",
"version": "v1.3.0",
@ -112,71 +277,6 @@
},
"time": "2018-02-13T20:26:39+00:00"
},
{
"name": "opis/closure",
"version": "3.6.3",
"source": {
"type": "git",
"url": "https://github.com/opis/closure.git",
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad",
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad",
"shasum": ""
},
"require": {
"php": "^5.4 || ^7.0 || ^8.0"
},
"require-dev": {
"jeremeamia/superclosure": "^2.0",
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.6.x-dev"
}
},
"autoload": {
"files": [
"functions.php"
],
"psr-4": {
"Opis\\Closure\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Marius Sarca",
"email": "marius.sarca@gmail.com"
},
{
"name": "Sorin Sarca",
"email": "sarca_sorin@hotmail.com"
}
],
"description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
"homepage": "https://opis.io/closure",
"keywords": [
"anonymous functions",
"closure",
"function",
"serializable",
"serialization",
"serialize"
],
"support": {
"issues": "https://github.com/opis/closure/issues",
"source": "https://github.com/opis/closure/tree/3.6.3"
},
"time": "2022-01-27T09:35:39+00:00"
},
{
"name": "php-di/invoker",
"version": "2.3.3",
@ -234,21 +334,21 @@
},
{
"name": "php-di/php-di",
"version": "6.3.5",
"version": "6.4.0",
"source": {
"type": "git",
"url": "https://github.com/PHP-DI/PHP-DI.git",
"reference": "b8126d066ce144765300ee0ab040c1ed6c9ef588"
"reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/b8126d066ce144765300ee0ab040c1ed6c9ef588",
"reference": "b8126d066ce144765300ee0ab040c1ed6c9ef588",
"url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/ae0f1b3b03d8b29dff81747063cbfd6276246cc4",
"reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4",
"shasum": ""
},
"require": {
"opis/closure": "^3.5.5",
"php": ">=7.2.0",
"laravel/serializable-closure": "^1.0",
"php": ">=7.4.0",
"php-di/invoker": "^2.0",
"php-di/phpdoc-reader": "^2.0.1",
"psr/container": "^1.0"
@ -257,12 +357,12 @@
"psr/container-implementation": "^1.0"
},
"require-dev": {
"doctrine/annotations": "~1.2",
"doctrine/annotations": "~1.10",
"friendsofphp/php-cs-fixer": "^2.4",
"mnapoli/phpunit-easymock": "^1.2",
"ocramius/proxy-manager": "^2.0.2",
"ocramius/proxy-manager": "^2.11.2",
"phpstan/phpstan": "^0.12",
"phpunit/phpunit": "^8.5|^9.0"
"phpunit/phpunit": "^9.5"
},
"suggest": {
"doctrine/annotations": "Install it if you want to use annotations (version ~1.2)",
@ -294,7 +394,7 @@
],
"support": {
"issues": "https://github.com/PHP-DI/PHP-DI/issues",
"source": "https://github.com/PHP-DI/PHP-DI/tree/6.3.5"
"source": "https://github.com/PHP-DI/PHP-DI/tree/6.4.0"
},
"funding": [
{
@ -306,7 +406,7 @@
"type": "tidelift"
}
],
"time": "2021-09-02T09:49:58+00:00"
"time": "2022-04-09T16:46:38+00:00"
},
{
"name": "php-di/phpdoc-reader",
@ -839,22 +939,22 @@
},
{
"name": "slim/slim",
"version": "4.9.0",
"version": "4.10.0",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim.git",
"reference": "44d3c9c0bfcc47e52e42b097b6062689d21b904b"
"reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/44d3c9c0bfcc47e52e42b097b6062689d21b904b",
"reference": "44d3c9c0bfcc47e52e42b097b6062689d21b904b",
"url": "https://api.github.com/repos/slimphp/Slim/zipball/0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0",
"reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0",
"shasum": ""
},
"require": {
"ext-json": "*",
"nikic/fast-route": "^1.3",
"php": "^7.3 || ^8.0",
"php": "^7.4 || ^8.0",
"psr/container": "^1.0 || ^2.0",
"psr/http-factory": "^1.0",
"psr/http-message": "^1.0",
@ -865,13 +965,15 @@
"require-dev": {
"adriansuter/php-autoload-override": "^1.2",
"ext-simplexml": "*",
"guzzlehttp/psr7": "^2.0",
"guzzlehttp/psr7": "^2.1",
"httpsoft/http-message": "^1.0",
"httpsoft/http-server-request": "^1.0",
"laminas/laminas-diactoros": "^2.8",
"nyholm/psr7": "^1.4",
"nyholm/psr7": "^1.5",
"nyholm/psr7-server": "^1.0",
"phpspec/prophecy": "^1.14",
"phpspec/prophecy": "^1.15",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "^0.12.99",
"phpstan/phpstan": "^1.4",
"phpunit/phpunit": "^9.5",
"slim/http": "^1.2",
"slim/psr7": "^1.5",
@ -948,7 +1050,7 @@
"type": "tidelift"
}
],
"time": "2021-10-05T03:00:00+00:00"
"time": "2022-03-14T14:18:23+00:00"
},
{
"name": "slim/twig-view",
@ -1017,16 +1119,16 @@
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.25.0",
"version": "v1.26.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
"shasum": ""
},
"require": {
@ -1041,7 +1143,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
"dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1079,7 +1181,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
},
"funding": [
{
@ -1095,20 +1197,20 @@
"type": "tidelift"
}
],
"time": "2021-10-20T20:35:02+00:00"
"time": "2022-05-24T11:49:31+00:00"
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.25.0",
"version": "v1.26.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
"shasum": ""
},
"require": {
@ -1123,7 +1225,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
"dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1162,7 +1264,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
},
"funding": [
{
@ -1178,20 +1280,20 @@
"type": "tidelift"
}
],
"time": "2021-11-30T18:21:41+00:00"
"time": "2022-05-24T11:49:31+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.25.0",
"version": "v1.26.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c"
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c",
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
"shasum": ""
},
"require": {
@ -1200,7 +1302,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
"dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1245,7 +1347,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
},
"funding": [
{
@ -1261,20 +1363,20 @@
"type": "tidelift"
}
],
"time": "2022-03-04T08:16:47+00:00"
"time": "2022-05-10T07:21:04+00:00"
},
{
"name": "symfony/polyfill-php81",
"version": "v1.25.0",
"version": "v1.26.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f"
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
"reference": "5de4ba2d41b15f9bd0e19b2ab9674135813ec98f",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/13f6d1271c663dc5ae9fb843a8f16521db7687a1",
"reference": "13f6d1271c663dc5ae9fb843a8f16521db7687a1",
"shasum": ""
},
"require": {
@ -1283,7 +1385,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "1.23-dev"
"dev-main": "1.26-dev"
},
"thanks": {
"name": "symfony/polyfill",
@ -1324,7 +1426,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.25.0"
"source": "https://github.com/symfony/polyfill-php81/tree/v1.26.0"
},
"funding": [
{
@ -1340,24 +1442,24 @@
"type": "tidelift"
}
],
"time": "2021-09-13T13:58:11+00:00"
"time": "2022-05-24T11:49:31+00:00"
},
{
"name": "symfony/var-dumper",
"version": "v6.0.6",
"version": "v6.1.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/var-dumper.git",
"reference": "38358405ae948963c50a3aae3dfea598223ba15e"
"reference": "98587d939cb783aa04e828e8fa857edaca24c212"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/38358405ae948963c50a3aae3dfea598223ba15e",
"reference": "38358405ae948963c50a3aae3dfea598223ba15e",
"url": "https://api.github.com/repos/symfony/var-dumper/zipball/98587d939cb783aa04e828e8fa857edaca24c212",
"reference": "98587d939cb783aa04e828e8fa857edaca24c212",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"php": ">=8.1",
"symfony/polyfill-mbstring": "~1.0"
},
"conflict": {
@ -1412,7 +1514,7 @@
"dump"
],
"support": {
"source": "https://github.com/symfony/var-dumper/tree/v6.0.6"
"source": "https://github.com/symfony/var-dumper/tree/v6.1.0"
},
"funding": [
{
@ -1428,24 +1530,24 @@
"type": "tidelift"
}
],
"time": "2022-03-02T12:58:14+00:00"
"time": "2022-05-21T13:34:40+00:00"
},
{
"name": "symfony/yaml",
"version": "v6.0.3",
"version": "v6.1.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/yaml.git",
"reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5"
"reference": "b01c4e7dc6a51cbf114567af04a19789fd1011fe"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/yaml/zipball/e77f3ea0b21141d771d4a5655faa54f692b34af5",
"reference": "e77f3ea0b21141d771d4a5655faa54f692b34af5",
"url": "https://api.github.com/repos/symfony/yaml/zipball/b01c4e7dc6a51cbf114567af04a19789fd1011fe",
"reference": "b01c4e7dc6a51cbf114567af04a19789fd1011fe",
"shasum": ""
},
"require": {
"php": ">=8.0.2",
"php": ">=8.1",
"symfony/polyfill-ctype": "^1.8"
},
"conflict": {
@ -1486,7 +1588,7 @@
"description": "Loads and dumps YAML files",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/yaml/tree/v6.0.3"
"source": "https://github.com/symfony/yaml/tree/v6.1.2"
},
"funding": [
{
@ -1502,20 +1604,20 @@
"type": "tidelift"
}
],
"time": "2022-01-26T17:23:29+00:00"
"time": "2022-06-20T12:01:07+00:00"
},
{
"name": "twig/twig",
"version": "v3.3.8",
"version": "v3.4.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
"reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/e939eae92386b69b49cfa4599dd9bead6bf4a342",
"reference": "e939eae92386b69b49cfa4599dd9bead6bf4a342",
"shasum": ""
},
"require": {
@ -1530,7 +1632,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "3.3-dev"
"dev-master": "3.4-dev"
}
},
"autoload": {
@ -1566,7 +1668,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
"source": "https://github.com/twigphp/Twig/tree/v3.4.1"
},
"funding": [
{
@ -1578,7 +1680,7 @@
"type": "tidelift"
}
],
"time": "2022-02-04T06:59:48+00:00"
"time": "2022-05-17T05:48:52+00:00"
}
],
"packages-dev": [],
@ -1589,5 +1691,5 @@
"prefer-lowest": false,
"platform": [],
"platform-dev": [],
"plugin-api-version": "2.2.0"
"plugin-api-version": "2.3.0"
}

View file

@ -1,16 +0,0 @@
links:
youtube: https://www.youtube.com/channel/UC0x-gNbsyyrC69HjT5Z44MQ
twitter: https://twitter.com/_le_futuriste
github: https://github.com/lefuturiste
gitlab: https://gitlab.com/lefuturiste
malt: https://www.malt.fr/profile/matthieubessat
discord_guild: https://discord.com/invite/3W94m7ts3H
soundcloud: https://soundcloud.com/lefuturiste
stackoverflow: https://stackoverflow.com/users/7100565/lefuturiste
osm: https://openstreetmap.org/user/lefuturiste
osm_wiki: https://wiki.openstreetmap.org/wiki/User:Lefuturiste
wikipedia_fr: https://fr.wikipedia.org/wiki/Utilisateur:Matthieu2743
wikidata: https://www.wikidata.org/wiki/User:Matthieu2743
mastodon: https://mstdn.io/users/lefuturiste

58
config/links.yaml Normal file
View file

@ -0,0 +1,58 @@
links:
- id: mastodon
name: Mastodon
url: https://mstdn.io/users/lefuturiste
# - id: youtube
# name: YouTube
# url: https://www.youtube.com/channel/UC0x-gNbsyyrC69HjT5Z44MQ
# - id: twitter
# name: Twitter
# url: https://twitter.com/_le_futuriste
- id: github
name: GitHub
url: https://github.com/lefuturiste
- id: gitlab
name: GitLab
url: https://gitlab.com/lefuturiste
thumbnail:
src: gitlab.svg
alt: Gitlab logo
style: "transform: scale(1.5)"
- id: malt
name: Malt
url: https://www.malt.fr/profile/matthieubessat
thumbnail:
src: malt.svg
- id: discord
name:
fr: Mon serveur discord
en: My discord server
url: https://discord.com/invite/3W94m7ts3H
thumbnail:
src: discord.svg
style: "transform: scale(0.75)"
# - id: soundcloud
# name: Sound Cloud
# url: https://soundcloud.com/lefuturiste
- id: stackoverflow
name: StackOverflow
url: https://stackoverflow.com/users/7100565/lefuturiste
- id: osm
name: OpenStreetMap
url: https://openstreetmap.org/user/lefuturiste
# - id: osm_wiki
# name:
# en: OpenStreetMap wiki
# fr: Le wiki d'OpenStreetMap
# url: https://wiki.openstreetmap.org/wiki/User:Lefuturiste
- id: wikipedia_fr
name: Wikipedia
url: https://fr.wikipedia.org/wiki/Utilisateur:Matthieu2743
thumbnail:
src: wikipedia.svg
alt: Wikipedia logo
- id: wikidata
name: Wikidata
url: https://www.wikidata.org/wiki/User:Matthieu2743

4
config/misc.yaml Normal file
View file

@ -0,0 +1,4 @@
email: bonjour@matthieubessat.fr
updated_at: '2022-07-04'

View file

@ -1,99 +1,214 @@
pro_projects:
- id: associations.espacecondorcet
- id: forum_asso
detailled_page: true
date: 2020-07
link: https://associations.espacecondorcet.org
images:
- id: public_list
name:
fr: Liste des associations
en: f
description:
fr: Page regroupant toute les associations de la localité triées par catégories
en: Public page
- id: public_page
name:
fr: Page d'une association
en: e
description:
fr: Page publique d'une association contenenant toute les informations de sa fiche
en: e
- id: manage_organizations
name:
fr: Gestion des associations
en: e
description:
fr: L'administrateur peut ajouter, modifier, valider toute les associations depuis une unique interface
en: e
- id: changes_approval
name:
fr: Vérification par l'administrateur
en: e
description:
fr: L'administrateur peut valider ou refuser les changements apportés par l'association
en: e
- id: edit_main
name:
fr: Panel associatif
en: e
description:
fr: Les associations disposent d'un espace réservé pour remplir leur fiche
en: e
- id: edit_images
name:
fr: Images et vidéos
en: e
description:
fr: Les associations peuvent ajouter des images ou des vidéos
en: e
technologies:
- Node.js
- TypeScript
- Express.js
- Vue.js
- Vuetify
- MongoDB
locales:
fr:
name: Annuaire assocatif administré
description: Création d'une application web de gestion de contenu
background: |
En Juin 2020, l'Espace condorcet voulais un moyen de partager les associations locales
en:
name: Administered association directory
description: Created a web app of content management
background: |
In June of 2020, the Espace Condorcet wanted a way to have the public discover associations around them in around the town.
- node
- typescript
- express
- vue
- vuetify
- mongodb
name:
fr: Annuaire assocatif administré
en: Administered association directory
description:
fr: |
Application web de gestion de fiche associative avec interface administrative.
en: |
Web application of association management with administration interface.
background:
fr: |
En Juin 2020, l'Espace condorcet cherchait un moyen numérique afin de substituer le forum associatif physique qui était compromis pour cause sanitaire, ils voulait un moyen simple pour que les associations puissent se décrire et en même temps que le personnel de l'espace condorcet puissent valider le contenu facilement sans d'étapes manuelle pour la publication.
en: |
In June of 2020, the Espace Condorcet wanted a way to have the public discover associations around them in around the town.
solution:
fr: |
Afin de répondre aux besoins du client, j'ai développé pendant l'été le logiciel qui se compose en plusieurs parties : une partie accessible par tous qui permet de naviguer dans les associations basé sur un serveur Node.js et une interface d'administration et d'édition par les associations qui communique avec une API web en Node.js qui va ensuite mettre à jour la base de données MongoDB.
en: |
Foo bar
action:
fr: Site web externe
en: External website
thumbnail:
src: condorcet.jpg
alt:
fr: Un dessin enfantin représentant le soleil éclairant un chemin
en: A childish drawing representing the sun lighting up a footway.
- id: tracklift
detailled_page: false
date: 2022-01
link: https://tracklift.fr
name: Tracklift
# En 2021 Socobat Environnement avait le besoin d'une application web afin de modéliser la gestion des déchets sur des chantiers d'ascenseurs (collecte, retraitement, revalorisation) et de partager le résultat à des clients. J'ai démarré le développement à partir d'une base et j'ai mit en production le site web.
description:
fr: |
Gestion de la récupération, le traitement et la revalorisation des déchets issues des chantiers d'ascenseurs.
# De façon plus général, logiciel de gestion de l'activité d'une entreprise.
en: |
Management of the recovering, the processing and the revaluation of wastes from elevator's worksite
thumbnail:
src: logo_square.svg
alt: A 'T' letter encapsulated into brackets
technologies:
- Vue
- Vuetify
- Symfony 5
- Api Platform
- PHP
- MySQL
locales:
fr:
name: Tracklift
description: Site pour gérer l'activité de démontage d'ascenseur de l'entreprise Socobat
background: |
Sometimes none run start somebody learn card.
Once remain foreign player task decision social. Student police lawyer pattern local.
en:
name: Tracklift
description: Web site to manage business of the tracklift society
background: |
Sometimes none run start somebody learn card.
Once remain foreign player task decision social. Student police lawyer pattern local.
- vue
- vuetify
- symfony
- api-platform
- php
- mysql
side_projects:
- id: werobot
detailled_page: false
date: 2018-11
link: https://werobot.fr
technologies:
- Nuxt.js
- Vue.js
- PHP
- Slim Framework
locales:
fr:
name: WeRobot.fr
description: Création d'un site vitrine accompagné d'un blog pour l'association robotique locale We Robot
en:
name: WeRobot.fr
description: Creation of a presentation website along with a blog for the robotic club We Robot
- nuxt
- vue
- php
- slim
- mariadb
thumbnail:
src: logo-300.png
alt:
fr: Une flèche rouge poussant vers le haut la lettre W bleu surmonté par la lettre R en blanc
en: A red arrow pushing up a blue W letter which is supporting a white R letter
name: WeRobot.fr
description:
en: A presentation website along with a blog for the robotic club We Robot.
fr: Un site vitrine accompagné d'un blog pour l'association de robotique locale We Robot.
- id: retrobox
detailled_page: false
name: RetroBox
description:
en: E-commerce website from scratch to sell the retrobox console and allow the customer to manage it remotely.
fr: E-commerce de A à Z pour vendre des consoles RetroBox et permettre la gestion à distance de celle ci par le client.
date: 2018-08
link: https://retrobox.tech
thumbnail:
src: logo_alone_square.png
alt:
fr: Une console démontrant qu'il peut afficher plusieurs couleurs de façon verticale
en: A console demonstrating that it can show many coulor in an vertical mannel
technologies:
- Nuxt.js
- nuxt
- vue
- php
- slim
- mariadb
- socket-io
- stripe
- paypal
- electron
- id: jobatator
name: Jobatator
date: 2020-06
link: https://github.com/jobatator
link: https://github.com/jobatator
logo: logo.png
description:
fr: |
Un serveur TCP développé comme alternative simplifié à RabbitMQ afin de dispatcher des tâches à des processus.
en: |
A simplified alternative to RabbitMQ to dispatch jobs to workers (in order to for example send an email).
technologies:
- Go
- go
action:
fr: GitHub du projet
en: GitHub page
- id: keyvaluer
detailled_page: false
name: KeyValuer
date: 2020-04
link: https://github.com/lefuturiste/keyvaluer
description:
fr: |
Une implementation d'un serveur supportant les commandes basiques de Redis formant une base clé-valeur.
en: |
An implementation of a redis compatible serveur to have a key-value database.
technologies:
- Go
- go
action:
fr: GitHub du projet
en: GitHub page
- id: discord-monolog-handler
detailled_page: false
name: Discord Monolog handler
date: 2017-08
link: https://github.com/lefuturiste/monolog-discord-handler
technologies:
- PHP
- php
description:
fr: |
Un projet open-source pour permettre à la librairie monolog d'envoyer des logs via un webhook discord.
en: |
An open-source project to extend the monolog library in order to send logs via discord webhooks.
action:
fr: GitHub du projet
en: GitHub page
- id: langatator
detailled_page: false
name: Langatator
date: 2022-05
link: https://gitlab.com/lefuturiste/langatator
description:
fr: |
Développement d'un langage de programmation impératif interprété afin de découvrir le fonctionnement d'un interpréteur.
en: |
Creation of an impérative interpreted langage, trying to enforce a grammar with a lexer, a parser and an evaluator.
technologies:
- C
- id: quasator
name: Quasator
date: 2022-03
link: https://gitlab.com/lefuturiste/quasator
technologies:
- Python
- Latex
- c
action:
fr: GitLab du projet
en: GitLab page
# - id: quasator
# name: Quasator
# date: 2022-03
# link: https://gitlab.com/lefuturiste/quasator
# technologies:
# - python
# - latex

View file

@ -1,90 +1,212 @@
# TODO: add git; vim; mariadb; latex; socket-io; docker; c (programming language); express.js; stripe and paypal integrations
technologies:
# Front End
- image: css3.svg
- id: css
image: css3.svg
name: CSS 3
height: stretch
- image: html5.svg
website: https://www.w3.org/TR/css/
wikidata: Q11707176
- id: html
image: html5.svg
name: HTML 5
website: https://html.spec.whatwg.org/multipage/
wikidata: Q2053
# JavaScript
- image: jquery.svg
- id: jquery
image: jquery.svg
name: JQuery
- image: node.svg
name: Node.js
- image: yarn.svg
name: Yarn
- image: webpack.svg
name: Webpack
- image: sass.svg
name: SASS
- image: typescript.svg
name: Type Script
website: https://jquery.com/
wikidata: Q230036
- image: electron.png
- id: node
image: node.svg
name: Node.js
website: https://nodejs.org/
wikidata: Q756100
- id: yarn
image: yarn.svg
name: Yarn
website: https://yarnpkg.com
wikidata: Q28975591
- id: webpack
image: webpack.svg
name: Webpack
website: https://webpack.js.org/
wikidata:
- id: sass
image: sass.svg
name: SASS
website: https://sass-lang.com/
wikidata: Q1572865
- id: typescript
image: typescript.svg
name: Type Script
website: https://www.typescriptlang.org/
wikidata: Q978185
- id: electron
image: electron.svg
name: Electron
website: https://www.electronjs.org/
wikidata: Q21614124
# Python
- image: python.svg
- id: python
image: python.svg
name: Python
- image: gunicorn.svg
name: Gunicorn
website: https://python.org
wikidata: Q28865
- id: gunicorn
image: gunicorn.svg
name: Gunicorn
website: https://gunicorn.org/
wikidata: Q5618801
# Dev Ops
- image: docker.svg
- id: docker
image: docker.svg
name: Docker
- image: caddy.png
website: https://docker.com
wikidata: Q15206305
- id: caddy
image: caddy.png
name: Caddy
- image: nginx.svg
website: https://caddyserver.com/
wikidata: Q24008327
- id: nginx
image: nginx.svg
name: Nginx
height: stretch
- image: varnish.svg
name: Varnish
- image: cloudflare.svg
name: Cloudflare
website: https://nginx.org/en/
wikidata: Q306144
- id: varnish
image: varnish.svg
name: Varnish
website: https://varnish-cache.org
wikidata: Q1602447
- id: cloudflare
image: cloudflare.svg
name: Cloudflare
website: https://cloudflare.com
wikidata: Q4778915
# Databases
- image: mysql.svg
name: MySQL
description: Database management system
- image: mongodb.svg
- id: mariadb
alias: mysql
image: mariadb.svg
name: MariaDB
description: Database management system similar to MySQL
website: https://mariadb.org/
wikidata: Q787177
- id: mongodb
image: mongodb.svg
height: stretch
name: MongoDB
- image: elasticsearch.png
description: NoSQL database
website: https://mongodb.com
wikidata: Q1165204
- id: elasticsearch
image: elasticsearch.png
name: Elastic Search
- image: redis.svg
website: https://www.elastic.co/elasticsearch/
wikidata: Q3050461
- id: redis
image: redis.svg
name: Redis
website: https://redis.io
wikidata: Q2136322
# PHP ecosystem
- image: php.svg
- id: php
image: php.svg
name: PHP
description: Language to make cool website
website: https://php.net
color: blue
- image: api-platform.svg
wikidata: Q59
- id: api-platform
image: api-platform.svg
name: API Platform
- image: composer.svg
website: https://api-platform.com/
wikidata: Q108740058
- id: composer
image: composer.svg
name: Composer
height: stretch
- image: slim.svg
website: https://getcomposer.org/
wikidata: Q15252222
- id: slim
image: slim.svg
name: Slim Framework
- image: laravel.svg
website: https://www.slimframework.com/
wikidata: null
- id: laravel
image: laravel.svg
name: Laravel
- image: symfony.svg
website: https://laravel.com/
wikidata: Q13634357
- id: symfony
image: symfony.svg
name: Symfony
- image: phpunit.svg
website: https://symfony.com
wikidata: Q1322933
- id: phpunit
image: phpunit.svg
name: PHP Unit
website: https://phpunit.de/
wikidata: Q1663673
- image: vue.svg
- id: vue
image: vue.svg
name: Vue.js
- image: nuxt.svg
website: https://vuejs.org/
wikidata: Q24589705
- id: nuxt
image: nuxt.svg
name: Nuxt.js
- image: vuetify.svg
website: https://nuxtjs.org/
wikidata: Q55641291
- id: vuetify
image: vuetify.svg
name: Vuetify
website: https://vuetifyjs.com/
wikidata: Q112943923
- image: golang.svg
- id: go
image: go.svg
name: Go
website: https://go.dev
wikidata: Q37227
- id: express
image: express.svg
name: Express.js
wikidata: Q16878131

View file

@ -4,6 +4,7 @@
"build": "cross-env NODE_ENV=production webpack"
},
"dependencies": {
"axios": "^0.27.2",
"file-loader": "^6.2.0",
"node-sass-glob-importer": "^5.3.2",
"sass": "^1.42.1",
@ -14,5 +15,6 @@
},
"devDependencies": {
"cross-env": "^7.0.3"
}
},
"_comment": "YES"
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,233 @@
/*! ../adapters/xhr */
/*! ../cancel/CanceledError */
/*! ../cancel/isCancel */
/*! ../core/AxiosError */
/*! ../core/buildFullPath */
/*! ../defaults */
/*! ../defaults/transitional */
/*! ../env/data */
/*! ../helpers/buildURL */
/*! ../helpers/combineURLs */
/*! ../helpers/isAbsoluteURL */
/*! ../helpers/normalizeHeaderName */
/*! ../helpers/parseProtocol */
/*! ../helpers/toFormData */
/*! ../helpers/validator */
/*! ../lib/core/AxiosError */
/*! ../utils */
/*! ./../core/settle */
/*! ./../helpers/buildURL */
/*! ./../helpers/cookies */
/*! ./../helpers/isURLSameOrigin */
/*! ./../helpers/parseHeaders */
/*! ./../utils */
/*! ./AxiosError */
/*! ./CanceledError */
/*! ./InterceptorManager */
/*! ./buildFullPath */
/*! ./cancel/CancelToken */
/*! ./cancel/CanceledError */
/*! ./cancel/isCancel */
/*! ./contact */
/*! ./core/Axios */
/*! ./core/mergeConfig */
/*! ./defaults */
/*! ./dispatchRequest */
/*! ./env/FormData */
/*! ./env/data */
/*! ./helpers/bind */
/*! ./helpers/isAxiosError */
/*! ./helpers/spread */
/*! ./helpers/toFormData */
/*! ./lib/axios */
/*! ./mergeConfig */
/*! ./transformData */
/*! ./transitional */
/*! ./utils */
/*! axios */
/*!********************************!*\
!*** ./assets/scripts/main.js ***!
\********************************/
/*!*********************************!*\
!*** ./assets/styles/main.scss ***!
\*********************************/
/*!***********************************!*\
!*** ./assets/scripts/contact.js ***!
\***********************************/
/*!*************************************!*\
!*** ./node_modules/axios/index.js ***!
\*************************************/
/*!*****************************************!*\
!*** ./node_modules/axios/lib/axios.js ***!
\*****************************************/
/*!*****************************************!*\
!*** ./node_modules/axios/lib/utils.js ***!
\*****************************************/
/*!********************************************!*\
!*** ./node_modules/axios/lib/env/data.js ***!
\********************************************/
/*!**********************************************!*\
!*** ./node_modules/axios/lib/core/Axios.js ***!
\**********************************************/
/*!***********************************************!*\
!*** ./node_modules/axios/lib/core/settle.js ***!
\***********************************************/
/*!************************************************!*\
!*** ./node_modules/axios/lib/adapters/xhr.js ***!
\************************************************/
/*!************************************************!*\
!*** ./node_modules/axios/lib/helpers/bind.js ***!
\************************************************/
/*!************************************************!*\
!*** ./node_modules/axios/lib/helpers/null.js ***!
\************************************************/
/*!**************************************************!*\
!*** ./node_modules/axios/lib/defaults/index.js ***!
\**************************************************/
/*!**************************************************!*\
!*** ./node_modules/axios/lib/helpers/spread.js ***!
\**************************************************/
/*!***************************************************!*\
!*** ./node_modules/axios/lib/cancel/isCancel.js ***!
\***************************************************/
/*!***************************************************!*\
!*** ./node_modules/axios/lib/core/AxiosError.js ***!
\***************************************************/
/*!***************************************************!*\
!*** ./node_modules/axios/lib/helpers/cookies.js ***!
\***************************************************/
/*!****************************************************!*\
!*** ./node_modules/axios/lib/core/mergeConfig.js ***!
\****************************************************/
/*!****************************************************!*\
!*** ./node_modules/axios/lib/helpers/buildURL.js ***!
\****************************************************/
/*!*****************************************************!*\
!*** ./node_modules/axios/lib/helpers/validator.js ***!
\*****************************************************/
/*!******************************************************!*\
!*** ./node_modules/axios/lib/cancel/CancelToken.js ***!
\******************************************************/
/*!******************************************************!*\
!*** ./node_modules/axios/lib/core/buildFullPath.js ***!
\******************************************************/
/*!******************************************************!*\
!*** ./node_modules/axios/lib/core/transformData.js ***!
\******************************************************/
/*!******************************************************!*\
!*** ./node_modules/axios/lib/helpers/toFormData.js ***!
\******************************************************/
/*!*******************************************************!*\
!*** ./node_modules/axios/lib/helpers/combineURLs.js ***!
\*******************************************************/
/*!********************************************************!*\
!*** ./node_modules/axios/lib/cancel/CanceledError.js ***!
\********************************************************/
/*!********************************************************!*\
!*** ./node_modules/axios/lib/core/dispatchRequest.js ***!
\********************************************************/
/*!********************************************************!*\
!*** ./node_modules/axios/lib/helpers/isAxiosError.js ***!
\********************************************************/
/*!********************************************************!*\
!*** ./node_modules/axios/lib/helpers/parseHeaders.js ***!
\********************************************************/
/*!*********************************************************!*\
!*** ./node_modules/axios/lib/defaults/transitional.js ***!
\*********************************************************/
/*!*********************************************************!*\
!*** ./node_modules/axios/lib/helpers/isAbsoluteURL.js ***!
\*********************************************************/
/*!*********************************************************!*\
!*** ./node_modules/axios/lib/helpers/parseProtocol.js ***!
\*********************************************************/
/*!***********************************************************!*\
!*** ./node_modules/axios/lib/core/InterceptorManager.js ***!
\***********************************************************/
/*!***********************************************************!*\
!*** ./node_modules/axios/lib/helpers/isURLSameOrigin.js ***!
\***********************************************************/
/*!***************************************************************!*\
!*** ./node_modules/axios/lib/helpers/normalizeHeaderName.js ***!
\***************************************************************/

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><title>Checkmark Circle</title><path d="M448 256c0-106-86-192-192-192S64 150 64 256s86 192 192 192 192-86 192-192z" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M352 176L217.6 336 160 272"/></svg>

After

Width:  |  Height:  |  Size: 413 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" class="ionicon" viewBox="0 0 512 512"><title>Close Circle</title><path d="M448 256c0-106-86-192-192-192S64 150 64 256s86 192 192 192 192-86 192-192z" fill="none" stroke="currentColor" stroke-miterlimit="10" stroke-width="32"/><path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32" d="M320 320L192 192M192 320l128-128"/></svg>

After

Width:  |  Height:  |  Size: 415 B

View file

@ -0,0 +1,10 @@
<svg width="71" height="55" viewBox="0 0 71 55" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0)">
<path d="M60.1045 4.8978C55.5792 2.8214 50.7265 1.2916 45.6527 0.41542C45.5603 0.39851 45.468 0.440769 45.4204 0.525289C44.7963 1.6353 44.105 3.0834 43.6209 4.2216C38.1637 3.4046 32.7345 3.4046 27.3892 4.2216C26.905 3.0581 26.1886 1.6353 25.5617 0.525289C25.5141 0.443589 25.4218 0.40133 25.3294 0.41542C20.2584 1.2888 15.4057 2.8186 10.8776 4.8978C10.8384 4.9147 10.8048 4.9429 10.7825 4.9795C1.57795 18.7309 -0.943561 32.1443 0.293408 45.3914C0.299005 45.4562 0.335386 45.5182 0.385761 45.5576C6.45866 50.0174 12.3413 52.7249 18.1147 54.5195C18.2071 54.5477 18.305 54.5139 18.3638 54.4378C19.7295 52.5728 20.9469 50.6063 21.9907 48.5383C22.0523 48.4172 21.9935 48.2735 21.8676 48.2256C19.9366 47.4931 18.0979 46.6 16.3292 45.5858C16.1893 45.5041 16.1781 45.304 16.3068 45.2082C16.679 44.9293 17.0513 44.6391 17.4067 44.3461C17.471 44.2926 17.5606 44.2813 17.6362 44.3151C29.2558 49.6202 41.8354 49.6202 53.3179 44.3151C53.3935 44.2785 53.4831 44.2898 53.5502 44.3433C53.9057 44.6363 54.2779 44.9293 54.6529 45.2082C54.7816 45.304 54.7732 45.5041 54.6333 45.5858C52.8646 46.6197 51.0259 47.4931 49.0921 48.2228C48.9662 48.2707 48.9102 48.4172 48.9718 48.5383C50.038 50.6034 51.2554 52.5699 52.5959 54.435C52.6519 54.5139 52.7526 54.5477 52.845 54.5195C58.6464 52.7249 64.529 50.0174 70.6019 45.5576C70.6551 45.5182 70.6887 45.459 70.6943 45.3942C72.1747 30.0791 68.2147 16.7757 60.1968 4.9823C60.1772 4.9429 60.1437 4.9147 60.1045 4.8978ZM23.7259 37.3253C20.2276 37.3253 17.3451 34.1136 17.3451 30.1693C17.3451 26.225 20.1717 23.0133 23.7259 23.0133C27.308 23.0133 30.1626 26.2532 30.1066 30.1693C30.1066 34.1136 27.28 37.3253 23.7259 37.3253ZM47.3178 37.3253C43.8196 37.3253 40.9371 34.1136 40.9371 30.1693C40.9371 26.225 43.7636 23.0133 47.3178 23.0133C50.9 23.0133 53.7545 26.2532 53.6986 30.1693C53.6986 34.1136 50.9 37.3253 47.3178 37.3253Z" fill="#5865F2"/>
</g>
<defs>
<clipPath id="clip0">
<rect width="71" height="55" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6 0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6 0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3 0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1 0-6.2-.3-40.4-.3-61.4 0 0-70 15-84.7-29.8 0 0-11.4-29.1-27.8-36.6 0 0-22.9-15.7 1.6-15.4 0 0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5 0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9 0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4 0 33.7-.3 75.4-.3 83.6 0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6 0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9 0-6.2-1.4-2.3-4-3.3-5.6-2z"/></svg>
<!--
Font Awesome Free 5.4.1 by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
-->

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-2" d="M282.83,170.73l-.27-.69a88.3,88.3,0,0,0-35.15,15.8L190,229.25c19.55,14.79,36.57,27.64,36.57,27.64l40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-3" d="M153.43,256.89l19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91S209.55,244,190,229.25C170.45,244,153.43,256.89,153.43,256.89Z"/><path class="cls-2" d="M132.58,185.84A88.19,88.19,0,0,0,97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82s17-12.85,36.57-27.64Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 23.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
version="1.1"
id="Calque_1"
x="0px"
y="0px"
viewBox="0 0 300 300"
xml:space="preserve"
sodipodi:docname="malt.svg"
width="300"
height="300"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><defs
id="defs93" /><sodipodi:namedview
id="namedview91"
pagecolor="#ffffff"
bordercolor="#cccccc"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="1.1676101"
inkscape:cx="9.8491785"
inkscape:cy="181.13924"
inkscape:window-width="1920"
inkscape:window-height="1038"
inkscape:window-x="0"
inkscape:window-y="20"
inkscape:window-maximized="1"
inkscape:current-layer="g88" />
<style
type="text/css"
id="style60">
.st0{fill:#FC5656;}
</style>
<g
id="g88">
<path
id="path62"
class="st0"
d="m 149.6871,31.865607 c -19.42382,0 -26.71707,11.742455 -29.2386,24.70815 1.81227,1.585943 3.62361,3.176246 5.45481,5.028855 l 23.7961,24.055249 24.22008,-24.486367 c 1.64443,-1.666322 3.31412,-3.211652 4.99388,-4.670004 -2.53668,-12.937626 -9.83906,-24.635883 -29.22627,-24.635883 z m 63.48832,25.109363 c -10.95884,0.07618 -21.1727,7.44531 -28.60759,14.961948 L 128.383,128.74201 72.190779,185.55206 c -11.895837,12.02536 -24.345929,30.29761 -5.617495,49.2294 18.728432,18.93819 36.800696,6.34738 48.694016,-5.67926 l 56.18976,-56.80756 56.18976,-56.80508 C 239.5439,103.46165 251.05877,84.242808 233.26677,66.255169 226.59382,59.508841 219.7507,56.929261 213.17542,56.97497 Z M 87.061493,56.99241 c -6.547339,-0.07353 -13.477371,2.593482 -20.500535,9.693864 -13.733343,13.884342 -10.68349,27.396611 -3.399088,38.366796 2.218636,-0.16715 67.6811,-0.35637 67.68111,-0.35635 l 8.19578,-8.285905 -23.78133,-24.04527 C 107.82175,64.849698 97.97372,57.114957 87.061493,56.992412 Z m -17.123629,62.92794 c -16.821513,0 -38.407995,5.35719 -38.407995,30.79608 0,18.98158 12.017709,26.77466 25.107421,29.51271 1.549767,-1.80792 59.17475,-60.30879 59.17475,-60.30879 z m 172.900096,1.23602 c -1.45259,1.70713 -59.19938,60.35615 -59.19938,60.35615 h 45.22341 c 16.82155,0 38.40801,-4.01878 38.40801,-30.79609 0,-19.63482 -11.60989,-27.01083 -24.43204,-29.56006 z m -6.69466,75.23838 c -2.3625,0.17226 -67.59481,0.34139 -67.59481,0.34139 l -8.21551,8.30831 24.22253,24.48637 c 11.89583,12.02792 30.90446,23.6694 48.69646,5.68176 13.2765,-13.42375 10.23126,-27.52357 2.89133,-38.81783 z m -86.44142,19.4276 -23.78377,24.04278 c -1.80596,1.82712 -3.60364,3.49566 -5.39319,5.06374 2.72723,13.189 10.44751,25.26886 29.16711,25.26886 18.76884,0 26.47726,-12.14493 29.18683,-25.37601 -1.66462,-1.44815 -3.33233,-2.86807 -4.97171,-4.52546 z"
style="stroke-width:1.26894" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="61.076954mm" height="65.47831mm" viewBox="0 0 216.4144 232.00976">
<path fill="#2b90d9" d="M211.80734 139.0875c-3.18125 16.36625-28.4925 34.2775-57.5625 37.74875-15.15875 1.80875-30.08375 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.39125 27.9425 21.11625.7225 39.91875-5.20625 39.91875-5.20625l.8675 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234 213.82 1.40609 165.31125.20859 116.09125c-.365-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67234 3.45375 78.20359.2425 107.86484 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.975 14.7525 32.975 65.0825 0 0 .41375 37.13375-4.59875 62.915"/>
<path fill="#fff" d="M177.50984 80.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025 0-17.4175 7.5075-17.4175 22.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375 0-15.74 6.32875-15.74 18.7975v59.15H38.90484V80.077c0-12.455 3.17125-22.3525 9.54125-29.675 6.56875-7.3225 15.17125-11.07625 25.85-11.07625 12.355 0 21.71125 4.74875 27.8975 14.2475l6.01375 10.08125 6.015-10.08125c6.185-9.49875 15.54125-14.2475 27.8975-14.2475 10.6775 0 19.28 3.75375 25.85 11.07625 6.36875 7.3225 9.54 17.22 9.54 29.675"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 150 KiB

758
public/imgs/logos/osm.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 150 KiB

View file

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
aria-hidden="true"
class="svg-icon iconLogoGlyphMd native"
width="32"
height="37"
viewBox="0 0 32 37"
version="1.1"
id="svg6"
sodipodi:docname="stackoverflow_i.svg"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs10" />
<sodipodi:namedview
id="namedview8"
pagecolor="#ffffff"
bordercolor="#cccccc"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="8.2702703"
inkscape:cx="15.900327"
inkscape:cy="22.308824"
inkscape:window-width="958"
inkscape:window-height="1038"
inkscape:window-x="960"
inkscape:window-y="20"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="M26 33v-9h4v13H0V24h4v9h22Z"
fill="#BCBBBB"
style="--darkreader-inline-fill: #bdb8af;"
data-darkreader-inline-fill=""
id="path2" />
<path
d="m21.5 0-2.7 2 9.9 13.3 2.7-2L21.5 0ZM26 18.4 13.3 7.8l2.1-2.5 12.7 10.6-2.1 2.5ZM9.1 15.2l15 7 1.4-3-15-7-1.4 3Zm14 10.79.68-2.95-16.1-3.35L7 23l16.1 2.99ZM23 30H7v-3h16v3Z"
fill="#F48024"
style="--darkreader-inline-fill: #f58a35;"
data-darkreader-inline-fill=""
id="path4" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -0,0 +1,50 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
version="1.2"
width="1000"
height="1000"
id="svg1012"
sodipodi:docname="wikidata.svg"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs1016" />
<sodipodi:namedview
id="namedview1014"
pagecolor="#ffffff"
bordercolor="#cccccc"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="0.57452725"
inkscape:cx="813.71249"
inkscape:cy="585.69893"
inkscape:window-width="1920"
inkscape:window-height="1038"
inkscape:window-x="0"
inkscape:window-y="20"
inkscape:window-maximized="1"
inkscape:current-layer="svg1012" />
<path
d="m 95.59227,669.96435 h 29.16277 V 170.00006 H 95.59227 Z m 60.27256,0 h 89.42926 V 170.00006 h -89.42926 z m 118.5981,-499.96429 v 499.94645 h 89.42926 V 170.00006 Z"
style="fill:#990000"
id="path1004" />
<path
d="m 815.1509,669.99996 h 29.16883 V 170 H 815.1509 Z M 875.42952,170 v 499.99996 h 29.16277 V 170 Z M 394.02544,670.00003 h 29.16277 V 170.00006 H 394.02544 Z M 454.30406,170.00006 V 669.98219 H 483.4729 V 170.00006 Z"
style="fill:#339966"
id="path1006" />
<path
d="m 513.59401,670.00003 h 89.4414 V 170.00006 h -89.4414 z m 118.5981,0 h 31.10372 V 170.00006 h -31.10372 z m 60.27256,-499.99997 v 499.98213 h 89.42926 V 170.00006 Z"
style="fill:#006699"
id="path1008" />
<path
d="m 860.16016,725.03567 46.01562,103.68359 h -27.75781 l -8.53516,-20.48438 h -46.38672 l -7.71875,20.48438 h -27.38672 l 41.78516,-103.68359 h 29.98438 m 2.07812,65.08984 -16.69922,-40.30078 -15.21484,40.30078 h 31.91406 m -60.26562,-65.08984 v 19 h -37.55469 v 84.68359 h -25.82813 v -84.68359 h -37.55468 v -19 h 100.9375 m -133.59375,0 46.01562,103.68359 h -27.75781 l -8.53516,-20.48438 h -46.38672 l -7.71875,20.48438 h -27.38672 l 41.78516,-103.68359 h 29.98438 m 2.07812,65.08984 -16.69922,-40.30078 -15.21484,40.30078 h 31.91406 M 493.89062,725.03567 h 45.71875 c 17.66399,10e-5 31.6171,4.84905 41.85938,14.54687 10.29156,9.64852 15.43739,22.142 15.4375,37.48047 -1.1e-4,16.08076 -5.22016,28.72268 -15.66016,37.92578 -10.39071,9.15365 -25.23444,13.73047 -44.53125,13.73047 H 493.89062 V 725.03567 m 25.82813,19 v 65.68359 h 16.84766 c 10.93483,2e-5 19.2968,-2.99347 25.08593,-8.98047 5.78898,-6.03642 8.68351,-13.97782 8.6836,-23.82422 -9e-5,-10.19264 -2.9441,-18.20826 -8.83203,-24.04687 -5.83862,-5.88794 -14.25007,-8.83195 -25.23438,-8.83203 h -16.55078 m -49.72656,-19 V 828.71926 H 444.16406 V 725.03567 h 25.82813 m -48.91016,0 -33.76953,48.6875 45.79297,54.99609 h -32.35938 l -41.04297,-48.61328 v 48.61328 H 333.875 V 725.03567 h 25.82812 v 46.68359 l 32.95313,-46.68359 h 28.42578 m -111.10547,0 V 828.71926 H 284.14844 V 725.03567 h 25.82812 m -127.21094,37.40625 -32.0625,67.61328 H 139.57031 L 94,725.03567 h 27.38672 l 24.41797,57.29687 26.64453,-57.29687 h 20.78125 l 26.64453,57.29687 24.26953,-57.29687 h 27.38672 L 226.10937,830.0552 h -11.13281 l -32.21094,-67.61328"
style="font-family:'Gill Sans MT';fill:#484848;fill-opacity:1;stroke:none"
id="path1010" />
</svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 505 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 789 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View file

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
viewBox="0 0 100 100"
version="1.1"
id="svg140"
sodipodi:docname="tracklift_logo_square.svg"
inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
width="100"
height="100"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs144" />
<sodipodi:namedview
id="namedview142"
pagecolor="#ffffff"
bordercolor="#cccccc"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="1"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
showgrid="false"
inkscape:zoom="4.9820652"
inkscape:cx="31.412676"
inkscape:cy="62.724992"
inkscape:window-width="1920"
inkscape:window-height="1080"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg140" />
<path
d="M 90.52215,9.8348385 H 10.182101 V 47.545474 h 9.699831 V 19.528112 H 80.828878 V 80.475058 H 36.153252 v 9.693273 H 90.52215 Z M 35.399039,31.287271 h 9.706389 v 9.70639 h -9.706389 z"
fill="#3aaa35"
id="path134"
sodipodi:nodetypes="cccccccccccccccc"
style="stroke-width:0.655837" />
<path
d="m 46.502362,68.689665 h 9.693272 V 40.993661 h 11.083649 v -9.70639 H 46.502362 Z M 19.881932,52.772496 h -9.699831 v 37.395835 h 20.77692 v -9.70639 H 19.875374 V 52.772496 Z"
fill="#c8d400"
id="path136"
sodipodi:nodetypes="ccccccccccccccc"
style="stroke-width:0.655837" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -0,0 +1 @@
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle fill="#2B2E3A" cx="128" cy="128" r="128"/><g fill="#9FEAF9" fill-rule="nonzero"><path d="M100.502 71.69c-26.005-4.736-46.567.221-54.762 14.415-6.115 10.592-4.367 24.635 4.24 39.646a2.667 2.667 0 1 0 4.626-2.653c-7.752-13.522-9.261-25.641-4.247-34.326 6.808-11.791 25.148-16.213 49.187-11.835a2.667 2.667 0 0 0 .956-5.247zm-36.999 72.307c10.515 11.555 24.176 22.394 39.756 31.388 37.723 21.78 77.883 27.601 97.675 14.106a2.667 2.667 0 1 0-3.005-4.406c-17.714 12.078-55.862 6.548-92.003-14.318-15.114-8.726-28.343-19.222-38.478-30.36a2.667 2.667 0 1 0-3.945 3.59z"/><path d="M194.62 140.753c17.028-20.116 22.973-40.348 14.795-54.512-6.017-10.423-18.738-15.926-35.645-16.146a2.667 2.667 0 0 0-.069 5.333c15.205.198 26.165 4.939 31.096 13.48 6.792 11.765 1.49 29.807-14.248 48.399a2.667 2.667 0 1 0 4.071 3.446zm-43.761-68.175c-15.396 3.299-31.784 9.749-47.522 18.835-38.942 22.483-64.345 55.636-60.817 79.675a2.667 2.667 0 1 0 5.277-.775c-3.133-21.344 20.947-52.769 58.207-74.281 15.267-8.815 31.135-15.06 45.972-18.239a2.667 2.667 0 1 0-1.117-5.215z"/><path d="M87.77 187.753c8.904 24.86 23.469 40.167 39.847 40.167 11.945 0 22.996-8.143 31.614-22.478a2.667 2.667 0 1 0-4.571-2.748c-7.745 12.883-17.258 19.892-27.043 19.892-13.605 0-26.596-13.652-34.825-36.63a2.667 2.667 0 1 0-5.021 1.797zm81.322-4.863c4.61-14.728 7.085-31.718 7.085-49.423 0-44.179-15.463-82.263-37.487-92.042a2.667 2.667 0 0 0-2.164 4.874c19.643 8.723 34.317 44.866 34.317 87.168 0 17.177-2.397 33.63-6.84 47.83a2.667 2.667 0 1 0 5.09 1.593zm50.224-2.612c0-7.049-5.714-12.763-12.763-12.763-7.049 0-12.763 5.714-12.763 12.763 0 7.049 5.714 12.763 12.763 12.763 7.049 0 12.763-5.714 12.763-12.763zm-5.333 0a7.43 7.43 0 1 1-14.86 0 7.43 7.43 0 0 1 14.86 0zM48.497 193.041c7.05 0 12.764-5.714 12.764-12.763 0-7.049-5.715-12.763-12.764-12.763-7.048 0-12.763 5.714-12.763 12.763 0 7.049 5.715 12.763 12.763 12.763zm0-5.333a7.43 7.43 0 1 1 0-14.86 7.43 7.43 0 0 1 0 14.86z"/><path d="M127.617 54.444c7.049 0 12.763-5.714 12.763-12.763 0-7.049-5.714-12.763-12.763-12.763-7.049 0-12.763 5.714-12.763 12.763 0 7.049 5.714 12.763 12.763 12.763zm0-5.333a7.43 7.43 0 1 1 0-14.86 7.43 7.43 0 0 1 0 14.86zm1.949 93.382c-4.985 1.077-9.896-2.091-10.975-7.076a9.236 9.236 0 0 1 7.076-10.976c4.985-1.077 9.896 2.091 10.976 7.076 1.077 4.985-2.091 9.897-7.077 10.976z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

View file

@ -1,6 +1,7 @@
<?php
use App\App;
use App\Middlewares\TwigLocalizationMiddleware;
use App\Util\ContainerBuilder;
use App\Utils\DotEnv;
use Slim\Factory\AppFactory;
@ -8,6 +9,11 @@ use Slim\Views\Twig;
use Slim\Views\TwigMiddleware;
use DI\Container;
use App\Middlewares\LocalizationMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use function App\addRoutes;
require '../vendor/autoload.php';
@ -25,8 +31,12 @@ $app = AppFactory::createFromContainer($container);
$app->addRoutingMiddleware();
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
$twig = Twig::create('../templates', ['cache' => false]);
$app->add(TwigMiddleware::create($app, $twig));
// we place the definition BEFORE the localization middleware to get the locale attribute
$app->add(new TwigLocalizationMiddleware($container));
$app->add($container->get(LocalizationMiddleware::class));
$app->add(TwigMiddleware::create($app, $container->get(Twig::class)));
require '../src/routes.php';

View file

@ -2,6 +2,7 @@
namespace App\Controller;
use App\Middlewares\LocalizationMiddleware;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Slim\Views\Twig;
@ -10,14 +11,69 @@ class HomeController extends AbstractController
{
public function getHome(ServerRequestInterface $request, ResponseInterface $response)
{
return Twig::fromRequest($request)->render(
// $locale = $request->getAttributes()['locale'];
// dd($locale);
return $this->container->get(Twig::class)->render(
$response, 'home/home.html.twig',
[
'links' => array_map(function ($link) {
if (!isset($link['thumbnail'])) {
$link['thumbnail'] = ['src' => $link['id'] . '.svg', 'alt' => $link['id'] . " logo"];
}
return $link;
}, $this->container->get('links')),
'technologies' => $this->container->get('technologies'),
'pro_projects' => $this->container->get('pro_projects'),
'side_projects' => $this->container->get('side_projects'),
'locale' => 'fr'
'email' => $this->container->get('misc')['email']
]
);
}
public function getAbout(ServerRequestInterface $request, ResponseInterface $response)
{
return $this->container->get(Twig::class)->render(
$response, 'about.html.twig',
[
'email' => $this->container->get('misc')['email']
]
);
}
public function getProject(ServerRequestInterface $request, ResponseInterface $response)
{
$id = $request->getAttribute('id');
$projects = [...$this->container->get('pro_projects'), ...$this->container->get('side_projects')];
$projectsCandidates = array_values(array_filter($projects, fn ($p) => $p['id'] == $id));
if (count($projectsCandidates) === 0) {
// TODO: add proper response body
return $response
->withStatus(404);
}
$project = $projectsCandidates[0];
$technologies = $this->container->get('technologies');
$projectTechnologies = array_map(fn ($tId) => array_values(array_filter($technologies, fn ($t) => $t['id'] == $tId))[0], $project['technologies']);
return $this->container->get(Twig::class)->render(
$response, 'project.html.twig',
[
'project' => [...$project, 'technologies' => $projectTechnologies]
]
);
}
public function switchToLocale(ServerRequestInterface $request, ResponseInterface $response)
{
$newLocale = substr($request->getUri()->getPath(), 1);
$redir = $request->hasHeader('Referer') ? $request->getHeader('Referer') : '/';
return $this->container->get(LocalizationMiddleware::class)->withSetCookieLocale(
$response
->withStatus(302)
->withAddedHeader('Location', $redir),
$newLocale
);
}
}

View file

@ -0,0 +1,332 @@
<?php
namespace App\Middlewares;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
/**
* Middleware to assist primarily with language-based content negotiation
* and various other localization tasks
* Credits: https://raw.githubusercontent.com/tboronczyk/localization-middleware/master/src/LocalizationMiddleware.php
*/
class LocalizationMiddleware implements MiddlewareInterface
{
const FROM_URI_PATH = 1;
const FROM_URI_PARAM = 2;
const FROM_COOKIE = 3;
const FROM_HEADER = 4;
const FROM_CALLBACK = 5;
protected $availableLocales;
protected $defaultLocale;
protected $searchOrder;
protected $reqAttrName;
protected $uriParamName;
protected $cookieName;
protected $cookiePath;
protected $cookieExpire;
protected $localeCallback;
protected $searchCallback;
/**
* @param array $locales a list of available locales
* @param string $default the default locale
*/
public function __construct(array $locales, string $default)
{
$this->setAvailableLocales($locales);
$this->setDefaultLocale($default);
$this->setSearchOrder([
self::FROM_URI_PATH,
self::FROM_URI_PARAM,
self::FROM_COOKIE,
self::FROM_HEADER
]);
$this->setReqAttrName('locale');
$this->setUriParamName('locale');
$this->setCookieName('locale');
$this->setCookiePath('/');
$this->setCookieExpire(3600 * 24 * 30); // 30 days
$this->setLocaleCallback(function () { /* empty function */ });
}
/**
* @param array $locales a list of available locales
*/
public function setAvailableLocales(array $locales)
{
$this->availableLocales = [];
foreach ($locales as $locale) {
$this->availableLocales[] = $this->parseLocale($locale);
}
}
/**
* @param string $default the default locale
*/
public function setDefaultLocale(string $default)
{
$this->defaultLocale = $default;
}
/**
* @param array $order the order in which the search will be performed to
* resolve the locale
*/
public function setSearchOrder(array $order)
{
$this->searchOrder = $order;
}
/**
* @param callable $func callable to invoke when searching the locale
*/
public function setSearchCallback(callable $func)
{
$this->searchCallback = $func;
}
/**
* @param string $name the name for the attribute attached to the request
*/
public function setReqAttrName(string $name)
{
$this->reqAttrName = $name;
}
/**
* @param string $name the name for the locale URI parameter
*/
public function setUriParamName(string $name)
{
$this->uriParamName = $name;
}
/**
* @param string $name the name for the locale cookie
*/
public function setCookieName(string $name)
{
$this->cookieName = $name;
}
/**
* @param string $path the locale cookie's path
*/
public function setCookiePath(string $path)
{
$this->cookiePath = $path;
}
/**
* @param int $secs cookie expiration in seconds from now
*/
public function setCookieExpire(int $secs)
{
$this->cookieExpire = gmdate('D, d M Y H:i:s T', time() + $secs);
}
/**
* @param callable $func callable to invoke when locale is determined
*/
public function setLocaleCallback(callable $func)
{
$this->localeCallback = $func;
}
/**
* Add the locale to the request and response objects
*/
public function process(Request $req, RequestHandler $handler): Response
{
$locale = $this->getLocale($req);
$this->localeCallback->__invoke($locale);
$req = $req->withAttribute($this->reqAttrName, $locale);
$resp = $handler->handle($req);
if (in_array(self::FROM_COOKIE, $this->searchOrder) && $req->getAttribute('doSetLocaleCookie') === true) {
$resp = $this->withSetCookieLocale($resp, $locale);
}
return $resp;
}
public function withSetCookieLocale(Response $res, string $locale): Response
{
return $res->withAddedHeader('Set-Cookie', "{$this->cookieName}=$locale; Path={$this->cookiePath}; Expires={$this->cookieExpire}");
}
protected function getLocale(Request $req)
{
foreach ($this->searchOrder as $order) {
switch ($order) {
case self::FROM_URI_PATH:
$locale = $this->localeFromPath($req);
break;
case self::FROM_URI_PARAM:
$locale = $this->localeFromParam($req);
break;
case self::FROM_COOKIE:
$locale = $this->localeFromCookie($req);
break;
case self::FROM_HEADER:
$locale = $this->localeFromHeader($req);
break;
case self::FROM_CALLBACK:
$locale = $this->localeFromCallback($req);
break;
default:
throw new \DomainException('Unknown search option provided');
}
if (!empty($locale)) {
return $locale;
}
}
return $this->defaultLocale;
}
protected function localeFromPath(Request $req): string
{
list(, $value) = explode('/', $req->getUri()->getPath());
return $this->filterLocale($value);
}
protected function localeFromParam(Request $req): string
{
$params = $req->getQueryParams();
$value = $params[$this->uriParamName] ?? '';
return $this->filterLocale($value);
}
protected function localeFromCookie(Request $req): string
{
$cookies = $req->getCookieParams();
$value = $cookies[$this->cookieName] ?? '';
return $this->filterLocale($value);
}
protected function localeFromCallback(Request $req): string
{
if (!is_callable($this->searchCallback)) {
throw new \LogicException('Search callback not set');
}
$locale = $this->searchCallback->__invoke($req);
return $this->filterLocale($locale);
}
protected function localeFromHeader(Request $req): string
{
$header = $req->getHeaderLine('Accept-Language');
if (empty($header)) {
return '';
}
$values = $this->parse($header);
usort($values, [$this, 'sort']);
foreach ($values as $value) {
$value = $this->filterLocale($value['locale']);
if (!empty($value)) {
return $value;
}
}
// search language if a full locale is not found
foreach ($values as $value) {
$value = $this->filterLocale($value['language']);
if (!empty($value)) {
return $value;
}
}
return '';
}
protected function filterLocale(string $locale): string
{
// return the locale if it is available
foreach ($this->availableLocales as $avail) {
if ($locale == $avail['locale']) {
return $avail['locale'];
}
}
return '';
}
protected function parse(string $header): array
{
// the value may contain multiple languages separated by commas,
// possibly as locales (ex: en_US) with quality (ex: en_US;q=0.5)
$values = [];
foreach (explode(',', $header) as $lang) {
@list($locale, $quality) = explode(';', $lang, 2);
$val = $this->parseLocale(str_replace('*', $this->defaultLocale, $locale));
$val['quality'] = $this->parseQuality($quality ?? '');
$values[] = $val;
}
return $values;
}
protected function parseLocale(string $locale): array
{
// Locale format: language[_territory[.encoding[@modifier]]]
//
// Language and territory should be separated by an underscore
// although sometimes a hyphen is used. The language code should
// be lowercase. Territory should be uppercase. Take this into
// account but normalize the returned string as lowercase,
// underscore, uppercase.
//
// The possible codeset and modifier is discarded since the header
// *should* really list languages (not locales) in the first place
// and the chances of needing to present content at that level of
// granularity are pretty slim.
$lang = '([[:alpha:]]{2})';
$terr = '([[:alpha:]]{2})';
$code = '([-\\w]+)';
$mod = '([-\\w]+)';
$regex = "/$lang(?:[-_]$terr(?:\\.$code(?:@$mod)?)?)?/";
preg_match_all($regex, $locale, $m);
$locale = $language = strtolower($m[1][0]);
if (!empty($m[2][0])) {
$locale .= '_' . strtoupper($m[2][0]);
}
return [
'locale' => $locale,
'language' => $language
];
}
protected function parseQuality(string $quality): float
{
// If no quality is given then return 1 as this is the default quality
// defined in RFC 2616 HTTP/1.1 section 14.4 Accept-Language
// See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4
@list(, $value) = explode('=', $quality, 2);
return (float)($value ?: 1.0);
}
protected function sort(array $a, array $b): int
{
// Sort order is determined first by quality (higher values are
// placed first) then by order of their apperance in the header.
if ($a['quality'] < $b['quality']) {
return 1;
}
if ($a['quality'] == $b['quality']) {
return 0;
}
return -1;
}
}

View file

@ -0,0 +1,32 @@
<?php
namespace App\Middlewares;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Http\Factory\DecoratedResponseFactory;
use Slim\Http\Response;
use Slim\Psr7\Factory\ResponseFactory;
use Slim\Psr7\Factory\StreamFactory;
use Psr\Container\ContainerInterface;
use Slim\Views\Twig;
class TwigLocalizationMiddleware {
public function __construct(
private ContainerInterface $container
)
{
}
public function __invoke(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
{
$this->container->get(Twig::class)->getEnvironment()->addGlobal('locale', $request->getAttribute('locale'));
return $handler->handle($request);
}
}

View file

@ -10,6 +10,7 @@ class ContainerBuilder
{
private static array $definitions = [
'app',
'container'
];
public static function getContainerBuilder(\DI\ContainerBuilder $containerBuilder = null): \DI\ContainerBuilder

View file

@ -4,14 +4,22 @@ use App\App;
use Symfony\Component\Yaml\Yaml;
function getYAML($name) {
$path = App::getBasePath() . '/config/' . $name;
$path = App::getBasePath() . '/' . $name;
return Yaml::parseFile($path);
}
$misc = getYAML('config/misc.yaml');
return [
'basePath' => App::getBasePath(),
'foo' => 'barz',
'technologies' => getYAML('technologies.yaml')['technologies'],
'pro_projects' => getYAML('projects.yaml')['pro_projects'],
'side_projects' => getYAML('projects.yaml')['side_projects']
'misc' => $misc,
'last_update' => new DateTime($misc['updated_at']),
'links' => getYAML('config/links.yaml')['links'],
'technologies' => getYAML('config/technologies.yaml')['technologies'],
'pro_projects' => getYAML('config/projects.yaml')['pro_projects'],
'side_projects' => getYAML('config/projects.yaml')['side_projects'],
'locales' => [
'fr' => new \Adbar\Dot(getYAML('assets/locales/fr.yaml')),
'en' => new \Adbar\Dot(getYAML('assets/locales/en.yaml'))
],
];

47
src/config/container.php Normal file
View file

@ -0,0 +1,47 @@
<?php
use Psr\Container\ContainerInterface;
use Slim\Views\Twig;
use App\Middlewares\LocalizationMiddleware;
return [
Twig::class => function(ContainerInterface $container) {
$twig = Twig::create('../templates', ['cache' => false]);
$twig->getEnvironment()->addFunction(new \Twig\TwigFunction('getDynLocalizedStr', function (\Twig\Environment $env, $inp) {
$locale = $env->getGlobals()['locale'];
if (is_array($inp)) {
return $inp[$locale];
}
if (is_string($inp)) {
return $inp;
}
return 'invalid';
}, ['needs_environment' => true]));
$twig->getEnvironment()->addFunction(new \Twig\TwigFunction('getLocalizedStr', function (\Twig\Environment $env, $inp) use ($container) {
$locale = $env->getGlobals()['locale'];
if (!$container->get('locales')[$locale]->has($inp)) {
return $inp;
}
return $container->get('locales')[$locale]->get($inp);
}, ['needs_environment' => true]));
$twig->getEnvironment()->addFunction(new \Twig\TwigFunction('getLocale', function (\Twig\Environment $env) {
return $env->getGlobals()['locale'];
}, ['needs_environment' => true]));
$twig->getEnvironment()->addGlobal('lastUpdate', $container->get('last_update'));
return $twig;
},
LocalizationMiddleware::class => function () {
$availableLocales = ['en', 'fr'];
$defaultLocale = 'en';
return new LocalizationMiddleware($availableLocales, $defaultLocale);
}
];

View file

@ -2,6 +2,8 @@
namespace App;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Slim\Routing\RouteCollectorProxy;
function addRoutes(\Slim\App $app): void
@ -9,5 +11,14 @@ function addRoutes(\Slim\App $app): void
$container = $app->getContainer();
$app->get('/', [Controller\HomeController::class, 'getHome']);
$app->get('/project/{id}', [Controller\HomeController::class, 'getProject']);
$app->get('/about', [Controller\HomeController::class, 'getAbout']);
$app->get('/ping', [Controller\PingController::class, 'getPing']);
$addDoNotSetLocaleCookieMiddleware = function (ServerRequestInterface $req, RequestHandlerInterface $handler) {
return $handler->handle($req->withAttribute('doSetLocaleCookie', false));
};
$app->get('/en', [Controller\HomeController::class, 'switchToLocale'])->add($addDoNotSetLocaleCookieMiddleware);
$app->get('/fr', [Controller\HomeController::class, 'switchToLocale'])->add($addDoNotSetLocaleCookieMiddleware);
}

21
templates/about.html.twig Normal file
View file

@ -0,0 +1,21 @@
{% extends 'layout.html.twig' %}
{% block content %}
<div class="container">
<div class="about-header">
<a href="/">{{ getLocalizedStr('go-back-to-main') }}</a>
</div>
{% if getLocale() == 'fr' %}
<h1>A propos de ce site</h1>
<h2></h2>
<p>
</p>
{% endif %}
{% if getLocale() == 'en' %}
<h1>About this website</h1>
{% endif %}
</div>
{% endblock %}

View file

@ -0,0 +1,83 @@
<section id="contact">
<div class="container">
<div class="section-title">
<h2 class="title-text">{{ getLocalizedStr('contact.title') }}</h2>
</div>
<div class="means-of-contact">
<p>
{{ getLocalizedStr('contact.use-email') }} <a href="mailto:{{ email }}">{{ email }}</a>
</p>
<p>
{{ getLocalizedStr('contact.use-form') }}
</p>
</div>
<div class="contact-form-container">
<form id="contact-form">
<noscript>{{ getLocalizedStr('contact.no-script') }}</noscript>
<div class="alert alert-success hidden" id="contact-alert-success">
<div class="alert-content">
<div class="alert-icon">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-circle-check" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="12" cy="12" r="9"></circle>
<path d="M9 12l2 2l4 -4"></path>
</svg>
</div>
<div class="alert-text">
Thanks, your message has been sent!
</div>
</div>
</div>
<div class="alert alert-error hidden" id="contact-alert-error">
<div class="alert-content">
<div class="alert-icon">
<!-- <img src="/imgs/error.svg" alt="Error icon" /> -->
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-alert-circle" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<circle cx="12" cy="12" r="9"></circle>
<line x1="12" y1="8" x2="12" y2="12"></line>
<line x1="12" y1="16" x2="12.01" y2="16"></line>
</svg>
</div>
<div class="alert-text">
Sorry, there was an error while processing your message.
</div>
</div>
</div>
<div class="contact-form-first-group">
<div class="contact-form-input">
<label for="contact-form-name">{{ getLocalizedStr('contact.form.name') }}</label>
<input id="contact-form-name" type="text" name="name" minlength="2" maxlength="100" />
</div>
<div class="contact-form-input">
<label for="contact-form-email">{{ getLocalizedStr('contact.form.email') }}</label>
<input id="contact-form-email" type="email" name="email" required="required" maxlength="100" />
</div>
</div>
<div class="contact-form-subject contact-form-input">
<label for="contact-form-subject">{{ getLocalizedStr('contact.form.subject') }}</label>
<input id="contact-form-subject" type="text" name="subject" required="required" maxlength="100" />
</div>
<div class="contact-form-message contact-form-input">
<label for="contact-form-message">{{ getLocalizedStr('contact.form.message') }}</label>
<textarea id="contact-form-message" type="text" name="message" minlength="10" maxlength="8000"></textarea>
</div>
<div class="contact-form-container">
<button type="submit" class="btn contact-form-submit" id="contact-form-submit">
{{ getLocalizedStr('contact.form.submit') }}
</button>
<button class="btn contact-form-submit loading-button" id="contact-form-loading">
<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-refresh" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
<path d="M20 11a8.1 8.1 0 0 0 -15.5 -2m-.5 -4v4h4"></path>
<path d="M4 13a8.1 8.1 0 0 0 15.5 2m.5 4v-4h-4"></path>
</svg>
</button>
</div>
</form>
</div>
</div>
</section>

View file

@ -3,6 +3,7 @@
{% include('home/profile.html.twig') %}
{% include('home/technologies.html.twig') %}
{% include('home/projects.html.twig') %}
{# {% include('home/side_projects.html.twig') %} #}
{% include('home/links.html.twig') %}
{% include('home/contact.html.twig') %}
{% endblock %}

View file

@ -0,0 +1,26 @@
<section id="links">
<div class="container">
<div class="section-title">
<h2 class="title-text">{{ getLocalizedStr('links.title') }}</h2>
</div>
<div class="links-container">
<div class="links">
{% for link in links %}
<a class="link-card" href="{{ link.url }}" data-id="{{ link.id }}">
<div class="link-card-logo">
<img
src="imgs/logos/{{ link.thumbnail.src }}"
alt="{{ getDynLocalizedStr(link.thumbnail.alt) }}"
style="{{ link.thumbnail.style }}"
/>
</div>
<div class="link-card-title">
{{ getDynLocalizedStr(link.name) }}
</div>
</a>
{% endfor %}
</div>
</div>
</div>
</section>

View file

@ -1,28 +0,0 @@
<section>
<div class="container">
<div class="section-title">
<h2 class="title-text">Professional projects</h2>
</div>
<div class="projects">
<!-- Box thing -->
<div class="project-card">
<h3 class="project-title">
Annuaire associatif administré
</h3>
<p class="project-description">
Light add loss traditional on. Against capital political black network option.
Need education ready. Too knowledge once practice customer.
Environmental loss level southern protect guy.
</p>
</div>
<div class="project-card">
<h3 class="project-title">
Tracklift
</h3>
<p class="">
</p>
</div>
</div>
</div>
</section>

View file

@ -50,8 +50,8 @@
<div class="profile-shadow"></div>
</div>
<div class="profile-content">
<span class="title-1">Hi! I'm Matthieu</span>
<span class="title-2">I like to program stuff</span>
<span class="title-1">{{ getLocalizedStr('profile.intro.main') }}</span>
<span class="title-2">{{ getLocalizedStr('profile.intro.secondary') }}</span>
</div>
</div>
</section>

View file

@ -1,20 +1,22 @@
<section>
<section id="pro-projects">
<div class="container">
<div class="section-title">
<h2 class="title-text">Highlighted professional projects</h2>
<h2 class="title-text">
{{ getLocalizedStr('projects.pro.title') }}
</h2>
</div>
{% include 'home/projects_list.html.twig' with {'projects': pro_projects} %}
</div>
</section>
<section>
<section id="side-projects">
<div class="container">
<div class="section-title">
<h2 class="title-text">Highlighted side projects</h2>
<h2 class="title-text">
{{ getLocalizedStr('projects.side.title') }}
</h2>
</div>
{% include 'home/projects_list.html.twig' with {'projects': side_projects} %}
</div>
</section>

View file

@ -1,21 +1,43 @@
<div class="projects">
{% for project in projects %}
<div class="project-card" data-id="{{ project.id }}">
<h3 class="project-title">
<a href="{{ project.link }}">
{% if project.locales[locale].name is not defined %}
{{ project.name }}
{% else %}
{{ project.locales[locale].name }}
{% endif %}
<div class="project-top">
<div class="project-left">
<h3 class="project-title">
{% if project.detailled_page %}
<a href="/project/{{ project.id }}">
{{ getDynLocalizedStr(project.name) }}
</a>
{% else %}
{{ getDynLocalizedStr(project.name) }}
{% endif %}
</h3>
{% if project.description is defined %}
<p class="project-description">
{{ getDynLocalizedStr(project.description) }}
</p>
{% endif %}
</div>
<div class="project-right">
{% if project.thumbnail is defined %}
<img
class="project-img"
src="imgs/projects/{{ project.id }}/{{ project.thumbnail.src }}"
alt="{{ getDynLocalizedStr(project.thumbnail.alt) }}"
/>
{% endif %}
</div>
</div>
<div class="project-bottom">
<a href="{{ project.link}}">
{% if project.action is defined %}
{{ getDynLocalizedStr(project.action) }}
{% else %}
{{ getLocalizedStr('external-website') }}
{% endif %}
</a>
</h3>
{% if project.locales is defined %}
<p class="project-description">
{{ project.locales[locale].description }}
</p>
{% endif %}
</div>
</div>
{% endfor %}
</div>

View file

@ -1,23 +0,0 @@
<section>
<div class="container">
<div class="section-title">
<h2 class="title-text">Highlighted projects</h2>
</div>
<div class="projects">
<!-- Box thing -->
<div class="project-card">
<h3 class="project-title">
Annuaire associatif administré
</h3>
<p class="project-description">
Light add loss traditional on. Against capital political black network option.
Need education ready. Too knowledge once practice customer.
Environmental loss level southern protect guy.
</p>
</div>
<div class="project-card">
</div>
</div>
</div>
</section>

View file

@ -1,7 +1,9 @@
<section>
<section id="technologies">
<div class="container">
<div class="section-title">
<h2 class="title-text">Some of the technologies that I'm currently working with</h2>
<h2 class="title-text">
{{ getLocalizedStr('technologies.title') }}
</h2>
</div>
<div class="tech-mosaic">
{% for tech in technologies %}

View file

@ -1,14 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ getLocale() }}">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Matthieu Bessat - Web Developer</title>
<link href="dist/app.min.css" rel="stylesheet">
<title>{% block title %}{% endblock %}{{ getLocalizedStr('page.title') }}</title>
<link href="/dist/app.min.css" rel="stylesheet">
</head>
<body>
{% block content %}{% endblock %}
<script src="dist/app.min.js"></script>
<div class="footer-buffer"></div>
<footer class="footer">
<div class="footer-container container">
<div class="footer-left">
<div>
<a href="/about">{{ getLocalizedStr('about-website') }}</a>
</div>
<div>
{{ getLocalizedStr('last-update') }} <span>{{ lastUpdate | date("Y-m-d") }}</span>
</div>
</div>
<div class="footer-right">
<div class="locale-switch-short">
<a href="/fr">FR</a>
<a href="/en">EN</a>
</div>
<div class="locale-switch-large">
<div class="l">{{ getLocalizedStr('locales.choose') }}</div>
<div class="r">
<a href="/fr">Français</a>
<a href="/en">English</a>
</div>
</div>
</div>
</div>
</footer>
<script src="/dist/app.min.js"></script>
</body>
</html>

View file

@ -0,0 +1,40 @@
{% extends 'layout.html.twig' %}
{% block title %}
{{ getDynLocalizedStr(project.name) }}
{% endblock %}
{% block content %}
<div class="container">
<div class="about-header">
<a href="/">{{ getLocalizedStr('go-back-to-main') }}</a>
</div>
<div>
<h1>{{ getLocalizedStr('projects.project') }} {{ getDynLocalizedStr(project.name) }}</h1>
{% if project.background is defined %}
<h2>{{ getLocalizedStr('projects.background') }}</h2>
<p>
{{ getDynLocalizedStr(project.background) }}
</p>
{% endif %}
{% if project.solution is defined %}
<h2>{{ getLocalizedStr('projects.solution') }}</h2>
<p>
{{ getDynLocalizedStr(project.solution) }}
</p>
{% endif %}
<h2>{{ getLocalizedStr('projects.technologies') }}</h2>
<p>
<div class="chips">
{% for tech in project.technologies %}
<div class="project-technology chip">
{{ tech.name }}
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}

33
wikidata.txt Normal file
View file

@ -0,0 +1,33 @@
Q11707176 Cascading Style Sheets Level 3 style sheet language
Q2053 HTML5 fifth and current version of the hypertext markup language for structuring and presenting content for the World Wide Web
Q230036 jQuery JavaScript library
Q756100 Node.js JavaScript runtime environment
Q49007 yarn long continuous length of interlocked fibers
Q22909730 webpack open-source JavaScript module bundler
Q1572865 Sass Syntactically Awesome StyleSheets stylesheet language
no result
Q2225 electron subatomic particle with negative electric charge
Q28865 Python general-purpose programming language
Q5618801 Gunicorn web server
Q1537445 stevedore occupation of loading and unloading ships
Q411840 cadmium chloride chemical compound
Q306144 nginx open source web server and a reverse proxy server
Q81683 varnish transparent, hard, protective finish or film used in painting
Q4778915 Cloudflare Web infrastructure and website security company
Q787177 MariaDB Database management system, relational, open source, community developed fork of MySQL
Q1165204 MongoDB cross-platform document-oriented database
Q105097049 Curator tool to tend Elasticsearch indices
Q2136322 Redis NoSQL database management software
Q17193 Philippine peso official currency of the Philippines
Q108740058 API Platform
Q36834 composer musician who is an author of music in any form; person who creates music, either by musical notation or oral tradition
no result
Q13634357 Laravel open source web application framework, written in PHP
Q1322933 Symfony PHP web application framework for MVC applications
no result
Q24589705 Vue.js JavaScript framework
Q55641291 Nuxt.js JavaScript framework
no result
Q11413 go board game for two players that originated in China more than 2,500 years ago
Q16878131 Express.js JavaScript framework

View file

@ -251,11 +251,24 @@ assign-symbols@^1.0.0:
resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367"
integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=
asynckit@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
atob@^2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
axios@^0.27.2:
version "0.27.2"
resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972"
integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==
dependencies:
follow-redirects "^1.14.9"
form-data "^4.0.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
@ -456,6 +469,13 @@ colorette@^1.2.1:
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
combined-stream@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
dependencies:
delayed-stream "~1.0.0"
commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@ -554,6 +574,11 @@ define-property@^2.0.2:
is-descriptor "^1.0.2"
isobject "^3.0.1"
delayed-stream@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
detect-file@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7"
@ -756,11 +781,25 @@ findup-sync@^3.0.0:
micromatch "^3.0.4"
resolve-dir "^1.0.1"
follow-redirects@^1.14.9:
version "1.15.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5"
integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==
for-in@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fragment-cache@^0.2.1:
version "0.2.1"
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
@ -1224,6 +1263,18 @@ mime-db@1.49.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.49.0.tgz#f3dfde60c99e9cf3bc9701d687778f537001cbed"
integrity sha512-CIc8j9URtOVApSFCQIF+VBkX1RwXp/oMMOrqdyXSBXq5RWNEsRfyj1kiRnQgmNXmHxPoFIxOroKA3zcU9P+nAA==
mime-db@1.52.0:
version "1.52.0"
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
mime-types@^2.1.12:
version "2.1.35"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
dependencies:
mime-db "1.52.0"
mime-types@^2.1.27:
version "2.1.32"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.32.tgz#1d00e89e7de7fe02008db61001d9e02852670fd5"