add project detail
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -14,7 +14,7 @@
|
||||
{{ chip::chip(text=technology) }}
|
||||
{% endfor %}
|
||||
</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>
|
||||
<div class="flex flex-wrap gap-2 sm:justify-center md:justify-start">
|
||||
{% if project.githubUrl %}
|
||||
|
58
assets/views/website/project_detail.html
Normal file
58
assets/views/website/project_detail.html
Normal 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 %}
|
@@ -38,12 +38,29 @@ pub async fn render_projects(
|
||||
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 {
|
||||
Routes::new()
|
||||
.add("/", get(render_index))
|
||||
.add("/upload", get(render_upload))
|
||||
.add("/login", get(render_login))
|
||||
.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))
|
||||
}
|
||||
|
@@ -1,6 +1,15 @@
|
||||
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>> {
|
||||
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)
|
||||
}
|
||||
|
||||
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>> {
|
||||
let archived_projects = Entity::find()
|
||||
.filter(
|
||||
@@ -64,6 +82,60 @@ pub async fn get_all_projects_dto(ctx: &AppContext) -> Result<Vec<ProjectDto>> {
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
||||
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)
|
||||
}
|
||||
|
@@ -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}))
|
||||
}
|
||||
|
||||
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> {
|
||||
let age = services::website::get_current_age();
|
||||
|
||||
|
Reference in New Issue
Block a user