module View.Payment.Form ( view , Input(..) , Output(..) ) 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 Component (InputIn (..), InputOut (..), ModalFormIn (..), ModalFormOut (..), SelectIn (..), SelectOut (..)) import qualified Component as Component import qualified Util.Validation as ValidationUtil data Input m t a = Input { _input_cancel :: Event t () , _input_headerLabel :: Text , _input_categories :: [Category] , _input_paymentCategories :: [PaymentCategory] , _input_name :: Text , _input_cost :: Text , _input_date :: Day , _input_category :: CategoryId , _input_frequency :: Frequency , _input_mkPayload :: Text -> Text -> Text -> CategoryId -> Frequency -> a , _input_ajax :: Text -> Event t a -> m (Event t (Either Text SavedPayment)) } data Output t = Output { _output_hide :: Event t () , _output_addPayment :: Event t SavedPayment } view :: forall t m a. (MonadWidget t m, ToJSON a) => Input m t a -> m (Output t) view input = do rec let reset = R.leftmost [ "" <$ _modalFormOut_cancel modalForm , "" <$ _modalFormOut_validate modalForm , "" <$ _input_cancel input ] modalForm <- Component.modalForm $ ModalFormIn { _modalFormIn_headerLabel = _input_headerLabel input , _modalFormIn_ajax = _input_ajax input "/api/payment" , _modalFormIn_form = form reset (_modalFormOut_confirm modalForm) } return $ Output { _output_hide = _modalFormOut_hide modalForm , _output_addPayment = _modalFormOut_validate modalForm } where form :: Event t String -> Event t () -> m (Dynamic t (Validation (NonEmpty Text) a)) form reset confirm = do name <- Component.input (Component.defaultInputIn { _inputIn_label = Msg.get Msg.Payment_Name , _inputIn_initialValue = _input_name input , _inputIn_validation = PaymentValidation.name }) (_input_name input <$ reset) confirm cost <- _inputOut_raw <$> (Component.input (Component.defaultInputIn { _inputIn_label = Msg.get Msg.Payment_Cost , _inputIn_initialValue = _input_cost input , _inputIn_validation = PaymentValidation.cost }) (_input_cost input <$ reset) confirm) let initialDate = T.pack . Calendar.showGregorian . _input_date $ input date <- _inputOut_raw <$> (Component.input (Component.defaultInputIn { _inputIn_label = Msg.get Msg.Payment_Date , _inputIn_initialValue = initialDate , _inputIn_inputType = "date" , _inputIn_hasResetButton = False , _inputIn_validation = PaymentValidation.date }) (initialDate <$ reset) confirm) let setCategory = R.fmapMaybe id . R.updated $ R.ffor (_inputOut_raw name) $ \name -> findCategory name (_input_paymentCategories input) category <- _selectOut_value <$> (Component.select $ SelectIn { _selectIn_label = Msg.get Msg.Payment_Category , _selectIn_initialValue = _input_category input , _selectIn_value = setCategory , _selectIn_values = R.constDyn categories , _selectIn_reset = _input_category input <$ reset , _selectIn_isValid = PaymentValidation.category (map _category_id $ _input_categories input) , _selectIn_validate = confirm }) return $ do n <- _inputOut_value name c <- cost d <- date cat <- category return ((_input_mkPayload input) <$> ValidationUtil.nelError n <*> V.Success c <*> V.Success d <*> ValidationUtil.nelError cat <*> V.Success (_input_frequency input)) frequencies = M.fromList [ (Punctual, Msg.get Msg.Payment_PunctualMale) , (Monthly, Msg.get Msg.Payment_MonthlyMale) ] categories = M.fromList . flip map (_input_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)