packages feed

ihp-ide-1.5.0: IHP/IDE/SchemaDesigner/View/Migrations/Index.hs

module IHP.IDE.SchemaDesigner.View.Migrations.Index where
import IHP.ViewPrelude
import IHP.IDE.ToolServer.Helper.View
import IHP.SchemaMigration
import IHP.IDE.ToolServer.Types
import IHP.IDE.ToolServer.Routes ()

import qualified Data.Time.Clock.POSIX as Clock

type Revision = Int
data IndexView = IndexView
    { migrationsWithSql :: ![(Migration, Text)]
    , migratedRevisions :: ![Int]
    , lastError :: !(Maybe Text)
    }

instance View IndexView where
    html IndexView { migrationsWithSql = [] } = emptyState
    html IndexView { .. } = [hsx|
        <div class="pt-3 flex-grow-1" oncontextmenu="showContextMenu('context-menu-migrations')">
            <div class="d-flex align-items-center mb-3 pe-2">
                <a
                    href={NewMigrationAction}
                    class="ms-auto btn btn-link btn-add"
                    data-bs-toggle="tooltip"
                    data-bs-placement="bottom"
                    title="Add Migration"
                    >
                    {addIcon}
                </a>
            </div>
            <div class="mx-3">{renderFlashMessages}</div>
            {forEachWithIndex migrationsWithSql renderMigration}
        </div>

        {migrationsContextMenu}
    |]
        where
            renderMigration :: (Int, (Migration, Text)) -> Html
            renderMigration (index, (migration, sqlStatements)) = [hsx|
                <div class={classes ["migration mx-3", ("is-completed", not pending), ("pending", pending)]} oncontextmenu={"showContextMenu('" <> contextMenuId <> "'); event.stopPropagation();"}>
                    <div class="migration-actions d-flex justify-content-end align-items-center">
                        {runOrStatus}
                        {when pending editAndDelete}
                    </div>

                    {code "sql" sqlStatements}
                </div>
                {migrationContextMenu migration contextMenuId pending}
            |]
                where
                    pending = (migration.revision) `notElem` migratedRevisions
                    currentError = lastError

                    contextMenuId :: Text
                    contextMenuId = "context-menu-migration-" <> tshow (migration.revision)

                    editAndDelete = [hsx|
                        <div class="d-flex">
                            <a
                                href={EditMigrationAction (migration.revision)}
                                class="btn btn-link btn-add"
                                data-bs-toggle="tooltip"
                                data-bs-placement="bottom"
                                title="Edit Migration"
                            >{editIcon}</a>

                            <a
                                href={DeleteMigrationAction (migration.revision)}
                                class="btn btn-link btn-add js-delete"
                                data-bs-toggle="tooltip"
                                data-bs-placement="bottom"
                                title="Delete Migration"
                            >{deleteIcon}</a>
                        </div>
                    |]


                    currentErrorHtml = unless (isNothing currentError) [hsx|
                        <div class="text-danger fw-bold me-2">
                            {currentError}
                        </div>
                    |]

                    runOrStatus :: Html
                    runOrStatus =
                        if pending
                                then [hsx|
                                <form method="POST" action={RunMigrationAction (migration.revision)} class="me-2 d-flex align-items-center">
                                    {currentErrorHtml}
                                    <button
                                        class="btn btn-secondary migration-run-button"
                                    >{if isJust currentError then "Retry" else "Run" :: Text}</button>
                                </form>
                            |]
                            else [hsx|
                                <div class="d-flex justify-content-end mb-2">
                                    <span class="text-muted me-2 user-select-none" style="opacity: 0.5">{timeAgo revisionTime}</span>
                                    <strong class="text-success">
                                        {checkmark}
                                    </strong>
                                </div>
                            |]

                    revisionTime :: UTCTime
                    revisionTime = Clock.posixSecondsToUTCTime $ (fromInteger (fromIntegral (migration.revision)))


code :: Text -> Text -> Html
code _ src = [hsx|
    <div class="source-code">
        <pre><code class="language-sql">{src}</code></pre>
    </div>
    
|]

checkmark = preEscapedToHtml [plain|
<svg width="16px" height="12px" viewBox="0 0 16 12" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g id="Migrations" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
        <g id="Mig-Redesign-with-messages" transform="translate(-1401.000000, -607.000000)" fill="#059053">
            <g id="Task-10---24px" transform="translate(1401.000000, 607.000000)">
                <path d="M15.6987017,0.284560762 C16.0938017,0.670430762 16.1013017,1.30355076 15.7155017,1.69867076 L5.94984168,11.6987008 C5.75306168,11.9002008 5.48044168,12.0093008 5.19897168,11.9994008 C4.91751168,11.9894008 4.65330168,11.8612008 4.47128168,11.6463008 L0.23691168,6.64630076 C-0.12001832,6.22480076 -0.0676983198,5.59380076 0.35376168,5.23690076 C0.77521168,4.88000076 1.40621168,4.93230076 1.76314168,5.35370076 L5.28704168,9.51480076 L14.2846017,0.301330762 C14.6704017,-0.0937992379 15.3036017,-0.101309238 15.6987017,0.284560762 Z" id="Path"></path>
            </g>
        </g>
    </g>
</svg>
|]

emptyState :: Html
emptyState = [hsx|
    <div class="d-flex flex-column w-100 h-100 justify-content-center user-select-none" oncontextmenu="showContextMenu('context-menu-migrations')">
        <div class="text-center">
            <p class="text-muted">
                No Migration yet.
            </p>

            <p>
                <a href={NewMigrationAction} class="btn btn-secondary">+ New Migration</a>
            </p>
        </div>
    </div>
    {migrationsContextMenu}
|]


migrationsContextMenu :: Html
migrationsContextMenu = [hsx|
    <div class="custom-menu menu-for-table shadow backdrop-blur" id="context-menu-migrations">
        <a href={NewMigrationAction}>Add Migration</a>
    </div>
|]


migrationContextMenu migration contextMenuId pending = [hsx|
    <div class="custom-menu menu-for-table shadow backdrop-blur" id={contextMenuId}>
        {currentMigrationActions}
        <a href={NewMigrationAction}>Add Migration</a>
    </div>
|]
    where
        migrationId :: Int
        migrationId = migration.revision

        currentMigrationActions = when pending [hsx|
            <a href={EditMigrationAction migrationId}>Edit Migration</a>
            <a href={DeleteMigrationAction migrationId} class="js-delete">Delete Migration</a>
            <div></div>
        |]