diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala index 8f3429e..6b129ab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala @@ -6,6 +6,7 @@ import cz.e_bs.cmi.mdr.pdb.UserInfo import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage import pages.detail.DetailKriteriaPage +import pages.detail.components.DukazyKriteria import com.raquo.waypoint.Router import cz.e_bs.cmi.mdr.pdb.app.components.AppPage import cz.e_bs.cmi.mdr.pdb.ParameterCriteria @@ -28,7 +29,9 @@ (p.osobniCislo.value, p.parametr.value, p.kriterium.value) )((x, _, _) => x) val $pageChangeSignal = - $paramChangeSignal.map(FetchParameterCriteria(_, _, _)) + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.DetailKriteria(_, _, _)) + ) val $data = state.details.startWithNone val $params = state.parameters.startWithNone @@ -46,7 +49,18 @@ def apply: HtmlElement = AppPage(state.actionBus)( $merged.map(_.map(buildModel)) - .split(_ => ())((_, _, s) => DetailKriteriaPage(s)), + .split(_ => ())((_, s, $s) => + DetailKriteriaPage($s)(state.actionBus.contramap { + case DukazyKriteria.Add => + NavigateTo( + Page.NovyDukazKriteria( + Page.Titled(s.osoba.osobniCislo, Some(s.osoba.jmeno)), + Page.Titled(s.parametr.id, Some(s.parametr.nazev)), + Page.Titled(s.kriterium.id, Some(s.kriterium.id)) + ) + ) + }) + ), $pageChangeSignal --> state.actionBus ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala index 8f3429e..6b129ab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala @@ -6,6 +6,7 @@ import cz.e_bs.cmi.mdr.pdb.UserInfo import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage import pages.detail.DetailKriteriaPage +import pages.detail.components.DukazyKriteria import com.raquo.waypoint.Router import cz.e_bs.cmi.mdr.pdb.app.components.AppPage import cz.e_bs.cmi.mdr.pdb.ParameterCriteria @@ -28,7 +29,9 @@ (p.osobniCislo.value, p.parametr.value, p.kriterium.value) )((x, _, _) => x) val $pageChangeSignal = - $paramChangeSignal.map(FetchParameterCriteria(_, _, _)) + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.DetailKriteria(_, _, _)) + ) val $data = state.details.startWithNone val $params = state.parameters.startWithNone @@ -46,7 +49,18 @@ def apply: HtmlElement = AppPage(state.actionBus)( $merged.map(_.map(buildModel)) - .split(_ => ())((_, _, s) => DetailKriteriaPage(s)), + .split(_ => ())((_, s, $s) => + DetailKriteriaPage($s)(state.actionBus.contramap { + case DukazyKriteria.Add => + NavigateTo( + Page.NovyDukazKriteria( + Page.Titled(s.osoba.osobniCislo, Some(s.osoba.jmeno)), + Page.Titled(s.parametr.id, Some(s.parametr.nazev)), + Page.Titled(s.kriterium.id, Some(s.kriterium.id)) + ) + ) + }) + ), $pageChangeSignal --> state.actionBus ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala new file mode 100644 index 0000000..1d83006 --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala @@ -0,0 +1,66 @@ +package cz.e_bs.cmi.mdr.pdb.app +package connectors + +import com.raquo.laminar.api.L.{*, given} +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage +import pages.detail.DetailKriteriaPage +import pages.detail.NovyDukazKriteriaPage +import com.raquo.waypoint.Router +import cz.e_bs.cmi.mdr.pdb.app.components.AppPage +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria + +// TODO: extract common pieces for all detail kriteria pages +object NovyDukazKriteriaPageConnector { + trait AppState { + def details: EventStream[UserInfo] + def parameters: EventStream[List[Parameter]] + def actionBus: Observer[Action] + } +} + +case class NovyDukazKriteriaPageConnector( + state: NovyDukazKriteriaPageConnector.AppState +)( + $page: Signal[Page.NovyDukazKriteria] +)(using Router[Page]): + val $paramChangeSignal = + $page.splitOne(p => + (p.osobniCislo.value, p.parametr.value, p.kriterium.value) + )((x, _, _) => x) + val $pageChangeSignal = + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.NovyDukazKriteria(_, _, _)) + ) + + val $data = state.details.startWithNone + val $params = state.parameters.startWithNone + + val $merged = + $data.combineWithFn($params, $paramChangeSignal)((d, p, pc) => + for { + da <- d + pa <- p + pb <- pa.find(_.id == pc._2) + ka <- pb.criteria.find(_.id == pc._3) + } yield (da, pb, ka) + ) + + def apply: HtmlElement = + AppPage(state.actionBus)( + $merged.map(_.map(buildModel)) + .split(_ => ())((_, _, $s) => NovyDukazKriteriaPage($s)), + $pageChangeSignal --> state.actionBus + ) + + private def buildModel( + o: UserInfo, + p: Parameter, + k: ParameterCriteria + ): NovyDukazKriteriaPage.ViewModel = + NovyDukazKriteriaPage.ViewModel( + o.toDetailOsoby, + p.toParametr(_ => a()), + k.toKriterium(_ => a()) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala index 8f3429e..6b129ab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala @@ -6,6 +6,7 @@ import cz.e_bs.cmi.mdr.pdb.UserInfo import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage import pages.detail.DetailKriteriaPage +import pages.detail.components.DukazyKriteria import com.raquo.waypoint.Router import cz.e_bs.cmi.mdr.pdb.app.components.AppPage import cz.e_bs.cmi.mdr.pdb.ParameterCriteria @@ -28,7 +29,9 @@ (p.osobniCislo.value, p.parametr.value, p.kriterium.value) )((x, _, _) => x) val $pageChangeSignal = - $paramChangeSignal.map(FetchParameterCriteria(_, _, _)) + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.DetailKriteria(_, _, _)) + ) val $data = state.details.startWithNone val $params = state.parameters.startWithNone @@ -46,7 +49,18 @@ def apply: HtmlElement = AppPage(state.actionBus)( $merged.map(_.map(buildModel)) - .split(_ => ())((_, _, s) => DetailKriteriaPage(s)), + .split(_ => ())((_, s, $s) => + DetailKriteriaPage($s)(state.actionBus.contramap { + case DukazyKriteria.Add => + NavigateTo( + Page.NovyDukazKriteria( + Page.Titled(s.osoba.osobniCislo, Some(s.osoba.jmeno)), + Page.Titled(s.parametr.id, Some(s.parametr.nazev)), + Page.Titled(s.kriterium.id, Some(s.kriterium.id)) + ) + ) + }) + ), $pageChangeSignal --> state.actionBus ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala new file mode 100644 index 0000000..1d83006 --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala @@ -0,0 +1,66 @@ +package cz.e_bs.cmi.mdr.pdb.app +package connectors + +import com.raquo.laminar.api.L.{*, given} +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage +import pages.detail.DetailKriteriaPage +import pages.detail.NovyDukazKriteriaPage +import com.raquo.waypoint.Router +import cz.e_bs.cmi.mdr.pdb.app.components.AppPage +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria + +// TODO: extract common pieces for all detail kriteria pages +object NovyDukazKriteriaPageConnector { + trait AppState { + def details: EventStream[UserInfo] + def parameters: EventStream[List[Parameter]] + def actionBus: Observer[Action] + } +} + +case class NovyDukazKriteriaPageConnector( + state: NovyDukazKriteriaPageConnector.AppState +)( + $page: Signal[Page.NovyDukazKriteria] +)(using Router[Page]): + val $paramChangeSignal = + $page.splitOne(p => + (p.osobniCislo.value, p.parametr.value, p.kriterium.value) + )((x, _, _) => x) + val $pageChangeSignal = + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.NovyDukazKriteria(_, _, _)) + ) + + val $data = state.details.startWithNone + val $params = state.parameters.startWithNone + + val $merged = + $data.combineWithFn($params, $paramChangeSignal)((d, p, pc) => + for { + da <- d + pa <- p + pb <- pa.find(_.id == pc._2) + ka <- pb.criteria.find(_.id == pc._3) + } yield (da, pb, ka) + ) + + def apply: HtmlElement = + AppPage(state.actionBus)( + $merged.map(_.map(buildModel)) + .split(_ => ())((_, _, $s) => NovyDukazKriteriaPage($s)), + $pageChangeSignal --> state.actionBus + ) + + private def buildModel( + o: UserInfo, + p: Parameter, + k: ParameterCriteria + ): NovyDukazKriteriaPage.ViewModel = + NovyDukazKriteriaPage.ViewModel( + o.toDetailOsoby, + p.toParametr(_ => a()), + k.toKriterium(_ => a()) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala index 7729919..9ef0698 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala @@ -12,7 +12,9 @@ kriterium: DetailKriteria.ViewModel ) - def apply($m: Signal[ViewModel]): HtmlElement = + def apply($m: Signal[ViewModel])( + action: Observer[DukazyKriteria.Action] + ): HtmlElement = div( cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", div( @@ -23,7 +25,7 @@ ), div( DetailKriteria($m.map(_.kriterium)), - DukazyKriteria($m.map(_.kriterium.dukazy)) + DukazyKriteria($m.map(_.kriterium.dukazy))(action) ) ) ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala index 8f3429e..6b129ab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala @@ -6,6 +6,7 @@ import cz.e_bs.cmi.mdr.pdb.UserInfo import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage import pages.detail.DetailKriteriaPage +import pages.detail.components.DukazyKriteria import com.raquo.waypoint.Router import cz.e_bs.cmi.mdr.pdb.app.components.AppPage import cz.e_bs.cmi.mdr.pdb.ParameterCriteria @@ -28,7 +29,9 @@ (p.osobniCislo.value, p.parametr.value, p.kriterium.value) )((x, _, _) => x) val $pageChangeSignal = - $paramChangeSignal.map(FetchParameterCriteria(_, _, _)) + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.DetailKriteria(_, _, _)) + ) val $data = state.details.startWithNone val $params = state.parameters.startWithNone @@ -46,7 +49,18 @@ def apply: HtmlElement = AppPage(state.actionBus)( $merged.map(_.map(buildModel)) - .split(_ => ())((_, _, s) => DetailKriteriaPage(s)), + .split(_ => ())((_, s, $s) => + DetailKriteriaPage($s)(state.actionBus.contramap { + case DukazyKriteria.Add => + NavigateTo( + Page.NovyDukazKriteria( + Page.Titled(s.osoba.osobniCislo, Some(s.osoba.jmeno)), + Page.Titled(s.parametr.id, Some(s.parametr.nazev)), + Page.Titled(s.kriterium.id, Some(s.kriterium.id)) + ) + ) + }) + ), $pageChangeSignal --> state.actionBus ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala new file mode 100644 index 0000000..1d83006 --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala @@ -0,0 +1,66 @@ +package cz.e_bs.cmi.mdr.pdb.app +package connectors + +import com.raquo.laminar.api.L.{*, given} +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage +import pages.detail.DetailKriteriaPage +import pages.detail.NovyDukazKriteriaPage +import com.raquo.waypoint.Router +import cz.e_bs.cmi.mdr.pdb.app.components.AppPage +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria + +// TODO: extract common pieces for all detail kriteria pages +object NovyDukazKriteriaPageConnector { + trait AppState { + def details: EventStream[UserInfo] + def parameters: EventStream[List[Parameter]] + def actionBus: Observer[Action] + } +} + +case class NovyDukazKriteriaPageConnector( + state: NovyDukazKriteriaPageConnector.AppState +)( + $page: Signal[Page.NovyDukazKriteria] +)(using Router[Page]): + val $paramChangeSignal = + $page.splitOne(p => + (p.osobniCislo.value, p.parametr.value, p.kriterium.value) + )((x, _, _) => x) + val $pageChangeSignal = + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.NovyDukazKriteria(_, _, _)) + ) + + val $data = state.details.startWithNone + val $params = state.parameters.startWithNone + + val $merged = + $data.combineWithFn($params, $paramChangeSignal)((d, p, pc) => + for { + da <- d + pa <- p + pb <- pa.find(_.id == pc._2) + ka <- pb.criteria.find(_.id == pc._3) + } yield (da, pb, ka) + ) + + def apply: HtmlElement = + AppPage(state.actionBus)( + $merged.map(_.map(buildModel)) + .split(_ => ())((_, _, $s) => NovyDukazKriteriaPage($s)), + $pageChangeSignal --> state.actionBus + ) + + private def buildModel( + o: UserInfo, + p: Parameter, + k: ParameterCriteria + ): NovyDukazKriteriaPage.ViewModel = + NovyDukazKriteriaPage.ViewModel( + o.toDetailOsoby, + p.toParametr(_ => a()), + k.toKriterium(_ => a()) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala index 7729919..9ef0698 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala @@ -12,7 +12,9 @@ kriterium: DetailKriteria.ViewModel ) - def apply($m: Signal[ViewModel]): HtmlElement = + def apply($m: Signal[ViewModel])( + action: Observer[DukazyKriteria.Action] + ): HtmlElement = div( cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", div( @@ -23,7 +25,7 @@ ), div( DetailKriteria($m.map(_.kriterium)), - DukazyKriteria($m.map(_.kriterium.dukazy)) + DukazyKriteria($m.map(_.kriterium.dukazy))(action) ) ) ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala new file mode 100644 index 0000000..8e9bdce --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala @@ -0,0 +1,29 @@ +package cz.e_bs.cmi.mdr.pdb.app.pages.detail + +import com.raquo.laminar.api.L.{*, given} + +import components.* + +object NovyDukazKriteriaPage: + + case class ViewModel( + osoba: DetailOsoby.ViewModel, + parametr: DetailParametru.ViewModel, + kriterium: DetailKriteria.ViewModel + ) + + def apply($m: Signal[ViewModel]): HtmlElement = + div( + cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", + div( + cls := "flex flex-col space-y-4", + div( + DetailOsoby.header($m.map(_.osoba)), + DetailParametru.header($m.map(_.parametr)).amend(cls := "mt-2") + ), + div( + DetailKriteria($m.map(_.kriterium)), + NovyDukazForm() + ) + ) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala index 8f3429e..6b129ab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala @@ -6,6 +6,7 @@ import cz.e_bs.cmi.mdr.pdb.UserInfo import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage import pages.detail.DetailKriteriaPage +import pages.detail.components.DukazyKriteria import com.raquo.waypoint.Router import cz.e_bs.cmi.mdr.pdb.app.components.AppPage import cz.e_bs.cmi.mdr.pdb.ParameterCriteria @@ -28,7 +29,9 @@ (p.osobniCislo.value, p.parametr.value, p.kriterium.value) )((x, _, _) => x) val $pageChangeSignal = - $paramChangeSignal.map(FetchParameterCriteria(_, _, _)) + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.DetailKriteria(_, _, _)) + ) val $data = state.details.startWithNone val $params = state.parameters.startWithNone @@ -46,7 +49,18 @@ def apply: HtmlElement = AppPage(state.actionBus)( $merged.map(_.map(buildModel)) - .split(_ => ())((_, _, s) => DetailKriteriaPage(s)), + .split(_ => ())((_, s, $s) => + DetailKriteriaPage($s)(state.actionBus.contramap { + case DukazyKriteria.Add => + NavigateTo( + Page.NovyDukazKriteria( + Page.Titled(s.osoba.osobniCislo, Some(s.osoba.jmeno)), + Page.Titled(s.parametr.id, Some(s.parametr.nazev)), + Page.Titled(s.kriterium.id, Some(s.kriterium.id)) + ) + ) + }) + ), $pageChangeSignal --> state.actionBus ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala new file mode 100644 index 0000000..1d83006 --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala @@ -0,0 +1,66 @@ +package cz.e_bs.cmi.mdr.pdb.app +package connectors + +import com.raquo.laminar.api.L.{*, given} +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage +import pages.detail.DetailKriteriaPage +import pages.detail.NovyDukazKriteriaPage +import com.raquo.waypoint.Router +import cz.e_bs.cmi.mdr.pdb.app.components.AppPage +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria + +// TODO: extract common pieces for all detail kriteria pages +object NovyDukazKriteriaPageConnector { + trait AppState { + def details: EventStream[UserInfo] + def parameters: EventStream[List[Parameter]] + def actionBus: Observer[Action] + } +} + +case class NovyDukazKriteriaPageConnector( + state: NovyDukazKriteriaPageConnector.AppState +)( + $page: Signal[Page.NovyDukazKriteria] +)(using Router[Page]): + val $paramChangeSignal = + $page.splitOne(p => + (p.osobniCislo.value, p.parametr.value, p.kriterium.value) + )((x, _, _) => x) + val $pageChangeSignal = + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.NovyDukazKriteria(_, _, _)) + ) + + val $data = state.details.startWithNone + val $params = state.parameters.startWithNone + + val $merged = + $data.combineWithFn($params, $paramChangeSignal)((d, p, pc) => + for { + da <- d + pa <- p + pb <- pa.find(_.id == pc._2) + ka <- pb.criteria.find(_.id == pc._3) + } yield (da, pb, ka) + ) + + def apply: HtmlElement = + AppPage(state.actionBus)( + $merged.map(_.map(buildModel)) + .split(_ => ())((_, _, $s) => NovyDukazKriteriaPage($s)), + $pageChangeSignal --> state.actionBus + ) + + private def buildModel( + o: UserInfo, + p: Parameter, + k: ParameterCriteria + ): NovyDukazKriteriaPage.ViewModel = + NovyDukazKriteriaPage.ViewModel( + o.toDetailOsoby, + p.toParametr(_ => a()), + k.toKriterium(_ => a()) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala index 7729919..9ef0698 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala @@ -12,7 +12,9 @@ kriterium: DetailKriteria.ViewModel ) - def apply($m: Signal[ViewModel]): HtmlElement = + def apply($m: Signal[ViewModel])( + action: Observer[DukazyKriteria.Action] + ): HtmlElement = div( cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", div( @@ -23,7 +25,7 @@ ), div( DetailKriteria($m.map(_.kriterium)), - DukazyKriteria($m.map(_.kriterium.dukazy)) + DukazyKriteria($m.map(_.kriterium.dukazy))(action) ) ) ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala new file mode 100644 index 0000000..8e9bdce --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala @@ -0,0 +1,29 @@ +package cz.e_bs.cmi.mdr.pdb.app.pages.detail + +import com.raquo.laminar.api.L.{*, given} + +import components.* + +object NovyDukazKriteriaPage: + + case class ViewModel( + osoba: DetailOsoby.ViewModel, + parametr: DetailParametru.ViewModel, + kriterium: DetailKriteria.ViewModel + ) + + def apply($m: Signal[ViewModel]): HtmlElement = + div( + cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", + div( + cls := "flex flex-col space-y-4", + div( + DetailOsoby.header($m.map(_.osoba)), + DetailParametru.header($m.map(_.parametr)).amend(cls := "mt-2") + ), + div( + DetailKriteria($m.map(_.kriterium)), + NovyDukazForm() + ) + ) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala index ac27b9f..977fc9d 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala @@ -4,20 +4,26 @@ import cz.e_bs.cmi.mdr.pdb.app.components.Icons object DukazyKriteria: + sealed trait Action + case object Add extends Action + type ViewModel = List[DukazKriteria.ViewModel] - def apply($m: Signal[ViewModel]): HtmlElement = + + def apply($m: Signal[ViewModel])(actions: Observer[Action]): HtmlElement = div( child.maybe <-- $m.map(m => - if (m.isEmpty) then Some(prazdnyDukaz) else None + if (m.isEmpty) then Some(prazdnyDukaz(actions)) + else None ), children <-- $m.map(_.zipWithIndex).split(_._2)((_, _, $s) => DukazKriteria($s.map(_._1)) ) ) - private def prazdnyDukaz: HtmlElement = + private def prazdnyDukaz(actions: Observer[Action]): HtmlElement = button( tpe := "button", + onClick.preventDefault.mapTo(Add) --> actions, cls := "relative block w-full border-2 border-gray-300 border-dashed rounded-lg p-12 text-center hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", Icons.outline .`document-add`(12) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala index 8f3429e..6b129ab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala @@ -6,6 +6,7 @@ import cz.e_bs.cmi.mdr.pdb.UserInfo import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage import pages.detail.DetailKriteriaPage +import pages.detail.components.DukazyKriteria import com.raquo.waypoint.Router import cz.e_bs.cmi.mdr.pdb.app.components.AppPage import cz.e_bs.cmi.mdr.pdb.ParameterCriteria @@ -28,7 +29,9 @@ (p.osobniCislo.value, p.parametr.value, p.kriterium.value) )((x, _, _) => x) val $pageChangeSignal = - $paramChangeSignal.map(FetchParameterCriteria(_, _, _)) + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.DetailKriteria(_, _, _)) + ) val $data = state.details.startWithNone val $params = state.parameters.startWithNone @@ -46,7 +49,18 @@ def apply: HtmlElement = AppPage(state.actionBus)( $merged.map(_.map(buildModel)) - .split(_ => ())((_, _, s) => DetailKriteriaPage(s)), + .split(_ => ())((_, s, $s) => + DetailKriteriaPage($s)(state.actionBus.contramap { + case DukazyKriteria.Add => + NavigateTo( + Page.NovyDukazKriteria( + Page.Titled(s.osoba.osobniCislo, Some(s.osoba.jmeno)), + Page.Titled(s.parametr.id, Some(s.parametr.nazev)), + Page.Titled(s.kriterium.id, Some(s.kriterium.id)) + ) + ) + }) + ), $pageChangeSignal --> state.actionBus ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala new file mode 100644 index 0000000..1d83006 --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala @@ -0,0 +1,66 @@ +package cz.e_bs.cmi.mdr.pdb.app +package connectors + +import com.raquo.laminar.api.L.{*, given} +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage +import pages.detail.DetailKriteriaPage +import pages.detail.NovyDukazKriteriaPage +import com.raquo.waypoint.Router +import cz.e_bs.cmi.mdr.pdb.app.components.AppPage +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria + +// TODO: extract common pieces for all detail kriteria pages +object NovyDukazKriteriaPageConnector { + trait AppState { + def details: EventStream[UserInfo] + def parameters: EventStream[List[Parameter]] + def actionBus: Observer[Action] + } +} + +case class NovyDukazKriteriaPageConnector( + state: NovyDukazKriteriaPageConnector.AppState +)( + $page: Signal[Page.NovyDukazKriteria] +)(using Router[Page]): + val $paramChangeSignal = + $page.splitOne(p => + (p.osobniCislo.value, p.parametr.value, p.kriterium.value) + )((x, _, _) => x) + val $pageChangeSignal = + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.NovyDukazKriteria(_, _, _)) + ) + + val $data = state.details.startWithNone + val $params = state.parameters.startWithNone + + val $merged = + $data.combineWithFn($params, $paramChangeSignal)((d, p, pc) => + for { + da <- d + pa <- p + pb <- pa.find(_.id == pc._2) + ka <- pb.criteria.find(_.id == pc._3) + } yield (da, pb, ka) + ) + + def apply: HtmlElement = + AppPage(state.actionBus)( + $merged.map(_.map(buildModel)) + .split(_ => ())((_, _, $s) => NovyDukazKriteriaPage($s)), + $pageChangeSignal --> state.actionBus + ) + + private def buildModel( + o: UserInfo, + p: Parameter, + k: ParameterCriteria + ): NovyDukazKriteriaPage.ViewModel = + NovyDukazKriteriaPage.ViewModel( + o.toDetailOsoby, + p.toParametr(_ => a()), + k.toKriterium(_ => a()) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala index 7729919..9ef0698 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala @@ -12,7 +12,9 @@ kriterium: DetailKriteria.ViewModel ) - def apply($m: Signal[ViewModel]): HtmlElement = + def apply($m: Signal[ViewModel])( + action: Observer[DukazyKriteria.Action] + ): HtmlElement = div( cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", div( @@ -23,7 +25,7 @@ ), div( DetailKriteria($m.map(_.kriterium)), - DukazyKriteria($m.map(_.kriterium.dukazy)) + DukazyKriteria($m.map(_.kriterium.dukazy))(action) ) ) ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala new file mode 100644 index 0000000..8e9bdce --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala @@ -0,0 +1,29 @@ +package cz.e_bs.cmi.mdr.pdb.app.pages.detail + +import com.raquo.laminar.api.L.{*, given} + +import components.* + +object NovyDukazKriteriaPage: + + case class ViewModel( + osoba: DetailOsoby.ViewModel, + parametr: DetailParametru.ViewModel, + kriterium: DetailKriteria.ViewModel + ) + + def apply($m: Signal[ViewModel]): HtmlElement = + div( + cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", + div( + cls := "flex flex-col space-y-4", + div( + DetailOsoby.header($m.map(_.osoba)), + DetailParametru.header($m.map(_.parametr)).amend(cls := "mt-2") + ), + div( + DetailKriteria($m.map(_.kriterium)), + NovyDukazForm() + ) + ) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala index ac27b9f..977fc9d 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala @@ -4,20 +4,26 @@ import cz.e_bs.cmi.mdr.pdb.app.components.Icons object DukazyKriteria: + sealed trait Action + case object Add extends Action + type ViewModel = List[DukazKriteria.ViewModel] - def apply($m: Signal[ViewModel]): HtmlElement = + + def apply($m: Signal[ViewModel])(actions: Observer[Action]): HtmlElement = div( child.maybe <-- $m.map(m => - if (m.isEmpty) then Some(prazdnyDukaz) else None + if (m.isEmpty) then Some(prazdnyDukaz(actions)) + else None ), children <-- $m.map(_.zipWithIndex).split(_._2)((_, _, $s) => DukazKriteria($s.map(_._1)) ) ) - private def prazdnyDukaz: HtmlElement = + private def prazdnyDukaz(actions: Observer[Action]): HtmlElement = button( tpe := "button", + onClick.preventDefault.mapTo(Add) --> actions, cls := "relative block w-full border-2 border-gray-300 border-dashed rounded-lg p-12 text-center hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", Icons.outline .`document-add`(12) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/NovyDukazForm.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/NovyDukazForm.scala new file mode 100644 index 0000000..eba327c --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/NovyDukazForm.scala @@ -0,0 +1,531 @@ +package cz.e_bs.cmi.mdr.pdb.app.pages.detail.components + +import com.raquo.laminar.api.L.{*, given} + +object NovyDukazForm: + def apply(): HtmlElement = + span("Hello World!") +/* + form( + cls := "space-y-8 divide-y divide-gray-200", + div( + cls := "space-y-8 divide-y divide-gray-200 sm:space-y-5", + div( + div( + h3( + cls := "text-lg leading-6 font-medium text-gray-900", + """Profile""" + ), + p( + cls := "mt-1 max-w-2xl text-sm text-gray-500", + """This information will be displayed publicly so be careful what you share.""" + ) + ), + div( + cls := "mt-6 sm:mt-5 space-y-6 sm:space-y-5", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "username", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Username""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + div( + cls := "max-w-lg flex rounded-md shadow-sm", + span( + cls := "inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm", + """workcation.com/""" + ), + input( + tpe := "text", + name := "username", + id := "username", + autocomplete := "username", + cls := "flex-1 block w-full focus:ring-indigo-500 focus:border-indigo-500 min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300" + ) + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "about", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """About""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + textarea( + id := "about", + name := "about", + rows := "3", + cls := "max-w-lg shadow-sm block w-full focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border border-gray-300 rounded-md" + ), + p( + cls := "mt-2 text-sm text-gray-500", + """Write a few sentences about yourself.""" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-center sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "photo", + cls := "block text-sm font-medium text-gray-700", + """Photo""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + div( + cls := "flex items-center", + span( + cls := "h-12 w-12 rounded-full overflow-hidden bg-gray-100", + svg( + cls := "h-full w-full text-gray-300", + fill := "currentColor", + viewBox := "0 0 24 24", + path( + d := "M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" + ) + ) + ), + button( + tpe := "button", + cls := "ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", + """Change""" + ) + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "cover-photo", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Cover photo""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + div( + cls := "max-w-lg flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md", + div( + cls := "space-y-1 text-center", + svg( + cls := "mx-auto h-12 w-12 text-gray-400", + stroke := "currentColor", + fill := "none", + viewBox := "0 0 48 48", + aria.hidden := true, + path( + d := "M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02", + strokeWidth := "2", + strokeLineCap := "round", + strokeLineJoin := "round" + ) + ), + div( + cls := "flex text-sm text-gray-600", + label( + forId := "file-upload", + cls := "relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500", + span( + """Upload a file""" + ), + input( + id := "file-upload", + name := "file-upload", + tpe := "file", + cls := "sr-only" + ) + ), + p( + cls := "pl-1", + """or drag and drop""" + ) + ), + p( + cls := "text-xs text-gray-500", + """PNG, JPG, GIF up to 10MB""" + ) + ) + ) + ) + ) + ) + ), + div( + cls := "pt-8 space-y-6 sm:pt-10 sm:space-y-5", + div( + h3( + cls := "text-lg leading-6 font-medium text-gray-900", + """Personal Information""" + ), + p( + cls := "mt-1 max-w-2xl text-sm text-gray-500", + """Use a permanent address where you can receive mail.""" + ) + ), + div( + cls := "space-y-6 sm:space-y-5", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "first-name", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """First name""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "first-name", + id := "first-name", + autocomplete := "given-name", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "last-name", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Last name""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "last-name", + id := "last-name", + autocomplete := "family-name", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "email", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Email address""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + id := "email", + name := "email", + tpe := "email", + autocomplete := "email", + cls := "block max-w-lg w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "country", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Country""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + select( + id := "country", + name := "country", + autocomplete := "country-name", + cls := "max-w-lg block focus:ring-indigo-500 focus:border-indigo-500 w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-md", + option( + """United States""" + ), + option( + """Canada""" + ), + option( + """Mexico""" + ) + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "street-address", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Street address""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "street-address", + id := "street-address", + autocomplete := "street-address", + cls := "block max-w-lg w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "city", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """City""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "city", + id := "city", + autocomplete := "address-level2", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "region", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """State / Province""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "region", + id := "region", + autocomplete := "address-level1", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "postal-code", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """ZIP / Postal code""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "postal-code", + id := "postal-code", + autocomplete := "postal-code", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ) + ) + ), + div( + cls := "divide-y divide-gray-200 pt-8 space-y-6 sm:pt-10 sm:space-y-5", + div( + h3( + cls := "text-lg leading-6 font-medium text-gray-900", + """Notifications""" + ), + p( + cls := "mt-1 max-w-2xl text-sm text-gray-500", + """We'll always let you know about important changes, but you pick what else you want to hear about.""" + ) + ), + div( + cls := "space-y-6 sm:space-y-5 divide-y divide-gray-200", + div( + cls := "pt-6 sm:pt-5", + div( + role := "group", + aria.labelledby := "label-email", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline", + div( + div( + cls := "text-base font-medium text-gray-900 sm:text-sm sm:text-gray-700", + id := "label-email", + """By Email""" + ) + ), + div( + cls := "mt-4 sm:mt-0 sm:col-span-2", + div( + cls := "max-w-lg space-y-4", + div( + cls := "relative flex items-start", + div( + cls := "flex items-center h-5", + input( + id := "comments", + name := "comments", + tpe := "checkbox", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" + ) + ), + div( + cls := "ml-3 text-sm", + label( + forId := "comments", + cls := "font-medium text-gray-700", + """Comments""" + ), + p( + cls := "text-gray-500", + """Get notified when someones posts a comment on a posting.""" + ) + ) + ), + div( + div( + cls := "relative flex items-start", + div( + cls := "flex items-center h-5", + input( + id := "candidates", + name := "candidates", + tpe := "checkbox", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" + ) + ), + div( + cls := "ml-3 text-sm", + label( + forId := "candidates", + cls := "font-medium text-gray-700", + """Candidates""" + ), + p( + cls := "text-gray-500", + """Get notified when a candidate applies for a job.""" + ) + ) + ) + ), + div( + div( + cls := "relative flex items-start", + div( + cls := "flex items-center h-5", + input( + id := "offers", + name := "offers", + tpe := "checkbox", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" + ) + ), + div( + cls := "ml-3 text-sm", + label( + forId := "offers", + cls := "font-medium text-gray-700", + """Offers""" + ), + p( + cls := "text-gray-500", + """Get notified when a candidate accepts or rejects an offer.""" + ) + ) + ) + ) + ) + ) + ) + ) + ), + div( + cls := "pt-6 sm:pt-5", + div( + role := "group", + aria.labelledby := "label-notifications", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline", + div( + div( + cls := "text-base font-medium text-gray-900 sm:text-sm sm:text-gray-700", + id := "label-notifications", + """Push Notifications""" + ) + ), + div( + cls := "sm:col-span-2", + div( + cls := "max-w-lg", + p( + cls := "text-sm text-gray-500", + """These are delivered via SMS to your mobile phone.""" + ), + div( + cls := "mt-4 space-y-4", + div( + cls := "flex items-center", + input( + id := "push-everything", + name := "push-notifications", + tpe := "radio", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" + ), + label( + forId := "push-everything", + cls := "ml-3 block text-sm font-medium text-gray-700", + """Everything""" + ) + ), + div( + cls := "flex items-center", + input( + id := "push-email", + name := "push-notifications", + tpe := "radio", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" + ), + label( + forId := "push-email", + cls := "ml-3 block text-sm font-medium text-gray-700", + """Same as email""" + ) + ), + div( + cls := "flex items-center", + input( + id := "push-nothing", + name := "push-notifications", + tpe := "radio", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" + ), + label( + forId := "push-nothing", + cls := "ml-3 block text-sm font-medium text-gray-700", + """No push notifications""" + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ), + div( + cls := "pt-5", + div( + cls := "flex justify-end", + button( + tpe := "button", + cls := "bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", + """Cancel""" + ), + button( + tpe := "submit", + cls := "ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", + """Save""" + ) + ) + ) + ) + */ diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala index e0e9297..15821f9 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/Main.scala @@ -75,6 +75,11 @@ .DetailKriteriaPageConnector(state)(_) .apply ) + .collectSignal[Page.NovyDukazKriteria]( + connectors + .NovyDukazKriteriaPageConnector(state)(_) + .apply + ) .collectStatic(Page.Dashboard)( connectors.DashboardPageConnector(state.actionBus).apply ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala index 615c016..ced54cc 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/actions.scala @@ -1,6 +1,9 @@ package cz.e_bs.cmi.mdr.pdb.app import cz.e_bs.cmi.mdr.pdb.OsobniCislo +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria sealed trait Action @@ -11,6 +14,7 @@ case class FetchParameterCriteria( osc: OsobniCislo, paramId: String, - critId: String + critId: String, + page: (UserInfo, Parameter, ParameterCriteria) => Page ) extends Action case class NavigateTo(page: Page) extends Action diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala index 8f3429e..6b129ab 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/DetailKriteriaPageConnector.scala @@ -6,6 +6,7 @@ import cz.e_bs.cmi.mdr.pdb.UserInfo import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage import pages.detail.DetailKriteriaPage +import pages.detail.components.DukazyKriteria import com.raquo.waypoint.Router import cz.e_bs.cmi.mdr.pdb.app.components.AppPage import cz.e_bs.cmi.mdr.pdb.ParameterCriteria @@ -28,7 +29,9 @@ (p.osobniCislo.value, p.parametr.value, p.kriterium.value) )((x, _, _) => x) val $pageChangeSignal = - $paramChangeSignal.map(FetchParameterCriteria(_, _, _)) + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.DetailKriteria(_, _, _)) + ) val $data = state.details.startWithNone val $params = state.parameters.startWithNone @@ -46,7 +49,18 @@ def apply: HtmlElement = AppPage(state.actionBus)( $merged.map(_.map(buildModel)) - .split(_ => ())((_, _, s) => DetailKriteriaPage(s)), + .split(_ => ())((_, s, $s) => + DetailKriteriaPage($s)(state.actionBus.contramap { + case DukazyKriteria.Add => + NavigateTo( + Page.NovyDukazKriteria( + Page.Titled(s.osoba.osobniCislo, Some(s.osoba.jmeno)), + Page.Titled(s.parametr.id, Some(s.parametr.nazev)), + Page.Titled(s.kriterium.id, Some(s.kriterium.id)) + ) + ) + }) + ), $pageChangeSignal --> state.actionBus ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala new file mode 100644 index 0000000..1d83006 --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/connectors/NovyDukazKriteriaPageConnector.scala @@ -0,0 +1,66 @@ +package cz.e_bs.cmi.mdr.pdb.app +package connectors + +import com.raquo.laminar.api.L.{*, given} +import cz.e_bs.cmi.mdr.pdb.Parameter +import cz.e_bs.cmi.mdr.pdb.UserInfo +import cz.e_bs.cmi.mdr.pdb.app.pages.detail.DetailParametruPage +import pages.detail.DetailKriteriaPage +import pages.detail.NovyDukazKriteriaPage +import com.raquo.waypoint.Router +import cz.e_bs.cmi.mdr.pdb.app.components.AppPage +import cz.e_bs.cmi.mdr.pdb.ParameterCriteria + +// TODO: extract common pieces for all detail kriteria pages +object NovyDukazKriteriaPageConnector { + trait AppState { + def details: EventStream[UserInfo] + def parameters: EventStream[List[Parameter]] + def actionBus: Observer[Action] + } +} + +case class NovyDukazKriteriaPageConnector( + state: NovyDukazKriteriaPageConnector.AppState +)( + $page: Signal[Page.NovyDukazKriteria] +)(using Router[Page]): + val $paramChangeSignal = + $page.splitOne(p => + (p.osobniCislo.value, p.parametr.value, p.kriterium.value) + )((x, _, _) => x) + val $pageChangeSignal = + $paramChangeSignal.map( + FetchParameterCriteria(_, _, _, Page.NovyDukazKriteria(_, _, _)) + ) + + val $data = state.details.startWithNone + val $params = state.parameters.startWithNone + + val $merged = + $data.combineWithFn($params, $paramChangeSignal)((d, p, pc) => + for { + da <- d + pa <- p + pb <- pa.find(_.id == pc._2) + ka <- pb.criteria.find(_.id == pc._3) + } yield (da, pb, ka) + ) + + def apply: HtmlElement = + AppPage(state.actionBus)( + $merged.map(_.map(buildModel)) + .split(_ => ())((_, _, $s) => NovyDukazKriteriaPage($s)), + $pageChangeSignal --> state.actionBus + ) + + private def buildModel( + o: UserInfo, + p: Parameter, + k: ParameterCriteria + ): NovyDukazKriteriaPage.ViewModel = + NovyDukazKriteriaPage.ViewModel( + o.toDetailOsoby, + p.toParametr(_ => a()), + k.toKriterium(_ => a()) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala index 7729919..9ef0698 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/DetailKriteriaPage.scala @@ -12,7 +12,9 @@ kriterium: DetailKriteria.ViewModel ) - def apply($m: Signal[ViewModel]): HtmlElement = + def apply($m: Signal[ViewModel])( + action: Observer[DukazyKriteria.Action] + ): HtmlElement = div( cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", div( @@ -23,7 +25,7 @@ ), div( DetailKriteria($m.map(_.kriterium)), - DukazyKriteria($m.map(_.kriterium.dukazy)) + DukazyKriteria($m.map(_.kriterium.dukazy))(action) ) ) ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala new file mode 100644 index 0000000..8e9bdce --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/NovyDukazKriteriaPage.scala @@ -0,0 +1,29 @@ +package cz.e_bs.cmi.mdr.pdb.app.pages.detail + +import com.raquo.laminar.api.L.{*, given} + +import components.* + +object NovyDukazKriteriaPage: + + case class ViewModel( + osoba: DetailOsoby.ViewModel, + parametr: DetailParametru.ViewModel, + kriterium: DetailKriteria.ViewModel + ) + + def apply($m: Signal[ViewModel]): HtmlElement = + div( + cls := "max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8", + div( + cls := "flex flex-col space-y-4", + div( + DetailOsoby.header($m.map(_.osoba)), + DetailParametru.header($m.map(_.parametr)).amend(cls := "mt-2") + ), + div( + DetailKriteria($m.map(_.kriterium)), + NovyDukazForm() + ) + ) + ) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala index ac27b9f..977fc9d 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/DukazyKriteria.scala @@ -4,20 +4,26 @@ import cz.e_bs.cmi.mdr.pdb.app.components.Icons object DukazyKriteria: + sealed trait Action + case object Add extends Action + type ViewModel = List[DukazKriteria.ViewModel] - def apply($m: Signal[ViewModel]): HtmlElement = + + def apply($m: Signal[ViewModel])(actions: Observer[Action]): HtmlElement = div( child.maybe <-- $m.map(m => - if (m.isEmpty) then Some(prazdnyDukaz) else None + if (m.isEmpty) then Some(prazdnyDukaz(actions)) + else None ), children <-- $m.map(_.zipWithIndex).split(_._2)((_, _, $s) => DukazKriteria($s.map(_._1)) ) ) - private def prazdnyDukaz: HtmlElement = + private def prazdnyDukaz(actions: Observer[Action]): HtmlElement = button( tpe := "button", + onClick.preventDefault.mapTo(Add) --> actions, cls := "relative block w-full border-2 border-gray-300 border-dashed rounded-lg p-12 text-center hover:border-gray-400 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", Icons.outline .`document-add`(12) diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/NovyDukazForm.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/NovyDukazForm.scala new file mode 100644 index 0000000..eba327c --- /dev/null +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/pages/detail/components/NovyDukazForm.scala @@ -0,0 +1,531 @@ +package cz.e_bs.cmi.mdr.pdb.app.pages.detail.components + +import com.raquo.laminar.api.L.{*, given} + +object NovyDukazForm: + def apply(): HtmlElement = + span("Hello World!") +/* + form( + cls := "space-y-8 divide-y divide-gray-200", + div( + cls := "space-y-8 divide-y divide-gray-200 sm:space-y-5", + div( + div( + h3( + cls := "text-lg leading-6 font-medium text-gray-900", + """Profile""" + ), + p( + cls := "mt-1 max-w-2xl text-sm text-gray-500", + """This information will be displayed publicly so be careful what you share.""" + ) + ), + div( + cls := "mt-6 sm:mt-5 space-y-6 sm:space-y-5", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "username", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Username""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + div( + cls := "max-w-lg flex rounded-md shadow-sm", + span( + cls := "inline-flex items-center px-3 rounded-l-md border border-r-0 border-gray-300 bg-gray-50 text-gray-500 sm:text-sm", + """workcation.com/""" + ), + input( + tpe := "text", + name := "username", + id := "username", + autocomplete := "username", + cls := "flex-1 block w-full focus:ring-indigo-500 focus:border-indigo-500 min-w-0 rounded-none rounded-r-md sm:text-sm border-gray-300" + ) + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "about", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """About""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + textarea( + id := "about", + name := "about", + rows := "3", + cls := "max-w-lg shadow-sm block w-full focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border border-gray-300 rounded-md" + ), + p( + cls := "mt-2 text-sm text-gray-500", + """Write a few sentences about yourself.""" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-center sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "photo", + cls := "block text-sm font-medium text-gray-700", + """Photo""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + div( + cls := "flex items-center", + span( + cls := "h-12 w-12 rounded-full overflow-hidden bg-gray-100", + svg( + cls := "h-full w-full text-gray-300", + fill := "currentColor", + viewBox := "0 0 24 24", + path( + d := "M24 20.993V24H0v-2.996A14.977 14.977 0 0112.004 15c4.904 0 9.26 2.354 11.996 5.993zM16.002 8.999a4 4 0 11-8 0 4 4 0 018 0z" + ) + ) + ), + button( + tpe := "button", + cls := "ml-5 bg-white py-2 px-3 border border-gray-300 rounded-md shadow-sm text-sm leading-4 font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", + """Change""" + ) + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "cover-photo", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Cover photo""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + div( + cls := "max-w-lg flex justify-center px-6 pt-5 pb-6 border-2 border-gray-300 border-dashed rounded-md", + div( + cls := "space-y-1 text-center", + svg( + cls := "mx-auto h-12 w-12 text-gray-400", + stroke := "currentColor", + fill := "none", + viewBox := "0 0 48 48", + aria.hidden := true, + path( + d := "M28 8H12a4 4 0 00-4 4v20m32-12v8m0 0v8a4 4 0 01-4 4H12a4 4 0 01-4-4v-4m32-4l-3.172-3.172a4 4 0 00-5.656 0L28 28M8 32l9.172-9.172a4 4 0 015.656 0L28 28m0 0l4 4m4-24h8m-4-4v8m-12 4h.02", + strokeWidth := "2", + strokeLineCap := "round", + strokeLineJoin := "round" + ) + ), + div( + cls := "flex text-sm text-gray-600", + label( + forId := "file-upload", + cls := "relative cursor-pointer bg-white rounded-md font-medium text-indigo-600 hover:text-indigo-500 focus-within:outline-none focus-within:ring-2 focus-within:ring-offset-2 focus-within:ring-indigo-500", + span( + """Upload a file""" + ), + input( + id := "file-upload", + name := "file-upload", + tpe := "file", + cls := "sr-only" + ) + ), + p( + cls := "pl-1", + """or drag and drop""" + ) + ), + p( + cls := "text-xs text-gray-500", + """PNG, JPG, GIF up to 10MB""" + ) + ) + ) + ) + ) + ) + ), + div( + cls := "pt-8 space-y-6 sm:pt-10 sm:space-y-5", + div( + h3( + cls := "text-lg leading-6 font-medium text-gray-900", + """Personal Information""" + ), + p( + cls := "mt-1 max-w-2xl text-sm text-gray-500", + """Use a permanent address where you can receive mail.""" + ) + ), + div( + cls := "space-y-6 sm:space-y-5", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "first-name", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """First name""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "first-name", + id := "first-name", + autocomplete := "given-name", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "last-name", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Last name""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "last-name", + id := "last-name", + autocomplete := "family-name", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "email", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Email address""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + id := "email", + name := "email", + tpe := "email", + autocomplete := "email", + cls := "block max-w-lg w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "country", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Country""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + select( + id := "country", + name := "country", + autocomplete := "country-name", + cls := "max-w-lg block focus:ring-indigo-500 focus:border-indigo-500 w-full shadow-sm sm:max-w-xs sm:text-sm border-gray-300 rounded-md", + option( + """United States""" + ), + option( + """Canada""" + ), + option( + """Mexico""" + ) + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "street-address", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """Street address""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "street-address", + id := "street-address", + autocomplete := "street-address", + cls := "block max-w-lg w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "city", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """City""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "city", + id := "city", + autocomplete := "address-level2", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "region", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """State / Province""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "region", + id := "region", + autocomplete := "address-level1", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ), + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-200 sm:pt-5", + label( + forId := "postal-code", + cls := "block text-sm font-medium text-gray-700 sm:mt-px sm:pt-2", + """ZIP / Postal code""" + ), + div( + cls := "mt-1 sm:mt-0 sm:col-span-2", + input( + tpe := "text", + name := "postal-code", + id := "postal-code", + autocomplete := "postal-code", + cls := "max-w-lg block w-full shadow-sm focus:ring-indigo-500 focus:border-indigo-500 sm:max-w-xs sm:text-sm border-gray-300 rounded-md" + ) + ) + ) + ) + ), + div( + cls := "divide-y divide-gray-200 pt-8 space-y-6 sm:pt-10 sm:space-y-5", + div( + h3( + cls := "text-lg leading-6 font-medium text-gray-900", + """Notifications""" + ), + p( + cls := "mt-1 max-w-2xl text-sm text-gray-500", + """We'll always let you know about important changes, but you pick what else you want to hear about.""" + ) + ), + div( + cls := "space-y-6 sm:space-y-5 divide-y divide-gray-200", + div( + cls := "pt-6 sm:pt-5", + div( + role := "group", + aria.labelledby := "label-email", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline", + div( + div( + cls := "text-base font-medium text-gray-900 sm:text-sm sm:text-gray-700", + id := "label-email", + """By Email""" + ) + ), + div( + cls := "mt-4 sm:mt-0 sm:col-span-2", + div( + cls := "max-w-lg space-y-4", + div( + cls := "relative flex items-start", + div( + cls := "flex items-center h-5", + input( + id := "comments", + name := "comments", + tpe := "checkbox", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" + ) + ), + div( + cls := "ml-3 text-sm", + label( + forId := "comments", + cls := "font-medium text-gray-700", + """Comments""" + ), + p( + cls := "text-gray-500", + """Get notified when someones posts a comment on a posting.""" + ) + ) + ), + div( + div( + cls := "relative flex items-start", + div( + cls := "flex items-center h-5", + input( + id := "candidates", + name := "candidates", + tpe := "checkbox", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" + ) + ), + div( + cls := "ml-3 text-sm", + label( + forId := "candidates", + cls := "font-medium text-gray-700", + """Candidates""" + ), + p( + cls := "text-gray-500", + """Get notified when a candidate applies for a job.""" + ) + ) + ) + ), + div( + div( + cls := "relative flex items-start", + div( + cls := "flex items-center h-5", + input( + id := "offers", + name := "offers", + tpe := "checkbox", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300 rounded" + ) + ), + div( + cls := "ml-3 text-sm", + label( + forId := "offers", + cls := "font-medium text-gray-700", + """Offers""" + ), + p( + cls := "text-gray-500", + """Get notified when a candidate accepts or rejects an offer.""" + ) + ) + ) + ) + ) + ) + ) + ) + ), + div( + cls := "pt-6 sm:pt-5", + div( + role := "group", + aria.labelledby := "label-notifications", + div( + cls := "sm:grid sm:grid-cols-3 sm:gap-4 sm:items-baseline", + div( + div( + cls := "text-base font-medium text-gray-900 sm:text-sm sm:text-gray-700", + id := "label-notifications", + """Push Notifications""" + ) + ), + div( + cls := "sm:col-span-2", + div( + cls := "max-w-lg", + p( + cls := "text-sm text-gray-500", + """These are delivered via SMS to your mobile phone.""" + ), + div( + cls := "mt-4 space-y-4", + div( + cls := "flex items-center", + input( + id := "push-everything", + name := "push-notifications", + tpe := "radio", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" + ), + label( + forId := "push-everything", + cls := "ml-3 block text-sm font-medium text-gray-700", + """Everything""" + ) + ), + div( + cls := "flex items-center", + input( + id := "push-email", + name := "push-notifications", + tpe := "radio", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" + ), + label( + forId := "push-email", + cls := "ml-3 block text-sm font-medium text-gray-700", + """Same as email""" + ) + ), + div( + cls := "flex items-center", + input( + id := "push-nothing", + name := "push-notifications", + tpe := "radio", + cls := "focus:ring-indigo-500 h-4 w-4 text-indigo-600 border-gray-300" + ), + label( + forId := "push-nothing", + cls := "ml-3 block text-sm font-medium text-gray-700", + """No push notifications""" + ) + ) + ) + ) + ) + ) + ) + ) + ) + ) + ), + div( + cls := "pt-5", + div( + cls := "flex justify-end", + button( + tpe := "button", + cls := "bg-white py-2 px-4 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", + """Cancel""" + ), + button( + tpe := "submit", + cls := "ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500", + """Save""" + ) + ) + ) + ) + */ diff --git a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/state/AppState.scala b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/state/AppState.scala index 642046f..508b257 100644 --- a/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/state/AppState.scala +++ b/app/src/main/scala/cz/e_bs/cmi/mdr/pdb/app/state/AppState.scala @@ -18,7 +18,8 @@ trait AppState extends connectors.DetailPageConnector.AppState with connectors.DetailParametruPageConnector.AppState - with connectors.DetailKriteriaPageConnector.AppState: + with connectors.DetailKriteriaPageConnector.AppState + with connectors.NovyDukazKriteriaPageConnector.AppState: def users: EventStream[List[UserInfo]] def details: EventStream[UserInfo] def parameters: EventStream[List[Parameter]] @@ -83,7 +84,7 @@ pushDetails(o) pushParameters(mockParameters) router.replaceState(Page.DetailParametru(o, p)) - case FetchParameterCriteria(osc, paramId, critId) => + case FetchParameterCriteria(osc, paramId, critId, page) => for o <- mockData.find(_.personalNumber == osc) p <- mockParameters.find(_.id == paramId) @@ -91,7 +92,7 @@ do pushDetails(o) pushParameters(mockParameters) - router.replaceState(Page.DetailKriteria(o, p, c)) + router.replaceState(page(o, p, c)) case NavigateTo(page) => router.pushState(page) }