Add billable list (in-progress)

[?]
Feb 6, 2021, 6:42 PM
KET5QGQPM5STWGRDL72HTZ5T57QRKQQ3L564PST2PNG4YJHKATSAC

Dependencies

  • [2] 3PFXXJTL WIP
  • [3] ANDJ6GEY Add billing component skeleton.
  • [4] YBLHJFCN Implement billing modal.
  • [5] NAFJ6RB3 Minor module reorg.
  • [6] 4GOBY5NQ WIP on modals.
  • [7] 3HTCTHHU Add halogen-portal dependency and update argonaut.
  • [8] VTZT2ILU Wire up billing navigation.
  • [9] T2DN23M7 Factor out billing create component.
  • [10] N6FG4EW6 Working bootstrap modal! Only a little FFI.
  • [11] 27H4DECZ Add billing create API call.
  • [*] RFYEVKZQ Add nix-shell based build environment.
  • [*] DJATFGIC Support client builds in nix-shell --pure.

Change contents

  • replacement in client/src/Aftok/Api/Billing.purs at line 4
    [3.49][3.5:119]()
    import Control.Monad.Error.Class (throwError)
    import Control.Monad.Except.Trans (runExceptT, except, withExceptT)
    [3.49]
    [3.105]
    import Control.Alternative ((<|>))
    -- import Control.Monad.Error.Class (throwError)
    -- import Control.Monad.Except.Trans (runExceptT, except, withExceptT)
  • replacement in client/src/Aftok/Api/Billing.purs at line 10
    [3.153][3.21:107]()
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, JsonDecodeError(..), (.:))
    [3.153]
    [3.212]
    import Data.Argonaut.Decode (class DecodeJson, decodeJson, JsonDecodeError(..), (.:), (.:?))
  • replacement in client/src/Aftok/Api/Billing.purs at line 12
    [3.253][3.253:283]()
    import Data.BigInt (toNumber)
    [3.253]
    [3.335]
    import Data.BigInt (toNumber, fromNumber) as BigInt
  • edit in client/src/Aftok/Api/Billing.purs at line 16
    [3.459]
    [3.459]
    import Foreign.Object (Object)
  • replacement in client/src/Aftok/Api/Billing.purs at line 24
    [2.53][3.315:341](),[3.142][3.315:341](),[3.336][3.315:341](),[3.755][3.315:341](),[3.341][3.785:842](),[3.785][3.785:842]()
    import Data.Tuple (Tuple)
    -- import Data.Traversable (class Traversable, traverse)
    [2.53]
    [3.842]
    import Data.Tuple (Tuple(..))
    import Data.Traversable (traverse)
  • replacement in client/src/Aftok/Api/Billing.purs at line 31
    [3.990][3.342:375]()
    import Affjax (post, printError)
    [3.990]
    [3.375]
    import Affjax (post, get)
  • replacement in client/src/Aftok/Api/Billing.purs at line 34
    [3.442][3.442:484]()
    import Affjax.StatusCode (StatusCode(..))
    [3.442]
    [3.1051]
    -- import Affjax.StatusCode (StatusCode(..))
  • replacement in client/src/Aftok/Api/Billing.purs at line 38
    [3.312][3.312:326]()
    ( Zatoshi )
    [3.312]
    [3.1086]
    ( Zatoshi(..) )
  • replacement in client/src/Aftok/Api/Billing.purs at line 41
    [3.1126][3.1126:1244]()
    -- import Aftok.Api.Json
    -- ( Decode
    -- , decompose
    -- , parseDatedResponse
    -- , parseDatedResponseMay
    -- )
    [3.1126]
    [3.1244]
    import Aftok.Api.Json
    ( parseResponse
    )
  • replacement in client/src/Aftok/Api/Billing.purs at line 94
    [3.984][3.984:1023]()
    , amount: toNumber (unwrap b.amount)
    [3.984]
    [2.319]
    , amount: BigInt.toNumber (unwrap b.amount)
  • replacement in client/src/Aftok/Api/Billing.purs at line 101
    [3.1028][2.464:974]()
    -- parseBillableJSON :: Object Json -> Either JsonDecodeError (Tuple BillableId Billable)
    -- parseBillableJSON obj = do
    -- billableId <- parseBillableIdJSON =<< obj .: "billableId"
    -- bobj <- obj .: "billable"
    -- amount <-
    -- Zatoshi <$> (note (TypeMismatch "Failed to decode as Zatoshi") <<< BigInt.fromNumber)
    -- =<< (_ .: "zatoshi")
    -- =<< (bobj .: "amount")
    -- gracePeriod <- Hours <$> bobj .: "gracePeriod"
    -- recurrence <- parseRecurrence =<< bobj .: "recurrence"
    [3.1028]
    [3.1710]
    parseRecurrence :: Json -> Either JsonDecodeError Recurrence
    parseRecurrence json = do
    obj <- decodeJson json
    let annually = traverse (map \(_ :: Unit) -> Annually) (obj .:? "annually")
    monthly = traverse (map Monthly) (obj .:? "monthly")
    weekly = traverse (map Weekly) (obj .:? "weekly")
    onetime = traverse (map \(_ :: Unit) -> OneTime) (obj .:? "onetime")
    join $ note (UnexpectedValue json) (annually <|> monthly <|> weekly <|> onetime)
  • edit in client/src/Aftok/Api/Billing.purs at line 112
    [2.976]
    [2.976]
    parseBillableJSON :: Object Json -> Either JsonDecodeError (Tuple BillableId Billable)
    parseBillableJSON obj = do
    billableId <- parseBillableIdJSON =<< obj .: "billableId"
    bobj <- obj .: "billable"
    name :: String <- bobj .: "name"
    description :: String <- bobj .: "description"
    let message = ""
    recurrence :: Recurrence <- parseRecurrence =<< bobj .: "recurrence"
    amount <-
    map Zatoshi
    $ (note (TypeMismatch "Failed to decode as Zatoshi") <<< BigInt.fromNumber)
    =<< (_ .: "zatoshi")
    =<< (bobj .: "amount")
    gracePeriod <- Days <$> bobj .: "gracePeriod"
    expiryPeriod <- Hours <$> bobj .: "gracePeriod"
    pure $ Tuple billableId {name, description, message, recurrence, amount, gracePeriod, expiryPeriod }
  • edit in client/src/Aftok/Api/Billing.purs at line 129
    [2.977]
    [3.1711]
  • replacement in client/src/Aftok/Api/Billing.purs at line 154
    [3.1195][3.1195:1387](),[3.1387][3.882:962](),[3.962][3.1476:1556](),[3.1476][3.1476:1556]()
    runExceptT $ case response of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError $ Forbidden
    StatusCode 200 -> withExceptT ParseFailure <<< except $ decodeJson r.body
    other -> throwError $ Error { status: Just other, message: r.statusText }
    [3.1195]
    [3.2396]
    parseResponse decodeJson response
  • replacement in client/src/Aftok/Api/Billing.purs at line 157
    [3.2492][3.2492:2541]()
    listProjectBillables pid = pure $ Left Forbidden
    [3.2492]
    [3.2541]
    listProjectBillables pid = do
    response <- get RF.json ("/api/projects/" <> pidStr pid <> "/billables")
    parseResponse (traverse parseBillableJSON <=< decodeJson) response
  • replacement in client/src/Aftok/Api/Json.purs at line 5
    [3.113][3.113:178]()
    import Control.Monad.Except.Trans (ExceptT, except, withExceptT)
    [3.113]
    [3.178]
    import Control.Monad.Except.Trans (ExceptT, except, withExceptT, runExceptT)
  • edit in client/src/Aftok/Api/Json.purs at line 19
    [3.718]
    [3.718]
    import Effect.Aff (Aff)
  • edit in client/src/Aftok/Api/Json.purs at line 62
    [3.1194]
    [3.2130]
    parseResponse ::
    forall a.
    Decode a ->
    Either AJAX.Error (Response Json) ->
    Aff (Either APIError a)
    parseResponse decode response =
    runExceptT $ case response of
    Left err -> throwError $ Error { status: Nothing, message: printError err }
    Right r -> case r.status of
    StatusCode 403 -> throwError $ Forbidden
    StatusCode 200 -> withExceptT ParseFailure <<< except $ decode r.body
    other -> throwError $ Error { status: Just other, message: r.statusText }
  • replacement in client/src/Aftok/Billing.purs at line 9
    [3.2980][3.2980:3011]()
    -- import Data.Unfoldable as U
    [3.2980]
    [3.3011]
    import Data.Unfoldable as U
  • replacement in client/src/Aftok/Billing.purs at line 14
    [3.3107][3.3107:3133]()
    import Data.Tuple (Tuple)
    [3.3107]
    [3.3133]
    import Data.Tuple (Tuple(..))
  • edit in client/src/Aftok/Billing.purs at line 37
    [3.2666]
    [3.3674]
    import Aftok.Zcash (toZEC, zecString)
  • replacement in client/src/Aftok/Billing.purs at line 122
    [3.4825][3.2710:2784]()
    [ Modals.modalButton "createBillable" "Create billable"
    [3.4825]
    [3.2223]
    [ renderBillableList st.billables
    , Modals.modalButton "createBillable" "Create billable"
  • edit in client/src/Aftok/Billing.purs at line 137
    [3.6183]
    [3.6300]
    renderBillableList :: Array (Tuple BillableId Billable) -> H.ComponentHTML BillingAction Slots m
    renderBillableList billables =
    HH.div
    [ P.classes (ClassName <$> [ "container-fluid" ]) ]
    [ HH.section
    [ P.id_ "projectOverview", P.classes (ClassName <$> [ "pt-3" ]) ]
    ([ HH.div
    -- header
    [ P.classes (ClassName <$> [ "row", "pt-3", "font-weight-bold" ]) ]
    [ colmd2 (Just "Billable Name")
    , colmd2 (Just "Description")
    , colmd2 (Just "Amount")
    , colmd2 (Just "Recurrence")
    , colmd2 Nothing
    ]
    ] <> (billableRow <$> billables))
    ]
    where
    billableRow (Tuple bid b) =
    HH.div
    [ P.classes (ClassName <$> [ "row", "pt-3" ]) ]
    [ colmd2 (Just b.name)
    , colmd2 (Just b.description)
    , colmd2 (Just (zecString <<< toZEC $ b.amount))
    ]
    colmd2 :: forall i w. Maybe String -> HH.HTML i w
    colmd2 xs = HH.div [ P.classes (ClassName <$> [ "col-md-2" ]) ] (U.fromMaybe $ HH.text <$> xs)
  • replacement in client/src/Aftok/Billing.purs at line 173
    [3.6510][3.6510:6796]()
    billables <- lift $ traverse (caps.listProjectBillables <<< (_.projectId) <<< unwrap) currentProject
    case billables of
    Nothing -> pure unit
    Just (Left err) -> lift $ system.error (show err)
    Just (Right b) -> H.modify_ (_ { billables = b })
    [3.6510]
    [3.6796]
    refreshBillables currentProject
  • edit in client/src/Aftok/Billing.purs at line 177
    [3.6880]
    [3.6880]
    refreshBillables currentProject
  • replacement in client/src/Aftok/Billing.purs at line 182
    [3.7093][3.5165:5210]()
    BillableCreated _ ->
    pure unit
    [3.7093]
    [3.7093]
    BillableCreated _ -> do
    currentProject <- H.gets (_.selectedProject)
    refreshBillables currentProject
    where
    refreshBillables currentProject = do
    billables <- lift $ traverse (caps.listProjectBillables <<< (_.projectId) <<< unwrap) currentProject
    case billables of
    Nothing -> pure unit
    Just (Left err) -> lift $ system.error (show err)
    Just (Right b) -> H.modify_ (_ { billables = b })
  • replacement in client/src/Aftok/Zcash.purs at line 7
    [3.19516][3.19516:19584]()
    import Data.Fixed (Fixed, P1000000, TenTimes, fromInt, numerator)
    [3.19516]
    [3.19584]
    import Data.Fixed (Fixed, P1000000, TenTimes, fromInt, numerator, fromBigInt)
  • edit in client/src/Aftok/Zcash.purs at line 20
    [3.19699]
    [3.1875]
    toZEC :: Zatoshi -> ZEC
    toZEC (Zatoshi z) =
    ZEC $ fromBigInt z / fromInt 100000000
  • edit in shell.nix at line 23
    [14.38]
    [13.99129]
    haskellPackages.HDBC-postgresql
    haskellPackages.dbmigrations-postgresql