hledger-web 1.26.1 → 1.27
raw patch · 13 files changed
+217/−183 lines, 13 filesdep +breakpointdep ~basedep ~hledgerdep ~hledger-libPVP ok
version bump matches the API change (PVP)
Dependencies added: breakpoint
Dependency ranges changed: base, hledger, hledger-lib
API changes (from Hackage documentation)
- Hledger.Web.Import: parseTime :: ParseTime t => TimeLocale -> String -> String -> Maybe t
- Hledger.Web.Import: readTime :: ParseTime t => TimeLocale -> String -> String -> t
- Hledger.Web.Import: readsTime :: ParseTime t => TimeLocale -> String -> ReadS t
+ Hledger.Web.Import: catchHandlerExceptions :: (Yesod site, MonadUnliftIO m) => site -> m a -> (SomeException -> m a) -> m a
+ Hledger.Web.Import: class SafeToInsert a
+ Hledger.Web.Import: dayOfWeekDiff :: DayOfWeek -> DayOfWeek -> Int
+ Hledger.Web.Import: firstDayOfWeekOnAfter :: DayOfWeek -> Day -> Day
+ Hledger.Web.Import: parseTimeMultipleM :: (MonadFail m, ParseTime t) => Bool -> TimeLocale -> [(String, String)] -> m t
+ Hledger.Web.Import: pastMidnight :: DiffTime -> TimeOfDay
+ Hledger.Web.Import: pattern YearMonthDay :: Year -> MonthOfYear -> DayOfMonth -> Day
+ Hledger.Web.Import: sinceMidnight :: TimeOfDay -> DiffTime
+ Hledger.Web.Import: type DayOfMonth = Int
+ Hledger.Web.Import: type MonthOfYear = Int
+ Hledger.Web.Import: type Year = Integer
- Hledger.Web.Foundation: type Form x = Html -> MForm (HandlerFor App) (FormResult x, Widget)
+ Hledger.Web.Foundation: type Form a = Html -> MForm Handler (FormResult a, Widget)
- Hledger.Web.Import: class Foldable (t :: Type -> Type)
+ Hledger.Web.Import: class Foldable (t :: TYPE LiftedRep -> Type)
- Hledger.Web.Import: fromGregorian :: Integer -> Int -> Int -> Day
+ Hledger.Web.Import: fromGregorian :: Year -> MonthOfYear -> DayOfMonth -> Day
- Hledger.Web.Import: fromGregorianValid :: Integer -> Int -> Int -> Maybe Day
+ Hledger.Web.Import: fromGregorianValid :: Year -> MonthOfYear -> DayOfMonth -> Maybe Day
- Hledger.Web.Import: gregorianMonthLength :: Integer -> Int -> Int
+ Hledger.Web.Import: gregorianMonthLength :: Year -> MonthOfYear -> DayOfMonth
- Hledger.Web.Import: infixl 6 +
+ Hledger.Web.Import: infixl 6 -
- Hledger.Web.Import: isLeapYear :: Integer -> Bool
+ Hledger.Web.Import: isLeapYear :: Year -> Bool
- Hledger.Web.Import: mapAccumL :: Traversable t => (a -> b -> (a, c)) -> a -> t b -> (a, t c)
+ Hledger.Web.Import: mapAccumL :: Traversable t => (s -> a -> (s, b)) -> s -> t a -> (s, t b)
- Hledger.Web.Import: mapAccumR :: Traversable t => (a -> b -> (a, c)) -> a -> t b -> (a, t c)
+ Hledger.Web.Import: mapAccumR :: Traversable t => (s -> a -> (s, b)) -> s -> t a -> (s, t b)
- Hledger.Web.Import: seq :: forall (r :: RuntimeRep) a (b :: TYPE r). a -> b -> b
+ Hledger.Web.Import: seq :: forall {r :: RuntimeRep} a (b :: TYPE r). a -> b -> b
- Hledger.Web.Import: toGregorian :: Day -> (Integer, Int, Int)
+ Hledger.Web.Import: toGregorian :: Day -> (Year, MonthOfYear, DayOfMonth)
- Hledger.Web.Import: type Form x = Html -> MForm (HandlerFor App) (FormResult x, Widget)
+ Hledger.Web.Import: type Form a = Html -> MForm Handler (FormResult a, Widget)
- Hledger.Web.Widget.AddForm: addForm :: (site ~ HandlerSite m, RenderMessage site FormMessage, MonadHandler m) => Journal -> Day -> Markup -> MForm m (FormResult Transaction, WidgetFor site ())
+ Hledger.Web.Widget.AddForm: addForm :: Journal -> Day -> Markup -> MForm Handler (FormResult (Transaction, FilePath), Widget)
- Hledger.Web.Widget.AddForm: addModal :: (MonadWidget m, r ~ Route (HandlerSite m), m ~ WidgetFor (HandlerSite m), RenderMessage (HandlerSite m) FormMessage) => r -> Journal -> Day -> m ()
+ Hledger.Web.Widget.AddForm: addModal :: Route App -> Journal -> Day -> Widget
Files
- CHANGES.md +22/−0
- Hledger/Web/Foundation.hs +4/−3
- Hledger/Web/Handler/AddR.hs +6/−7
- Hledger/Web/Handler/EditR.hs +1/−1
- Hledger/Web/Widget/AddForm.hs +88/−86
- Hledger/Web/Widget/Common.hs +2/−2
- hledger-web.1 +7/−6
- hledger-web.cabal +13/−10
- hledger-web.info +22/−21
- hledger-web.txt +13/−12
- static/hledger.js +12/−3
- templates/add-form.hamlet +26/−31
- templates/manage.hamlet +1/−1
CHANGES.md view
@@ -9,11 +9,33 @@ User-visible changes in hledger-web. See also the hledger changelog. +# 1.27 2022-09-01++Improvements++- Improve the add form's layout and space usage.++- Pre-fill the add form's date field.++- Highlight today in the add form's date picker.++- Focus the add form's description field by default.++- Allow an empty description in the add form.++- Use hledger 1.27++Fixes++- Respect the add form's file selector again.+ (Simon Michael, Kerstin, #1229)+ # 1.26.1 2022-07-11 - Uses hledger 1.26.1. # 1.26 2022-06-04+ Fixes - Don't add link URLs when printing.
Hledger/Web/Foundation.hs view
@@ -84,11 +84,12 @@ -- usually require access to the AppRoute datatype. Therefore, we -- split these actions into two functions and place them in separate files. mkYesodData "App" $(parseRoutesFile "config/routes")+-- ^ defines things like:+-- type Handler = HandlerFor App -- HandlerT App IO, https://www.yesodweb.com/book/routing-and-handlers#routing-and-handlers_handler_monad+-- type Widget = WidgetFor App () -- WidgetT App IO (), https://www.yesodweb.com/book/widgets --- | A convenience alias. type AppRoute = Route App--type Form x = Html -> MForm (HandlerFor App) (FormResult x, Widget)+type Form a = Html -> MForm Handler (FormResult a, Widget) -- Please see the documentation for the Yesod typeclass. There are a number -- of settings which can be configured by overriding methods here.
Hledger/Web/Handler/AddR.hs view
@@ -35,12 +35,11 @@ ((res, view), enctype) <- runFormPost $ addForm j today case res of- FormSuccess res' -> do- let t = txnTieKnot res'- -- XXX(?) move into balanceTransaction- liftIO $ ensureJournalFileExists (journalFilePath j)- -- XXX why not journalAddTransaction ?- liftIO $ appendToJournalFileOrStdout (journalFilePath j) (showTransaction t)+ FormSuccess (t,f) -> do+ let t' = txnTieKnot t+ liftIO $ do+ ensureJournalFileExists f+ appendToJournalFileOrStdout f (showTransaction t') setMessage "Transaction added." redirect JournalR FormMissing -> showForm view enctype@@ -52,7 +51,7 @@ sendResponse =<< defaultLayout [whamlet| <h2>Add transaction <div .row style="margin-top:1em">- <form#addform.form.col-xs-12.col-md-8 method=post enctype=#{enctype}>+ <form#addform.form.col-xs-12.col-sm-11 method=post enctype=#{enctype}> ^{view} |]
Hledger/Web/Handler/EditR.hs view
@@ -15,7 +15,7 @@ import Hledger.Web.Widget.Common (fromFormSuccess, helplink, journalFile404, writeJournalTextIfValidAndChanged) -editForm :: FilePath -> Text -> Markup -> MForm Handler (FormResult Text, Widget)+editForm :: FilePath -> Text -> Form Text editForm f txt = identifyForm "edit" $ \extra -> do (tRes, tView) <- mreq textareaField fs (Just (Textarea txt))
Hledger/Web/Widget/AddForm.hs view
@@ -14,7 +14,7 @@ import Data.Bifunctor (first) import Data.Foldable (toList) import Data.List (dropWhileEnd, unfoldr)-import Data.Maybe (isJust)+import Data.Maybe (isJust, fromMaybe) import qualified Data.Set as S import Data.Text (Text) import Data.Text.Encoding.Base64 (encodeBase64)@@ -25,17 +25,13 @@ import Yesod import Hledger+import Hledger.Web.Foundation (App, Handler, Widget) import Hledger.Web.Settings (widgetFile)+import Data.Function ((&)) -addModal ::- ( MonadWidget m- , r ~ Route (HandlerSite m)- , m ~ WidgetFor (HandlerSite m)- , RenderMessage (HandlerSite m) FormMessage- )- => r -> Journal -> Day -> m ()+addModal :: Route App -> Journal -> Day -> Widget addModal addR j today = do- (addView, addEnctype) <- generateFormPost (addForm j today)+ (addView, addEnctype) <- handlerToWidget $ generateFormPost (addForm j today) [whamlet| <div .modal #addmodal tabindex="-1" role="dialog" aria-labelledby="addLabel" aria-hidden="true"> <div .modal-dialog .modal-lg>@@ -48,106 +44,88 @@ ^{addView} |] -addForm ::- (site ~ HandlerSite m, RenderMessage site FormMessage, MonadHandler m)- => Journal- -> Day- -> Markup- -> MForm m (FormResult Transaction, WidgetFor site ())+addForm :: Journal -> Day -> Markup -> MForm Handler (FormResult (Transaction,FilePath), Widget) addForm j today = identifyForm "add" $ \extra -> do- (dateRes, dateView) <- mreq dateField dateFS Nothing- (descRes, descView) <- mreq textField descFS Nothing- (acctRes, _) <- mreq listField acctFS Nothing- (amtRes, _) <- mreq listField amtFS Nothing- let (postRes, displayRows) = validatePostings acctRes amtRes-- -- bindings used in add-form.hamlet- let descriptions = foldMap S.fromList [journalPayeesDeclaredOrUsed j, journalDescriptions j]- journals = fst <$> jfiles j-- pure (validateTransaction dateRes descRes postRes, $(widgetFile "add-form"))-+ let -- bindings used in add-form.hamlet+ descriptions = foldMap S.fromList [journalPayeesDeclaredOrUsed j, journalDescriptions j]+ files = fst <$> jfiles j+ (dateRes, dateView) <- mreq dateField dateSettings Nothing+ (descRes, descView) <- mopt textField descSettings Nothing+ (acctsRes, _) <- mreq listField acctSettings Nothing+ (amtsRes, _) <- mreq listField amtSettings Nothing+ (fileRes, fileView) <- mreq fileField' fileSettings Nothing+ let+ (postingsRes, displayRows) = validatePostings acctsRes amtsRes+ formRes = validateTransaction dateRes descRes postingsRes fileRes+ return (formRes, $(widgetFile "add-form")) where- dateFS = FieldSettings "date" Nothing Nothing (Just "date")- [("class", "form-control input-lg"), ("placeholder", "Date")]- descFS = FieldSettings "desc" Nothing Nothing (Just "description")- [("class", "form-control input-lg typeahead"), ("placeholder", "Description"), ("size", "40")]- acctFS = FieldSettings "amount" Nothing Nothing (Just "account") []- amtFS = FieldSettings "amount" Nothing Nothing (Just "amount") []- dateField = checkMMap (pure . validateDate) (T.pack . show) textField- validateDate s =- first (const ("Invalid date format" :: Text)) $- fixSmartDateStrEither' today (T.strip s)-+ -- custom fields+ dateField = textField & checkMMap (pure . validateDate) (T.pack . show)+ where+ validateDate s =+ first (const ("Invalid date format" :: Text)) $+ fixSmartDateStrEither' today (T.strip s) listField = Field { fieldParse = const . pure . Right . Just . dropWhileEnd T.null- , fieldView = error "Don't render using this!" -- PARTIAL:+ , fieldView = error' "listField should not be used for rendering" -- PARTIAL: , fieldEnctype = UrlEncoded }-- -- Used in add-form.hamlet- toBloodhoundJson :: [Text] -> Markup- toBloodhoundJson ts =- -- This used to work, but since 1.16, it seems like something changed.- -- toJSON ("a"::Text) gives String "a" instead of "a", etc.- -- preEscapedString . escapeJSSpecialChars . show . toJSON- preEscapedText $ T.concat [- "[",- T.intercalate "," $ map (- ("{\"value\":" <>).- (<> "}").- -- This will convert a value such as ``hledger!`` into- -- ``atob("aGxlZGdlciE=")``. When this gets evaluated on the client,- -- the resulting string is ``hledger!`` again. The same data is- -- passed, but the user-controlled bit of that string can only use- -- characters [a-zA-Z0-9+=/], making it impossible to break out of- -- string context.- b64wrap- ) ts,- "]"- ]-b64wrap :: Text -> Text-b64wrap = ("atob(\""<>) . (<>"\")") . encodeBase64+ fileField' :: Field Handler FilePath+ fileField' = selectFieldList [(T.pack f, f) | f <- fs] & check validateFilepath+ where+ fs = journalFilePaths j+ validateFilepath :: FilePath -> Either FormMessage FilePath+ validateFilepath f+ | f `elem` fs = Right f+ | otherwise = Left $ MsgInputNotFound $ T.pack f+ -- field settings+ dateSettings = FieldSettings "date" Nothing Nothing (Just "date") [("class", "form-control input-lg"), ("placeholder", "Date")]+ descSettings = FieldSettings "desc" Nothing Nothing (Just "description") [("class", "form-control input-lg typeahead"), ("placeholder", "Description"), ("size", "40")]+ acctSettings = FieldSettings "account" Nothing Nothing (Just "account") []+ amtSettings = FieldSettings "amount" Nothing Nothing (Just "amount") []+ fileSettings = FieldSettings "file" Nothing Nothing (Just "file") [("class", "form-control input-lg")] validateTransaction ::- FormResult Day- -> FormResult Text- -> FormResult [Posting]- -> FormResult Transaction-validateTransaction dateRes descRes postingsRes =- case makeTransaction <$> dateRes <*> descRes <*> postingsRes of- FormSuccess txn -> case balanceTransaction defbalancingopts txn of- Left e -> FormFailure [T.pack e]- Right txn' -> FormSuccess txn'+ FormResult Day -> FormResult (Maybe Text) -> FormResult [Posting] -> FormResult FilePath+ -> FormResult (Transaction, FilePath)+validateTransaction dateRes descRes postingsRes fileRes =+ case makeTransaction <$> dateRes <*> descRes <*> postingsRes <*> fileRes of+ FormSuccess (txn,f) -> case balanceTransaction defbalancingopts txn of+ Left e -> FormFailure [T.pack e]+ Right txn' -> FormSuccess (txn',f) x -> x where- makeTransaction date desc postings =- nulltransaction {tdate = date, tdescription = desc, tpostings = postings}-+ makeTransaction date mdesc postings f =+ (nulltransaction {+ tdate = date+ ,tdescription = fromMaybe "" mdesc+ ,tpostings = postings+ ,tsourcepos = (initialPos f, initialPos f)+ }, f) -- | Parse a list of postings out of a list of accounts and a corresponding list -- of amounts validatePostings ::- FormResult [Text]- -> FormResult [Text]+ FormResult [Text] -> FormResult [Text] -> (FormResult [Posting], [(Int, (Text, Text, Maybe Text, Maybe Text))])-validatePostings acctRes amtRes = let+validatePostings acctsRes amtsRes = let -- Zip accounts and amounts, fill in missing values and drop empty rows. rows :: [(Text, Text)]- rows = filter (/= ("", "")) $ zipDefault "" (formSuccess [] acctRes) (formSuccess [] amtRes)+ rows = filter (/= ("", "")) $ zipDefault "" (formSuccess [] acctsRes) (formSuccess [] amtsRes) -- Parse values and check for incomplete rows with only an account or an amount. -- The boolean in unfoldr state is for special handling of 'missingamt', where -- one row may have only an account and not an amount. postings :: [(Text, Text, Either (Maybe Text, Maybe Text) Posting)] postings = unfoldr go (True, rows)- go (True, (x, ""):y:xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (True, y:xs))- go (True, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Right missingamt)), (False, xs))- go (False, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (False, xs))- go (_, ("", y):xs) = Just (("", y, zipRow (Left "Missing account") (checkAmount y)), (False, xs))- go (_, (x, y):xs) = Just ((x, y, zipRow (checkAccount x) (checkAmount y)), (True, xs))- go (_, []) = Nothing+ where+ go (True, (x, ""):y:xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (True, y:xs))+ go (True, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Right missingamt)), (False, xs))+ go (False, (x, ""):xs) = Just ((x, "", zipRow (checkAccount x) (Left "Missing amount")), (False, xs))+ go (_, ("", y):xs) = Just (("", y, zipRow (Left "Missing account") (checkAmount y)), (False, xs))+ go (_, (x, y):xs) = Just ((x, y, zipRow (checkAccount x) (checkAmount y)), (True, xs))+ go (_, []) = Nothing zipRow (Left e) (Left e') = Left (Just e, Just e') zipRow (Left e) (Right _) = Left (Just e, Nothing)@@ -165,7 +143,7 @@ -- Add errors to forms with zero rows if the form is not a FormMissing result :: [(Text, Text, Either (Maybe Text, Maybe Text) Posting)]- result = case (acctRes, amtRes) of+ result = case (acctsRes, amtsRes) of (FormMissing, FormMissing) -> postings _ -> case postings of [] -> [ ("", "", Left (Just "Missing account", Just "Missing amount"))@@ -187,6 +165,30 @@ in (formResult, zip [(1 :: Int)..] display) +-- helper for add-form.hamlet+toBloodhoundJson :: [Text] -> Markup+toBloodhoundJson ts =+ -- This used to work, but since 1.16, it seems like something changed.+ -- toJSON ("a"::Text) gives String "a" instead of "a", etc.+ -- preEscapedString . escapeJSSpecialChars . show . toJSON++ preEscapedText $ T.concat [+ "[",+ T.intercalate "," $ map (+ ("{\"value\":" <>).+ (<> "}").+ -- This will convert a value such as ``hledger!`` into+ -- ``atob("aGxlZGdlciE=")``. When this gets evaluated on the client,+ -- the resulting string is ``hledger!`` again. The same data is+ -- passed, but the user-controlled bit of that string can only use+ -- characters [a-zA-Z0-9+=/], making it impossible to break out of+ -- string context.+ b64wrap+ ) ts,+ "]"+ ]+ where+ b64wrap = ("atob(\""<>) . (<>"\")") . encodeBase64 zipDefault :: a -> [a] -> [a] -> [(a, a)] zipDefault def (b:bs) (c:cs) = (b, c):(zipDefault def bs cs)
Hledger/Web/Widget/Common.hs view
@@ -118,14 +118,14 @@ map quoteIfSpaced . filter (\term -> not $ T.isPrefixOf "date:" term || T.isPrefixOf "date2:" term) .- Query.words'' Query.prefixes+ Query.words'' queryprefixes removeInacct :: Text -> [Text] removeInacct = map quoteIfSpaced . filter (\term -> not $ T.isPrefixOf "inacct:" term || T.isPrefixOf "inacctonly:" term) .- Query.words'' Query.prefixes+ Query.words'' queryprefixes replaceInacct :: Text -> Text -> Text replaceInacct q acct = T.unwords $ acct : removeInacct q
hledger-web.1 view
@@ -1,12 +1,12 @@ -.TH "HLEDGER-WEB" "1" "July 2022" "hledger-web-1.26.1 " "hledger User Manuals"+.TH "HLEDGER-WEB" "1" "September 2022" "hledger-web-1.27 " "hledger User Manuals" .SH NAME .PP hledger-web is a web interface (WUI) for the hledger accounting tool.-This manual is for hledger-web 1.26.1.+This manual is for hledger-web 1.27. .SH SYNOPSIS .PP \f[C]hledger-web [OPTIONS]\f[R]@@ -615,9 +615,10 @@ reboot. .PP On Windows computers, the default value is probably-\f[C]C:\[rs]Users\[rs]MyUserName\[rs].hledger.journal\f[R].+\f[C]C:\[rs]Users\[rs]YOURNAME\[rs].hledger.journal\f[R]. You can change this by running a command like this in a powershell-window:+window (let us know if you need to be an Administrator, and if this+persists across a reboot): .IP .nf \f[C]@@ -625,8 +626,8 @@ \f[R] .fi .PP-(Let us know if you need to be an Administrator, and if this persists-across a reboot.)+Or, change it in settings: see+https://www.java.com/en/download/help/path.html. .SH FILES .PP Reads data from one or more files in hledger journal, timeclock,
hledger-web.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: hledger-web-version: 1.26.1+version: 1.27 synopsis: Web-based user interface for the hledger accounting system description: A simple web-based user interface for the hledger accounting system, providing a more modern UI than the command-line or terminal interfaces.@@ -28,7 +28,7 @@ license-file: LICENSE build-type: Simple tested-with:- GHC==8.8.4, GHC==8.10.4, GHC==9.0.1+ GHC==8.10.7, GHC==9.0.2, GHC==9.2.4 extra-source-files: CHANGES.md README.md@@ -151,14 +151,15 @@ hs-source-dirs: ./ ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns- cpp-options: -DVERSION="1.26.1"+ cpp-options: -DVERSION="1.27" build-depends: Decimal >=0.5.1 , aeson >=1- , base >=4.11 && <4.17+ , base >=4.14 && <4.17 , base64 , blaze-html , blaze-markup+ , breakpoint , bytestring , case-insensitive , clientsession@@ -171,8 +172,8 @@ , extra >=1.6.3 , filepath , hjsmin- , hledger >=1.26.1 && <1.27- , hledger-lib >=1.26.1 && <1.27+ , hledger ==1.27.*+ , hledger-lib ==1.27.* , hspec , http-client , http-conduit@@ -212,9 +213,10 @@ hs-source-dirs: app ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns- cpp-options: -DVERSION="1.26.1"+ cpp-options: -DVERSION="1.27" build-depends:- base+ base >=4.14 && <4.17+ , breakpoint , hledger-web if (flag(dev)) || (flag(library-only)) cpp-options: -DDEVELOPMENT@@ -232,9 +234,10 @@ hs-source-dirs: test ghc-options: -Wall -Wredundant-constraints -Wincomplete-record-updates -Wincomplete-uni-patterns- cpp-options: -DVERSION="1.26.1"+ cpp-options: -DVERSION="1.27" build-depends:- base+ base >=4.14 && <4.17+ , breakpoint , hledger , hledger-lib , hledger-web
hledger-web.info view
@@ -12,7 +12,7 @@ ************** hledger-web is a web interface (WUI) for the hledger accounting tool.-This manual is for hledger-web 1.26.1.+This manual is for hledger-web 1.27. 'hledger-web [OPTIONS]' 'hledger web -- [OPTIONS]'@@ -592,13 +592,14 @@ For this to take effect you might need to 'killall Dock', or reboot. On Windows computers, the default value is probably-'C:\Users\MyUserName\.hledger.journal'. You can change this by running-a command like this in a powershell window:+'C:\Users\YOURNAME\.hledger.journal'. You can change this by running a+command like this in a powershell window (let us know if you need to be+an Administrator, and if this persists across a reboot): > setx LEDGER_FILE "C:\Users\MyUserName\finance\2021.journal" - (Let us know if you need to be an Administrator, and if this persists-across a reboot.)+ Or, change it in settings: see+https://www.java.com/en/download/help/path.html. File: hledger-web.info, Node: FILES, Next: BUGS, Prev: ENVIRONMENT, Up: Top@@ -631,22 +632,22 @@ Tag Table: Node: Top223-Node: OPTIONS1888-Ref: #options1993-Node: PERMISSIONS9904-Ref: #permissions10043-Node: EDITING UPLOADING DOWNLOADING11255-Ref: #editing-uploading-downloading11436-Node: RELOADING12270-Ref: #reloading12404-Node: JSON API12837-Ref: #json-api12951-Node: ENVIRONMENT18441-Ref: #environment18557-Node: FILES19791-Ref: #files19891-Node: BUGS20104-Ref: #bugs20182+Node: OPTIONS1886+Ref: #options1991+Node: PERMISSIONS9902+Ref: #permissions10041+Node: EDITING UPLOADING DOWNLOADING11253+Ref: #editing-uploading-downloading11434+Node: RELOADING12268+Ref: #reloading12402+Node: JSON API12835+Ref: #json-api12949+Node: ENVIRONMENT18439+Ref: #environment18555+Node: FILES19866+Ref: #files19966+Node: BUGS20179+Ref: #bugs20257 End Tag Table
hledger-web.txt view
@@ -5,7 +5,7 @@ NAME hledger-web is a web interface (WUI) for the hledger accounting tool.- This manual is for hledger-web 1.26.1.+ This manual is for hledger-web 1.27. SYNOPSIS hledger-web [OPTIONS]@@ -537,23 +537,24 @@ For this to take effect you might need to killall Dock, or reboot. - On Windows computers, the default value is probably C:\Users\MyUser-- Name\.hledger.journal. You can change this by running a command like- this in a powershell window:+ On Windows computers, the default value is probably C:\Users\YOUR-+ NAME\.hledger.journal. You can change this by running a command like+ this in a powershell window (let us know if you need to be an Adminis-+ trator, and if this persists across a reboot): > setx LEDGER_FILE "C:\Users\MyUserName\finance\2021.journal" - (Let us know if you need to be an Administrator, and if this persists- across a reboot.)+ Or, change it in settings: see https://www.java.com/en/down-+ load/help/path.html. FILES- Reads data from one or more files in hledger journal, timeclock, time-- dot, or CSV format specified with -f, or $LEDGER_FILE, or- $HOME/.hledger.journal (on windows, perhaps+ Reads data from one or more files in hledger journal, timeclock, time-+ dot, or CSV format specified with -f, or $LEDGER_FILE, or+ $HOME/.hledger.journal (on windows, perhaps C:/Users/USER/.hledger.journal). BUGS- The need to precede options with -- when invoked from hledger is awk-+ The need to precede options with -- when invoked from hledger is awk- ward. -f- doesn't work (hledger-web can't read from stdin).@@ -567,7 +568,7 @@ REPORTING BUGS- Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel+ Report bugs at http://bugs.hledger.org (or on the #hledger IRC channel or hledger mail list) @@ -585,4 +586,4 @@ -hledger-web-1.26.1 July 2022 HLEDGER-WEB(1)+hledger-web-1.27 September 2022 HLEDGER-WEB(1)
static/hledger.js view
@@ -4,16 +4,20 @@ // STARTUP $(document).ready(function() {++ // add form helpers XXX move to addForm ?+ // date picker // http://bootstrap-datepicker.readthedocs.io/en/latest/options.html var dateEl = $('#dateWrap').datepicker({ showOnFocus: false, autoclose: true, format: 'yyyy-mm-dd',+ todayHighlight: true, weekStart: 1 // Monday });; - // ensure add form always focuses its first field+ // focus and pre-fill the add form whenever it is shown $('#addmodal') .on('shown.bs.modal', function() { addformFocus();@@ -163,9 +167,14 @@ $('.amount-input:last').keypress(addformAddPosting); } -// Focus the first add form field.+// Pre-fill today's date and focus the description field in the add form. function addformFocus() {- focus($('#addform input#date'));+ $('#addform input[name=date]').val(isoDate());+ focus($('#addform input[name=description]'));+}++function isoDate() {+ return new Date().toLocaleDateString("sv"); // https://stackoverflow.com/a/58633651/84401 } // Focus a jquery-wrapped element, working around http://stackoverflow.com/a/7046837.
templates/add-form.hamlet view
@@ -21,47 +21,42 @@ }); ^{extra} -<div .form-group>- <div .row>- <div .col-md-3 .col-xs-6 .col-sm-6 :isJust (fvErrors dateView):.has-error>- <div #dateWrap .form-group.input-group.date>- ^{fvInput dateView}- <div .input-group-addon>- <span .glyphicon .glyphicon-th>- $maybe err <- fvErrors dateView- <span .help-block .error-block>#{err}- <div .col-md-9 .col-xs-6 .col-sm-6 :isJust (fvErrors descView):.has-error>- <div .form-group>- ^{fvInput descView}- $maybe err <- fvErrors descView- <span .help-block .error-block>#{err}- <div .row>- <div .col-md-3 .col-xs-6 .col-sm-6>- <div .col-md-9 .col-xs-6 .col-sm-6>+<div .row>+ <div .col-md-3.col-sm-5.col-xs-6 :isJust (fvErrors dateView):.has-error>+ <div #dateWrap .form-group.input-group.date>+ ^{fvInput dateView}+ <div .input-group-addon>+ <span .glyphicon .glyphicon-th>+ $maybe err <- fvErrors dateView+ <span .help-block .error-block>#{err}+ <div .col-md-9.col-sm-7.col-xs-6 :isJust (fvErrors descView):.has-error>+ <div .form-group>+ ^{fvInput descView}+ $maybe err <- fvErrors descView+ <span .help-block .error-block>#{err} <div .account-postings> $forall (n, (acc, amt, accE, amtE)) <- displayRows- <div .form-group .row .account-group>- <div .col-md-8 .col-xs-8 .col-sm-8 :isJust accE:.has-error>+ <div .form-group .row .account-group style="padding-left:1em;">+ <div .col-sm-9.col-xs-9 :isJust accE:.has-error> <input .account-input.form-control.input-lg.typeahead type=text name=account placeholder="Account #{n}" value="#{acc}"> $maybe err <- accE <span .help-block .error-block>_{err}- <div .col-md-4 .col-xs-4 .col-sm-4 :isJust amtE:.has-error>+ <div .col-sm-3.col-xs-3 :isJust amtE:.has-error> <input .amount-input.form-control.input-lg type=text name=amount placeholder="Amount #{n}" value="#{amt}"> $maybe err <- amtE <span .help-block .error-block>_{err} <div .row>- <div .col-md-8 .col-xs-8 .col-sm-8>- <div .col-md-4 .col-xs-4 .col-sm-4>- <button type=submit .btn .btn-default .btn-lg name=submit>add--$if length journals > 1- <br>- <span .input-lg>to:- <select #journalselect .form-control.input-lg name=journal style="width:auto; display:inline-block;">- $forall p <- journals- <option value=#{p}>#{p}-<span .small style="padding-left:2em;">+ <div .col-sm-9.col-xs-9>+ $if length files > 1+ Add to:+ + <div style="display:inline-block; width:auto;" .form-group :isJust (fvErrors fileView):.has-error>+ ^{fvInput fileView}+ $maybe err <- fvErrors fileView+ <span .help-block .error-block>#{err}+ <div .col-sm-3.col-xs-3 style="text-align:right;">+ <button type=submit .btn .btn-default .btn-lg name=submit style="font-weight:bold;">Save
templates/manage.hamlet view
@@ -2,7 +2,7 @@ Your journal's files <div.row>- <div .col-xs-12.col-sm-8.col-md-6>+ <div .col-md-6.col-sm-8.col-xs-12> <table .table.table-condensed> <thead> <th>