module View.Payment.Form ( view , In(..) , Out(..) ) where import Data.Aeson (ToJSON) import qualified Data.List as L import Data.List.NonEmpty (NonEmpty) import qualified Data.Map as M import qualified Data.Maybe as Maybe import Data.Text (Text) import qualified Data.Text as T import Data.Time.Calendar (Day) import qualified Data.Time.Calendar as Calendar import Data.Validation (Validation) import qualified Data.Validation as V import Reflex.Dom (Dynamic, Event, MonadWidget) import qualified Reflex.Dom as R import qualified Text.Read as T import Common.Model (Category (..), CategoryId, Frequency (..), Payment (..), PaymentCategory (..), SavedPayment (..)) import qualified Common.Msg as Msg import qualified Common.Validation.Payment as PaymentValidation import qualified Component.Input as Input import qualified Component.ModalForm as ModalForm import qualified Component.Select as Select import qualified Util.Validation as ValidationUtil data In m t a = In { _in_cancel :: Event t () , _in_headerLabel :: Text , _in_categories :: [Category] , _in_paymentCategories :: [PaymentCategory] , _in_name :: Text , _in_cost :: Text , _in_date :: Day , _in_category :: CategoryId , _in_frequency :: Frequency , _in_mkPayload :: Text -> Text -> Text -> CategoryId -> Frequency -> a , _in_ajax :: Text -> Event t a -> m (Event t (Either Text SavedPayment)) } data Out t = Out { _output_hide :: Event t () , _output_addPayment :: Event t SavedPayment } view :: forall t m a. (MonadWidget t m, ToJSON a) => In m t a -> m (Out t) view input = do rec let reset = R.leftmost [ "" <$ ModalForm._out_cancel modalForm , "" <$ ModalForm._out_validate modalForm , "" <$ _in_cancel input ] modalForm <- ModalForm.view $ ModalForm.In { ModalForm._in_headerLabel = _in_headerLabel input , ModalForm._in_ajax = _in_ajax input "/api/payment" , ModalForm._in_form = form reset (ModalForm._out_confirm modalForm) } return $ Out { _output_hide = ModalForm._out_hide modalForm , _output_addPayment = ModalForm._out_validate modalForm } where form :: Event t String -> Event t () -> m (Dynamic t (Validation (NonEmpty Text) a)) form reset confirm = do name <- Input.view (Input.defaultIn { Input._in_label = Msg.get Msg.Payment_Name , Input._in_initialValue = _in_name input , Input._in_validation = PaymentValidation.name }) (_in_name input <$ reset) confirm cost <- Input._out_raw <$> (Input.view (Input.defaultIn { Input._in_label = Msg.get Msg.Payment_Cost , Input._in_initialValue = _in_cost input , Input._in_validation = PaymentValidation.cost }) (_in_cost input <$ reset) confirm) let initialDate = T.pack . Calendar.showGregorian . _in_date $ input date <- Input._out_raw <$> (Input.view (Input.defaultIn { Input._in_label = Msg.get Msg.Payment_Date , Input._in_initialValue = initialDate , Input._in_inputType = "date" , Input._in_hasResetButton = False , Input._in_validation = PaymentValidation.date }) (initialDate <$ reset) confirm) let setCategory = R.fmapMaybe id . R.updated $ R.ffor (Input._out_raw name) $ \name -> findCategory name (_in_paymentCategories input) category <- Select._out_value <$> (Select.view $ Select.In { Select._in_label = Msg.get Msg.Payment_Category , Select._in_initialValue = _in_category input , Select._in_value = setCategory , Select._in_values = R.constDyn categories , Select._in_reset = _in_category input <$ reset , Select._in_isValid = PaymentValidation.category (map _category_id $ _in_categories input) , Select._in_validate = confirm }) return $ do n <- Input._out_value name c <- cost d <- date cat <- category return ((_in_mkPayload input) <$> ValidationUtil.nelError n <*> V.Success c <*> V.Success d <*> ValidationUtil.nelError cat <*> V.Success (_in_frequency input)) frequencies = M.fromList [ (Punctual, Msg.get Msg.Payment_PunctualMale) , (Monthly, Msg.get Msg.Payment_MonthlyMale) ] categories = M.fromList . flip map (_in_categories input) $ \c -> (_category_id c, _category_name c) findCategory :: Text -> [PaymentCategory] -> Maybe CategoryId findCategory paymentName = fmap _paymentCategory_category . L.find ((==) (T.toLower paymentName) . _paymentCategory_name)