Backend: Added the ability to gather a project title from a software/programming language. Frontend: Tooltip implementation with backend.
This commit is contained in:
parent
1a3297199f
commit
b9e78d7e49
7 changed files with 168 additions and 8 deletions
|
@ -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>
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"/>
|
||||||
|
|
Loading…
Add table
Reference in a new issue