diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ee063b5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +./assets/kurious binary diff --git a/assets/kurious/about.txt b/assets/kurious/about.txt new file mode 100644 index 0000000..8924230 --- /dev/null +++ b/assets/kurious/about.txt @@ -0,0 +1,6 @@ +This favicon was generated using the following font: + +- Font Title: Lemon +- Font Author: Copyright 2011 The Lemon Project Authors (https://github.com/etunni/lemon) with Reserved Font Name "Lemon" +- Font Source: http://fonts.gstatic.com/s/lemon/v17/HI_EiYEVKqRMq0jBSZXAQ4-d.ttf +- Font License: SIL Open Font License, 1.1 (http://scripts.sil.org/OFL)) diff --git a/assets/kurious/android-chrome-192x192.png b/assets/kurious/android-chrome-192x192.png new file mode 100644 index 0000000..8d3531b Binary files /dev/null and b/assets/kurious/android-chrome-192x192.png differ diff --git a/assets/kurious/android-chrome-512x512.png b/assets/kurious/android-chrome-512x512.png new file mode 100644 index 0000000..0d09007 Binary files /dev/null and b/assets/kurious/android-chrome-512x512.png differ diff --git a/assets/kurious/apple-touch-icon.png b/assets/kurious/apple-touch-icon.png new file mode 100644 index 0000000..756b7dc Binary files /dev/null and b/assets/kurious/apple-touch-icon.png differ diff --git a/assets/kurious/embed.go b/assets/kurious/embed.go index 9f79499..3ab80c2 100644 --- a/assets/kurious/embed.go +++ b/assets/kurious/embed.go @@ -5,7 +5,7 @@ import ( "net/http" ) -//go:embed robots.txt static/* +//go:embed * var root embed.FS func AsHTTPFileHandler() http.Handler { diff --git a/assets/kurious/favicon-16x16.png b/assets/kurious/favicon-16x16.png new file mode 100644 index 0000000..ab7b9ed Binary files /dev/null and b/assets/kurious/favicon-16x16.png differ diff --git a/assets/kurious/favicon-32x32.png b/assets/kurious/favicon-32x32.png new file mode 100644 index 0000000..9153feb Binary files /dev/null and b/assets/kurious/favicon-32x32.png differ diff --git a/assets/kurious/favicon.ico b/assets/kurious/favicon.ico new file mode 100644 index 0000000..9684f32 Binary files /dev/null and b/assets/kurious/favicon.ico differ diff --git a/assets/kurious/site.webmanifest b/assets/kurious/site.webmanifest new file mode 100644 index 0000000..45dc8a2 --- /dev/null +++ b/assets/kurious/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/cmd/kuriweb/http.go b/cmd/kuriweb/http.go index 8e8b2eb..67f9161 100644 --- a/cmd/kuriweb/http.go +++ b/cmd/kuriweb/http.go @@ -14,11 +14,6 @@ import ( "github.com/gorilla/mux" ) -const ( - pathParamLearningType = "learning_type" - pathParamThematicType = "thematic_type" -) - func makePathTemplate(params ...string) string { var sb strings.Builder for _, param := range params { @@ -41,9 +36,9 @@ func setupHTTP(cfg config.HTTP, srv xhttp.Server, log *slog.Logger) *http.Server router.HandleFunc("/updatedesc", coursesAPI.UdpateDescription).Methods(http.MethodPost) coursesRouter := router.PathPrefix("/courses").Subrouter() coursesRouter.HandleFunc("/", coursesAPI.List).Methods(http.MethodGet) - coursesListLearningOnlyPath := makePathTemplate(pathParamLearningType) + coursesListLearningOnlyPath := makePathTemplate(xhttp.LearningTypePathParam) coursesRouter.HandleFunc(coursesListLearningOnlyPath, coursesAPI.List).Methods(http.MethodGet) - coursesListFullPath := makePathTemplate(pathParamLearningType, pathParamThematicType) + coursesListFullPath := makePathTemplate(xhttp.LearningTypePathParam, xhttp.ThematicTypePathParam) coursesRouter.HandleFunc(coursesListFullPath, coursesAPI.List).Methods(http.MethodGet) courseRouter := router.PathPrefix("/course").PathPrefix("/{course_id}").Subrouter() @@ -56,9 +51,29 @@ func setupHTTP(cfg config.HTTP, srv xhttp.Server, log *slog.Logger) *http.Server fs := http.FileServer(http.Dir("./assets/kurious/static/")) router.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs)).Methods(http.MethodGet) - router.HandleFunc("/robots.txt", func(w http.ResponseWriter, r *http.Request) { - http.ServeFile(w, r, "./assets/kurious/robots.txt") - }).Methods(http.MethodGet) + registerFile := func(filepath string) { + if !strings.HasPrefix(filepath, "/") { + filepath = "/" + filepath + } + + relativePath := "./assets/kurious" + filepath + router.HandleFunc(filepath, func(w http.ResponseWriter, r *http.Request) { + http.ServeFile(w, r, relativePath) + }).Methods(http.MethodGet) + } + + for _, file := range []string{ + "robots.txt", + "android-chrome-192x192.png", + "android-chrome-512x512.png", + "apple-touch-icon.png", + "favicon-16x16.png", + "favicon-32x32.png", + "favicon.ico", + "site.webmanifest", + } { + registerFile(file) + } } else { fs := kurious.AsHTTPFileHandler() router.PathPrefix("/").Handler(fs).Methods(http.MethodGet) diff --git a/internal/kurious/adapters/ydb_course_repository.go b/internal/kurious/adapters/ydb_course_repository.go index 51dae0a..3bd695c 100644 --- a/internal/kurious/adapters/ydb_course_repository.go +++ b/internal/kurious/adapters/ydb_course_repository.go @@ -322,7 +322,7 @@ func (r *ydbCourseRepository) ListCourseThematics( learningTypeValue := types.TextValue(params.LearningTypeID) d := queryTemplateDeclaration{ - Name: "course_thematic", + Name: "learning_type", Type: learningTypeValue.Type().String(), } qtParams.Declares = append(qtParams.Declares, d) diff --git a/internal/kurious/ports/http/course.go b/internal/kurious/ports/http/course.go index 148434f..c189d97 100644 --- a/internal/kurious/ports/http/course.go +++ b/internal/kurious/ports/http/course.go @@ -44,6 +44,11 @@ func parsePaginationFromQuery(r *http.Request) (out pagination, err error) { return out, nil } +const ( + LearningTypePathParam = "learning_type" + ThematicTypePathParam = "thematic_type" +) + func parseListCoursesParams(r *http.Request) (out listCoursesParams, err error) { out.pagination, err = parsePaginationFromQuery(r) if err != nil { @@ -51,8 +56,8 @@ func parseListCoursesParams(r *http.Request) (out listCoursesParams, err error) } vars := mux.Vars(r) - out.learningType = vars["learning_type"] - out.courseThematic = vars["thematic_type"] + out.learningType = vars[LearningTypePathParam] + out.courseThematic = vars[ThematicTypePathParam] return out, nil } @@ -82,9 +87,23 @@ type subcategoryInfo struct { Courses []domain.Course } +type IDNamePair struct { + ID string + Name string + IsActive bool +} + type listCoursesTemplateParams struct { - Categories []categoryInfo - NextPageToken string + Categories []categoryInfo + NextPageToken string + AvailableLearningTypes []IDNamePair + AvailableCourseThematics []IDNamePair + + ActiveLearningType string + LearningTypeName string + + ActiveCourseThematic string + CourseThematicName string } func mapDomainCourseToTemplate(in ...domain.Course) listCoursesTemplateParams { @@ -149,6 +168,45 @@ func (c courseServer) List(w http.ResponseWriter, r *http.Request) { templateCourses := mapDomainCourseToTemplate(courses...) templateCourses.NextPageToken = result.NextPageToken + learningTypeList, err := c.app.Queries.ListLearningTypes.Handle(ctx, query.ListLearningTypes{}) + if handleError(ctx, err, w, c.log, "unable to list learning types") { + return + } + + templateCourses.AvailableLearningTypes = xslices.Map(learningTypeList.LearningTypes, func(in query.LearningType) IDNamePair { + if in.ID == params.learningType { + templateCourses.LearningTypeName = in.Name + } + return IDNamePair{ + ID: in.ID, + Name: in.Name, + IsActive: in.ID == params.learningType, + } + }) + + templateCourses.ActiveLearningType = params.learningType + templateCourses.ActiveCourseThematic = params.courseThematic + + if params.learningType != "" { + courseThematicsResult, err := c.app.Queries.ListCourseThematics.Handle(ctx, query.ListCourseThematics{ + LearningTypeID: params.learningType, + }) + if handleError(ctx, err, w, c.log, "unable to list course thematics") { + return + } + + templateCourses.AvailableCourseThematics = xslices.Map(courseThematicsResult.CourseThematics, func(in query.CourseThematic) IDNamePair { + if in.ID == params.courseThematic { + templateCourses.CourseThematicName = in.Name + } + return IDNamePair{ + ID: in.ID, + Name: in.Name, + IsActive: in.ID == params.courseThematic, + } + }) + } + err = getCoreTemplate(ctx, c.log).ExecuteTemplate(w, "courses", templateCourses) if handleError(ctx, err, w, c.log, "unable to execute template") { return diff --git a/internal/kurious/ports/http/templates/common.tmpl b/internal/kurious/ports/http/templates/common.tmpl index 1d2c25e..57ca335 100644 --- a/internal/kurious/ports/http/templates/common.tmpl +++ b/internal/kurious/ports/http/templates/common.tmpl @@ -8,6 +8,11 @@ + + + + + {{ end }} diff --git a/internal/kurious/ports/http/templates/list.tmpl b/internal/kurious/ports/http/templates/list.tmpl index 125fd09..6fb9de9 100644 --- a/internal/kurious/ports/http/templates/list.tmpl +++ b/internal/kurious/ports/http/templates/list.tmpl @@ -34,6 +34,45 @@