Skip to content

Commit

Permalink
IHRDS 3363 note section for test (#933)
Browse files Browse the repository at this point in the history
* added note section and display the first line of note in the preview

* add edit -> save logic and display

* unify placeholder text
  • Loading branch information
lofoyet committed Apr 3, 2024
1 parent 1448f0a commit 279ebe6
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 14 deletions.
12 changes: 12 additions & 0 deletions core/src/main/scala/com/iheart/thomas/abtest/AbtestAlg.scala
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ trait AbtestAlg[F[_]] extends TestsDataProvider[F] with FeatureRetriever[F] {
spec: AbtestSpec
): F[Entity[Abtest]]

def updateTestNote(
testId: TestId,
note: String
): F[Entity[Abtest]]

/** @param feature
* @return
* tests for this feature in chronological descending order
Expand Down Expand Up @@ -294,6 +299,13 @@ final class DefaultAbtestAlg[F[_]](

} yield r

override def updateTestNote(testId: TestId, note: String): F[Entity[Abtest]] = {
for {
test <- getTest(testId)
r <- abTestDao.update(test.copy(data = test.data.copy(note = Some(note))))
} yield r
}

def continue(testSpec: AbtestSpec): F[Entity[Abtest]] =
addTestWithLock(testSpec) {
for {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ package model {
userMetaCriteria: UserMetaCriteria = None,
salt: Option[String] = None,
segmentRanges: List[GroupRange] = Nil,
specialization: Option[Abtest.Specialization] = None) {
specialization: Option[Abtest.Specialization] = None,
note: Option[String] = None){

val hasEligibilityControl: Boolean =
requiredTags.nonEmpty || userMetaCriteria.nonEmpty
Expand Down Expand Up @@ -101,6 +102,8 @@ package model {
end = end.map(_.toOffsetDateTimeSystemDefault),
groups = groups.map(g => g.copy(meta = g.meta))
)

def shortNote: String = note.map(_.split("\n").head).getOrElse("")
}

/** Data used to create an A/B tests
Expand Down Expand Up @@ -160,7 +163,8 @@ package model {
userMetaCriteria: UserMetaCriteria = None,
reshuffle: Boolean = false,
segmentRanges: List[GroupRange] = Nil,
specialization: Option[Abtest.Specialization] = None) {
specialization: Option[Abtest.Specialization] = None,
note: Option[String] = None) {

val startI = start.toInstant
val endI = end.map(_.toInstant)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,29 @@ class AbtestManagementUI[F[_]](
Ok(redirect(reverseRoutes.tests, s"Successfully $message."))
}

case se @ POST -> Root / "tests" / testId / "note" asAuthed u =>
get(testId).flatMap { test =>
se.request
.as[NoteForm]
.redeemWith(
e => BadRequest(errorMsg(displayError(e))),
note =>
alg
.updateTestNote(
testId.coerce[EntityId],
note.note.get
)
.flatMap(redirectToTest)
.handleErrorWith(_ =>
u.canManage(test)
.flatMap(_ =>
BadRequest(
)
)
)
)
}

// Add new test to a feature
case se @ POST -> Root / "features" / feature / "tests" asAuthed u =>
se.request
Expand Down Expand Up @@ -551,7 +574,8 @@ object AbtestManagementUI {
alternativeIdName: Option[MetaFieldName] = None,
userMetaCriteria: UserMetaCriteria = None,
reshuffle: Boolean = false,
segmentRanges: List[GroupRange] = Nil) {
segmentRanges: List[GroupRange] = Nil,
note: Option[String] = None) {
def toAbtestSpec[F[_]: Functor: Clock](
u: User,
feature: FeatureName
Expand All @@ -568,13 +592,16 @@ object AbtestManagementUI {
alternativeIdName = alternativeIdName,
userMetaCriteria = userMetaCriteria,
reshuffle = reshuffle,
segmentRanges = segmentRanges
segmentRanges = segmentRanges,
note = note
)
}

}
}

case class NoteForm(note: Option[String] = Some(""))

implicit val userGroupQueryFormDecoder: FormDataDecoder[UserGroupQuery] = {
implicit val mapQPD = mapQueryParamDecoder
(
Expand All @@ -595,9 +622,15 @@ object AbtestManagementUI {
fieldOptional[MetaFieldName]("alternativeIdName"),
fieldOptional[UserMetaCriterion.And]("userMetaCriteria"),
fieldEither[Boolean]("reshuffle").default(false),
list[GroupRange]("segmentRanges")
list[GroupRange]("segmentRanges"),
fieldOptional[String]("note")
).mapN(SpecForm.apply).sanitized

implicit val noteFormDecoder: FormDataDecoder[NoteForm] =
(
fieldOptional[String]("note")
).map(NoteForm.apply).sanitized

implicit val FeatureFormDecoder: FormDataDecoder[Feature] = {
implicit val mapQPD = mapQueryParamDecoder

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

<a href="tests/@test._id" class="list-group-item list-group-item-action flex-column align-items-start">
<div class="row">
<div class="col-auto me-auto">
<div class="col-auto">
<div>
<span class="mb-1 h5 fw-light text-success">@test.data.name</span>
<span class="badge ml-3 bg-@formatStatus(test)._2">
Expand All @@ -69,6 +69,9 @@
</div>
</div>

<div class="col flex-grow-1 mb-1 h6 fw-normal text-secondary">
<div class="overflow-y-auto" style="max-height: 60px;">@test.data.shortNote</div>
</div>
<div class="col-auto">
<div class="text-end">
<small class="fw-heavy">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@

@topNav("A/B Test for feature " + test.data.feature, "A/B Tests") {


<form action="#" method="post">
<div class="m-3">
<span class="h5">A/B Test <span class="text-success">@test.data.name</span> for </span>
Expand Down Expand Up @@ -93,8 +92,27 @@
}
</div>

<div class="row mt-4 mb-4">
<div class="col-md">
<div class="card mt-4">
<div class="card-header">
<span class="h5">Description</span>
</div>
<div class="card-body">
<form action="@test._id/note" method="post" id="noteForm">
<button disabled type="submit" class="btn btn-success me-2" role="button" id="noteSaveBtn">Save</button>
<button type="button" class="btn btn-primary" role="button" id="noteEditBtn">Edit</button>
<div class="form-group mt-4">
<label for="noteArea">Note</label>
<textarea readonly id="noteArea" name="note" class="form-control" rows="5" placeholder="Purposes and results.">@test.data.note.getOrElse("")</textarea>
</div>
</form>
</div>
</div>
</div>
</div>

<div class="row mt-6">
<div class="row mt-4 mb-4">
<div class="col-6">
<div class="card">
<div class="card-header">
Expand Down Expand Up @@ -132,8 +150,20 @@
</div>
</div>
</div>


}


<script>
document.addEventListener("DOMContentLoaded", function() {
var textarea = document.getElementById("noteArea");
var editBtn = document.getElementById("noteEditBtn");
var saveBtn = document.getElementById("noteSaveBtn");
var noteForm = document.getElementById("noteForm");

editBtn.addEventListener("click", function() {
textarea.readOnly = false;
textarea.focus();
editBtn.disabled = true;
saveBtn.disabled = false;
});
});
</script>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
@msg
</div>
}
<div class="row">

<div class="row">
<div class="col-md">
<div class="card">
<div class="card-header">
Expand Down Expand Up @@ -100,6 +101,20 @@
</div>
</div>

@if(!readonly) {
<div class="card mt-4">
<div class="card-header">
<span class="h5">Description</span>
</div>
<div class="card-body">
<div class="form-group">
<label class="form-label" for="testDescription">Note</label>
<textarea required id="testDescription" name="note" class="form-control" rows="5" placeholder="Purposes and results."></textarea>
</div>
</div>
</div>
}

</div>

<script>
Expand Down
5 changes: 3 additions & 2 deletions http4s/src/main/twirl/com/iheart/thomas/main.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
<title>@title</title>
<meta charset="UTF-8"/>
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@@5.2.1/dist/css/bootstrap.min.css"
<link href="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.3/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-iYQeCzEYFbKjA/T2uDLTpkwGzCiq6soy8tYaI1GyVh/UjpbCx/TYkiZhlZB6+fzT"
integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH"
crossorigin="anonymous">

<link rel="stylesheet"
Expand All @@ -24,6 +24,7 @@
integrity="sha512-aD9ophpFQ61nFZP6hXYu4Q/b/USW7rpLCQLX6Bi0WJHXNO7Js/fUENpBQf/+P4NtpzNX0jSgR5zVvPOJp+W2Kg=="
crossorigin="anonymous">
<link href="https://cdnjs.cloudflare.com/ajax/libs/jsoneditor/9.10.2/jsoneditor.min.css" rel="stylesheet">

<style>
.disabled-input {
pointer-events: none;
Expand Down

0 comments on commit 279ebe6

Please sign in to comment.