changes
This commit is contained in:
@@ -9,3 +9,11 @@ window.onload = function() {
|
|||||||
function is_positive_integer(val) {
|
function is_positive_integer(val) {
|
||||||
return /^\d+$/.test(val);
|
return /^\d+$/.test(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function inventory_new_item_check_input() {
|
||||||
|
return document.getElementById('new-item-name').value.length != 0
|
||||||
|
&& is_positive_integer(document.getElementById('new-item-weight').value)
|
||||||
|
}
|
||||||
|
function check_weight() {
|
||||||
|
return document.getElementById('new-item-weight').validity.valid;
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,22 +63,44 @@
|
|||||||
/* ON inner.category_id = category.id */
|
/* ON inner.category_id = category.id */
|
||||||
/* WHERE category.id = '1293c6b6-eef5-4269-bf10-a1ac20549dac' */
|
/* WHERE category.id = '1293c6b6-eef5-4269-bf10-a1ac20549dac' */
|
||||||
|
|
||||||
SELECT
|
/* SELECT */
|
||||||
trip.id AS id,
|
/* trip.id AS id, */
|
||||||
trip.name AS name,
|
/* trip.name AS name, */
|
||||||
CAST (date_start AS TEXT) date_start,
|
/* CAST (date_start AS TEXT) date_start, */
|
||||||
CAST (date_end AS TEXT) date_end,
|
/* CAST (date_end AS TEXT) date_end, */
|
||||||
state,
|
/* state, */
|
||||||
location,
|
/* location, */
|
||||||
temp_min,
|
/* temp_min, */
|
||||||
temp_max,
|
/* temp_max, */
|
||||||
comment,
|
/* comment, */
|
||||||
SUM(i_item.weight) AS total_weight
|
/* SUM(i_item.weight) AS total_weight */
|
||||||
FROM trips AS trip
|
/* FROM trips AS trip */
|
||||||
INNER JOIN trips_items AS t_item
|
/* INNER JOIN trips_items AS t_item */
|
||||||
ON t_item.trip_id = trip.id
|
/* ON t_item.trip_id = trip.id */
|
||||||
INNER JOIN inventory_items AS i_item
|
/* INNER JOIN inventory_items AS i_item */
|
||||||
ON t_item.item_id = i_item.id
|
/* ON t_item.item_id = i_item.id */
|
||||||
WHERE
|
/* WHERE */
|
||||||
trip.id = '0535193c-7b47-4ba4-bca5-40e54c15c2d0'
|
/* trip.id = '0535193c-7b47-4ba4-bca5-40e54c15c2d0' */
|
||||||
AND t_item.pick = true
|
/* AND t_item.pick = true */
|
||||||
|
|
||||||
|
/* UPDATE trips_items */
|
||||||
|
/* SET pick = true */
|
||||||
|
/* WHERE trip_id = '0535193c-7b47-4ba4-bca5-40e54c15c2d0' */
|
||||||
|
/* AND item_id = '2418ab2d-4e11-4a68-8761-265d442742f6' */
|
||||||
|
/* RETURNING */
|
||||||
|
/* trips_items.item_id */
|
||||||
|
|
||||||
|
SELECT *
|
||||||
|
FROM inventory_items
|
||||||
|
WHERE id = (
|
||||||
|
UPDATE trips_items
|
||||||
|
SET pick = true
|
||||||
|
WHERE trip_id = '0535193c-7b47-4ba4-bca5-40e54c15c2d0'
|
||||||
|
AND item_id = '2418ab2d-4e11-4a68-8761-265d442742f6'
|
||||||
|
RETURNING
|
||||||
|
trips_items.item_id
|
||||||
|
/* SELECT trips_items.item_id */
|
||||||
|
/* FROM trips_items */
|
||||||
|
/* WHERE trip_id = '0535193c-7b47-4ba4-bca5-40e54c15c2d0' */
|
||||||
|
/* AND item_id = '2418ab2d-4e11-4a68-8761-265d442742f6' */
|
||||||
|
)
|
||||||
|
|||||||
@@ -5,13 +5,327 @@ pub struct Home;
|
|||||||
impl Home {
|
impl Home {
|
||||||
pub fn build() -> Markup {
|
pub fn build() -> Markup {
|
||||||
html!(
|
html!(
|
||||||
div id="home" class={"p-8" "max-w-xl"} {
|
div
|
||||||
p {
|
id="home"
|
||||||
a href="/inventory/" { "Inventory" }
|
hx-boost="true"
|
||||||
|
."p-8"
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-8"
|
||||||
|
."flex-nowrap"
|
||||||
|
{
|
||||||
|
h1
|
||||||
|
."text-2xl"
|
||||||
|
."m-auto"
|
||||||
|
."my-4"
|
||||||
|
{
|
||||||
|
"Welcome!"
|
||||||
}
|
}
|
||||||
p {
|
section
|
||||||
a href="/trips/" { "Trips" }
|
."border-2"
|
||||||
|
."border-gray-200"
|
||||||
|
."rounded-md"
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
{
|
||||||
|
a
|
||||||
|
href="/inventory/"
|
||||||
|
hx-boost="true"
|
||||||
|
."p-8"
|
||||||
|
."w-1/5"
|
||||||
|
."flex"
|
||||||
|
."hover:bg-gray-200"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."m-auto"
|
||||||
|
."text-xl"
|
||||||
|
{ "Inventory" }
|
||||||
|
}
|
||||||
|
div
|
||||||
|
."p-8"
|
||||||
|
."w-4/5"
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-3"
|
||||||
|
{
|
||||||
|
p {
|
||||||
|
"The inventory contains all the items that you own."
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"It is effectively a list of items, sectioned into
|
||||||
|
arbitrary categories"
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Each item has some important data attached to it,
|
||||||
|
like its weight"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
section
|
||||||
|
."border-2"
|
||||||
|
."border-gray-200"
|
||||||
|
."rounded-md"
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
{
|
||||||
|
a
|
||||||
|
href="/trips/"
|
||||||
|
hx-boost="true"
|
||||||
|
."p-8"
|
||||||
|
."w-1/5"
|
||||||
|
."flex"
|
||||||
|
."hover:bg-gray-200"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."m-auto"
|
||||||
|
."text-xl"
|
||||||
|
{ "Trips" }
|
||||||
|
}
|
||||||
|
div
|
||||||
|
."p-8"
|
||||||
|
."w-4/5"
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-6"
|
||||||
|
{
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-3"
|
||||||
|
{
|
||||||
|
p {
|
||||||
|
"Trips is where it gets interesting, as you can put
|
||||||
|
your inventory to good use"
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
r#"With trips, you record any trips you plan to do. A
|
||||||
|
"trip" can be anything you want it to be. Anything
|
||||||
|
from a multi-week hike, a high altitude mountaineering
|
||||||
|
tour or just a visit to the library. Whenever it makes
|
||||||
|
sense to do some planning, creating a trip makes sense."#
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Each trip has some metadata attached to it, like start-
|
||||||
|
and end dates or the expected temperature."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-3"
|
||||||
|
{
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."gap-2"
|
||||||
|
."items-center"
|
||||||
|
."justify-start"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."mdi-pound"
|
||||||
|
."text-lg"
|
||||||
|
."text-gray-300"
|
||||||
|
{}
|
||||||
|
h3 ."text-lg" {
|
||||||
|
"States"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"One of the most important parts of each trip is
|
||||||
|
its " em{"state"} ", which determines certain
|
||||||
|
actions on the trip and can have the following values:"
|
||||||
|
}
|
||||||
|
table
|
||||||
|
."table"
|
||||||
|
."table-auto"
|
||||||
|
."border-collapse"
|
||||||
|
{
|
||||||
|
tr
|
||||||
|
."border-b-2"
|
||||||
|
."last:border-b-0"
|
||||||
|
{
|
||||||
|
td ."py-2" ."pr-4" ."border-r-2" {
|
||||||
|
"Init"
|
||||||
|
}
|
||||||
|
td ."py-2" ."w-full" ."pl-4" {
|
||||||
|
"The new trip was just created"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr
|
||||||
|
."border-b-2"
|
||||||
|
."last:border-b-0"
|
||||||
|
{
|
||||||
|
td ."py-2" ."pr-4" ."border-r-2" {
|
||||||
|
"Planning"
|
||||||
|
}
|
||||||
|
td ."py-2" ."w-full" ."pl-4" {
|
||||||
|
"Now, you actually start planning the trip.
|
||||||
|
Setting the location, going through your
|
||||||
|
items to decide what to take with you."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr
|
||||||
|
."border-b-2"
|
||||||
|
."last:border-b-0"
|
||||||
|
{
|
||||||
|
td ."py-2" ."pr-4" ."border-r-2" {
|
||||||
|
"Planned"
|
||||||
|
}
|
||||||
|
td ."py-2" ."w-full" ."pl-4" {
|
||||||
|
"You are done with the planning. It's time
|
||||||
|
to pack up your stuff and get going."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr
|
||||||
|
."border-b-2"
|
||||||
|
."last:border-b-0"
|
||||||
|
{
|
||||||
|
td ."py-2" ."pr-4" ."border-r-2" {
|
||||||
|
"Active"
|
||||||
|
}
|
||||||
|
td ."py-2" ."w-full" ."pl-4" {
|
||||||
|
"The trip is finally underway!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr
|
||||||
|
."border-b-2"
|
||||||
|
."last:border-b-0"
|
||||||
|
{
|
||||||
|
td ."py-2" ."pr-4" ."border-r-2" {
|
||||||
|
"Review"
|
||||||
|
}
|
||||||
|
td ."py-2" ."w-full" ."pl-4" {
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-2"
|
||||||
|
{
|
||||||
|
p {
|
||||||
|
"You returned from your trip. It may make
|
||||||
|
sense to take a look back and see what
|
||||||
|
went well and what went not so well."
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Anything you missed? Any items that you
|
||||||
|
took with you that turned out to be useless?
|
||||||
|
Record it and you will remember on your next
|
||||||
|
trip"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tr
|
||||||
|
."border-b-2"
|
||||||
|
."last:border-b-0"
|
||||||
|
{
|
||||||
|
td ."py-2" ."pr-4" ."border-r-2" {
|
||||||
|
"Done"
|
||||||
|
}
|
||||||
|
td ."py-2" ."w-full" ."pl-4" {
|
||||||
|
"Your review is done and the trip can be laid to rest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-3"
|
||||||
|
{
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."gap-2"
|
||||||
|
."items-center"
|
||||||
|
."justify-start"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."mdi-pound"
|
||||||
|
."text-lg"
|
||||||
|
."text-gray-300"
|
||||||
|
{}
|
||||||
|
h3 ."text-lg" {
|
||||||
|
"Items"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Of course, you can use items defined in your
|
||||||
|
inventory in your trips"
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Generally, all items are available to you in
|
||||||
|
the same way as the inventory. For each item,
|
||||||
|
there are two specific states for the trip: An
|
||||||
|
item can be " em{"picked"} ", which means that
|
||||||
|
you plan to take it on the trip, and it can
|
||||||
|
be " em{"packed"} ", which means that you actually
|
||||||
|
packed it into your bag (and therefore, you cannot
|
||||||
|
forget it any more)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
."gap-3"
|
||||||
|
{
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."gap-2"
|
||||||
|
."items-center"
|
||||||
|
."justify-start"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."mdi-pound"
|
||||||
|
."text-lg"
|
||||||
|
."text-gray-300"
|
||||||
|
{}
|
||||||
|
h3 ."text-lg" {
|
||||||
|
"Types & Presets"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Often, you will take a certain set of items to
|
||||||
|
certain trips. Whenever you plan to sleep outdoors,
|
||||||
|
it makes sense to take your sleeping bag and mat
|
||||||
|
with you"
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"To reflect this, you can attach " em {"types"} " "
|
||||||
|
"to your trips. Types define arbitrary characteristics
|
||||||
|
about a trip and reference a certain set of items."
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Here are some examples of types that might make sense:"
|
||||||
|
}
|
||||||
|
ul
|
||||||
|
."list-disc"
|
||||||
|
."list-inside"
|
||||||
|
{
|
||||||
|
li {
|
||||||
|
r#""Biking": Make sure to pack your helmet and
|
||||||
|
some repair tools"#
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
r#""Climbing": You certainly don't want to forget
|
||||||
|
your climbing shoes"#
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
r#""Rainy": Pack a rain jacket and some waterproof
|
||||||
|
shoes"#
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
"Types are super flexible, it's up to you how to use
|
||||||
|
them"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ impl InventoryCategoryList {
|
|||||||
id="select-category"
|
id="select-category"
|
||||||
href={
|
href={
|
||||||
"/inventory/category/"
|
"/inventory/category/"
|
||||||
(category.id)
|
(category.id) "/"
|
||||||
}
|
}
|
||||||
hx-post={
|
hx-post={
|
||||||
"/inventory/categories/"
|
"/inventory/categories/"
|
||||||
@@ -152,6 +152,7 @@ impl InventoryItemList {
|
|||||||
name="edit-item"
|
name="edit-item"
|
||||||
id="edit-item"
|
id="edit-item"
|
||||||
action={"/inventory/item/" (edit_item_id) "/edit"}
|
action={"/inventory/item/" (edit_item_id) "/edit"}
|
||||||
|
hx-boost="true"
|
||||||
target="_self"
|
target="_self"
|
||||||
method="post"
|
method="post"
|
||||||
{}
|
{}
|
||||||
@@ -230,12 +231,13 @@ impl InventoryItemList {
|
|||||||
."h-full"
|
."h-full"
|
||||||
{
|
{
|
||||||
a
|
a
|
||||||
|
href=(format!("/inventory/item/{id}/cancel", id = item.id))
|
||||||
|
hx-boost="true"
|
||||||
."aspect-square"
|
."aspect-square"
|
||||||
."flex"
|
."flex"
|
||||||
."w-full"
|
."w-full"
|
||||||
."h-full"
|
."h-full"
|
||||||
."p-0"
|
."p-0"
|
||||||
href=(format!("/inventory/item/{id}/cancel", id = item.id))
|
|
||||||
{
|
{
|
||||||
span
|
span
|
||||||
."m-auto"
|
."m-auto"
|
||||||
@@ -253,10 +255,11 @@ impl InventoryItemList {
|
|||||||
."p-2" ."w-full" ."inline-block"
|
."p-2" ."w-full" ."inline-block"
|
||||||
href=(
|
href=(
|
||||||
format!("/inventory/item/{id}/", id=item.id)
|
format!("/inventory/item/{id}/", id=item.id)
|
||||||
) {
|
)
|
||||||
|
hx-boost="true"
|
||||||
(item.name.clone())
|
{
|
||||||
}
|
(item.name.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
td ."border" ."p-2" style="position:relative;" {
|
td ."border" ."p-2" style="position:relative;" {
|
||||||
p { (item.weight.to_string()) }
|
p { (item.weight.to_string()) }
|
||||||
@@ -276,10 +279,11 @@ impl InventoryItemList {
|
|||||||
."h-full"
|
."h-full"
|
||||||
{
|
{
|
||||||
a
|
a
|
||||||
|
href=(format!("?edit_item={id}", id = item.id))
|
||||||
|
hx-boost="true"
|
||||||
."aspect-square"
|
."aspect-square"
|
||||||
."flex"
|
."flex"
|
||||||
."w-full"
|
."w-full"
|
||||||
href=(format!("?edit_item={id}", id = item.id))
|
|
||||||
{
|
{
|
||||||
span ."m-auto" ."mdi" ."mdi-pencil" ."text-xl" {}
|
span ."m-auto" ."mdi" ."mdi-pencil" ."text-xl" {}
|
||||||
}
|
}
|
||||||
@@ -293,10 +297,11 @@ impl InventoryItemList {
|
|||||||
."h-full"
|
."h-full"
|
||||||
{
|
{
|
||||||
a
|
a
|
||||||
|
href=(format!("/inventory/item/{id}/delete", id = item.id))
|
||||||
|
hx-boost="true"
|
||||||
."aspect-square"
|
."aspect-square"
|
||||||
."flex"
|
."flex"
|
||||||
."w-full"
|
."w-full"
|
||||||
href=(format!("/inventory/item/{id}/delete", id = item.id))
|
|
||||||
{
|
{
|
||||||
span ."m-auto" ."mdi" ."mdi-delete" ."text-xl" {}
|
span ."m-auto" ."mdi" ."mdi-delete" ."text-xl" {}
|
||||||
}
|
}
|
||||||
@@ -317,17 +322,6 @@ pub struct InventoryNewItemFormName;
|
|||||||
impl InventoryNewItemFormName {
|
impl InventoryNewItemFormName {
|
||||||
pub fn build(value: Option<&str>, error: bool) -> Markup {
|
pub fn build(value: Option<&str>, error: bool) -> Markup {
|
||||||
html!(
|
html!(
|
||||||
script {
|
|
||||||
(PreEscaped("
|
|
||||||
function inventory_new_item_check_input() {
|
|
||||||
return document.getElementById('new-item-name').value.length != 0
|
|
||||||
&& is_positive_integer(document.getElementById('new-item-weight').value)
|
|
||||||
}
|
|
||||||
function check_weight() {
|
|
||||||
return document.getElementById('new-item-weight').validity.valid;
|
|
||||||
}
|
|
||||||
"))
|
|
||||||
}
|
|
||||||
div
|
div
|
||||||
."grid"
|
."grid"
|
||||||
."grid-cols-[2fr,3fr]"
|
."grid-cols-[2fr,3fr]"
|
||||||
@@ -376,17 +370,6 @@ pub struct InventoryNewItemFormWeight;
|
|||||||
impl InventoryNewItemFormWeight {
|
impl InventoryNewItemFormWeight {
|
||||||
pub fn build() -> Markup {
|
pub fn build() -> Markup {
|
||||||
html!(
|
html!(
|
||||||
script {
|
|
||||||
(PreEscaped("
|
|
||||||
function inventory_new_item_check_input() {
|
|
||||||
return document.getElementById('new-item-name').value.length != 0
|
|
||||||
&& is_positive_integer(document.getElementById('new-item-weight').value)
|
|
||||||
}
|
|
||||||
function check_weight() {
|
|
||||||
return document.getElementById('new-item-weight').validity.valid;
|
|
||||||
}
|
|
||||||
"))
|
|
||||||
}
|
|
||||||
div
|
div
|
||||||
."grid"
|
."grid"
|
||||||
."grid-cols-[2fr,3fr]"
|
."grid-cols-[2fr,3fr]"
|
||||||
@@ -468,17 +451,6 @@ pub struct InventoryNewItemForm;
|
|||||||
impl InventoryNewItemForm {
|
impl InventoryNewItemForm {
|
||||||
pub fn build(active_category: Option<&Category>, categories: &Vec<Category>) -> Markup {
|
pub fn build(active_category: Option<&Category>, categories: &Vec<Category>) -> Markup {
|
||||||
html!(
|
html!(
|
||||||
script {
|
|
||||||
(PreEscaped("
|
|
||||||
function inventory_new_item_check_input() {
|
|
||||||
return document.getElementById('new-item-name').value.length != 0
|
|
||||||
&& is_positive_integer(document.getElementById('new-item-weight').value)
|
|
||||||
}
|
|
||||||
function check_weight() {
|
|
||||||
return document.getElementById('new-item-weight').validity.valid;
|
|
||||||
}
|
|
||||||
"))
|
|
||||||
}
|
|
||||||
form
|
form
|
||||||
x-data="{
|
x-data="{
|
||||||
save_active: inventory_new_item_check_input(),
|
save_active: inventory_new_item_check_input(),
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ pub use trip::*;
|
|||||||
|
|
||||||
pub struct Root;
|
pub struct Root;
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
pub enum TopLevelPage {
|
pub enum TopLevelPage {
|
||||||
Inventory,
|
Inventory,
|
||||||
Trips,
|
Trips,
|
||||||
@@ -36,25 +37,30 @@ impl Root {
|
|||||||
{
|
{
|
||||||
header
|
header
|
||||||
#header
|
#header
|
||||||
."h-full"
|
."h-16"
|
||||||
."bg-gray-200"
|
."bg-gray-200"
|
||||||
."p-5"
|
|
||||||
."flex"
|
."flex"
|
||||||
."flex-row"
|
."flex-row"
|
||||||
."flex-nowrap"
|
."flex-nowrap"
|
||||||
."justify-between"
|
."justify-between"
|
||||||
."items-center"
|
."items-stretch"
|
||||||
{
|
{
|
||||||
span
|
a
|
||||||
."text-xl"
|
#home
|
||||||
."font-semibold"
|
hx-boost="true"
|
||||||
|
href="/"
|
||||||
."flex"
|
."flex"
|
||||||
."flex-row"
|
."flex-row"
|
||||||
."items-center"
|
."items-center"
|
||||||
."gap-3"
|
."gap-3"
|
||||||
|
."px-5"
|
||||||
|
."hover:bg-gray-300"
|
||||||
{
|
{
|
||||||
img ."h-12" src="/assets/luggage.svg" {}
|
img ."h-12" src="/assets/luggage.svg" {}
|
||||||
a #home href="/" { "Packager" }
|
span
|
||||||
|
."text-xl"
|
||||||
|
."font-semibold"
|
||||||
|
{ "Packager" }
|
||||||
}
|
}
|
||||||
nav
|
nav
|
||||||
."grow"
|
."grow"
|
||||||
@@ -62,22 +68,44 @@ impl Root {
|
|||||||
."flex-row"
|
."flex-row"
|
||||||
."justify-center"
|
."justify-center"
|
||||||
."gap-x-10"
|
."gap-x-10"
|
||||||
."content-stretch"
|
."items-stretch"
|
||||||
{
|
{
|
||||||
a href="/inventory/"
|
a
|
||||||
|
href="/inventory/"
|
||||||
|
hx-boost="true"
|
||||||
#header-link-inventory
|
#header-link-inventory
|
||||||
|
."px-5"
|
||||||
|
."flex"
|
||||||
."h-full"
|
."h-full"
|
||||||
."text-lg"
|
."text-lg"
|
||||||
."font-bold"[matches!(active_page, TopLevelPage::Inventory)]
|
."hover:bg-gray-300"
|
||||||
."underline"[matches!(active_page, TopLevelPage::Inventory)]
|
|
||||||
{ "Inventory" }
|
// invisible top border to fix alignment
|
||||||
a href="/trips/"
|
."border-t-gray-200"[active_page == &TopLevelPage::Inventory]
|
||||||
|
."hover:border-t-gray-300"[active_page == &TopLevelPage::Inventory]
|
||||||
|
|
||||||
|
."border-b-gray-500"[active_page == &TopLevelPage::Inventory]
|
||||||
|
."border-y-4"[active_page == &TopLevelPage::Inventory]
|
||||||
|
."font-bold"[active_page == &TopLevelPage::Inventory]
|
||||||
|
{ span ."m-auto" ."font-semibold" { "Inventory" }}
|
||||||
|
a
|
||||||
|
href="/trips/"
|
||||||
|
hx-boost="true"
|
||||||
#header-link-trips
|
#header-link-trips
|
||||||
|
."px-5"
|
||||||
|
."flex"
|
||||||
."h-full"
|
."h-full"
|
||||||
."text-lg"
|
."text-lg"
|
||||||
."font-bold"[matches!(active_page, TopLevelPage::Trips)]
|
."hover:bg-gray-300"
|
||||||
."underline"[matches!(active_page, TopLevelPage::Trips)]
|
|
||||||
{ "Trips" }
|
// invisible top border to fix alignment
|
||||||
|
."border-t-gray-200"[active_page == &TopLevelPage::Trips]
|
||||||
|
."hover:border-t-gray-300"[active_page == &TopLevelPage::Trips]
|
||||||
|
|
||||||
|
."border-gray-500"[active_page == &TopLevelPage::Trips]
|
||||||
|
."border-y-4"[active_page == &TopLevelPage::Trips]
|
||||||
|
."font-bold"[active_page == &TopLevelPage::Trips]
|
||||||
|
{ span ."m-auto" ."font-semibold" { "Trips" }}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(body)
|
(body)
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ use serde_variant::to_variant_name;
|
|||||||
use crate::ClientState;
|
use crate::ClientState;
|
||||||
pub struct TripManager;
|
pub struct TripManager;
|
||||||
|
|
||||||
|
pub mod packagelist;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
|
|
||||||
impl TripManager {
|
impl TripManager {
|
||||||
@@ -105,8 +107,14 @@ impl TripTableRow {
|
|||||||
pub fn build(trip_id: Uuid, value: impl maud::Render) -> Markup {
|
pub fn build(trip_id: Uuid, value: impl maud::Render) -> Markup {
|
||||||
html!(
|
html!(
|
||||||
td ."border" ."p-0" ."m-0" {
|
td ."border" ."p-0" ."m-0" {
|
||||||
a ."inline-block" ."p-2" ."m-0" ."w-full"
|
a
|
||||||
href=(format!("/trips/{id}/", id=trip_id))
|
href={"/trips/" (trip_id) "/"}
|
||||||
|
hx-boost="true"
|
||||||
|
."inline-block"
|
||||||
|
."p-2"
|
||||||
|
."m-0"
|
||||||
|
."w-full"
|
||||||
|
|
||||||
{ (value) }
|
{ (value) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -123,6 +131,7 @@ impl NewTrip {
|
|||||||
action="/trips/"
|
action="/trips/"
|
||||||
target="_self"
|
target="_self"
|
||||||
method="post"
|
method="post"
|
||||||
|
hx-boost="true"
|
||||||
."p-5" ."border-2" ."border-gray-200"
|
."p-5" ."border-2" ."border-gray-200"
|
||||||
{
|
{
|
||||||
div ."mb-5" ."flex" ."flex-row" ."trips-center" {
|
div ."mb-5" ."flex" ."flex-row" ."trips-center" {
|
||||||
@@ -225,93 +234,116 @@ impl Trip {
|
|||||||
div
|
div
|
||||||
."flex"
|
."flex"
|
||||||
."flex-row"
|
."flex-row"
|
||||||
."items-stretch"
|
."justify-between"
|
||||||
."gap-x-5"
|
|
||||||
{
|
{
|
||||||
a
|
div
|
||||||
href="/trips/"
|
|
||||||
."text-sm"
|
|
||||||
."text-gray-500"
|
|
||||||
."flex"
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."items-stretch"
|
||||||
|
."gap-x-5"
|
||||||
{
|
{
|
||||||
div
|
a
|
||||||
."m-auto"
|
href="/trips/"
|
||||||
|
hx-boost="true"
|
||||||
|
."text-sm"
|
||||||
|
."text-gray-500"
|
||||||
|
."flex"
|
||||||
{
|
{
|
||||||
span
|
div
|
||||||
."mdi"
|
."m-auto"
|
||||||
."mdi-arrow-left"
|
{
|
||||||
{}
|
span
|
||||||
"back"
|
."mdi"
|
||||||
|
."mdi-arrow-left"
|
||||||
|
{}
|
||||||
|
"back"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div ."flex" ."flex-row" ."items-center" ."gap-x-3" {
|
||||||
|
@if trip_edit_attribute.as_ref().map_or(false, |a| *a == TripAttribute::Name) {
|
||||||
|
form
|
||||||
|
id="edit-trip"
|
||||||
|
action=(format!("edit/{}/submit", to_variant_name(&TripAttribute::Name).unwrap()))
|
||||||
|
hx-boost="true"
|
||||||
|
target="_self"
|
||||||
|
method="post"
|
||||||
|
{
|
||||||
|
div
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."items-center"
|
||||||
|
."gap-x-3"
|
||||||
|
."items-stretch"
|
||||||
|
{
|
||||||
|
input
|
||||||
|
."bg-blue-200"
|
||||||
|
."w-full"
|
||||||
|
."text-2xl"
|
||||||
|
."font-semibold"
|
||||||
|
type=(<InputType as Into<&'static str>>::into(InputType::Text))
|
||||||
|
name="new-value"
|
||||||
|
form="edit-trip"
|
||||||
|
value=(trip.name)
|
||||||
|
{}
|
||||||
|
a
|
||||||
|
href="."
|
||||||
|
hx-boost="true"
|
||||||
|
."bg-red-200"
|
||||||
|
."hover:bg-red-300"
|
||||||
|
."w-8"
|
||||||
|
."flex"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."mdi-cancel"
|
||||||
|
."text-xl"
|
||||||
|
."m-auto"
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
button
|
||||||
|
type="submit"
|
||||||
|
form="edit-trip"
|
||||||
|
."bg-green-200"
|
||||||
|
."hover:bg-green-300"
|
||||||
|
."w-8"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."mdi-content-save"
|
||||||
|
."text-xl"
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
h1 ."text-2xl" { (trip.name) }
|
||||||
|
span {
|
||||||
|
a
|
||||||
|
href={"?edit=" (to_variant_name(&TripAttribute::Name).unwrap())}
|
||||||
|
hx-boost="true"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."mdi-pencil"
|
||||||
|
."text-xl"
|
||||||
|
."opacity-50"
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div ."flex" ."flex-row" ."items-center" ."gap-x-3" {
|
a
|
||||||
@if trip_edit_attribute.as_ref().map_or(false, |a| *a == TripAttribute::Name) {
|
href={"/trips/" (trip.id) "/packagelist/"}
|
||||||
form
|
hx-boost="true"
|
||||||
id="edit-trip"
|
."p-2"
|
||||||
action=(format!("edit/{}/submit", to_variant_name(&TripAttribute::Name).unwrap()))
|
."border-2"
|
||||||
target="_self"
|
."border-gray-500"
|
||||||
method="post"
|
."rounded-md"
|
||||||
{
|
."bg-blue-200"
|
||||||
div
|
."hover:bg-blue-200"
|
||||||
."flex"
|
{
|
||||||
."flex-row"
|
"Show Package List"
|
||||||
."items-center"
|
|
||||||
."gap-x-3"
|
|
||||||
."items-stretch"
|
|
||||||
{
|
|
||||||
input
|
|
||||||
."bg-blue-200"
|
|
||||||
."w-full"
|
|
||||||
."text-2xl"
|
|
||||||
."font-semibold"
|
|
||||||
type=(<InputType as Into<&'static str>>::into(InputType::Text))
|
|
||||||
name="new-value"
|
|
||||||
form="edit-trip"
|
|
||||||
value=(trip.name)
|
|
||||||
{}
|
|
||||||
a
|
|
||||||
href="."
|
|
||||||
."bg-red-200"
|
|
||||||
."hover:bg-red-300"
|
|
||||||
."w-8"
|
|
||||||
."flex"
|
|
||||||
{
|
|
||||||
span
|
|
||||||
."mdi"
|
|
||||||
."mdi-cancel"
|
|
||||||
."text-xl"
|
|
||||||
."m-auto"
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
button
|
|
||||||
type="submit"
|
|
||||||
form="edit-trip"
|
|
||||||
."bg-green-200"
|
|
||||||
."hover:bg-green-300"
|
|
||||||
."w-8"
|
|
||||||
{
|
|
||||||
span
|
|
||||||
."mdi"
|
|
||||||
."mdi-content-save"
|
|
||||||
."text-xl"
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} @else {
|
|
||||||
h1 ."text-2xl" { (trip.name) }
|
|
||||||
span {
|
|
||||||
a href=(format!("?edit={}", to_variant_name(&TripAttribute::Name).unwrap()))
|
|
||||||
{
|
|
||||||
span
|
|
||||||
."mdi"
|
|
||||||
."mdi-pencil"
|
|
||||||
."text-xl"
|
|
||||||
."opacity-50"
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(TripInfo::build(trip_edit_attribute, trip))
|
(TripInfo::build(trip_edit_attribute, trip))
|
||||||
@@ -339,6 +371,7 @@ impl TripInfoRow {
|
|||||||
name="edit-trip"
|
name="edit-trip"
|
||||||
id="edit-trip"
|
id="edit-trip"
|
||||||
action=(format!("edit/{key}/submit", key=(to_variant_name(&attribute_key).unwrap()) ))
|
action=(format!("edit/{key}/submit", key=(to_variant_name(&attribute_key).unwrap()) ))
|
||||||
|
hx-boost="true"
|
||||||
target="_self"
|
target="_self"
|
||||||
method="post"
|
method="post"
|
||||||
{}
|
{}
|
||||||
@@ -377,11 +410,12 @@ impl TripInfoRow {
|
|||||||
."h-full"
|
."h-full"
|
||||||
{
|
{
|
||||||
a
|
a
|
||||||
|
href="." // strips query parameters
|
||||||
|
hx-boost="true"
|
||||||
."flex"
|
."flex"
|
||||||
."w-full"
|
."w-full"
|
||||||
."h-full"
|
."h-full"
|
||||||
."p-0"
|
."p-0"
|
||||||
href="." // strips query parameters
|
|
||||||
{
|
{
|
||||||
span
|
span
|
||||||
."m-auto"
|
."m-auto"
|
||||||
@@ -426,10 +460,11 @@ impl TripInfoRow {
|
|||||||
."h-full"
|
."h-full"
|
||||||
{
|
{
|
||||||
a
|
a
|
||||||
.flex
|
href={ "?edit=" (to_variant_name(&attribute_key).unwrap()) }
|
||||||
|
hx-boost="true"
|
||||||
|
."flex"
|
||||||
."w-full"
|
."w-full"
|
||||||
."h-full"
|
."h-full"
|
||||||
href={ "?edit=" (to_variant_name(&attribute_key).unwrap()) }
|
|
||||||
{
|
{
|
||||||
span
|
span
|
||||||
."m-auto"
|
."m-auto"
|
||||||
@@ -649,7 +684,10 @@ impl TripInfo {
|
|||||||
."justify-start"
|
."justify-start"
|
||||||
{
|
{
|
||||||
@for triptype in active_triptypes {
|
@for triptype in active_triptypes {
|
||||||
a href=(format!("type/{}/remove", triptype.id)) {
|
a
|
||||||
|
href={"type/" (triptype.id) "/remove"}
|
||||||
|
hx-boost="true"
|
||||||
|
{
|
||||||
li
|
li
|
||||||
."border"
|
."border"
|
||||||
."rounded-2xl"
|
."rounded-2xl"
|
||||||
@@ -679,7 +717,10 @@ impl TripInfo {
|
|||||||
."justify-start"
|
."justify-start"
|
||||||
{
|
{
|
||||||
@for triptype in inactive_triptypes {
|
@for triptype in inactive_triptypes {
|
||||||
a href=(format!("type/{}/add", triptype.id)) {
|
a
|
||||||
|
href={"type/" (triptype.id) "/add"}
|
||||||
|
hx-boost="true"
|
||||||
|
{
|
||||||
li
|
li
|
||||||
."border"
|
."border"
|
||||||
."rounded-2xl"
|
."rounded-2xl"
|
||||||
@@ -703,7 +744,9 @@ impl TripInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a href="/trips/types/"
|
a
|
||||||
|
href="/trips/types/"
|
||||||
|
hx-boost="true"
|
||||||
."text-sm"
|
."text-sm"
|
||||||
."text-gray-500"
|
."text-gray-500"
|
||||||
."mr-2"
|
."mr-2"
|
||||||
@@ -742,6 +785,7 @@ impl TripComment {
|
|||||||
form
|
form
|
||||||
id="edit-comment"
|
id="edit-comment"
|
||||||
action="comment/submit"
|
action="comment/submit"
|
||||||
|
hx-boost="true"
|
||||||
target="_self"
|
target="_self"
|
||||||
method="post"
|
method="post"
|
||||||
{}
|
{}
|
||||||
@@ -1118,6 +1162,7 @@ impl TripItemListRow {
|
|||||||
href=(
|
href=(
|
||||||
format!("/inventory/item/{id}/", id=item.item.id)
|
format!("/inventory/item/{id}/", id=item.item.id)
|
||||||
)
|
)
|
||||||
|
hx-boost="true"
|
||||||
{
|
{
|
||||||
(item.item.name.clone())
|
(item.item.name.clone())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ impl TypeList {
|
|||||||
."hidden"
|
."hidden"
|
||||||
id="edit-trip-type"
|
id="edit-trip-type"
|
||||||
action={ (trip_type.id) "/edit/name/submit" }
|
action={ (trip_type.id) "/edit/name/submit" }
|
||||||
|
hx-boost="true"
|
||||||
target="_self"
|
target="_self"
|
||||||
method="post"
|
method="post"
|
||||||
{}
|
{}
|
||||||
@@ -56,6 +57,7 @@ impl TypeList {
|
|||||||
{
|
{
|
||||||
a
|
a
|
||||||
href="."
|
href="."
|
||||||
|
hx-boost="true"
|
||||||
."bg-red-200"
|
."bg-red-200"
|
||||||
."hover:bg-red-300"
|
."hover:bg-red-300"
|
||||||
."w-8"
|
."w-8"
|
||||||
@@ -96,10 +98,11 @@ impl TypeList {
|
|||||||
."w-8"
|
."w-8"
|
||||||
{
|
{
|
||||||
a
|
a
|
||||||
|
href={ "?edit=" (trip_type.id) }
|
||||||
|
hx-boost="true"
|
||||||
.flex
|
.flex
|
||||||
."w-full"
|
."w-full"
|
||||||
."h-full"
|
."h-full"
|
||||||
href={ "?edit=" (trip_type.id) }
|
|
||||||
{
|
{
|
||||||
span
|
span
|
||||||
."m-auto"
|
."m-auto"
|
||||||
@@ -119,6 +122,7 @@ impl TypeList {
|
|||||||
action="/trips/types/"
|
action="/trips/types/"
|
||||||
target="_self"
|
target="_self"
|
||||||
method="post"
|
method="post"
|
||||||
|
hx-boost="true"
|
||||||
."mt-8" ."p-5" ."border-2" ."border-gray-200"
|
."mt-8" ."p-5" ."border-2" ."border-gray-200"
|
||||||
{
|
{
|
||||||
div ."mb-5" ."flex" ."flex-row" {
|
div ."mb-5" ."flex" ."flex-row" {
|
||||||
|
|||||||
114
rust/src/main.rs
114
rust/src/main.rs
@@ -166,13 +166,21 @@ async fn main() -> Result<(), sqlx::Error> {
|
|||||||
.nest(
|
.nest(
|
||||||
"/trips/",
|
"/trips/",
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(trips))
|
.route("/", get(trips).post(trip_create))
|
||||||
.route("/types/", get(trips_types).post(trip_type_create))
|
.route("/types/", get(trips_types).post(trip_type_create))
|
||||||
.route("/types/:id/edit/name/submit", post(trips_types_edit_name))
|
.route("/types/:id/edit/name/submit", post(trips_types_edit_name))
|
||||||
.route("/", post(trip_create))
|
|
||||||
.route("/:id/", get(trip))
|
.route("/:id/", get(trip))
|
||||||
.route("/:id/comment/submit", post(trip_comment_set))
|
.route("/:id/comment/submit", post(trip_comment_set))
|
||||||
.route("/:id/categories/:id/select", post(trip_category_select))
|
.route("/:id/categories/:id/select", post(trip_category_select))
|
||||||
|
.route("/:id/packagelist/", get(trip_packagelist))
|
||||||
|
.route(
|
||||||
|
"/:id/packagelist/item/:id/pack",
|
||||||
|
post(trip_item_packagelist_set_pack_htmx),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/:id/packagelist/item/:id/unpack",
|
||||||
|
post(trip_item_packagelist_set_unpack_htmx),
|
||||||
|
)
|
||||||
.route("/:id/state/:id", post(trip_state_set))
|
.route("/:id/state/:id", post(trip_state_set))
|
||||||
.route("/:id/total_weight", get(trip_total_weight_htmx))
|
.route("/:id/total_weight", get(trip_total_weight_htmx))
|
||||||
.route("/:id/type/:id/add", get(trip_type_add))
|
.route("/:id/type/:id/add", get(trip_type_add))
|
||||||
@@ -199,18 +207,15 @@ async fn main() -> Result<(), sqlx::Error> {
|
|||||||
"/inventory/",
|
"/inventory/",
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(inventory_inactive))
|
.route("/", get(inventory_inactive))
|
||||||
.route("/category/", post(inventory_category_create))
|
|
||||||
.route("/item/:id/", get(inventory_item))
|
|
||||||
.route("/categories/:id/select", post(inventory_category_select))
|
.route("/categories/:id/select", post(inventory_category_select))
|
||||||
.route("/item/", post(inventory_item_create))
|
.route("/category/", post(inventory_category_create))
|
||||||
.route("/item/name/validate", post(inventory_item_validate_name))
|
|
||||||
.route("/category/:id/", get(inventory_active))
|
.route("/category/:id/", get(inventory_active))
|
||||||
|
.route("/item/", post(inventory_item_create))
|
||||||
|
.route("/item/:id/", get(inventory_item))
|
||||||
|
.route("/item/:id/cancel", get(inventory_item_cancel))
|
||||||
.route("/item/:id/delete", get(inventory_item_delete))
|
.route("/item/:id/delete", get(inventory_item_delete))
|
||||||
.route("/item/:id/edit", post(inventory_item_edit))
|
.route("/item/:id/edit", post(inventory_item_edit))
|
||||||
.route("/item/:id/cancel", get(inventory_item_cancel)), // .route(
|
.route("/item/name/validate", post(inventory_item_validate_name)),
|
||||||
// "/inventory/category/:id/items",
|
|
||||||
// post(htmx_inventory_category_items),
|
|
||||||
// );
|
|
||||||
)
|
)
|
||||||
.fallback(|| async { (StatusCode::NOT_FOUND, "not found") })
|
.fallback(|| async { (StatusCode::NOT_FOUND, "not found") })
|
||||||
.with_state(state);
|
.with_state(state);
|
||||||
@@ -1777,7 +1782,7 @@ async fn inventory_category_select(
|
|||||||
let mut headers = HeaderMap::new();
|
let mut headers = HeaderMap::new();
|
||||||
headers.insert::<HeaderName>(
|
headers.insert::<HeaderName>(
|
||||||
HtmxResponseHeaders::PushUrl.into(),
|
HtmxResponseHeaders::PushUrl.into(),
|
||||||
format!("/inventory/category/{category_id}")
|
format!("/inventory/category/{category_id}/")
|
||||||
.parse()
|
.parse()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
@@ -1792,3 +1797,90 @@ async fn inventory_category_select(
|
|||||||
),
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn trip_packagelist(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path(trip_id): Path<Uuid>,
|
||||||
|
) -> Result<(StatusCode, Markup), (StatusCode, Markup)> {
|
||||||
|
let mut trip = models::Trip::find(&state.database_pool, trip_id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
ErrorPage::build(&e.to_string()),
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.ok_or((
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
ErrorPage::build(&format!("trip with id {trip_id} not found")),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
trip.load_categories(&state.database_pool)
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
ErrorPage::build(&e.to_string()),
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
StatusCode::OK,
|
||||||
|
Root::build(
|
||||||
|
&components::packagelist::TripPackageList::build(&trip),
|
||||||
|
&TopLevelPage::None,
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn trip_item_packagelist_set_pack_htmx(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
|
||||||
|
) -> Result<(StatusCode, Markup), (StatusCode, Markup)> {
|
||||||
|
trip_item_set_state(&state, trip_id, item_id, TripItemStateKey::Pack, true).await?;
|
||||||
|
|
||||||
|
let item = models::TripItem::find(&state.database_pool, trip_id, item_id)
|
||||||
|
.await
|
||||||
|
.map_err(|error| {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
ErrorPage::build(&error.to_string()),
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.ok_or((
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
ErrorPage::build(&format!("an item with id {item_id} does not exist")),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
StatusCode::OK,
|
||||||
|
components::packagelist::TripPackageListRow::build(trip_id, &item),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn trip_item_packagelist_set_unpack_htmx(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((trip_id, item_id)): Path<(Uuid, Uuid)>,
|
||||||
|
) -> Result<(StatusCode, Markup), (StatusCode, Markup)> {
|
||||||
|
trip_item_set_state(&state, trip_id, item_id, TripItemStateKey::Pack, false).await?;
|
||||||
|
|
||||||
|
// note that this cannot fail due to a missing item, as trip_item_set_state would already
|
||||||
|
// return 404. but error handling cannot hurt ;)
|
||||||
|
let item = models::TripItem::find(&state.database_pool, trip_id, item_id)
|
||||||
|
.await
|
||||||
|
.map_err(|error| {
|
||||||
|
(
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
ErrorPage::build(&error.to_string()),
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.ok_or((
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
ErrorPage::build(&format!("an item with id {item_id} does not exist")),
|
||||||
|
))?;
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
StatusCode::OK,
|
||||||
|
components::packagelist::TripPackageListRow::build(trip_id, &item),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user