generated from coulomb/repo-seed
fix(WP-0014): pre-flight compilation fixes, Tailwind pipeline, and admin seed
A2 — Compilation fixes: - Remove inline FK constraints from Schema.sql; IHP schema compiler cannot parse them. Add 1744329600-restore-fk-constraints.sql migration to restore referential integrity at the DB level. - Rename `#label` → `#label_` throughout to avoid clash with Haskell built-in. - Fix `hub.id == hid` UUID comparisons to use `toUUID hub.id`. - Replace non-existent `setStatus`/`respondJson` calls with `renderJsonWithStatusCode` throughout Api controllers. - Fix qualified package import for `cryptohash-sha256` in Auth.hs. - Add `CanSelect (Text, Text)` instance in Helper.View. - Refactor HSX inline lambdas to named helper functions in 100+ views (GHC cannot infer types for anonymous functions inside quasi-quoted HSX). - Fix missing imports (IHP.QueryBuilder, IHP.Fetch, Web.Routes, Only, etc.) across helpers and controllers. - Remove duplicate `diffUTCTime` definition in BottleneckDetector. - Change `createEventForHub` return type from `IO ResponseReceived` to `IO ()`. - Seed type-registry vocabulary via 1744502400-seed-type-registries.sql (moved from Schema.sql where IHP does not execute INSERT statements). A3 — Tailwind build pipeline: - Add `tailwindcss` to flake.nix native packages. - Uncomment `tailwind.exec` process in devenv shell config. - Add tailwind/tailwind.config.js (scans Web/View/**/*.hs). - Add tailwind/app.css with @tailwind directives. A4 — Admin user seed: - Add 1744416000-seed-admin-user.sql: inserts admin@inter-hub.local with bcrypt-hashed password admin1234! (cost 10). - Add .env.example documenting all required environment variables and default admin credentials. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ViewPrelude
|
||||
import Web.Routes ()
|
||||
|
||||
data AnnotationCategoriesView = AnnotationCategoriesView { entries :: ![AnnotationCategoryRegistry], hubs :: ![Hub] }
|
||||
data ShowAnnotationCategoryView = ShowAnnotationCategoryView { entry :: !AnnotationCategoryRegistry, mOwner :: !(Maybe Hub) }
|
||||
@@ -59,12 +60,12 @@ renderRow :: [Hub] -> AnnotationCategoryRegistry -> Html
|
||||
renderRow hubs e = [hsx|
|
||||
<tr class="border-b border-gray-100 hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-mono text-xs">{e.name}</td>
|
||||
<td class="px-4 py-3">{e.label}</td>
|
||||
<td class="px-4 py-3">{e.label_}</td>
|
||||
<td class="px-4 py-3 text-gray-500">{hubName hubs e.ownerHubId}</td>
|
||||
<td class="px-4 py-3">{statusBadge e.status}</td>
|
||||
<td class="px-4 py-3 text-right text-xs">
|
||||
<a href={ShowAnnotationCategoryAction { annotationCategoryRegistryId = e.id }} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditAnnotationCategoryAction { annotationCategoryRegistryId = e.id }} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
<a href={ShowAnnotationCategoryAction (e.id)} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditAnnotationCategoryAction (e.id)} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
|]
|
||||
@@ -80,13 +81,13 @@ instance View ShowAnnotationCategoryView where
|
||||
{statusBadge entry.status}
|
||||
</div>
|
||||
<dl class="space-y-3 text-sm">
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label}</dd></div>
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label_}</dd></div>
|
||||
<div><dt class="text-gray-500">Description</dt><dd>{fromMaybe "—" entry.description}</dd></div>
|
||||
<div><dt class="text-gray-500">Owner</dt><dd>{maybe "Framework (cross-domain)" (.name) mOwner}</dd></div>
|
||||
<div><dt class="text-gray-500">Replaced by</dt><dd>{fromMaybe "—" entry.deprecatedInFavourOf}</dd></div>
|
||||
</dl>
|
||||
<div class="mt-6">
|
||||
<a href={EditAnnotationCategoryAction { annotationCategoryRegistryId = entry.id }}
|
||||
<a href={EditAnnotationCategoryAction (entry.id)}
|
||||
class="text-sm border border-gray-300 px-3 py-1.5 rounded hover:bg-gray-50">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,20 +97,10 @@ typeForm :: AnnotationCategoryRegistry -> [Hub] -> Bool -> Html
|
||||
typeForm entry hubs isNew = [hsx|
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-6 max-w-lg">
|
||||
<div class="space-y-4">
|
||||
{if isNew then [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent, lowercase-underscored)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|] else [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{entry.name}</p>
|
||||
</div>
|
||||
|]}
|
||||
{renderNameField isNew entry.name}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
|
||||
{(textField #label) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
{(textField #label_) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Description <span class="text-gray-400 text-xs">(optional)</span></label>
|
||||
@@ -145,7 +136,21 @@ instance View EditAnnotationCategoryView where
|
||||
<a href={AnnotationCategoryRegistryAction} class="text-sm text-gray-500 hover:text-gray-700">← Annotation Categories</a>
|
||||
</div>
|
||||
<h1 class="text-xl font-semibold mb-6">Edit Annotation Category</h1>
|
||||
<form method="POST" action={UpdateAnnotationCategoryAction { annotationCategoryRegistryId = entry.id }}>
|
||||
<form method="POST" action={UpdateAnnotationCategoryAction (entry.id)}>
|
||||
{typeForm entry hubs False}
|
||||
</form>
|
||||
|]
|
||||
|
||||
renderNameField :: Bool -> Text -> Html
|
||||
renderNameField True _ = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent, lowercase-underscored)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|]
|
||||
renderNameField False name = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{name}</p>
|
||||
</div>
|
||||
|]
|
||||
|
||||
@@ -4,6 +4,7 @@ import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ViewPrelude
|
||||
import Web.Routes ()
|
||||
|
||||
data EventTypesView = EventTypesView { entries :: ![EventTypeRegistry], hubs :: ![Hub] }
|
||||
data ShowEventTypeView = ShowEventTypeView { entry :: !EventTypeRegistry, mOwner :: !(Maybe Hub) }
|
||||
@@ -59,12 +60,12 @@ renderRow :: [Hub] -> EventTypeRegistry -> Html
|
||||
renderRow hubs e = [hsx|
|
||||
<tr class="border-b border-gray-100 hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-mono text-xs">{e.name}</td>
|
||||
<td class="px-4 py-3">{e.label}</td>
|
||||
<td class="px-4 py-3">{e.label_}</td>
|
||||
<td class="px-4 py-3 text-gray-500">{hubName hubs e.ownerHubId}</td>
|
||||
<td class="px-4 py-3">{statusBadge e.status}</td>
|
||||
<td class="px-4 py-3 text-right text-xs">
|
||||
<a href={ShowEventTypeAction { eventTypeRegistryId = e.id }} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditEventTypeAction { eventTypeRegistryId = e.id }} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
<a href={ShowEventTypeAction (e.id)} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditEventTypeAction (e.id)} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
|]
|
||||
@@ -80,13 +81,13 @@ instance View ShowEventTypeView where
|
||||
{statusBadge entry.status}
|
||||
</div>
|
||||
<dl class="space-y-3 text-sm">
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label}</dd></div>
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label_}</dd></div>
|
||||
<div><dt class="text-gray-500">Description</dt><dd>{fromMaybe "—" entry.description}</dd></div>
|
||||
<div><dt class="text-gray-500">Owner</dt><dd>{maybe "Framework (cross-domain)" (.name) mOwner}</dd></div>
|
||||
<div><dt class="text-gray-500">Replaced by</dt><dd>{fromMaybe "—" entry.deprecatedInFavourOf}</dd></div>
|
||||
</dl>
|
||||
<div class="mt-6 flex gap-2">
|
||||
<a href={EditEventTypeAction { eventTypeRegistryId = entry.id }}
|
||||
<a href={EditEventTypeAction (entry.id)}
|
||||
class="text-sm border border-gray-300 px-3 py-1.5 rounded hover:bg-gray-50">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,20 +97,10 @@ typeForm :: EventTypeRegistry -> [Hub] -> Bool -> Html
|
||||
typeForm entry hubs isNew = [hsx|
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-6 max-w-lg">
|
||||
<div class="space-y-4">
|
||||
{if isNew then [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent, lowercase-underscored)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|] else [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{entry.name}</p>
|
||||
</div>
|
||||
|]}
|
||||
{renderNameField isNew entry.name}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
|
||||
{(textField #label) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
{(textField #label_) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Description <span class="text-gray-400 text-xs">(optional)</span></label>
|
||||
@@ -145,7 +136,21 @@ instance View EditEventTypeView where
|
||||
<a href={EventTypeRegistryAction} class="text-sm text-gray-500 hover:text-gray-700">← Event Types</a>
|
||||
</div>
|
||||
<h1 class="text-xl font-semibold mb-6">Edit Event Type</h1>
|
||||
<form method="POST" action={UpdateEventTypeAction { eventTypeRegistryId = entry.id }}>
|
||||
<form method="POST" action={UpdateEventTypeAction (entry.id)}>
|
||||
{typeForm entry hubs False}
|
||||
</form>
|
||||
|]
|
||||
|
||||
renderNameField :: Bool -> Text -> Html
|
||||
renderNameField True _ = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent, lowercase-underscored)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|]
|
||||
renderNameField False name = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{name}</p>
|
||||
</div>
|
||||
|]
|
||||
|
||||
@@ -4,6 +4,7 @@ import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ViewPrelude
|
||||
import Web.Routes ()
|
||||
|
||||
data PolicyScopesView = PolicyScopesView { entries :: ![PolicyScopeRegistry], hubs :: ![Hub] }
|
||||
data ShowPolicyScopeView = ShowPolicyScopeView { entry :: !PolicyScopeRegistry, mOwner :: !(Maybe Hub) }
|
||||
@@ -59,12 +60,12 @@ renderRow :: [Hub] -> PolicyScopeRegistry -> Html
|
||||
renderRow hubs e = [hsx|
|
||||
<tr class="border-b border-gray-100 hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-mono text-xs">{e.name}</td>
|
||||
<td class="px-4 py-3">{e.label}</td>
|
||||
<td class="px-4 py-3">{e.label_}</td>
|
||||
<td class="px-4 py-3 text-gray-500">{hubName hubs e.ownerHubId}</td>
|
||||
<td class="px-4 py-3">{statusBadge e.status}</td>
|
||||
<td class="px-4 py-3 text-right text-xs">
|
||||
<a href={ShowPolicyScopeAction { policyScopeRegistryId = e.id }} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditPolicyScopeAction { policyScopeRegistryId = e.id }} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
<a href={ShowPolicyScopeAction (e.id)} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditPolicyScopeAction (e.id)} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
|]
|
||||
@@ -80,13 +81,13 @@ instance View ShowPolicyScopeView where
|
||||
{statusBadge entry.status}
|
||||
</div>
|
||||
<dl class="space-y-3 text-sm">
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label}</dd></div>
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label_}</dd></div>
|
||||
<div><dt class="text-gray-500">Description</dt><dd>{fromMaybe "—" entry.description}</dd></div>
|
||||
<div><dt class="text-gray-500">Owner</dt><dd>{maybe "Framework (cross-domain)" (.name) mOwner}</dd></div>
|
||||
<div><dt class="text-gray-500">Replaced by</dt><dd>{fromMaybe "—" entry.deprecatedInFavourOf}</dd></div>
|
||||
</dl>
|
||||
<div class="mt-6">
|
||||
<a href={EditPolicyScopeAction { policyScopeRegistryId = entry.id }}
|
||||
<a href={EditPolicyScopeAction (entry.id)}
|
||||
class="text-sm border border-gray-300 px-3 py-1.5 rounded hover:bg-gray-50">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,20 +97,10 @@ typeForm :: PolicyScopeRegistry -> [Hub] -> Bool -> Html
|
||||
typeForm entry hubs isNew = [hsx|
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-6 max-w-lg">
|
||||
<div class="space-y-4">
|
||||
{if isNew then [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent, lowercase-hyphenated)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|] else [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{entry.name}</p>
|
||||
</div>
|
||||
|]}
|
||||
{renderNameField isNew entry.name}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
|
||||
{(textField #label) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
{(textField #label_) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Description <span class="text-gray-400 text-xs">(optional)</span></label>
|
||||
@@ -145,7 +136,21 @@ instance View EditPolicyScopeView where
|
||||
<a href={PolicyScopeRegistryAction} class="text-sm text-gray-500 hover:text-gray-700">← Policy Scopes</a>
|
||||
</div>
|
||||
<h1 class="text-xl font-semibold mb-6">Edit Policy Scope</h1>
|
||||
<form method="POST" action={UpdatePolicyScopeAction { policyScopeRegistryId = entry.id }}>
|
||||
<form method="POST" action={UpdatePolicyScopeAction (entry.id)}>
|
||||
{typeForm entry hubs False}
|
||||
</form>
|
||||
|]
|
||||
|
||||
renderNameField :: Bool -> Text -> Html
|
||||
renderNameField True _ = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent, lowercase-hyphenated)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|]
|
||||
renderNameField False name = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{name}</p>
|
||||
</div>
|
||||
|]
|
||||
|
||||
@@ -4,6 +4,7 @@ import Web.Types
|
||||
import Generated.Types
|
||||
import IHP.Prelude
|
||||
import IHP.ViewPrelude
|
||||
import Web.Routes ()
|
||||
|
||||
data WidgetTypesView = WidgetTypesView { entries :: ![WidgetTypeRegistry], hubs :: ![Hub] }
|
||||
data ShowWidgetTypeView = ShowWidgetTypeView { entry :: !WidgetTypeRegistry, mOwner :: !(Maybe Hub) }
|
||||
@@ -59,12 +60,12 @@ renderRow :: [Hub] -> WidgetTypeRegistry -> Html
|
||||
renderRow hubs e = [hsx|
|
||||
<tr class="border-b border-gray-100 hover:bg-gray-50">
|
||||
<td class="px-4 py-3 font-mono text-xs">{e.name}</td>
|
||||
<td class="px-4 py-3">{e.label}</td>
|
||||
<td class="px-4 py-3">{e.label_}</td>
|
||||
<td class="px-4 py-3 text-gray-500">{hubName hubs e.ownerHubId}</td>
|
||||
<td class="px-4 py-3">{statusBadge e.status}</td>
|
||||
<td class="px-4 py-3 text-right text-xs">
|
||||
<a href={ShowWidgetTypeAction { widgetTypeRegistryId = e.id }} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditWidgetTypeAction { widgetTypeRegistryId = e.id }} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
<a href={ShowWidgetTypeAction (e.id)} class="text-gray-500 hover:text-gray-700 mr-2">View</a>
|
||||
<a href={EditWidgetTypeAction (e.id)} class="text-gray-500 hover:text-gray-700">Edit</a>
|
||||
</td>
|
||||
</tr>
|
||||
|]
|
||||
@@ -80,22 +81,15 @@ instance View ShowWidgetTypeView where
|
||||
{statusBadge entry.status}
|
||||
</div>
|
||||
<dl class="space-y-3 text-sm">
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label}</dd></div>
|
||||
<div><dt class="text-gray-500">Label</dt><dd class="font-medium">{entry.label_}</dd></div>
|
||||
<div><dt class="text-gray-500">Description</dt><dd>{fromMaybe "—" entry.description}</dd></div>
|
||||
<div><dt class="text-gray-500">Owner</dt><dd>{maybe "Framework (cross-domain)" (.name) mOwner}</dd></div>
|
||||
<div><dt class="text-gray-500">Replaced by</dt><dd>{fromMaybe "—" entry.deprecatedInFavourOf}</dd></div>
|
||||
</dl>
|
||||
<div class="mt-6 flex gap-2">
|
||||
<a href={EditWidgetTypeAction { widgetTypeRegistryId = entry.id }}
|
||||
<a href={EditWidgetTypeAction (entry.id)}
|
||||
class="text-sm border border-gray-300 px-3 py-1.5 rounded hover:bg-gray-50">Edit</a>
|
||||
{if entry.status == "active"
|
||||
then [hsx|
|
||||
<form method="POST" action={DeprecateWidgetTypeAction { widgetTypeRegistryId = entry.id }}>
|
||||
<input type="hidden" name="deprecated_in_favour_of" value="" placeholder="replacement name" />
|
||||
<button type="submit" class="text-sm border border-amber-300 text-amber-700 px-3 py-1.5 rounded hover:bg-amber-50">Deprecate</button>
|
||||
</form>
|
||||
|]
|
||||
else mempty}
|
||||
{if entry.status == "active" then renderDeprecateForm entry.id else mempty}
|
||||
</div>
|
||||
</div>
|
||||
|]
|
||||
@@ -104,20 +98,10 @@ typeForm :: WidgetTypeRegistry -> [Hub] -> Bool -> Html
|
||||
typeForm entry hubs isNew = [hsx|
|
||||
<div class="bg-white rounded-lg border border-gray-200 p-6 max-w-lg">
|
||||
<div class="space-y-4">
|
||||
{if isNew then [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent identifier, lowercase-hyphenated)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|] else [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{entry.name}</p>
|
||||
</div>
|
||||
|]}
|
||||
{renderNameField isNew entry.name}
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Label</label>
|
||||
{(textField #label) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
{(textField #label_) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Description <span class="text-gray-400 text-xs">(optional)</span></label>
|
||||
@@ -153,7 +137,29 @@ instance View EditWidgetTypeView where
|
||||
<a href={WidgetTypeRegistryAction} class="text-sm text-gray-500 hover:text-gray-700">← Widget Types</a>
|
||||
</div>
|
||||
<h1 class="text-xl font-semibold mb-6">Edit Widget Type</h1>
|
||||
<form method="POST" action={UpdateWidgetTypeAction { widgetTypeRegistryId = entry.id }}>
|
||||
<form method="POST" action={UpdateWidgetTypeAction (entry.id)}>
|
||||
{typeForm entry hubs False}
|
||||
</form>
|
||||
|]
|
||||
|
||||
renderNameField :: Bool -> Text -> Html
|
||||
renderNameField True _ = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(permanent identifier, lowercase-hyphenated)</span></label>
|
||||
{(textField #name) { fieldClass = "w-full border border-gray-300 rounded px-3 py-2 text-sm" }}
|
||||
</div>
|
||||
|]
|
||||
renderNameField False name = [hsx|
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-1">Name <span class="text-gray-400 text-xs">(immutable)</span></label>
|
||||
<p class="font-mono text-sm bg-gray-50 border border-gray-200 rounded px-3 py-2">{name}</p>
|
||||
</div>
|
||||
|]
|
||||
|
||||
renderDeprecateForm :: Id WidgetTypeRegistry -> Html
|
||||
renderDeprecateForm entryId = [hsx|
|
||||
<form method="POST" action={DeprecateWidgetTypeAction (entryId)}>
|
||||
<input type="hidden" name="deprecated_in_favour_of" value="" placeholder="replacement name" />
|
||||
<button type="submit" class="text-sm border border-amber-300 text-amber-700 px-3 py-1.5 rounded hover:bg-amber-50">Deprecate</button>
|
||||
</form>
|
||||
|]
|
||||
|
||||
Reference in New Issue
Block a user