Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Renew subscriptions #682

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions apps/mitt-konto/src/Components/Renew.purs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
module MittKonto.Components.Renew where

import Prelude

import Bottega.Models (PaymentMethod(..))
import Data.Array (snoc)
import Data.Nullable as Nullable
import KSF.Api.Subscription as Subscription
import MittKonto.Main.UserView.Subscription.Types as Types
import Prenumerera.Package as Package
import Prenumerera.Page.SelectPeriod as SelectPeriod
import Prenumerera.Page.Payment as Payment
import React.Basic.DOM as DOM
import React.Basic.Events (handler_)
import React.Basic.Hooks (Component, useState', (/\))
import React.Basic.Hooks as React

data Stage = SelectPeriod | Payment Package.PackageOffer PaymentMethod | Finished

component :: Component Types.RenewSubscription
component = do
selectPeriod <- SelectPeriod.component
payment <- Payment.component
React.component "Renew" $ \props@{package, description, user, onCancel} -> React.do
stage /\ setStage <- useState' SelectPeriod
let initialPaymentMethod =
case props.subscription.paymentMethod of
Subscription.CreditCard -> CreditCard
Subscription.UnknownPaymentMethod -> CreditCard
_ -> if props.user.address /= Nullable.null then PaperInvoice else CreditCard
-- The default action of SelectPeriod is to ask for address if
-- none is set and paper invoice is selected, but that makes
-- less sense when used in Mitt Konto.
availablePaymentMethods =
if props.user.address /= Nullable.null then [ CreditCard, PaperInvoice ] else [ CreditCard ]
closable content = DOM.div
{ className: "route-wrapper payment-popup"
, children:
[ DOM.div
{ className: "header-x-button"
, children:
[ DOM.div
{ className: "close-button"
, children: [ DOM.div { className: "close-icon" } ]
, onClick: handler_ onCancel
}
]
}
] `snoc` content
}

pure $ case stage of
SelectPeriod ->
DOM.div
{ className: "route-wrapper"
, children:
[ DOM.div
{ className: "close-button"
, children: [ DOM.div { className: "close-icon" } ]
, onClick: handler_ onCancel
}
]
} <>
selectPeriod
{ package
, description
, user
, initialPaymentMethod
, availablePaymentMethods
, cancel: onCancel
, next: \offer paymentMethod _ -> setStage $ Payment offer paymentMethod
}
Payment offer method ->
closable $ payment
{ user
, package
, description
, offer
, method
, next: setStage Finished
}
Finished ->
closable $ DOM.text "Köpet är klart"
3 changes: 1 addition & 2 deletions apps/mitt-konto/src/Components/Subscription.purs
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,10 @@ render :: Types.Self -> JSX -> JSX
render self@{ props: { now, subscription: sub } } informationColumn =
Grid.row2
informationColumn
(if expired then mempty else Elements.subscriptionUpdates self)
(Elements.subscriptionUpdates self)
{ extraClasses: [ "subscription--container" ]
, _data: [ Tuple "subsno" subsno ]
, id: "subscription-" <> subsno
}
where
expired = isSubscriptionExpired sub now
subsno = Subsno.toString sub.subsno
12 changes: 8 additions & 4 deletions apps/mitt-konto/src/Components/User.purs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Data.Nullable as Nullable
import Data.String (contains)
import Data.String.Pattern (Pattern(..))
import MittKonto.Components.Mailinglists as Mailinglists
import MittKonto.Components.Renew as Renew
import MittKonto.Components.Subscription (component) as Subscription
import MittKonto.Main.UserView.AccountEdit as AccountEdit
import MittKonto.Main.UserView.IconAction as IconAction
Expand All @@ -22,7 +23,7 @@ import KSF.User.Cusno as Cusno
import React.Basic (JSX)
import React.Basic.DOM as DOM
import React.Basic.Hooks as React
import React.Basic.Hooks (Component)
import React.Basic.Hooks (Component, useState', (/\))
import Routing.PushState (PushStateInterface)

foreign import images :: { subscribe :: String }
Expand All @@ -34,7 +35,9 @@ component router logger = do
subscriptionComponent <- Subscription.component
profile <- Profile.component
mailinglists <- Mailinglists.component
renewSubscription <- Renew.component
React.component "UserView" \{ state: { news, now }, setState, user } -> React.do
renewingSubscription /\ setRenewingSubscription <- useState' Nothing
let profileView =
Helpers.componentBlock
"Mina uppgifter:"
Expand All @@ -59,10 +62,11 @@ component router logger = do
}

subscriptionView subscription =
subscriptionComponent { subscription, user, logger, now, router }
subscriptionComponent { subscription, user, logger, now, router, renewSubscription, setRenewingSubscription }
subscriptionsView =
Helpers.componentBlock "Mina prenumerationer:" $ subscriptions <> [ Elements.break, subscribeImage ]
where
renewing subscription = if Just subscription.subsno == renewingSubscription then " renewing" else ""
subscriptions =
-- Sort the canceled subscriptions to the end of the list
case sortBy (comparing _.state) user.subs of
Expand All @@ -75,10 +79,10 @@ component router logger = do
subscriptionComponentBlockContent subscription
-- If the subscription has a canceled state, we want to add extra css to it.
| Subscription.isSubscriptionCanceled subscription =
Helpers.componentBlockContent " mitt-konto--canceled-subscription" $
Helpers.componentBlockContent (" mitt-konto--canceled-subscription" <> (renewing subscription)) $
subscriptionView subscription
| Subscription.isSubscriptionExpired subscription now =
Helpers.componentBlockContent " mitt-konto--expired-subscription" $
Helpers.componentBlockContent (" mitt-konto--expired-subscription" <> (renewing subscription)) $
subscriptionView subscription
| otherwise = Helpers.componentBlockContent "" $ subscriptionView subscription

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import Data.Enum (enumFromTo)
import Data.Foldable (foldMap, for_, null, maximum)
import Data.JSDate (toDate, toDateTime)
import Data.List (intercalate)
import Data.Map as Map
import Data.Maybe (Maybe(..), fromMaybe, isNothing, maybe)
import Data.Monoid (guard)
import Data.Newtype (unwrap)
import Data.Nullable as Nullable
import Data.Nullable (toMaybe)
import Data.String (length, splitAt, trim)
import Data.Tuple (Tuple(..))
import Effect.Aff as Aff
import Effect.Class (liftEffect)
import Foreign (unsafeToForeign)
import KSF.Api.Subscription (PausedSubscription, SubscriptionPaymentMethod(..), isSubscriptionPausable, isSubscriptionTemporaryAddressChangable, isPause)
import KSF.Api.Subscription (PausedSubscription, SubscriptionPaymentMethod(..), isPause, isSubscriptionExpired, isSubscriptionPausable, isSubscriptionTemporaryAddressChangable)
import KSF.Api.Subscription (toString) as Subsno
import KSF.AsyncWrapper as AsyncWrapper
import KSF.DeliveryReclamation as DeliveryReclamation
Expand All @@ -37,6 +39,8 @@ import MittKonto.Main.UserView.Subscription.Helpers as Helpers
import MittKonto.Main.UserView.Subscription.Types as Types
import MittKonto.Wrappers.ActionsWrapper (actionsWrapper) as ActionsWrapper
import MittKonto.Wrappers.Elements (successWrapper)
import Prenumerera.Package as Prenumerera.Package
import Prenumerera.Package.Description (Description, packageDescriptions)
import React.Basic (JSX)
import React.Basic.DOM as DOM
import React.Basic.DOM.Events (capture_, preventDefault)
Expand Down Expand Up @@ -141,15 +145,25 @@ subscriptionEndTerm { props: { subscription: { dates: { suspend } } } } = foldMa
) $ trim <<< formatDateDots <$> (toDate =<< toMaybe suspend)

subscriptionUpdates :: Types.Self -> JSX
subscriptionUpdates self@{ props: props@{ now, subscription: sub@{ subsno, package } }, state } =
subscriptionUpdates self@{ props: props@{ now, subscription: sub@{ subsno } }, state } =
Grid.row_ [ actionsWrapper ]
where
actionsWrapper = ActionsWrapper.actionsWrapper
{ actions: (if package.digitalOnly then
mempty
else
paperOnlyActions)
<> extraActions
{ actions: if isSubscriptionExpired sub now
then case Tuple
<$> Map.lookup sub.package.id packageDescriptions
<*> Prenumerera.Package.fromApiPackage sub.package
of
Just (Tuple description package)
| sub.cusno == props.user.cusno && not (null sub.package.offers) &&
(sub.package.digitalOnly || props.user.address /= Nullable.null) ->
[ renewUpdateIcon description package ]
_ -> mempty
else (if sub.package.digitalOnly then
mempty
else
paperOnlyActions)
<> extraActions
, wrapperState: self.state.wrapperProgress
, onTryAgain: self.setState _ { wrapperProgress = updateProgress }
, containerClass: "subscription--actions-container flex"
Expand Down Expand Up @@ -183,6 +197,7 @@ subscriptionUpdates self@{ props: props@{ now, subscription: sub@{ subsno, packa
Just (Types.EditTemporaryAddressChange change) ->
AsyncWrapper.Editing $ temporaryAddressChangeComponent self $ Just change
Just Types.DeliveryReclamation -> AsyncWrapper.Editing deliveryReclamationComponent
Just (Types.RenewSubscription d p)-> AsyncWrapper.Editing $ renewSubscriptionComponent self d p
Nothing -> AsyncWrapper.Ready

deliveryReclamationComponent =
Expand Down Expand Up @@ -360,6 +375,30 @@ subscriptionUpdates self@{ props: props@{ now, subscription: sub@{ subsno, packa
]
}

renewUpdateIcon description package =
DOM.div
{ className: "subscription--action-item"
, children:
[ DOM.div
{ className: "subscription--renew-icon circle"
, onClick: showRenewSubscription
}
, DOM.span
{ className: "subscription--update-action-text"
, children:
[ DOM.u_ [ DOM.text "Förnya prenumerationen" ]]
, onClick: showRenewSubscription
}
]
}
where
showRenewSubscription = handler_ do
self.props.setRenewingSubscription $ Just subsno
self.setState _
{ updateAction = Just $ Types.RenewSubscription description package
, wrapperProgress = AsyncWrapper.Editing $ renewSubscriptionComponent self description package
}

pauseSubscriptionComponent :: Types.Self -> Maybe User.PausedSubscription -> JSX
pauseSubscriptionComponent self@{ props: props@{ subscription: sub@{ package } } } editing =
PauseSubscription.pauseSubscription
Expand Down Expand Up @@ -505,3 +544,21 @@ changeButton self updateAction component =
{ updateAction = Just updateAction
, wrapperProgress = AsyncWrapper.Editing component
}

renewSubscriptionComponent :: Types.Self -> Description -> Prenumerera.Package.Package -> JSX
renewSubscriptionComponent self@{props} description package =
props.renewSubscription
{ subscription: props.subscription
, user: props.user
, description
, package
, onCancel: do
self.props.setRenewingSubscription Nothing
self.setState _ { wrapperProgress = AsyncWrapper.Ready }
, onSuccess: do
self.props.setRenewingSubscription Nothing
self.setState _ { wrapperProgress = AsyncWrapper.Ready }
, onError: const do
self.props.setRenewingSubscription Nothing
self.setState _ { wrapperProgress = AsyncWrapper.Error "Något gick fel" }
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import Prelude
import Data.Date (Date)
import Data.Maybe (Maybe)
import Effect (Effect)
import KSF.Api.Subscription (Subsno)
import KSF.AsyncWrapper as AsyncWrapper
import KSF.Sentry as Sentry
import KSF.User as User
import KSF.User (User)
import Prenumerera.Package as Prenumerera.Package
import Prenumerera.Package.Description (Description)
import React.Basic (JSX)
import Routing.PushState (PushStateInterface)

Expand All @@ -24,6 +27,8 @@ type Props =
, logger :: Sentry.Logger
, now :: Date
, router :: PushStateInterface
, renewSubscription :: RenewSubscription -> JSX
, setRenewingSubscription :: Maybe Subsno -> Effect Unit
}

type State =
Expand All @@ -39,6 +44,7 @@ data SubscriptionUpdateAction
| TemporaryAddressChange
| EditTemporaryAddressChange User.PendingAddressChange
| DeliveryReclamation
| RenewSubscription Description Prenumerera.Package.Package

type Subscription =
{ package :: { name :: String
Expand All @@ -47,3 +53,13 @@ type Subscription =
, state :: String
, dates :: User.SubscriptionDates
}

type RenewSubscription =
{ subscription :: User.Subscription
, user :: User
, package :: Prenumerera.Package.Package
, description :: Description
, onCancel :: Effect Unit
, onSuccess :: Effect Unit
, onError :: User.UserError -> Effect Unit
}
4 changes: 0 additions & 4 deletions apps/prenumerera/spago.dhall
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,11 @@
, "arrays"
, "console"
, "control"
, "datetime"
, "effect"
, "either"
, "exceptions"
, "foldable-traversable"
, "foreign"
, "lists"
, "maybe"
, "now"
, "nullable"
Expand All @@ -23,9 +21,7 @@
, "react-basic-hooks"
, "routing"
, "strings"
, "transformers"
, "tuples"
, "validation"
, "web-html"
]
, packages = ../../packages.dhall
Expand Down
3 changes: 3 additions & 0 deletions apps/prenumerera/src/Prenumerera.purs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module Prenumerera where
import Prelude

import Bottega as Bottega
import Bottega.Models (PaymentMethod(..))
import Control.Alt ((<|>))
import Data.Array (elem, filter, mapMaybe)
import Data.Either (Either(..), either, isLeft)
Expand Down Expand Up @@ -201,6 +202,8 @@ app = do
{ package
, description
, user: u
, availablePaymentMethods: [ CreditCard, PaperInvoice ]
, initialPaymentMethod: CreditCard
, next: offerAndMethodSelected
, cancel: nav.pushState (unsafeToForeign {}) "/"
}
Expand Down
Loading