Skip to content

Commit

Permalink
add selectable start task - fixes #23
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesward committed Jul 24, 2018
1 parent 2a022e6 commit f4a9cef
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 27 deletions.
13 changes: 7 additions & 6 deletions app/controllers/Application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -101,28 +101,29 @@ class Application @Inject()
}
}

def newRequest(maybeName: Option[String], maybeProgramKey: Option[String]) = userAction.async { implicit userRequest =>
def newRequest(maybeName: Option[String], maybeProgramKey: Option[String], maybeStartTask: Option[String]) = userAction.async { implicit userRequest =>
withUserInfo { userInfo =>
metadataService.fetchMetadata.map { metadata =>
val maybeTaskView = for {
name <- maybeName
programKey <- maybeProgramKey
programMetadata <- metadata.programs.get(programKey)
startTask <- programMetadata.tasks.get("start")
} yield Ok(newRequestFormView(programKey, name, startTask, userInfo))
startTask <- maybeStartTask
task <- programMetadata.tasks.get(startTask)
} yield Ok(newRequestFormView(programKey, name, startTask, task, userInfo))

maybeTaskView.getOrElse(Ok(newRequestView(userInfo, metadata)))
maybeTaskView.getOrElse(Ok(newRequestView(userInfo, metadata, maybeName, maybeProgramKey, maybeStartTask)))
}
}
}

def createRequest(name: String, programKey: String) = userAction.async(parse.json) { implicit userRequest =>
def createRequest(name: String, programKey: String, startTask: String) = userAction.async(parse.json) { implicit userRequest =>
withUserInfo { userInfo =>
metadataService.fetchMetadata.flatMap { metadata =>
metadata.programs.get(programKey).fold {
Future.successful(NotFound(s"Program '$programKey' not found"))
} { programMetadata =>
programMetadata.tasks.get("start").fold(Future.successful(InternalServerError("Could not find task named 'start'"))) { metaTask =>
programMetadata.tasks.get(startTask).fold(Future.successful(InternalServerError(s"Could not find task named '$startTask'"))) { metaTask =>
dataFacade.createRequest(programKey, name, userInfo.email).flatMap { request =>
completableByWithDefaults(metaTask.completableBy, Some(userInfo.email), None).flatMap(programMetadata.completableBy).fold {
Future.successful(BadRequest("Could not determine who can complete the task"))
Expand Down
3 changes: 2 additions & 1 deletion app/utils/Metadata.scala
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ object Metadata {
implicit val jsonReads: Reads[Metadata] = multiprogramReads.orElse(singleprogramReads)
}

case class Program(name: String, description: Option[String], groups: Map[String, Set[String]], tasks: Map[String, Task.Prototype]) {
case class Program(name: String, description: Option[String], startTasks: Set[String], groups: Map[String, Set[String]], tasks: Map[String, Task.Prototype]) {
val admins: Set[String] = groups.getOrElse("admin", Set.empty[String])

def isAdmin(userInfo: UserInfo): Boolean = isAdmin(userInfo.email)
Expand All @@ -62,6 +62,7 @@ object Program {
implicit val jsonReads: Reads[Program] = (
(__ \ "name").read[String].orElse(Reads.pure("Default")) ~
(__ \ "description").readNullable[String] ~
(__ \ "start_tasks").read[Set[String]].orElse(Reads.pure(Set.empty[String])) ~
(__ \ "groups").read[Map[String, Set[String]]] ~
(__ \ "tasks").read[Map[String, Task.Prototype]]
)(Program.apply _)
Expand Down
2 changes: 1 addition & 1 deletion app/views/Main.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<ul class="nav navbar-nav">
<li @if(title == "Your Open Tasks") { class="active" }><a href="@routes.Application.openUserTasks()">Your Open Tasks</a></li>
<li @if(title == "OSS Requests") { class="active" }><a href="@routes.Application.requests()">Requests</a></li>
<li @if(title == "New Request") { class="active" }><a href="@routes.Application.newRequest(None, None)">New Request</a></li>
<li @if(title == "New Request") { class="active" }><a href="@routes.Application.newRequest(None, None, None)">New Request</a></li>
</ul>

<div class="navbar-header navbar-right">
Expand Down
75 changes: 65 additions & 10 deletions app/views/NewRequest.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,26 @@

@this(mainView: Main)

@(userInfo: _root_.utils.UserInfo, metadata: _root_.utils.Metadata)(implicit request: RequestHeader)
@(userInfo: _root_.utils.UserInfo, metadata: _root_.utils.Metadata, maybeName: Option[String], maybeProgramKey: Option[String], maybeStartTask: Option[String])(implicit request: RequestHeader)

@mainView(Some(userInfo))("New Request") {

<form action="@routes.Application.newRequest(None, None)" method="get" class="form-horizontal">
<form id="new-request-form" action="@routes.Application.newRequest(None, None, None)" method="get" class="form-horizontal">
<div class="form-group">
<label for="name" class="col-sm-2 control-label">Request Name</label>
<div class="col-sm-3">
<input id="name" name="name" required class="form-control">
<input id="name" name="name" required class="form-control" @for(name <- maybeName) { value="@name" }>
</div>
</div>

@if(metadata.programs.size == 1) {
<input name="program" value="@metadata.programs.head._1" type="hidden">
} else {
<div class="form-group">
<label for="program" class="col-sm-2 control-label">Program</label>
@maybeProgramKey.fold {
@if(metadata.programs.size == 1) {
<input name="program" value="@metadata.programs.head._1" type="hidden">
} else {
<div class="form-group">
<label for="program" class="col-sm-2 control-label">Program</label>

<div class="col-sm-6">
<div class="col-sm-6">
@for(program <- metadata.programs) {
<div class="radio">
<label>
Expand All @@ -37,8 +38,40 @@
</label>
</div>
}
</div>
</div>
</div>
}
} { programKey =>
<input type="hidden" name="program" value="@programKey">
}

@maybeStartTask.fold {
<input type="hidden" name="startTask">
@for(program <- metadata.programs) {
<div class="start-task" data-program="@program._1" style="display: none">
@if(program._2.startTasks.isEmpty) {
<input type="hidden" class="input" data-program="@program._1" value="start">
} else if(program._2.startTasks.size == 1) {
<input type="hidden" class="input" data-program="@program._1" value="@program._2.startTasks.head">
} else {
<div class="form-group">
<label for="startTask-@program._1" class="col-sm-2 control-label">Start With</label>

<div class="col-sm-3">
<select id="startTask-@program._1" data-program="@program._1" class="form-control input" required>
@for(taskKey <- program._2.startTasks) {
@for(task <- program._2.tasks.get(taskKey)) {
<option value="@taskKey">@task.label</option>
}
}
</select>
</div>
</div>
}
</div>
}
} { startTask =>
<input type="hidden" name="startTask" value="@startTask">
}

<div class="form-group">
Expand All @@ -48,4 +81,26 @@
</div>
</form>

<script>
function programKey() {
return $('input[name=program]:checked').val();
}

function refreshStartTask() {
$('.start-task').hide();
$('.start-task[data-program=' + programKey() + ']').show();
}

$(function () {
refreshStartTask();

$('input[name=program]').change(refreshStartTask);

$('#new-request-form').submit(function(event) {
const startTask = $('.input[data-program=' + programKey() + ']').val();
$('input[name=startTask]').val(startTask);
});
});
</script>

}
4 changes: 2 additions & 2 deletions app/views/NewRequestForm.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

@this(mainView: Main, taskEditView: utils.TaskEdit)

@(programKey: String, projectName: String, taskPrototype: models.Task.Prototype, userInfo: _root_.utils.UserInfo)(implicit request: RequestHeader)
@(programKey: String, projectName: String, taskKey: String, taskPrototype: models.Task.Prototype, userInfo: _root_.utils.UserInfo)(implicit request: RequestHeader)

@mainView(Some(userInfo))("New Request") {

Expand All @@ -16,7 +16,7 @@
<h3 class="panel-title">New Request - @projectName</h3>
</div>

@taskEditView(Left((programKey, projectName, taskPrototype, Set(userInfo.email))))
@taskEditView(Left((programKey, projectName, taskKey, taskPrototype, Set(userInfo.email))))
</div>

}
6 changes: 3 additions & 3 deletions app/views/utils/TaskEdit.scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,20 @@

@this()

@(newOrExistingTask: Either[(String, String, models.Task.Prototype, Set[String]), models.Task])(implicit request: RequestHeader)
@(newOrExistingTask: Either[(String, String, String, models.Task.Prototype, Set[String]), models.Task])(implicit request: RequestHeader)

@import helper._

@taskPrototype = @{
newOrExistingTask match {
case Left((_, _, taskPrototype, _)) => taskPrototype
case Left((_, _, _, taskPrototype, _)) => taskPrototype
case Right(task) => task.prototype
}
}

@formUrl(state: models.State.State = State.Completed) = @{
newOrExistingTask match {
case Left((programKey, name, _, _)) => CSRF(routes.Application.createRequest(name, programKey)).url
case Left((programKey, name, taskKey, _, _)) => CSRF(routes.Application.createRequest(name, programKey, taskKey)).url
case Right(task) => CSRF(routes.Application.updateTaskState(task.requestSlug, task.id, state)).url
}
}
Expand Down
4 changes: 2 additions & 2 deletions conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
# User Visible URLs
GET / controllers.Application.openUserTasks
GET /requests controllers.Application.requests
GET /request controllers.Application.newRequest(name: Option[String], program: Option[String])
GET /request controllers.Application.newRequest(name: Option[String], program: Option[String], startTask: Option[String])
GET /request/:requestSlug controllers.Application.request(requestSlug: String)
GET /request/:requestSlug/task/:taskId controllers.Application.task(requestSlug: String, taskId: Int)

# API and Form Post URLs
POST /request controllers.Application.createRequest(name: String, program: String)
POST /request controllers.Application.createRequest(name: String, program: String, startTask: String)
POST /request/:requestSlug controllers.Application.updateRequest(requestSlug: String, state: models.State.State)
POST /request/:requestSlug/task controllers.Application.addTask(requestSlug: String)
POST /request/:requestSlug/task/:taskId/state controllers.Application.updateTaskState(requestSlug: String, taskId: Int, state: models.State.State)
Expand Down
22 changes: 20 additions & 2 deletions examples/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@
},
"two": {
"name": "Program Two",
"start_tasks": ["oss_request_info", "another_task"],
"groups": {
"admin": [
"foo@bar.com",
Expand All @@ -141,22 +142,39 @@
]
},
"tasks": {
"start": {
"oss_request_info": {
"label": "OSS Request Info",
"type": "INPUT",
"info": "Please fill in all the info for your OSS request.",
"form": {
"schema": {
"type": "object",
"properties": {
"project_name": {
"project_description": {
"title": "Project Description",
"type": "string",
"required": true
}
}
}
}
},
"another_task": {
"label": "Another Task",
"type": "INPUT",
"info": "Fill out the form",
"form": {
"schema": {
"type": "object",
"properties": {
"favorite_color": {
"title": "What is your favorite color?",
"type": "string",
"required": true
}
}
}
}
}
}
}
Expand Down

0 comments on commit f4a9cef

Please sign in to comment.