(Semi-)Working Prototype

This commit is contained in:
Gregor Kleen 2019-03-20 15:13:41 +01:00
parent 705fdec695
commit 4b2d6d3aa2
6 changed files with 238 additions and 44 deletions

View File

@ -2,6 +2,7 @@ module Handler.Admin where
import Import import Import
import Handler.Utils import Handler.Utils
import Handler.Utils.Form.MassInput
import Jobs import Jobs
import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder) import Data.Aeson.Encode.Pretty (encodePrettyToTextBuilder)
@ -15,6 +16,12 @@ import Control.Monad.Trans.Except
import Database.Persist.Sql (fromSqlKey) import Database.Persist.Sql (fromSqlKey)
import qualified Data.Text as Text
import Data.Char (isDigit)
import qualified Data.Map as Map
import qualified Data.Set as Set
-- import Colonnade hiding (fromMaybe) -- import Colonnade hiding (fromMaybe)
-- import Yesod.Colonnade -- import Yesod.Colonnade
@ -125,6 +132,45 @@ postAdminTestR = do
<li>#{m} <li>#{m}
|] |]
let
-- | Make a form for adding a point/line/plane/hyperplane/... (in this case: cell)
--
-- This /needs/ to use @nudge@ (for deterministic field naming) and to replace all occurances of @mreq@ with @mpreq@ (no fields should be /actually/ required)
mkAddForm :: ListPosition -- ^ Approximate position of the add-widget
-> Natural -- ^ Dimension Index, outermost dimension ist 0 i.e. if dimension is 3 hyperplane-adders get passed 0, planes get passed 1, lines get 2, and points get 3
-> ListLength -- ^ Liveliness
-> (Text -> Text) -- ^ Nudge deterministic field ids so they're unique
-> FieldView UniWorX -- ^ Submit-Button for this add-widget
-> Maybe (Form (ListPosition, Int)) -- ^ Nothing if no further cells should be added; returns index of new cell and data needed to initialize cell
mkAddForm 0 0 listLength nudge submitBtn
| listLength >= 7 = Nothing
| otherwise = Just $ \csrf -> do
(addRes, addView) <- mpreq textField ("" & addName (nudge "text")) Nothing
let addRes' = fromMaybe 0 . readMay . Text.filter isDigit <$> addRes
return ((fromIntegral listLength, ) <$> addRes', toWidget csrf >> fvInput addView >> fvInput submitBtn)
mkAddForm _pos _dim _ _ _ = error "Dimension and Position is always 0 for our 1-dimensional form"
-- | Make a single massInput-Cell
--
-- This /needs/ to use @nudge@ for deterministic field naming
mkCellForm :: ListPosition -- ^ Position of this cell
-> Int -- ^ Data needed to initialize the cell (see return of @mkAddForm@)
-> Maybe Int -- ^ Initial cell result from Argument to `massInput`
-> (Text -> Text) -- ^ Nudge deterministic field ids so they're unique
-> Form Int
mkCellForm _pos initial previous nudge csrf = do
(intRes, intView) <- mreq intField ("" & addName (nudge "int")) $ previous <|> Just initial
return (intRes, toWidget csrf >> fvInput intView)
-- | How does the shape (`ListLength`) change if a certain cell is deleted?
deleteCell :: ListLength -- ^ Current shape
-> ListPosition -- ^ Coordinate to delete
-> MaybeT (MForm (HandlerT UniWorX IO)) (Map ListPosition ListPosition) -- ^ Monadically compute a set of new positions and their associated old positions
deleteCell l pos
| l >= 2 = return . Map.fromSet (\pos' -> bool pos' (succ pos') $ pos' >= pos) $ Set.fromList [0..fromIntegral (l - 2)]
| otherwise = return Map.empty
((miResult, (fvInput -> miForm)), miEnc) <- runFormPost . identifyForm ("massinput" :: Text) $ massInput (MassInput mkAddForm mkCellForm deleteCell) "" True Nothing
let locallyDefinedPageHeading = [whamlet|Admin TestPage for Uni2work|] let locallyDefinedPageHeading = [whamlet|Admin TestPage for Uni2work|]
siteLayout locallyDefinedPageHeading $ do siteLayout locallyDefinedPageHeading $ do
-- defaultLayout $ do -- defaultLayout $ do
@ -135,6 +181,22 @@ postAdminTestR = do
$(widgetFile "formPage") $(widgetFile "formPage")
showDemoResult showDemoResult
[whamlet|
<h2>Mass-Input
<form enctype=#{miEnc} method=POST>
^{miForm}
^{submitButtonView}
$case miResult
$of FormMissing
$of FormFailure errs
<ul>
$forall err <- errs
<li>#{err}
$of FormSuccess res
<pre>
#{tshow res}
|]
getAdminErrMsgR, postAdminErrMsgR :: Handler Html getAdminErrMsgR, postAdminErrMsgR :: Handler Html
getAdminErrMsgR = postAdminErrMsgR getAdminErrMsgR = postAdminErrMsgR

View File

@ -1,20 +1,23 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Handler.Utils.Form.MassInput module Handler.Utils.Form.MassInput
( massInput ( MassInput(..)
, massInput
, BoxDimension(..) , BoxDimension(..)
, IsBoxCoord(..), boxDimension , IsBoxCoord(..), boxDimension
, Liveliness(..) , Liveliness(..)
, ListLength(..), ListPosition(..)
) where ) where
import Import import Import
import Utils.Form import Utils.Form
import Utils.Lens
import Handler.Utils.Form (secretJsonField) import Handler.Utils.Form (secretJsonField)
import Data.Aeson import Data.Aeson
import Algebra.Lattice import Algebra.Lattice
import Control.Lens hiding (universe)
import Text.Blaze (Markup) import Text.Blaze (Markup)
import qualified Data.Text as Text import qualified Data.Text as Text
@ -23,6 +26,9 @@ import qualified Data.Set as Set
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.List (genericLength, genericIndex, iterate) import Data.List (genericLength, genericIndex, iterate)
import Control.Monad.Trans.Maybe
import Control.Monad.Fix
data BoxDimension x = forall n. (Enum n, Num n) => BoxDimension (Lens' x n) data BoxDimension x = forall n. (Enum n, Num n) => BoxDimension (Lens' x n)
@ -44,29 +50,71 @@ class (IsBoxCoord (BoxCoord a), Lattice a, BoundedJoinSemiLattice a) => Liveline
type BoxCoord a :: * type BoxCoord a :: *
liveCoords :: Prism' (Set (BoxCoord a)) a liveCoords :: Prism' (Set (BoxCoord a)) a
liveCoord :: BoxCoord a -> Prism' Bool a liveCoord :: BoxCoord a -> Prism' Bool a
liveCoord bC = prism' (\l -> bC `Set.member` review liveCoords l) (bool (Just bottom) (preview liveCoords $ Set.singleton bC))
newtype ListLength = ListLength { unListLength :: Natural }
deriving newtype (Num, Integral, Real, Enum, PathPiece)
deriving (Eq, Ord, Generic, Typeable, Read, Show)
makeWrapped ''ListLength
instance JoinSemiLattice ListLength where
(\/) = max
instance MeetSemiLattice ListLength where
(/\) = min
instance Lattice ListLength
instance BoundedJoinSemiLattice ListLength where
bottom = 0
newtype ListPosition = ListPosition { unListPosition :: Natural }
deriving newtype (Num, Integral, Real, Enum, PathPiece, ToJSONKey, FromJSONKey)
deriving (Eq, Ord, Generic, Typeable, Read, Show)
makeWrapped ''ListPosition
instance IsBoxCoord ListPosition where
boxDimensions = [BoxDimension id]
boxOrigin = 0
instance Liveliness ListLength where
type BoxCoord ListLength = ListPosition
liveCoords = prism' toSet fromSet
where
toSet n
| n > 0 = Set.fromList [0..pred (fromIntegral n)]
| otherwise = Set.empty
fromSet ns
| ns == maybe Set.empty (\n -> Set.fromList [0..n]) max'
= fmap (succ . fromIntegral) max' <|> Just 0
| otherwise
= Nothing
where
max' = Set.lookupMax ns
liveCoord (ListPosition n) = prism' (\(ListLength l) -> n < l) (bool (Just 0) (1 <$ guard (n == 0)))
data ButtonMassInput coord data ButtonMassInput coord
= MassInputAddDimension Natural = MassInputAddDimension Natural coord
| MassInputDeleteCell coord | MassInputDeleteCell coord
deriving (Eq, Ord, Read, Show, Generic, Typeable) deriving (Eq, Ord, Read, Show, Generic, Typeable)
instance PathPiece coord => PathPiece (ButtonMassInput coord) where instance PathPiece coord => PathPiece (ButtonMassInput coord) where
toPathPiece = \case toPathPiece = \case
MassInputAddDimension n -> "add__" <> toPathPiece n MassInputAddDimension n c -> "add__" <> toPathPiece n <> "__" <> toPathPiece c
MassInputDeleteCell c -> "delete__" <> toPathPiece c MassInputDeleteCell c -> "delete__" <> toPathPiece c
fromPathPiece t = addDim <|> delCell fromPathPiece t = addDim <|> delCell
where where
addDim = do addDim = do
nT <- stripPrefix "add__" t (dimT, Text.stripPrefix "__" -> Just coordT) <- Text.breakOn "__" <$> stripPrefix "add__" t
MassInputAddDimension <$> fromPathPiece nT MassInputAddDimension <$> fromPathPiece dimT <*> fromPathPiece coordT
delCell = do delCell = do
coordT <- stripPrefix "delete__" t coordT <- stripPrefix "delete__" t
MassInputDeleteCell <$> fromPathPiece coordT MassInputDeleteCell <$> fromPathPiece coordT
instance RenderMessage UniWorX (ButtonMassInput coord) where instance RenderMessage UniWorX (ButtonMassInput coord) where
renderMessage f ls = \case renderMessage f ls = \case
MassInputAddDimension _ -> mr MsgMassInputAddDimension MassInputAddDimension _ _ -> mr MsgMassInputAddDimension
MassInputDeleteCell _ -> mr MsgMassInputDeleteCell MassInputDeleteCell _ -> mr MsgMassInputDeleteCell
where where
mr = renderMessage f ls mr = renderMessage f ls
@ -74,21 +122,25 @@ instance RenderMessage UniWorX (ButtonMassInput coord) where
instance PathPiece coord => Button UniWorX (ButtonMassInput coord) where instance PathPiece coord => Button UniWorX (ButtonMassInput coord) where
btnValidate _ _ = False btnValidate _ _ = False
btnClasses (MassInputAddDimension _) = [BCIsButton, BCDefault] btnClasses (MassInputAddDimension _ _) = [BCIsButton, BCDefault]
btnClasses (MassInputDeleteCell _) = [BCIsButton, BCWarning] btnClasses (MassInputDeleteCell _) = [BCIsButton, BCWarning]
data MassInputFieldName coord data MassInputFieldName coord
= MassInputShape { miName :: Text } = MassInputShape { miName :: Text }
| MassInputAddWidget { miName :: Text, miCoord :: coord, miAddWidgetField :: Text } | MassInputAddWidget { miName :: Text, miCoord :: coord, miAddWidgetField :: Text }
| MassInputAddButton { miName :: Text, miCoord :: coord }
| MassInputDeleteButton { miName :: Text, miCoord :: coord }
| MassInputCell { miName :: Text, miCoord :: coord, miCellField :: Text } | MassInputCell { miName :: Text, miCoord :: coord, miCellField :: Text }
deriving (Eq, Ord, Read, Show, Generic, Typeable) deriving (Eq, Ord, Read, Show, Generic, Typeable)
instance IsBoxCoord coord => PathPiece (MassInputFieldName coord) where instance IsBoxCoord coord => PathPiece (MassInputFieldName coord) where
toPathPiece = \case toPathPiece = \case
MassInputShape{..} -> [st|#{miName}__shape|] MassInputShape{..} -> [st|#{miName}__shape|]
MassInputAddWidget{..} -> [st|#{miName}__#{toPathPiece miCoord}__#{miAddWidgetField}|] MassInputAddWidget{..} -> [st|#{miName}__add__#{toPathPiece miCoord}__fields__#{miAddWidgetField}|]
MassInputCell{..} -> [st|#{miName}__#{toPathPiece miCoord}__#{miCellField}|] MassInputAddButton{..} -> [st|#{miName}__add__#{toPathPiece miCoord}__submit|]
MassInputDeleteButton{..} -> [st|#{miName}__delete__#{toPathPiece miCoord}|]
MassInputCell{..} -> [st|#{miName}__cells__#{toPathPiece miCoord}__#{miCellField}|]
fromPathPiece t = do fromPathPiece t = do
(miName, Text.stripPrefix "__" -> Just t') <- return $ Text.breakOn "__" t (miName, Text.stripPrefix "__" -> Just t') <- return $ Text.breakOn "__" t
@ -97,11 +149,26 @@ instance IsBoxCoord coord => PathPiece (MassInputFieldName coord) where
guard $ t' == "shape" guard $ t' == "shape"
return MassInputShape{..} return MassInputShape{..}
, do , do
(coordT, Text.stripPrefix "__" -> Just miAddWidgetField) <- return $ Text.breakOn "__" t' t'' <- Text.stripPrefix "add__" t'
(coordT, Text.stripPrefix "__" -> Just rest) <- return $ Text.breakOn "__" t''
miAddWidgetField <- Text.stripPrefix "fields__" rest
miCoord <- fromPathPiece coordT miCoord <- fromPathPiece coordT
return MassInputAddWidget{..} return MassInputAddWidget{..}
, do , do
(coordT, Text.stripPrefix "__" -> Just miCellField) <- return $ Text.breakOn "__" t' t'' <- Text.stripPrefix "add__" t'
(coordT, Text.stripPrefix "__" -> Just ident) <- return $ Text.breakOn "__" t''
guard $ ident == "submit"
miCoord <- fromPathPiece coordT
return MassInputAddButton{..}
, do
t'' <- Text.stripPrefix "delete__" t'
(coordT, rest) <- return $ Text.breakOn "__" t''
guard $ Text.null rest
miCoord <- fromPathPiece coordT
return MassInputDeleteButton{..}
, do
t'' <- Text.stripPrefix "cells__" t'
(coordT, Text.stripPrefix "__" -> Just miCellField) <- return $ Text.breakOn "__" t''
miCoord <- fromPathPiece coordT miCoord <- fromPathPiece coordT
return MassInputCell{..} return MassInputCell{..}
] ]
@ -111,49 +178,50 @@ data MassInputException = MassInputInvalidShape
instance Exception MassInputException instance Exception MassInputException
data MassInput handler liveliness cellData cellResult = MassInput
{ miAdd :: BoxCoord liveliness -- Position (dimensions after @dimIx@ are zero)
-> Natural -- Zero-based dimension index @dimIx@
-> liveliness
-> (Text -> Text) -- Nudge deterministic field ids
-> FieldView UniWorX -- Submit button
-> Maybe (Markup -> MForm handler (FormResult (BoxCoord liveliness, cellData), Widget))
, miCell :: BoxCoord liveliness -- Position
-> cellData -- Initialisation data
-> Maybe cellResult -- Previous result
-> (Text -> Text) -- Nudge deterministic field ids
-> (Markup -> MForm handler (FormResult cellResult, Widget))
, miDelete :: liveliness -> BoxCoord liveliness -> MaybeT (MForm handler) (Map (BoxCoord liveliness) (BoxCoord liveliness))
}
massInput :: forall handler cellData cellResult liveliness. massInput :: forall handler cellData cellResult liveliness.
( MonadHandler handler, HandlerSite handler ~ UniWorX ( MonadHandler handler, HandlerSite handler ~ UniWorX
, ToJSON cellData, FromJSON cellData , ToJSON cellData, FromJSON cellData
, Liveliness liveliness , Liveliness liveliness
, MonadFix handler, MonadLogger handler
) )
=> ( Natural -- ^ Zero-based dimension index => MassInput handler liveliness cellData cellResult
-> liveliness -- ^ Currently live positions
-> (Text -> Text) -- ^ Nudge deterministic field ids
-> Maybe (Markup -> MForm handler (FormResult (BoxCoord liveliness, cellData), Widget))
) -- ^ Generate a cell-addition widget
-> ( BoxCoord liveliness
-> cellData
-> Maybe cellResult
-> (Text -> Text) -- ^ Nudge deterministic field ids
-> (Markup -> MForm handler (FormResult cellResult, Widget))
) -- ^ Cell-Widget
-> FieldSettings UniWorX -> FieldSettings UniWorX
-> Bool -- ^ Required? -> Bool -- ^ Required?
-> Maybe (Map (BoxCoord liveliness) (cellData, cellResult)) -> Maybe (Map (BoxCoord liveliness) (cellData, cellResult))
-> MForm handler (FormResult (Map (BoxCoord liveliness) (cellData, cellResult)), FieldView UniWorX) -> (Markup -> MForm handler (FormResult (Map (BoxCoord liveliness) (cellData, cellResult)), FieldView UniWorX))
massInput mkAddWidget mkCellWidget FieldSettings{..} fvRequired initialResult = do massInput MassInput{..} FieldSettings{..} fvRequired initialResult csrf = do
let initialShape = fmap fst <$> initialResult let initialShape = fmap fst <$> initialResult
miName <- maybe newFormIdent return fsName miName <- maybe newFormIdent return fsName
let let
shapeName :: MassInputFieldName (BoxCoord liveliness) shapeName :: MassInputFieldName (BoxCoord liveliness)
shapeName = MassInputShape{..} shapeName = MassInputShape{..}
(shape', _shapeWdgt) <- mreq secretJsonField ("" & addName shapeName) $ initialShape shapeField = secretJsonField
shape <- if sentShape <- runMaybeT $ do
| FormSuccess s <- shape' -> return s ts <- fromMaybe [] . Map.lookup (toPathPiece shapeName) <$> MaybeT askParams
fs <- fromMaybe [] . Map.lookup (toPathPiece shapeName) <$> MaybeT askFiles
MaybeT $ either (const Nothing) id <$> lift (fieldParse shapeField ts fs)
sentShape' <- if
| Just s <- sentShape -> return s
| Just iS <- initialShape -> return iS | Just iS <- initialShape -> return iS
| Set.null $ review liveCoords (bottom :: liveliness) -> return Map.empty | Set.null $ review liveCoords (bottom :: liveliness) -> return Map.empty
| otherwise -> throwM MassInputInvalidShape | otherwise -> throwM MassInputInvalidShape
sentLiveliness <- maybe (throwM MassInputInvalidShape) return $ Map.keysSet sentShape' ^? liveCoords :: MForm handler liveliness
cellResults <- flip Map.traverseWithKey shape $ \miCoord cData -> do
let
nudgeCellName :: Text -> Text
nudgeCellName miCellField = toPathPiece (MassInputCell{..} :: MassInputFieldName (BoxCoord liveliness))
(cData, ) <$> mkCellWidget miCoord cData (fmap snd . Map.lookup miCoord =<< initialResult) nudgeCellName mempty
liveliness <- maybe (throwM MassInputInvalidShape) return $ Map.keysSet shape ^? liveCoords
let result :: FormResult (Map (BoxCoord liveliness) (cellData, cellResult))
result = traverse (\(cData, (cResult, _)) -> (cData, ) <$> cResult) cellResults
let addForm :: [BoxDimension (BoxCoord liveliness)] -> MForm handler (Map (Natural, BoxCoord liveliness) (FormResult (BoxCoord liveliness, cellData), Widget)) let addForm :: [BoxDimension (BoxCoord liveliness)] -> MForm handler (Map (Natural, BoxCoord liveliness) (FormResult (BoxCoord liveliness, cellData), Widget))
addForm = addForm' boxOrigin . zip [0..] addForm = addForm' boxOrigin . zip [0..]
@ -162,17 +230,65 @@ massInput mkAddWidget mkCellWidget FieldSettings{..} fvRequired initialResult =
addForm' miCoord ((dimIx, _) : remDims) = do addForm' miCoord ((dimIx, _) : remDims) = do
let nudgeAddWidgetName :: Text -> Text let nudgeAddWidgetName :: Text -> Text
nudgeAddWidgetName miAddWidgetField = toPathPiece MassInputAddWidget{..} nudgeAddWidgetName miAddWidgetField = toPathPiece MassInputAddWidget{..}
dimRes <- traverse ($ mempty) $ mkAddWidget dimIx liveliness nudgeAddWidgetName dimRes <- runMaybeT $ do
(btnRes, btnView) <- lift $ mpreq (buttonField $ MassInputAddDimension dimIx miCoord) ("" & addName MassInputAddButton{..}) Nothing
(addRes, addView) <- MaybeT . traverse ($ mempty) $ miAdd miCoord dimIx sentLiveliness nudgeAddWidgetName btnView
return (btnRes *> addRes, addView)
let dimRes' = maybe Map.empty (Map.singleton (dimIx, miCoord)) dimRes let dimRes' = maybe Map.empty (Map.singleton (dimIx, miCoord)) dimRes
case remDims of case remDims of
[] -> return dimRes' [] -> return dimRes'
((_, BoxDimension dim) : _) -> do ((_, BoxDimension dim) : _) -> do
let let
miCoords = Set.fromList . takeWhile (\c -> review (liveCoord c) liveliness) $ iterate (over dim succ) miCoord miCoords = Set.fromList . takeWhile (\c -> review (liveCoord c) sentLiveliness) $ iterate (over dim succ) miCoord
dimRess <- sequence $ Map.fromSet (\c -> addForm' c remDims) miCoords dimRess <- sequence $ Map.fromSet (\c -> addForm' c remDims) miCoords
return $ dimRes' `Map.union` fold dimRess return $ dimRes' `Map.union` fold dimRess
addResults <- addForm boxDimensions addResults <- addForm boxDimensions
let addShape
| [FormSuccess (bCoord, cData)] <- Map.elems . Map.filter (is _FormSuccess) $ fmap fst addResults = Just $ Map.insert bCoord cData sentShape'
| otherwise = Nothing
addedShape <- if
| Just s <- addShape -> return s
| otherwise -> return sentShape'
addedLiveliness <- maybe (throwM MassInputInvalidShape) return $ Map.keysSet addedShape ^? liveCoords :: MForm handler liveliness
let
delForm :: BoxCoord liveliness -> MaybeT (MForm handler) (FormResult (Map (BoxCoord liveliness) (BoxCoord liveliness)), FieldView UniWorX)
delForm miCoord = do
(delRes, delView) <- lift $ mpreq (buttonField $ MassInputDeleteCell miCoord) ("" & addName MassInputDeleteButton{..}) Nothing
$logDebugS "delForm" . tshow $ fmap toPathPiece delRes
shapeUpdate <- miDelete addedLiveliness miCoord
guard $ isJust (Map.keysSet shapeUpdate ^? liveCoords :: Maybe liveliness)
return (shapeUpdate <$ delRes, delView)
delResults <- fmap (Map.mapMaybe id) . sequence $ Map.fromSet (runMaybeT . delForm) (Map.keysSet addedShape)
let delShape
| [FormSuccess shapeUpdate'] <- Map.elems . Map.filter (is _FormSuccess) $ fmap fst delResults = traverse (flip Map.lookup addedShape) shapeUpdate'
| otherwise = Nothing
shape <- if
| Just s <- delShape -> return s
| Just s <- addShape -> return s
| otherwise -> return sentShape'
shapeId <- newIdent
let
shapeInput = fieldView shapeField shapeId (toPathPiece shapeName) [] (Right shape) True
cellResults <- flip Map.traverseWithKey shape $ \miCoord cData -> do
let
nudgeCellName :: Text -> Text
nudgeCellName miCellField = toPathPiece (MassInputCell{..} :: MassInputFieldName (BoxCoord liveliness))
(cData, ) <$> miCell miCoord cData (fmap snd . Map.lookup miCoord =<< initialResult) nudgeCellName mempty
let result :: FormResult (Map (BoxCoord liveliness) (cellData, cellResult))
result
| isJust addShape || isJust delShape = FormMissing
| otherwise = traverse (\(cData, (cResult, _)) -> (cData, ) <$> cResult) cellResults
liveliness <- maybe (throwM MassInputInvalidShape) return $ Map.keysSet shape ^? liveCoords :: MForm handler liveliness
let miWidget :: BoxCoord liveliness -> [(Natural, BoxDimension (BoxCoord liveliness))] -> Widget let miWidget :: BoxCoord liveliness -> [(Natural, BoxDimension (BoxCoord liveliness))] -> Widget
miWidget _ [] = mempty miWidget _ [] = mempty
@ -182,6 +298,7 @@ massInput mkAddWidget mkCellWidget FieldSettings{..} fvRequired initialResult =
| [] <- remDims = do | [] <- remDims = do
coord <- coords coord <- coords
Just (_data, (_cellRes, cellWdgt)) <- return $ Map.lookup coord cellResults Just (_data, (_cellRes, cellWdgt)) <- return $ Map.lookup coord cellResults
let deleteButton = snd <$> Map.lookup coord delResults
return (coord, $(widgetFile "widgets/massinput/cell")) return (coord, $(widgetFile "widgets/massinput/cell"))
| otherwise = | otherwise =
[ (coord, miWidget coord remDims) | coord <- coords ] [ (coord, miWidget coord remDims) | coord <- coords ]
@ -194,6 +311,10 @@ massInput mkAddWidget mkCellWidget FieldSettings{..} fvRequired initialResult =
let let
fvLabel = toHtml $ mr fsLabel fvLabel = toHtml $ mr fsLabel
fvTooltip = toHtml . mr <$> fsTooltip fvTooltip = toHtml . mr <$> fsTooltip
fvInput = miWidget boxOrigin $ zip [0..] boxDimensions fvInput = mconcat
[ toWidget csrf
, shapeInput
, miWidget boxOrigin $ zip [0..] boxDimensions
]
fvErrors = Nothing fvErrors = Nothing
return (result, FieldView{..}) in return (result, FieldView{..})

View File

@ -497,7 +497,7 @@ assertM f x = x >>= assertM' f
assertM_ :: MonadPlus m => (a -> Bool) -> m a -> m () assertM_ :: MonadPlus m => (a -> Bool) -> m a -> m ()
assertM_ f x = guard . f =<< x assertM_ f x = guard . f =<< x
assertM' :: MonadPlus m => (a -> Bool) -> a -> m a assertM' :: Alternative m => (a -> Bool) -> a -> m a
assertM' f x = x <$ guard (f x) assertM' f x = x <$ guard (f x)
-- Some Utility Functions from Agda.Utils.Monad -- Some Utility Functions from Agda.Utils.Monad

View File

@ -250,6 +250,7 @@ identifyForm' resLens identVal form fragment = do
identifyForm :: (Monad m, PathPiece ident, Eq ident) => ident -> (Html -> MForm m (FormResult a, widget)) -> (Html -> MForm m (FormResult a, widget)) identifyForm :: (Monad m, PathPiece ident, Eq ident) => ident -> (Html -> MForm m (FormResult a, widget)) -> (Html -> MForm m (FormResult a, widget))
identifyForm = identifyForm' id identifyForm = identifyForm' id
{- Hinweise zur Erinnerung: {- Hinweise zur Erinnerung:
- identForm primär, wenn es mehr als ein Formular pro Handler gibt - identForm primär, wenn es mehr als ein Formular pro Handler gibt
- nur einmal pro makeForm reicht - nur einmal pro makeForm reicht
@ -573,6 +574,12 @@ apreq f fs mx = formToAForm $ do
mr <- getMessageRender mr <- getMessageRender
over _1 (maybe (FormFailure [mr MsgValueRequired]) return =<<) . over _2 (pure . (\fv -> fv { fvRequired = True } )) <$> mopt f fs (Just <$> mx) over _1 (maybe (FormFailure [mr MsgValueRequired]) return =<<) . over _2 (pure . (\fv -> fv { fvRequired = True } )) <$> mopt f fs (Just <$> mx)
mpreq :: (RenderMessage site FormMessage, HandlerSite m ~ site, MonadHandler m)
=> Field m a -> FieldSettings site -> Maybe a -> MForm m (FormResult a, FieldView site)
mpreq f fs mx = do
mr <- getMessageRender
over _1 (maybe (FormFailure [mr MsgValueRequired]) return =<<) . over _2 (\fv -> fv { fvRequired = True } ) <$> mopt f fs (Just <$> mx)
wpreq :: (RenderMessage site FormMessage, HandlerSite m ~ site, MonadHandler m) wpreq :: (RenderMessage site FormMessage, HandlerSite m ~ site, MonadHandler m)
=> Field m a -> FieldSettings site -> Maybe a -> WForm m (FormResult a) => Field m a -> FieldSettings site -> Maybe a -> WForm m (FormResult a)
wpreq f fs mx = mFormToWForm $ do wpreq f fs mx = mFormToWForm $ do

View File

@ -80,6 +80,8 @@ makeLenses_ ''SheetType
makePrisms ''AuthResult makePrisms ''AuthResult
makePrisms ''FormResult
-- makeClassy_ ''Load -- makeClassy_ ''Load

View File

@ -1 +1,3 @@
^{cellWdgt} ^{cellWdgt}
$maybe delWdgt <- fmap fvInput deleteButton
^{delWdgt}