fix: send URL-encoded form data instead of multipart, show errors inline, default empty date to today
Build and Deploy / build-and-deploy (push) Successful in 15m46s
Build and Deploy / build-and-deploy (push) Successful in 15m46s
This commit is contained in:
+11
-5
@@ -658,11 +658,13 @@ cookLogJs =
|
|||||||
, "var form = document.getElementById('roux-record-form');"
|
, "var form = document.getElementById('roux-record-form');"
|
||||||
, "var dateInput = document.getElementById('roux-record-date');"
|
, "var dateInput = document.getElementById('roux-record-date');"
|
||||||
, "var commentInput = document.getElementById('roux-record-comment');"
|
, "var commentInput = document.getElementById('roux-record-comment');"
|
||||||
|
, "var errorEl = document.getElementById('roux-record-error');"
|
||||||
, "if (!btn || !modal || !form) return;"
|
, "if (!btn || !modal || !form) return;"
|
||||||
, ""
|
, ""
|
||||||
, "function showModal() {"
|
, "function showModal() {"
|
||||||
, " dateInput.value = new Date().toISOString().slice(0,10);"
|
, " dateInput.value = new Date().toISOString().slice(0,10);"
|
||||||
, " commentInput.value = '';"
|
, " commentInput.value = '';"
|
||||||
|
, " errorEl.style.display = 'none';"
|
||||||
, " modal.style.display = 'block';"
|
, " modal.style.display = 'block';"
|
||||||
, " dateInput.focus();"
|
, " dateInput.focus();"
|
||||||
, "}"
|
, "}"
|
||||||
@@ -681,20 +683,23 @@ cookLogJs =
|
|||||||
, ""
|
, ""
|
||||||
, "form.addEventListener('submit', async function(e) {"
|
, "form.addEventListener('submit', async function(e) {"
|
||||||
, " e.preventDefault();"
|
, " e.preventDefault();"
|
||||||
|
, " var date = dateInput.value;"
|
||||||
|
, " var comment = commentInput.value;"
|
||||||
, " var filename = btn.dataset.filename;"
|
, " var filename = btn.dataset.filename;"
|
||||||
, " var fd = new FormData(form);"
|
, " var body = 'cooked-date=' + encodeURIComponent(date);"
|
||||||
|
, " if (comment) body += '&comment=' + encodeURIComponent(comment);"
|
||||||
, " var res = await fetch('/cook-log/' + encodeURIComponent(filename), {"
|
, " var res = await fetch('/cook-log/' + encodeURIComponent(filename), {"
|
||||||
, " method: 'POST',"
|
, " method: 'POST',"
|
||||||
, " body: fd"
|
, " headers: { 'Content-Type': 'application/x-www-form-urlencoded' },"
|
||||||
|
, " body: body"
|
||||||
, " });"
|
, " });"
|
||||||
, " if (!res.ok) {"
|
, " if (!res.ok) {"
|
||||||
, " var err = await res.json();"
|
, " var err = await res.json();"
|
||||||
, " alert(err.error || 'Failed to log cooking');"
|
, " errorEl.textContent = err.error || 'Failed to log cooking';"
|
||||||
|
, " errorEl.style.display = 'block';"
|
||||||
, " return;"
|
, " return;"
|
||||||
, " }"
|
, " }"
|
||||||
, " hideModal();"
|
, " hideModal();"
|
||||||
, " var date = fd.get('cooked-date');"
|
|
||||||
, " var comment = fd.get('comment');"
|
|
||||||
, " var entryDiv = document.createElement('div');"
|
, " var entryDiv = document.createElement('div');"
|
||||||
, " entryDiv.style.margin = '0.3rem 0';"
|
, " entryDiv.style.margin = '0.3rem 0';"
|
||||||
, " entryDiv.style.fontSize = '0.9rem';"
|
, " entryDiv.style.fontSize = '0.9rem';"
|
||||||
@@ -983,6 +988,7 @@ renderRecipe filename recipe entries = do
|
|||||||
H.input ! A.type_ "date" ! A.id "roux-record-date" ! A.name "cooked-date" ! A.required ""
|
H.input ! A.type_ "date" ! A.id "roux-record-date" ! A.name "cooked-date" ! A.required ""
|
||||||
H.label ! A.for "roux-record-comment" $ "Comment (optional)"
|
H.label ! A.for "roux-record-comment" $ "Comment (optional)"
|
||||||
H.textarea ! A.id "roux-record-comment" ! A.name "comment" ! A.placeholder "How did it go?" ! A.maxlength "500" $ ""
|
H.textarea ! A.id "roux-record-comment" ! A.name "comment" ! A.placeholder "How did it go?" ! A.maxlength "500" $ ""
|
||||||
|
H.p ! A.id "roux-record-error" ! A.style "color: var(--roux-accent); font-size: 0.8rem; margin: 0 0 0.5rem; display: none;" $ ""
|
||||||
H.div ! A.class_ "roux-modal-actions" $ do
|
H.div ! A.class_ "roux-modal-actions" $ do
|
||||||
H.button ! A.type_ "button" ! A.class_ "roux-modal-cancel" ! A.id "roux-modal-cancel" $ "Cancel"
|
H.button ! A.type_ "button" ! A.class_ "roux-modal-cancel" ! A.id "roux-modal-cancel" $ "Cancel"
|
||||||
H.button ! A.type_ "submit" ! A.class_ "roux-modal-confirm" $ "Log it"
|
H.button ! A.type_ "submit" ! A.class_ "roux-modal-confirm" $ "Log it"
|
||||||
|
|||||||
+13
-6
@@ -26,7 +26,6 @@ import qualified Data.Map.Strict as Map
|
|||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
import Data.Text.Encoding (encodeUtf8)
|
import Data.Text.Encoding (encodeUtf8)
|
||||||
import Data.Time.Clock (diffUTCTime, getCurrentTime)
|
|
||||||
|
|
||||||
import qualified Network.HTTP.Types as HTTP
|
import qualified Network.HTTP.Types as HTTP
|
||||||
import qualified Network.Wai as Wai
|
import qualified Network.Wai as Wai
|
||||||
@@ -48,6 +47,7 @@ import qualified Data.ByteString as BS
|
|||||||
import Data.Maybe (fromMaybe, mapMaybe)
|
import Data.Maybe (fromMaybe, mapMaybe)
|
||||||
import qualified Data.Text.Encoding as TE
|
import qualified Data.Text.Encoding as TE
|
||||||
import Data.Time.Calendar (Day, fromGregorianValid)
|
import Data.Time.Calendar (Day, fromGregorianValid)
|
||||||
|
import Data.Time.Clock (diffUTCTime, getCurrentTime, utctDay)
|
||||||
import qualified Data.Time.Format as Time
|
import qualified Data.Time.Format as Time
|
||||||
import qualified Database.SQLite.Simple as SQL
|
import qualified Database.SQLite.Simple as SQL
|
||||||
import Roux.Config (AnthropicConfig (..), RouxConfig (..))
|
import Roux.Config (AnthropicConfig (..), RouxConfig (..))
|
||||||
@@ -672,16 +672,19 @@ handleCookLogPost db filename request respond = do
|
|||||||
let params = parseFormBody body
|
let params = parseFormBody body
|
||||||
dateStr = fromMaybe "" (lookup "cooked-date" params)
|
dateStr = fromMaybe "" (lookup "cooked-date" params)
|
||||||
comment = lookup "comment" params
|
comment = lookup "comment" params
|
||||||
case parseDate dateStr of
|
let doInsert d = do
|
||||||
Just day -> do
|
_ <- CookLog.insertEntry db filename d comment
|
||||||
_ <- CookLog.insertEntry db filename day comment
|
respond $
|
||||||
let resp =
|
|
||||||
Wai.responseLBS
|
Wai.responseLBS
|
||||||
HTTP.status200
|
HTTP.status200
|
||||||
[("Content-Type", "application/json")]
|
[("Content-Type", "application/json")]
|
||||||
(LB.fromStrict (encodeUtf8 "{\"ok\":true}"))
|
(LB.fromStrict (encodeUtf8 "{\"ok\":true}"))
|
||||||
respond resp
|
case parseDate dateStr of
|
||||||
|
Just d -> doInsert d
|
||||||
Nothing ->
|
Nothing ->
|
||||||
|
if T.null dateStr
|
||||||
|
then getCurrentDay >>= doInsert
|
||||||
|
else
|
||||||
let resp =
|
let resp =
|
||||||
Wai.responseLBS
|
Wai.responseLBS
|
||||||
HTTP.status400
|
HTTP.status400
|
||||||
@@ -689,6 +692,10 @@ handleCookLogPost db filename request respond = do
|
|||||||
(LB.fromStrict (encodeUtf8 "{\"error\":\"Invalid date format\"}"))
|
(LB.fromStrict (encodeUtf8 "{\"error\":\"Invalid date format\"}"))
|
||||||
in respond resp
|
in respond resp
|
||||||
|
|
||||||
|
-- | Get today's date.
|
||||||
|
getCurrentDay :: IO Day
|
||||||
|
getCurrentDay = utctDay <$> getCurrentTime
|
||||||
|
|
||||||
-- | Parse a YYYY-MM-DD date string.
|
-- | Parse a YYYY-MM-DD date string.
|
||||||
parseDate :: Text -> Maybe Day
|
parseDate :: Text -> Maybe Day
|
||||||
parseDate t = case T.splitOn "-" t of
|
parseDate t = case T.splitOn "-" t of
|
||||||
|
|||||||
Reference in New Issue
Block a user