Backend: Added the ability to gather a project title from a software/programming language. Frontend: Tooltip implementation with backend.

This commit is contained in:
Yohan Boujon 2024-01-28 21:28:07 +01:00
parent 1a3297199f
commit b9e78d7e49
7 changed files with 168 additions and 8 deletions

View file

@ -86,3 +86,9 @@ pub struct AllTags {
pub type_icon: Option<String>, pub type_icon: Option<String>,
pub color: Option<String> pub color: Option<String>
} }
#[derive(Deserialize, Serialize)]
pub struct SimpleProject {
pub project_id: Option<i32>,
pub title: Option<String>
}

View file

@ -10,7 +10,10 @@ use std::net::SocketAddr;
use tower_http::cors::CorsLayer; use tower_http::cors::CorsLayer;
mod db; mod db;
use db::{Education, Experience, Info, Languages, ProgrammingLanguages, Project, Softwares, Tags, AllTags}; use db::{
AllTags, Education, Experience, Info, Languages, ProgrammingLanguages, Project, SimpleProject,
Softwares, Tags,
};
#[tokio::main] #[tokio::main]
async fn main() -> Result<()> { async fn main() -> Result<()> {
@ -30,6 +33,14 @@ async fn main() -> Result<()> {
.route("/skills/:id", get(skills)) .route("/skills/:id", get(skills))
.route("/tags/:info_id/:project_id", get(tags)) .route("/tags/:info_id/:project_id", get(tags))
.route("/tags/:id", get(alltags)) .route("/tags/:id", get(alltags))
.route(
"/getproject_programming/:programming_id",
get(getproject_programming),
)
.route(
"/getproject_software/:software_id",
get(getproject_software),
)
.with_state(pool) .with_state(pool)
.layer(CorsLayer::very_permissive()); .layer(CorsLayer::very_permissive());
@ -166,3 +177,41 @@ async fn alltags(Path(info_id): Path<i32>, State(pool): State<PgPool>) -> Json<V
.unwrap_or(vec![]); .unwrap_or(vec![]);
Json(datas) Json(datas)
} }
async fn getproject_programming(
Path(programming_id): Path<i32>,
State(pool): State<PgPool>,
) -> Json<Vec<SimpleProject>> {
let datas = sqlx::query_as!(
SimpleProject,
"SELECT project_id, title
FROM public.project p
JOIN public.project_tags pt ON p.id = pt.project_id
WHERE pt.programming_languages_id = $1
",
programming_id
)
.fetch_all(&pool)
.await
.unwrap_or(vec![]);
Json(datas)
}
async fn getproject_software(
Path(software_id): Path<i32>,
State(pool): State<PgPool>,
) -> Json<Vec<SimpleProject>> {
let datas = sqlx::query_as!(
SimpleProject,
"SELECT project_id, title
FROM public.project p
JOIN public.project_tags pt ON p.id = pt.project_id
WHERE pt.softwares_id = $1
",
software_id
)
.fetch_all(&pool)
.await
.unwrap_or(vec![]);
Json(datas)
}

View file

@ -9,9 +9,15 @@
export let color = "#000000"; export let color = "#000000";
export let type_icon = "mdi"; export let type_icon = "mdi";
export let shadow_color = null; export let shadow_color = null;
export let show_tooltip = false;
export let tooltip_data = [];
const white = shouldColorBeWhite(color.slice(1)); const white = shouldColorBeWhite(color.slice(1));
let style; let style;
let pill_arrow;
let pill_tooltip;
let main_pill;
if (shadow_color === null) { if (shadow_color === null) {
style = `background-color: ${color}; style = `background-color: ${color};
box-shadow: 0px 8px 18px -1px ${color}60;`; box-shadow: 0px 8px 18px -1px ${color}60;`;
@ -19,12 +25,47 @@
style = `background-color: ${color}; style = `background-color: ${color};
box-shadow: 0px 8px 18px -1px ${shadow_color}60;`; box-shadow: 0px 8px 18px -1px ${shadow_color}60;`;
} }
function showingTooltip(visible) {
if (visible && tooltip_data.length > 0) {
pill_tooltip.style.visibility = "visible";
pill_arrow.style.visibility = "visible";
// adjusting top
pill_tooltip.style.top = `${
main_pill.offsetTop - pill_tooltip.offsetHeight - 17
}px`;
if (white) {
pill_tooltip.style.boxShadow = `0px 8px 18px -1px ${color}60`;
pill_arrow.style.filter = `drop-shadow(0px 8px 18px ${color}40)`;
} else {
pill_tooltip.style.boxShadow = `0px 8px 18px -1px #261C2C30`;
pill_arrow.style.filter = `drop-shadow(0px 8px 18px #261C2C20)`;
}
} else {
pill_tooltip.style.visibility = "hidden";
pill_arrow.style.visibility = "hidden";
}
}
</script> </script>
<div <div
class={white ? "pill-container pill-white" : "pill-container pill-black"} class={white ? "pill-container pill-white" : "pill-container pill-black"}
{style} {style}
on:focus={() => true}
on:mouseover={() => showingTooltip(true)}
on:mouseleave={() => showingTooltip(false)}
role="link"
tabindex="0"
bind:this={main_pill}
> >
{#if show_tooltip === true}
<div class="pill-arrow" bind:this={pill_arrow} />
<div class="pill-tooltip" bind:this={pill_tooltip}>
{#each tooltip_data as td}
<p>{td.title}</p>
{/each}
</div>
{/if}
{#if type_icon === "simpleicons"} {#if type_icon === "simpleicons"}
<img <img
height="20" height="20"

View file

@ -37,3 +37,35 @@
.pill-container svg { .pill-container svg {
margin-right: 0.5rem; margin-right: 0.5rem;
} }
.pill-tooltip {
position: absolute;
z-index: 1;
background-color: var(--color-background);
color: var(--color-text);
min-width: 5rem;
border-radius: 0.4rem;
display: flex;
justify-content: center;
visibility: hidden;
transition: all .3s ease 0s;
box-shadow: none;
display: flex;
flex-direction: column;
padding-left: 1rem;
padding-right: 1rem;
}
.pill-arrow {
position: absolute;
z-index: 2;
transform: translateY(-2.2rem);
width: 0px;
height: 0px;
border-left: 20px solid transparent;
border-right: 20px solid transparent;
border-top: 20px solid var(--color-background);
visibility: hidden;
transition: all .3s ease 0s;
filter: none;
}

View file

@ -13,8 +13,10 @@ export function processData(data) {
const education = arrangeById(data.education); const education = arrangeById(data.education);
const skills = data.skills; const skills = data.skills;
const tags = data.tags; const tags = data.tags;
const project_programming = data.project_programming;
const project_software = data.project_software;
return {info, experiences, education, skills, tags}; return {info, experiences, education, skills, tags, project_programming, project_software};
} else { } else {
return null; // Indicates an error return null; // Indicates an error
} }

View file

@ -18,10 +18,14 @@ export async function load(context) {
} }
const infos = []; const infos = [];
const dataToGather = ['info', 'education', 'experience', 'skills/1', 'tags/1']; const project_software = [];
for (const url of dataToGather) { const project_programming = [];
const dataToGather =
['info', 'education', 'experience', 'skills/1', 'tags/1'];
for (const [index, url] of dataToGather.entries()) {
const res = await fetchData(url); const res = await fetchData(url);
if (res.status == 0) { if (res.status == 0) {
// Pushing data
infos.push(res.data); infos.push(res.data);
} else { } else {
return { return {
@ -30,6 +34,30 @@ export async function load(context) {
} }
} }
for (let i = 0; i < infos[3][2].length; i++) {
const res =
await fetchData(`getproject_software/${i + 1}`);
if (res.status == 0) {
project_software.push(res.data);
} else {
return {
status: res.status
}
}
}
for (let i = 0; i < infos[3][1].length; i++) {
const res =
await fetchData(`getproject_programming/${i + 1}`);
if (res.status == 0) {
project_programming.push(res.data);
}
else {
return {
status: res.status
}
}
}
return { return {
status: 0, status: 0,
info: infos[0], info: infos[0],
@ -42,5 +70,7 @@ export async function load(context) {
languages: infos[3][3], languages: infos[3][3],
}, },
tags: infos[4], tags: infos[4],
project_programming: project_programming,
project_software: project_software,
}; };
} }

View file

@ -134,14 +134,14 @@
<Section icon={mdiPencil} title="Skills" /> <Section icon={mdiPencil} title="Skills" />
<SubSection icon={mdiXml} title="Programming Languages"/> <SubSection icon={mdiXml} title="Programming Languages"/>
<div class="subsection"> <div class="subsection">
{#each cv.skills.programming_languages as pilldata} {#each cv.skills.programming_languages as pilldata, index (index)}
<Pill name={pilldata.lang} type_icon={pilldata.type_icon} icon={pilldata.icon} color={pilldata.color}/> <Pill name={pilldata.lang} type_icon={pilldata.type_icon} icon={pilldata.icon} color={pilldata.color} show_tooltip={true} tooltip_data={cv.project_programming[index]}/>
{/each} {/each}
</div> </div>
<SubSection icon={mdiApplication} title="Software"/> <SubSection icon={mdiApplication} title="Software"/>
<div class="subsection"> <div class="subsection">
{#each cv.skills.softwares as pilldata} {#each cv.skills.softwares as pilldata, index (index)}
<Pill name={pilldata.software} type_icon={pilldata.type_icon} icon={pilldata.icon} color={pilldata.color}/> <Pill name={pilldata.software} type_icon={pilldata.type_icon} icon={pilldata.icon} color={pilldata.color} show_tooltip={true} tooltip_data={cv.project_software[index]}/>
{/each} {/each}
</div> </div>
<SubSection icon={mdiEarth} title="Languages"/> <SubSection icon={mdiEarth} title="Languages"/>