diff --git a/src/Roux/Html.hs b/src/Roux/Html.hs index 14d2133..d15b344 100644 --- a/src/Roux/Html.hs +++ b/src/Roux/Html.hs @@ -546,6 +546,36 @@ indexPage _mode recipes = filterOk = filter (isRight . Idx.riRecipe) filterBad = filter (isLeft . Idx.riRecipe) +-- | Inline JavaScript for converting file uploads to base64 before form submission. +fileUploadJs :: Text +fileUploadJs = + T.unlines + [ "(function(){" + , "'use strict';" + , "var form = document.getElementById('import-form');" + , "var fileInput = document.getElementById('file-input');" + , "var b64Field = document.getElementById('file-b64');" + , "var nameField = document.getElementById('file-name');" + , "var mimeField = document.getElementById('file-mime');" + , "" + , "form.addEventListener('submit', function(e) {" + , " if (fileInput.files.length > 0) {" + , " e.preventDefault();" + , " var file = fileInput.files[0];" + , " var reader = new FileReader();" + , " reader.onload = function() {" + , " var b64 = reader.result.split(',')[1];" + , " b64Field.value = b64;" + , " nameField.value = file.name;" + , " mimeField.value = file.type;" + , " form.submit();" + , " };" + , " reader.readAsDataURL(file);" + , " }" + , "});" + , "})();" + ] + -- --------------------------------------------------------------------------- -- Import page (form + result) -- --------------------------------------------------------------------------- @@ -560,16 +590,41 @@ importPage merror = case merror of Just err -> H.p ! A.style "color: var(--roux-accent);" $ H.toHtml err Nothing -> pure () - H.form ! A.method "POST" ! A.action "/import" $ do + H.p ! A.style "font-size: 0.85rem; opacity: 0.7; margin-bottom: 1.5rem;" $ + "Paste a recipe URL, upload a PDF or image, or paste recipe text directly." + H.form ! A.method "POST" ! A.action "/import" ! A.id "import-form" $ do + -- URL input H.label ! A.for "url" $ "Recipe URL" H.input ! A.type_ "url" ! A.id "url" ! A.name "url" ! A.placeholder "https://cooking.nytimes.com/recipes/..." - ! A.required "" - ! A.style "width: 100%; margin-bottom: 1rem;" + ! A.style "width: 100%; margin-bottom: 1.5rem;" + -- File upload + H.label ! A.for "file-input" $ "Or upload a PDF or image" + H.input + ! A.type_ "file" + ! A.id "file-input" + ! A.accept ".pdf,image/*" + ! A.style "width: 100%; margin-bottom: 1.5rem;" + -- Hidden fields for base64 file data (set by JS) + H.input ! A.type_ "hidden" ! A.id "file-b64" ! A.name "file-b64" ! A.value "" + H.input ! A.type_ "hidden" ! A.id "file-name" ! A.name "file-name" ! A.value "" + H.input ! A.type_ "hidden" ! A.id "file-mime" ! A.name "file-mime" ! A.value "" + -- Text area + H.label ! A.for "text" $ "Or paste recipe text" + H.textarea + ! A.id "text" + ! A.name "text" + ! A.rows "8" + ! A.placeholder "Paste recipe text here..." + ! A.style "width: 100%; margin-bottom: 1.5rem; font-family: monospace; font-size: 0.85rem;" + $ "" + -- Submit H.button ! A.type_ "submit" $ "Import Recipe" + -- Inline JS for base64 file conversion + H.script ! A.type_ "text/javascript" $ H.preEscapedText fileUploadJs -- | Render the import result page with an error message. importResultPage :: ImportError -> ByteString