diff --git a/scss/style.scss b/scss/style.scss new file mode 100644 index 0000000..809015c --- /dev/null +++ b/scss/style.scss @@ -0,0 +1,85 @@ +$off-grey-color: #ededed; +$off-color: #eef2f9; + +body { + font-family: sans-serif; +} + +.main { + margin: auto; + max-width: 1600px; +} + +.split-pane { + display: flex; + flex-direction: vertical; +} + +.split-elem { + flex-grow: 1; +} + +.table-wrapper { + border: 1px solid $off-grey-color; + padding-top: 0.5rem; + padding-bottom: 1rem; + border-radius: 0.25rem; + display: inline-block; + margin-top: 1rem; + box-shadow: 0px 0px 5px rgba(grey, 0.1); +} + +.class-table { + border-collapse: collapse; + + th, td { + padding: 0.5rem; + padding-left: 1rem; + padding-right: 1rem; + } + + th { + text-align: left; + } + + tr { + transition: background-color 0.2s; + + &:nth-child(2n+3) { + background-color: $off-color; + } + + &.hoverable:hover { + background-color: lighten(yellow, 30%); + } + + &.selected { + border: 1px dashed black; + } + } +} + +.feather { + vertical-align: middle; + height: 1rem; +} + +.button { + border: 1px solid black; + padding: 0.25rem; + border-radius: 0.25rem; + + vertical-align: middle; + + &.color-green { + background-color: lighten(#5bc275, 30%); + border-color: #5bc275; + color: darken(#5bc275, 30%); + } + + &.color-red { + background-color: lighten(#c25b75, 30%); + border-color: #c25b75; + color: darken(#c25b75, 30%); + } +} diff --git a/src/ClassSchedule/Model.elm b/src/ClassSchedule/Model.elm index 7cbcf8c..3af019a 100644 --- a/src/ClassSchedule/Model.elm +++ b/src/ClassSchedule/Model.elm @@ -49,29 +49,20 @@ type alias Course = , times : List (DayOfWeek, Time, Time) } -type CourseStatus = None | Selected | Added - -isSelected : CourseStatus -> Bool -isSelected cs = - case cs of - Selected -> True - _ -> False - -isAdded : CourseStatus -> Bool -isAdded cs = - case cs of - Added -> True - _ -> False +type CourseStatus = NotAdded | Added type alias Flags = () type Msg = SearchInput String | SelectTerm String - | MarkCourse Int CourseStatus + | SelectCourse Int + | AddCourse Int + | RemoveCourse Int type alias Model = { terms : Dict String (List (CourseStatus, Course)) , term : String + , selected : Maybe Int , searchInput : String } diff --git a/src/ClassSchedule/View.elm b/src/ClassSchedule/View.elm index 3cef2d4..3e7b1e7 100644 --- a/src/ClassSchedule/View.elm +++ b/src/ClassSchedule/View.elm @@ -1,9 +1,15 @@ module ClassSchedule.View exposing (..) import ClassSchedule.Model exposing (..) -import Html exposing (Html, div, text, table, td, th, tr, span, input, h1) +import Html exposing (Html, Attribute, div, text, table, td, th, tr, span, input, h1) import Html.Attributes exposing (class, classList, type_) +import Html.Events exposing (onClick) import Tuple exposing (..) import Dict exposing (..) +import FeatherIcons + +iconButton : String -> FeatherIcons.Icon -> String -> List (Attribute Msg) -> Html Msg +iconButton c i s attrs = span ([ class "button", class ("color-" ++ c) ] ++ attrs) + [ FeatherIcons.toHtml [] i, text s ] viewDept : Department -> String viewDept d = @@ -50,7 +56,7 @@ unique l = extractTimes : Course -> List (Time, Time) extractTimes c = - let extractTime (d, t1, t2) = (t2, t2) + let extractTime (d, t1, t2) = (t1, t2) in unique <| List.map extractTime (c.times) extractClasses : Course -> (Time, Time) -> List DayOfWeek @@ -66,25 +72,41 @@ extractTimeCodes c = in dayCodes ++ " " ++ viewTimeRange tr in List.map fromTime <| extractTimes c -viewClass : Course -> Html Msg -viewClass c = tr [] - [ td [] [ text <| viewCrn (c.crn) ] - , td [] [ text <| c.name ] - , td [] [ text <| String.join ", " <| c.instructors ] - , td [] [ text <| String.join ", " <| extractTimeCodes c ] - ] +viewClass : Maybe Int -> Int -> (CourseStatus, Course) -> Html Msg +viewClass sel i (cs, c) = + let + isSelected = sel == Just i + addedIndicator = + case cs of + Added -> [ FeatherIcons.check |> FeatherIcons.toHtml [] ] + NotAdded -> [] + in + tr [ class "hoverable", onClick (SelectCourse i), classList [("selected", isSelected)] ] + [ td [] addedIndicator + , td [] [ text <| viewCrn (c.crn) ] + , td [] [ text <| c.name ] + , td [] [ text <| String.join ", " <| c.instructors ] + , td [] [ text <| String.join ", " <| extractTimeCodes c ] + , td [] <| + case (isSelected, cs) of + (True, NotAdded) -> [ iconButton "green" (FeatherIcons.check) "Add" [ onClick (AddCourse i)] ] + (True, Added) -> [ iconButton "red" (FeatherIcons.x) "Remove" [onClick (RemoveCourse i)] ] + _ -> [] + ] -viewClassTable : List (CourseStatus, Course) -> Html Msg -viewClassTable = +viewClassTable : Maybe Int -> List (CourseStatus, Course) -> Html Msg +viewClassTable sel = let header = tr [] - [ th [] [ text "Crn." ] + [ th [] [ ] + , th [] [ text "Crn." ] , th [] [ text "Course Name" ] , th [] [ text "Instructors" ] , th [] [ text "Times" ] + , th [] [] ] in - table [] << (::) header << List.map (viewClass << second) + div [ class "table-wrapper" ] << List.singleton << table [ class "class-table" ] << (::) header << List.indexedMap (viewClass sel) viewToolbar : Model -> Html Msg viewToolbar m = div [] @@ -95,12 +117,14 @@ viewClassList : Model -> Html Msg viewClassList m = div [] [ viewToolbar m , case get (m.term) (m.terms) of - Just cs -> viewClassTable cs + Just cs -> viewClassTable (m.selected) cs Nothing -> text "Please select a term!" ] viewClassSchedule : Model -> Html Msg -viewClassSchedule m = text "Class schedule goes here!" +viewClassSchedule m = div [ class "table-wrapper" ] + [ text "Nothing here yet!" + ] viewModel : Model -> Html Msg viewModel m = div [ class "main" ] diff --git a/src/Main.elm b/src/Main.elm index aa1a16f..5ecfba6 100644 --- a/src/Main.elm +++ b/src/Main.elm @@ -34,14 +34,19 @@ classes = , instructors = ["Eric Walkingshaw"] , times = onDays [Monday, Wednesday] <| oneHour <| nAm 10 } + , { crn = (ComputerScience, 582) + , name = "Programming Languages II" + , instructors = ["Martin Erwig"] + , times = onDays [Monday, Wednesday] <| oneHour <| nPm 4 + } ] terms : Dict String (List (CourseStatus, Course)) terms = Dict.singleton "Spring 2021" - <| List.map (pair None) classes + <| List.map (pair NotAdded) classes init : Flags -> (Model, Cmd Msg) -init () = ({ terms = terms, term = "Spring 2021", searchInput = "" }, Cmd.none) +init () = ({ terms = terms, term = "Spring 2021", searchInput = "", selected = Nothing }, Cmd.none) view : Model -> Document Msg view m = @@ -49,8 +54,21 @@ view m = , body = [ viewModel m ] } +modifyCurrent : (List (CourseStatus, Course) -> List (CourseStatus, Course)) -> Model -> Model +modifyCurrent f m = { m | terms = Dict.update (m.term) (Maybe.map f) (m.terms) } + +changeCourse : Int -> CourseStatus -> List (CourseStatus, Course) -> List (CourseStatus, Course) +changeCourse i ncs = + let updateCourses j (cs, c) = if i == j then (ncs, c) else (cs, c) + in List.indexedMap updateCourses + update : Msg -> Model -> (Model, Cmd Msg) -update msg m = (m, Cmd.none) +update msg m = + case msg of + SelectCourse i -> ({ m | selected = Just i}, Cmd.none) + AddCourse i -> (modifyCurrent (changeCourse i Added) m, Cmd.none) + RemoveCourse i -> (modifyCurrent (changeCourse i NotAdded) m, Cmd.none) + _ -> (m, Cmd.none) subscriptions : Model -> Sub Msg subscriptions m = Sub.none