module IHP.IDE.SchemaDesigner.View.Layout
( schemaDesignerLayout
, findStatementByName
, visualNav
, renderColumnSelector
, renderColumn
, renderEnumSelector
, renderValue
, renderObjectSelector
, removeQuotes
, replace
, findForeignKey
, findTableIndex
, migrationStatus
, emptyColumnSelectorContainer
) where

import IHP.ViewPrelude
import IHP.IDE.SchemaDesigner.Types
import IHP.IDE.ToolServer.Types
import IHP.IDE.ToolServer.Helper.View
import IHP.IDE.ToolServer.Layout hiding (tableIcon)
import IHP.IDE.SchemaDesigner.Compiler (compilePostgresType, compileExpression)
import qualified Data.List as List

schemaDesignerLayout :: Html -> Html
schemaDesignerLayout :: Html -> Html
schemaDesignerLayout Html
inner = Html -> Html
toolServerLayout Html
[hsx|
    <div class={classes ["d-flex d-flex flex-column h-100", ("migration-status-visible", hasUnmigratedChanges)]}>
        {visualNav}
        <div class="h-100 d-flex flex-column">
            {inner}
        </div>
    </div>
|]
    where
        (DatabaseNeedsMigration Bool
hasUnmigratedChanges) = forall value.
(?context::ControllerContext, Typeable value) =>
value
fromFrozenContext @DatabaseNeedsMigration

unmigratedChanges :: Html
unmigratedChanges :: Html
unmigratedChanges = [hsx|
<div class="alert alert-primary d-flex align-items-center" role="alert">
    <div style="height: fit-content">
        <strong>Unmigrated Changes</strong>
        Your app database is not in sync with the Schema.sql
    </div>
    {databaseControls}
</div>
|]

migrationStatus :: Html
migrationStatus :: Html
migrationStatus = if Bool
hasPendingMigrations
        then Html
Html
pendingMigrations
        else if Bool
databaseNeedsMigration
            then Html
Html
unmigratedChanges
            else Html
forall a. Monoid a => a
mempty
    where
        (DatabaseNeedsMigration Bool
databaseNeedsMigration) = forall value.
(?context::ControllerContext, Typeable value) =>
value
fromFrozenContext @DatabaseNeedsMigration

        hasPendingMigrations :: Bool
        hasPendingMigrations :: Bool
hasPendingMigrations = Bool
False

        unmigratedChanges :: Html
        unmigratedChanges :: Html
unmigratedChanges = [hsx|
        <div id="migration-status-container">
            <div class="alert alert-primary d-flex align-items-center shadow-lg" role="alert">
                {migrationStatusIcon}
                <div class="user-select-none">
                    <div><strong>Unmigrated Changes</strong></div>
                    Your app database is not in sync with the Schema
                </div>
                <div class="ml-auto d-flex justify-content-end">
                    <a
                        href={NewMigrationAction}
                        class="btn px-4 btn-dark"
                        >Migrate DB <span class="btn-arrow">→</span></a>
                </div>
            </div>
        </div>
        |]

        pendingMigrations :: Html
        pendingMigrations :: Html
pendingMigrations = [hsx|
        <div id="migration-status-container">
            <div class="alert alert-primary d-flex align-items-center shadow-lg" role="alert">
                {migrationStatusIcon}
                <div class="user-select-none">
                    <div><strong>Pending Changes</strong></div>
                    You have migrations that haven't been run yet
                </div>
                <div class="ml-auto d-flex justify-content-end">
                    <a
                        href="#"
                        class="btn px-4 btn-dark"
                        >Run Migrations <span class="btn-arrow">→</span></a>
                </div>
            </div>
        </div>
        |]

        migrationStatusIcon :: Html
        migrationStatusIcon :: Html
migrationStatusIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|
            <svg width="33px" height="33px" viewBox="0 0 33 33" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="mr-3">
                <g id="Schema" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
                    <g id="Message" transform="translate(-383.000000, -813.000000)" fill="#FFCD1B">
                        <g id="Group" transform="translate(383.000000, 813.000000)">
                            <rect id="Rectangle" fill-opacity="0.322170017" x="0" y="0" width="33" height="33" rx="5"></rect>
                            <g id="Arrow-Double-3---24px" transform="translate(7.000000, 7.000000)">
                                <path d="M4.39179,10.2850058 C4.78668,9.89890559 5.41981,9.9060056 5.80592,10.3009058 C6.19203,10.6958061 6.18492,11.3289064 5.79003,11.7150067 L3.4531,14.0000172 L19,14.0000172 C19.5523,14.0000172 20,14.4477083 20,15.0000172 C20,15.5523089 19.5523,16.0000172 19,16.0000172 L3.4531,16.0000172 L5.79003,18.2850105 C6.18492,18.6711107 6.19203,19.3042111 5.80592,19.6991113 C5.41981,20.0940116 4.78668,20.1011116 4.39179,19.7150113 L0.30088,15.715009 C0.10847,15.5269089 0,15.2691087 0,15.0000172 C0,14.7309084 0.10847,14.4731083 0.30088,14.2850082 L4.39179,10.2850058 Z M15.6082,0.284989971 C15.2133,-0.101120255 14.5802,-0.0940002508 14.1941,0.30087998 C13.808,0.695770211 13.8151,1.32890058 14.21,1.71501081 L16.5469,4 L1,4 C0.44772,4 0,4.44771241 0,5 C0,5.55228305 0.44772,6 1,6 L16.5469,6 L14.21,8.28500465 C13.8151,8.67110488 13.808,9.30420525 14.1941,9.69910548 C14.5802,10.0940057 15.2133,10.1011057 15.6082,9.71500549 L19.6991,5.71501315 C19.8915,5.52687304 20,5.26911289 20,5 C20,4.73089257 19.8915,4.47313242 19.6991,4.28499231 L15.6082,0.284989971 Z" id="Shape"></path>
                            </g>
                        </g>
                    </g>
                </g>
            </svg>
        |]

databaseControls :: Html
databaseControls :: Html
databaseControls = [hsx|
<div class="d-flex justify-content-end ml-auto">
    <form method="POST" action={pathTo UpdateDbAction} id="update-db-form"/>
    <form method="POST" action={pathTo PushToDbAction} id="push-to-db-form"/>
    <form method="POST" action={pathTo DumpDbAction} id="db-to-fixtures-form"/>
    <div class="btn-group btn-group-sm">
        <a
            class="btn btn-primary"
            href={NewMigrationAction}
            onclick="checkBeforeUnload()"
            >Migrate DB →</a>

        <button type="button" class="btn btn-primary dropdown-toggle dropdown-toggle-split" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
            <span class="sr-only">Toggle Dropdown</span>
        </button>

        <div class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownMenuLink">
            <button
                type="submit"
                form="update-db-form"
                class="dropdown-item"
                data-toggle="tooltip"
                data-placement="left"
                data-html="true"
                title="Dumps DB to Fixtures.sql.<br><br>Delete the DB.<br><br>Recreate using Schema.sql and Fixtures.sql"
                onclick="checkBeforeUnload()"
                >Update DB</button>

            <button
                type="submit"
                class="dropdown-item"
                form="db-to-fixtures-form"
                data-toggle="tooltip"
                data-placement="left"
                data-html="true"
                title="Saves the content of all tables to Application/Fixtures.sql"
                onclick="checkBeforeUnload()"
                >Save DB to Fixtures</button>
            <button
                type="submit"
                class="dropdown-item"
                form="push-to-db-form"
                data-toggle="tooltip"
                data-placement="left"
                data-html="true"
                title="Delete the DB and recreate using Application/Schema.sql and Application/Fixture.sql<br><br><strong class=text-danger>Save DB to Fixtures before using this to avoid data loss</strong>"
                onclick="checkBeforeUnload()"
                >Push to DB</button>
        </div>
    </div>
</div>
|]

findStatementByName :: Text -> t Statement -> Maybe Statement
findStatementByName Text
statementName t Statement
statements = (Statement -> Bool) -> t Statement -> Maybe Statement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find Statement -> Bool
pred t Statement
statements
    where
        pred :: Statement -> Bool
pred (StatementCreateTable CreateTable { Text
name :: Text
$sel:name:CreateTable :: CreateTable -> Text
name }) | (Text -> Text
toUpper Text
name) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== (Text -> Text
toUpper Text
statementName) = Bool
True
        pred (StatementCreateTable CreateTable { Text
$sel:name:CreateTable :: CreateTable -> Text
name :: Text
name }) | (Text -> Text
toUpper Text
name) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== (Text -> Text
toUpper (Text -> Text
forall a. Show a => a -> Text
tshow Text
statementName)) = Bool
True
        pred CreateEnumType { Text
name :: Text
$sel:name:StatementCreateTable :: Statement -> Text
name } | (Text -> Text
toUpper Text
name) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== (Text -> Text
toUpper Text
statementName) = Bool
True
        pred CreateEnumType { Text
$sel:name:StatementCreateTable :: Statement -> Text
name :: Text
name } | (Text -> Text
toUpper Text
name) Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== (Text -> Text
toUpper (Text -> Text
forall a. Show a => a -> Text
tshow Text
statementName)) = Bool
True
        pred Statement
_ = Bool
False

visualNav :: Html
visualNav :: Html
visualNav = [hsx|
    <div class="view-selector">
        <div class="container-fluid">
            <a href={TablesAction} class={classes [("active", tableViewActive)]}>
                Schema Designer
            </a>

            <a href={ShowCodeAction} class={classes [("active", codeEditorActive)]}>
                Code Editor
            </a>
            <a href={MigrationsAction} class={classes [("active", migrationsActive)]}>
                Migrations
            </a>
        </div>
    </div>
|]
    where
        codeEditorActive :: Bool
codeEditorActive = SchemaController -> Bool
forall controller.
(?context::ControllerContext, PathString controller) =>
controller -> Bool
isActivePath SchemaController
ShowCodeAction
        tableViewActive :: Bool
tableViewActive = Bool -> Bool
not Bool
codeEditorActive Bool -> Bool -> Bool
&& Bool -> Bool
not Bool
migrationsActive
        migrationsActive :: Bool
migrationsActive = forall controller.
(?context::ControllerContext, Typeable controller) =>
Bool
forall {k} (controller :: k).
(?context::ControllerContext, Typeable controller) =>
Bool
isActiveController @MigrationsController

emptyColumnSelectorContainer :: Html
emptyColumnSelectorContainer = [hsx|
<div class="col-md-8 col-lg-10 column-selector d-flex">
</div>
|]

renderColumnSelector :: Text -> [(Int, Column)] -> [Statement] -> Html
renderColumnSelector :: Text -> [(Int, Column)] -> [Statement] -> Html
renderColumnSelector Text
tableName [(Int, Column)]
columns [Statement]
statements = [hsx|
<div class="col-md-8 col-lg-10 column-selector d-flex">
    <section class="flex-grow-1" oncontextmenu="showContextMenu('context-menu-column-root')">
        <div class="d-flex align-items-center">
            <h5>Columns</h5>
            <div class="toolbox">
                <a
                    href={NewColumnAction tableName}
                    class="btn btn-link btn-add"
                    data-toggle="tooltip"
                    data-placement="bottom"
                    title="Add Column"
                >{addIcon}</a>
            </div>
        </div>
        <table class="table table-hover table-sm">
            <tbody>
                {forEach columns (\column -> renderColumn (snd column) (fst column) tableName statements)}
            </tbody>
        </table>
        {suggestedColumnsSection tableName columns}
    </section>
    {auth}

    <section>
        {columnIndexes}
    </section>
</div>
<div class="custom-menu menu-for-column shadow backdrop-blur" id="context-menu-column-root">
    <a href={NewColumnAction tableName}>Add Column</a>
    <a href={NewPolicyAction tableName}>Add Policy</a>
</div>
|]
    where
        columnIndexes :: Html
columnIndexes =
            case [Statement] -> Text -> Maybe Statement
findTableIndex [Statement]
statements Text
tableName of
                Just Statement
_ -> [hsx|
                    <div>
                        <h5>Indexes</h5>
                    </div>
                    <table class="table table-hover table-sm">
                        {renderColumnIndexes tableName statements}
                    </table>
                |]
                Maybe Statement
Nothing -> [hsx||]

        auth :: Html
        auth :: Html
auth = Text -> [Statement] -> Html
renderPolicies Text
tableName [Statement]
statements

suggestedColumnsSection :: Text -> [(Int, Column)] -> Html
suggestedColumnsSection :: Text -> [(Int, Column)] -> Html
suggestedColumnsSection Text
tableName [(Int, Column)]
indexAndColumns = Bool -> Html -> Html
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
isUsersTable [hsx|
    <div class="mt-5">
        {mconcat suggestions}
    </div>
|]
    where
        columns :: [Column]
        columns :: [Column]
columns = ((Int, Column) -> Column) -> [(Int, Column)] -> [Column]
forall a b. (a -> b) -> [a] -> [b]
map (Int, Column) -> Column
forall a b. (a, b) -> b
snd [(Int, Column)]
indexAndColumns

        hasColumn :: Text -> Bool
        hasColumn :: Text -> Bool
hasColumn Text
name = [Column]
columns [Column] -> ([Column] -> Maybe Column) -> Maybe Column
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (Column -> Bool) -> [Column] -> Maybe Column
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\Column
column -> Column
column.name Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
name) Maybe Column -> (Maybe Column -> Bool) -> Bool
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Maybe Column -> Bool
forall a. Maybe a -> Bool
isJust

        isUsersTable :: Bool
isUsersTable = Text
tableName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"users"

        suggestions :: [Html]
suggestions = [Maybe Html
createdAt, Maybe Html
updatedAt, Maybe Html
userId] [Maybe Html] -> ([Maybe Html] -> [Html]) -> [Html]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> [Maybe Html] -> [Html]
forall a. [Maybe a] -> [a]
catMaybes

        createdAt :: Maybe Html
createdAt = if Text -> Bool
hasColumn Text
"created_at"
                then Maybe Html
forall a. Maybe a
Nothing
                else Html -> Maybe Html
forall a. a -> Maybe a
Just [hsx|
                    <form method="POST" action={CreateColumnAction} class="mb-2">
                        <input type="hidden" name="tableName" value={tableName} />
                        <input type="hidden" name="name" value="created_at"/>
                        <input type="hidden" name="columnType" value="TIMESTAMP WITH TIME ZONE"/>
                        <input type="hidden" name="primaryKey" value={inputValue False}/>
                        <input type="hidden" name="isArray" value={inputValue False}/>
                        <input type="hidden" name="defaultValue" value="NOW()"/>
                        <input type="hidden" name="allowNull" value={inputValue False}/>
                        <input type="hidden" name="isUnique" value={inputValue False}/>
                        <input type="hidden" name="isReference" value={inputValue False}/>
                        <input type="hidden" name="withIndex" value={inputValue True}/>

                        <button type="submit" class="btn btn-suggested-table">
                            <table class="table table-sm mb-0">
                                <tbody>
                                    <tr class="column">
                                        <td style="width: 40px">+</td>
                                        <td class="context-column column-name">created_at</td>
                                        <td class="context-column">TIMESTAMP WITH TIME ZONE</td>
                                        <td class="context-column">NOW()</td>
                                        <td class="context-column"></td>
                                    </tr>
                                </tbody>
                            </table>
                        </button>
                    </form>
                |]

        updatedAt :: Maybe Html
updatedAt = if Text -> Bool
hasColumn Text
"updated_at"
                then Maybe Html
forall a. Maybe a
Nothing
                else Html -> Maybe Html
forall a. a -> Maybe a
Just [hsx|
                    <form method="POST" action={CreateColumnAction} class="mb-2">
                        <input type="hidden" name="tableName" value={tableName} />
                        <input type="hidden" name="name" value="updated_at"/>
                        <input type="hidden" name="columnType" value="TIMESTAMP WITH TIME ZONE"/>
                        <input type="hidden" name="primaryKey" value={inputValue False}/>
                        <input type="hidden" name="isArray" value={inputValue False}/>
                        <input type="hidden" name="defaultValue" value="NOW()"/>
                        <input type="hidden" name="allowNull" value={inputValue False}/>
                        <input type="hidden" name="isUnique" value={inputValue False}/>
                        <input type="hidden" name="isReference" value={inputValue False}/>
                        <input type="hidden" name="withIndex" value={inputValue False}/>

                        <button type="submit" class="btn btn-suggested-table">
                            <table class="table table-sm mb-0">
                                <tbody>
                                    <tr class="column">
                                        <td style="width: 40px">+</td>
                                        <td class="context-column column-name">updated_at</td>
                                        <td class="context-column">TIMESTAMP WITH TIME ZONE</td>
                                        <td class="context-column">NOW()</td>
                                        <td class="context-column"></td>
                                    </tr>
                                </tbody>
                            </table>
                        </button>
                    </form>
                |]

        userId :: Maybe Html
userId = if Text -> Bool
hasColumn Text
"user_id"
            then Maybe Html
forall a. Maybe a
Nothing
            else Html -> Maybe Html
forall a. a -> Maybe a
Just [hsx|
                <form method="POST" action={CreateColumnAction}>
                    <input type="hidden" name="tableName" value={tableName} />
                    <input type="hidden" name="name" value="user_id"/>
                    <input type="hidden" name="columnType" value="UUID"/>
                    <input type="hidden" name="primaryKey" value={inputValue False}/>
                    <input type="hidden" name="isArray" value={inputValue False}/>
                    <input type="hidden" name="defaultValue" value={if usesRLS then "ihp_user_id()" :: Text else ""}/>
                    <input type="hidden" name="allowNull" value={inputValue False}/>
                    <input type="hidden" name="isUnique" value={inputValue False}/>
                    <input type="hidden" name="isReference" value={inputValue True}/>
                    <input type="hidden" name="referenceTable" value="users"/>
                    <input type="hidden" name="autoPolicy" value={inputValue usesRLS}/>

                    <button type="submit" class="btn btn-suggested-table">
                        <table class="table table-sm mb-0">
                            <tbody>
                                <tr class="column">
                                    <td style="width: 40px">+</td>
                                    <td class="context-column column-name">user_id</td>
                                    <td class="context-column">UUID</td>
                                    <td class="context-column"></td>
                                    <td class="context-column">FOREIGN KEY: users</td>
                                </tr>
                            </tbody>
                        </table>
                    </button>
                </form>
            |]

        -- TODO: this should be set to True if the Schema.sql contains any RLS related code
        usesRLS :: Bool
        usesRLS :: Bool
usesRLS = Bool
False


renderColumn :: Column -> Int -> Text -> [Statement] -> Html
renderColumn :: Column -> Int -> Text -> [Statement] -> Html
renderColumn Column { Text
name :: Text
$sel:name:Column :: Column -> Text
name, PostgresType
columnType :: PostgresType
$sel:columnType:Column :: Column -> PostgresType
columnType, Maybe Expression
defaultValue :: Maybe Expression
$sel:defaultValue:Column :: Column -> Maybe Expression
defaultValue, Bool
notNull :: Bool
$sel:notNull:Column :: Column -> Bool
notNull, Bool
isUnique :: Bool
$sel:isUnique:Column :: Column -> Bool
isUnique } Int
id Text
tableName [Statement]
statements = [hsx|
<tr class="column">
    <td class="context-column column-name" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}><a href={EditColumnAction tableName id} class="d-block text-body nounderline">{name}</a></td>
    <td class="context-column" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>{compilePostgresType columnType}{renderAllowNull}</td>
    <td class="context-column" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>{renderDefault}{renderIsUnique}</td>
    <td class="context-column" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>{renderPrimaryKey}{renderForeignKey}</td>
</tr>
<div class="custom-menu menu-for-column shadow backdrop-blur" id={contextMenuId}>
    <a href={EditColumnAction tableName id}>Edit Column</a>
    <a href={DeleteColumnAction tableName id name} class="js-delete">Delete Column</a>
    <div></div>
    <form action={ToggleColumnUniqueAction tableName id}>
        <button type="submit" class="link-button">
            {toggleButtonText}
        </button>
    </form>
    {foreignKeyOption}
    {addIndex}
    <div></div>
    <a href={NewColumnAction tableName}>Add Column</a>
</div>
|]
    where
        toggleButtonText :: Html
toggleButtonText = if Bool
isUnique then [hsx|Remove Unique|] else [hsx|Make Unique|]
        contextMenuId :: Text
contextMenuId = Text
"context-menu-column-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow Int
id
        renderPrimaryKey :: Html
renderPrimaryKey = if Bool
inPrimaryKey then [hsx|PRIMARY KEY|] else Html
forall a. Monoid a => a
mempty
        inPrimaryKey :: Bool
inPrimaryKey = case [Statement] -> Text -> Maybe [Text]
findPrimaryKey [Statement]
statements Text
tableName of
          Maybe [Text]
Nothing -> Bool
False
          Just [Text]
columnNames -> Text
name Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
columnNames
        renderAllowNull :: Html
renderAllowNull = if Bool
notNull then Html
forall a. Monoid a => a
mempty else [hsx|{" | " :: Text}NULL|]
        renderIsUnique :: Html
renderIsUnique = if Bool
isUnique then [hsx|IS UNIQUE|] else Html
forall a. Monoid a => a
mempty
        renderDefault :: Html
renderDefault =
            case Maybe Expression
defaultValue of
                Just Expression
value -> [hsx|default: {compileExpression value} |]
                Maybe Expression
Nothing -> Html
forall a. Monoid a => a
mempty
        renderForeignKey :: Html
renderForeignKey = case [Statement] -> Text -> Text -> Maybe Statement
findForeignKey [Statement]
statements Text
tableName Text
name of
            Just addConstraint :: Statement
addConstraint@AddConstraint { $sel:constraint:StatementCreateTable :: Statement -> Constraint
constraint = ForeignKeyConstraint { $sel:name:ForeignKeyConstraint :: Constraint -> Maybe Text
name = Just Text
constraintName, Text
referenceTable :: Text
$sel:referenceTable:ForeignKeyConstraint :: Constraint -> Text
referenceTable } } -> [hsx|<a href={EditForeignKeyAction tableName name constraintName referenceTable} class="d-block nounderline">FOREIGN KEY: {referenceTable}</a>|]
            Maybe Statement
_ -> Html
forall a. Monoid a => a
mempty
        foreignKeyOption :: Html
foreignKeyOption = case [Statement] -> Text -> Text -> Maybe Statement
findForeignKey [Statement]
statements Text
tableName Text
name of
            Just addConstraint :: Statement
addConstraint@AddConstraint { $sel:constraint:StatementCreateTable :: Statement -> Constraint
constraint = ForeignKeyConstraint { $sel:name:ForeignKeyConstraint :: Constraint -> Maybe Text
name = Just Text
constraintName, Text
$sel:referenceTable:ForeignKeyConstraint :: Constraint -> Text
referenceTable :: Text
referenceTable } } ->
                [hsx|<a href={EditForeignKeyAction tableName name constraintName referenceTable}>Edit Foreign Key Constraint</a>
                <a href={DeleteForeignKeyAction constraintName tableName} class="js-delete">Delete Foreign Key Constraint</a>|]
            Maybe Statement
_ -> [hsx|<a href={NewForeignKeyAction tableName name}>Add Foreign Key Constraint</a>|]
        
        addIndex :: Html
        addIndex :: Html
addIndex = Bool -> Html -> Html
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless Bool
alreadyHasIndex [hsx|
            <form method="POST" action={CreateIndexAction tableName name}>
                <button type="submit" class="link-button">
                    Add Index
                </button>
            </form>
        |]

        alreadyHasIndex :: Bool
        alreadyHasIndex :: Bool
alreadyHasIndex =
            [Statement]
statements
            [Statement] -> ([Statement] -> Maybe Statement) -> Maybe Statement
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (Statement -> Bool) -> [Statement] -> Maybe Statement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find \case
                CreateIndex { $sel:tableName:StatementCreateTable :: Statement -> Text
tableName = Text
tableName', [IndexColumn]
columns :: [IndexColumn]
$sel:columns:StatementCreateTable :: Statement -> [IndexColumn]
columns } -> Text
tableName' Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
tableName Bool -> Bool -> Bool
&& (Text -> Expression
VarExpression Text
name) Expression -> [Expression] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` ((IndexColumn -> Expression) -> [IndexColumn] -> [Expression]
forall a b. (a -> b) -> [a] -> [b]
map (.column) [IndexColumn]
columns)
                Statement
otherwise -> Bool
False
            Maybe Statement -> (Maybe Statement -> Bool) -> Bool
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Maybe Statement -> Bool
forall a. Maybe a -> Bool
isJust

renderColumnIndexes :: Text -> [Statement] -> Html
renderColumnIndexes Text
tableName [Statement]
statements = [Statement] -> ((Int, Statement) -> Html) -> Html
forall (m :: * -> *) a.
Applicative m =>
[a] -> ((Int, a) -> m ()) -> m ()
forEachWithIndex ([Statement] -> Text -> [Statement]
findTableIndexes [Statement]
statements Text
tableName) (Int, Statement) -> Html
(Int, Statement) -> Html
renderIndex
    where
        renderIndex :: (Int, Statement) -> Html
        renderIndex :: (Int, Statement) -> Html
renderIndex (Int
id, Statement
index) = [hsx|
            <tr class="index">
                <td class="index-name" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>
                    <a href={EditIndexAction tableName index.indexName} class="d-block text-body nounderline">
                        {index.indexName}
                    </a>
                </td>
                <td class="index-expressions" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>
                    {unique}
                </td>
                <td class="index-expressions" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>
                    {expressions}
                </td>
            </tr>
            <div class="custom-menu menu-for-column shadow backdrop-blur" id={contextMenuId}>
                <a href={EditIndexAction tableName index.indexName}>Edit Index</a>
                <a href={DeleteIndexAction tableName index.indexName} class="js-delete">Delete Index</a>
            </div>
        |]
            where
                unique :: Html
unique = Bool -> Html -> Html
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Statement
index.unique [hsx|UNIQUE|]
                showColumnOrder :: [IndexColumnOrder] -> Text
showColumnOrder [IndexColumnOrder]
columnOrder =
                    [IndexColumnOrder]
columnOrder
                        [IndexColumnOrder] -> ([IndexColumnOrder] -> [Text]) -> [Text]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (IndexColumnOrder -> Text) -> [IndexColumnOrder] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (\case { IndexColumnOrder
Asc -> Text
"ASC"; IndexColumnOrder
Desc -> Text
"DESC"; IndexColumnOrder
NullsFirst -> Text
"NULLS FIRST"; IndexColumnOrder
NullsLast -> Text
"NULLS LAST" })
                        [Text] -> ([Text] -> Text) -> Text
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> [Text] -> Text
unwords
                expressions :: Text
expressions = Statement
index.columns
                    [IndexColumn] -> ([IndexColumn] -> [Text]) -> [Text]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (IndexColumn -> Text) -> [IndexColumn] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (\IndexColumn
column -> (Expression -> Text
compileExpression IndexColumn
column.column) Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> ([IndexColumnOrder] -> Text
showColumnOrder IndexColumn
column.columnOrder))
                    [Text] -> ([Text] -> Text) -> Text
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Text -> [Text] -> Text
intercalate Text
", "

                contextMenuId :: Text
contextMenuId = Text
"context-menu-index-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow Int
id



renderPolicies :: Text -> [Statement] -> Html
renderPolicies :: Text -> [Statement] -> Html
renderPolicies Text
tableName [Statement]
statements = [Statement] -> Html -> Html
forall a (f :: * -> *).
(IsEmpty a, Applicative f) =>
a -> f () -> f ()
whenNonEmpty [Statement]
tablePolicies Html
policiesTable
    where
        policiesTable :: Html
policiesTable = [hsx|
            <section>
                <h5>Policies</h5>
                <table class="table table-hover table-sm">
                    {forEach tablePolicies renderPolicy}
                </table>
            </section>
        |]
        tablePolicies :: [Statement]
        tablePolicies :: [Statement]
tablePolicies = [Statement]
statements
                [Statement] -> ([Statement] -> [Statement]) -> [Statement]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (Statement -> Bool) -> [Statement] -> [Statement]
forall a. (a -> Bool) -> [a] -> [a]
filter \case
                    CreatePolicy { $sel:tableName:StatementCreateTable :: Statement -> Text
tableName = Text
policyTable } -> Text
policyTable Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
tableName
                    Statement
otherwise -> Bool
False

        renderPolicy :: Statement -> Html
renderPolicy Statement
policy = [hsx|
            <tr class="policy">
                <td class="policy-name" oncontextmenu={"showContextMenu('" <> contextMenuId <> "')"}>
                    <a href={EditPolicyAction tableName policyName} class="text-body nounderline">
                        {policy.name}
                    </a>
                </td>
                {renderExpressions policy}
            </tr>
            <div class="custom-menu menu-for-column shadow backdrop-blur" id={contextMenuId}>
                <a href={EditPolicyAction tableName policyName}>Edit Policy</a>
                <a href={DeletePolicyAction tableName policyName} class="js-delete">Delete Policy</a>
                <div></div>
                <a href={NewPolicyAction tableName}>Add Policy</a>
            </div>
        |]
            where
                policyName :: Text
policyName = Statement
policy.name
                contextMenuId :: Text
contextMenuId = Text
"policy-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text -> Text
toSlug Text
policyName

        renderExpressions :: r -> Html
renderExpressions r
policy = case (r
policy.using, r
policy.check) of
                (Just Expression
using, Just Expression
check) | Expression
using Expression -> Expression -> Bool
forall a. Eq a => a -> a -> Bool
== Expression
check ->
                    [hsx|
                        <td class="policy-expression">
                            <small>read & write if</small>
                            {compileExpression using}
                        </td>
                    |]
                (Maybe Expression
using, Maybe Expression
check) ->
                    [hsx|
                        <td class="policy-expression">
                            <small>read if</small>
                            {maybe "" compileExpression using}
                        </td>
                        <td class="policy-expression">
                            <small>write if</small>
                            {maybe "" compileExpression check}
                        </td>
                    |]

renderEnumSelector :: Text -> [(Int, Text)] -> Html
renderEnumSelector :: Text -> [(Int, Text)] -> Html
renderEnumSelector Text
enumName [(Int, Text)]
values = [hsx|
<div class="col-8 column-selector" oncontextmenu="showContextMenu('context-menu-value-root')">
    <div class="d-flex">
        <h5>Enum Values</h5>
        <div class="toolbox">
            <a
                href={NewEnumValueAction enumName}
                class="btn btn-link btn-add mr-1"
                data-toggle="tooltip"
                data-placement="bottom"
                title="Add Table"
                >{addIcon}</a>
        </div>
    </div>
    <table class="table table-hover table-sm">
        <tbody>
            {forEach values (\value -> renderValue (snd value) (fst value) enumName)}
        </tbody>
    </table>
</div>
<div class="custom-menu menu-for-column shadow backdrop-blur" id="context-menu-value-root">
    <a href={NewEnumValueAction enumName}>Add Value</a>
</div>|]

renderValue :: Text -> Int -> Text -> Html
renderValue :: Text -> Int -> Text -> Html
renderValue Text
value Int
valueId Text
enumName = [hsx|
<tr class="column">
    <td class="context-column column-name" oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>
        {value}
    </td>
</tr>
<div class="custom-menu menu-for-column shadow backdrop-blur" id={contextMenuId}>
    <a href={EditEnumValueAction enumName valueId}>Edit Value</a>
    <a href={DeleteEnumValueAction enumName valueId} class="js-delete">Delete Value</a>
    <div></div>
    <a href={NewEnumValueAction enumName}>Add Value</a>
</div>
|]
    where
        contextMenuId :: Text
contextMenuId = Text
"context-menu-value-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow Int
valueId

renderObjectSelector :: [(Int, Statement)] -> Maybe Text -> Html
renderObjectSelector [(Int, Statement)]
statements Maybe Text
activeObjectName = [hsx|
    <div class={classes ["col", "object-selector", ("empty", isEmptySelector)]} oncontextmenu="showContextMenu('context-menu-object-root')">
        <div class="d-flex align-items-center pl-2">
            <h5>Tables</h5>
            <div class="toolbox">
                <a
                    href={NewTableAction}
                    class="btn btn-link btn-add mr-1"
                    data-toggle="tooltip"
                    data-placement="bottom"
                    title="Add Table"
                    >{addIcon}</a>
            </div>
        </div>
        {forEach tableStatements (\statement -> renderObject (snd statement) (fst statement))}
        {enums}

        <div class="text-muted context-menu-notice">Right click to open context menu</div>
    </div>
    <div class="custom-menu menu-for-table shadow backdrop-blur" id="context-menu-object-root">
        <a href={NewTableAction}>Add Table</a>
        <a href={NewEnumAction}>Add Enum</a>
    </div>
|]
    where
        isEmptySelector :: Bool
        isEmptySelector :: Bool
isEmptySelector = [(Int, Statement)]
statements [(Int, Statement)]
-> ([(Int, Statement)] -> [Statement]) -> [Statement]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ((Int, Statement) -> Statement)
-> [(Int, Statement)] -> [Statement]
forall a b. (a -> b) -> [a] -> [b]
map (Int, Statement) -> Statement
forall a b. (a, b) -> b
snd [Statement] -> ([Statement] -> [Statement]) -> [Statement]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (Statement -> Bool) -> [Statement] -> [Statement]
forall a. (a -> Bool) -> [a] -> [a]
filter Statement -> Bool
shouldRenderObject [Statement] -> ([Statement] -> Bool) -> Bool
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> [Statement] -> Bool
forall value. IsEmpty value => value -> Bool
isEmpty

        tableStatements :: [(Int, Statement)]
        tableStatements :: [(Int, Statement)]
tableStatements = [(Int, Statement)]
statements [(Int, Statement)]
-> ([(Int, Statement)] -> [(Int, Statement)]) -> [(Int, Statement)]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ((Int, Statement) -> Bool)
-> [(Int, Statement)] -> [(Int, Statement)]
forall a. (a -> Bool) -> [a] -> [a]
filter \case
            (Int
_, StatementCreateTable CreateTable {}) -> Bool
True
            (Int, Statement)
otherwise -> Bool
False

        enumStatements :: [(Int, Statement)]
        enumStatements :: [(Int, Statement)]
enumStatements = [(Int, Statement)]
statements [(Int, Statement)]
-> ([(Int, Statement)] -> [(Int, Statement)]) -> [(Int, Statement)]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ((Int, Statement) -> Bool)
-> [(Int, Statement)] -> [(Int, Statement)]
forall a. (a -> Bool) -> [a] -> [a]
filter \case
            (Int
_, CreateEnumType {}) -> Bool
True
            (Int, Statement)
otherwise -> Bool
False

        enums :: Html
enums = [(Int, Statement)] -> Html -> Html
forall a (f :: * -> *).
(IsEmpty a, Applicative f) =>
a -> f () -> f ()
whenNonEmpty [(Int, Statement)]
enumStatements [hsx|
            <div class="d-flex pl-2">
                <h5>Enums</h5>
            </div>
            {forEach enumStatements (\statement -> renderObject (snd statement) (fst statement))}
        |]

        renderObject :: Statement -> Int -> Html
        renderObject :: Statement -> Int -> Html
renderObject (StatementCreateTable CreateTable { Text
$sel:name:CreateTable :: CreateTable -> Text
name :: Text
name }) Int
id = [hsx|
            <div class={classes [("object object-table w-100 context-table pl-3", True), ("active", Just name == activeObjectName)]} oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>
                <div class="d-flex justify-content-between">
                    <a href={ShowTableAction name} class="flex-grow-1">{name}</a>

                    {when rlsEnabled rlsIcon}
                </div>
            </div>
            <div class="custom-menu menu-for-table shadow backdrop-blur" id={contextMenuId}>
                <a href={EditTableAction name id}>Rename Table</a>
                <a href={DeleteTableAction id name} class="js-delete">Delete Table</a>
                <div></div>
                <a href={ShowGeneratedCodeAction name}>Show Generated Haskell Code</a>
                {when controllerDoesNotExist generateControllerLink}
                {unless controllerDoesNotExist openControllerLink}
                <div></div>
                <a href={NewColumnAction name}>Add Column to Table</a>
                <div></div>
                <a href={NewTableAction}>Add Table</a>
                <a href={NewEnumAction}>Add Enum</a>
            </div>
        |]
            where
                contextMenuId :: Text
contextMenuId = Text
"context-menu-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow Int
id
                generateControllerLink :: Html
generateControllerLink = [hsx|<a href={pathTo NewControllerAction <> "?name=" <> name}>Generate Controller</a>|]
                openControllerLink :: Html
openControllerLink = [hsx|<a href={pathTo OpenControllerAction <> "?name=" <> name} target="_blank">Open Controller</a>|]
                controllerDoesNotExist :: Bool
controllerDoesNotExist = Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ (Text -> Text
ucfirst Text
name) Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [Text]
webControllers
                (WebControllers [Text]
webControllers) = forall value.
(?context::ControllerContext, Typeable value) =>
value
fromFrozenContext @WebControllers

                rlsEnabled :: Bool
rlsEnabled = [(Int, Statement)]
statements
                        [(Int, Statement)]
-> ([(Int, Statement)] -> [Statement]) -> [Statement]
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> ((Int, Statement) -> Statement)
-> [(Int, Statement)] -> [Statement]
forall a b. (a -> b) -> [a] -> [b]
map (Int, Statement) -> Statement
forall a b. (a, b) -> b
snd
                        [Statement] -> ([Statement] -> Maybe Statement) -> Maybe Statement
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> (Statement -> Bool) -> [Statement] -> Maybe Statement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find \case
                            EnableRowLevelSecurity { $sel:tableName:StatementCreateTable :: Statement -> Text
tableName = Text
rlsTable } -> Text
rlsTable Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
name
                            Statement
otherwise                                       -> Bool
False
                        Maybe Statement -> (Maybe Statement -> Bool) -> Bool
forall {t1} {t2}. t1 -> (t1 -> t2) -> t2
|> Maybe Statement -> Bool
forall a. Maybe a -> Bool
isJust

                rlsIcon :: Html
rlsIcon = [hsx|
                        <span
                            class="rls-enabled"
                            data-toggle="tooltip"
                            data-placement="right"
                            data-html="true"
                            title="Row Level Security enabled"
                            >{shieldIcon}</span>
                        |]

        renderObject CreateEnumType { Text
$sel:name:StatementCreateTable :: Statement -> Text
name :: Text
name } Int
id = [hsx|
            <a href={ShowEnumAction name} class={classes [("object object-table w-100 context-enum pl-3", True), ("active", Just name == activeObjectName)]} oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>
                <div class="d-flex">
                    {name}
                </div>
            </a>
            <div class="custom-menu menu-for-enum shadow backdrop-blur" id={contextMenuId}>
                <a href={EditEnumAction name id}>Rename Enum</a>
                <a href={DeleteEnumAction id} class="js-delete">Delete Enum</a>
                <div></div>
                <a href={ShowGeneratedCodeAction name}>Show Generated Haskell Code</a>
                <a href={NewEnumValueAction name}>Add Column to Table</a>
                <div></div>
                <a href={NewTableAction}>Add Table</a>
                <a href={NewEnumAction}>Add Enum</a>
            </div>
        |]
            where
                contextMenuId :: Text
contextMenuId = Text
"context-menu-" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall a. Show a => a -> Text
tshow Int
id

        renderObject Comment {} Int
id = Html
forall a. Monoid a => a
mempty
        renderObject AddConstraint {} Int
id = Html
forall a. Monoid a => a
mempty
        renderObject CreateExtension {} Int
id = Html
forall a. Monoid a => a
mempty
        renderObject CreateIndex {} Int
id = Html
forall a. Monoid a => a
mempty
        renderObject CreateFunction {} Int
id = Html
forall a. Monoid a => a
mempty
        renderObject UnknownStatement {} Int
id = Html
forall a. Monoid a => a
mempty
        renderObject EnableRowLevelSecurity {} Int
id = Html
forall a. Monoid a => a
mempty
        renderObject CreatePolicy {} Int
id = Html
forall a. Monoid a => a
mempty

        shouldRenderObject :: Statement -> Bool
shouldRenderObject (StatementCreateTable CreateTable {}) = Bool
True
        shouldRenderObject CreateEnumType {} = Bool
True
        shouldRenderObject Statement
_ = Bool
False

removeQuotes :: [Char] -> Text
removeQuotes :: [Char] -> Text
removeQuotes (Char
x:[Char]
xs) = [Char] -> Text
forall a b. ConvertibleStrings a b => a -> b
cs ([Char] -> Text) -> [Char] -> Text
forall a b. (a -> b) -> a -> b
$ [Char] -> Maybe [Char] -> [Char]
forall a. a -> Maybe a -> a
fromMaybe [] ([Char] -> Maybe [Char]
forall a. [a] -> Maybe [a]
init [Char]
xs)
removeQuotes [Char]
n = [Char] -> Text
forall a b. ConvertibleStrings a b => a -> b
cs [Char]
n

findForeignKey :: [Statement] -> Text -> Text -> Maybe Statement
findForeignKey :: [Statement] -> Text -> Text -> Maybe Statement
findForeignKey [Statement]
statements Text
tableName Text
columnName =
    (Statement -> Bool) -> [Statement] -> Maybe Statement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find
        (\case
            AddConstraint { $sel:tableName:StatementCreateTable :: Statement -> Text
tableName = Text
fkTable, $sel:constraint:StatementCreateTable :: Statement -> Constraint
constraint = ForeignKeyConstraint { $sel:columnName:ForeignKeyConstraint :: Constraint -> Text
columnName = Text
fkColumn } } -> Text
tableName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
fkTable Bool -> Bool -> Bool
&& Text
columnName Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
fkColumn
            Statement
otherwise -> Bool
False
        )
        [Statement]
statements

findPrimaryKey :: [Statement] -> Text -> Maybe [Text]
findPrimaryKey :: [Statement] -> Text -> Maybe [Text]
findPrimaryKey [Statement]
statements Text
tableName = do
    (StatementCreateTable CreateTable
createTable) <- (Statement -> Bool) -> [Statement] -> Maybe Statement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (Text -> Statement -> Bool
isCreateTable Text
tableName) [Statement]
statements
    [Text] -> Maybe [Text]
forall a. a -> Maybe a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([Text] -> Maybe [Text])
-> (PrimaryKeyConstraint -> [Text])
-> PrimaryKeyConstraint
-> Maybe [Text]
forall b c a. (b -> c) -> (a -> b) -> a -> c
forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. PrimaryKeyConstraint -> [Text]
primaryKeyColumnNames (PrimaryKeyConstraint -> Maybe [Text])
-> PrimaryKeyConstraint -> Maybe [Text]
forall a b. (a -> b) -> a -> b
$ CreateTable
createTable.primaryKeyConstraint
    where
      isCreateTable :: Text -> Statement -> Bool
isCreateTable Text
tableName (StatementCreateTable CreateTable { Text
$sel:name:CreateTable :: CreateTable -> Text
name :: Text
name }) = Text
name Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
tableName
      isCreateTable Text
_ Statement
_ = Bool
False

findTableIndex :: [Statement] -> Text -> Maybe Statement
findTableIndex :: [Statement] -> Text -> Maybe Statement
findTableIndex [Statement]
statements Text
tableName =
    (Statement -> Bool) -> [Statement] -> Maybe Statement
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\case CreateIndex { $sel:tableName:StatementCreateTable :: Statement -> Text
tableName = Text
tableName' } -> Text
tableName' Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
tableName; Statement
otherwise -> Bool
False) [Statement]
statements

findTableIndexes :: [Statement] -> Text -> [Statement]
findTableIndexes :: [Statement] -> Text -> [Statement]
findTableIndexes [Statement]
statements Text
tableName =
    (Statement -> Bool) -> [Statement] -> [Statement]
forall a. (a -> Bool) -> [a] -> [a]
filter (\case CreateIndex { $sel:tableName:StatementCreateTable :: Statement -> Text
tableName = Text
tableName' } -> Text
tableName' Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
tableName; Statement
otherwise -> Bool
False) [Statement]
statements


replace :: Int -> a -> [a] -> [a]
replace :: forall a. Int -> a -> [a] -> [a]
replace Int
i a
e [a]
xs = case Int -> [a] -> ([a], [a])
forall a. Int -> [a] -> ([a], [a])
List.splitAt Int
i [a]
xs of
   ([a]
before, a
_:[a]
after) -> [a]
before [a] -> [a] -> [a]
forall a. Semigroup a => a -> a -> a
++ (a
ea -> [a] -> [a]
forall a. a -> [a] -> [a]
: [a]
after)
   ([a]
a, [a]
b) -> [a]
a [a] -> [a] -> [a]
forall a. Semigroup a => a -> a -> a
++ [a]
b

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/static/img/table.svg
tableIcon :: Html
tableIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="24" height="24"><defs><style>.cls-table-1{fill:#f2f2f2;}.cls-table-2{fill:#2195e7;}.cls-table-3{fill:none;stroke:#c1cbd5;stroke-linejoin:round;}.cls-table-3,.cls-table-4{stroke-width:0.75px;}.cls-table-4{fill:#def4fd;stroke:#2195e7;stroke-miterlimit:1;}</style></defs><title>table</title><g id="_2" data-name="2"><rect class="cls-table-1" x="2.92" y="3.65" width="10.15" height="8.71" rx="0.53" ry="0.53"/><path class="cls-table-2" d="M12.55,4a.15.15,0,0,1,.15.15v7.66a.15.15,0,0,1-.15.15H3.45a.15.15,0,0,1-.15-.15V4.17A.15.15,0,0,1,3.45,4h9.1m0-.75H3.45a.9.9,0,0,0-.9.9v7.66a.9.9,0,0,0,.9.9h9.1a.9.9,0,0,0,.9-.9V4.17a.9.9,0,0,0-.9-.9Z"/><line class="cls-table-3" x1="3.32" y1="9.43" x2="12.69" y2="9.43"/><line class="cls-table-3" x1="8.01" y1="7.09" x2="8" y2="11.97"/><line class="cls-table-4" x1="8.01" y1="4.03" x2="8" y2="6.58"/><line class="cls-table-4" x1="12.68" y1="6.74" x2="3.32" y2="6.74"/></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/types/static/img/type.svg
enumIcon :: Html
enumIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="24" height="24"><defs><style>.cls-enum-1{fill:#d6ffea;}.cls-enum-2{fill:#5acb9a;}</style></defs><title>type</title><g id="_2" data-name="2"><path class="cls-enum-1" d="M3.9,3.47h8.19a.44.44,0,0,1,.44.44V12.1a.43.43,0,0,1-.43.43H3.91a.44.44,0,0,1-.44-.44V3.9A.43.43,0,0,1,3.9,3.47Z"/><path class="cls-enum-2" d="M12.1,3.85a.06.06,0,0,1,.06.06V12.1a.06.06,0,0,1-.06.06H3.9a.06.06,0,0,1-.06-.06V3.91a.06.06,0,0,1,.06-.06H12.1m0-.75H3.9a.81.81,0,0,0-.81.81V12.1a.81.81,0,0,0,.81.81H12.1a.81.81,0,0,0,.81-.81V3.91a.81.81,0,0,0-.81-.81Z"/></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/indexes/static/img/index.svg
indexIcon :: Html
indexIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-index-1,.cls-index-3{fill:#def4fd;}.cls-index-2{fill:#357fd3;}.cls-index-3,.cls-index-4{stroke:#357fd3;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}.cls-index-4{fill:none;}</style></defs><title>index</title><g id="_8" data-name="8"><path class="cls-index-1" d="M5.81,3.37h4.39a.44.44,0,0,1,.44.44V5.49a.44.44,0,0,1-.44.44H5.8a.43.43,0,0,1-.43-.43V3.81A.44.44,0,0,1,5.81,3.37Z"/><path class="cls-index-2" d="M10.2,3.75a.06.06,0,0,1,.06.06V5.49a.06.06,0,0,1-.06.06H5.8a.06.06,0,0,1-.06-.06V3.81a.06.06,0,0,1,.06-.06H10.2m0-.75H5.8A.81.81,0,0,0,5,3.81V5.49a.81.81,0,0,0,.81.81H10.2A.81.81,0,0,0,11,5.49V3.81A.81.81,0,0,0,10.2,3Z"/><rect class="cls-index-1" x="11.38" y="10.57" width="2.24" height="2.06" rx="0.44" ry="0.44"/><path class="cls-index-2" d="M13.19,10.94a.06.06,0,0,1,.06.06v1.19a.06.06,0,0,1-.06.06H11.81a.06.06,0,0,1-.06-.06V11a.06.06,0,0,1,.06-.06h1.37m0-.75H11.81A.81.81,0,0,0,11,11v1.19a.81.81,0,0,0,.81.81h1.37a.81.81,0,0,0,.81-.81V11a.81.81,0,0,0-.81-.81Z"/><rect class="cls-index-1" x="2.38" y="10.57" width="2.24" height="2.06" rx="0.44" ry="0.44"/><path class="cls-index-2" d="M4.19,10.94a.06.06,0,0,1,.06.06v1.19a.06.06,0,0,1-.06.06H2.81a.06.06,0,0,1-.06-.06V11a.06.06,0,0,1,.06-.06H4.19m0-.75H2.81A.81.81,0,0,0,2,11v1.19a.81.81,0,0,0,.81.81H4.19A.81.81,0,0,0,5,12.19V11a.81.81,0,0,0-.81-.81Z"/><rect class="cls-index-1" x="6.88" y="10.57" width="2.24" height="2.06" rx="0.44" ry="0.44"/><path class="cls-index-2" d="M8.69,10.94a.06.06,0,0,1,.06.06v1.19a.06.06,0,0,1-.06.06H7.32a.06.06,0,0,1-.06-.06V11a.06.06,0,0,1,.06-.06H8.69m0-.75H7.32a.81.81,0,0,0-.81.81v1.19a.81.81,0,0,0,.81.81H8.69a.81.81,0,0,0,.81-.81V11a.81.81,0,0,0-.81-.81Z"/><line class="cls-index-3" x1="7.98" y1="6.3" x2="7.98" y2="8.54"/><line class="cls-index-3" x1="7.98" y1="8.54" x2="7.98" y2="10.19"/><polyline class="cls-index-4" points="12.5 10.19 12.5 8.17 3.5 8.17 3.5 10.19"/></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/index_constraint/static/img/unique_constraint.svg
uniqueIndexIcon :: Html
uniqueIndexIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-unique-index-1{fill:#cbe7f6;}.cls-unique-index-1,.cls-unique-index-2{stroke:#2c66bd;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}.cls-unique-index-2{font-size:8px;fill:#2c66bd;font-family:Georgia, Georgia;}</style></defs><title>unique index</title><g id="_2" data-name="2"><circle class="cls-unique-index-1" cx="8" cy="8" r="5.4"/><text class="cls-unique-index-2" transform="translate(6.28 10.17)">1</text></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/constraints/check_constraint/static/img/check-constraint.svg
constraintIcon :: Html
constraintIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-constraint-1{fill:none;stroke:#719317;stroke-linecap:round;stroke-linejoin:round;stroke-width:2px;}</style></defs><title>constraint</title><g id="_2" data-name="2"><polyline class="cls-constraint-1" points="4 9.25 6.5 11.75 12 4.25"/></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/languages/static/img/language.svg
commentIcon :: Html
commentIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-comment-1{fill:#f8f8f8;stroke:#d5d4d5;stroke-linejoin:round;stroke-width:0.75px;}</style></defs><title>comment</title><g id="_2" data-name="2"><path class="cls-comment-1" d="M13.78,7c0,.62-.57,1.19-1.49,1.61-.39.18.07,2.59-.43,2.72s-2-2-2.53-2c-.42,0-.86.06-1.32.06C4.81,9.44,2.23,8.36,2.23,7S4.81,4.63,8,4.63,13.78,5.71,13.78,7Z"/></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/extensions/static/img/extension.svg
extensionIcon :: Html
extensionIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-extension-1,.cls-extension-4{fill:#c9edd2;}.cls-extension-2{fill:#1a6016;}.cls-extension-3{fill:none;}.cls-extension-3,.cls-extension-4{stroke:#1a6016;stroke-linejoin:round;stroke-width:0.75px;}.cls-extension-4{stroke-linecap:round;}</style></defs><title>extension</title><g id="_2" data-name="2"><path class="cls-extension-1" d="M5.62,13.13A.62.62,0,0,1,5,12.51v-9a.62.62,0,0,1,.62-.62h6.61a.62.62,0,0,1,.62.62v6.66l-3,3Z"/><path class="cls-extension-2" d="M12.23,3.25a.24.24,0,0,1,.24.24V10L9.72,12.75H5.62a.24.24,0,0,1-.24-.24v-9a.24.24,0,0,1,.24-.24h6.61m0-.75H5.62a1,1,0,0,0-1,1v9a1,1,0,0,0,1,1H10l3.19-3.19V3.49a1,1,0,0,0-1-1Z"/><polyline class="cls-extension-3" points="13 9.52 9.49 9.54 9.49 13.5"/><rect class="cls-extension-4" x="2.78" y="5.13" width="6.01" height="2.01"/></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/browser/server_groups/servers/databases/schemas/functions/static/img/function.svg
functionIcon :: Html
functionIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.cls-function-1{fill:#edfffd;}.cls-function-2{fill:#34495e;stroke-miterlimit:10;stroke-width:0.5px;}.cls-function-2,.cls-function-3{stroke:#2f91a3;}.cls-function-3{fill:#2980b9;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.75px;}</style></defs><title>function</title><g id="_2" data-name="2"><polygon class="cls-function-1" points="10.99 13.41 12.26 12.74 12.61 11.24 12.61 10.54 12.8 9.04 13.24 8.48 14.37 7.92 13.24 7.92 13.24 7.92 13.24 7.92 14.02 7.78 12.85 6.91 12.78 6.32 12.78 5.24 12.61 3.76 12.14 3.21 11.39 2.88 10.99 2.84 4.93 2.72 4.06 3.33 3.52 3.77 3.52 4.38 3.61 4.21 3.42 6.23 3.52 6.93 2 7.92 1.78 8.11 2 7.92 2.98 8.63 3.12 9.04 3.7 10.56 3.67 11.22 3.65 11.68 3.67 10.88 3.67 11.27 3.89 12.23 4.24 12.71 4.87 13.09 5.37 13.41 10.99 13.41"/><path class="cls-function-2" d="M10.57,13.2a2.46,2.46,0,0,0,1.14-.28,1.56,1.56,0,0,0,.59-.6,2,2,0,0,0,.22-.78q0-.42,0-.81v.2c0-.38,0-.7,0-1a5.15,5.15,0,0,1,.06-.67,1.53,1.53,0,0,1,.14-.46A1.32,1.32,0,0,1,13,8.51a1.4,1.4,0,0,1,.34-.26,2.21,2.21,0,0,1,.36-.15A2,2,0,0,1,14,8c.1,0,.1-.05,0-.07a2,2,0,0,1-.33-.07,2,2,0,0,1-.36-.15A1.4,1.4,0,0,1,13,7.49a1.34,1.34,0,0,1-.24-.33,1.54,1.54,0,0,1-.14-.46A5.17,5.17,0,0,1,12.56,6q0-.39,0-1v.2c0-.26,0-.53,0-.81a2,2,0,0,0-.22-.78,1.57,1.57,0,0,0-.59-.6,2.48,2.48,0,0,0-1.14-.28V2.55l.3,0a2.29,2.29,0,0,1,.37.06,2.84,2.84,0,0,1,.4.13A1.61,1.61,0,0,1,12,3a1.85,1.85,0,0,1,.46.5,2,2,0,0,1,.24.59,3.87,3.87,0,0,1,.09.72c0,.26,0,1,0,1a4.39,4.39,0,0,0,.07.83,1.32,1.32,0,0,0,.26.59,1.47,1.47,0,0,0,.55.41.38.38,0,0,1,0,.65,1.48,1.48,0,0,0-.55.4,1.33,1.33,0,0,0-.26.59,4.39,4.39,0,0,0-.07.83s-.05,1.54-.1,1.76a2,2,0,0,1-.24.59A1.84,1.84,0,0,1,12,13a1.61,1.61,0,0,1-.39.23,3,3,0,0,1-.4.13,2.41,2.41,0,0,1-.37.06l-.3,0Z"/><path class="cls-function-2" d="M5.66,2.8a2.46,2.46,0,0,0-1.14.28,1.56,1.56,0,0,0-.59.6,2,2,0,0,0-.22.78q0,.42,0,.81v-.2c0,.38,0,.7,0,1a5.15,5.15,0,0,1-.06.67,1.53,1.53,0,0,1-.14.46,1.32,1.32,0,0,1-.24.33,1.4,1.4,0,0,1-.34.26,2.21,2.21,0,0,1-.36.15A2,2,0,0,1,2.18,8c-.1,0-.1.05,0,.07a2,2,0,0,1,.33.07,2,2,0,0,1,.36.15,1.4,1.4,0,0,1,.34.26,1.34,1.34,0,0,1,.24.33,1.54,1.54,0,0,1,.14.46,5.17,5.17,0,0,1,.06.67q0,.39,0,1v-.2c0,.26,0,.53,0,.81a2,2,0,0,0,.22.78,1.57,1.57,0,0,0,.59.6,2.48,2.48,0,0,0,1.14.28v.24l-.3,0A2.29,2.29,0,0,1,5,13.36a2.84,2.84,0,0,1-.4-.13A1.61,1.61,0,0,1,4.2,13a1.85,1.85,0,0,1-.46-.5,2,2,0,0,1-.24-.59,3.87,3.87,0,0,1-.09-.72c0-.26,0-1,0-1a4.39,4.39,0,0,0-.07-.83,1.32,1.32,0,0,0-.26-.59,1.47,1.47,0,0,0-.55-.41.38.38,0,0,1,0-.65,1.48,1.48,0,0,0,.55-.4,1.33,1.33,0,0,0,.26-.59,4.39,4.39,0,0,0,.07-.83s.05-1.54.1-1.76a2,2,0,0,1,.24-.59A1.84,1.84,0,0,1,4.2,3a1.61,1.61,0,0,1,.39-.23A3,3,0,0,1,5,2.64a2.41,2.41,0,0,1,.37-.06l.3,0Z"/><line class="cls-function-3" x1="6.32" y1="5.41" x2="10.07" y2="5.41"/><line class="cls-function-3" x1="6.32" y1="7.91" x2="10.07" y2="7.91"/><line class="cls-function-3" x1="6.32" y1="10.41" x2="8.82" y2="10.41"/></g></svg>|]

-- | https://github.com/postgres/pgadmin4/blob/master/web/pgadmin/misc/static/explain/img/ex_unknown.svg
unknownIcon :: Html
unknownIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg id="_1" data-name="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><defs><style>.cls-unknown-1{fill:#1cafe4;}</style></defs><title>unknown</title><path class="cls-unknown-1" d="M23.34,13.2a14.76,14.76,0,0,1,8.16-2.14,17.57,17.57,0,0,1,10.56,3q4.2,3,4.2,9a10.18,10.18,0,0,1-1.82,6.16,21,21,0,0,1-4.09,3.88l-2,1.55a6,6,0,0,0-2.16,3A12.5,12.5,0,0,0,35.83,41H28.2a22.11,22.11,0,0,1,.9-6.57,11.57,11.57,0,0,1,3.76-4.18l2-1.6A7.17,7.17,0,0,0,36.53,27a5.67,5.67,0,0,0,1.12-3.4,6.58,6.58,0,0,0-1.25-3.9q-1.25-1.76-4.56-1.76t-4.62,2.17a8.34,8.34,0,0,0-1.36,4.5H17.74Q18.08,16.55,23.34,13.2ZM28,44.81h8.41v8.13H28Z"/></svg>|]

-- | https://fonts.google.com/icons?icon.query=shield
shieldIcon :: Html
shieldIcon = [Char] -> Html
forall a. ToMarkup a => a -> Html
preEscapedToHtml [plain|<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 24 24" height="1rem" viewBox="0 0 24 24" width="1rem" fill="currentColor"><g><rect fill="none" height="24" width="24"/></g><g><path d="M12,2L4,5v6.09c0,5.05,3.41,9.76,8,10.91c4.59-1.15,8-5.86,8-10.91V5L12,2z M18,11.09c0,4-2.55,7.7-6,8.83 c-3.45-1.13-6-4.82-6-8.83v-4.7l6-2.25l6,2.25V11.09z"/></g></svg>|]