mirror of
https://github.com/corda/corda.git
synced 2024-12-28 00:38:55 +00:00
Change tabs to search field in state machine view. Some carving of UI.
This commit is contained in:
parent
e402f4d5af
commit
608550c920
@ -4,7 +4,6 @@ import com.google.common.net.HostAndPort
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.CordaRPCClientConfiguration
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import net.corda.core.node.services.NetworkMapCache.MapChange
|
||||
@ -63,7 +62,9 @@ class NodeMonitorModel {
|
||||
Observable.empty<ProgressTrackingEvent>()
|
||||
}
|
||||
}
|
||||
futureProgressTrackerUpdates.startWith(currentProgressTrackerUpdates).flatMap { it }.subscribe(progressTrackingSubject)
|
||||
|
||||
// We need to retry, because when flow errors, we unsubscribe from progressTrackingSubject. So we end up with stream of state machine updates and no progress trackers.
|
||||
futureProgressTrackerUpdates.startWith(currentProgressTrackerUpdates).flatMap { it }.retry().subscribe(progressTrackingSubject)
|
||||
|
||||
// Now the state machines
|
||||
val currentStateMachines = stateMachines.map { StateMachineUpdate.Added(it) }
|
||||
|
@ -1,20 +1,18 @@
|
||||
package net.corda.client.jfx.model
|
||||
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.beans.property.SimpleObjectProperty
|
||||
import javafx.beans.value.ObservableValue
|
||||
import javafx.collections.FXCollections
|
||||
import net.corda.client.jfx.utils.LeftOuterJoinedMap
|
||||
import net.corda.client.jfx.utils.flatten
|
||||
import net.corda.client.jfx.utils.fold
|
||||
import net.corda.client.jfx.utils.getObservableValues
|
||||
import net.corda.client.jfx.utils.map
|
||||
import net.corda.client.jfx.utils.recordAsAssociation
|
||||
import net.corda.core.ErrorOr
|
||||
import net.corda.core.flows.FlowInitiator
|
||||
import net.corda.core.flows.StateMachineRunId
|
||||
import net.corda.core.messaging.StateMachineInfo
|
||||
import net.corda.core.messaging.StateMachineUpdate
|
||||
import org.fxmisc.easybind.EasyBind
|
||||
import rx.Observable
|
||||
|
||||
data class ProgressTrackingEvent(val stateMachineId: StateMachineRunId, val message: String) {
|
||||
@ -28,20 +26,19 @@ data class ProgressTrackingEvent(val stateMachineId: StateMachineRunId, val mess
|
||||
}
|
||||
}
|
||||
|
||||
data class ProgressStatus(val status: String)
|
||||
data class ProgressStatus(val status: String?)
|
||||
|
||||
sealed class StateMachineStatus {
|
||||
data class Added(val stateMachineName: String, val flowInitiator: FlowInitiator) : StateMachineStatus()
|
||||
data class Removed(val result: ErrorOr<*>) : StateMachineStatus()
|
||||
}
|
||||
|
||||
// TODO StateMachineData and StateMachineInfo
|
||||
data class StateMachineData(
|
||||
val id: StateMachineRunId,
|
||||
val stateMachineName: String,
|
||||
val flowInitiator: FlowInitiator,
|
||||
val addRmStatus: ObservableValue<StateMachineStatus>,
|
||||
val stateMachineStatus: ObservableValue<ProgressStatus?>
|
||||
val stateMachineStatus: ObservableValue<ProgressStatus>
|
||||
)
|
||||
|
||||
class StateMachineDataModel {
|
||||
@ -65,22 +62,17 @@ class StateMachineDataModel {
|
||||
}
|
||||
}
|
||||
|
||||
val stateMachineDataList = LeftOuterJoinedMap(stateMachineStatus, progressEvents) { id, status, progress ->
|
||||
val smStatus = status.value as StateMachineStatus.Added // TODO not always added
|
||||
// todo easybind
|
||||
Bindings.createObjectBinding({
|
||||
StateMachineData(id, smStatus.stateMachineName, smStatus.flowInitiator, status, progress.map { it?.let { ProgressStatus(it.message) } })
|
||||
}, arrayOf(progress, status))
|
||||
}.getObservableValues().flatten()
|
||||
private val stateMachineDataList = LeftOuterJoinedMap(stateMachineStatus, progressEvents) { id, status, progress ->
|
||||
val smStatus = status.value as StateMachineStatus.Added
|
||||
StateMachineData(id, smStatus.stateMachineName, smStatus.flowInitiator, status,
|
||||
EasyBind.map(progress) {
|
||||
if (it == null) {
|
||||
ProgressStatus(null)
|
||||
} else {
|
||||
ProgressStatus(it.message)
|
||||
}
|
||||
})
|
||||
}.getObservableValues()
|
||||
|
||||
val stateMachinesInProgress = stateMachineDataList.filtered { it.addRmStatus.value !is StateMachineStatus.Removed }
|
||||
val stateMachinesDone = stateMachineDataList.filtered { it.addRmStatus.value is StateMachineStatus.Removed }
|
||||
val stateMachinesFinished = stateMachinesDone.filtered {
|
||||
val res = it.addRmStatus.value as StateMachineStatus.Removed
|
||||
res.result.error == null
|
||||
}
|
||||
val stateMachinesError = stateMachinesDone.filtered {
|
||||
val res = it.addRmStatus.value as StateMachineStatus.Removed
|
||||
res.result.error != null
|
||||
}
|
||||
val stateMachinesAll = stateMachineDataList
|
||||
}
|
||||
|
@ -54,7 +54,6 @@ data class PartiallyResolvedTransaction(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Do we want to have mapping tx <-> StateMachine?
|
||||
/**
|
||||
* This model provides an observable list of transactions and what state machines/flows recorded them
|
||||
*/
|
||||
|
@ -18,7 +18,7 @@ open class EventGenerator(val parties: List<Party>, val currencies: List<Currenc
|
||||
protected val issueRefGenerator = Generator.intRange(0, 1).map { number -> OpaqueBytes(ByteArray(1, { number.toByte() })) }
|
||||
protected val amountGenerator = Generator.longRange(10000, 1000000)
|
||||
protected val currencyGenerator = Generator.pickOne(currencies)
|
||||
protected val currencyMap: MutableMap<Currency, Long> = mutableMapOf(USD to 0L, GBP to 0L) // Used for rough estimation of how much money we have in general.
|
||||
protected val currencyMap: MutableMap<Currency, Long> = mutableMapOf(USD to 0L, GBP to 0L) // Used for estimation of how much money we have in general.
|
||||
|
||||
protected fun addToMap(ccy: Currency, amount: Long) {
|
||||
val value = currencyMap[ccy]
|
||||
@ -47,7 +47,7 @@ open class EventGenerator(val parties: List<Party>, val currencies: List<Currenc
|
||||
}
|
||||
|
||||
/**
|
||||
* [Generator]s for incoming/outgoing events of starting different [Cash] flows. It invokes flows that throw exceptions
|
||||
* [Generator]s for incoming/outgoing events of starting different cash flows. It invokes flows that throw exceptions
|
||||
* for use in explorer flow triage. Exceptions are of kind spending/exiting too much cash.
|
||||
*/
|
||||
class ErrorFlowsEventGenerator(parties: List<Party>, currencies: List<Currency>, notary: Party): EventGenerator(parties, currencies, notary) {
|
||||
|
@ -217,7 +217,7 @@ abstract class FlowLogic<out T> {
|
||||
fun track(): Pair<String, Observable<String>>? {
|
||||
// TODO this is not threadsafe, needs an atomic get-step-and-subscribe
|
||||
return progressTracker?.let {
|
||||
it.currentStep.toString() to it.changes.map { it.toString() }
|
||||
it.currentStep.label to it.changes.map { it.toString() }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,14 +5,13 @@ import net.corda.client.mock.ErrorFlowsEventGenerator
|
||||
import net.corda.client.mock.EventGenerator
|
||||
import net.corda.client.mock.Generator
|
||||
import net.corda.client.mock.pickOne
|
||||
import net.corda.client.rpc.CordaRPCClient
|
||||
import net.corda.client.rpc.CordaRPCConnection
|
||||
import net.corda.contracts.asset.Cash
|
||||
import net.corda.core.contracts.Amount
|
||||
import net.corda.core.contracts.GBP
|
||||
import net.corda.core.contracts.USD
|
||||
import net.corda.core.crypto.Party
|
||||
import net.corda.core.failure
|
||||
import net.corda.core.identity.Party
|
||||
import net.corda.core.messaging.CordaRPCOps
|
||||
import net.corda.core.messaging.FlowHandle
|
||||
import net.corda.core.node.services.ServiceInfo
|
||||
@ -154,7 +153,7 @@ class ExplorerSimulation(val options: OptionSet) {
|
||||
is CashFlowCommand.IssueCash -> issuers[command.amount.token]?.let {
|
||||
println("${Instant.now()} [$i] ISSUING ${command.amount} with ref ${command.issueRef} to ${command.recipient}")
|
||||
command.startFlow(it).log(i, "${command.amount.token}Issuer")
|
||||
} ?: command.startFlow(issuers[USD]!!).log(i, "${command.amount.token}Issuer") // TODO workaround
|
||||
}
|
||||
is CashFlowCommand.ExitCash -> issuers[command.amount.token]?.let {
|
||||
println("${Instant.now()} [$i] EXITING ${command.amount} with ref ${command.issueRef}")
|
||||
command.startFlow(it).log(i, "${command.amount.token}Exit")
|
||||
|
@ -5,9 +5,9 @@ import net.corda.core.flows.FlowInitiator
|
||||
object FlowInitiatorFormatter : Formatter<FlowInitiator> {
|
||||
override fun format(value: FlowInitiator): String {
|
||||
return when (value) {
|
||||
is FlowInitiator.Scheduled -> "Started by scheduled state:: " + value.scheduledState.ref.toString() // TODO format that
|
||||
is FlowInitiator.Scheduled -> "Started by scheduled state:: " + value.scheduledState.ref.toString() // TODO How do we want to format that?
|
||||
is FlowInitiator.Shell -> "Started via shell"
|
||||
is FlowInitiator.Peer -> "Peer legal name: " + value.party.name //TODO format that
|
||||
is FlowInitiator.Peer -> "Peer legal name: " + PartyNameFormatter.short.format(value.party.name)
|
||||
is FlowInitiator.RPC -> "Rpc username: " + value.username
|
||||
}
|
||||
}
|
||||
|
@ -198,8 +198,8 @@ fun identicon(secureHash: SecureHash, size: Double): ImageView {
|
||||
}
|
||||
}
|
||||
|
||||
fun identiconToolTip(secureHash: SecureHash): Tooltip {
|
||||
return Tooltip(Splitter.fixedLength(16).split("$secureHash").joinToString("\n")).apply {
|
||||
fun identiconToolTip(secureHash: SecureHash, description: String? = null): Tooltip {
|
||||
return Tooltip(Splitter.fixedLength(16).split("${description ?: secureHash}").joinToString("\n")).apply {
|
||||
contentDisplay = ContentDisplay.TOP
|
||||
textAlignment = TextAlignment.CENTER
|
||||
graphic = identicon(secureHash, 90.0)
|
||||
|
@ -23,7 +23,8 @@ import tornadofx.*
|
||||
* TODO : Predictive text?
|
||||
* TODO : Regex?
|
||||
*/
|
||||
class SearchField<T>(private val data: ObservableList<T>, vararg filterCriteria: Pair<String, (T, String) -> Boolean>) : UIComponent() {
|
||||
class SearchField<T>(private val data: ObservableList<T>, val filterCriteria: List<Pair<String, (T, String) -> Boolean>>,
|
||||
val disabledFields: List<String> = emptyList()) : UIComponent() {
|
||||
override val root: Parent by fxml()
|
||||
private val textField by fxid<TextField>()
|
||||
private val clearButton by fxid<Node>()
|
||||
@ -34,7 +35,7 @@ class SearchField<T>(private val data: ObservableList<T>, vararg filterCriteria:
|
||||
val text = textField.text
|
||||
val category = searchCategory.value
|
||||
data.filtered { data ->
|
||||
text.isNullOrBlank() || if (category == ALL) {
|
||||
(text.isNullOrBlank() && textField.isVisible) || if (category == ALL) {
|
||||
filterCriteria.any { it.second(data, text) }
|
||||
} else {
|
||||
filterCriteria.toMap()[category]?.invoke(data, text) ?: false
|
||||
@ -73,5 +74,7 @@ class SearchField<T>(private val data: ObservableList<T>, vararg filterCriteria:
|
||||
}
|
||||
"Filter by $category."
|
||||
})
|
||||
textField.visibleProperty().bind(searchCategory.valueProperty().map { it !in disabledFields })
|
||||
// TODO Maybe it will be better to replace these categories with comboBox? For example Result with choice: succes, in progress, error.
|
||||
}
|
||||
}
|
@ -5,14 +5,10 @@ import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView
|
||||
import javafx.beans.binding.Bindings
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.geometry.HPos
|
||||
import javafx.geometry.Insets
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.Parent
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.control.ScrollPane
|
||||
import javafx.scene.control.TabPane
|
||||
import javafx.scene.control.TableView
|
||||
import javafx.scene.control.TitledPane
|
||||
import javafx.scene.layout.BorderPane
|
||||
import javafx.scene.layout.GridPane
|
||||
import javafx.scene.layout.VBox
|
||||
@ -37,33 +33,29 @@ import net.corda.explorer.model.CordaWidget
|
||||
import net.corda.explorer.ui.setCustomCellFactory
|
||||
import tornadofx.*
|
||||
|
||||
// TODO Rethink whole idea of showing communication as table, it should be tree view for each StateMachine (with subflows and other communication)
|
||||
// TODO Rethink whole idea of showing communication as table, it should be tree view for each StateMachine (with subflows).
|
||||
class StateMachineViewer : CordaView("Flow Triage") {
|
||||
override val root by fxml<TabPane>()
|
||||
override val root by fxml<BorderPane>()
|
||||
override val icon = FontAwesomeIcon.HEARTBEAT
|
||||
override val widgets = listOf(CordaWidget(title, StateMachineViewer.StateMachineWidget())).observable()
|
||||
private val progressViewTable by fxid<TableView<StateMachineData>>()
|
||||
private val doneViewTable by fxid<TableView<StateMachineData>>()
|
||||
private val errorViewTable by fxid<TableView<StateMachineData>>()
|
||||
private val allViewTable by fxid<TableView<StateMachineData>>()
|
||||
private val matchingFlowsLabel by fxid<Label>()
|
||||
|
||||
private val stateMachinesAll by observableList(StateMachineDataModel::stateMachinesAll)
|
||||
|
||||
private class StateMachineWidget : BorderPane() {
|
||||
private val flows by observableListReadOnly(StateMachineDataModel::stateMachinesInProgress)
|
||||
private val stateMachinesAll by observableListReadOnly(StateMachineDataModel::stateMachinesAll)
|
||||
|
||||
// TODO can add stats: in progress, errored, maybe done to the widget?
|
||||
init {
|
||||
right {
|
||||
label {
|
||||
textProperty().bind(Bindings.size(flows).map(Number::toString))
|
||||
textProperty().bind(Bindings.size(stateMachinesAll).map(Number::toString))
|
||||
BorderPane.setAlignment(this, Pos.BOTTOM_RIGHT)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val stateMachinesInProgress by observableList(StateMachineDataModel::stateMachinesInProgress)
|
||||
private val stateMachinesFinished by observableList(StateMachineDataModel::stateMachinesFinished)
|
||||
private val stateMachinesError by observableList(StateMachineDataModel::stateMachinesError)
|
||||
|
||||
fun makeColumns(table: TableView<StateMachineData>, tableItems: ObservableList<StateMachineData>, withResult: Boolean = true) {
|
||||
table.apply {
|
||||
items = tableItems
|
||||
@ -76,23 +68,21 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
maxWidth = 26.0
|
||||
}
|
||||
}
|
||||
column("ID", StateMachineData::id) { // TODO kill that ID column
|
||||
// TODO Kill that ID column or replace it with something useful when we will have flow audit utilities.
|
||||
// For now it's rather for visual purpose, so you can observe flow.
|
||||
column("ID", StateMachineData::id) {
|
||||
minWidth = 100.0
|
||||
maxWidth = 200.0
|
||||
}.setCustomCellFactory {
|
||||
label("$it") {
|
||||
val hash = SecureHash.sha256(it.toString())
|
||||
graphic = identicon(hash, 15.0)
|
||||
tooltip = identiconToolTip(hash) //TODO Have id instead of hash.
|
||||
tooltip = identiconToolTip(hash, it.toString())
|
||||
}
|
||||
}
|
||||
column("Flow name", StateMachineData::stateMachineName).cellFormat { text = FlowNameFormatter.boring.format(it) }
|
||||
column("Initiator", StateMachineData::flowInitiator).cellFormat { text = FlowInitiatorFormatter.format(it) }
|
||||
column("Flow Status", StateMachineData::stateMachineStatus).cellFormat {
|
||||
if (it == null)
|
||||
text = "No progress data"
|
||||
else text = it.status
|
||||
} // TODO null
|
||||
column("Flow Status", StateMachineData::stateMachineStatus).cellFormat { text = it.status ?: "No progress data" }
|
||||
column("Result", StateMachineData::addRmStatus).setCustomCellFactory {
|
||||
if (it is StateMachineStatus.Removed) {
|
||||
if (it.result.error == null) {
|
||||
@ -115,8 +105,7 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
}
|
||||
else {
|
||||
label("In progress") {
|
||||
// TODO Other icons: spnner, hourglass-half, hourglass-1, send-o, space-shuttle ;)
|
||||
graphic = FontAwesomeIconView(FontAwesomeIcon.ROCKET).apply {
|
||||
graphic = FontAwesomeIconView(FontAwesomeIcon.ROCKET).apply { // Blazing fast! Try not to blink.
|
||||
glyphSize = 15.0
|
||||
textAlignment = TextAlignment.CENTER
|
||||
style = "-fx-fill: lightslategrey"
|
||||
@ -128,15 +117,33 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
}
|
||||
|
||||
init {
|
||||
makeColumns(progressViewTable, stateMachinesInProgress, false)
|
||||
makeColumns(doneViewTable, stateMachinesFinished)
|
||||
makeColumns(errorViewTable, stateMachinesError)
|
||||
val searchField = SearchField(stateMachinesAll, listOf(
|
||||
"Flow name" to { sm, s -> sm.stateMachineName.contains(s, true) },
|
||||
"Initiator" to { sm, s -> FlowInitiatorFormatter.format(sm.flowInitiator).contains(s, true) },
|
||||
"Flow Status" to { sm, s -> val stat = sm.stateMachineStatus.value.status ?: "No progress data"
|
||||
stat.contains(s, true) },
|
||||
"Error" to { sm, _ -> val smAddRm = sm.addRmStatus.value
|
||||
if (smAddRm is StateMachineStatus.Removed)
|
||||
smAddRm.result.error != null
|
||||
else false },
|
||||
"Done" to { sm, _ -> val smAddRm = sm.addRmStatus.value
|
||||
if (smAddRm is StateMachineStatus.Removed)
|
||||
smAddRm.result.error == null
|
||||
else false },
|
||||
"In progress" to { sm, _ -> sm.addRmStatus.value !is StateMachineStatus.Removed }),
|
||||
listOf("Error", "Done", "In progress")
|
||||
)
|
||||
root.top = searchField.root
|
||||
makeColumns(allViewTable, searchField.filteredData)
|
||||
matchingFlowsLabel.textProperty().bind(Bindings.size(allViewTable.items).map {
|
||||
"$it matching flow${if (it == 1) "" else "s"}"
|
||||
})
|
||||
}
|
||||
|
||||
private inner class StateMachineDetailsView(val smmData: StateMachineData) : Fragment() {
|
||||
override val root by fxml<Parent>()
|
||||
private val flowNameLabel by fxid<Label>()
|
||||
private val flowProgressPane by fxid<TitledPane>()
|
||||
private val flowProgressVBox by fxid<VBox>()
|
||||
private val flowInitiatorGrid by fxid<GridPane>()
|
||||
private val flowResultVBox by fxid<VBox>()
|
||||
|
||||
@ -144,9 +151,10 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
flowNameLabel.apply {
|
||||
text = FlowNameFormatter.boring.format(smmData.stateMachineName)
|
||||
}
|
||||
flowProgressPane.apply {
|
||||
content = label {
|
||||
text = smmData.stateMachineStatus.value?.status // TODO later we can do some magic with showing progress steps with subflows
|
||||
//TODO It would be nice to have flow graph with showing progress steps with subflows + timestamps (left it for second iteration).
|
||||
flowProgressVBox.apply {
|
||||
label {
|
||||
text = smmData.stateMachineStatus.value?.status ?: "No progress data"
|
||||
}
|
||||
}
|
||||
when (smmData.flowInitiator) {
|
||||
@ -164,27 +172,27 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
|
||||
private fun <T>makeResultVBox(vbox: VBox, result: T) {
|
||||
if (result == null) {
|
||||
vbox.apply { label("No return value from flow.") }
|
||||
vbox.apply { label("No return value from flow.").apply { style { fontWeight = FontWeight.BOLD } } }
|
||||
} else if (result is SignedTransaction) {
|
||||
// scrollpane {
|
||||
// hbarPolicy = ScrollPane.ScrollBarPolicy.AS_NEEDED
|
||||
// vbarPolicy = ScrollPane.ScrollBarPolicy.NEVER
|
||||
// TODO Make link to transaction view
|
||||
vbox.apply {
|
||||
label("Signed transaction")
|
||||
label("Signed transaction").apply { style { fontWeight = FontWeight.BOLD } }
|
||||
label {
|
||||
text = result.id.toString()
|
||||
graphic = identicon(result.id, 30.0)
|
||||
tooltip = identiconToolTip(result.id)
|
||||
}
|
||||
// }
|
||||
|
||||
}
|
||||
} else if (result is Unit) {
|
||||
vbox.apply { label("Flow completed with success.") }
|
||||
vbox.apply { label("Flow completed with success.").apply { style { fontWeight = FontWeight.BOLD } } }
|
||||
}
|
||||
else {
|
||||
// TODO Here we could have sth different than SignedTransaction
|
||||
vbox.apply { label(result.toString()) }
|
||||
// TODO Here we could have sth different than SignedTransaction/Unit
|
||||
vbox.apply {
|
||||
label("Flow completed with success. Result: ").apply { style { fontWeight = FontWeight.BOLD } }
|
||||
label(result.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -198,21 +206,15 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO border styling?
|
||||
vbox.apply {
|
||||
vbox {
|
||||
spacing = 10.0
|
||||
label { text = error::class.simpleName }
|
||||
scrollpane { //TODO do that error scroll pane nicely
|
||||
hbarPolicy = ScrollPane.ScrollBarPolicy.AS_NEEDED
|
||||
vbarPolicy = ScrollPane.ScrollBarPolicy.NEVER
|
||||
label { text = error.message }
|
||||
}
|
||||
label { text = error.message }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO test
|
||||
private fun makeShellGrid(gridPane: GridPane) {
|
||||
val title = gridPane.lookup("#flowInitiatorTitle") as Label
|
||||
title.apply {
|
||||
@ -226,9 +228,6 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
text = "Flow started by a peer node"
|
||||
}
|
||||
gridPane.apply{
|
||||
// scrollpane { // TODO scrollbar vbox + hbox
|
||||
// hbarPolicy = ScrollPane.ScrollBarPolicy.AS_NEEDED
|
||||
// vbarPolicy = ScrollPane.ScrollBarPolicy.NEVER
|
||||
row {
|
||||
label("Legal name: ") {
|
||||
gridpaneConstraints { hAlignment = HPos.LEFT }
|
||||
@ -236,7 +235,7 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
minWidth = 150.0
|
||||
prefWidth = 150.0
|
||||
}
|
||||
label(initiator.party.name) { gridpaneConstraints { hAlignment = HPos.LEFT } }
|
||||
label(initiator.party.name.toString()) { gridpaneConstraints { hAlignment = HPos.LEFT } }
|
||||
}
|
||||
row {
|
||||
label("Owning key: ") {
|
||||
@ -269,7 +268,7 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
|
||||
// TODO test
|
||||
private fun makeScheduledGrid(gridPane: GridPane, initiator: FlowInitiator.Scheduled) {
|
||||
val title = gridPane.lookup("flowInitiatorTitle") as Label
|
||||
val title = gridPane.lookup("#flowInitiatorTitle") as Label
|
||||
title.apply {
|
||||
text = "Flow started as scheduled activity"
|
||||
}
|
||||
@ -280,7 +279,7 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
style { fontWeight = FontWeight.BOLD }
|
||||
prefWidth = 150.0
|
||||
}
|
||||
label(initiator.scheduledState.ref.toString()) { gridpaneConstraints { hAlignment = HPos.LEFT } } //TODO format
|
||||
label(initiator.scheduledState.ref.toString()) { gridpaneConstraints { hAlignment = HPos.LEFT } }
|
||||
}
|
||||
row {
|
||||
label("Scheduled at: ") {
|
||||
@ -288,7 +287,7 @@ class StateMachineViewer : CordaView("Flow Triage") {
|
||||
style { fontWeight = FontWeight.BOLD }
|
||||
prefWidth = 150.0
|
||||
}
|
||||
label(initiator.scheduledState.scheduledAt.toString()) { gridpaneConstraints { hAlignment = HPos.LEFT } } //TODO format
|
||||
label(initiator.scheduledState.scheduledAt.toString()) { gridpaneConstraints { hAlignment = HPos.LEFT } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,13 +105,13 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
)
|
||||
}
|
||||
|
||||
val searchField = SearchField(transactions,
|
||||
val searchField = SearchField(transactions, listOf(
|
||||
"Transaction ID" to { tx, s -> "${tx.id}".contains(s, true) },
|
||||
"Input" to { tx, s -> tx.inputs.resolved.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } },
|
||||
"Output" to { tx, s -> tx.outputs.any { it.state.data.contract.javaClass.simpleName.contains(s, true) } },
|
||||
"Input Party" to { tx, s -> tx.inputParties.any { it.any { it.value?.legalIdentity?.name?.commonName?.contains(s, true) ?: false } } },
|
||||
"Output Party" to { tx, s -> tx.outputParties.any { it.any { it.value?.legalIdentity?.name?.commonName?.contains(s, true) ?: false } } },
|
||||
"Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } }
|
||||
"Command Type" to { tx, s -> tx.commandTypes.any { it.simpleName.contains(s, true) } })
|
||||
)
|
||||
root.top = searchField.root
|
||||
// Transaction table
|
||||
@ -163,6 +163,7 @@ class TransactionViewer : CordaView("Transactions") {
|
||||
prefHeight = 400.0
|
||||
}.apply {
|
||||
// Column stays the same size, but we don't violate column restricted resize policy for the whole table view.
|
||||
// It removes that irritating column at the end of table that does nothing.
|
||||
minWidth = 26.0
|
||||
maxWidth = 26.0
|
||||
}
|
||||
|
@ -146,9 +146,9 @@ class CashViewer : CordaView("Cash") {
|
||||
* one which produces more results, which seems to work, as the set of currency strings don't really overlap with
|
||||
* issuer strings.
|
||||
*/
|
||||
val searchField = SearchField(cashStates,
|
||||
val searchField = SearchField(cashStates, listOf(
|
||||
"Currency" to { state, text -> state.state.data.amount.token.product.toString().contains(text, true) },
|
||||
"Issuer" to { state, text -> state.resolveIssuer().value?.name?.commonName?.contains(text, true) ?: false }
|
||||
"Issuer" to { state, text -> state.resolveIssuer().value?.name?.commonName?.contains(text, true) ?: false })
|
||||
)
|
||||
root.top = hbox(5.0) {
|
||||
button("New Transaction", FontAwesomeIconView(FontAwesomeIcon.PLUS)) {
|
||||
|
@ -269,4 +269,11 @@
|
||||
|
||||
.connection-bank-to-regulator {
|
||||
-fx-stroke: red;
|
||||
}
|
||||
}
|
||||
|
||||
/* Flow details */
|
||||
.smm-detail-grid {
|
||||
-fx-border-color: -color-3;
|
||||
-fx-border-width: 1;
|
||||
-fx-border-radius: 2;
|
||||
}
|
||||
|
@ -2,47 +2,54 @@
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.ScrollPane?>
|
||||
<?import javafx.scene.control.TitledPane?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<GridPane stylesheets="@../css/corda.css" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<GridPane styleClass="smm-detail-grid" stylesheets="@../css/corda.css" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5" />
|
||||
</padding>
|
||||
<TitledPane fx:id="flowNamePane" collapsible="false" text="Flow name" GridPane.columnSpan="2" GridPane.fillWidth="true" GridPane.rowIndex="0">
|
||||
<!--TODO text styling-->
|
||||
<Label fx:id="flowNameLabel"/>
|
||||
<Label fx:id="flowNameLabel" style="-fx-font-weight: bold"/>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="flowInitiatorPane" collapsible="false" maxHeight="Infinity" text="Flow initiator"
|
||||
GridPane.columnIndex="0" GridPane.fillWidth="true" GridPane.rowIndex="1">
|
||||
<GridPane fx:id="flowInitiatorGrid" vgap="10.0" hgap="10.0">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<!--TODO text styling-->
|
||||
<Label fx:id="flowInitiatorTitle" GridPane.columnSpan="2" GridPane.rowIndex="0" GridPane.columnIndex="1" GridPane.halignment="LEFT"/>
|
||||
</GridPane>
|
||||
<TitledPane fx:id="flowInitiatorPane" collapsible="false" maxHeight="Infinity" text="Flow initiator" GridPane.columnIndex="0" GridPane.fillWidth="true" GridPane.rowIndex="1">
|
||||
<ScrollPane maxHeight="Infinity" minHeight="120">
|
||||
<GridPane fx:id="flowInitiatorGrid" hgap="10.0" vgap="10.0">
|
||||
<padding>
|
||||
<Insets bottom="5.0" left="5.0" right="10.0" top="10.0" />
|
||||
</padding>
|
||||
<Label fx:id="flowInitiatorTitle" GridPane.columnIndex="0" GridPane.columnSpan="2" GridPane.halignment="LEFT" GridPane.rowIndex="0" style="-fx-font-weight: bold"/>
|
||||
</GridPane>
|
||||
</ScrollPane>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="flowResultPane" collapsible="false" maxHeight="Infinity" text="Result" GridPane.columnIndex="1" GridPane.rowIndex="1">
|
||||
<VBox fx:id="flowResultVBox" spacing="10.0">
|
||||
<ScrollPane maxHeight="Infinity">
|
||||
<VBox fx:id="flowResultVBox" spacing="10.0">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5" />
|
||||
</padding>
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="flowProgressPane" collapsible="false" text="Progress steps" GridPane.columnSpan="2" GridPane.rowIndex="2">
|
||||
<!--TODO add progress graph-->
|
||||
<VBox fx:id="flowProgressVBox" spacing="10.0">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5"/>
|
||||
<Insets bottom="5" left="5" right="5" top="5" />
|
||||
</padding>
|
||||
</VBox>
|
||||
</TitledPane>
|
||||
<TitledPane fx:id="flowProgressPane" collapsible="false" maxWidth="Infinity" text="Progress steps" GridPane.columnSpan="2" GridPane.rowIndex="2">
|
||||
<!--TODO add progress graph-->
|
||||
</TitledPane>
|
||||
<columnConstraints>
|
||||
<ColumnConstraints minWidth="450.0"/>
|
||||
<ColumnConstraints hgrow="ALWAYS"/>
|
||||
<ColumnConstraints minWidth="450.0" />
|
||||
<ColumnConstraints hgrow="ALWAYS" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints vgrow="ALWAYS"/>
|
||||
<RowConstraints vgrow="ALWAYS"/>
|
||||
<RowConstraints vgrow="ALWAYS"/>
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
<RowConstraints vgrow="ALWAYS" />
|
||||
</rowConstraints>
|
||||
</GridPane>
|
||||
|
@ -3,36 +3,22 @@
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.TableView?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
|
||||
<?import javafx.scene.control.TabPane?>
|
||||
<?import javafx.scene.control.Tab?>
|
||||
<TabPane stylesheets="@../css/corda.css" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1"
|
||||
tabClosingPolicy="UNAVAILABLE">
|
||||
<BorderPane stylesheets="@../css/corda.css" xmlns="http://javafx.com/javafx/8.0.76-ea"
|
||||
xmlns:fx="http://javafx.com/fxml/1">
|
||||
<padding>
|
||||
<Insets bottom="5" left="5" right="5" top="5"/>
|
||||
<Insets top="5" left="5" right="5" bottom="5"/>
|
||||
</padding>
|
||||
<Tab text="In progress" fx:id="progressFlowsTab">
|
||||
<TableView fx:id="progressViewTable" VBox.vgrow="ALWAYS">
|
||||
<center>
|
||||
<TableView fx:id="allViewTable" VBox.vgrow="ALWAYS">
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||
</columnResizePolicy>
|
||||
</TableView>
|
||||
</Tab>
|
||||
<Tab text="Finished" fx:id="doneFlowsTab">
|
||||
<TableView fx:id="doneViewTable" VBox.vgrow="ALWAYS">
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||
</columnResizePolicy>
|
||||
</TableView>
|
||||
</Tab>
|
||||
<Tab text="With errors" fx:id="errorFlowsTab">
|
||||
<TableView fx:id="errorViewTable" VBox.vgrow="ALWAYS">
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
|
||||
</columnResizePolicy>
|
||||
</TableView>
|
||||
</Tab>
|
||||
<!--<bottom>-->
|
||||
<!--<Label fx:id="matchingFlowsLabel" text="matching flow(s)" />-->
|
||||
<!--</bottom>-->
|
||||
</TabPane>
|
||||
</center>
|
||||
<bottom>
|
||||
<Label fx:id="matchingFlowsLabel" text="matching flows(s)"/>
|
||||
</bottom>
|
||||
</BorderPane>
|
||||
|
Loading…
Reference in New Issue
Block a user