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
    { ShowTableRowsView -> [Text]
tableNames :: [Text]
    , ShowTableRowsView -> Text
tableName :: Text
    , ShowTableRowsView -> [[DynamicField]]
rows :: [[DynamicField]]
    , ShowTableRowsView -> [ColumnDefinition]
tableCols :: [ColumnDefinition]
    , ShowTableRowsView -> [Text]
primaryKeyFields :: [Text]
    , ShowTableRowsView -> Int
pageSize :: Int
    , ShowTableRowsView -> Int
page :: Int
    , ShowTableRowsView -> Int
totalRows :: Int
    }

instance View ShowTableRowsView where
    html :: ShowTableRowsView -> Html
html ShowTableRowsView { Int
[[DynamicField]]
[Text]
[ColumnDefinition]
Text
totalRows :: Int
page :: Int
pageSize :: Int
primaryKeyFields :: [Text]
tableCols :: [ColumnDefinition]
rows :: [[DynamicField]]
tableName :: Text
tableNames :: [Text]
$sel:totalRows:ShowTableRowsView :: ShowTableRowsView -> Int
$sel:page:ShowTableRowsView :: ShowTableRowsView -> Int
$sel:pageSize:ShowTableRowsView :: ShowTableRowsView -> Int
$sel:primaryKeyFields:ShowTableRowsView :: ShowTableRowsView -> [Text]
$sel:tableCols:ShowTableRowsView :: ShowTableRowsView -> [ColumnDefinition]
$sel:rows:ShowTableRowsView :: ShowTableRowsView -> [[DynamicField]]
$sel:tableName:ShowTableRowsView :: ShowTableRowsView -> Text
$sel:tableNames:ShowTableRowsView :: ShowTableRowsView -> [Text]
.. } = [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 :: Html
tableBody = [hsx|<tbody>{forEach rows renderRow}</tbody>|]
            renderRow :: [DynamicField] -> Html
renderRow [DynamicField]
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 :: Text
contextMenuId = Text
"context-menu-column-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
forall a. Show a => a -> Text
tshow Text
primaryKey
                    primaryKey :: Text
primaryKey = Text -> [Text] -> Text
intercalate Text
"---" ([Text] -> Text)
-> ([DynamicField] -> [Text]) -> [DynamicField] -> Text
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. (DynamicField -> Text) -> [DynamicField] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (ByteString -> Text)
-> (DynamicField -> ByteString) -> DynamicField -> Text
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Maybe ByteString -> ByteString
forall a. a -> Maybe a -> a
fromMaybe ByteString
"" (Maybe ByteString -> ByteString)
-> (DynamicField -> Maybe ByteString) -> DynamicField -> ByteString
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. 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] -> Text) -> [DynamicField] -> Text
forall a b. (a -> b) -> a -> b
$ (DynamicField -> Bool) -> [DynamicField] -> [DynamicField]
forall a. (a -> Bool) -> [a] -> [a]
filter ((Text -> [Text] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
primaryKeyFields) (Text -> Bool) -> (DynamicField -> Text) -> DynamicField -> Bool
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs (ByteString -> Text)
-> (DynamicField -> ByteString) -> DynamicField -> Text
forall k (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Proxy "fieldName" -> DynamicField -> ByteString
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "fieldName" (Proxy "fieldName")
Proxy "fieldName"
#fieldName) [DynamicField]
fields
            renderField :: Text -> DynamicField -> Html
renderField Text
primaryKey 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="border rounded p-1" href={EditRowValueAction tableName (cs fieldName) primaryKey}>{renderId (sqlValueToText fieldValue)}</a></span></td>|]
                | 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 primaryKey} checked={sqlValueToText fieldValue == "t"} /></span></td>|]
                | Bool
otherwise = Text -> DynamicField -> Html
renderNormalField Text
primaryKey DynamicField :: Maybe ByteString -> ByteString -> DynamicField
DynamicField { Maybe ByteString
ByteString
$sel:fieldName:DynamicField :: ByteString
$sel:fieldValue:DynamicField :: Maybe ByteString
fieldName :: ByteString
fieldValue :: Maybe ByteString
.. }

            renderNormalField :: Text -> DynamicField -> Html
renderNormalField Text
primaryKey DynamicField { Maybe ByteString
ByteString
fieldName :: ByteString
fieldValue :: Maybe ByteString
$sel:fieldName:DynamicField :: DynamicField -> ByteString
$sel:fieldValue:DynamicField :: DynamicField -> Maybe ByteString
.. } = [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 :: Bool
isForeignKeyColumn = ByteString
"_id" ByteString -> ByteString -> Bool
`ByteString.isSuffixOf` ByteString
fieldName
                    foreignKeyHoverCardUrl :: Maybe Text
foreignKeyHoverCardUrl = if Bool
isForeignKeyColumn
                        then Text -> Maybe Text
forall a. a -> Maybe a
Just (Text -> Maybe Text) -> Text -> Maybe Text
forall a b. (a -> b) -> a -> b
$ DataController -> Text
forall controller. HasPath controller => controller -> Text
pathTo ShowForeignKeyHoverCardAction :: Text -> Text -> Text -> DataController
ShowForeignKeyHoverCardAction { Text
$sel:tableName:ShowDatabaseAction :: Text
tableName :: Text
tableName, $sel:id:ShowDatabaseAction :: Text
id = Text
primaryKey, $sel:columnName:ShowDatabaseAction :: Text
columnName = ByteString -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ByteString
fieldName }
                        else Maybe Text
forall a. Maybe a
Nothing


            columnNames :: [ByteString]
columnNames = (DynamicField -> ByteString) -> [DynamicField] -> [ByteString]
forall a b. (a -> b) -> [a] -> [b]
map (Proxy "fieldName" -> DynamicField -> ByteString
forall model (name :: Symbol) value.
(KnownSymbol name, HasField name model value) =>
Proxy name -> model -> value
get IsLabel "fieldName" (Proxy "fieldName")
Proxy "fieldName"
#fieldName) ([DynamicField] -> Maybe [DynamicField] -> [DynamicField]
forall a. a -> Maybe a -> a
fromMaybe [] ([[DynamicField]] -> Maybe [DynamicField]
forall a. [a] -> Maybe a
head [[DynamicField]]
rows))

            onClick :: Text -> a -> Text -> Text
onClick Text
tableName a
fieldName Text
primaryKey = 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
primaryKey)) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
")"

            totalPages :: [Int]
totalPages = [Int
1..Double -> Int
forall a b. (RealFrac a, Integral b) => a -> b
ceiling (Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral(Int
totalRows) Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral(Int
pageSize))]

            pageMenu :: Html
pageMenu = Bool -> Html -> Html
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when ([Int] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Int]
totalPages Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
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 :: Html
backButton = if Int
page Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
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 :: Html
nextButton = if Int
page Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< [Int] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Int]
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 :: Int -> Html
renderPageButton Int
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 :: 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>
            |]