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

@ -85,4 +85,10 @@ pub struct AllTags {
pub icon: Option<String>,
pub type_icon: 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;
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]
async fn main() -> Result<()> {
@ -30,6 +33,14 @@ async fn main() -> Result<()> {
.route("/skills/:id", get(skills))
.route("/tags/:info_id/:project_id", get(tags))
.route("/tags/:id", get(alltags))
.route(
"/getproject_programming/:programming_id",
get(getproject_programming),
)
.route(
"/getproject_software/:software_id",
get(getproject_software),
)
.with_state(pool)
.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![]);
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 type_icon = "mdi";
export let shadow_color = null;
export let show_tooltip = false;
export let tooltip_data = [];
const white = shouldColorBeWhite(color.slice(1));
let style;
let pill_arrow;
let pill_tooltip;
let main_pill;
if (shadow_color === null) {
style = `background-color: ${color};
box-shadow: 0px 8px 18px -1px ${color}60;`;
@ -19,12 +25,47 @@
style = `background-color: ${color};
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>
<div
class={white ? "pill-container pill-white" : "pill-container pill-black"}
{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"}
<img
height="20"

View file

@ -36,4 +36,36 @@
.pill-container svg {
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 skills = data.skills;
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 {
return null; // Indicates an error
}

View file

@ -18,10 +18,14 @@ export async function load(context) {
}
const infos = [];
const dataToGather = ['info', 'education', 'experience', 'skills/1', 'tags/1'];
for (const url of dataToGather) {
const project_software = [];
const project_programming = [];
const dataToGather =
['info', 'education', 'experience', 'skills/1', 'tags/1'];
for (const [index, url] of dataToGather.entries()) {
const res = await fetchData(url);
if (res.status == 0) {
// Pushing data
infos.push(res.data);
} else {
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 {
status: 0,
info: infos[0],
@ -42,5 +70,7 @@ export async function load(context) {
languages: infos[3][3],
},
tags: infos[4],
project_programming: project_programming,
project_software: project_software,
};
}

View file

@ -134,14 +134,14 @@
<Section icon={mdiPencil} title="Skills" />
<SubSection icon={mdiXml} title="Programming Languages"/>
<div class="subsection">
{#each cv.skills.programming_languages as pilldata}
<Pill name={pilldata.lang} type_icon={pilldata.type_icon} icon={pilldata.icon} color={pilldata.color}/>
{#each cv.skills.programming_languages as pilldata, index (index)}
<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}
</div>
<SubSection icon={mdiApplication} title="Software"/>
<div class="subsection">
{#each cv.skills.softwares as pilldata}
<Pill name={pilldata.software} type_icon={pilldata.type_icon} icon={pilldata.icon} color={pilldata.color}/>
{#each cv.skills.softwares as pilldata, index (index)}
<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}
</div>
<SubSection icon={mdiEarth} title="Languages"/>