add project detail

This commit is contained in:
2024-11-10 05:18:23 +01:00
parent c1cce38148
commit e206765044
6 changed files with 165 additions and 6 deletions

File diff suppressed because one or more lines are too long

View File

@@ -14,7 +14,7 @@
{{ chip::chip(text=technology) }} {{ chip::chip(text=technology) }}
{% endfor %} {% endfor %}
</div> </div>
<a href="/projects/{{ project.name }}" <a href="/projects/project/{{ project.name }}"
class="w-full p-2 text-center border border-yellow-400 rounded-xl hover:bg-yellow-400">Read more</a> class="w-full p-2 text-center border border-yellow-400 rounded-xl hover:bg-yellow-400">Read more</a>
<div class="flex flex-wrap gap-2 sm:justify-center md:justify-start"> <div class="flex flex-wrap gap-2 sm:justify-center md:justify-start">
{% if project.githubUrl %} {% if project.githubUrl %}

View File

@@ -0,0 +1,58 @@
{% import "website/macros/chip.html" as chip %}
{% extends "website/base.html" %} {% block content %}
<div
class="flex flex-col items-center justify-center w-full gap-4 p-4 mt-16 prose text-white lg:prose-lg xl:prose-xl dark:prose-invert">
<h1>{{ project.name }}</h1>
<span class="mt-8"></span>
<section class="mx-auto prose lg:prose-lg xl:prose-xl dark:prose-invert">
{{ project.description| markdown | safe }}
</section>
<span class="mt-8"></span>
<section class="flex flex-col items-center justify-center w-full mx-auto">
<h1>Technologies</h1>
<div class="flex flex-wrap gap-2">
{% for technology in project.technologies %}
{{ chip::chip(text=technology) }}
{% endfor %}
</div>
</section>
<span class="mt-8"></span>
{% if project.thumbnails|length > 0 %}
<section class="flex flex-col items-center justify-center w-full mx-auto">
<h1>Gallery</h1>
<div class="flex flex-col gap-4">
{% for thumbnail in project.thumbnails %}
<img src="{{ thumbnail }}" alt="project thumbnail" class="mx-auto">
{% endfor %}
</div>
</section>
{% endif %}
<span class="mt-8"></span>
<section class="flex flex-col items-center justify-center w-full mx-auto">
{% if project.github_url == "" and project.visit_url == "" and project.download_url == "" %}
{% else %}
<h1>Links</h1>
<div class="flex flex-wrap gap-2">
{% if project.github_url %}
<a href="{{ project.github_url }}" target="_blank"
class="w-full p-2 text-center text-white no-underline border border-yellow-400 hover:text-white rounded-xl hover:bg-yellow-400">
<span class="fab fa-github"></span> CODE
</a>
{% endif %}
{% if project.visit_url %}
<a href="{{ project.visit_url }}" target="_blank"
class="w-full p-2 text-center text-white no-underline border border-yellow-400 hover:text-white rounded-xl hover:bg-yellow-400">
<span class="fas fa-eye"></span> LIVE
</a>
{% endif %}
{% if project.download_url %}
<a href="{{ project.download_url }}" target="_blank"
class="w-full p-2 text-center text-white no-underline border border-yellow-400 hover:text-white rounded-xl hover:bg-yellow-400">
<span class="fas fa-cloud-download-alt"></span> DOWNLOAD
</a>
{% endif %}
</div>
{% endif %}
</section>
</div>
{% endblock content %}

View File

@@ -38,12 +38,29 @@ pub async fn render_projects(
views::website::projects(v, &ctx).await views::website::projects(v, &ctx).await
} }
pub async fn render_project_detail(
ViewEngine(v): ViewEngine<TeraView>,
State(ctx): State<AppContext>,
Path(id): Path<i32>,
) -> Result<impl IntoResponse> {
views::website::project_detail(v, &ctx, id).await
}
pub async fn render_project_detail_from_name(
ViewEngine(v): ViewEngine<TeraView>,
State(ctx): State<AppContext>,
Path(name): Path<String>,
) -> Result<impl IntoResponse> {
views::website::project_detail_from_name(v, &ctx, name).await
}
pub fn routes() -> Routes { pub fn routes() -> Routes {
Routes::new() Routes::new()
.add("/", get(render_index)) .add("/", get(render_index))
.add("/upload", get(render_upload)) .add("/upload", get(render_upload))
.add("/login", get(render_login)) .add("/login", get(render_login))
.add("/projects", get(render_projects)) .add("/projects", get(render_projects))
.add("/projets/:project_name", get(render_projects)) .add("/projects/:id", get(render_project_detail))
.add("/projects/project/:name", get(render_project_detail_from_name))
.add("/about", get(render_about)) .add("/about", get(render_about))
} }

View File

@@ -1,6 +1,15 @@
use loco_rs::prelude::*; use loco_rs::prelude::*;
use crate::{models::{_entities::{project_thumbnails, projects::{self, Entity, Model}}, projects::{get_category_from_string, ProjectDto}}, shared::get_technologies_from_string::get_technologies_from_string}; use crate::{
models::{
_entities::{
project_thumbnails,
projects::{self, Entity, Model},
},
projects::{get_category_from_string, ProjectDto},
},
shared::get_technologies_from_string::get_technologies_from_string,
};
pub async fn get_all_projects(ctx: &AppContext) -> Result<Vec<Model>> { pub async fn get_all_projects(ctx: &AppContext) -> Result<Vec<Model>> {
let projects = Entity::find().all(&ctx.db).await?; let projects = Entity::find().all(&ctx.db).await?;
@@ -13,6 +22,15 @@ pub async fn get_project_by_id(ctx: &AppContext, id: i32) -> Result<Model> {
Ok(project) Ok(project)
} }
pub async fn get_project_by_name(ctx: &AppContext, name: &str) -> Result<Model> {
let project = Entity::find()
.filter(projects::Column::Name.contains(name))
.one(&ctx.db)
.await?;
let project = project.ok_or_else(|| ModelError::EntityNotFound)?;
Ok(project)
}
pub async fn get_archived_projects(ctx: &AppContext) -> Result<Vec<Model>> { pub async fn get_archived_projects(ctx: &AppContext) -> Result<Vec<Model>> {
let archived_projects = Entity::find() let archived_projects = Entity::find()
.filter( .filter(
@@ -64,6 +82,60 @@ pub async fn get_all_projects_dto(ctx: &AppContext) -> Result<Vec<ProjectDto>> {
} }
}) })
.collect(); .collect();
Ok(projects_dto) Ok(projects_dto)
} }
pub async fn get_project_dto(ctx: &AppContext, id: i32) -> Result<ProjectDto> {
let project = get_project_by_id(ctx, id).await?;
let thumbnails = project
.find_related(project_thumbnails::Entity)
.all(&ctx.db)
.await?;
let thumbnails = thumbnails
.into_iter()
.map(|thumbnail| thumbnail.data_id.to_string())
.collect();
let project_dto = ProjectDto {
id: project.id,
name: project.name,
short_description: project.short_description,
description: project.description,
category: get_category_from_string(&project.category),
github_url: project.github_url,
download_url: project.download_url,
visit_url: project.visit_url,
technologies: get_technologies_from_string(&project.technology),
thumbnails,
};
Ok(project_dto)
}
pub async fn get_project_dto_by_name(ctx: &AppContext, name: &str) -> Result<ProjectDto> {
let project = get_project_by_name(ctx, name).await?;
let thumbnails = project
.find_related(project_thumbnails::Entity)
.all(&ctx.db)
.await?;
let thumbnails = thumbnails
.into_iter()
.map(|thumbnail| thumbnail.data_id.to_string())
.collect();
let project_dto = ProjectDto {
id: project.id,
name: project.name,
short_description: project.short_description,
description: project.description,
category: get_category_from_string(&project.category),
github_url: project.github_url,
download_url: project.download_url,
visit_url: project.visit_url,
technologies: get_technologies_from_string(&project.technology),
thumbnails,
};
Ok(project_dto)
}

View File

@@ -19,6 +19,18 @@ pub async fn projects(v: impl ViewRenderer, ctx: &AppContext) -> Result<impl Int
format::render().view(&v, "website/projects.html", data!({"projects": projects})) format::render().view(&v, "website/projects.html", data!({"projects": projects}))
} }
pub async fn project_detail(v: impl ViewRenderer, ctx: &AppContext, id: i32) -> Result<impl IntoResponse> {
let project = services::projects::get_project_dto(ctx, id).await?;
format::render().view(&v, "website/project_detail.html", data!({"project": project}))
}
pub async fn project_detail_from_name(v: impl ViewRenderer, ctx: &AppContext, name: String) -> Result<impl IntoResponse> {
let project = services::projects::get_project_dto_by_name(ctx, &name).await?;
format::render().view(&v, "website/project_detail.html", data!({"project": project}))
}
pub async fn about(v: impl ViewRenderer) -> Result<impl IntoResponse> { pub async fn about(v: impl ViewRenderer) -> Result<impl IntoResponse> {
let age = services::website::get_current_age(); let age = services::website::get_current_age();