Initial commit: NutriApp - Rust + Axum + Dioxus - CI/CD con Gitea Runner
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
[package]
|
||||
name = "nutriapp-frontend"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
# --- Dioxus ---
|
||||
dioxus = { version = "0.6", features = ["web"] }
|
||||
|
||||
# --- Shared ---
|
||||
shared = { path = "../shared" }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
chrono = { workspace = true }
|
||||
|
||||
# --- HTTP calls al backend ---
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
|
||||
# --- WASM bindings ---
|
||||
wasm-bindgen = "0.2"
|
||||
web-sys = { version = "0.3", features = ["console"] }
|
||||
console_error_panic_hook = "0.1"
|
||||
|
||||
# --- Logging ---
|
||||
log = "0.4"
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>NutriApp</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: system-ui, -apple-system, sans-serif;
|
||||
background: #0f172a;
|
||||
color: #e2e8f0;
|
||||
min-height: 100vh;
|
||||
}
|
||||
#main { min-height: 100vh; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main"></div>
|
||||
<script type="module">
|
||||
import init from './assets/dioxus/nutriapp-frontend.js';
|
||||
init('./assets/dioxus/nutriapp-frontend_bg.wasm');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,55 @@
|
||||
// Cliente HTTP para la API del backend (Axum en :3100)
|
||||
// TODO: reemplazar con llamadas reales a /api/v1/*
|
||||
|
||||
use reqwest::Client;
|
||||
use shared::{RecetaDto, SlotComidaDto, ItemCompraDto, ListaCompraRequest};
|
||||
|
||||
const API_BASE: &str = "http://localhost:3100/api/v1";
|
||||
|
||||
pub async fn listar_recetas() -> Result<Vec<RecetaDto>, String> {
|
||||
let client = Client::new();
|
||||
let resp = client
|
||||
.get(format!("{API_BASE}/recetas"))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
resp.json::<Vec<RecetaDto>>()
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub async fn listar_slots(
|
||||
familia_id: &str,
|
||||
desde: &str,
|
||||
hasta: &str,
|
||||
) -> Result<Vec<SlotComidaDto>, String> {
|
||||
let client = Client::new();
|
||||
let resp = client
|
||||
.get(format!(
|
||||
"{API_BASE}/calendario?familia_id={familia_id}&desde={desde}&hasta={hasta}"
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
resp.json::<Vec<SlotComidaDto>>()
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub async fn generar_lista_compras(
|
||||
req: &ListaCompraRequest,
|
||||
) -> Result<Vec<ItemCompraDto>, String> {
|
||||
let client = Client::new();
|
||||
let resp = client
|
||||
.post(format!("{API_BASE}/lista-compras"))
|
||||
.json(req)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
resp.json::<Vec<ItemCompraDto>>()
|
||||
.await
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
// API client para llamadas al backend (Axum en :3100)
|
||||
pub mod client;
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod receta_card;
|
||||
pub mod slot_calendario;
|
||||
pub mod nav;
|
||||
@@ -0,0 +1 @@
|
||||
// Barra de navegación reutilizable
|
||||
@@ -0,0 +1,22 @@
|
||||
// Componente tarjeta de receta — nombre, porciones, tiempo, ingredientes
|
||||
use dioxus::prelude::*;
|
||||
use shared::RecetaDto;
|
||||
|
||||
#[component]
|
||||
pub fn RecetaCard(receta: RecetaDto, on_click: Option<EventHandler<()>>) -> Element {
|
||||
rsx! {
|
||||
div {
|
||||
class: "receta-card",
|
||||
onclick: move |_| {
|
||||
if let Some(ref handler) = on_click {
|
||||
handler.call(());
|
||||
}
|
||||
},
|
||||
h3 { "{receta.nombre}" }
|
||||
span { "🍽️ {receta.porciones} porciones" }
|
||||
if let Some(tiempo) = receta.tiempo_preparacion_min {
|
||||
span { "⏱️ {tiempo} min" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
// Slot de comida en el calendario (desayuno/comida/cena de un día)
|
||||
@@ -0,0 +1,34 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
mod pages;
|
||||
mod components;
|
||||
mod api;
|
||||
|
||||
fn main() {
|
||||
console_error_panic_hook::set_once();
|
||||
dioxus::launch(App);
|
||||
}
|
||||
|
||||
#[component]
|
||||
fn App() -> Element {
|
||||
rsx! {
|
||||
div { class: "app-container",
|
||||
header { class: "header",
|
||||
h1 { "🍽️ NutriApp" }
|
||||
nav {
|
||||
a { href: "/", "Calendario" }
|
||||
a { href: "/recetas", "Recetas" }
|
||||
a { href: "/lista", "Lista de Compras" }
|
||||
a { href: "/inventario", "Inventario" }
|
||||
}
|
||||
}
|
||||
main { class: "main-content",
|
||||
// Placeholder — routing se agrega después
|
||||
h2 { "Bienvenido a NutriApp" }
|
||||
p { "Planifica tus comidas semanales en familia." }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// Página de calendario semanal
|
||||
// Grid 7 columnas (días) × 3 filas (desayuno, comida, cena)
|
||||
// Drag & drop de recetas a slots
|
||||
@@ -0,0 +1,2 @@
|
||||
// Página de inventario — productos disponibles en casa
|
||||
// Se descuenta de la lista de compras
|
||||
@@ -0,0 +1 @@
|
||||
// Página de lista de compras generada automáticamente desde el menú semanal
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod calendario;
|
||||
pub mod recetas;
|
||||
pub mod lista_compras;
|
||||
pub mod inventario;
|
||||
@@ -0,0 +1,2 @@
|
||||
// Página de recetas
|
||||
// Lista, búsqueda, crear/editar receta con ingredientes dinámicos
|
||||
Reference in New Issue
Block a user