feat(15): adding dynamic form data source
This commit is contained in:
parent
076dc76ca4
commit
998cc778e6
|
@ -38,7 +38,7 @@
|
||||||
<form
|
<form
|
||||||
class="flex flex-col items-center w-screen h-screen md:grid md:items-start md:p-5 md:bg-white md:rounded-2xl md:grid-cols-[auto_1fr] md:w-desktop-form md:h-desktop-form md:drop-shadow-2xl"
|
class="flex flex-col items-center w-screen h-screen md:grid md:items-start md:p-5 md:bg-white md:rounded-2xl md:grid-cols-[auto_1fr] md:w-desktop-form md:h-desktop-form md:drop-shadow-2xl"
|
||||||
id="form-step"
|
id="form-step"
|
||||||
th:fragment="formFragment(formData)"
|
th:fragment="formFragment(formData.userAnswers)"
|
||||||
hx-post="/submit-step/1/2"
|
hx-post="/submit-step/1/2"
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
action="/submit-step/1/2"
|
action="/submit-step/1/2"
|
||||||
|
@ -105,7 +105,7 @@
|
||||||
<label for="name" class="pt-3 text-sm md:pt-5 md:pb-2 text-marine-blue">Name</label>
|
<label for="name" class="pt-3 text-sm md:pt-5 md:pb-2 text-marine-blue">Name</label>
|
||||||
<input
|
<input
|
||||||
id="name"
|
id="name"
|
||||||
th:value="${formData.step1.name}"
|
th:value="${formData.userAnswers.step1.name}"
|
||||||
name="name"
|
name="name"
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="e.g. Stephen King"
|
placeholder="e.g. Stephen King"
|
||||||
|
@ -116,7 +116,7 @@
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
id="email"
|
id="email"
|
||||||
th:value="${formData.step1.email}"
|
th:value="${formData.userAnswers.step1.email}"
|
||||||
name="email"
|
name="email"
|
||||||
type="email"
|
type="email"
|
||||||
placeholder="e.g. stephenking@lorem.com"
|
placeholder="e.g. stephenking@lorem.com"
|
||||||
|
@ -127,7 +127,7 @@
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
id="phone"
|
id="phone"
|
||||||
th:value="${formData.step1.phone}"
|
th:value="${formData.userAnswers.step1.phone}"
|
||||||
name="phone"
|
name="phone"
|
||||||
type="tel"
|
type="tel"
|
||||||
placeholder="e.g. +1 234 567 890"
|
placeholder="e.g. +1 234 567 890"
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
action="/submit-step/2/3"
|
action="/submit-step/2/3"
|
||||||
method="post"
|
method="post"
|
||||||
th:fragment="formFragment(formData)"
|
th:fragment="formFragment(formData.userAnswers)"
|
||||||
>
|
>
|
||||||
<summary
|
<summary
|
||||||
class="w-full h-44 bg-no-repeat md:row-span-2 bg-sidebar-mobile marker:text-white md:bg-sidebar-desktop md:h-[568px] md:w-[274px]"
|
class="w-full h-44 bg-no-repeat md:row-span-2 bg-sidebar-mobile marker:text-white md:bg-sidebar-desktop md:h-[568px] md:w-[274px]"
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
name="plan-type"
|
name="plan-type"
|
||||||
value="Arcade"
|
value="Arcade"
|
||||||
class="hidden peer"
|
class="hidden peer"
|
||||||
th:checked="${formData.step2.planType.toString()} == 'Arcade'"
|
th:checked="${formData.userAnswers.step2.planType.toString()} == 'Arcade'"
|
||||||
checked
|
checked
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
|
@ -136,7 +136,7 @@
|
||||||
name="plan-type"
|
name="plan-type"
|
||||||
value="Advanced"
|
value="Advanced"
|
||||||
class="hidden peer"
|
class="hidden peer"
|
||||||
th:checked="${formData.step2.planType.toString()} == 'Advanced'"
|
th:checked="${formData.userAnswers.step2.planType.toString()} == 'Advanced'"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia"
|
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia"
|
||||||
|
@ -152,7 +152,7 @@
|
||||||
name="plan-type"
|
name="plan-type"
|
||||||
value="Pro"
|
value="Pro"
|
||||||
class="hidden peer"
|
class="hidden peer"
|
||||||
th:checked="${formData.step2.planType.toString()} == 'Pro'"
|
th:checked="${formData.userAnswers.step2.planType.toString()} == 'Pro'"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia"
|
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia"
|
||||||
|
@ -172,7 +172,7 @@
|
||||||
name="isPackageYearly"
|
name="isPackageYearly"
|
||||||
role="switch"
|
role="switch"
|
||||||
id="packageDuration"
|
id="packageDuration"
|
||||||
th:checked="${formData.step2.isYearly}"
|
th:checked="${formData.userAnswers.step2.isYearly}"
|
||||||
/>
|
/>
|
||||||
<span class="row-start-1 text-marine-blue peer-checked:text-cool-gray">Monthly</span>
|
<span class="row-start-1 text-marine-blue peer-checked:text-cool-gray">Monthly</span>
|
||||||
<span class="row-start-1 text-cool-gray peer-checked:text-marine-blue">Yearly</span>
|
<span class="row-start-1 text-cool-gray peer-checked:text-marine-blue">Yearly</span>
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
hx-swap="outerHTML"
|
hx-swap="outerHTML"
|
||||||
action="/submit-step/3/4"
|
action="/submit-step/3/4"
|
||||||
method="post"
|
method="post"
|
||||||
th:fragment="formFragment(formData)"
|
th:fragment="formFragment(formData.userAnswers)"
|
||||||
>
|
>
|
||||||
<summary
|
<summary
|
||||||
class="w-full h-44 bg-no-repeat md:row-span-2 bg-sidebar-mobile marker:text-white md:bg-sidebar-desktop md:h-[568px] md:w-[274px]"
|
class="w-full h-44 bg-no-repeat md:row-span-2 bg-sidebar-mobile marker:text-white md:bg-sidebar-desktop md:h-[568px] md:w-[274px]"
|
||||||
|
@ -117,7 +117,7 @@
|
||||||
value="OnlineService"
|
value="OnlineService"
|
||||||
name="addon-services"
|
name="addon-services"
|
||||||
class="my-7 w-6 h-6 peer"
|
class="my-7 w-6 h-6 peer"
|
||||||
th:checked="${formData.step3.containsAddon('OnlineService')}"
|
th:checked="${formData.userAnswers.step3.containsAddon('OnlineService')}"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia/50"
|
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia/50"
|
||||||
|
@ -141,7 +141,7 @@
|
||||||
name="addon-services"
|
name="addon-services"
|
||||||
value="LargerStorage"
|
value="LargerStorage"
|
||||||
class="my-7 w-6 h-6 peer"
|
class="my-7 w-6 h-6 peer"
|
||||||
th:checked="${formData.step3.containsAddon('LargerStorage')}"
|
th:checked="${formData.userAnswers.step3.containsAddon('LargerStorage')}"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia/50"
|
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia/50"
|
||||||
|
@ -165,7 +165,7 @@
|
||||||
name="addon-services"
|
name="addon-services"
|
||||||
value="CustomProfile"
|
value="CustomProfile"
|
||||||
class="my-7 w-6 h-6 peer"
|
class="my-7 w-6 h-6 peer"
|
||||||
th:checked="${formData.step3.containsAddon('CustomProfile')}"
|
th:checked="${formData.userAnswers.step3.containsAddon('CustomProfile')}"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia/50"
|
class="absolute inset-y-0 inset-x-0 rounded-lg border border-cool-gray peer-checked:border-purplish-blue peer-checked:bg-magnolia/50"
|
||||||
|
|
|
@ -12,6 +12,53 @@ object Models {
|
||||||
step4 = StepAnswers.Step4(true)
|
step4 = StepAnswers.Step4(true)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** Labels and form info which dynamically depend on user answers e.g plan
|
||||||
|
* name 'Arcade (Yearly)' vs 'Pro (Monthly)'
|
||||||
|
*/
|
||||||
|
final case class FormData(
|
||||||
|
userAnswers: Answers
|
||||||
|
) {
|
||||||
|
def planDiscountMessage: Option[String] =
|
||||||
|
if (userAnswers.step2.isYearly) Some("2 monts free") else None
|
||||||
|
|
||||||
|
// yeah, in real world it will not be this simple
|
||||||
|
def yearlyCost(monthlyCost: Int): Int = 10 * monthlyCost
|
||||||
|
|
||||||
|
def periodCostLabel: String = {
|
||||||
|
if (userAnswers.step2.isYearly) "yr" else "mo"
|
||||||
|
}
|
||||||
|
|
||||||
|
def planCost: Int = {
|
||||||
|
val monthlyPlanCost = userAnswers.step2.planType match {
|
||||||
|
case PlanType.Arcade => 9
|
||||||
|
case PlanType.Advanced => 12
|
||||||
|
case PlanType.Pro => 15
|
||||||
|
}
|
||||||
|
if (userAnswers.step2.isYearly) yearlyCost(monthlyPlanCost)
|
||||||
|
else monthlyPlanCost
|
||||||
|
}
|
||||||
|
|
||||||
|
def addonMontlyCost: Addons => Int = {
|
||||||
|
case Addons.OnlineService => 1
|
||||||
|
case Addons.LargerStorage => 2
|
||||||
|
case Addons.CustomProfile => 2
|
||||||
|
}
|
||||||
|
|
||||||
|
def addonCost(addon: Addons): Int = {
|
||||||
|
val monthCost = addonMontlyCost(addon)
|
||||||
|
if (userAnswers.step2.isYearly) yearlyCost(monthCost) else monthCost
|
||||||
|
}
|
||||||
|
|
||||||
|
def fullPlanName: String = {
|
||||||
|
val period = if (userAnswers.step2.isYearly) "Yearly" else "Monthly"
|
||||||
|
s"${userAnswers.step2.planType} (${period})"
|
||||||
|
}
|
||||||
|
|
||||||
|
def fullOrderPrice: Int = {
|
||||||
|
planCost + userAnswers.step3.addons.map(addonCost).sum
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final case class Answers(
|
final case class Answers(
|
||||||
sessionId: String = "id1",
|
sessionId: String = "id1",
|
||||||
currentStep: Int = 1,
|
currentStep: Int = 1,
|
||||||
|
|
|
@ -75,7 +75,8 @@ case class Routes()(implicit cc: castor.Context, log: cask.Logger)
|
||||||
val state = Sessions.sessionReplies.getOrElse(id, Answers(id))
|
val state = Sessions.sessionReplies.getOrElse(id, Answers(id))
|
||||||
println(s"starting form for $state")
|
println(s"starting form for $state")
|
||||||
val context = new Context()
|
val context = new Context()
|
||||||
context.setVariable(formDataContextVarName, state)
|
val formData = Models.FormData(state)
|
||||||
|
context.setVariable(formDataContextVarName, formData)
|
||||||
val formFragment = templateEngine.process(
|
val formFragment = templateEngine.process(
|
||||||
state.fragmentName,
|
state.fragmentName,
|
||||||
Set("formFragment").asJava,
|
Set("formFragment").asJava,
|
||||||
|
@ -106,7 +107,8 @@ case class Routes()(implicit cc: castor.Context, log: cask.Logger)
|
||||||
Sessions.sessionReplies.update(id, updatedAnswers)
|
Sessions.sessionReplies.update(id, updatedAnswers)
|
||||||
|
|
||||||
val context = new Context()
|
val context = new Context()
|
||||||
context.setVariable(formDataContextVarName, updatedAnswers)
|
val formData = Models.FormData(updatedAnswers)
|
||||||
|
context.setVariable(formDataContextVarName, formData)
|
||||||
val nextFormFragment = templateEngine.process(
|
val nextFormFragment = templateEngine.process(
|
||||||
updatedAnswers.fragmentName,
|
updatedAnswers.fragmentName,
|
||||||
Set("formFragment").asJava,
|
Set("formFragment").asJava,
|
||||||
|
|
Loading…
Reference in New Issue