packages feed

yesod-crud-persist-0.2.1: src/Yesod/Crud/Simple/Generic.hs

module Yesod.Crud.Simple.Generic where

import Prelude
import Yesod.Crud
import Yesod.Crud.Simple
import Yesod.Core
import Yesod.Form
import Yesod.Form.Bootstrap3
import GHC.Generics
import qualified Data.Text as Text
import Data.Text (Text)
import Data.Char (isLower)
import Database.Persist
import Database.Persist.Sql
import Yesod.Persist
import Data.Either.Combinators
import Data.Time
import Yesod.Markdown

class HasName a where
  gcrudName :: a -> Text
  gcrudNameField :: EntityField a Text

class GCrud c where
  gcrudForm :: (Yesod site, YesodPersist site, YesodPersistBackend site ~ SqlBackend, m ~ HandlerT site IO, MonadHandler m, RenderMessage (HandlerSite m) FormMessage) => Maybe c -> AForm m c

-- instance GCrud (U1 p) where
--   gcrudForm _ = return (FormSuccess U1, mempty)

instance (GCrud (f p), GCrud (g p)) => GCrud ((:*:) f g p) where
  gcrudForm m = case m of
    Just (a :*: b) -> (:*:) <$> gcrudForm (Just a) <*> gcrudForm (Just b)
    Nothing ->        (:*:) <$> gcrudForm Nothing <*> gcrudForm Nothing

instance (GCrud (f p)) => GCrud (M1 D b f p) where
  gcrudForm m = fmap M1 . gcrudForm $ fmap unM1 m

instance (GCrud (f p)) => GCrud (M1 C b f p) where
  gcrudForm m = fmap M1 . gcrudForm $ fmap unM1 m

instance (Selector b, GCrudNamed c) => GCrud (M1 S b (K1 R c) p) where
  gcrudForm m = let lbl = Text.pack $ selName (undefined :: M1 S b (K1 R c) ()) in
    fmap (M1 . K1) $ case m of
      Just (M1 (K1 c)) -> gcrudFormNamed lbl (Just c)
      Nothing -> gcrudFormNamed lbl Nothing

class GCrudNamed c where
  gcrudFormNamed :: (Yesod site, YesodPersist site, YesodPersistBackend site ~ SqlBackend, m ~ HandlerT site IO, MonadHandler m, RenderMessage (HandlerSite m) FormMessage) => Text -> Maybe c -> AForm m c

instance GCrudNamed Markdown where
  gcrudFormNamed lbl m = areq markdownField (bfs $ Text.dropWhile isLower lbl) m

instance GCrudNamed Text where
  gcrudFormNamed lbl m = areq textField (bfs $ Text.dropWhile isLower lbl) m

instance GCrudNamed Day where
  gcrudFormNamed lbl m = areq dayField (bfs $ Text.dropWhile isLower lbl) m

instance GCrudNamed [Text] where
  gcrudFormNamed lbl m = areq (convertField (Text.splitOn " ") (Text.intercalate " ") textField) (bfs $ Text.dropWhile isLower lbl) m

instance GCrudNamed Int where
  gcrudFormNamed lbl m = areq intField (bfs $ Text.dropWhile isLower lbl) m

instance (HasName a, Eq (Key a), PathPiece (Key a), PersistEntity a, PersistEntityBackend a ~ SqlBackend) => GCrudNamed (Key a) where
  gcrudFormNamed lbl m = areq (selectField genericNamedOpts) (bfs $ Text.dropWhile isLower lbl) m

applyGenericForm :: (Yesod master, Generic c, GCrud (Rep c ()), RenderMessage master FormMessage, YesodPersist master, YesodPersistBackend master ~ SqlBackend)
  => SimpleCrud master p c -> SimpleCrud master p c
applyGenericForm sc = sc
  { _scForm = \e -> let m = rightToMaybe e in renderBootstrap3 BootstrapBasicForm $ id
      <$> (fmap to (gcrudForm (fmap from' m))) 
      <*  bootstrapSubmit ("Submit" :: BootstrapSubmit Text)
  }
  where from' :: Generic a => a -> Rep a ()
        from' = from  -- A hack to stop the type checker from whining about p

genericForm :: (Yesod master, Generic a, GCrud (Rep a ()), RenderMessage master FormMessage, YesodPersist master, YesodPersistBackend master ~ SqlBackend) 
  => Maybe a -> Html -> MForm (HandlerT master IO) (FormResult a, WidgetT master IO ())
genericForm m = renderBootstrap3 BootstrapBasicForm $ id
  <$> (fmap to (gcrudForm (fmap from' m))) 
  <*  bootstrapSubmit ("Submit" :: BootstrapSubmit Text)
  where from' :: Generic a => a -> Rep a ()
        from' = from  -- A hack to stop the type checker from whining about p

genericIndex :: (PersistCrudEntity site c, HasName c)
  => p -> HandlerT (Crud site p c) (HandlerT site IO) Html
genericIndex = basicSimpleCrudIndex (toWidget . toHtml . gcrudName . entityVal)

genericNamedOpts :: (PersistCrudEntity site c, HasName c) => HandlerT site IO (OptionList (Key c))
genericNamedOpts = optionsPersistKey [] [Asc gcrudNameField] gcrudName