coach form WIP

coach
Thibault Duplessis 2016-08-22 12:55:55 +02:00
parent 2b0fb9e4f7
commit 6ab672ffa6
9 changed files with 137 additions and 6 deletions

View File

@ -3,7 +3,7 @@ package controllers
import play.api.mvc._, Results._
import lila.app._
import lila.coach.{ Coach => CoachModel }
import lila.coach.{ Coach => CoachModel, CoachForm }
import lila.user.{ User => UserModel, UserRepo }
import views._
@ -22,4 +22,22 @@ object Coach extends LilaController {
html.coach.show(coach)
}
}
def edit = Auth { implicit ctx =>
me =>
OptionOk(api find me) { c =>
html.coach.edit(c, CoachForm edit c.coach)
}
}
def editApply = AuthBody { implicit ctx =>
me =>
OptionFuResult(api find me) { c =>
implicit val req = ctx.body
CoachForm.edit(c.coach).bindFromRequest.fold(
form => fuccess(BadRequest(html.coach.edit(c, form))),
data => api.update(c, data) inject Redirect(routes.Coach.show(me.username))
)
}
}
}

View File

@ -0,0 +1,31 @@
@(c: lila.coach.Coach.WithUser, form: Form[_])(implicit ctx: Context)
@group(field: play.api.data.Field, name: Html, half: Boolean = false)(html: Html) = {
<div class="form-group@if(half){ half}@if(field.hasErrors){ has-error}">
@html
<label for="@field.id" class="control-label">@name</label>
<i class="bar"></i>
</div>
}
@moreCss = {
@cssTag("material.form.css")
@cssTag("coach.css")
}
@layout(title = c.user.titleUsername, active = "coach",
evenMoreCss = Some(cssTag("material.form.css"))) {
<div class="coach_edit content_box small_box no_padding">
<h1 class="lichess_title">
Coach @userLink(c.user)
</h1>
<form class="content_box_content material form" action="@routes.Coach.edit" method="POST">
@group(form("headline"), Html("Headline")) {
@base.input(form("headline"))
}
<div class="button-container">
<button type="submit" class="submit button text" data-icon="E">Save now</button>
</div>
</form>
</div>
}

View File

@ -1,11 +1,13 @@
@(title: String, active: String)(body: Html)(implicit ctx: Context)
@(title: String, active: String, evenMoreCss: Option[Html] = None)(body: Html)(implicit ctx: Context)
@moreCss = {
@cssTag("coach.css")
@evenMoreCss
}
@menu = {
<a class="@active.active("all")" href="@routes.Coach.index">All coaches</a>
<a class="text" data-icon="" href="//lichess.org/blog/V0KrLSkAAMo3hsi4/study-chess-the-lichess-way">What are studies?</a>
}
@base.layout(

View File

@ -371,6 +371,8 @@ POST /inbox/$id<\w{8}>/delete controllers.Message.delete(id: String)
# Coach
GET /coach controllers.Coach.index
GET /coach/:username controllers.Coach.show(username: String)
GET /coach/edit controllers.Coach.edit
POST /coach/edit controllers.Coach.editApply
# Paste
GET /paste controllers.Importer.importGame

View File

@ -9,7 +9,9 @@ private[coach] object BsonHandlers {
implicit val CoachEnabledBSONHandler = booleanAnyValHandler[Coach.Enabled](_.value, Coach.Enabled.apply)
implicit val CoachAvailableBSONHandler = booleanAnyValHandler[Coach.Available](_.value, Coach.Available.apply)
implicit val CoachCentsBSONHandler = intAnyValHandler[Coach.Cents](_.value, Coach.Cents.apply)
implicit val CoachMarkdownBSONHandler = stringAnyValHandler[Coach.Markdown](_.value, Coach.Markdown.apply)
implicit val CoachProfileMarkdownBSONHandler = stringAnyValHandler[CoachProfile.Markdown](_.value, CoachProfile.Markdown.apply)
implicit val CoachProfileBSONHandler = Macros.handler[CoachProfile]
implicit val CoachBSONHandler = Macros.handler[Coach]
}

View File

@ -9,8 +9,8 @@ case class Coach(
enabledByUser: Coach.Enabled,
enabledByMod: Coach.Enabled,
available: Coach.Available,
hourlyWage: Coach.Cents,
description: Coach.Markdown,
hourlyRate: Option[Coach.Cents],
profile: CoachProfile,
createdAt: DateTime,
updatedAt: DateTime) {
@ -21,11 +21,20 @@ case class Coach(
object Coach {
def make(user: User) = Coach(
_id = Id(user.id),
enabledByUser = Enabled(false),
enabledByMod = Enabled(false),
available = Available(false),
hourlyRate = None,
profile = CoachProfile(),
createdAt = DateTime.now,
updatedAt = DateTime.now)
case class WithUser(coach: Coach, user: User)
case class Id(value: String) extends AnyVal with StringValue
case class Enabled(value: Boolean) extends AnyVal
case class Available(value: Boolean) extends AnyVal
case class Cents(value: Int) extends AnyVal
case class Markdown(value: String) extends AnyVal with StringValue
}

View File

@ -22,6 +22,9 @@ final class CoachApi(coll: Coll) {
}
}
def update(c: Coach.WithUser, data: CoachForm.Data): Funit =
coll.update($id(c.coach.id), data(c.coach)).void
private def withUser(user: User)(coach: Coach): Coach.WithUser =
Coach.WithUser(coach, user)
}

View File

@ -0,0 +1,49 @@
package lila.coach
import org.joda.time.DateTime
import play.api.data._
import play.api.data.format.Formatter
import play.api.data.Forms._
object CoachForm {
def edit(coach: Coach) = Form(mapping(
"hourlyRate" -> optional(number(min = 5, max = 500)),
"available" -> optional(number),
"profile" -> profileMapping
)(Data.apply)(Data.unapply)) fill Data(
hourlyRate = coach.hourlyRate.map(_.value),
available = coach.available.value option 1,
profile = coach.profile)
case class Data(
hourlyRate: Option[Int],
available: Option[Int],
profile: CoachProfile) {
def apply(coach: Coach) = coach.copy(
hourlyRate = hourlyRate.map(_ * 100) map Coach.Cents.apply,
available = Coach.Available(available.isDefined),
profile = profile,
updatedAt = DateTime.now)
}
private def profileMapping = mapping(
"headline" -> optional(nonEmptyText(minLength = 5, maxLength = 140)),
"description" -> optional(markdown),
"playingExperience" -> optional(markdown),
"teachingExperience" -> optional(markdown),
"otherExperience" -> optional(markdown),
"skills" -> optional(markdown),
"methodology" -> optional(markdown)
)(CoachProfile.apply)(CoachProfile.unapply)
import CoachProfile.Markdown
private def markdown = of[Markdown]
private implicit def markdownFormat: Formatter[Markdown] = new Formatter[Markdown] {
def bind(key: String, data: Map[String, String]) =
data.get(key).map(Markdown.apply).toRight(Seq(FormError(key, "error.required", Nil)))
def unbind(key: String, value: Markdown) = Map(key -> value.value)
}
}

View File

@ -0,0 +1,15 @@
package lila.coach
case class CoachProfile(
headline: Option[String] = None,
description: Option[CoachProfile.Markdown] = None,
playingExperience: Option[CoachProfile.Markdown] = None,
teachingExperience: Option[CoachProfile.Markdown] = None,
otherExperience: Option[CoachProfile.Markdown] = None,
skills: Option[CoachProfile.Markdown] = None,
methodology: Option[CoachProfile.Markdown] = None)
object CoachProfile {
case class Markdown(value: String) extends AnyVal with StringValue
}