mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-12-25 18:13:52 +03:00
Implemented List component for entity. (#32)
This commit is contained in:
parent
c9d52d6c20
commit
46ed578d36
@ -7,6 +7,7 @@ import * as {= entityLowerName =}State from '{= entityStatePath =}'
|
|||||||
import * as {= entityLowerName =}Actions from '{= entityActionsPath =}'
|
import * as {= entityLowerName =}Actions from '{= entityActionsPath =}'
|
||||||
import {= entity.name =} from '{= entityClassPath =}'
|
import {= entity.name =} from '{= entityClassPath =}'
|
||||||
import {= entity.name =}CreateForm from '{= entityCreateFormPath =}'
|
import {= entity.name =}CreateForm from '{= entityCreateFormPath =}'
|
||||||
|
import {= entity.name =}List from '{= entityListPath =}'
|
||||||
{=/ entities =}
|
{=/ entities =}
|
||||||
|
|
||||||
|
|
||||||
|
68
stic/data/Generator/templates/react-app/src/entities/_entity/components/List.js
vendored
Normal file
68
stic/data/Generator/templates/react-app/src/entities/_entity/components/List.js
vendored
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
{{={= =}=}}
|
||||||
|
import _ from 'lodash'
|
||||||
|
import React from 'react'
|
||||||
|
import { connect } from 'react-redux'
|
||||||
|
|
||||||
|
import Paper from '@material-ui/core/Paper'
|
||||||
|
import Table from '@material-ui/core/Table'
|
||||||
|
import TableBody from '@material-ui/core/TableBody'
|
||||||
|
import TableCell from '@material-ui/core/TableCell'
|
||||||
|
import TableHead from '@material-ui/core/TableHead'
|
||||||
|
import TableRow from '@material-ui/core/TableRow'
|
||||||
|
|
||||||
|
import * as {= entityLowerName =}State from '../state'
|
||||||
|
|
||||||
|
import {= entityClassName =} from '../{= entityClassName =}'
|
||||||
|
|
||||||
|
|
||||||
|
export class List extends React.Component {
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div style={ { margin: '20px' } }>
|
||||||
|
<Paper>
|
||||||
|
<Table>
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
{=# entityTypedFields =}
|
||||||
|
{=# boolean =}
|
||||||
|
<TableCell>{= name =} (bool)</TableCell>
|
||||||
|
{=/ boolean =}
|
||||||
|
{=# string =}
|
||||||
|
<TableCell>{= name =} (string)</TableCell>
|
||||||
|
{=/ string =}
|
||||||
|
{=/ entityTypedFields =}
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
|
||||||
|
<TableBody>
|
||||||
|
{this.props.{= entityLowerName =}List.map({= entityLowerName =} => (
|
||||||
|
<TableRow>
|
||||||
|
{=# entityTypedFields =}
|
||||||
|
{=# boolean =}
|
||||||
|
<TableCell>
|
||||||
|
{{= entityLowerName =}.{= name =} || 'no bool value'}
|
||||||
|
</TableCell>
|
||||||
|
{=/ boolean =}
|
||||||
|
{=# string =}
|
||||||
|
<TableCell>
|
||||||
|
{{= entityLowerName =}.{= name =} || 'no string value'}
|
||||||
|
</TableCell>
|
||||||
|
{=/ string =}
|
||||||
|
{=/ entityTypedFields =}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connect(state => ({
|
||||||
|
// Selectors
|
||||||
|
{= entityLowerName =}List: {= entityLowerName =}State.selectors.all(state)
|
||||||
|
}), {
|
||||||
|
// Actions
|
||||||
|
})(List)
|
@ -6,6 +6,7 @@ module Generator.EntityGenerator
|
|||||||
, entityStatePathInSrc
|
, entityStatePathInSrc
|
||||||
, entityActionsPathInSrc
|
, entityActionsPathInSrc
|
||||||
, entityCreateFormPathInSrc
|
, entityCreateFormPathInSrc
|
||||||
|
, entityListPathInSrc
|
||||||
|
|
||||||
-- EXPORTED FOR TESTING:
|
-- EXPORTED FOR TESTING:
|
||||||
, generateEntityClass
|
, generateEntityClass
|
||||||
@ -17,6 +18,7 @@ module Generator.EntityGenerator
|
|||||||
|
|
||||||
import Data.Aeson ((.=), object)
|
import Data.Aeson ((.=), object)
|
||||||
import qualified Data.Aeson as Aeson
|
import qualified Data.Aeson as Aeson
|
||||||
|
import qualified Data.Text as Text
|
||||||
import System.FilePath (FilePath, (</>), (<.>))
|
import System.FilePath (FilePath, (</>), (<.>))
|
||||||
|
|
||||||
import qualified Util
|
import qualified Util
|
||||||
@ -56,6 +58,7 @@ generateEntityActions wasp entity
|
|||||||
generateEntityComponents :: Wasp -> Entity -> [FileDraft]
|
generateEntityComponents :: Wasp -> Entity -> [FileDraft]
|
||||||
generateEntityComponents wasp entity =
|
generateEntityComponents wasp entity =
|
||||||
[ generateEntityCreateForm wasp entity
|
[ generateEntityCreateForm wasp entity
|
||||||
|
, generateEntityList wasp entity
|
||||||
]
|
]
|
||||||
|
|
||||||
-- TODO: add tests / update tests.
|
-- TODO: add tests / update tests.
|
||||||
@ -76,6 +79,13 @@ generateEntityCreateForm wasp entity
|
|||||||
= createSimpleEntityFileDraft wasp entity (entityCreateFormPathInSrc entity)
|
= createSimpleEntityFileDraft wasp entity (entityCreateFormPathInSrc entity)
|
||||||
("components" </> "CreateForm.js")
|
("components" </> "CreateForm.js")
|
||||||
|
|
||||||
|
-- TODO(matija): do I need wasp at all?
|
||||||
|
-- | Generates list component for the specified entity, so user can see all the
|
||||||
|
-- entity instances.
|
||||||
|
generateEntityList :: Wasp -> Entity -> FileDraft
|
||||||
|
generateEntityList wasp entity
|
||||||
|
= createSimpleEntityFileDraft wasp entity (entityListPathInSrc entity)
|
||||||
|
("components" </> "List.js")
|
||||||
|
|
||||||
-- | Helper function that captures common logic for generating entity file draft.
|
-- | Helper function that captures common logic for generating entity file draft.
|
||||||
createSimpleEntityFileDraft :: Wasp -> Entity -> FilePath -> FilePath -> FileDraft
|
createSimpleEntityFileDraft :: Wasp -> Entity -> FilePath -> FilePath -> FileDraft
|
||||||
@ -86,6 +96,22 @@ createSimpleEntityFileDraft wasp entity dstPathInSrc srcPathInEntityTemplatesDir
|
|||||||
dstPath = "src" </> dstPathInSrc
|
dstPath = "src" </> dstPathInSrc
|
||||||
templateData = entityTemplateData wasp entity
|
templateData = entityTemplateData wasp entity
|
||||||
|
|
||||||
|
{- | Converts entity field to a JSON where field type is a key to the object holding
|
||||||
|
all the other properties. E.g. a field of type boolean could look this as JSON:
|
||||||
|
|
||||||
|
{ boolean: { name: "description", type: "boolean" }
|
||||||
|
|
||||||
|
This method is needed to achieve conditional rendering with Mustache.
|
||||||
|
-}
|
||||||
|
entityFieldToJsonWithTypeAsKey :: EntityField -> Aeson.Value
|
||||||
|
entityFieldToJsonWithTypeAsKey entityField = object
|
||||||
|
-- TODO(matija): maybe it would be cleaner to have a flat structure, like
|
||||||
|
-- { boolean: true, type: "boolean", name: "description" }
|
||||||
|
[ (toText $ entityFieldType entityField) .= entityField
|
||||||
|
]
|
||||||
|
where
|
||||||
|
toText = Text.pack . show
|
||||||
|
|
||||||
-- | Default generic data for entity templates.
|
-- | Default generic data for entity templates.
|
||||||
entityTemplateData :: Wasp -> Entity -> Aeson.Value
|
entityTemplateData :: Wasp -> Entity -> Aeson.Value
|
||||||
entityTemplateData wasp entity = object
|
entityTemplateData wasp entity = object
|
||||||
@ -95,6 +121,7 @@ entityTemplateData wasp entity = object
|
|||||||
-- TODO: this entityClassName is used only in CreateForm, use it also when creating
|
-- TODO: this entityClassName is used only in CreateForm, use it also when creating
|
||||||
-- Class file itself and in other files.
|
-- Class file itself and in other files.
|
||||||
, "entityClassName" .= (Util.toUpperFirst $ entityName entity)
|
, "entityClassName" .= (Util.toUpperFirst $ entityName entity)
|
||||||
|
, "entityTypedFields" .= map entityFieldToJsonWithTypeAsKey (entityFields entity)
|
||||||
]
|
]
|
||||||
|
|
||||||
-- | Location in templates where entity related templates reside.
|
-- | Location in templates where entity related templates reside.
|
||||||
@ -119,8 +146,13 @@ entityActionTypesPathInSrc entity = (entityDirPathInSrc entity) </> "actionTypes
|
|||||||
entityClassPathInSrc :: Entity -> FilePath
|
entityClassPathInSrc :: Entity -> FilePath
|
||||||
entityClassPathInSrc entity = (entityDirPathInSrc entity) </> (entityName entity) <.> "js"
|
entityClassPathInSrc entity = (entityDirPathInSrc entity) </> (entityName entity) <.> "js"
|
||||||
|
|
||||||
|
-- * Components
|
||||||
|
|
||||||
entityComponentsDirPathInSrc :: Entity -> FilePath
|
entityComponentsDirPathInSrc :: Entity -> FilePath
|
||||||
entityComponentsDirPathInSrc entity = (entityDirPathInSrc entity) </> "components"
|
entityComponentsDirPathInSrc entity = (entityDirPathInSrc entity) </> "components"
|
||||||
|
|
||||||
entityCreateFormPathInSrc :: Entity -> FilePath
|
entityCreateFormPathInSrc :: Entity -> FilePath
|
||||||
entityCreateFormPathInSrc entity = (entityComponentsDirPathInSrc entity) </> "CreateForm.js"
|
entityCreateFormPathInSrc entity = (entityComponentsDirPathInSrc entity) </> "CreateForm.js"
|
||||||
|
|
||||||
|
entityListPathInSrc :: Entity -> FilePath
|
||||||
|
entityListPathInSrc entity = (entityComponentsDirPathInSrc entity) </> "List.js"
|
||||||
|
@ -35,4 +35,5 @@ generatePage wasp page = createTemplateFileDraft dstPath srcPath templateData
|
|||||||
, "entityActionsPath" .= ("./" ++ (EntityGenerator.entityActionsPathInSrc entity))
|
, "entityActionsPath" .= ("./" ++ (EntityGenerator.entityActionsPathInSrc entity))
|
||||||
, "entityClassPath" .= ("./" ++ (EntityGenerator.entityClassPathInSrc entity))
|
, "entityClassPath" .= ("./" ++ (EntityGenerator.entityClassPathInSrc entity))
|
||||||
, "entityCreateFormPath" .= ("./" ++ (EntityGenerator.entityCreateFormPathInSrc entity))
|
, "entityCreateFormPath" .= ("./" ++ (EntityGenerator.entityCreateFormPathInSrc entity))
|
||||||
|
, "entityListPath" .= ("./" ++ (EntityGenerator.entityListPathInSrc entity))
|
||||||
]
|
]
|
||||||
|
@ -5,6 +5,7 @@ module Generator.Templates
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Text.Mustache as Mustache
|
import qualified Text.Mustache as Mustache
|
||||||
|
import Text.Mustache.Render (SubstitutionError(..))
|
||||||
import qualified Data.Aeson as Aeson
|
import qualified Data.Aeson as Aeson
|
||||||
import System.FilePath ((</>))
|
import System.FilePath ((</>))
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
@ -50,11 +51,23 @@ compileMustacheTemplate templateRelPath = do
|
|||||||
raiseCompileError err = error $ -- TODO: Handle these errors better?
|
raiseCompileError err = error $ -- TODO: Handle these errors better?
|
||||||
printf "Compilation of template %s failed. %s" templateRelPath (show err)
|
printf "Compilation of template %s failed. %s" templateRelPath (show err)
|
||||||
|
|
||||||
|
areAllErrorsSectionDataNotFound :: [SubstitutionError] -> Bool
|
||||||
|
areAllErrorsSectionDataNotFound subsErrors = all isSectionDataNotFoundError subsErrors
|
||||||
|
where
|
||||||
|
isSectionDataNotFoundError e = case e of
|
||||||
|
SectionTargetNotFound _ -> True
|
||||||
|
_ -> False
|
||||||
|
|
||||||
renderMustacheTemplate :: Mustache.Template -> Aeson.Value -> IO Text
|
renderMustacheTemplate :: Mustache.Template -> Aeson.Value -> IO Text
|
||||||
renderMustacheTemplate mustacheTemplate templateData = do
|
renderMustacheTemplate mustacheTemplate templateData = do
|
||||||
let mustacheTemplateData = Mustache.toMustache templateData
|
let mustacheTemplateData = Mustache.toMustache templateData
|
||||||
let (errors, fileText) =
|
let (errors, fileText) =
|
||||||
Mustache.checkedSubstituteValue mustacheTemplate mustacheTemplateData
|
Mustache.checkedSubstituteValue mustacheTemplate mustacheTemplateData
|
||||||
if (null errors) -- TODO: Handle these errors better.
|
|
||||||
|
-- NOTE(matija): Mustache reports errors when object does
|
||||||
|
-- not have a property specified in the template, which we use to implement
|
||||||
|
-- conditionals. This is why we ignore these errors.
|
||||||
|
if (null errors) || (areAllErrorsSectionDataNotFound errors)
|
||||||
then (return fileText)
|
then (return fileText)
|
||||||
else (error $ "Errors occured while rendering template: " ++ (show errors))
|
else (error $ "Unexpected errors occured while rendering template: "
|
||||||
|
++ (show errors))
|
||||||
|
@ -20,6 +20,8 @@ module Wasp
|
|||||||
) where
|
) where
|
||||||
|
|
||||||
import Data.Aeson ((.=), object, ToJSON(..))
|
import Data.Aeson ((.=), object, ToJSON(..))
|
||||||
|
import qualified Data.Aeson as Aeson
|
||||||
|
import qualified Data.Text as T
|
||||||
|
|
||||||
|
|
||||||
-- * Wasp
|
-- * Wasp
|
||||||
@ -88,7 +90,11 @@ data EntityField = EntityField
|
|||||||
, entityFieldType :: !EntityFieldType
|
, entityFieldType :: !EntityFieldType
|
||||||
} deriving (Show, Eq)
|
} deriving (Show, Eq)
|
||||||
|
|
||||||
data EntityFieldType = EftString | EftBoolean deriving (Show, Eq)
|
data EntityFieldType = EftString | EftBoolean deriving (Eq)
|
||||||
|
|
||||||
|
instance Show EntityFieldType where
|
||||||
|
show EftString = "string"
|
||||||
|
show EftBoolean = "boolean"
|
||||||
|
|
||||||
getEntities :: Wasp -> [Entity]
|
getEntities :: Wasp -> [Entity]
|
||||||
getEntities (Wasp elems) = [entity | (WaspElementEntity entity) <- elems]
|
getEntities (Wasp elems) = [entity | (WaspElementEntity entity) <- elems]
|
||||||
@ -96,6 +102,7 @@ getEntities (Wasp elems) = [entity | (WaspElementEntity entity) <- elems]
|
|||||||
addEntity :: Wasp -> Entity -> Wasp
|
addEntity :: Wasp -> Entity -> Wasp
|
||||||
addEntity (Wasp elems) entity = Wasp $ (WaspElementEntity entity):elems
|
addEntity (Wasp elems) entity = Wasp $ (WaspElementEntity entity):elems
|
||||||
|
|
||||||
|
|
||||||
-- * ToJSON instances.
|
-- * ToJSON instances.
|
||||||
|
|
||||||
-- NOTE(martin): Here I define general transformation of App into JSON that I can then easily use
|
-- NOTE(martin): Here I define general transformation of App into JSON that I can then easily use
|
||||||
@ -128,8 +135,7 @@ instance ToJSON EntityField where
|
|||||||
]
|
]
|
||||||
|
|
||||||
instance ToJSON EntityFieldType where
|
instance ToJSON EntityFieldType where
|
||||||
toJSON EftString = "string"
|
toJSON = Aeson.String . T.pack . show
|
||||||
toJSON EftBoolean = "boolean"
|
|
||||||
|
|
||||||
instance ToJSON Wasp where
|
instance ToJSON Wasp where
|
||||||
toJSON wasp = object
|
toJSON wasp = object
|
||||||
|
@ -37,6 +37,9 @@ spec_parseWasp =
|
|||||||
\ submitButtonLabel={'Create new task'}\n\
|
\ submitButtonLabel={'Create new task'}\n\
|
||||||
\ />\n\
|
\ />\n\
|
||||||
\ </div>\n\
|
\ </div>\n\
|
||||||
|
\\n\
|
||||||
|
\ My tasks\n\
|
||||||
|
\ <TaskList />\n\
|
||||||
\ </div>"
|
\ </div>"
|
||||||
}
|
}
|
||||||
, WaspElementPage $ Page
|
, WaspElementPage $ Page
|
||||||
|
@ -19,6 +19,9 @@ page Landing {
|
|||||||
submitButtonLabel={'Create new task'}
|
submitButtonLabel={'Create new task'}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
My tasks
|
||||||
|
<TaskList />
|
||||||
</div>
|
</div>
|
||||||
jsx=}
|
jsx=}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user