diff --git a/.lintstagedrc.mjs b/.lintstagedrc.mjs index c25569f..04fed52 100644 --- a/.lintstagedrc.mjs +++ b/.lintstagedrc.mjs @@ -1,6 +1,10 @@ +// Quote each path: lint-staged splits a command string on spaces, and +// a checkout can live under a folder with spaces in its name. +const quoted = (files) => files.map((f) => JSON.stringify(f)).join(" ") + export default { "packages/webapp/**/*.{ts,tsx}": (files) => [ - `pnpm --filter webapp exec prettier --write --ignore-unknown ${files.join(" ")}`, - `pnpm --filter webapp exec eslint --fix ${files.join(" ")}`, + `pnpm --filter webapp exec prettier --write --ignore-unknown ${quoted(files)}`, + `pnpm --filter webapp exec eslint --fix ${quoted(files)}`, ], } diff --git a/docs/handbook-pages.md b/docs/handbook-pages.md index c29c771..ada3581 100644 --- a/docs/handbook-pages.md +++ b/docs/handbook-pages.md @@ -71,6 +71,7 @@ The `sort` parameter picks the order of /search results: | Default | With a query, relevance first (the tiers above). Without one, a score: `3·ln(1+reviews) + 2·ln(1+core) + ln(1+views) + ln(1+planners)`. | | A to Z | Title. | | Highest rated | Mean overall stars, counted as if every entity had 2 extra 3-star reviews. Entities with no reviews go last. | +| Lowest rated | The same score, lowest first. Entities with no reviews still go last. | | Easiest first | Mean review difficulty, with the same 2 extra 3s. Entities with no difficulty ratings go last. | | Most reviews | Published review count. | | Most planned | Distinct students with it on a plan. | diff --git a/packages/webapp/components/handbook/sort-select.tsx b/packages/webapp/components/handbook/sort-select.tsx index 18eaaab..e2fb30d 100644 --- a/packages/webapp/components/handbook/sort-select.tsx +++ b/packages/webapp/components/handbook/sort-select.tsx @@ -16,6 +16,7 @@ export const SORT_LABEL: Record = { default: "Default", alpha: "A to Z", rating: "Highest rated", + lowest: "Lowest rated", easiest: "Easiest first", reviews: "Most reviews", planned: "Most planned", diff --git a/packages/webapp/lib/db/handbook.ts b/packages/webapp/lib/db/handbook.ts index c2ca9e4..64bf3be 100644 --- a/packages/webapp/lib/db/handbook.ts +++ b/packages/webapp/lib/db/handbook.ts @@ -858,6 +858,8 @@ function searchOrder(sort: SearchSort, hasQuery: boolean): SQL { return sql`title, kind_order, code` case "rating": return sql`rating_score DESC NULLS LAST, reviews DESC, ${tail}` + case "lowest": + return sql`rating_score ASC NULLS LAST, reviews DESC, ${tail}` case "easiest": return sql`ease_score ASC NULLS LAST, difficulty_n DESC, ${tail}` case "reviews": diff --git a/packages/webapp/lib/handbook/search-url.test.ts b/packages/webapp/lib/handbook/search-url.test.ts index 796ef3c..2f72e17 100644 --- a/packages/webapp/lib/handbook/search-url.test.ts +++ b/packages/webapp/lib/handbook/search-url.test.ts @@ -21,6 +21,7 @@ test("unknown tab, study and period values are ignored", () => { assert.equal(parse({ type: "units", level: "12" }).level, null) assert.equal(parse({ sort: "views" }).sort, "default") assert.equal(parse({ sort: "planned" }).sort, "planned") + assert.equal(parse({ sort: "lowest" }).sort, "lowest") }) test("page numbers below 2 or not whole give page 1, and cap at 500", () => { diff --git a/packages/webapp/lib/handbook/search-url.ts b/packages/webapp/lib/handbook/search-url.ts index 3192f02..e568677 100644 --- a/packages/webapp/lib/handbook/search-url.ts +++ b/packages/webapp/lib/handbook/search-url.ts @@ -14,13 +14,14 @@ export type StudyLevel = "Undergraduate" | "Honours" | "Postgraduate" | "Research" export type SearchSort = - "default" | "alpha" | "rating" | "easiest" | "reviews" | "planned" + "default" | "alpha" | "rating" | "lowest" | "easiest" | "reviews" | "planned" export const SEARCH_TABS: SearchTab[] = ["all", "courses", "aos", "units"] export const SEARCH_SORTS: SearchSort[] = [ "default", "alpha", "rating", + "lowest", "easiest", "reviews", "planned",