class design

This commit is contained in:
Thibault Duplessis 2020-01-19 19:42:56 -06:00
parent 16e6d86ed1
commit 1f7d91b653
7 changed files with 145 additions and 85 deletions

View file

@ -113,7 +113,9 @@ final class Clas(
def edit(id: String) = Secure(_.Teacher) { implicit ctx => me => def edit(id: String) = Secure(_.Teacher) { implicit ctx => me =>
WithClass(me, id) { _ => clas => WithClass(me, id) { _ => clas =>
Ok(html.clas.clas.edit(clas, env.clas.forms.edit(clas))).fuccess env.clas.api.student.activeWithUsers(clas) map { students =>
Ok(html.clas.clas.edit(clas, students, env.clas.forms.edit(clas)))
}
} }
} }
@ -123,7 +125,10 @@ final class Clas(
.edit(clas) .edit(clas)
.bindFromRequest()(ctx.body) .bindFromRequest()(ctx.body)
.fold( .fold(
err => BadRequest(html.clas.clas.edit(clas, err)).fuccess, err =>
env.clas.api.student.activeWithUsers(clas) map { students =>
BadRequest(html.clas.clas.edit(clas, students, err))
},
data => data =>
env.clas.api.clas.update(clas, data) map { clas => env.clas.api.clas.update(clas, data) map { clas =>
Redirect(routes.Clas.show(clas.id.value)).flashSuccess Redirect(routes.Clas.show(clas.id.value)).flashSuccess

View file

@ -5,7 +5,7 @@ import play.api.data.Form
import lila.api.Context import lila.api.Context
import lila.app.templating.Environment._ import lila.app.templating.Environment._
import lila.app.ui.ScalatagsTemplate._ import lila.app.ui.ScalatagsTemplate._
import lila.clas.Clas import lila.clas.{ Clas, Student }
import lila.clas.ClasForm.ClasData import lila.clas.ClasForm.ClasData
import controllers.routes import controllers.routes
@ -39,7 +39,7 @@ object clas {
h1("Lichess Classes"), h1("Lichess Classes"),
a( a(
href := routes.Clas.form, href := routes.Clas.form,
cls := "new button button-green button-empty", cls := "new button button-empty",
title := "New Class", title := "New Class",
dataIcon := "O" dataIcon := "O"
) )
@ -86,19 +86,19 @@ object clas {
innerForm(form, none) innerForm(form, none)
) )
def edit(c: lila.clas.Clas, form: Form[ClasData])(implicit ctx: Context) = def edit(c: lila.clas.Clas, students: List[Student.WithUser], form: Form[ClasData])(implicit ctx: Context) =
bits.layout(c.name, Left(c withStudents Nil))( teacherDashboard.layout(c, students, "edit")(
cls := "box-pad", div(cls := "box-pad")(
h1("Edit ", c.name), innerForm(form, c.some),
innerForm(form, c.some), hr,
hr, c.isActive option postForm(
c.isActive option postForm( action := routes.Clas.archive(c.id.value, true),
action := routes.Clas.archive(c.id.value, true), cls := "clas-edit__archive"
cls := "clas-edit__archive" )(
)( form3.submit("Archive", icon = none)(
form3.submit("Archive", icon = none)( cls := "confirm button-red button-empty",
cls := "confirm button-red button-empty", title := "Disband the class"
title := "Disband the class" )
) )
) )
) )

View file

@ -10,48 +10,48 @@ import lila.common.String.html.richText
object teacherDashboard { object teacherDashboard {
private def dashboard( private[clas] def layout(
c: Clas, c: Clas,
students: List[Student.WithUser], students: List[Student.WithUser],
active: String active: String
)(modifiers: Modifier*)(implicit ctx: Context) = )(modifiers: Modifier*)(implicit ctx: Context) =
bits.layout(c.name, Left(c withStudents students.map(_.student)))( bits.layout(c.name, Left(c withStudents students.map(_.student)))(
cls := "clas-show dashboard dashboard-teacher", cls := s"clas-show dashboard dashboard-teacher dashboard-teacher-$active",
div(cls := "box__top")( div(cls := "clas-show__top")(
h1(dataIcon := "f", cls := "text")(c.name), div(cls := "box__top")(
div(cls := "box__top__actions")( h1(dataIcon := "f", cls := "text")(c.name),
div(cls := "box__top__actions")(
a(
href := routes.Clas.studentForm(c.id.value),
cls := "button button-green text",
dataIcon := "O"
)("Add student")
)
),
st.nav(cls := "dashboard-nav")(
a(cls := active.active("overview"), href := routes.Clas.show(c.id.value))("Overview"),
a( a(
href := routes.Clas.edit(c.id.value), cls := active.active("progress"),
cls := "button button-empty" href := routes.Clas.progress(c.id.value, PerfType.Blitz.key, 7)
)("Edit"), )(
a( "Progress"
href := routes.Clas.studentForm(c.id.value), ),
cls := "button button-green text", a(cls := active.active("edit"), href := routes.Clas.edit(c.id.value))("Edit"),
dataIcon := "O" a(cls := active.active("archived"), href := routes.Clas.archived(c.id.value))("Archived")
)("Add student")
) )
), ),
div(cls := "box__pad")( standardFlash(),
standardFlash(), c.archived map { archived =>
c.archived map { archived => div(cls := "clas-show__archived archived")(
div(cls := "clas-show__archived archived")( bits.showArchived(archived),
bits.showArchived(archived), postForm(action := routes.Clas.archive(c.id.value, false))(
postForm(action := routes.Clas.archive(c.id.value, false))( form3.submit("Restore", icon = none)(
form3.submit("Restore", icon = none)( cls := "confirm button-empty",
cls := "confirm button-empty", title := "Revive the class"
title := "Revive the class"
)
) )
) )
} )
), },
st.nav(cls := "dashboard-nav tabs-horiz")(
a(cls := active.active("overview"), href := routes.Clas.show(c.id.value))("Overview"),
a(cls := active.active("progress"), href := routes.Clas.progress(c.id.value, PerfType.Blitz.key, 7))(
"Progress"
),
a(cls := active.active("archived"), href := routes.Clas.archived(c.id.value))("Archived")
),
modifiers modifiers
) )
@ -59,7 +59,7 @@ object teacherDashboard {
c: Clas, c: Clas,
students: List[Student.WithUser] students: List[Student.WithUser]
)(implicit ctx: Context) = )(implicit ctx: Context) =
dashboard(c, students, "overview")( layout(c, students, "overview")(
div(cls := "clas-show__overview")( div(cls := "clas-show__overview")(
c.desc.trim.nonEmpty option div(cls := "clas-desc")(richText(c.desc)), c.desc.trim.nonEmpty option div(cls := "clas-desc")(richText(c.desc)),
clas.teachers(c) clas.teachers(c)
@ -74,7 +74,7 @@ object teacherDashboard {
c: Clas, c: Clas,
students: List[Student.WithUser] students: List[Student.WithUser]
)(implicit ctx: Context) = )(implicit ctx: Context) =
dashboard(c, students.filter(_.student.isActive), "archived") { layout(c, students.filter(_.student.isActive), "archived") {
val archived = students.filter(_.student.isArchived) val archived = students.filter(_.student.isArchived)
if (archived.isEmpty) if (archived.isEmpty)
p(cls := "box__pad students__empty")("No archived students.") p(cls := "box__pad students__empty")("No archived students.")
@ -87,7 +87,7 @@ object teacherDashboard {
students: List[Student.WithUser], students: List[Student.WithUser],
progress: ClasProgress progress: ClasProgress
)(implicit ctx: Context) = )(implicit ctx: Context) =
dashboard(c, students, "progress")( layout(c, students, "progress")(
div(cls := "progress")( div(cls := "progress")(
div(cls := "progress-perf")( div(cls := "progress-perf")(
label("Variant"), label("Variant"),

View file

@ -17,6 +17,12 @@
%box-radius-bottom { %box-radius-bottom {
border-radius: 0 0 $box-radius-size $box-radius-size; border-radius: 0 0 $box-radius-size $box-radius-size;
} }
%box-radius-top-left {
border-radius: $box-radius-size 0 0 0;
}
%box-radius-top-right {
border-radius: 0 $box-radius-size 0 0;
}
%box-shadow { %box-shadow {
@include box-shadow; @include box-shadow;
} }

View file

@ -1,3 +1,5 @@
$c-tabs-active: $c-accent !default;
.tabs-horiz { .tabs-horiz {
@extend %flex-center-nowrap, %page-text; @extend %flex-center-nowrap, %page-text;
justify-content: center; justify-content: center;
@ -6,7 +8,7 @@
@include breakpoint($mq-not-xx-small) { @include breakpoint($mq-not-xx-small) {
font-size: .9em; font-size: .9em;
} }
span, > a { span {
@extend %roboto; @extend %roboto;
flex: 1 1 auto; flex: 1 1 auto;
text-align: center; text-align: center;
@ -21,7 +23,7 @@
} }
&::after { &::after {
content: ''; content: '';
background: fade-out($c-accent, .3); background: fade-out($c-tabs-active, .3);
height: 2px; height: 2px;
position: absolute; position: absolute;
width: 96%; width: 96%;
@ -37,7 +39,7 @@
} }
} }
&.active { &.active {
color: $c-accent; color: $c-tabs-active;
} }
} }
.unread { .unread {

View file

@ -1,4 +1,9 @@
$clas-color: rgb(127, 90, 240); $c-clas: rgb(127, 90, 240);
$c-font-clas: mix($c-font-clear, $c-clas, 30%);
$c-bg-clas: $c-clas;
$c-bg-clas-over: white;
$c-bg-clas-over-dim: mix($c-bg-clas-over, $c-bg-clas, 80%);
%archived { %archived {
@extend %box-radius, %flex-between; @extend %box-radius, %flex-between;
@ -13,6 +18,12 @@ $clas-color: rgb(127, 90, 240);
@include breakpoint($mq-medium) { @include breakpoint($mq-medium) {
max-width: 15vw; max-width: 15vw;
} }
&.active {
color: $c-font-clas;
}
&::after {
background: $c-clas;
}
&.student { &.student {
em { em {
@extend %roboto; @extend %roboto;
@ -21,12 +32,6 @@ $clas-color: rgb(127, 90, 240);
@include breakpoint($mq-subnav-side) { @include breakpoint($mq-subnav-side) {
font-weight: bold; font-weight: bold;
padding: .4rem 2vw .4rem .8rem; padding: .4rem 2vw .4rem .8rem;
&.active {
color: $clas-color;
}
&::after {
background: $clas-color;
}
em { em {
font-weight: normal; font-weight: normal;
display: block; display: block;
@ -37,7 +42,12 @@ $clas-color: rgb(127, 90, 240);
.clas-index { .clas-index {
.box__top { .box__top {
background: $c-bg-clas;
&, a {
color: $c-bg-clas-over;
}
flex-flow: row nowrap; flex-flow: row nowrap;
h1 { margin: 0 }
} }
.new::before { .new::before {
font-size: 3em; font-size: 3em;
@ -53,7 +63,7 @@ $clas-color: rgb(127, 90, 240);
position: relative; position: relative;
@include transition(); @include transition();
&::before { &::before {
color: mix($c-link, $c-bg-box, 80%); color: mix($c-clas, $c-bg-box, 80%);
font-size: 5em; font-size: 5em;
margin-right: .4em; margin-right: .4em;
@include transition(); @include transition();
@ -62,9 +72,9 @@ $clas-color: rgb(127, 90, 240);
} }
} }
&:hover { &:hover {
background: mix($c-link, $c-bg-box, 10%); background: mix($c-clas, $c-bg-box, 10%);
&::before { &::before {
color: mix($c-link, $c-bg-box, 100%); color: mix($c-clas, $c-bg-box, 100%);
} }
} }
.overlay { .overlay {
@ -72,7 +82,7 @@ $clas-color: rgb(127, 90, 240);
} }
h3 { h3 {
@include fluid-size('font-size', 23px, 35px); @include fluid-size('font-size', 23px, 35px);
color: $c-link; color: $c-clas;
} }
p { p {
margin: .5em 0 0 0; margin: .5em 0 0 0;
@ -88,6 +98,13 @@ $clas-color: rgb(127, 90, 240);
.clas-show { .clas-show {
&__top {
background: $c-bg-clas;
&, a, .button, .title {
color: $c-bg-clas-over;
}
}
&__overview { &__overview {
@extend %box-padding; @extend %box-padding;
background: $c-bg-zebra; background: $c-bg-zebra;
@ -100,12 +117,11 @@ $clas-color: rgb(127, 90, 240);
.dashboard { .dashboard {
.tabs-horiz a { .flash {
letter-spacing: normal; margin: 0;
font-size: 1.1em; &__content {
color: $c-font; padding: 2em var(--box-padding);
&.active { border-radius: 0;
color: $c-accent;
} }
} }
@ -141,6 +157,39 @@ $clas-color: rgb(127, 90, 240);
} }
} }
&-nav {
@extend %flex-center-nowrap;
justify-content: center;
align-items: flex-end;
@include breakpoint($mq-not-xx-small) {
font-size: .9em;
}
a {
@extend %box-radius-top;
flex: 1 1 auto;
text-align: center;
padding: 1.2em 0 .8em 0;
&:first-child {
@extend %box-radius-top-right;
}
&:last-child {
@extend %box-radius-top-left;
}
&:hover {
background: mix($c-bg-zebra, $c-clas, 20%);
}
&.active {
background: $c-bg-zebra;
color: $c-font-clas;
font-weight: bold;
.dashboard-teacher-edit &,
.dashboard-teacher-archived & {
background: $c-bg-box;
}
}
}
}
&-student { &-student {
.teachers { .teachers {
margin-bottom: 2em; margin-bottom: 2em;
@ -170,6 +219,9 @@ $clas-color: rgb(127, 90, 240);
} }
&-choices { &-choices {
@extend %flex-center, %box-neat-force; @extend %flex-center, %box-neat-force;
@if $theme-dark {
box-shadow: none;
}
flex: 1 1 auto; flex: 1 1 auto;
background: $c-bg-box; background: $c-bg-box;
} }
@ -178,13 +230,13 @@ $clas-color: rgb(127, 90, 240);
text-align: center; text-align: center;
font-weight: bold; font-weight: bold;
padding: .7em; padding: .7em;
color: $c-good; color: $c-clas;
&:hover { &:hover {
background: mix($c-bg-box, $c-good, 70%); background: mix($c-bg-box, $c-clas, 70%);
color: $c-font-clear; color: $c-font-clear;
} }
&.active { &.active {
background: $c-good; background: $c-clas;
color: $c-good-over; color: $c-good-over;
} }
border-right: 1px solid $c-border; border-right: 1px solid $c-border;
@ -213,22 +265,18 @@ $clas-color: rgb(127, 90, 240);
.student-show { .student-show {
$top-background: $clas-color;
$top-font: white;
$top-font-dim: mix($top-font, $top-background, 80%);
padding-bottom: 3em; padding-bottom: 3em;
&__top { &__top {
padding: calc(5vh - 1rem) var(--box-padding); padding: calc(5vh - 1rem) var(--box-padding);
background: $top-background; background: $c-bg-clas;
margin-bottom: 3em; margin-bottom: 3em;
&, a, .button, .title { &, a, .button, .title {
color: $top-font; color: $c-bg-clas-over;
} }
a:not(.button) { a:not(.button) {
font-weight: bold; font-weight: bold;
border-bottom: 1px dotted $top-font-dim; border-bottom: 1px dotted $c-bg-clas-over-dim;
} }
h1 { h1 {
@extend %flex-center; @extend %flex-center;
@ -236,14 +284,14 @@ $clas-color: rgb(127, 90, 240);
&::before { &::before {
font-size: 2.5em; font-size: 2.5em;
margin-right: .4em; margin-right: .4em;
color: $top-font-dim; color: $c-bg-clas-over-dim;
} }
strong { strong {
display: block; display: block;
} }
em { em {
@extend %roboto; @extend %roboto;
color: $top-font-dim; color: $c-bg-clas-over-dim;
} }
} }
&__meta { &__meta {

View file

@ -2,6 +2,5 @@
@import '../../../common/css/form/form3'; @import '../../../common/css/form/form3';
@import '../../../common/css/component/slist'; @import '../../../common/css/component/slist';
@import '../../../common/css/component/tablesort'; @import '../../../common/css/component/tablesort';
@import '../../../common/css/component/tabs-horiz';
@import '../user/activity'; @import '../user/activity';
@import '../clas'; @import '../clas';