
.uniform.common-web_2.13.5.0.0-RC6.source-code.WebAskList.scala Maven / Gradle / Ivy
The newest version!
package ltbs.uniform
package common.web
import validation._
import concurrent._
import cats.implicits._
trait WebAskList[Html, A] extends Primatives[Html] {
import WebAskList._
def menuPage: WebInteraction[Html, ListingTable[A], WebAskList.ListAction]
implicit def codec: Codec[List[A]]
def apply(
key: String,
askJourney: (Option[Int], List[A]) => WebMonad[Html,A],
deleteConfirmationJourney: (Int, List[A]) => WebMonad[Html,Boolean],
default: Option[List[A]],
validation: Rule[List[A]],
customContent: Map[String,(String, List[Any])],
): WebMonad[Html, List[A]] = new WebMonad[Html, List[A]] {
val subRules = validation.subRules
val (min, max) = subRules.collect {
case Rule.MaxLength(h, _) => (0, h)
case Rule.MinLength(l,_) => (l,Int.MaxValue)
case Rule.LengthBetween(l,h) => (l,h)
case Rule.NonEmpty(_) => (1,Int.MaxValue)
}.foldLeft((0, Int.MaxValue)){
case ((al, ah),(bl, bh)) => (Math.max(al, bl), Math.min(ah, bh))
}
val elementValidation: Rule[A] = subRules.collect {
case Rule.ForEachInList(r) => r
}.combineAll
def apply(pageIn: PageIn[Html])(implicit ec: ExecutionContext): Future[PageOut[Html,List[A]]] = {
db.get[List[A]](List(s"${key}-zzdata")).flatMap { dataRead =>
val data: List[A] = dataRead match {
case Some(Right(d)) => d
case _ => default.getOrElse(Nil)
}
val branchValidation = {
import cats.data.Validated.Valid
Rule.cond[ListAction]({
case ListAction.Add if data.size >= max => false
case _ => true
}, "maxLength") followedBy {
case ListAction.Continue => validation(data).map { _ => ListAction.Continue }
case x => Valid(x)
}
}
if (data.isEmpty && min > 0 && pageIn.config.askFirstListItem) {
subjourneyWM(_.copy(leapAhead = false), Seq(key, "add"):_*)(for {
r <- askJourney(None, data)
_ <- db(List(s"${key}-zzdata")) = data :+ r
_ <- db.deleteRecursive(List(key))
_ <- goto[Unit](key)
} yield (List.empty[A]))
} else {
menuPage(key, new ListingTable(data).some, None, branchValidation, customContent) flatMap {
case ListAction.Continue =>
data.pure[WebMonad[Html, *]]
case ListAction.Add =>
subjourneyWM(_.copy(leapAhead = false), Seq(key, "add"):_*)(for {
r <- askJourney(None, data)
_ <- db(List(s"${key}-zzdata")) = data :+ r
_ <- db.deleteRecursive(List(key))
_ <- goto[Unit](key)
} yield (List.empty[A]))
case ListAction.Delete(index) =>
subjourneyWM(_.copy(leapAhead = false), Seq(key, "delete") :_ *)(for {
_ <- pushBreadcrumb(key.split("/").toList)
confirm <- deleteConfirmationJourney(index, data)
_ <- confirm match {
case true =>
db(List(s"${key}-zzdata")) = data.deleteAtIndex(index)
case false =>
().pure[WebMonad[Html, *]]
}
_ <- db.deleteRecursive(List(key))
_ <- goto[Unit](key)
} yield (List.empty[A]))
case ListAction.Edit(index) => {
subjourneyWM(_.copy(leapAhead = false), Seq(key, "edit", index.toString) :_ *)(for {
_ <- pushBreadcrumb(key.split("/").toList)
r <- askJourney(Some(index), data)
_ <- db(List(s"${key}-zzdata")) = data.replaceAtIndex(index, r)
_ <- db.deleteRecursive(List(key))
_ <- goto[Unit](key)
} yield (List.empty[A]))
}
}
}
}.apply(pageIn)
}
}
}
object WebAskList {
sealed trait ListAction
sealed trait ListActionRow extends ListAction
sealed trait ListActionGeneral extends ListAction
/** A representation of a list used for WebTell. This will typically
* contain links as well as the data (for edit and delete)
*/
final class ListingTable[A](val value: List[A]) extends AnyVal
object ListAction {
case class Delete(index: Int) extends ListActionRow
case class Edit(index: Int) extends ListActionRow
case object Continue extends ListActionGeneral
case object Add extends ListActionGeneral
}
implicit def autoWebAskList[Html, A](
implicit tellList: WebTell[Html, WebAskList.ListingTable[A]],
codecIn: Codec[A],
listActionCodec: Codec[WebAskList.ListAction],
ff: WebAsk[Html, WebAskList.ListActionGeneral]
): WebAskList[Html, A] = new WebAskList[Html, A] {
object Pos {
def unapply(value: String): Option[Int] =
Either.catchOnly[NumberFormatException](value.toInt).toOption
}
implicit def codec = new Codec[List[A]] {
def decode(out: Input): Either[ErrorTree,List[A]] = {
out.listSubtrees.sorted.traverse {
index => codecIn.decode(out.atPath(index :: Nil))
}
}
def encode(in: List[A]): Input = in.zipWithIndex.flatMap {
case (v, i) => codecIn.encode(v).prefixWith(i.toString)
}.toMap
}
def menuPage: WebInteraction[Html,WebAskList.ListingTable[A],WebAskList.ListAction] = new PostAndGetPage[Html, WebAskList.ListingTable[A], WebAskList.ListAction] {
def codec: Codec[WebAskList.ListAction] = listActionCodec
def getPage(
pageIn: PageIn[Html],
key: List[String],
tell: Option[WebAskList.ListingTable[A]],
existing: Input,
rule: Rule[WebAskList.ListAction]
)(implicit ec: ExecutionContext): Option[Html] = {
val tellHtml = tell.flatMap(tellList.render(_, key, pageIn))
ff.render(pageIn, StepDetails[Html, WebAskList.ListActionGeneral](key, key, tellHtml, existing, ErrorTree.empty, Rule.alwaysPass))
}
def postPage(
pageIn: PageIn[Html],
key: List[String],
tell: Option[WebAskList.ListingTable[A]],
request: Input,
rule: Rule[WebAskList.ListAction],
errors: ErrorTree
)(implicit ec: ExecutionContext): Option[Html] = {
val tellHtml = tell.flatMap(tellList.render(_, key, pageIn))
ff.render(pageIn, StepDetails[Html, WebAskList.ListActionGeneral](key, key, tellHtml, request, errors, Rule.alwaysPass))
}
override val customRouting: PartialFunction[List[String],WebAskList.ListAction] = {
case "edit" :: Pos(x) :: _ => WebAskList.ListAction.Edit(x)
case "delete" :: Pos(x) :: Nil => WebAskList.ListAction.Delete(x)
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy