Add ability to update items using htmx.
This commit is contained in:
parent
381910d462
commit
e6ff8adb14
|
@ -125,6 +125,7 @@ checksum = "edca88bc138befd0323b20752846e6587272d3b03b0343c8ea28a6f819e6e71f"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
"http",
|
||||
|
@ -172,6 +173,17 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "axum-macros"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57d123550fa8d071b7255cb0cc04dc302baa6c8c4a79f55701552684d8399bce"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.90",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.74"
|
||||
|
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
askama = { version = "0.12.1", features = ["with-axum"] }
|
||||
askama_axum = "0.4.0"
|
||||
axum = "0.7"
|
||||
axum = { version = "0.7.2", features = ["macros"] }
|
||||
dotenvy = "0.15"
|
||||
http = "1.1.0"
|
||||
serde = "1.0"
|
||||
|
|
|
@ -8,7 +8,6 @@ There are 3 servers to run to enable hot reloading of the frontend
|
|||
and backend. The server can then be accessed at `localhost:8000` in
|
||||
the browser.
|
||||
|
||||
|
||||
```bash
|
||||
$ cargo watch -x run
|
||||
$ cd frontend/
|
||||
|
|
|
@ -4,7 +4,7 @@ mod routes;
|
|||
|
||||
use axum::http::{StatusCode, Uri};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::routing::{delete, get};
|
||||
use axum::routing::{get, put};
|
||||
use dotenvy::dotenv;
|
||||
use http::{HeaderName, Method};
|
||||
use sqlx::SqlitePool;
|
||||
|
@ -35,7 +35,11 @@ async fn main() {
|
|||
"/tasks",
|
||||
get(routes::tasks::list).post(routes::tasks::create),
|
||||
)
|
||||
.route("/tasks/:task_id", delete(routes::tasks::delete))
|
||||
.route(
|
||||
"/tasks/:task_id",
|
||||
put(routes::tasks::put).delete(routes::tasks::delete),
|
||||
)
|
||||
.route("/tasks/:task_id/edit", get(routes::tasks::edit))
|
||||
.layer(
|
||||
ServiceBuilder::new().layer(
|
||||
CorsLayer::new()
|
||||
|
|
|
@ -22,6 +22,18 @@ impl Task {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &SqlitePool, id: i64) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
sqlx::query!("DELETE FROM task WHERE id = ?1", id)
|
||||
.execute(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn by_id(pool: &SqlitePool, id: i64) -> Result<Task, sqlx::Error> {
|
||||
sqlx::query_as!(Task, "SELECT * FROM task WHERE id = ?1", id,)
|
||||
.fetch_one(pool)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn insert(&self, pool: &SqlitePool) -> Result<Task, sqlx::Error> {
|
||||
sqlx::query_as!(
|
||||
Task,
|
||||
|
@ -33,8 +45,13 @@ impl Task {
|
|||
.await
|
||||
}
|
||||
|
||||
pub async fn delete(pool: &SqlitePool, id: i64) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
sqlx::query!("DELETE FROM task WHERE id = ?1", id)
|
||||
pub async fn update(&self, pool: &SqlitePool) -> Result<SqliteQueryResult, sqlx::Error> {
|
||||
sqlx::query!(
|
||||
"UPDATE task SET title=?2, description=?3 WHERE id = ?1",
|
||||
self.id,
|
||||
self.title,
|
||||
self.description,
|
||||
)
|
||||
.execute(pool)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ use askama_axum::Template;
|
|||
use axum::extract::{Json, Path, State};
|
||||
use axum::http::StatusCode;
|
||||
use axum::response::IntoResponse;
|
||||
use axum::Form;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::global::FormErrorResponse;
|
||||
|
@ -60,3 +61,33 @@ pub async fn delete(state: State<AppState>, Path(id): Path<i64>) -> impl IntoRes
|
|||
|
||||
(StatusCode::OK, "")
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "partials/tasks/edit.html")]
|
||||
struct TaskEditTemplate {
|
||||
pub task: Task,
|
||||
}
|
||||
|
||||
pub async fn edit(state: State<AppState>, Path(id): Path<i64>) -> impl IntoResponse {
|
||||
TaskEditTemplate {
|
||||
task: Task::by_id(&state.db_pool, id).await.unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Template)]
|
||||
#[template(path = "partials/tasks/item.html")]
|
||||
struct TaskItemTemplate {
|
||||
pub task: Task,
|
||||
}
|
||||
|
||||
pub async fn put(
|
||||
state: State<AppState>,
|
||||
Path(id): Path<i64>,
|
||||
Form(req): Form<NewTask>,
|
||||
) -> impl IntoResponse {
|
||||
let mut task = Task::by_id(&state.db_pool, id).await.unwrap();
|
||||
task.title = req.title;
|
||||
task.description = req.description;
|
||||
task.update(&state.db_pool).await.unwrap();
|
||||
TaskItemTemplate { task }
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Captains Log</title>
|
||||
<script type="text/javascript" src="/build/main.js"></script>
|
||||
<script src="https://unpkg.com/htmx.org@2.0.3"></script>
|
||||
<link href="/assets/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
|
||||
</head>
|
||||
|
@ -22,7 +22,7 @@
|
|||
</header>
|
||||
<ul>
|
||||
{% for task in tasks %}
|
||||
<li>{{ task.title }}</li>
|
||||
{% include "partials/tasks/item.html" %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</body>
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<form hx-target="this", hx-swap="outerHtml">
|
||||
<div>
|
||||
<label>Title</label>
|
||||
<input type="text" name="title" value="{{ task.title }}"> </input>
|
||||
</div>
|
||||
<div>
|
||||
<label>Description</label>
|
||||
{% if let Some(desc) = task.description %}
|
||||
<input type="text" name="description" value="{{ desc }}"> </input>
|
||||
{% else %}
|
||||
<input type="text" name="description" value=""> </input>
|
||||
{% endif %}
|
||||
</div>
|
||||
<button hx-get="/tasks/{{ task.id }}">
|
||||
Cancel
|
||||
</button>
|
||||
<button hx-put="/tasks/{{ task.id }}">
|
||||
Submit
|
||||
</button>
|
||||
|
||||
</form>
|
|
@ -0,0 +1,9 @@
|
|||
<li hx-target="this" hx-swap="outerHtml">
|
||||
{{ task.title }}
|
||||
{% match task.description %}
|
||||
{% when Some with (val) %}
|
||||
-- <span> {{ val }}</span>
|
||||
{% when None %}
|
||||
{% endmatch %}
|
||||
<button hx-get="/tasks/{{task.id}}/edit">Edit</button>
|
||||
</li>
|
Loading…
Reference in New Issue