Add dedicated Companion Planting page
Backend: new GET /api/v1/companions endpoint returning all companion relationships with joined species names, slugs and images. Adds CompanionWithNames model and list_all DB query. Frontend: new /companions route with search bar, beneficial (green) and antagonistic (red) sections, species thumbnails, mechanism text, source links, and species detail links. Full DE/EN i18n support.
This commit is contained in:
@@ -6,6 +6,13 @@ use crate::db::{companions as db, models::*};
|
||||
use crate::error::{AppError, Result};
|
||||
use crate::state::AppState;
|
||||
|
||||
pub async fn list_all(
|
||||
State(state): State<AppState>,
|
||||
) -> Result<Json<Vec<CompanionWithNames>>> {
|
||||
let companions = db::list_all(&state.pool).await?;
|
||||
Ok(Json(companions))
|
||||
}
|
||||
|
||||
pub async fn list_for_species(
|
||||
State(state): State<AppState>,
|
||||
Path(r): Path<String>,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
mod companions;
|
||||
mod cultivars;
|
||||
mod docs;
|
||||
mod families;
|
||||
mod health;
|
||||
mod images;
|
||||
@@ -35,7 +36,7 @@ pub fn router(state: AppState) -> Router {
|
||||
.route("/api/v1/suppliers", get(suppliers::list).post(suppliers::create))
|
||||
.route("/api/v1/suppliers/{ref}", get(suppliers::get_by_slug).put(suppliers::update).delete(suppliers::remove))
|
||||
// Companions
|
||||
.route("/api/v1/companions", post(companions::create))
|
||||
.route("/api/v1/companions", get(companions::list_all).post(companions::create))
|
||||
.route("/api/v1/companions/{id}", delete(companions::remove))
|
||||
// Images
|
||||
.route("/api/v1/images/{entity_type}/{entity_id}", get(images::list_for_entity))
|
||||
@@ -45,6 +46,9 @@ pub fn router(state: AppState) -> Router {
|
||||
.route("/img/{*path}", get(images::serve_image))
|
||||
// Search
|
||||
.route("/api/v1/search", get(search::search))
|
||||
// API docs
|
||||
.route("/api/openapi.yaml", get(docs::openapi_yaml))
|
||||
.route("/api/docs", get(docs::swagger_ui))
|
||||
// OIDC auth
|
||||
.route("/auth/oidc/login", get(crate::auth::oidc::login))
|
||||
.route("/auth/oidc/callback", get(crate::auth::oidc::callback))
|
||||
|
||||
@@ -2,7 +2,29 @@ use sqlx::PgPool;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::error::{AppError, Result};
|
||||
use super::models::{CompanionRelationship, CreateCompanion};
|
||||
use super::models::{CompanionRelationship, CompanionWithNames, CreateCompanion};
|
||||
|
||||
pub async fn list_all(pool: &PgPool) -> Result<Vec<CompanionWithNames>> {
|
||||
sqlx::query_as::<_, CompanionWithNames>(
|
||||
"SELECT cr.id, cr.species_a_id, cr.species_b_id, cr.relationship,
|
||||
cr.mechanism, cr.source_url, cr.created_at,
|
||||
sa.name_scientific AS species_a_scientific,
|
||||
sa.name_de AS species_a_de,
|
||||
sa.slug AS species_a_slug,
|
||||
sa.primary_image_key AS species_a_image,
|
||||
sb.name_scientific AS species_b_scientific,
|
||||
sb.name_de AS species_b_de,
|
||||
sb.slug AS species_b_slug,
|
||||
sb.primary_image_key AS species_b_image
|
||||
FROM companion_relationships cr
|
||||
JOIN species sa ON cr.species_a_id = sa.id
|
||||
JOIN species sb ON cr.species_b_id = sb.id
|
||||
ORDER BY sa.name_scientific, sb.name_scientific"
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
pub async fn list_for_species(pool: &PgPool, species_id: Uuid) -> Result<Vec<CompanionRelationship>> {
|
||||
sqlx::query_as::<_, CompanionRelationship>(
|
||||
|
||||
@@ -438,6 +438,25 @@ pub struct CompanionRelationship {
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, FromRow, Serialize)]
|
||||
pub struct CompanionWithNames {
|
||||
pub id: Uuid,
|
||||
pub species_a_id: Uuid,
|
||||
pub species_b_id: Uuid,
|
||||
pub relationship: String,
|
||||
pub mechanism: Option<String>,
|
||||
pub source_url: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub species_a_scientific: String,
|
||||
pub species_a_de: Option<String>,
|
||||
pub species_a_slug: String,
|
||||
pub species_a_image: Option<String>,
|
||||
pub species_b_scientific: String,
|
||||
pub species_b_de: Option<String>,
|
||||
pub species_b_slug: String,
|
||||
pub species_b_image: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateCompanion {
|
||||
pub species_a_id: Uuid,
|
||||
|
||||
Reference in New Issue
Block a user