From 24633871359ec9fbd63fdfebf79a6351b2792f77 Mon Sep 17 00:00:00 2001 From: Joris Date: Sun, 6 Sep 2015 00:05:50 +0200 Subject: Can add monthly payments, not visible at the moment though, just the count is printed --- src/client/Main.elm | 14 +++++++++++--- src/client/Model/Payment.elm | 22 ++++++---------------- src/client/Model/View/LoggedView.elm | 8 +++++--- src/client/Model/View/Payment/Add.elm | 6 +++--- src/client/ServerCommunication.elm | 9 +++++---- src/client/Update.elm | 6 +++--- src/client/Update/Payment.elm | 2 +- src/client/View/Payments.elm | 2 ++ src/client/View/Payments/Add.elm | 2 +- src/client/View/Payments/Monthly.elm | 24 ++++++++++++++++++++++++ src/client/View/Payments/Table.elm | 13 ++++++------- src/server/Controller/Payment.hs | 17 ++++++++++++----- src/server/Design/Global.hs | 7 +++++++ src/server/Main.hs | 7 ++++++- src/server/Model/Database.hs | 3 +++ src/server/Model/Frequency.hs | 19 +++++++++++++++++++ src/server/Model/Json/TotalPayment.hs | 1 - src/server/Model/Message/Key.hs | 2 ++ src/server/Model/Message/Translations.hs | 10 ++++++++++ src/server/Model/Payment.hs | 30 +++++++++++++++++++++++------- 20 files changed, 149 insertions(+), 55 deletions(-) create mode 100644 src/client/View/Payments/Monthly.elm create mode 100644 src/server/Model/Frequency.hs diff --git a/src/client/Main.elm b/src/client/Main.elm index 368d930..3174ba6 100644 --- a/src/client/Main.elm +++ b/src/client/Main.elm @@ -57,9 +57,14 @@ port initView = Just msg -> Signal.send actions.address (SignInError msg) Nothing -> - Task.map5 GoLoggedView getUsers whoAmI getPayments getPaymentsCount getPayers - |> flip Task.andThen (Signal.send actions.address) - |> flip Task.onError (\_ -> Signal.send actions.address GoSignInView) + Task.andThen getUsers <| \users -> + Task.andThen whoAmI <| \me -> + Task.andThen getMonthlyPayments <| \monthlyPayments -> + Task.andThen getPayments <| \payments -> + Task.andThen getPaymentsCount <| \paymentsCount -> + Task.andThen getPayers <| \payers -> + Signal.send actions.address (GoLoggedView users me monthlyPayments payments paymentsCount payers) + |> flip Task.onError (\_ -> Signal.send actions.address GoSignInView) getUsers : Task Http.Error Users getUsers = Http.get usersDecoder "/users" @@ -67,6 +72,9 @@ getUsers = Http.get usersDecoder "/users" whoAmI : Task Http.Error UserId whoAmI = Http.get ("id" := userIdDecoder) "/whoAmI" +getMonthlyPayments : Task Http.Error Payments +getMonthlyPayments = Http.get paymentsDecoder "/monthlyPayments" + getPayments : Task Http.Error Payments getPayments = Http.get paymentsDecoder ("/payments?page=1&perPage=" ++ toString perPage) diff --git a/src/client/Model/Payment.elm b/src/client/Model/Payment.elm index 4ae50de..313c6be 100644 --- a/src/client/Model/Payment.elm +++ b/src/client/Model/Payment.elm @@ -3,26 +3,22 @@ module Model.Payment , Payments , Payment , PaymentId - , PaymentWithId , paymentsDecoder - , removePayment ) where import Date exposing (..) import Json.Decode as Json exposing ((:=)) -import Dict exposing (..) import Model.User exposing (UserId, userIdDecoder) perPage : Int perPage = 8 -type alias Payments = Dict PaymentId Payment - -type alias PaymentWithId = (PaymentId, Payment) +type alias Payments = List Payment type alias Payment = - { creation : Date + { id : PaymentId + , creation : Date , name : String , cost : Int , userId : UserId @@ -31,15 +27,12 @@ type alias Payment = type alias PaymentId = Int paymentsDecoder : Json.Decoder Payments -paymentsDecoder = Json.map Dict.fromList (Json.list paymentWithIdDecoder) - -paymentWithIdDecoder : Json.Decoder (PaymentId, Payment) -paymentWithIdDecoder = - paymentDecoder `Json.andThen` (\payment -> Json.map (\id -> (id, payment)) ("id" := paymentIdDecoder)) +paymentsDecoder = Json.list paymentDecoder paymentDecoder : Json.Decoder Payment paymentDecoder = - Json.object4 Payment + Json.object5 Payment + ("id" := paymentIdDecoder) ("creation" := dateDecoder) ("name" := Json.string) ("cost" := Json.int) @@ -50,6 +43,3 @@ paymentIdDecoder = Json.int dateDecoder : Json.Decoder Date dateDecoder = Json.customDecoder Json.string Date.fromString - -removePayment : Payments -> PaymentId -> Payments -removePayment payments paymentId = Dict.remove paymentId payments diff --git a/src/client/Model/View/LoggedView.elm b/src/client/Model/View/LoggedView.elm index 35fd9e5..34a55a2 100644 --- a/src/client/Model/View/LoggedView.elm +++ b/src/client/Model/View/LoggedView.elm @@ -13,6 +13,7 @@ type alias LoggedView = { users : Users , me : UserId , add : AddPayment + , monthlyPayments : Payments , payments : Payments , paymentsCount : Int , payers : Payers @@ -20,11 +21,12 @@ type alias LoggedView = , currentPage : Int } -initLoggedView : Users -> UserId -> Payments -> Int -> Payers -> LoggedView -initLoggedView users me payments paymentsCount payers = +initLoggedView : Users -> UserId -> Payments -> Payments -> Int -> Payers -> LoggedView +initLoggedView users me monthlyPayments payments paymentsCount payers = { users = users , me = me - , add = initAddPayment + , add = initAddPayment Punctual + , monthlyPayments = monthlyPayments , payments = payments , paymentsCount = paymentsCount , payers = payers diff --git a/src/client/Model/View/Payment/Add.elm b/src/client/Model/View/Payment/Add.elm index da5d0f2..dc00e86 100644 --- a/src/client/Model/View/Payment/Add.elm +++ b/src/client/Model/View/Payment/Add.elm @@ -20,13 +20,13 @@ type alias AddPayment = , frequency : Frequency } -initAddPayment : AddPayment -initAddPayment = +initAddPayment : Frequency -> AddPayment +initAddPayment frequency = { name = "" , nameError = Nothing , cost = "" , costError = Nothing - , frequency = Punctual + , frequency = frequency } validateName : String -> Translations -> Result String String diff --git a/src/client/ServerCommunication.elm b/src/client/ServerCommunication.elm index c38805b..1f35fa1 100644 --- a/src/client/ServerCommunication.elm +++ b/src/client/ServerCommunication.elm @@ -13,6 +13,7 @@ import Date import Model.Message exposing (messageDecoder) import Model.User exposing (UserId) import Model.Payment exposing (PaymentId, perPage, paymentsDecoder) +import Model.View.Payment.Add exposing (Frequency) import Update as U import Update.SignIn exposing (..) @@ -21,7 +22,7 @@ import Update.Payment as UP type Communication = NoCommunication | SignIn String - | AddPayment UserId String Int + | AddPayment UserId String Int Frequency | DeletePayment PaymentId UserId Int Int | UpdatePage Int | SignOut @@ -45,8 +46,8 @@ getRequest communication = Nothing SignIn login -> Just (simple "post" ("/signIn?login=" ++ login)) - AddPayment userId paymentName cost -> - Just (simple "post" ("/payment/add?name=" ++ paymentName ++ "&cost=" ++ (toString cost))) + AddPayment userId paymentName cost frequency -> + Just (simple "post" ("/payment/add?name=" ++ paymentName ++ "&cost=" ++ (toString cost) ++ "&frequency=" ++ (toString frequency))) DeletePayment paymentId _ _ _ -> Just (simple "post" ("payment/delete?id=" ++ (toString paymentId))) UpdatePage page -> @@ -75,7 +76,7 @@ serverResult communication response = Task.succeed U.NoOp SignIn login -> Task.succeed (U.UpdateSignIn (ValidLogin login)) - AddPayment userId paymentName cost -> + AddPayment userId paymentName cost frequency -> Http.send Http.defaultSettings (updatePageRequest 1) |> Task.map (\response -> if response.status == 200 diff --git a/src/client/Update.elm b/src/client/Update.elm index d643d4e..08547e3 100644 --- a/src/client/Update.elm +++ b/src/client/Update.elm @@ -23,7 +23,7 @@ type Action = | GoSignInView | SignInError String | UpdateSignIn SignInAction - | GoLoggedView Users UserId Payments Int Payers + | GoLoggedView Users UserId Payments Payments Int Payers | UpdatePayment PaymentAction actions : Signal.Mailbox Action @@ -38,8 +38,8 @@ updateModel action model = { model | currentTime <- time } GoSignInView -> { model | view <- V.SignInView initSignInView } - GoLoggedView users me payments paymentsCount payers -> - { model | view <- V.LoggedView (initLoggedView users me payments paymentsCount payers) } + GoLoggedView users me monthlyPayments payments paymentsCount payers -> + { model | view <- V.LoggedView (initLoggedView users me monthlyPayments payments paymentsCount payers) } SignInError msg -> let signInView = { initSignInView | result <- Just (Err msg) } in { model | view <- V.SignInView signInView } diff --git a/src/client/Update/Payment.elm b/src/client/Update/Payment.elm index f063b4c..2cae679 100644 --- a/src/client/Update/Payment.elm +++ b/src/client/Update/Payment.elm @@ -34,7 +34,7 @@ updatePayment model action loggedView = { loggedView | payments <- payments , currentPage <- 1 - , add <- initAddPayment + , add <- initAddPayment loggedView.add.frequency , payers <- updatePayers loggedView.payers userId cost , paymentsCount <- loggedView.paymentsCount + 1 } diff --git a/src/client/View/Payments.elm b/src/client/View/Payments.elm index b51c9a0..ac19df7 100644 --- a/src/client/View/Payments.elm +++ b/src/client/View/Payments.elm @@ -11,6 +11,7 @@ import Model.View.LoggedView exposing (LoggedView) import View.Payments.ExceedingPayer exposing (exceedingPayers) import View.Payments.Add exposing (addPayment) +import View.Payments.Monthly exposing (monthlyPayments) import View.Payments.Table exposing (paymentsTable) import View.Payments.Paging exposing (paymentsPaging) @@ -20,6 +21,7 @@ renderPayments model loggedView = [ class "payments" ] [ exceedingPayers model loggedView , addPayment model loggedView + , monthlyPayments model loggedView , paymentsTable model loggedView , paymentsPaging loggedView ] diff --git a/src/client/View/Payments/Add.elm b/src/client/View/Payments/Add.elm index 085b16d..a22c1f1 100644 --- a/src/client/View/Payments/Add.elm +++ b/src/client/View/Payments/Add.elm @@ -31,7 +31,7 @@ addPayment model loggedView = [ class "add" , case (validateName loggedView.add.name model.translations, validateCost loggedView.add.cost model.translations) of (Ok name, Ok cost) -> - onSubmitPrevDefault serverCommunications.address (SC.AddPayment loggedView.me name cost) + onSubmitPrevDefault serverCommunications.address (SC.AddPayment loggedView.me name cost loggedView.add.frequency) (resName, resCost) -> onSubmitPrevDefault actions.address (UpdatePayment <| UpdateAdd <| AddError (toMaybeError resName) (toMaybeError resCost)) ] diff --git a/src/client/View/Payments/Monthly.elm b/src/client/View/Payments/Monthly.elm new file mode 100644 index 0000000..366af92 --- /dev/null +++ b/src/client/View/Payments/Monthly.elm @@ -0,0 +1,24 @@ +module View.Payments.Monthly + ( monthlyPayments + ) where + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) + +import Model exposing (Model) +import Model.Payment exposing (Payments) +import Model.View.LoggedView exposing (LoggedView) +import Model.Translations exposing (getVarMessage) + +monthlyPayments : Model -> LoggedView -> Html +monthlyPayments model loggedView = + div + [ class "monthlyPayments" ] + [ monthlyCount model loggedView.monthlyPayments ] + +monthlyCount : Model -> Payments -> Html +monthlyCount model monthlyPayments = + let count = List.length monthlyPayments + key = if count > 1 then "PluralMonthlyCount" else "SingularMonthlyCount" + in text (getVarMessage [toString count] key model.translations) diff --git a/src/client/View/Payments/Table.elm b/src/client/View/Payments/Table.elm index 5374c44..4a1ed50 100644 --- a/src/client/View/Payments/Table.elm +++ b/src/client/View/Payments/Table.elm @@ -43,16 +43,15 @@ paymentsTable model loggedView = paymentLines : Model -> LoggedView -> List Html paymentLines model loggedView = loggedView.payments - |> Dict.toList - |> List.sortBy (\(_, payment) -> Date.toTime payment.creation) + |> List.sortBy (Date.toTime << .creation) |> List.reverse |> List.map (paymentLine model loggedView) -paymentLine : Model -> LoggedView -> PaymentWithId -> Html -paymentLine model loggedView (id, payment) = +paymentLine : Model -> LoggedView -> Payment -> Html +paymentLine model loggedView payment = a - [ class ("row " ++ (if loggedView.edition == Just id then "edition" else "")) - , onClick actions.address (UpdatePayment (ToggleEdit id)) + [ class ("row " ++ (if loggedView.edition == Just payment.id then "edition" else "")) + , onClick actions.address (UpdatePayment (ToggleEdit payment.id)) ] [ div [ class "cell category" ] [ text payment.name ] , div [ class "cell cost" ] [ text ((toString payment.cost) ++ " " ++ (getMessage "MoneySymbol" model.translations)) ] @@ -76,7 +75,7 @@ paymentLine model loggedView (id, payment) = then div [ class "cell remove" - , onClick serverCommunications.address (SC.DeletePayment id payment.userId payment.cost loggedView.currentPage) + , onClick serverCommunications.address (SC.DeletePayment payment.id payment.userId payment.cost loggedView.currentPage) ] [ renderIcon "times" ] else diff --git a/src/server/Controller/Payment.hs b/src/server/Controller/Payment.hs index 0a40771..7944ecd 100644 --- a/src/server/Controller/Payment.hs +++ b/src/server/Controller/Payment.hs @@ -1,5 +1,6 @@ module Controller.Payment ( getPaymentsAction + , getMonthlyPaymentsAction , createPaymentAction , deletePaymentAction , getTotalPaymentsAction @@ -20,6 +21,7 @@ import qualified Secure import Model.Database import Model.Payment +import Model.Frequency import Model.Json.Message import Model.Json.Number import Model.Message @@ -28,14 +30,19 @@ import Model.Message.Key (Key(PaymentNotDeleted)) getPaymentsAction :: Int -> Int -> ActionM () getPaymentsAction page perPage = Secure.loggedAction (\_ -> do - payments <- liftIO $ runDb (getPayments page perPage) - json payments + (liftIO $ runDb (getPunctualPayments page perPage)) >>= json ) -createPaymentAction :: Text -> Int -> ActionM () -createPaymentAction name cost = +getMonthlyPaymentsAction :: ActionM () +getMonthlyPaymentsAction = Secure.loggedAction (\user -> do - _ <- liftIO . runDb $ createPayment (entityKey user) name cost + (liftIO $ runDb (getMonthlyPayments (entityKey user))) >>= json + ) + +createPaymentAction :: Text -> Int -> Frequency -> ActionM () +createPaymentAction name cost frequency = + Secure.loggedAction (\user -> do + _ <- liftIO . runDb $ createPayment (entityKey user) name cost frequency status ok200 ) diff --git a/src/server/Design/Global.hs b/src/server/Design/Global.hs index e0cc244..0af071e 100644 --- a/src/server/Design/Global.hs +++ b/src/server/Design/Global.hs @@ -140,6 +140,13 @@ global = do top (px (inputHeight + 10)) left (px 0) + ".monthlyPayments" ? do + width (pct 95) + margin (px 0) auto (px 45) auto + padding (px 10) (px 10) (px 10) (px 10) + backgroundColor C.lightGrey + borderRadius radius radius radius radius + ".table" ? do display D.table width (pct 100) diff --git a/src/server/Main.hs b/src/server/Main.hs index 27908b4..c3d285e 100644 --- a/src/server/Main.hs +++ b/src/server/Main.hs @@ -13,6 +13,7 @@ import Controller.Payment import Controller.User import Model.Database (runMigrations) +import Model.Frequency import Config @@ -54,10 +55,14 @@ main = do perPage <- param "perPage" :: ActionM Int getPaymentsAction page perPage + get "/monthlyPayments" $ do + getMonthlyPaymentsAction + post "/payment/add" $ do name <- param "name" :: ActionM Text cost <- param "cost" :: ActionM Int - createPaymentAction name cost + frequency <- param "frequency" :: ActionM Frequency + createPaymentAction name cost frequency post "/payment/delete" $ do paymentId <- param "id" :: ActionM Text diff --git a/src/server/Model/Database.hs b/src/server/Model/Database.hs index a6ce4f4..d4a7d50 100644 --- a/src/server/Model/Database.hs +++ b/src/server/Model/Database.hs @@ -21,6 +21,8 @@ import Data.Int (Int64) import Database.Persist.Sqlite import Database.Persist.TH +import Model.Frequency + share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| User creation UTCTime @@ -35,6 +37,7 @@ Payment name Text cost Int deletedAt UTCTime Maybe + frequency Frequency deriving Show SignIn token Text diff --git a/src/server/Model/Frequency.hs b/src/server/Model/Frequency.hs new file mode 100644 index 0000000..2b747b7 --- /dev/null +++ b/src/server/Model/Frequency.hs @@ -0,0 +1,19 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} + +module Model.Frequency + ( Frequency(..) + ) where + +import Web.Scotty + +import Database.Persist.TH + +data Frequency = + Punctual + | Monthly + deriving (Eq, Show, Read) + +derivePersistField "Frequency" + +instance Parsable Frequency where parseParam = readEither diff --git a/src/server/Model/Json/TotalPayment.hs b/src/server/Model/Json/TotalPayment.hs index 5ae68c9..2b1cd06 100644 --- a/src/server/Model/Json/TotalPayment.hs +++ b/src/server/Model/Json/TotalPayment.hs @@ -6,7 +6,6 @@ module Model.Json.TotalPayment import GHC.Generics -import Data.Text (Text) import Data.Aeson import Model.Database (UserId) diff --git a/src/server/Model/Message/Key.hs b/src/server/Model/Message/Key.hs index 8a37a2a..163a21f 100644 --- a/src/server/Model/Message/Key.hs +++ b/src/server/Model/Message/Key.hs @@ -54,5 +54,7 @@ data Key = | MoneySymbol | Punctual | Monthly + | SingularMonthlyCount + | PluralMonthlyCount deriving (Enum, Bounded, Show) diff --git a/src/server/Model/Message/Translations.hs b/src/server/Model/Message/Translations.hs index ec8cf19..501f00f 100644 --- a/src/server/Model/Message/Translations.hs +++ b/src/server/Model/Message/Translations.hs @@ -209,3 +209,13 @@ m l Monthly = case l of English -> "Monthly" French -> "Mensuel" + +m l SingularMonthlyCount = + case l of + English -> "You have {1} monthly payment." + French -> "Vous avez {1} paiement mensuel." + +m l PluralMonthlyCount = + case l of + English -> "You have {1} monthly payments." + French -> "Vous avez {1} paiements mensuels." diff --git a/src/server/Model/Payment.hs b/src/server/Model/Payment.hs index d7632f0..381578a 100644 --- a/src/server/Model/Payment.hs +++ b/src/server/Model/Payment.hs @@ -1,5 +1,6 @@ module Model.Payment - ( getPayments + ( getPunctualPayments + , getMonthlyPayments , createPayment , deleteOwnPayment , getTotalPayments @@ -18,25 +19,38 @@ import Database.Esqueleto import qualified Database.Esqueleto as E import Model.Database +import Model.Frequency import qualified Model.Json.Payment as P import qualified Model.Json.TotalPayment as TP -getPayments :: Int -> Int -> Persist [P.Payment] -getPayments page perPage = do +getPunctualPayments :: Int -> Int -> Persist [P.Payment] +getPunctualPayments page perPage = do xs <- select $ from $ \(payment `InnerJoin` user) -> do on (payment ^. PaymentUserId E.==. user ^. UserId) where_ (isNothing (payment ^. PaymentDeletedAt)) + where_ (payment ^. PaymentFrequency E.==. val Punctual) orderBy [desc (payment ^. PaymentCreation)] limit . fromIntegral $ perPage offset . fromIntegral $ (page - 1) * perPage return (payment, user) return (map getJsonPayment xs) +getMonthlyPayments :: UserId -> Persist [P.Payment] +getMonthlyPayments userId = do + xs <- select $ + from $ \(payment `InnerJoin` user) -> do + on (payment ^. PaymentUserId E.==. user ^. UserId) + where_ (isNothing (payment ^. PaymentDeletedAt)) + where_ (payment ^. PaymentFrequency E.==. val Monthly) + where_ (payment ^. PaymentUserId E.==. val userId) + orderBy [desc (payment ^. PaymentCreation)] + return (payment, user) + return (map getJsonPayment xs) + getJsonPayment :: (Entity Payment, Entity User) -> P.Payment getJsonPayment (paymentEntity, userEntity) = let payment = entityVal paymentEntity - user = entityVal userEntity in P.Payment { P.id = entityKey paymentEntity , P.creation = paymentCreation payment @@ -45,10 +59,10 @@ getJsonPayment (paymentEntity, userEntity) = , P.userId = entityKey userEntity } -createPayment :: UserId -> Text -> Int -> Persist PaymentId -createPayment userId name cost = do +createPayment :: UserId -> Text -> Int -> Frequency -> Persist PaymentId +createPayment userId name cost frequency = do now <- liftIO getCurrentTime - insert $ Payment userId now name cost Nothing + insert $ Payment userId now name cost Nothing frequency deleteOwnPayment :: Entity User -> PaymentId -> Persist Bool deleteOwnPayment user paymentId = do @@ -71,6 +85,7 @@ getTotalPayments = do from $ \(payment `InnerJoin` user) -> do on (payment ^. PaymentUserId E.==. user ^. UserId) where_ (isNothing (payment ^. PaymentDeletedAt)) + where_ (payment ^. PaymentFrequency E.==. val Punctual) groupBy (payment ^. PaymentUserId) return (user ^. UserId, sum_ (payment ^. PaymentCost)) return $ catMaybes . map (getTotalPayment . unValueTuple) $ values @@ -88,4 +103,5 @@ getPaymentsCount = (select $ from $ \payment -> do where_ (isNothing (payment ^. PaymentDeletedAt)) + where_ (payment ^. PaymentFrequency E.==. val Punctual) return countRows) :: Persist Int -- cgit v1.2.3