ihp-ide-1.4.0: IHP/IDE/Data/View/ShowTableRows.hs
module IHP.IDE.Data.View.ShowTableRows where
import IHP.ViewPrelude
import IHP.IDE.ToolServer.Types
import IHP.IDE.Data.View.ShowDatabase
import IHP.IDE.Data.View.Layout
import qualified Data.ByteString.Char8 as ByteString
data ShowTableRowsView = ShowTableRowsView
{ tableNames :: [Text]
, tableName :: Text
, rows :: [[DynamicField]]
, tableCols :: [ColumnDefinition]
, primaryKeyFields :: [Text]
, pageSize :: Int
, page :: Int
, totalRows :: Int
}
instance View ShowTableRowsView where
html ShowTableRowsView { .. } = [hsx|
<div class="h-100">
{headerNav}
<div class="h-100 row no-gutters">
{renderTableSelector tableNames tableName}
<div class="col" oncontextmenu="showContextMenu('context-menu-data-root')">
<div style="overflow: scroll; max-height: 80vh">
{whenNonEmpty rows $ renderRows rows tableBody tableName}
{whenEmpty rows emptyState}
</div>
{pageMenu}
</div>
</div>
</div>
<div class="custom-menu menu-for-column shadow backdrop-blur" id="context-menu-data-root">
<a href={NewRowAction tableName}>Add Row</a>
</div>
|]
where
tableBody = [hsx|<tbody>{forEach rows renderRow}</tbody>|]
renderRow fields = [hsx|<tr oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>{forEach fields (renderField primaryKey)}</tr>
<div class="custom-menu menu-for-column shadow backdrop-blur" id={contextMenuId}>
<a href={EditRowAction tableName primaryKey}>Edit Row</a>
<a href={DeleteEntryAction primaryKey tableName} class="js-delete">Delete Row</a>
<div></div>
<a href={NewRowAction tableName}>Add Row</a>
</div>|]
where
contextMenuId = "context-menu-column-" <> tshow primaryKey
primaryKey = intercalate "---" . map (cs . fromMaybe "" . (.fieldValue)) $ filter ((`elem` primaryKeyFields) . cs . (.fieldName)) fields
renderField primaryKey DynamicField { .. }
| fieldName == "id" = [hsx|<td><span data-fieldname={fieldName}><a class="border rounded p-1" href={EditRowValueAction tableName (cs fieldName) primaryKey}>{renderId (sqlValueToText fieldValue)}</a></span></td>|]
| isBoolField fieldName tableCols && not (isNothing fieldValue) = [hsx|<td><span data-fieldname={fieldName}><input type="checkbox" onclick={onClick tableName fieldName primaryKey} checked={sqlValueToText fieldValue == "t"} /></span></td>|]
| otherwise = renderNormalField primaryKey DynamicField { .. }
renderNormalField primaryKey DynamicField { .. } = [hsx|
<td data-foreign-key-column={foreignKeyHoverCardUrl}>
<span data-fieldname={fieldName}>
<a href={EditRowValueAction tableName (cs fieldName) primaryKey}>
{sqlValueToText fieldValue}
</a>
</span>
</td>
|]
where
isForeignKeyColumn = "_id" `ByteString.isSuffixOf` fieldName
foreignKeyHoverCardUrl = if isForeignKeyColumn
then Just $ pathTo ShowForeignKeyHoverCardAction { tableName, id = primaryKey, columnName = cs fieldName }
else Nothing
columnNames = map (.fieldName) (fromMaybe [] (head rows))
onClick tableName fieldName primaryKey = "window.location.assign(" <> tshow (pathTo (ToggleBooleanFieldAction tableName (cs fieldName) primaryKey)) <> ")"
totalPages = [1..ceiling (fromIntegral(totalRows) / fromIntegral(pageSize))]
pageMenu = when (length totalPages > 1)
[hsx|
<div style="position: absolute; bottom: 0; height: 30px" class="d-flex justify-content-center w-100" oncontextmenu="showContextMenu('context-menu-pagination'); event.stopPropagation();">
{backButton}
{forEach (totalPages) renderPageButton}
{nextButton}
</div>
<div class="custom-menu menu-for-column shadow backdrop-blur" id="context-menu-pagination">
<span class="text-muted mx-3">Display Rows</span>
<a href={pathTo (ShowTableRowsAction tableName) <> "&page=" <> show page <> "&rows=20"}>20</a>
<a href={pathTo (ShowTableRowsAction tableName) <> "&page=" <> show page <> "&rows=50"}>50</a>
<a href={pathTo (ShowTableRowsAction tableName) <> "&page=" <> show page <> "&rows=100"}>100</a>
</div>
|]
where
backButton = if page > 1
then [hsx|<a href={pathTo (ShowTableRowsAction tableName) <> "&page=" <> show (page-1) <> "&rows=" <> show pageSize} class="mx-3 text-muted">{"< Back" :: Text}</a>|]
else [hsx|<a class="mx-3 text-muted" style="cursor: not-allowed">{"< Back" :: Text}</a>|]
nextButton = if page < length totalPages
then [hsx|<a href={pathTo (ShowTableRowsAction tableName) <> "&page=" <> show (page+1) <> "&rows=" <> show pageSize} class="mx-3 text-muted">{"Next >" :: Text}</a>|]
else [hsx|<a class="mx-3 text-muted" style="cursor: not-allowed">{"Next >" :: Text}</a>|]
renderPageButton :: Int -> Html
renderPageButton nr = [hsx|<a href={pathTo (ShowTableRowsAction tableName) <> "&page=" <> show nr <> "&rows=" <> show pageSize} class={classes ["mx-2", (if page==nr then "text-dark font-weight-bold" else "text-muted")]}>{nr}</a>|]
emptyState :: Html
emptyState = [hsx|
<div class="d-flex w-100 h-100 justify-content-center mt-5">
<div class="text-center">
<p class="text-muted">This table has no rows yet.</p>
<a href={NewRowAction tableName} class="btn btn-secondary">+ Add first {tableNameToModelName tableName}</a>
</div>
</div>
|]