summaryrefslogtreecommitdiff
path: root/Handler
diff options
context:
space:
mode:
Diffstat (limited to 'Handler')
-rw-r--r--Handler/Common.hs43
-rw-r--r--Handler/Common/Types.hs4
-rw-r--r--Handler/InventoryListing.hs21
-rw-r--r--Handler/Item.hs27
-rw-r--r--Handler/List.hs44
-rw-r--r--Handler/ReferenceItem.hs17
-rw-r--r--Handler/ReferenceListing.hs21
-rw-r--r--Handler/UpdateItem.hs23
8 files changed, 130 insertions, 70 deletions
diff --git a/Handler/Common.hs b/Handler/Common.hs
index 2416d15..1cf63de 100644
--- a/Handler/Common.hs
+++ b/Handler/Common.hs
@@ -18,6 +18,9 @@ import Data.Unique
18 18
19import qualified Data.Text as Text (pack) 19import qualified Data.Text as Text (pack)
20 20
21import Data.Set (Set)
22import qualified Data.Set as Set
23
21import Control.Lens 24import Control.Lens
22 25
23import Handler.Common.Types 26import Handler.Common.Types
@@ -26,11 +29,14 @@ dayFormat :: Day -> String
26dayFormat = formatTime defaultTimeLocale "%e. %b %y" 29dayFormat = formatTime defaultTimeLocale "%e. %b %y"
27 30
28itemForm :: Maybe Item -- ^ Update existing item or insert new? 31itemForm :: Maybe Item -- ^ Update existing item or insert new?
29 -> Html -> MForm Handler (FormResult Item, Widget) 32 -> Html -> MForm Handler (FormResult (WithType Item), Widget)
30itemForm proto identView = do 33itemForm proto identView = do
31 today <- utctDay <$> liftIO getCurrentTime 34 today <- utctDay <$> liftIO getCurrentTime
35
36 t <- lift . runDB $ maybe (return Nothing) (fmap (Just . kindType) . getType) proto
32 37
33 (kindRes, kindWidget) <- kindField $ itemKind <$> proto 38 (kindRes, kindWidget) <- kindField $ itemKind <$> proto
39 (typeRes, typeWidget) <- typeField $ t
34 (boughtRes, boughtWidget) <- dayForm (maybe (Just $ Just today) Just $ fmap itemBought proto) "Unknown" 40 (boughtRes, boughtWidget) <- dayForm (maybe (Just $ Just today) Just $ fmap itemBought proto) "Unknown"
35 (expiresRes, expiresWidget) <- dayForm (fmap itemExpires proto) "Never" 41 (expiresRes, expiresWidget) <- dayForm (fmap itemExpires proto) "Never"
36 (openedRes, openedWidget) <- dayForm (fmap itemOpened proto) "Never" 42 (openedRes, openedWidget) <- dayForm (fmap itemOpened proto) "Never"
@@ -40,13 +46,15 @@ itemForm proto identView = do
40 itemBought <- boughtRes 46 itemBought <- boughtRes
41 itemExpires <- expiresRes 47 itemExpires <- expiresRes
42 itemOpened <- openedRes 48 itemOpened <- openedRes
43 return Item{ itemNormKind = normalizeKind itemKind, ..} 49 t <- typeRes
50 return $ Item{ itemNormKind = normalizeKind itemKind, ..} `WithType` t
44 51
45 return . (itemRes, ) $ 52 return . (itemRes, ) $
46 [whamlet| 53 [whamlet|
47 $newline never 54 $newline never
48 #{identView} 55 #{identView}
49 <div .td>^{kindWidget} 56 <div .td>^{kindWidget}
57 <div .td>^{typeWidget}
50 <div .td>^{boughtWidget} 58 <div .td>^{boughtWidget}
51 <div .td>^{expiresWidget} 59 <div .td>^{expiresWidget}
52 <div .td>^{openedWidget} 60 <div .td>^{openedWidget}
@@ -81,19 +89,24 @@ inventoryListing :: InventoryState -> Widget
81inventoryListing InventoryState{ invFormState = formState, ..} = $(widgetFile "inventoryListing") 89inventoryListing InventoryState{ invFormState = formState, ..} = $(widgetFile "inventoryListing")
82 90
83referenceForm :: Maybe Reference -- ^ Update existing item or insert new? 91referenceForm :: Maybe Reference -- ^ Update existing item or insert new?
84 -> Html -> MForm Handler (FormResult Reference, Widget) 92 -> Html -> MForm Handler (FormResult (WithType Reference), Widget)
85referenceForm proto identView = do 93referenceForm proto identView = do
94 t <- lift . runDB $ maybe (return Nothing) (fmap (Just . kindType) . getType) proto
95
86 (kindRes, kindWidget) <- kindField $ referenceKind <$> proto 96 (kindRes, kindWidget) <- kindField $ referenceKind <$> proto
97 (typeRes, typeWidget) <- typeField $ t
87 98
88 let referenceRes = do 99 let referenceRes = do
89 referenceKind <- kindRes 100 referenceKind <- kindRes
90 return Reference{ referenceNormKind = normalizeKind referenceKind, .. } 101 t <- typeRes
102 return $ Reference{ referenceNormKind = normalizeKind referenceKind, .. } `WithType` t
91 103
92 return . (referenceRes, ) $ 104 return . (referenceRes, ) $
93 [whamlet| 105 [whamlet|
94 $newline never 106 $newline never
95 #{identView} 107 #{identView}
96 <div .td>^{kindWidget} 108 <div .td>^{kindWidget}
109 <div .td>^{typeWidget}
97 |] 110 |]
98 111
99referenceListing :: ReferenceState -> Widget 112referenceListing :: ReferenceState -> Widget
@@ -121,6 +134,28 @@ kindField proto = do
121 <option value=#{opt}> 134 <option value=#{opt}>
122 |] 135 |]
123 136
137typeField :: Maybe Text -> MForm Handler (FormResult Text, Widget)
138typeField proto = do
139 optionId <- ("options" <>) . tshow . hashUnique <$> liftIO newUnique
140
141 let
142 attrs = [ ("list", optionId)
143 , ("autocomplete", "off")
144 ]
145
146 (typeRes, typeView) <- mreq textField ("" { fsAttrs = attrs }) proto
147
148 (Set.fromList . map (kindType . entityVal) -> options) <- lift . runDB $ selectList [] []
149
150 return . (typeRes, ) $
151 [whamlet|
152 $newline never
153 ^{fvInput typeView}
154 <datalist ##{optionId}>
155 $forall opt <- Set.toList options
156 <option value=#{opt}>
157 |]
158
124kinds :: Handler [Text] 159kinds :: Handler [Text]
125kinds = do 160kinds = do
126 stock <- runDB $ selectList [] [] 161 stock <- runDB $ selectList [] []
diff --git a/Handler/Common/Types.hs b/Handler/Common/Types.hs
index 08653af..9150f16 100644
--- a/Handler/Common/Types.hs
+++ b/Handler/Common/Types.hs
@@ -7,12 +7,12 @@ import Import
7import Control.Lens 7import Control.Lens
8 8
9data InventoryState = InventoryState 9data InventoryState = InventoryState
10 { stock :: [Entity Item] 10 { stock :: [WithType (Entity Item)]
11 , invFormState :: Maybe (FormState ItemId) 11 , invFormState :: Maybe (FormState ItemId)
12 } 12 }
13 13
14data ReferenceState = ReferenceState 14data ReferenceState = ReferenceState
15 { reference :: [Entity Reference] 15 { reference :: [WithType (Entity Reference)]
16 , refFormState :: Maybe (FormState ReferenceId) 16 , refFormState :: Maybe (FormState ReferenceId)
17 } 17 }
18 18
diff --git a/Handler/InventoryListing.hs b/Handler/InventoryListing.hs
index 12f36ba..6d4d8fa 100644
--- a/Handler/InventoryListing.hs
+++ b/Handler/InventoryListing.hs
@@ -8,19 +8,26 @@ getInventoryListingR = postInventoryListingR
8postInventoryListingR = do 8postInventoryListingR = do
9 ((insertResult, fsInsertForm), fsInsertEncoding) <- runFormPost $ itemForm Nothing 9 ((insertResult, fsInsertForm), fsInsertEncoding) <- runFormPost $ itemForm Nothing
10 10
11 mapM_ (addMessage "formError" . toHtml) =<< case insertResult of 11 case insertResult of
12 FormSuccess newItem -> [] <$ runDB (insert newItem) 12 FormSuccess (Item{..} `WithType` t) -> runDB $ do
13 FormFailure errors -> return errors 13 upsertBy (UniqueKind itemNormKind) (Kind itemNormKind t) [ KindType =. t ]
14 _ -> return [] 14 insert Item{..}
15 return ()
16 FormFailure errors -> mapM_ (addMessage "formError" . toHtml) errors
17 _ -> return ()
15 18
16 (sortOn entityVal -> stock) <- runDB $ selectList [] [] 19 (sortOn (entityVal . typedVal) -> stock) <- runDB $ mapM withType =<< selectList [] []
17 20
18 selectRep $ do 21 selectRep $ do
19 provideJson (stock :: [Entity Item]) 22 provideJson (stock :: [WithType (Entity Item)])
20 provideRep . defaultLayout $ inventoryListing InventoryState 23 provideRep . defaultLayout $ inventoryListing InventoryState
21 { invFormState = Just InsertForm{..} 24 { invFormState = Just InsertForm{..}
22 , .. 25 , ..
23 } 26 }
24 27
25putInventoryListingR :: Handler Value 28putInventoryListingR :: Handler Value
26putInventoryListingR = returnJson =<< runDB . insertEntity =<< (requireCheckJsonBody :: Handler Item) 29putInventoryListingR = do
30 (Item{..} `WithType` t) <- requireCheckJsonBody
31 returnJson <=< runDB $ do
32 upsertBy (UniqueKind itemNormKind) (Kind itemNormKind t) [ KindType =. t ]
33 withType =<< insertEntity Item{..}
diff --git a/Handler/Item.hs b/Handler/Item.hs
index 0f48261..abb1b12 100644
--- a/Handler/Item.hs
+++ b/Handler/Item.hs
@@ -4,26 +4,31 @@ import Import
4 4
5getItemR :: ItemId -> Handler TypedContent 5getItemR :: ItemId -> Handler TypedContent
6getItemR itemId = do 6getItemR itemId = do
7 entity <- runDB $ Entity itemId <$> get404 itemId 7 entity <- runDB $ withType =<< Entity itemId <$> get404 itemId
8 selectRep $ do 8 selectRep $ do
9 provideJson entity 9 provideJson entity
10 provideRep (redirect $ InventoryListingR :#: itemId :: Handler Html) 10 provideRep (redirect $ InventoryListingR :#: itemId :: Handler Html)
11 11
12putItemR :: ItemId -> Handler Value 12putItemR :: ItemId -> Handler Value
13putItemR itemId = do 13putItemR itemId = do
14 Item{..} <- requireCheckJsonBody 14 (Item{..} `WithType` t) <- requireCheckJsonBody
15 returnJson . Entity itemId =<< runDB 15 returnJson <=< runDB $ do
16 (updateGet itemId [ ItemKind =. itemKind 16 entity <- Entity itemId <$> (updateGet itemId [ ItemKind =. itemKind
17 , ItemNormKind =. itemNormKind 17 , ItemNormKind =. itemNormKind
18 , ItemBought =. itemBought 18 , ItemBought =. itemBought
19 , ItemExpires =. itemExpires 19 , ItemExpires =. itemExpires
20 , ItemOpened =. itemOpened 20 , ItemOpened =. itemOpened
21 ]) 21 ])
22 update (itemFkType $ entityVal entity) [ KindType =. t ]
23 withType entity
22 24
23patchItemR :: ItemId -> Handler Value 25patchItemR :: ItemId -> Handler Value
24patchItemR itemId = do 26patchItemR itemId = do
25 diffs <- (requireCheckJsonBody :: Handler ItemDiffs) 27 (itemUpdates, typeUpdates) <- toUpdate <$> (requireCheckJsonBody :: Handler ItemDiffs)
26 returnJson . Entity itemId =<< runDB (updateGet itemId $ toUpdate diffs) 28 returnJson <=< runDB $ do
29 entity <- Entity itemId <$> updateGet itemId itemUpdates
30 update (itemFkType $ entityVal entity) typeUpdates
31 withType entity
27 32
28deleteItemR :: ItemId -> Handler () 33deleteItemR :: ItemId -> Handler ()
29deleteItemR = runDB . delete 34deleteItemR = runDB . delete
diff --git a/Handler/List.hs b/Handler/List.hs
index cfd3f7c..70f323a 100644
--- a/Handler/List.hs
+++ b/Handler/List.hs
@@ -18,17 +18,18 @@ import Database.Persist.Sql (Single(..), rawSql)
18 18
19import Thermoprint.Client 19import Thermoprint.Client
20 20
21list :: Handler (Set Text) 21list :: Handler (Set (WithType Text))
22list = do 22list = do
23 (map unSingle -> kinds) <- runDB $ rawSql "select reference.kind from reference where not exists (select * from item where COALESCE(item.expires >= CURRENT_DATE, TRUE) and (item.norm_kind = reference.norm_kind)) " [] 23 (map (uncurry WithType . bimap unSingle unSingle) -> kinds) <- runDB $ rawSql "select reference.kind, kind.type from reference,kind where (not exists (select * from item where COALESCE(item.expires >= CURRENT_DATE, TRUE) and (item.norm_kind = reference.norm_kind))) and (reference.norm_kind = kind.norm_kind)" []
24 return $ Set.fromList kinds 24 return $ Set.fromList kinds
25 25
26mkPrintout :: Set Text -> Printout 26mkPrintout :: Set (WithType Text) -> Printout
27mkPrintout list = Printout 27mkPrintout list = Printout ps
28 [ Paragraph 28 where
29 [Cooked . either id Line . text . Lazy.Text.fromStrict . Text.unlines . map (" - " <>) $ Set.toAscList list 29 ps = Map.foldMapWithKey (fmap (pure . Paragraph . pure . Cooked) . toLines) $ group list
30 ] 30 group = Map.fromListWith (<>) . fmap (\(kind `WithType` t) -> (t, Set.singleton kind)) . Set.toAscList
31 ] 31 toLines t (Set.toAscList -> kinds) = either id Line . text . Lazy.Text.fromStrict . Text.unlines $
32 pure t ++ map (" - " <>) kinds
32 33
33getListR, postListR :: Handler TypedContent 34getListR, postListR :: Handler TypedContent
34getListR = postListR 35getListR = postListR
@@ -54,18 +55,21 @@ postListR = do
54 _ -> return () 55 _ -> return ()
55 56
56 selectRep $ do 57 selectRep $ do
57 provideJson list 58 provideJson $ typeToJSON "item" <$> Set.toAscList list
58 provideRep . defaultLayout $ 59 provideRep . defaultLayout $
59 [whamlet| 60 [whamlet|
60 <div .table .main> 61 <table .main>
61 <div .tr .sepBelow> 62 <tr .sepBelow>
62 <div .th>Item 63 <th>Item
63 $forall item <- Set.toAscList list 64 <th>Type
64 <div .tr .color> 65 $forall WithType item itemType <- Set.toAscList list
65 <div .kind>#{item} 66 <tr .color>
66 <form .tr .sepAbove method=post action=@{ListR} enctype=#{printEnc}> 67 <td .kind>#{item}
67 <div .td> 68 <td .type>#{itemType}
68 ^{printView} 69 <tr .sepAbove>
69 <button type=submit :Set.null list:disabled> 70 <td colspan=2>
70 Print 71 <form method=post action=@{ListR} enctype=#{printEnc}>
72 ^{printView}
73 <button type=submit :Set.null list:disabled>
74 Print
71 |] 75 |]
diff --git a/Handler/ReferenceItem.hs b/Handler/ReferenceItem.hs
index 738c9f3..44cd0a8 100644
--- a/Handler/ReferenceItem.hs
+++ b/Handler/ReferenceItem.hs
@@ -4,7 +4,7 @@ import Import
4 4
5getReferenceItemR :: ReferenceId -> Handler TypedContent 5getReferenceItemR :: ReferenceId -> Handler TypedContent
6getReferenceItemR referenceId = do 6getReferenceItemR referenceId = do
7 entity <- runDB $ Entity referenceId <$> get404 referenceId 7 entity <- runDB $ withType =<< Entity referenceId <$> get404 referenceId
8 selectRep $ do 8 selectRep $ do
9 provideJson entity 9 provideJson entity
10 provideRep (redirect $ ReferenceListingR :#: referenceId :: Handler Html) 10 provideRep (redirect $ ReferenceListingR :#: referenceId :: Handler Html)
@@ -12,14 +12,13 @@ getReferenceItemR referenceId = do
12 12
13putReferenceItemR :: ReferenceId -> Handler Value 13putReferenceItemR :: ReferenceId -> Handler Value
14putReferenceItemR referenceId = do 14putReferenceItemR referenceId = do
15 Reference{..} <- requireCheckJsonBody 15 (Reference{..} `WithType` t) <- requireCheckJsonBody
16 returnJson . Entity referenceId =<< runDB 16 returnJson <=< runDB $ do
17 (updateGet referenceId [ ReferenceKind =. referenceKind 17 entity <- Entity referenceId <$> updateGet referenceId [ ReferenceKind =. referenceKind
18 , ReferenceNormKind =. referenceNormKind 18 , ReferenceNormKind =. referenceNormKind
19 ]) 19 ]
20 20 update (referenceFkType $ entityVal entity) [ KindType =. t ]
21patchReferenceItemR :: ReferenceId -> Handler Value 21 withType entity
22patchReferenceItemR = putReferenceItemR -- Just one field
23 22
24deleteReferenceItemR :: ReferenceId -> Handler () 23deleteReferenceItemR :: ReferenceId -> Handler ()
25deleteReferenceItemR = runDB . delete 24deleteReferenceItemR = runDB . delete
diff --git a/Handler/ReferenceListing.hs b/Handler/ReferenceListing.hs
index 0f777ee..0b89a20 100644
--- a/Handler/ReferenceListing.hs
+++ b/Handler/ReferenceListing.hs
@@ -9,19 +9,26 @@ getReferenceListingR = postReferenceListingR
9postReferenceListingR = do 9postReferenceListingR = do
10 ((insertResult, fsInsertForm), fsInsertEncoding) <- runFormPost $ referenceForm Nothing 10 ((insertResult, fsInsertForm), fsInsertEncoding) <- runFormPost $ referenceForm Nothing
11 11
12 mapM_ (addMessage "formError" . toHtml) =<< case insertResult of 12 case insertResult of
13 FormSuccess newReference -> [] <$ runDB (insert newReference) 13 FormSuccess (Reference{..} `WithType` t) -> runDB $ do
14 FormFailure errors -> return errors 14 upsertBy (UniqueKind referenceNormKind) (Kind referenceNormKind t) [ KindType =. t ]
15 _ -> return [] 15 insert Reference{..}
16 return ()
17 FormFailure errors -> mapM_ (addMessage "formError" . toHtml) errors
18 _ -> return ()
16 19
17 reference <- runDB $ selectList [] [Asc ReferenceKind] 20 reference <- runDB $ mapM withType =<< selectList [] [Asc ReferenceKind]
18 21
19 selectRep $ do 22 selectRep $ do
20 provideJson (reference :: [Entity Reference]) 23 provideJson (reference :: [WithType (Entity Reference)])
21 provideRep . defaultLayout $ referenceListing ReferenceState 24 provideRep . defaultLayout $ referenceListing ReferenceState
22 { refFormState = Just InsertForm{..} 25 { refFormState = Just InsertForm{..}
23 , .. 26 , ..
24 } 27 }
25 28
26putReferenceListingR :: Handler Value 29putReferenceListingR :: Handler Value
27putReferenceListingR = returnJson =<< runDB . insertEntity =<< (requireCheckJsonBody :: Handler Reference) 30putReferenceListingR = do
31 (Reference{..} `WithType` referenceType) <- requireCheckJsonBody
32 returnJson <=< runDB $ do
33 upsertBy (UniqueKind referenceNormKind) (Kind referenceNormKind referenceType) [ KindType =. referenceType ]
34 withType =<< insertEntity Reference{..}
diff --git a/Handler/UpdateItem.hs b/Handler/UpdateItem.hs
index a4a29c2..befbe99 100644
--- a/Handler/UpdateItem.hs
+++ b/Handler/UpdateItem.hs
@@ -11,21 +11,24 @@ postUpdateItemR fsUpdateId = do
11 11
12 ((updateResult, fsUpdateForm), fsUpdateEncoding) <- runFormPost . itemForm . Just $ entityVal entity 12 ((updateResult, fsUpdateForm), fsUpdateEncoding) <- runFormPost . itemForm . Just $ entityVal entity
13 13
14 mapM_ (addMessage "formError" . toHtml) =<< case updateResult of 14 case updateResult of
15 FormSuccess Item{..} -> [] <$ runDB (update fsUpdateId [ ItemKind =. itemKind 15 FormSuccess (Item{..} `WithType` t) -> runDB $ do
16 , ItemNormKind =. normalizeKind itemKind 16 upsertBy (UniqueKind itemNormKind) (Kind itemNormKind t) [ KindType =. t ]
17 , ItemBought =. itemBought 17 update fsUpdateId [ ItemKind =. itemKind
18 , ItemExpires =. itemExpires 18 , ItemNormKind =. itemNormKind
19 , ItemOpened =. itemOpened 19 , ItemBought =. itemBought
20 ]) 20 , ItemExpires =. itemExpires
21 FormFailure errors -> return errors 21 , ItemOpened =. itemOpened
22 _ -> return [] 22 ]
23 return ()
24 FormFailure errors -> mapM_ (addMessage "formError" . toHtml) errors
25 _ -> return ()
23 26
24 selectRep $ do 27 selectRep $ do
25 provideRep $ case updateResult of 28 provideRep $ case updateResult of
26 FormSuccess _ -> redirect $ InventoryListingR :#: fsUpdateId :: Handler Html 29 FormSuccess _ -> redirect $ InventoryListingR :#: fsUpdateId :: Handler Html
27 _ -> do 30 _ -> do
28 (sortOn entityVal -> stock) <- runDB $ selectList [] [] 31 (sortOn (entityVal . typedVal) -> stock) <- runDB $ mapM withType =<< selectList [] []
29 defaultLayout $ inventoryListing InventoryState 32 defaultLayout $ inventoryListing InventoryState
30 { invFormState = Just UpdateForm{..} 33 { invFormState = Just UpdateForm{..}
31 , .. 34 , ..