Skip to content

Commit

Permalink
Bump Misk, make full dashboard page layout to add per user services/b…
Browse files Browse the repository at this point in the history
…ackfills list
  • Loading branch information
adrw committed Jan 9, 2025
1 parent d2cbe3b commit e29a5b0
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 55 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
jooq = "3.18.1"
ktlint = "0.47.1"
kotlin = "1.9.23"
misk = "2024.06.26.052833-88986ac"
misk = "2025.01.09.184726-680bca2"
okhttp = "5.0.0-alpha.14"
wire = "5.0.0"
sqldelight = "2.0.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
package app.cash.backfila.ui.components

import app.cash.backfila.dashboard.GetBackfillRunsAction
import jakarta.inject.Inject
import kotlinx.html.TagConsumer
import kotlinx.html.div
import kotlinx.html.main
import kotlinx.html.script
import misk.MiskCaller
import misk.config.AppName
import misk.hotwire.buildHtml
import misk.scope.ActionScoped
import misk.tailwind.Link
import misk.tailwind.pages.MenuSection
import misk.tailwind.pages.Navbar
import misk.web.HttpCall
import misk.web.dashboard.DashboardHomeUrl
import misk.web.dashboard.DashboardNavbarItem
import misk.web.dashboard.DashboardTab
import misk.web.dashboard.HtmlLayout
import wisp.deployment.Deployment

/**
* Builds dashboard UI for index homepage.
*
* Must be called within a Web Action.
*/
class DashboardPageLayout @Inject constructor(
private val allHomeUrls: List<DashboardHomeUrl>,
@AppName private val appName: String,
private val allNavbarItem: List<DashboardNavbarItem>,
private val allTabs: List<DashboardTab>,
private val callerProvider: ActionScoped<MiskCaller?>,
private val deployment: Deployment,
private val clientHttpCall: ActionScoped<HttpCall>,
private val getBackfillRunsAction: GetBackfillRunsAction,
) {
private var newBuilder = false
private var headBlock: TagConsumer<*>.() -> Unit = {}
private var title: String = "Backfila"

private val path by lazy {
clientHttpCall.get().url.encodedPath
}
private val dashboardHomeUrl by lazy {
allHomeUrls.firstOrNull { path.startsWith(it.url) }
}
private val homeUrl by lazy {
dashboardHomeUrl?.url ?: "/"
}

private fun setNewBuilder() = apply { newBuilder = true }

fun newBuilder(): DashboardPageLayout = DashboardPageLayout(
allHomeUrls = allHomeUrls,
appName = appName,
allNavbarItem = allNavbarItem,
allTabs = allTabs,
callerProvider = callerProvider,
deployment = deployment,
clientHttpCall = clientHttpCall,
getBackfillRunsAction = getBackfillRunsAction,
).setNewBuilder()

fun title(title: String) = apply {
this.title = title
}

fun headBlock(block: TagConsumer<*>.() -> Unit) = apply { this.headBlock = block }

@JvmOverloads
fun build(block: TagConsumer<*>.() -> Unit = { }): String {
check(newBuilder) {
"You must call newBuilder() before calling build() to prevent builder reuse."
}
newBuilder = false

return buildHtml {
HtmlLayout(
appRoot = "/",
title = title,
// TODO only use play CDN in development, using it always for demo purporses to avoid UI bugs
// playCdn = deployment.isLocalDevelopment,
playCdn = true,
headBlock = {
script {
type = "module"
src = "/static/js/autocomplete_controller.js"
}
script {
type = "module"
src = "/static/js/search_bar_controller.js"
}
},
) {
div("min-h-full") {
if (true) {
// Uses Misk's Navbar with sidebar
Navbar(
appName = "Backfila",
deployment = deployment,
homeHref = "/",
menuSections = buildMenuSections(
currentPath = path,
),
) {
div("py-10") {
main {
div("mx-auto max-w-7xl sm:px-6 lg:px-8") {
// TODO remove when new UI is stable and preferred
UseOldUIAlert()
block()
}
}
}
}
} else {
// Old UI
NavBar(path)
div("py-10") {
main {
div("mx-auto max-w-7xl sm:px-6 lg:px-8") {
// TODO remove when new UI is stable and preferred
UseOldUIAlert()
block()
}
}
}
}
}
}
}
}

private fun buildMenuSections(
currentPath: String,
): List<MenuSection> {
val callerBackfills = getBackfillRunsAction.backfillRuns(serviceName, variant)

return listOf(
MenuSection(
title = "Backfila",
links = listOf(
Link(
label = "Services",
href = "/services/",
isSelected = currentPath.startsWith("/services/"),
),
Link(
label = "Backfills",
href = "/backfills/",
isSelected = currentPath.startsWith("/backfills/"),
),
),
),
MenuSection(
title = "Your Services",
links = listOf(
Link(
label = "Fine Dining",
href = "/services/?q=FineDining",
isSelected = currentPath.startsWith("/services/?q=FindDining"),
),
),
),
MenuSection(
title = "Your Backfills",
links = listOf(
Link(
label = "FineDining #0034",
href = "/services/",
isSelected = currentPath.startsWith("/backfill/"),
),
Link(
label = "FineDining #0067",
href = "/backfill/",
isSelected = currentPath.startsWith("/backfill/"),
),
),
),
)
}

companion object {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import app.cash.backfila.service.BackfilaConfig
import app.cash.backfila.ui.PathBuilder
import app.cash.backfila.ui.actions.ServiceAutocompleteAction
import app.cash.backfila.ui.components.AlertSupport
import app.cash.backfila.ui.components.DashboardLayout
import app.cash.backfila.ui.components.DashboardPageLayout
import app.cash.backfila.ui.components.PageTitle
import javax.inject.Inject
import kotlinx.html.InputType
Expand All @@ -17,7 +17,6 @@ import kotlinx.html.p
import kotlinx.html.role
import kotlinx.html.span
import kotlinx.html.ul
import misk.hotwire.buildHtml
import misk.security.authz.Authenticated
import misk.web.Get
import misk.web.QueryParam
Expand All @@ -28,62 +27,61 @@ import misk.web.mediatype.MediaTypes
class ServiceIndexAction @Inject constructor(
private val config: BackfilaConfig,
private val serviceAutocompleteAction: ServiceAutocompleteAction,
private val dashboardPageLayout: DashboardPageLayout,
) : WebAction {
@Get(PATH)
@ResponseContentType(MediaTypes.TEXT_HTML)
@Authenticated(capabilities = ["users"])
fun get(
@QueryParam sc: String?,
): String {
return buildHtml {
DashboardLayout(
title = "Backfila",
path = PATH,
) {
PageTitle("Services")
val pathBuilder = PathBuilder(path = PATH)
): String = dashboardPageLayout
.newBuilder()
.title("Backfila Home")
.build {
PageTitle("Services")
val pathBuilder = PathBuilder(path = PATH)

div {
attributes["data-controller"] = "search-bar"
div {
attributes["data-controller"] = "search-bar"

// Search Bar
div {
input(
type = InputType.search,
classes = "flex h-10 w-full bg-gray-100 hover:bg-gray-200 duration-500 border-none rounded-lg text-sm",
) {
attributes["data-action"] = "input->search-bar#search"
placeholder = "Search"
}
// Search Bar
div {
input(
type = InputType.search,
classes = "flex h-10 w-full bg-gray-100 hover:bg-gray-200 duration-500 border-none rounded-lg text-sm",
) {
attributes["data-action"] = "input->search-bar#search"
placeholder = "Search"
}
}

// List of Services
val services = serviceAutocompleteAction.getFlattenedServices()
div("py-10") {
ul("grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3") {
role = "list"
// List of Services
val services = serviceAutocompleteAction.getFlattenedServices()
div("py-10") {
ul("grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3") {
role = "list"

services.map { (path, service) ->
li("registration col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow") {
div("flex w-full items-center justify-between space-x-6 p-6") {
div("flex-1 truncate") {
div("flex items-center space-x-3") {
// Don't include default variant in label, only for unique variants
val label = if (path.split("/").last() == "default") service.name else path
val variant = if (path.split("/").last() == "default") null else path.split("/").last()
h3("truncate text-sm font-medium text-gray-900") {
+"""$label (${service.running_backfills})"""
}
variant?.let { span("inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20") { +it } }
services.map { (path, service) ->
li("registration col-span-1 divide-y divide-gray-200 rounded-lg bg-white shadow") {
div("flex w-full items-center justify-between space-x-6 p-6") {
div("flex-1 truncate") {
div("flex items-center space-x-3") {
// Don't include default variant in label, only for unique variants
val label = if (path.split("/").last() == "default") service.name else path
val variant = if (path.split("/").last() == "default") null else path.split("/").last()
h3("truncate text-sm font-medium text-gray-900") {
+"""$label (${service.running_backfills})"""
}
p("mt-1 truncate text-sm text-gray-500") { +"""Regional Paradigm Technician""" }
variant?.let { span("inline-flex shrink-0 items-center rounded-full bg-green-50 px-1.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20") { +it } }
}
p("mt-1 truncate text-sm text-gray-500") { +"""Regional Paradigm Technician""" }
}
div {
div("-mt-px flex divide-x divide-gray-200") {
div("flex w-0 flex-1") {
a(classes = "relative -mr-px inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-bl-lg border border-transparent py-4 text-sm font-semibold text-gray-900") {
href = "mailto:[email protected]"
}
div {
div("-mt-px flex divide-x divide-gray-200") {
div("flex w-0 flex-1") {
a(classes = "relative -mr-px inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-bl-lg border border-transparent py-4 text-sm font-semibold text-gray-900") {
href = "mailto:[email protected]"
// svg("size-5 text-gray-400") {
// viewbox = "0 0 20 20"
// fill = "currentColor"
Expand All @@ -98,12 +96,12 @@ class ServiceIndexAction @Inject constructor(
// "m19 8.839-7.77 3.885a2.75 2.75 0 0 1-2.46 0L1 8.839V14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V8.839Z"
// }
// }
+"""Email"""
}
+"""Email"""
}
div("-ml-px flex w-0 flex-1") {
a(classes = "relative inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-br-lg border border-transparent py-4 text-sm font-semibold text-gray-900") {
href = "tel:+1-202-555-0170"
}
div("-ml-px flex w-0 flex-1") {
a(classes = "relative inline-flex w-0 flex-1 items-center justify-center gap-x-3 rounded-br-lg border border-transparent py-4 text-sm font-semibold text-gray-900") {
href = "tel:+1-202-555-0170"
// svg("size-5 text-gray-400") {
// viewbox = "0 0 20 20"
// fill = "currentColor"
Expand All @@ -116,8 +114,7 @@ class ServiceIndexAction @Inject constructor(
// attributes["clip-rule"] = "evenodd"
// }
// }
+"""Call"""
}
+"""Call"""
}
}
}
Expand All @@ -126,11 +123,10 @@ class ServiceIndexAction @Inject constructor(
}
}
}

AlertSupport(config.support_button_label, config.support_button_url)
}

AlertSupport(config.support_button_label, config.support_button_url)
}
}

companion object {
const val PATH = "/"
Expand Down

0 comments on commit e29a5b0

Please sign in to comment.