diff --git a/frontend/src/Dashboard.elm b/frontend/src/Dashboard.elm index f905a74..671c349 100644 --- a/frontend/src/Dashboard.elm +++ b/frontend/src/Dashboard.elm @@ -44,7 +44,12 @@ type Status a init : Nav.Key -> ( Model, Cmd Msg ) init navKey = - ( Model navKey Loading True defaultCreateTask + reloadTasks (Model navKey Loading True defaultCreateTask) + + +reloadTasks : Model -> ( Model, Cmd Msg ) +reloadTasks model = + ( model , Http.get { url = "http://localhost:3000/tasks" , expect = Http.expectJson GotTasks (list taskDecoder) @@ -55,10 +60,12 @@ init navKey = type Msg = GotTasks (Result Http.Error (List Task)) | ToggleCreateTask - | CreateTask - | GotTaskCreated (Result Http.Error Task) | CreateTaskUpdateTitle String | CreateTaskUpdateDescription String + | CreateTask + | GotTaskCreated (Result Http.Error Task) + | DeleteTask Int + | GotTaskDeleted (Result Http.Error ()) update : Msg -> Model -> ( Model, Cmd Msg ) @@ -113,6 +120,22 @@ update msg model = in ( { model | createTask = { createTask | description = description } }, Cmd.none ) + DeleteTask id -> + ( model + , Http.request + { method = "DELETE" + , headers = [] + , url = "http://localhost:3000/tasks/" ++ String.fromInt id + , body = Http.emptyBody + , expect = Http.expectWhatever GotTaskDeleted + , timeout = Nothing + , tracker = Nothing + } + ) + + GotTaskDeleted _ -> + reloadTasks model + taskDecoder : Decoder Task taskDecoder = @@ -186,6 +209,7 @@ viewTaskListItem task = "" ) ) + , button [ onClick (DeleteTask task.id) ] [ text "Delete Task" ] ] diff --git a/src/main.rs b/src/main.rs index 7f6dfc1..0a7c01e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ mod routes; use axum::http::{StatusCode, Uri}; use axum::response::IntoResponse; -use axum::routing::get; +use axum::routing::{delete, get}; use dotenvy::dotenv; use sqlx::SqlitePool; @@ -28,6 +28,10 @@ async fn main() { .post(routes::tasks::create) .options(routes::tasks::options), ) + .route( + "/tasks/:task_id", + delete(routes::tasks::delete).options(routes::tasks::options), + ) .with_state(state); let listener = tokio::net::TcpListener::bind("127.0.0.1:3000") diff --git a/src/models/task.rs b/src/models/task.rs index 196bcab..0bd37e7 100644 --- a/src/models/task.rs +++ b/src/models/task.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use sqlx::{FromRow, SqlitePool}; +use sqlx::{sqlite::SqliteQueryResult, FromRow, SqlitePool}; #[derive(Serialize, Deserialize, FromRow)] pub struct Task { @@ -32,4 +32,10 @@ impl Task { .fetch_one(pool) .await } + + pub async fn delete(pool: &SqlitePool, id: i64) -> Result { + sqlx::query!("DELETE FROM task WHERE id = ?1", id) + .execute(pool) + .await + } } diff --git a/src/routes/tasks.rs b/src/routes/tasks.rs index 78124a8..cd5ec41 100644 --- a/src/routes/tasks.rs +++ b/src/routes/tasks.rs @@ -1,6 +1,4 @@ -use std::collections::HashMap; - -use axum::extract::{Json, State}; +use axum::extract::{Json, Path, State}; use axum::http::header; use axum::http::StatusCode; use axum::response::IntoResponse; @@ -59,9 +57,22 @@ pub async fn options() -> impl IntoResponse { StatusCode::OK, [ (header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"), - (header::ACCESS_CONTROL_ALLOW_METHODS, "POST, GET, OPTIONS"), + ( + header::ACCESS_CONTROL_ALLOW_METHODS, + "POST, GET, OPTIONS, DELETE", + ), (header::ACCESS_CONTROL_ALLOW_HEADERS, "Content-Type"), (header::ACCESS_CONTROL_MAX_AGE, "86400"), ], ) } + +pub async fn delete(state: State, Path(id): Path) -> impl IntoResponse { + Task::delete(&state.db_pool, id).await.unwrap(); + + ( + StatusCode::OK, + [(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")], + "", + ) +}