module IHP.IDE.Data.View.EditRow where import IHP.ViewPrelude import IHP.IDE.ToolServer.Types import IHP.IDE.Data.View.ShowDatabase import IHP.IDE.Data.View.Layout import Data.Maybe import qualified Data.Text as T import qualified Data.ByteString as BS data EditRowView = EditRowView { EditRowView -> [Text] tableNames :: [Text] , EditRowView -> Text tableName :: Text , EditRowView -> [[DynamicField]] rows :: [[DynamicField]] , EditRowView -> [ColumnDefinition] tableCols :: [ColumnDefinition] , EditRowView -> [DynamicField] rowValues :: [DynamicField] , EditRowView -> [Text] primaryKeyFields :: [Text] , EditRowView -> Text targetPrimaryKey :: Text } instance View EditRowView where html :: EditRowView -> Html html EditRowView { [[DynamicField]] [Text] [ColumnDefinition] [DynamicField] Text targetPrimaryKey :: Text primaryKeyFields :: [Text] rowValues :: [DynamicField] tableCols :: [ColumnDefinition] rows :: [[DynamicField]] tableName :: Text tableNames :: [Text] $sel:targetPrimaryKey:EditRowView :: EditRowView -> Text $sel:primaryKeyFields:EditRowView :: EditRowView -> [Text] $sel:rowValues:EditRowView :: EditRowView -> [DynamicField] $sel:tableCols:EditRowView :: EditRowView -> [ColumnDefinition] $sel:rows:EditRowView :: EditRowView -> [[DynamicField]] $sel:tableName:EditRowView :: EditRowView -> Text $sel:tableNames:EditRowView :: EditRowView -> [Text] .. } = [hsx| <div class="h-100"> {headerNav} <div class="h-100 row no-gutters"> {renderTableSelector tableNames tableName} <div class="col" style="overflow: scroll; max-height: 80vh"> {renderRows rows tableBody tableName} </div> </div> </div> {renderModal modal} |] where tableBody :: Html tableBody = [hsx|<tbody>{forEach rows renderRow}</tbody>|] renderRow :: [DynamicField] -> Html renderRow [DynamicField] fields = [hsx|<tr>{forEach fields (renderField id)}</tr>|] where id :: Text id = (ByteString -> Text forall a b. ConvertibleStrings a b => a -> b cs (ByteString -> Maybe ByteString -> ByteString forall a. a -> Maybe a -> a fromMaybe ByteString "" (Proxy "fieldValue" -> DynamicField -> Maybe ByteString forall model (name :: Symbol) value. (KnownSymbol name, HasField name model value) => Proxy name -> model -> value get IsLabel "fieldValue" (Proxy "fieldValue") Proxy "fieldValue" #fieldValue (Maybe DynamicField -> DynamicField forall a. HasCallStack => Maybe a -> a fromJust ([DynamicField] -> Maybe DynamicField forall a. [a] -> Maybe a headMay [DynamicField] fields))))) renderField :: Text -> DynamicField -> Html renderField Text id DynamicField { Maybe ByteString ByteString $sel:fieldName:DynamicField :: DynamicField -> ByteString $sel:fieldValue:DynamicField :: DynamicField -> Maybe ByteString fieldName :: ByteString fieldValue :: Maybe ByteString .. } | ByteString fieldName ByteString -> ByteString -> Bool forall a. Eq a => a -> a -> Bool == ByteString "id" = [hsx|<td><span data-fieldname={fieldName}><a class="no-link border rounded p-1" href={EditRowValueAction tableName (cs fieldName) id}>{renderId (sqlValueToText fieldValue)}</a></span></td>|] renderField Text id DynamicField { Maybe ByteString ByteString fieldName :: ByteString fieldValue :: Maybe ByteString $sel:fieldName:DynamicField :: DynamicField -> ByteString $sel:fieldValue:DynamicField :: DynamicField -> Maybe ByteString .. } | ByteString -> [ColumnDefinition] -> Bool forall a1 a2 a3 (t :: * -> *) model. (Eq a1, Eq a2, ConvertibleStrings a3 a2, Foldable t, HasField "columnName" model a2, HasField "columnType" model a1, IsString a1) => a3 -> t model -> Bool isBoolField ByteString fieldName [ColumnDefinition] tableCols Bool -> Bool -> Bool && Bool -> Bool not (Maybe ByteString -> Bool forall a. Maybe a -> Bool isNothing Maybe ByteString fieldValue) = [hsx|<td><span data-fieldname={fieldName}><input type="checkbox" onclick={onClick tableName fieldName id} checked={sqlValueToText fieldValue == "t"} /></span></td>|] renderField Text id DynamicField { Maybe ByteString ByteString fieldName :: ByteString fieldValue :: Maybe ByteString $sel:fieldName:DynamicField :: DynamicField -> ByteString $sel:fieldValue:DynamicField :: DynamicField -> Maybe ByteString .. } = [hsx|<td><span data-fieldname={fieldName}><a class="no-link" href={EditRowValueAction tableName (cs fieldName) id}>{sqlValueToText fieldValue}</a></span></td>|] modalContent :: Html modalContent = [hsx| <form method="POST" action={UpdateRowAction}> <input type="hidden" name="tableName" value={tableName}/> {forEach (zip tableCols rowValues) renderFormField} {forEach (zip primaryKeyFields (T.splitOn "---" targetPrimaryKey)) renderPrimaryKeyInput} <div class="text-right"> <button type="submit" class="btn btn-primary">Edit Row</button> </div> </form> |] modalFooter :: Maybe Html modalFooter = Maybe Html forall a. Monoid a => a mempty modalCloseUrl :: Text modalCloseUrl = DataController -> Text forall controller. HasPath controller => controller -> Text pathTo ShowTableRowsAction :: Text -> DataController ShowTableRowsAction { Text $sel:tableName:ShowDatabaseAction :: Text tableName :: Text tableName } modalTitle :: Text modalTitle = Text "Edit Row" modal :: Modal modal = Modal :: Html -> Maybe Html -> Text -> Text -> Modal Modal { Html $sel:modalContent:Modal :: Html modalContent :: Html modalContent, Maybe Html $sel:modalFooter:Modal :: Maybe Html modalFooter :: Maybe Html modalFooter, Text $sel:modalCloseUrl:Modal :: Text modalCloseUrl :: Text modalCloseUrl, Text $sel:modalTitle:Modal :: Text modalTitle :: Text modalTitle } renderPrimaryKeyInput :: (value, value) -> Html renderPrimaryKeyInput (value primaryKeyField, value primaryKeyValue) = [hsx|<input type="hidden" name={primaryKeyField <> "-pk"} value={primaryKeyValue}>|] renderFormField :: (ColumnDefinition, DynamicField) -> Html renderFormField :: (ColumnDefinition, DynamicField) -> (?context::ControllerContext) => Html renderFormField (ColumnDefinition def, DynamicField val) = [hsx| <div class="form-group"> <label class="row-form">{get #columnName def}</label> <span style="float:right;"> <a class="text-muted row-form">{get #columnType def}</a> </span> <div class="input-group"> {renderInputMethod (def, val)} </div> </div>|] onClick :: Text -> a -> Text -> Text onClick Text tableName a fieldName Text id = Text "window.location.assign(" Text -> Text -> Text forall a. Semigroup a => a -> a -> a <> Text -> Text forall a. Show a => a -> Text tshow (DataController -> Text forall controller. HasPath controller => controller -> Text pathTo (Text -> Text -> Text -> DataController ToggleBooleanFieldAction Text tableName (a -> Text forall a b. ConvertibleStrings a b => a -> b cs a fieldName) Text id)) Text -> Text -> Text forall a. Semigroup a => a -> a -> a <> Text ")" renderInputMethod :: (ColumnDefinition, DynamicField) -> Html renderInputMethod :: (ColumnDefinition, DynamicField) -> (?context::ControllerContext) => Html renderInputMethod (ColumnDefinition def, DynamicField val) | (Proxy "columnType" -> ColumnDefinition -> Text forall model (name :: Symbol) value. (KnownSymbol name, HasField name model value) => Proxy name -> model -> value get IsLabel "columnType" (Proxy "columnType") Proxy "columnType" #columnType ColumnDefinition def) Text -> Text -> Bool forall a. Eq a => a -> a -> Bool == Text "boolean" Bool -> Bool -> Bool && Maybe ByteString -> Bool forall a. Maybe a -> Bool isNothing (Proxy "fieldValue" -> DynamicField -> Maybe ByteString forall model (name :: Symbol) value. (KnownSymbol name, HasField name model value) => Proxy name -> model -> value get IsLabel "fieldValue" (Proxy "fieldValue") Proxy "fieldValue" #fieldValue DynamicField val) = [hsx| {isBooleanParam True def} <input id={get #columnName def <> "-alt"} type="text" name={get #columnName def} class="form-control text-monospace text-secondary bg-light" value="NULL" /> <div class="form-control" id={get #columnName def <> "-boxcontainer"}> <input id={get #columnName def <> "-input"} type="checkbox" class="d-none" name={get #columnName def <> "-inactive"} checked={(value val) == "t"} /> </div> <input id={get #columnName def <> "-hidden"} type="hidden" name={get #columnName def} value={inputValue False} /> <div class="input-group-append"> <button class="btn dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button> <div class="dropdown-menu dropdown-menu-right custom-menu menu-for-column shadow backdrop-blur"> <a class="dropdown-item" data-value="DEFAULT" data-issql="True" onclick={fillField def "DEFAULT" "true"}>DEFAULT</a> <a class="dropdown-item" data-value="NULL" data-issql="True" onclick={fillField def "NULL" "true"}>NULL</a> <a class="dropdown-item"> <input id={get #columnName def <> "-sqlbox"} type="checkbox" name={get #columnName def <> "_"} checked={True} class="mr-1" onclick={"sqlModeCheckbox('" <> get #columnName def <> "', this, true)"} /> <label class="form-check-label" for={get #columnName def <> "-sqlbox"}> Parse as SQL</label> </a> <input type="hidden" name={get #columnName def <> "_"} value={inputValue False} /> </div> </div> |] renderInputMethod (ColumnDefinition def, DynamicField val) | (Proxy "columnType" -> ColumnDefinition -> Text forall model (name :: Symbol) value. (KnownSymbol name, HasField name model value) => Proxy name -> model -> value get IsLabel "columnType" (Proxy "columnType") Proxy "columnType" #columnType ColumnDefinition def) Text -> Text -> Bool forall a. Eq a => a -> a -> Bool == Text "boolean" = [hsx| {isBooleanParam True def} <input id={get #columnName def <> "-alt"} type="text" name={get #columnName def <> "-inactive"} class="form-control text-monospace text-secondary bg-light d-none" /> <div class="form-control" id={get #columnName def <> "-boxcontainer"}> <input id={get #columnName def <> "-input"} type="checkbox" name={get #columnName def} checked={(value val) == "t"} /> </div> <input id={get #columnName def <> "-hidden"} type="hidden" name={get #columnName def} value={inputValue False} /> <div class="input-group-append"> <button class="btn dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button> <div class="dropdown-menu dropdown-menu-right custom-menu menu-for-column shadow backdrop-blur"> <a class="dropdown-item" data-value="DEFAULT" data-issql="True" onclick={fillField def "DEFAULT" "true"}>DEFAULT</a> <a class="dropdown-item" data-value="NULL" data-issql="True" onclick={fillField def "NULL" "true"}>NULL</a> <a class="dropdown-item"> <input id={get #columnName def <> "-sqlbox"} type="checkbox" name={get #columnName def <> "_"} checked={isSqlFunction (getColDefaultValue def)} class="mr-1" onclick={"sqlModeCheckbox('" <> get #columnName def <> "', this, true)"} /> <label class="form-check-label" for={get #columnName def <> "-sqlbox"}> Parse as SQL</label> </a> <input type="hidden" name={get #columnName def <> "_"} value={inputValue False} /> </div> </div> |] renderInputMethod (ColumnDefinition def, DynamicField val) = [hsx| {isBooleanParam False def} <input id={get #columnName def <> "-input"} type="text" name={get #columnName def} class={classes ["form-control", ("text-monospace text-secondary bg-light", isSqlFunction_ (value val))]} value={value val} oninput={"stopSqlModeOnInput('" <> get #columnName def <> "')"} /> <div class="input-group-append"> <button class="btn dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"></button> <div class="dropdown-menu dropdown-menu-right custom-menu menu-for-column shadow backdrop-blur"> <a class="dropdown-item" data-value="DEFAULT" data-issql="True" onclick={fillField def "DEFAULT" "false"}>DEFAULT</a> <a class="dropdown-item" data-value="NULL" data-issql="True" onclick={fillField def "NULL" "false"}>NULL</a> <a class="dropdown-item"> <input id={get #columnName def <> "-sqlbox"} type="checkbox" name={get #columnName def <> "_"} checked={isSqlFunction_ (value val)} class="mr-1" onclick={"sqlModeCheckbox('" <> get #columnName def <> "', this)"} /> <label class="form-check-label" for={get #columnName def <> "-sqlbox"}> Parse as SQL</label> </a> <input type="hidden" name={get #columnName def <> "_"} value={inputValue False} /> </div> </div>|] value :: model -> ByteString value model val = ByteString -> Maybe ByteString -> ByteString forall a. a -> Maybe a -> a fromMaybe ByteString BS.empty (Proxy "fieldValue" -> model -> Maybe ByteString forall model (name :: Symbol) value. (KnownSymbol name, HasField name model value) => Proxy name -> model -> value get IsLabel "fieldValue" (Proxy "fieldValue") Proxy "fieldValue" #fieldValue model val)