module Client.Features.Quizzes.Browse.View open Feliz open Domain.Contracts open Client.Shared open Client.Features.Quizzes.Browse.Types let private attemptRow (a: MyAttemptSummary) = Html.li [ prop.key (string a.AttemptId) prop.className "attempt-row" prop.children [ Html.span [ prop.className "tag-mono" prop.text (a.StartedAt.ToLocalTime().ToString("dd.MM.yyyy HH:mm")) ] Html.span [ prop.className "score-value" prop.text (sprintf "%s / %s" (Format.points a.Score) (Format.points a.MaxScore)) ] match a.Passed with | Some true -> Html.span [ prop.className "grade-stamp passed"; prop.text "Пройден" ] | Some false -> Html.span [ prop.className "grade-stamp failed"; prop.text "Не пройден" ] | None -> Html.none ] ] let private resultsPanel (model: Model) = Html.div [ prop.className "results-panel" prop.children [ match model.ResultsError with | Some err -> Html.p [ prop.className "error"; prop.text err ] | None -> Html.none if model.ResultsLoading then Html.p "Загрузка…" elif model.Results.IsEmpty then Html.p "Нет завершённых попыток" else Html.ul [ prop.children [ for a in model.Results -> attemptRow a ] ] ] ] let view (model: Model) (dispatch: Msg -> unit) = Html.div [ prop.className "quiz-list-page" prop.children [ Html.h2 "Доступные тесты" match model.Error with | Some err -> Html.p [ prop.className "error"; prop.text err ] | None -> Html.none if model.Loading then Html.p "Загрузка…" elif model.Quizzes.IsEmpty then Html.p "Тесты недоступны" else Html.ul [ prop.children [ for quiz in model.Quizzes -> let remainingAttempts = quiz.MaxAttempts |> Option.map (fun m -> System.Math.Max(0, m - quiz.AttemptsCount)) Html.li [ prop.key (string quiz.Id) prop.className "quiz-card" prop.children [ Html.h3 quiz.Title Html.p quiz.Description Html.div [ prop.className "meta-row" prop.children [ Html.div [ prop.className "meta-item" prop.children [ Html.span [ prop.className "meta-label"; prop.text "Баллов" ] Html.span [ prop.className "meta-value" prop.text (Format.points quiz.TotalPoints) ] ] ] match quiz.TimeLimitMinutes with | Some minutes -> Html.div [ prop.className "meta-item" prop.children [ Html.span [ prop.className "meta-label"; prop.text "Лимит времени" ] Html.span [ prop.className "meta-value" prop.text (sprintf "%d мин" minutes) ] ] ] | None -> Html.none match quiz.MaxAttempts, remainingAttempts with | Some maxAttempts, Some remaining -> Html.div [ prop.className "meta-item" prop.children [ Html.span [ prop.className "meta-label"; prop.text "Осталось попыток" ] Html.span [ prop.className "meta-value" prop.text (sprintf "%d из %d" remaining maxAttempts) ] ] ] | _ -> Html.none ] ] if quiz.AlreadyPassed then Html.span [ prop.className "status-badge active"; prop.text "Тест пройден успешно" ] elif remainingAttempts = Some 0 then Html.p [ prop.className "hint"; prop.text "Попытки исчерпаны" ] else Html.button [ prop.disabled (model.StartingQuizId = Some quiz.Id) prop.onClick (fun _ -> dispatch (Start quiz.Id)) prop.text ( if model.StartingQuizId = Some quiz.Id then "Запуск…" else "Начать тест" ) ] if quiz.AttemptsCount > 0 then Html.button [ prop.type'.button prop.onClick (fun _ -> dispatch (ToggleResults quiz.Id)) prop.text ( if model.ExpandedResultsFor = Some quiz.Id then "Скрыть результаты" else sprintf "Посмотреть результаты (%d)" quiz.AttemptsCount ) ] if model.ExpandedResultsFor = Some quiz.Id then resultsPanel model ] ] ] ] ] ]