Frontend: Added 'Experience' component. Backend: Modified database.

This commit is contained in:
Yohan Boujon 2023-11-16 22:32:53 +01:00
parent 057d2c7955
commit a65045becb
11 changed files with 205 additions and 30 deletions

View file

@ -6,11 +6,12 @@
CREATE TABLE public.education ( CREATE TABLE public.education (
id serial4 NOT NULL, id serial4 NOT NULL,
start_year time NULL, start_year date NULL,
end_year time NULL, end_year date NULL,
school text NULL, school text NULL,
speciality text NULL, speciality text NULL,
school_location text NULL, school_location text NULL,
school_options text NULL, school_options text NULL,
picture_url text NULL,
CONSTRAINT education_pkey PRIMARY KEY (id) CONSTRAINT education_pkey PRIMARY KEY (id)
); );

View file

@ -12,5 +12,6 @@ CREATE TABLE public.experience (
enterprise_location text NULL, enterprise_location text NULL,
start_year date NULL, start_year date NULL,
end_year date NULL, end_year date NULL,
picture_url text NULL,
CONSTRAINT experience_pkey PRIMARY KEY (id) CONSTRAINT experience_pkey PRIMARY KEY (id)
); );

View file

@ -33,6 +33,7 @@ pub struct Experience {
pub enterprise_location: Option<String>, pub enterprise_location: Option<String>,
pub start_year: Option<NaiveDate>, pub start_year: Option<NaiveDate>,
pub end_year: Option<NaiveDate>, pub end_year: Option<NaiveDate>,
pub picture_url: Option<String>
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]

View file

@ -61,7 +61,7 @@ async fn education(State(pool): State<PgPool>) -> Result<Json<Vec<Education>>> {
async fn experience(State(pool): State<PgPool>) -> Result<Json<Vec<Experience>>> { async fn experience(State(pool): State<PgPool>) -> Result<Json<Vec<Experience>>> {
let datas = sqlx::query_as!( let datas = sqlx::query_as!(
Experience, Experience,
"SELECT id, job_position, job_description, enterprise, enterprise_location, start_year, end_year FROM public.experience" "SELECT * FROM public.experience"
) )
.fetch_all(&pool) .fetch_all(&pool)
.await?; .await?;

View file

@ -0,0 +1,54 @@
<script>
import SvgIcon from "@jamescoyle/svelte-icon";
import { mdiMapMarker, mdiCardText, mdiOfficeBuilding } from "@mdi/js";
import "$lib/css/experience.css";
import { formatDate } from "$lib/js/date.js"
export let active = false;
export let data;
const enterprise = data.enterprise;
const location = data.enterprise_location;
const description = data.job_description;
const position = data.job_position;
const end_year = data.end_year === null ? '' : formatDate(data.end_year);
const start_year = data.start_year === null ? '' : (data.end_year === null ? formatDate(data.start_year) : ' - '+formatDate(data.start_year));
const picture = data.picture_url;
</script>
<div class="experience-container">
<div class="experience-main {active ? '' : 'experience-unactive'}">
<div class="experience-img-container">
<img class="experience-img" src={picture} alt="Experience" />
</div>
<div class="experience-text-container">
<h1 class="experience-title">{position}</h1>
<div class="experience-subtitle-container">
<SvgIcon size="35" path={mdiOfficeBuilding} type="mdi" />
<p class="experience-subtitle experience-aftericon">
{enterprise}
</p>
</div>
{#if location}
<div class="experience-subtitle-container">
<SvgIcon size="35" path={mdiMapMarker} type="mdi" />
<p class="experience-subtitle experience-aftericon">
{location}
</p>
</div>
{/if}
{#if description}
<div class="experience-subtitle-container">
<SvgIcon size="35" path={mdiCardText} type="mdi" />
<p class="experience-subtitle experience-aftericon">
{description}
</p>
</div>
{/if}
</div>
</div>
<div class="experience-time">
<div class="experience-bubble" />
<h2 class="experience-date">{`${end_year}${start_year}`}</h2>
</div>
</div>

View file

@ -8,6 +8,8 @@
// Exported values // Exported values
export let data = []; export let data = [];
export let type; export let type;
export let typename;
export let timeline = false;
// Slideshow global variables // Slideshow global variables
let slideshow_index = 0; let slideshow_index = 0;
@ -16,32 +18,37 @@
// Timeline global variables // Timeline global variables
let slideshow; let slideshow;
let bubbles = []; let bubbles = [];
onMount(() => { if (timeline) {
for (const element of document.getElementsByClassName( onMount(() => {
"education-bubble" for (const element of document.getElementsByClassName(
)) { `${typename}-bubble`
bubbles.push({ )) {
left: element.offsetLeft, bubbles.push({
top: element.offsetTop, left: element.offsetLeft,
}); top: element.offsetTop,
} });
for (const div of createTimeLine(bubbles)) { }
slideshow.appendChild(div); for (const div of createTimeLine(bubbles,typename)) {
} slideshow.appendChild(div);
}); }
});
}
function slideEducation() { function slideCards() {
const slideshowElements = document.querySelectorAll( const slideshowElements = document.querySelectorAll(
".education-container" `.${typename}-container`
); );
const slideshowTimeline = const slideshowTimeline =
document.querySelectorAll(".education-string"); document.querySelectorAll(`.${typename}-string`);
console.log(slideshowTimeline);
if (slideshow_index >= data.length - 1) { if (slideshow_index >= data.length - 1) {
slideshow_hidden = []; slideshow_hidden = [];
slideshow_index = 0; slideshow_index = 0;
for(const timeline of slideshowTimeline) { if (timeline) {
timeline.style.backgroundColor = ''; for (const timeline of slideshowTimeline) {
timeline.style.backgroundColor = "";
}
} }
} else { } else {
slideshow_hidden.push(slideshow_index); slideshow_hidden.push(slideshow_index);
@ -58,10 +65,13 @@
newtransformValue *= 1.1; newtransformValue *= 1.1;
} }
element.style.transform = `translateX(-${newtransformValue}px)`; element.style.transform = `translateX(-${newtransformValue}px)`;
if(slideshowTimeline[id] != undefined) { if (slideshowTimeline[id] != undefined) {
slideshowTimeline[id].style.transform = `translateX(-${transformValue}px)`; slideshowTimeline[
id
].style.transform = `translateX(-${transformValue}px)`;
if (slideshow_hidden.includes(id)) { if (slideshow_hidden.includes(id)) {
slideshowTimeline[id].style.backgroundColor = 'var(--color-background)'; slideshowTimeline[id].style.backgroundColor =
"var(--color-background)";
} }
} }
}); });
@ -69,7 +79,7 @@
</script> </script>
<div class="slideshow" bind:this={slideshow}> <div class="slideshow" bind:this={slideshow}>
<button class="slideshow_btn" on:click={slideEducation}> <button class="slideshow_btn" on:click={slideCards}>
<div> <div>
<SvgIcon <SvgIcon
size="45" size="45"

View file

@ -26,6 +26,7 @@
.education-img { .education-img {
max-height: 15rem; max-height: 15rem;
min-width: 20rem; min-width: 20rem;
max-width: 25rem;
} }
.education-text-container { .education-text-container {

View file

@ -0,0 +1,103 @@
.experience-container {
display: flex;
flex-direction: column;
transition: all .3s ease 0s;
}
.experience-main {
display: flex;
border-radius: 0.4rem;
box-shadow: 0px 20px 50px -10px rgba(0, 0, 0, 0.2);
background-color: var(--color-background);
height: 22rem;
margin-left: 2.5rem;
margin-right: 2.5rem;
z-index: 1;
transition: all .3s ease 0s;
}
.experience-img-container{
display: flex;
margin: 1.5rem;
justify-content: center;
align-items: center;
}
.experience-img {
max-height: 15rem;
min-width: 20rem;
max-width: 25rem;
}
.experience-text-container {
margin: 1.5rem;
width: 25rem;
}
.experience-title {
color: var(--color-text);
font-family: 'Gabarito', sans-serif;
font-weight: 600;
font-size: 2rem;
}
.experience-subtitle-container {
display: flex;
flex-direction: row;
align-items: center;
}
.experience-aftericon {
margin-left: 0.5rem;
}
.experience-subtitle {
color: var(--color-text);
font-family: 'Gabarito', sans-serif;
font-weight: 500;
font-size: 1.5rem;
margin-top: 0.75rem;
margin-bottom: 0.75rem;
}
.experience-unactive {
scale: 0.9;
margin-left: 2.25rem !important;
margin-right: 2.25rem !important;
box-shadow: 0 7px 30px -10px rgba(150,170,180,0.5) !important;
}
.experience-time {
margin-top: 3rem;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.experience-bubble {
z-index: 2;
width: 5rem;
height: 5rem;
background-color: var(--color-special);
box-shadow: 0px 2px 5px -1px rgba(0, 0, 0, 0.2);
border-radius: 50%;
transition: all .3s ease 0s;
display: flex;
justify-content: center;
align-items: center;
}
.experience-date {
color: var(--color-special);
font-weight: 700;
}
.experience-string {
z-index: -1;
position: absolute;
height: 3rem;
background-color: var(--color-special);
opacity: 0.5;
transition: all .3s ease 0s;
}

View file

@ -9,7 +9,7 @@ function arrangeById(array) {
export function processData(data) { export function processData(data) {
if (data.status === 0) { if (data.status === 0) {
const info = data.info[0]; const info = data.info[0];
const experiences = data.experience; const experiences = arrangeById(data.experience);
const education = arrangeById(data.education); const education = arrangeById(data.education);
const skills = data.skills[1]; const skills = data.skills[1];
const projects = data.skills[0]; const projects = data.skills[0];

View file

@ -1,10 +1,10 @@
export function createTimeLine(positionsArray) { export function createTimeLine(positionsArray,typename) {
let divArray = [] let divArray = []
console.log(positionsArray); console.log(positionsArray);
for(let i=0; i<positionsArray.length-1; i++) for(let i=0; i<positionsArray.length-1; i++)
{ {
var newDiv = document.createElement('div'); var newDiv = document.createElement('div');
newDiv.className = 'education-string'; newDiv.className = `${typename}-string`;
const left = positionsArray[i].left + (2.5*16); const left = positionsArray[i].left + (2.5*16);
newDiv.style.left = `${left}px`; newDiv.style.left = `${left}px`;
const top = positionsArray[i].top + 16; const top = positionsArray[i].top + 16;

View file

@ -18,13 +18,15 @@
// Main // Main
import Section from "$lib/components/section.svelte"; import Section from "$lib/components/section.svelte";
import Education from "$lib/components/education.svelte"; import Education from "$lib/components/education.svelte";
import Experience from "$lib/components/experience.svelte"
import SlideShow from "$lib/components/slideshow.svelte"; import SlideShow from "$lib/components/slideshow.svelte";
import { mdiSchool } from "@mdi/js"; import { mdiSchool,mdiBriefcase } from "@mdi/js";
export let data; export let data;
const cv = data.status == 0 ? processData(data) : undefined; const cv = data.status == 0 ? processData(data) : undefined;
const birth_year = const birth_year =
data.status == 0 ? formatDate(cv.info.birth_year) : undefined; data.status == 0 ? formatDate(cv.info.birth_year) : undefined;
console.log(cv.experience)
</script> </script>
{#if data.status == 0} {#if data.status == 0}
@ -63,7 +65,9 @@
<h1 class="name">{cv.info.full_name}</h1> <h1 class="name">{cv.info.full_name}</h1>
<h2 class="name">Apprentice Engineer Automatic/Electronic</h2> <h2 class="name">Apprentice Engineer Automatic/Electronic</h2>
<Section icon={mdiSchool} title="Education" /> <Section icon={mdiSchool} title="Education" />
<SlideShow data={cv.education} type={Education} /> <SlideShow data={cv.education} type={Education} typename="education" timeline=true />
<Section icon={mdiBriefcase} title="Experience" />
<SlideShow data={cv.experiences} type={Experience} typename="experience" timeline=true />
</div> </div>
</div> </div>
{:else} {:else}