Add billing component skeleton.
[?]
Feb 1, 2021, 4:37 AM
ANDJ6GEY2IRDNKPVXESYEZKU24BAXFB5PPSZFIJRMBGL57A622FQCDependencies
- [2]
RV7ZIULZUpdate overview to have access to the real project detail capability. - [3]
NAFJ6RB3Minor module reorg. - [4]
Z5KNL332Add skeleton of project overview HTML. - [5]
PPW6ROC5Render project data - [6]
GLQSD33YUse mock capability for overview init. - [7]
B4MTB6UOPersist project across pages. - [8]
QH4UB73NFormat with purty. - [9]
U7YAT2ZKAdd error reporting to signup form. - [10]
QAC2QJ32Add project overview page to client. - [11]
7TQPQW3NBegin adding parsing for project detail. - [*]
RSF6UAJKBreak out api module for timeline. - [*]
EA5BFM5GSplit Login component into its own module.
Change contents
- file addition: Billing.purs[13.1]
module Aftok.Api.Billing whereimport Preludeimport Control.Monad.Except.Trans (ExceptT, runExceptT)-- import Control.Monad.Except.Trans (ExceptT, runExceptT, except, withExceptT)-- import Control.Monad.Error.Class (throwError)-- import Data.Argonaut.Core (Json)import Data.Argonaut.Decode (class DecodeJson, decodeJson, (.:))import Data.DateTime (DateTime)-- import Data.DateTime.Instant (Instant, toDateTime)import Data.Either (Either(..), note)-- import Data.Foldable (class Foldable, foldr, foldl, foldMapDefaultR)-- import Data.Functor.Compose (Compose(..))-- import Data.Map as Mimport Data.Maybe (Maybe)import Data.Newtype (class Newtype, unwrap)-- import Data.Ratio (Ratio, (%))-- import Data.Time.Duration (Hours(..), Days(..))import Data.Tuple (Tuple(..))-- import Data.Traversable (class Traversable, traverse)import Data.UUID (UUID, parseUUID)-- import Effect (Effect)import Effect.Aff (Aff)-- import Effect.Class as EC-- import Foreign.Object (Object)-- import Affjax (get)-- import Affjax.ResponseFormat as RFimport Aftok.Types( ProjectId )import Aftok.Api.Types(APIError(..))-- import Aftok.Api.Json-- ( Decode-- , decompose-- , parseDatedResponse-- , parseDatedResponseMay-- )newtype BillableId= BillableId UUIDderive instance billableIdEq :: Eq BillableIdderive instance billableIdOrd :: Ord BillableIdderive instance billableIdNewtype :: Newtype BillableId _instance billableIdDecodeJson :: DecodeJson BillableId wheredecodeJson json = douuidStr <- decodeJson jsonBillableId <$> (note "Failed to decode billable UUID" $ parseUUID uuidStr)newtype Billable' t = Billable{}type Billable = Billable' DateTimenewtype PaymentRequestId= PaymentRequestId UUIDderive instance paymentRequestIdEq :: Eq PaymentRequestIdderive instance paymentRequestIdOrd :: Ord PaymentRequestIdderive instance paymentRequestIdNewtype :: Newtype PaymentRequestId _instance paymentRequestIdDecodeJson :: DecodeJson PaymentRequestId wheredecodeJson json = douuidStr <- decodeJson jsonPaymentRequestId <$> (note "Failed to decode paymentRequest UUID" $ parseUUID uuidStr)newtype PaymentRequest' t = PaymentRequest{}type PaymentRequest = PaymentRequest' DateTimecreateBillable :: ProjectId -> Billable -> Aff (Either APIError BillableId)createBillable pid bid = pure $ Left ForbiddenlistProjectBillables :: ProjectId -> Aff (Either APIError (Array (Tuple BillableId Billable)))listProjectBillables pid = pure $ Left ForbiddenlistUnpaidPaymentRequests :: BillableId -> Aff (Either APIError (Array (Tuple PaymentRequestId PaymentRequest)))listUnpaidPaymentRequests billId = pure $ Left Forbidden - file addition: Billing.purs[14.1]
module Aftok.Billing whereimport Preludeimport Control.Monad.Trans.Class (lift)-- import Data.DateTime (DateTime, date)import Data.Either (Either(..))import Data.Foldable (all)import Data.Maybe (Maybe(..), isNothing)-- import Data.Unfoldable as Uimport Data.Newtype (unwrap)import Data.Symbol (SProxy(..))import Data.Traversable (traverse)import Data.Tuple (Tuple)import Effect.Aff (Aff)-- import Effect.Class (liftEffect)-- import Effect.Now (nowDateTime)import Halogen as Himport Halogen.HTML.Core (ClassName(..))import Halogen.HTML as HHimport Halogen.HTML.Properties as Pimport Aftok.ProjectList as ProjectListimport Aftok.Types (System, ProjectId)import Aftok.Api.Types (APIError(..))import Aftok.Api.Project (Project)import Aftok.Api.Billing( BillableId, Billable, PaymentRequestId, PaymentRequest, createBillable, listProjectBillables, listUnpaidPaymentRequests)type BillingInput= Maybe Projecttype BillingState= { selectedProject :: Maybe Project, billables :: Array (Tuple BillableId Billable), selectedBillable :: Maybe (Tuple BillableId Billable), paymentRequests :: Array (Tuple PaymentRequestId PaymentRequest)}data BillingAction= Initialize| ProjectSelected Projecttype Slot id= forall query. H.Slot query ProjectList.Event idtype Slots= ( projectList :: ProjectList.Slot Unit)_projectList = SProxy :: SProxy "projectList"type Capability (m :: Type -> Type)= { createBillable :: ProjectId -> Billable -> m (Either APIError BillableId), listProjectBillables :: ProjectId -> m (Either APIError (Array (Tuple BillableId Billable))), listUnpaidPaymentRequests :: BillableId -> m (Either APIError (Array (Tuple PaymentRequestId PaymentRequest)))}component ::forall query m.Monad m =>System m ->Capability m ->ProjectList.Capability m ->H.Component HH.HTML query BillingInput ProjectList.Event mcomponent system caps pcaps =H.mkComponent{ initialState, render, eval:H.mkEval$ H.defaultEval{ handleAction = eval, initialize = Just Initialize}}whereinitialState :: BillingInput -> BillingStateinitialState input ={ selectedProject: input, billables: [], selectedBillable: Nothing, paymentRequests: []}render :: BillingState -> H.ComponentHTML BillingAction Slots mrender st =HH.section[ P.classes (ClassName <$> [ "section-border", "border-primary" ]) ][ HH.div[ P.classes (ClassName <$> [ "container", "pt-6" ]) ][ HH.h1[ P.classes (ClassName <$> [ "mb-0", "font-weight-bold", "text-center" ]) ][ HH.text "Billing" ], HH.p[ P.classes (ClassName <$> [ "col-md-5", "text-muted", "text-center", "mx-auto" ]) ][ HH.text "Your project's payment requests & payments" ], HH.div_[ HH.slot_projectListunit(ProjectList.component system pcaps)st.selectedProject(Just <<< (\(ProjectList.ProjectChange p) -> ProjectSelected p))], HH.div[ P.classes (ClassName <$> if isNothing st.selectedProject then [ "collapse" ] else []) ][ billingDetail st ]]]billingDetail :: BillingState -> H.ComponentHTML BillingAction Slots mbillingDetail st = doHH.div [] []eval :: BillingAction -> H.HalogenM BillingState BillingAction Slots ProjectList.Event m Uniteval action = docase action ofInitialize -> docurrentProject <- H.gets (_.selectedProject)billables <- lift $ traverse (caps.listProjectBillables <<< (_.projectId) <<< unwrap) currentProjectcase billables ofNothing -> pure unitJust (Left err) -> lift $ system.error (show err)Just (Right b) -> H.modify_ (_ { billables = b })ProjectSelected p -> docurrentProject <- H.gets (_.selectedProject)when (all (\p' -> (unwrap p').projectId /= (unwrap p).projectId) currentProject)$ doH.raise (ProjectList.ProjectChange p)H.modify_ (_ { selectedProject = Just p })apiCapability :: Capability AffapiCapability ={ createBillable: createBillable, listProjectBillables: listProjectBillables, listUnpaidPaymentRequests: listUnpaidPaymentRequests}mockCapability :: Capability AffmockCapability ={ createBillable: \_ _ -> pure $ Left Forbidden, listProjectBillables: \_ -> pure $ Left Forbidden, listUnpaidPaymentRequests: \_ -> pure $ Left Forbidden} - edit in client/src/Aftok/Overview.purs at line 4
-- import Control.Alt ((<|>))-- import Control.Monad.Rec.Class (forever)-- import Control.Monad.State (State, put, get, evalState) - edit in client/src/Aftok/Overview.purs at line 5[3.44]→[3.313:373](∅→∅),[3.313]→[3.313:373](∅→∅),[3.373]→[3.698:701](∅→∅),[3.701]→[3.377:414](∅→∅),[3.377]→[3.377:414](∅→∅),[3.452]→[3.452:513](∅→∅)
-- import Control.Monad.Maybe.Trans (MaybeT(..), runMaybeT)---- import Data.Array (reverse, cons)-- import Data.Date.Component (Year(..), Month(..), Day(..)) - edit in client/src/Aftok/Overview.purs at line 9
-- import Data.Enum (fromEnum) - edit in client/src/Aftok/Overview.purs at line 11
-- import Data.Formatters.DateTime - edit in client/src/Aftok/Overview.purs at line 18
-- import Data.Tuple (Tuple(..))-- import Data.Unfoldable as U-- -- import Text.Format as F -- (format, zeroFill, width) - edit in client/src/Aftok/Overview.purs at line 19
-- import Effect.Aff as Aff - edit in client/src/Aftok/Overview.purs at line 21
-- import Effect.Exception (error)-- import Effect.Now (now) - edit in client/src/Aftok/Overview.purs at line 23
-- import Halogen.Query.EventSource (EventSource)-- import Halogen.Query.EventSource as EventSource - edit in client/src/Aftok/Overview.purs at line 25
-- import Halogen.HTML.CSS as CSS-- import Halogen.HTML.Events as E - edit in client/src/Aftok/Overview.purs at line 26[3.1520]→[3.1520:1722](∅→∅),[3.1723]→[3.1723:1758](∅→∅),[3.1758]→[3.760:811](∅→∅),[3.811]→[3.1811:1829](∅→∅),[3.1811]→[3.1811:1829](∅→∅),[3.1829]→[3.812:854](∅→∅),[3.854]→[3.1873:1892](∅→∅),[3.1873]→[3.1873:1892](∅→∅),[3.1892]→[3.855:872](∅→∅),[3.872]→[3.1910:1984](∅→∅),[3.1910]→[3.1910:1984](∅→∅)
-- import CSS (backgroundColor, clear, clearBoth, border, rgb, solid, borderRadius, marginLeft)-- import CSS.Display (display, flex)-- import CSS.Geometry (width, height)-- import CSS.Size (px, pct)-- import Aftok.Api.Overview as TL-- import Aftok.Api.Overview-- ( OverviewError,-- Event(..),-- Interval(..),-- TimeInterval,-- KeyedEvent,-- TimeSpan,-- start, end, interval,-- event, eventTime, keyedEvent-- )