coach integration and settings UI

es2016
Thibault Duplessis 2019-02-22 15:25:38 +07:00
parent 0b49aa6657
commit fa9d5ecba3
10 changed files with 152 additions and 154 deletions

View File

@ -18,7 +18,6 @@ object layout {
evenMoreJs: Html = emptyHtml
)(body: Html)(implicit ctx: Context) = views.html.base.layout(
title = title,
menu = Some(frag()),
moreCss = frag(responsiveCssTag("account"), evenMoreCss),
responsive = true,
moreJs = frag(jsTag("account.js"), evenMoreJs)
@ -37,7 +36,7 @@ object layout {
a(cls := active.activeO("editProfile"), href := routes.Account.profile())(
trans.editProfile.frag()
),
isGranted(_.Coach) option a(href := routes.Coach.edit)("Coach profile"),
isGranted(_.Coach) option a(cls := active.activeO("coach"), href := routes.Coach.edit)("Coach profile"),
div(cls := "sep"),
a(cls := active.activeO("password"), href := routes.Account.passwd())(
trans.changePassword.frag()

View File

@ -7,102 +7,101 @@
@jsTag("coach.form.js")
}
@base.layout(title = s"${c.user.titleUsername} coach page",
moreCss = responsiveCssTag("coach.editor"),
responsive = true,
moreJs = moreJs) {
<main class="coach-edit">
<aside class="coach-side">
<a href="@routes.Coach.show(c.user.username)" class="button button-empty text" data-icon="v">Preview coach page</a>
</aside>
<div class="coach-main box">
<div class="top">
<div class="picture_wrap">
@if(c.coach.hasPicture) {
<a class="upload_picture" href="@routes.Coach.picture" title="Change/delete your profile picture">
@widget.pic(c, 250).toHtml
</a>
} else {
<div class="upload_picture">
<a class="button" href="@routes.Coach.picture">Upload a profile picture</a>
@account.layout(
title = s"${c.user.titleUsername} coach page",
evenMoreCss = responsiveCssTag("coach.editor"),
evenMoreJs = moreJs,
active = "coach") {
<div class="account coach-edit box">
<div class="top">
<div class="picture_wrap">
@if(c.coach.hasPicture) {
<a class="upload_picture" href="@routes.Coach.picture" title="Change/delete your profile picture">
@widget.pic(c, 250).toHtml
</a>
} else {
<div class="upload_picture">
<a class="button" href="@routes.Coach.picture">Upload a profile picture</a>
</div>
}
</div>
<div class="overview">
<h1>@widget.titleName(c).toHtml</h1>
<div class="todo" data-profile="@c.user.profileOrDefault.isComplete">
<h3>TODO list before publishing your coach profile</h3>
<ul></ul>
</div>
<div>
<a href="@routes.Coach.show(c.user.username)" class="button button-empty text" data-icon="v">Preview coach page</a>
</div>
</div>
</div>
<form class="box__pad form3" action="@routes.Coach.edit" method="POST">
<div class="tabs">
<div data-tab="basics" class="active">Basics</div>
<div data-tab="texts">Texts</div>
<div data-tab="contents">Contents</div>
<div data-tab="reviews" data-count="@reviews.list.size" class="data-count">
Pending reviews
</div>
</div>
<div class="panels">
<div class="panel basics active">
@form3.split {
@form3.checkbox(form("listed"), raw("Publish on the coaches list"), help = raw("Enable when your profile is ready").some, half = true)
@form3.checkbox(form("available"), raw("Currently available for lessons"), help = raw("Enable to get more students").some, half = true)
}
@form3.group(form("profile.headline"), raw("Short and inspiring headline"), help = raw("Just one sentence to make students want to choose you").some)(form3.input(_))
@form3.split {
@form3.group(form("profile.languages"), raw("Languages spoken"), help = raw("Which languages can you give lessons in?").some, half = true)(form3.input(_))
@form3.group(form("profile.hourlyRate"), raw("Hourly rate"), help = raw("Indicative, non-contractual").some, half = true)(form3.input(_))
}
</div>
<div class="panel texts">
@form3.group(form("profile.description"), raw("Who are you?"), help = raw("Age, profession, country... let your students know you").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.playingExperience"), raw("Playing experience"), help = raw("Tournaments played, best wins, other achievements").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.teachingExperience"), raw("Teaching experience"), help = raw("Diplomas, years of practice, best student results").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.otherExperience"), raw("Other experiences"), help = raw("E.g. as chess commentator, or teaching other domains").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.skills"), raw("Best skills in chess and teaching"))(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.methodology"), raw("Teaching methodology"), help = raw("How you prepare and run lessons. How you follow up with students.").some)(form3.textarea(_)(*.rows := 8))
</div>
<div class="panel contents">
@form3.group(form("profile.publicStudies"), raw("Featured public lichess studies"), help = raw("Up to 6 lichess study URLs, one per line").some)(form3.textarea(_)())
@form3.group(form("profile.youtubeChannel"), raw("URL of your Youtube channel"))(form3.input(_))
@form3.group(form("profile.youtubeVideos"), raw("Featured youtube videos"), help = raw("Up to 6 Youtube video URLs, one per line").some)(form3.textarea(_)(*.rows := 6))
</div>
<div class="panel reviews">
<p class="help text" data-icon="">Reviews are visible only after you approve them.</p>
@reviews.list.map { r =>
<div class="review" data-action="@routes.Coach.approveReview(r.id)">
<div class="user">
@userIdLink(r.userId.some)
review.barRating(selected = r.score.some, enabled = false).toHtml
@momentFromNow(r.updatedAt)
</div>
<div class="content">
@if(r.moddedAt.isDefined) {
<div class="modded">
Moderators have disapproved this review. Please only accept reviews from
actual students, based on actual lessons. Reviews must be about your coaching services.
<br />
You may delete this review, or ask the author to rephrase it, then approve it.
</div>
}
@richText(r.text)
</div>
<div class="actions">
@if(r.moddedAt.fold(true)(_.isBefore(r.updatedAt))) {
<a data-value="1" class="yes" data-icon="E"></a>
}
<a data-value="0" class="no" data-icon="L"></a>
</div>
</div>
}
</div>
<div class="overview">
<h1>@widget.titleName(c).toHtml</h1>
<div class="todo" data-profile="@c.user.profileOrDefault.isComplete">
<h3>TODO list before publishing your coach profile</h3>
<ul></ul>
</div>
</div>
</div>
<form class="box__pad form3" action="@routes.Coach.edit" method="POST">
<div class="tabs">
<div data-tab="basics" class="active">Basics</div>
<div data-tab="texts">Texts</div>
<div data-tab="contents">Contents</div>
<div data-tab="reviews" data-count="@reviews.list.size" class="data-count">
Pending reviews
</div>
</div>
<div class="panels">
<div class="panel basics active">
@form3.split {
@form3.checkbox(form("listed"), raw("Publish on the coaches list"), help = raw("Enable when your profile is ready").some, half = true)
@form3.checkbox(form("available"), raw("Currently available for lessons"), help = raw("Enable to get more students").some, half = true)
}
@form3.group(form("profile.headline"), raw("Short and inspiring headline"), help = raw("Just one sentence to make students want to choose you").some)(form3.input(_))
@form3.split {
@form3.group(form("profile.languages"), raw("Languages spoken"), help = raw("Which languages can you give lessons in?").some, half = true)(form3.input(_))
@form3.group(form("profile.hourlyRate"), raw("Hourly rate"), help = raw("Indicative, non-contractual").some, half = true)(form3.input(_))
}
</div>
<div class="panel texts">
@form3.group(form("profile.description"), raw("Who are you?"), help = raw("Age, profession, country... let your students know you").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.playingExperience"), raw("Playing experience"), help = raw("Tournaments played, best wins, other achievements").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.teachingExperience"), raw("Teaching experience"), help = raw("Diplomas, years of practice, best student results").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.otherExperience"), raw("Other experiences"), help = raw("E.g. as chess commentator, or teaching other domains").some)(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.skills"), raw("Best skills in chess and teaching"))(form3.textarea(_)(*.rows := 8))
@form3.group(form("profile.methodology"), raw("Teaching methodology"), help = raw("How you prepare and run lessons. How you follow up with students.").some)(form3.textarea(_)(*.rows := 8))
</div>
<div class="panel contents">
@form3.group(form("profile.publicStudies"), raw("Featured public lichess studies"), help = raw("Up to 6 lichess study URLs, one per line").some)(form3.textarea(_)())
@form3.group(form("profile.youtubeChannel"), raw("URL of your Youtube channel"))(form3.input(_))
@form3.group(form("profile.youtubeVideos"), raw("Featured youtube videos"), help = raw("Up to 6 Youtube video URLs, one per line").some)(form3.textarea(_)(*.rows := 6))
</div>
<div class="panel reviews">
<p class="help text" data-icon="">Reviews are visible only after you approve them.</p>
@reviews.list.map { r =>
<div class="review" data-action="@routes.Coach.approveReview(r.id)">
<div class="user">
@userIdLink(r.userId.some)
review.barRating(selected = r.score.some, enabled = false).toHtml
@momentFromNow(r.updatedAt)
</div>
<div class="content">
@if(r.moddedAt.isDefined) {
<div class="modded">
Moderators have disapproved this review. Please only accept reviews from
actual students, based on actual lessons. Reviews must be about your coaching services.
<br />
You may delete this review, or ask the author to rephrase it, then approve it.
</div>
}
@richText(r.text)
</div>
<div class="actions">
@if(r.moddedAt.fold(true)(_.isBefore(r.updatedAt))) {
<a data-value="1" class="yes" data-icon="E"></a>
}
<a data-value="0" class="no" data-icon="L"></a>
</div>
</div>
}
</div>
</div>
<div class="status text" data-icon="E">Your changes have been saved.</div>
</form>
</div>
</main>
<div class="status text" data-icon="E">Your changes have been saved.</div>
</form>
</div>
}.toHtml

View File

@ -17,7 +17,7 @@ object index {
responsive = true,
moreJs = infiniteScrollTag
) {
main(cls := "coach-list")(
main(cls := "coach-list coach-full-page")(
st.aside(cls := "coach-list__side coach-side")(
img(src := staticUrl("images/icons/certification.svg"), cls := "coach-list__certification"),
h1("Certified coaches"),

View File

@ -10,37 +10,32 @@ import controllers.routes
object picture {
def apply(c: lila.coach.Coach.WithUser, error: Option[String] = None)(implicit ctx: Context) =
views.html.base.layout(
views.html.account.layout(
title = s"${c.user.titleUsername} coach picture",
moreJs = jsTag("coach.form.js"),
moreCss = responsiveCssTag("coach.editor"),
responsive = true
evenMoreJs = jsTag("coach.form.js"),
evenMoreCss = responsiveCssTag("coach.editor"),
active = "coach"
) {
main(cls := "coach-picture")(
st.aside(cls := "coach-side")(
a(href := routes.Coach.edit, cls := "text", dataIcon := "I")("Return to coach page form")
),
div(cls := "coach-main box")(
div(cls := "top")(
div(cls := "picture_wrap")(
widget.pic(c, 250)
),
h1(widget.titleName(c))
div(cls := "account coach-edit coach-picture box")(
div(cls := "top")(
div(cls := "picture_wrap")(
widget.pic(c, 250)
),
div(cls := "forms")(
error.map { e =>
p(cls := "error")(e)
},
st.form(action := routes.Coach.pictureApply, enctype := "multipart/form-data", cls := "upload")(
p("Max size: ", lila.db.Photographer.uploadMaxMb, "MB."),
form3.file.image("picture"),
button(tpe := "submit", cls := "button")("Upload profile picture")
),
c.coach.hasPicture option
st.form(action := routes.Coach.pictureDelete, cls := "delete")(
button(tpe := "submit", cls := "confirm button button-empty button-red")("Delete profile picture")
)
)
h1(widget.titleName(c))
),
div(cls := "forms")(
error.map { e =>
p(cls := "error")(e)
},
st.form(action := routes.Coach.pictureApply, enctype := "multipart/form-data", cls := "upload")(
p("Max size: ", lila.db.Photographer.uploadMaxMb, "MB."),
form3.file.image("picture"),
button(tpe := "submit", cls := "button")("Upload profile picture")
),
c.coach.hasPicture option
st.form(action := routes.Coach.pictureDelete, cls := "delete")(
button(tpe := "submit", cls := "confirm button button-empty button-red")("Delete profile picture")
)
)
)
}

View File

@ -48,7 +48,7 @@ $('.coach-review-form form').show();
image = c.coach.picturePath.map(p => dbImageUrl(p.value))
).some
) {
main(cls := "coach-show")(
main(cls := "coach-show coach-full-page")(
st.aside(cls := "coach-show__side coach-side")(
a(cls := "button button-empty", href := routes.User.show(c.user.username))("View ", c.user.username, " lichess profile"),
if (ctx.me.exists(c.coach.is)) frag(

View File

@ -15,7 +15,6 @@ group.radio {
justify-content: center;
padding: 10px;
height: 100%;
line-height: 1.8em;
cursor:pointer;
border-right: $border;
user-select: none;

View File

@ -85,13 +85,10 @@
}
& form section {
margin-bottom: 4rem;
& label {
font-size: 1.3em;
}
}
& h2 {
margin-bottom: 1.5rem;
font-size: 1.5em;
margin-bottom: 1.3rem;
font-size: 1.3em;
@include breakpoint($mq-x-small) {
overflow: hidden;
white-space: nowrap;

View File

@ -5,15 +5,15 @@
.coach-picture {
& .top {
display: flex;
line-height: 2;
}
& .top h1 {
@extend %roboto;
margin: 0;
padding: 0!important;
font-size: 32px;
font-size: 2rem;
line-height: 2;
text-transform: uppercase;
letter-spacing: 4px;
letter-spacing: 3px;
}
& .top .picture_wrap {
@ -37,12 +37,16 @@
flex-flow: column;
}
& .overview .todo,
& .overview.with_todo .analytics {
& .overview .todo {
display: none;
}
& .overview.with_todo .todo {
display: block;
@extend %box-radius;
border-radius: 20px;
padding: 1rem;
background: mix($c-error, $c-bg-box, 15%);
border: 1px solid $c-error;
margin-bottom: 2rem;
}
& .overview .todo h3 {
margin: 0;

View File

@ -1,26 +1,31 @@
$mq-coach-col2: $mq-medium;
main {
display: grid;
grid-template-areas:
'main'
'side';
& .coach-main {
grid-area: main;
}
& .coach-side {
grid-area: side;
margin: 2em 2em 0 2em;
}
@include breakpoint($mq-coach-col2) {
grid-template-columns: 300px auto;
grid-template-areas:
'side main';
grid-gap: 2rem;
&.coach-full-page {
& .coach-side {
margin: 4rem 0 0 0;
grid-area: side;
margin: 2em 2em 0 2em;
}
display: grid;
grid-template-areas:
'main'
'side';
@include breakpoint($mq-coach-col2) {
grid-template-columns: 300px auto;
grid-template-areas:
'side main';
grid-gap: 2rem;
& .coach-side {
margin: 4rem 0 0 0;
}
}
}
}

View File

@ -43,8 +43,8 @@
margin-bottom: 3rem;
}
&.erased {
background: rgba(128,128,128,0.2);
opacity: 0.5;
background: $c-shade;
opacity: 0.6;
}
&.erased .forum-post__message {
font-style: italic;