From 9d7ae9e6dc044e44bcd9dc5ee3d9ef7b65f58a33 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 00:36:24 +0100 Subject: [PATCH 01/36] feat: add ability to sort questions by update / edit date --- apps/api/modules/questions/questions.routes.ts | 2 ++ apps/api/modules/questions/questions.schemas.ts | 2 ++ apps/app/src/lib/order.ts | 4 +++- packages/openapi-types/types.ts | 12 ++++++++++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/apps/api/modules/questions/questions.routes.ts b/apps/api/modules/questions/questions.routes.ts index 1afc0180..fc6bc035 100644 --- a/apps/api/modules/questions/questions.routes.ts +++ b/apps/api/modules/questions/questions.routes.ts @@ -49,6 +49,7 @@ const questionsPlugin: FastifyPluginAsync = async (fastify) => { levelId: true, statusId: true, acceptedAt: true, + updatedAt: true, _count: { select: { QuestionVote: true, @@ -66,6 +67,7 @@ const questionsPlugin: FastifyPluginAsync = async (fastify) => { _levelId: q.levelId, _statusId: q.statusId, acceptedAt: q.acceptedAt?.toISOString(), + updatedAt: q.updatedAt?.toISOString(), votesCount: q._count.QuestionVote, }; }); diff --git a/apps/api/modules/questions/questions.schemas.ts b/apps/api/modules/questions/questions.schemas.ts index 369e7454..89ffe882 100644 --- a/apps/api/modules/questions/questions.schemas.ts +++ b/apps/api/modules/questions/questions.schemas.ts @@ -26,6 +26,7 @@ const generateGetQuestionsQuerySchema = < Type.Literal("acceptedAt"), Type.Literal("level"), Type.Literal("votesCount"), + Type.Literal("updatedAt"), ]), order: Type.Union([Type.Literal("asc"), Type.Literal("desc")]), userId: Type.Integer(), @@ -51,6 +52,7 @@ const generateQuestionShape = < _levelId: Type.Union(args.levels.map((val) => Type.Literal(val))), _statusId: Type.Union(args.statuses.map((val) => Type.Literal(val))), acceptedAt: Type.Optional(Type.String({ format: "date-time" })), + updatedAt: Type.Optional(Type.String({ format: "date-time" })), } as const; }; diff --git a/apps/app/src/lib/order.ts b/apps/app/src/lib/order.ts index b622ad27..3fa0a005 100644 --- a/apps/app/src/lib/order.ts +++ b/apps/app/src/lib/order.ts @@ -1,6 +1,6 @@ import { QueryParam } from "../types"; -const ordersBy = ["acceptedAt", "level", "votesCount"] as const; +const ordersBy = ["acceptedAt", "level", "votesCount", "updatedAt"] as const; const orders = ["asc", "desc"] as const; export const DEFAULT_SORT_BY_QUERY = "acceptedAt*desc"; @@ -11,6 +11,8 @@ export const sortByLabels: Record<`${OrderBy}*${Order}`, string> = { "level*desc": "od najtrudniejszych", "votesCount*asc": "od najmniej popularnych", "votesCount*desc": "od najpopularniejszych", + "updatedAt*desc": "daty edycji (najnowsze)", + "updatedAt*asc": "daty edycji (najstarsze)", }; type OrderBy = typeof ordersBy[number]; diff --git a/packages/openapi-types/types.ts b/packages/openapi-types/types.ts index 3bd75e52..9033a4eb 100644 --- a/packages/openapi-types/types.ts +++ b/packages/openapi-types/types.ts @@ -79,7 +79,7 @@ export interface paths { level?: string; limit?: number; offset?: number; - orderBy?: "acceptedAt" | "level" | "votesCount"; + orderBy?: "acceptedAt" | "level" | "votesCount" | "updatedAt"; order?: "asc" | "desc"; userId?: number; }; @@ -97,6 +97,8 @@ export interface paths { _statusId: "pending" | "accepted"; /** Format: date-time */ acceptedAt?: string; + /** Format: date-time */ + updatedAt?: string; votesCount: number; }[]; meta: { @@ -130,6 +132,8 @@ export interface paths { _statusId: "pending" | "accepted"; /** Format: date-time */ acceptedAt?: string; + /** Format: date-time */ + updatedAt?: string; votesCount: number; }; }; @@ -147,7 +151,7 @@ export interface paths { level?: string; limit?: number; offset?: number; - orderBy?: "acceptedAt" | "level" | "votesCount"; + orderBy?: "acceptedAt" | "level" | "votesCount" | "updatedAt"; order?: "asc" | "desc"; userId?: number; }; @@ -246,6 +250,8 @@ export interface paths { _statusId: "pending" | "accepted"; /** Format: date-time */ acceptedAt?: string; + /** Format: date-time */ + updatedAt?: string; votesCount: number; }; }; @@ -293,6 +299,8 @@ export interface paths { _statusId: "pending" | "accepted"; /** Format: date-time */ acceptedAt?: string; + /** Format: date-time */ + updatedAt?: string; votesCount: number; }; }; From 51ee25c62c5ac0589d2f2db6cf4d9334fcb3eaf1 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 02:28:57 +0100 Subject: [PATCH 02/36] feat: add ability to sort questions in user and admin dashboard --- .../admin/[status]/[page]/page.tsx | 13 ++++++++++-- .../user/questions/[page]/page.tsx | 12 +++++++++-- .../src/components/AdminPanel/AdminPanel.tsx | 16 ++++++++++++-- .../FilterableQuestionsList.tsx | 3 +++ .../FilterableQuestionsListHeader.tsx | 21 +++++++++++++++++++ .../UserQuestions/UserQuestions.tsx | 9 ++++++-- apps/app/src/hooks/useGetAllQuestions.ts | 9 +++++++- apps/app/src/lib/order.ts | 4 ++-- 8 files changed, 76 insertions(+), 11 deletions(-) diff --git a/apps/app/src/app/(main-layout)/admin/[status]/[page]/page.tsx b/apps/app/src/app/(main-layout)/admin/[status]/[page]/page.tsx index fe9584c1..c71aa4d2 100644 --- a/apps/app/src/app/(main-layout)/admin/[status]/[page]/page.tsx +++ b/apps/app/src/app/(main-layout)/admin/[status]/[page]/page.tsx @@ -5,6 +5,7 @@ import { parseQueryLevels } from "../../../../../lib/level"; import { statuses } from "../../../../../lib/question"; import { parseTechnologyQuery } from "../../../../../lib/technologies"; import { Params, SearchParams } from "../../../../../types"; +import { DEFAULT_SORT_BY_QUERY, parseQuerySortBy } from "../../../../../lib/order"; const AdminPanel = dynamic( () => @@ -19,11 +20,12 @@ export default function AdminPage({ searchParams, }: { params: Params<"status" | "page">; - searchParams?: SearchParams<"technology" | "level">; + searchParams?: SearchParams<"technology" | "level" | "sortBy">; }) { const page = Number.parseInt(params.page); const technology = parseTechnologyQuery(searchParams?.technology); const levels = parseQueryLevels(searchParams?.level); + const sortBy = parseQuerySortBy(searchParams?.sortBy || DEFAULT_SORT_BY_QUERY); if (Number.isNaN(page) || !statuses.includes(params.status)) { return redirect("/admin"); @@ -31,7 +33,14 @@ export default function AdminPage({ return ( - + ); } diff --git a/apps/app/src/app/(main-layout)/user/questions/[page]/page.tsx b/apps/app/src/app/(main-layout)/user/questions/[page]/page.tsx index 90e2d674..9fb363b8 100644 --- a/apps/app/src/app/(main-layout)/user/questions/[page]/page.tsx +++ b/apps/app/src/app/(main-layout)/user/questions/[page]/page.tsx @@ -2,6 +2,7 @@ import { redirect } from "next/navigation"; import { PrivateRoute } from "../../../../../components/PrivateRoute"; import { UserQuestions } from "../../../../../components/UserQuestions/UserQuestions"; import { parseQueryLevels } from "../../../../../lib/level"; +import { DEFAULT_SORT_BY_QUERY, parseQuerySortBy } from "../../../../../lib/order"; import { parseTechnologyQuery } from "../../../../../lib/technologies"; import { Params, SearchParams } from "../../../../../types"; @@ -10,11 +11,12 @@ export default function UserQuestionsPage({ searchParams, }: { params: Params<"page">; - searchParams?: SearchParams<"technology" | "level">; + searchParams?: SearchParams<"technology" | "level" | "sortBy">; }) { const page = Number.parseInt(params.page); const technology = parseTechnologyQuery(searchParams?.technology); const levels = parseQueryLevels(searchParams?.level); + const sortBy = parseQuerySortBy(searchParams?.sortBy || DEFAULT_SORT_BY_QUERY); if (Number.isNaN(page)) { return redirect("/user/questions"); @@ -22,7 +24,13 @@ export default function UserQuestionsPage({ return ( - + ); } diff --git a/apps/app/src/components/AdminPanel/AdminPanel.tsx b/apps/app/src/components/AdminPanel/AdminPanel.tsx index a0d1eaee..b6bec132 100644 --- a/apps/app/src/components/AdminPanel/AdminPanel.tsx +++ b/apps/app/src/components/AdminPanel/AdminPanel.tsx @@ -3,6 +3,7 @@ import { Suspense, useCallback } from "react"; import { useGetAllQuestions } from "../../hooks/useGetAllQuestions"; import { Level } from "../../lib/level"; +import { Order, OrderBy } from "../../lib/order"; import { QuestionStatus } from "../../lib/question"; import { Technology } from "../../lib/technologies"; import { FilterableQuestionsList } from "../FilterableQuestionsList/FilterableQuestionsList"; @@ -13,14 +14,25 @@ type AdminPanelProps = Readonly<{ technology: Technology | null; levels: Level[] | null; status: QuestionStatus; + order?: Order; + orderBy?: OrderBy; }>; -export const AdminPanel = ({ page, technology, levels, status }: AdminPanelProps) => { +export const AdminPanel = ({ + page, + technology, + levels, + status, + order, + orderBy, +}: AdminPanelProps) => { const { isSuccess, data, refetch } = useGetAllQuestions({ page, status, technology, levels, + order, + orderBy, }); const refetchQuestions = useCallback(() => { @@ -32,7 +44,7 @@ export const AdminPanel = ({ page, technology, levels, status }: AdminPanelProps page={page} total={data?.data.meta.total || 0} getHref={(page) => `/admin/${status}/${page}`} - data={{ status, technology, levels }} + data={{ status, technology, levels, order, orderBy }} > {isSuccess && data.data.data.length > 0 ? ( diff --git a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsList.tsx b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsList.tsx index 5c2a7f6c..650e379c 100644 --- a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsList.tsx +++ b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsList.tsx @@ -1,4 +1,5 @@ import { ComponentProps, ReactNode } from "react"; +import { Order, OrderBy } from "../../lib/order"; import { QuestionStatus } from "../../lib/question"; import { Technology } from "../../lib/technologies"; import { Level } from "../QuestionItem/QuestionLevel"; @@ -11,6 +12,8 @@ type FilterableQuestionsListProps = Readonly<{ status?: QuestionStatus; technology?: Technology | null; levels?: Level[] | null; + order?: Order; + orderBy?: OrderBy; }; children: ReactNode; }> & diff --git a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx index 69f2d20b..1cd65a88 100644 --- a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx +++ b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx @@ -2,6 +2,7 @@ import { useRouter } from "next/navigation"; import { ChangeEvent, ReactNode } from "react"; import { useDevFAQRouter } from "../../hooks/useDevFAQRouter"; import { levels } from "../../lib/level"; +import { Order, OrderBy, sortByLabels } from "../../lib/order"; import { QuestionStatus, statuses } from "../../lib/question"; import { technologies, technologiesLabels, Technology } from "../../lib/technologies"; import { Level } from "../QuestionItem/QuestionLevel"; @@ -11,6 +12,8 @@ type FilterableQuestionsListHeaderProps = Readonly<{ status?: QuestionStatus; technology?: Technology | null; levels?: Level[] | null; + order?: Order; + orderBy?: OrderBy; }>; const SelectLabel = ({ children }: { readonly children: ReactNode }) => ( @@ -21,6 +24,8 @@ export const FilterableQuestionsListHeader = ({ status, technology, levels: selectedLevels, + order, + orderBy, }: FilterableQuestionsListHeaderProps) => { const { mergeQueryParams } = useDevFAQRouter(); const router = useRouter(); @@ -66,6 +71,22 @@ export const FilterableQuestionsListHeader = ({ )} + {order && orderBy && ( + + Sortuj według: + + + )} {status !== undefined && ( Status: diff --git a/apps/app/src/components/UserQuestions/UserQuestions.tsx b/apps/app/src/components/UserQuestions/UserQuestions.tsx index b2b665cf..508112f3 100644 --- a/apps/app/src/components/UserQuestions/UserQuestions.tsx +++ b/apps/app/src/components/UserQuestions/UserQuestions.tsx @@ -3,6 +3,7 @@ import { Suspense } from "react"; import { useGetAllQuestions } from "../../hooks/useGetAllQuestions"; import { useUser } from "../../hooks/useUser"; +import { Order, OrderBy } from "../../lib/order"; import { Technology } from "../../lib/technologies"; import { FilterableQuestionsList } from "../FilterableQuestionsList/FilterableQuestionsList"; import { Level } from "../QuestionItem/QuestionLevel"; @@ -12,15 +13,19 @@ type UserQuestionsProps = Readonly<{ page: number; technology: Technology | null; levels: Level[] | null; + order?: Order; + orderBy?: OrderBy; }>; -export const UserQuestions = ({ page, technology, levels }: UserQuestionsProps) => { +export const UserQuestions = ({ page, technology, levels, order, orderBy }: UserQuestionsProps) => { const { userData } = useUser(); const { isSuccess, data } = useGetAllQuestions({ page, technology, levels, userId: userData?._user.id, + order, + orderBy, }); return ( @@ -28,7 +33,7 @@ export const UserQuestions = ({ page, technology, levels }: UserQuestionsProps) page={page} total={data?.data.meta.total || 0} getHref={(page) => `/user/questions/${page}`} - data={{ technology, levels }} + data={{ technology, levels, order, orderBy }} > {isSuccess && data.data.data.length > 0 ? ( diff --git a/apps/app/src/hooks/useGetAllQuestions.ts b/apps/app/src/hooks/useGetAllQuestions.ts index 372cd5dc..f7d2c3f3 100644 --- a/apps/app/src/hooks/useGetAllQuestions.ts +++ b/apps/app/src/hooks/useGetAllQuestions.ts @@ -1,6 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import { PAGE_SIZE } from "../lib/constants"; import { Level } from "../lib/level"; +import { Order, OrderBy, sortByLabels } from "../lib/order"; import { QuestionStatus } from "../lib/question"; import { Technology } from "../lib/technologies"; import { getAllQuestions } from "../services/questions.service"; @@ -11,15 +12,19 @@ export const useGetAllQuestions = ({ levels, status, userId, + order, + orderBy, }: { page: number; technology: Technology | null; levels: Level[] | null; status?: QuestionStatus; userId?: number; + order?: Order; + orderBy?: OrderBy; }) => { const query = useQuery({ - queryKey: ["questions", { page, technology, levels, status, userId }], + queryKey: ["questions", { page, technology, levels, status, userId, order, orderBy }], queryFn: () => getAllQuestions({ limit: PAGE_SIZE, @@ -28,6 +33,8 @@ export const useGetAllQuestions = ({ ...(levels && { level: levels.join(",") }), status, userId, + order: order, + orderBy: orderBy, }), keepPreviousData: true, }); diff --git a/apps/app/src/lib/order.ts b/apps/app/src/lib/order.ts index 3fa0a005..c0e256f0 100644 --- a/apps/app/src/lib/order.ts +++ b/apps/app/src/lib/order.ts @@ -15,8 +15,8 @@ export const sortByLabels: Record<`${OrderBy}*${Order}`, string> = { "updatedAt*asc": "daty edycji (najstarsze)", }; -type OrderBy = typeof ordersBy[number]; -type Order = typeof orders[number]; +export type OrderBy = typeof ordersBy[number]; +export type Order = typeof orders[number]; export const parseQuerySortBy = (query: QueryParam) => { if (typeof query !== "string") { From 57c9b9f974bd7f543a3b198cc29c19f6de748803 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 17:35:22 +0100 Subject: [PATCH 03/36] Update apps/app/src/hooks/useGetAllQuestions.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michał Miszczyszyn --- apps/app/src/hooks/useGetAllQuestions.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/app/src/hooks/useGetAllQuestions.ts b/apps/app/src/hooks/useGetAllQuestions.ts index f7d2c3f3..fdb49607 100644 --- a/apps/app/src/hooks/useGetAllQuestions.ts +++ b/apps/app/src/hooks/useGetAllQuestions.ts @@ -33,8 +33,8 @@ export const useGetAllQuestions = ({ ...(levels && { level: levels.join(",") }), status, userId, - order: order, - orderBy: orderBy, + order, + orderBy, }), keepPreviousData: true, }); From b3b68cad46ded3349795742c53cb9327854301d2 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 18:51:18 +0100 Subject: [PATCH 04/36] add "SelectLabel" component --- apps/app/src/components/SelectLabel.tsx | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 apps/app/src/components/SelectLabel.tsx diff --git a/apps/app/src/components/SelectLabel.tsx b/apps/app/src/components/SelectLabel.tsx new file mode 100644 index 00000000..ff0c95ec --- /dev/null +++ b/apps/app/src/components/SelectLabel.tsx @@ -0,0 +1,5 @@ +import { ReactNode } from "react"; + +export const SelectLabel = ({ children }: { readonly children: ReactNode }) => ( + +); From 764b5c63dfbc1e60ea9dca4c452372bb7c0027e5 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 18:52:07 +0100 Subject: [PATCH 05/36] add "SortBySelect" component --- apps/app/src/components/SortBySelect.tsx | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 apps/app/src/components/SortBySelect.tsx diff --git a/apps/app/src/components/SortBySelect.tsx b/apps/app/src/components/SortBySelect.tsx new file mode 100644 index 00000000..d8d8f952 --- /dev/null +++ b/apps/app/src/components/SortBySelect.tsx @@ -0,0 +1,23 @@ +import { ChangeEvent } from "react"; +import { Order, OrderBy, sortByLabels } from "../lib/order"; +import { Select } from "./Select/Select"; +import { SelectLabel } from "./SelectLabel"; + +type SortBySelectProps = { + order: Order; + orderBy: OrderBy; + onChange: (event: ChangeEvent) => void; +}; + +export const SortBySelect = ({ order, orderBy, onChange }: SortBySelectProps) => ( + + Sortowanie: + + +); From 89c1b67f7e6eb514fcb2b12daa6e227961f55551 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 18:53:12 +0100 Subject: [PATCH 06/36] implement newly created components --- .../FilterableQuestionsListHeader.tsx | 23 ++++--------------- apps/app/src/components/QuestionsHeader.tsx | 18 +++++---------- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx index 1cd65a88..7fd15e77 100644 --- a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx +++ b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx @@ -2,11 +2,13 @@ import { useRouter } from "next/navigation"; import { ChangeEvent, ReactNode } from "react"; import { useDevFAQRouter } from "../../hooks/useDevFAQRouter"; import { levels } from "../../lib/level"; -import { Order, OrderBy, sortByLabels } from "../../lib/order"; +import { Order, OrderBy } from "../../lib/order"; import { QuestionStatus, statuses } from "../../lib/question"; import { technologies, technologiesLabels, Technology } from "../../lib/technologies"; import { Level } from "../QuestionItem/QuestionLevel"; import { Select } from "../Select/Select"; +import { SelectLabel } from "../SelectLabel"; +import { SortBySelect } from "../SortBySelect"; type FilterableQuestionsListHeaderProps = Readonly<{ status?: QuestionStatus; @@ -16,10 +18,6 @@ type FilterableQuestionsListHeaderProps = Readonly<{ orderBy?: OrderBy; }>; -const SelectLabel = ({ children }: { readonly children: ReactNode }) => ( - -); - export const FilterableQuestionsListHeader = ({ status, technology, @@ -72,20 +70,7 @@ export const FilterableQuestionsListHeader = ({ )} {order && orderBy && ( - - Sortuj według: - - + )} {status !== undefined && ( diff --git a/apps/app/src/components/QuestionsHeader.tsx b/apps/app/src/components/QuestionsHeader.tsx index 2f62e123..2b8f0ca0 100644 --- a/apps/app/src/components/QuestionsHeader.tsx +++ b/apps/app/src/components/QuestionsHeader.tsx @@ -4,8 +4,8 @@ import { ChangeEvent } from "react"; import { technologiesLabels, Technology } from "../lib/technologies"; import { pluralize } from "../utils/intl"; import { useQuestionsOrderBy } from "../hooks/useQuestionsOrderBy"; -import { sortByLabels } from "../lib/order"; -import { Select } from "./Select/Select"; +import { parseQuerySortBy } from "../lib/order"; +import { SortBySelect } from "./SortBySelect"; const questionsPluralize = pluralize("pytanie", "pytania", "pytań"); @@ -16,6 +16,7 @@ type QuestionsHeaderProps = Readonly<{ export const QuestionsHeader = ({ technology, total }: QuestionsHeaderProps) => { const { sortBy, setSortByFromString } = useQuestionsOrderBy(); + const sort = parseQuerySortBy(sortBy); const handleSelectChange = (event: ChangeEvent) => { event.preventDefault(); @@ -28,16 +29,9 @@ export const QuestionsHeader = ({ technology, total }: QuestionsHeaderProps) => {technologiesLabels[technology]}: {total} {questionsPluralize(total)} - + {sort?.order && sort?.orderBy && ( + + )} ); }; From a18b4907521e681dac81f4d65d45886ad9f3bbed Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 18:54:09 +0100 Subject: [PATCH 07/36] rename sortByLabels --- apps/app/src/lib/order.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/app/src/lib/order.ts b/apps/app/src/lib/order.ts index c0e256f0..47599b3b 100644 --- a/apps/app/src/lib/order.ts +++ b/apps/app/src/lib/order.ts @@ -5,14 +5,14 @@ const orders = ["asc", "desc"] as const; export const DEFAULT_SORT_BY_QUERY = "acceptedAt*desc"; export const sortByLabels: Record<`${OrderBy}*${Order}`, string> = { - "acceptedAt*asc": "od najstarszych", - "acceptedAt*desc": "od najnowszych", - "level*asc": "od najprostszych", - "level*desc": "od najtrudniejszych", - "votesCount*asc": "od najmniej popularnych", - "votesCount*desc": "od najpopularniejszych", - "updatedAt*desc": "daty edycji (najnowsze)", - "updatedAt*asc": "daty edycji (najstarsze)", + "acceptedAt*asc": "data dodania: najstarsze", + "acceptedAt*desc": "data dodania: najnowsze", + "level*asc": "trudność: od najtrudniejszych", + "level*desc": "trudność: od najprostszych", + "votesCount*asc": "popularność: najmniejsza", + "votesCount*desc": "popularność: największa", + "updatedAt*asc": "data edycji: najstarsze", + "updatedAt*desc": "data edycji: najnowsze", }; export type OrderBy = typeof ordersBy[number]; From 3ef42b0d21487e3983bc1465a5d23a82a01f5f0d Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 23 Jan 2023 22:35:36 +0100 Subject: [PATCH 08/36] Fix 'sortByLabels' labels --- apps/app/src/lib/order.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/app/src/lib/order.ts b/apps/app/src/lib/order.ts index 47599b3b..bcdbb922 100644 --- a/apps/app/src/lib/order.ts +++ b/apps/app/src/lib/order.ts @@ -7,8 +7,8 @@ export const DEFAULT_SORT_BY_QUERY = "acceptedAt*desc"; export const sortByLabels: Record<`${OrderBy}*${Order}`, string> = { "acceptedAt*asc": "data dodania: najstarsze", "acceptedAt*desc": "data dodania: najnowsze", - "level*asc": "trudność: od najtrudniejszych", - "level*desc": "trudność: od najprostszych", + "level*asc": "trudność: od najłatwiejszych", + "level*desc": "trudność: od najtrudniejszych", "votesCount*asc": "popularność: najmniejsza", "votesCount*desc": "popularność: największa", "updatedAt*asc": "data edycji: najstarsze", From ab8e183dc73b82a08643bf3b7b40a73501657ce7 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Tue, 24 Jan 2023 20:04:02 +0100 Subject: [PATCH 09/36] feat(api): add simple "/answers" endpoint to fetch all answers --- apps/api/modules/answers/answers.routes.ts | 63 ++++++++++++++++++++- apps/api/modules/answers/answers.schemas.ts | 2 +- 2 files changed, 62 insertions(+), 3 deletions(-) diff --git a/apps/api/modules/answers/answers.routes.ts b/apps/api/modules/answers/answers.routes.ts index 25510a65..8ac19f8a 100644 --- a/apps/api/modules/answers/answers.routes.ts +++ b/apps/api/modules/answers/answers.routes.ts @@ -1,11 +1,12 @@ import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox"; import { Prisma } from "@prisma/client"; +import { Type } from "@sinclair/typebox"; import { FastifyPluginAsync, preHandlerAsyncHookHandler, preHandlerHookHandler } from "fastify"; import { PrismaErrorCode } from "../db/prismaErrors.js"; import { isPrismaError } from "../db/prismaErrors.util.js"; import { dbAnswerToDto } from "./answers.mapper.js"; import { - getAnswersSchema, + getAnswersRelatedToPostSchema, createAnswerSchema, deleteAnswerSchema, updateAnswerSchema, @@ -59,10 +60,68 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { } }; + fastify.withTypeProvider().route({ + url: "/answers", + method: "GET", + schema: { + response: { + 200: Type.Object({ + data: Type.Array( + Type.Object({ + id: Type.Number(), + content: Type.String(), + sources: Type.Array(Type.String()), + createdAt: Type.String({ format: "date-time" }), + updatedAt: Type.String({ format: "date-time" }), + CreatedBy: Type.Object({ + id: Type.Integer(), + firstName: Type.Union([Type.String(), Type.Null()]), + lastName: Type.Union([Type.String(), Type.Null()]), + }), + }), + ), + }), + }, + }, + async handler(request) { + const answers = await fastify.db.questionAnswer.findMany({ + select: { + id: true, + content: true, + sources: true, + createdAt: true, + updatedAt: true, + CreatedBy: { + select: { id: true, firstName: true, lastName: true }, + }, + }, + }); + + console.log(answers); + + return { + data: answers.map((a) => { + return { + id: a.id, + content: a.content, + sources: a.sources, + createdAt: a.createdAt.toISOString(), + updatedAt: a.createdAt.toISOString(), + CreatedBy: { + id: a.CreatedBy.id, + firstName: a.CreatedBy.firstName, + lastName: a.CreatedBy.lastName, + }, + }; + }), + }; + }, + }); + fastify.withTypeProvider().route({ url: "/questions/:id/answers", method: "GET", - schema: getAnswersSchema, + schema: getAnswersRelatedToPostSchema, async handler(request) { const { params: { id }, diff --git a/apps/api/modules/answers/answers.schemas.ts b/apps/api/modules/answers/answers.schemas.ts index 0ef87fea..e664ddfc 100644 --- a/apps/api/modules/answers/answers.schemas.ts +++ b/apps/api/modules/answers/answers.schemas.ts @@ -15,7 +15,7 @@ const answerSchema = Type.Object({ }), }); -export const getAnswersSchema = { +export const getAnswersRelatedToPostSchema = { params: Type.Object({ id: Type.Integer(), }), From 9b4bd00f907582a5cd4a69b57828942c314732df Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Tue, 24 Jan 2023 20:06:15 +0100 Subject: [PATCH 10/36] style(api): rename "CreateBy" => "createdBy in "/answers" endpoint" --- apps/api/modules/answers/answers.routes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/api/modules/answers/answers.routes.ts b/apps/api/modules/answers/answers.routes.ts index 8ac19f8a..090df1f6 100644 --- a/apps/api/modules/answers/answers.routes.ts +++ b/apps/api/modules/answers/answers.routes.ts @@ -73,7 +73,7 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { sources: Type.Array(Type.String()), createdAt: Type.String({ format: "date-time" }), updatedAt: Type.String({ format: "date-time" }), - CreatedBy: Type.Object({ + createdBy: Type.Object({ id: Type.Integer(), firstName: Type.Union([Type.String(), Type.Null()]), lastName: Type.Union([Type.String(), Type.Null()]), @@ -107,7 +107,7 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { sources: a.sources, createdAt: a.createdAt.toISOString(), updatedAt: a.createdAt.toISOString(), - CreatedBy: { + createdBy: { id: a.CreatedBy.id, firstName: a.CreatedBy.firstName, lastName: a.CreatedBy.lastName, From 87a9408c07294939b447498b359580d8f70ef8dc Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Tue, 24 Jan 2023 20:25:21 +0100 Subject: [PATCH 11/36] feat(api): add "votesCount" field to "/answers" response --- apps/api/modules/answers/answers.routes.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/api/modules/answers/answers.routes.ts b/apps/api/modules/answers/answers.routes.ts index 090df1f6..285c3eb3 100644 --- a/apps/api/modules/answers/answers.routes.ts +++ b/apps/api/modules/answers/answers.routes.ts @@ -78,6 +78,7 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { firstName: Type.Union([Type.String(), Type.Null()]), lastName: Type.Union([Type.String(), Type.Null()]), }), + votesCount: Type.Integer(), }), ), }), @@ -94,6 +95,11 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { CreatedBy: { select: { id: true, firstName: true, lastName: true }, }, + _count: { + select: { + QuestionAnswerVote: true, + }, + }, }, }); @@ -112,6 +118,7 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { firstName: a.CreatedBy.firstName, lastName: a.CreatedBy.lastName, }, + votesCount: a._count.QuestionAnswerVote, }; }), }; From 320979c94cc2d7c97de2fd293c8d932afd891f59 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Tue, 24 Jan 2023 20:28:35 +0100 Subject: [PATCH 12/36] fix(api): move "/answers" schema to separate file --- apps/api/modules/answers/answers.routes.ts | 23 ++------------------- apps/api/modules/answers/answers.schemas.ts | 22 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/apps/api/modules/answers/answers.routes.ts b/apps/api/modules/answers/answers.routes.ts index 285c3eb3..b7e30f33 100644 --- a/apps/api/modules/answers/answers.routes.ts +++ b/apps/api/modules/answers/answers.routes.ts @@ -11,6 +11,7 @@ import { deleteAnswerSchema, updateAnswerSchema, upvoteAnswerSchema, + getAnswersSchema, } from "./answers.schemas.js"; export const answerSelect = (userId: number) => { @@ -63,27 +64,7 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { fastify.withTypeProvider().route({ url: "/answers", method: "GET", - schema: { - response: { - 200: Type.Object({ - data: Type.Array( - Type.Object({ - id: Type.Number(), - content: Type.String(), - sources: Type.Array(Type.String()), - createdAt: Type.String({ format: "date-time" }), - updatedAt: Type.String({ format: "date-time" }), - createdBy: Type.Object({ - id: Type.Integer(), - firstName: Type.Union([Type.String(), Type.Null()]), - lastName: Type.Union([Type.String(), Type.Null()]), - }), - votesCount: Type.Integer(), - }), - ), - }), - }, - }, + schema: getAnswersSchema, async handler(request) { const answers = await fastify.db.questionAnswer.findMany({ select: { diff --git a/apps/api/modules/answers/answers.schemas.ts b/apps/api/modules/answers/answers.schemas.ts index e664ddfc..574dfa6f 100644 --- a/apps/api/modules/answers/answers.schemas.ts +++ b/apps/api/modules/answers/answers.schemas.ts @@ -89,3 +89,25 @@ export const downvoteAnswerSchema = { 204: Type.Never(), }, }; + +export const getAnswersSchema = { + response: { + 200: Type.Object({ + data: Type.Array( + Type.Object({ + id: Type.Number(), + content: Type.String(), + sources: Type.Array(Type.String()), + createdAt: Type.String({ format: "date-time" }), + updatedAt: Type.String({ format: "date-time" }), + createdBy: Type.Object({ + id: Type.Integer(), + firstName: Type.Union([Type.String(), Type.Null()]), + lastName: Type.Union([Type.String(), Type.Null()]), + }), + votesCount: Type.Integer(), + }), + ), + }), + }, +}; From 0de234a64567be3a7178d0fbc066ff0bfd685552 Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Tue, 24 Jan 2023 20:39:53 +0100 Subject: [PATCH 13/36] feat(api): add "socialLogin" field to "/amswers" response --- apps/api/modules/answers/answers.routes.ts | 3 ++- apps/api/modules/answers/answers.schemas.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/api/modules/answers/answers.routes.ts b/apps/api/modules/answers/answers.routes.ts index b7e30f33..595b2587 100644 --- a/apps/api/modules/answers/answers.routes.ts +++ b/apps/api/modules/answers/answers.routes.ts @@ -74,7 +74,7 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { createdAt: true, updatedAt: true, CreatedBy: { - select: { id: true, firstName: true, lastName: true }, + select: { id: true, firstName: true, lastName: true, socialLogin: true }, }, _count: { select: { @@ -98,6 +98,7 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { id: a.CreatedBy.id, firstName: a.CreatedBy.firstName, lastName: a.CreatedBy.lastName, + socialLogin: a.CreatedBy.socialLogin as Record, }, votesCount: a._count.QuestionAnswerVote, }; diff --git a/apps/api/modules/answers/answers.schemas.ts b/apps/api/modules/answers/answers.schemas.ts index 574dfa6f..d8f60fee 100644 --- a/apps/api/modules/answers/answers.schemas.ts +++ b/apps/api/modules/answers/answers.schemas.ts @@ -104,6 +104,7 @@ export const getAnswersSchema = { id: Type.Integer(), firstName: Type.Union([Type.String(), Type.Null()]), lastName: Type.Union([Type.String(), Type.Null()]), + socialLogin: Type.Record(Type.String(), Type.Union([Type.String(), Type.Number()])), }), votesCount: Type.Integer(), }), From f153bff03b48440777721d45233a4ca9e66eb9de Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Sun, 29 Jan 2023 14:12:07 +0100 Subject: [PATCH 14/36] remove console.log --- apps/api/modules/answers/answers.routes.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/api/modules/answers/answers.routes.ts b/apps/api/modules/answers/answers.routes.ts index 595b2587..158799d9 100644 --- a/apps/api/modules/answers/answers.routes.ts +++ b/apps/api/modules/answers/answers.routes.ts @@ -84,8 +84,6 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { }, }); - console.log(answers); - return { data: answers.map((a) => { return { From a1ce3bbd8075ffd29f4cf4c5ad2be357282cc85c Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Sun, 29 Jan 2023 18:08:35 +0100 Subject: [PATCH 15/36] feat(api): handle 'sortBy', 'sort', 'limit', 'offset' query params in '/answers' endpoint --- apps/api/modules/answers/answers.params.ts | 22 +++++++++++++ apps/api/modules/answers/answers.routes.ts | 4 ++- apps/api/modules/answers/answers.schemas.ts | 18 ++++++++++- .../api/modules/questions/questions.params.ts | 2 +- packages/openapi-types/types.ts | 31 +++++++++++++++++++ 5 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 apps/api/modules/answers/answers.params.ts diff --git a/apps/api/modules/answers/answers.params.ts b/apps/api/modules/answers/answers.params.ts new file mode 100644 index 00000000..402c5f98 --- /dev/null +++ b/apps/api/modules/answers/answers.params.ts @@ -0,0 +1,22 @@ +import { Prisma } from "@prisma/client"; +import { kv } from "../../utils.js"; +import { GetAnswersQuery } from "./answers.schemas"; + +export const getAnswersPrismaParams = ({ limit, offset, order, orderBy }: GetAnswersQuery) => { + return { + take: limit, + skip: offset, + ...(order && + orderBy && { + orderBy: { + ...(orderBy === "votesCount" + ? { + QuestionAnswerVote: { + _count: order, + }, + } + : kv(orderBy, order)), + }, + }), + } satisfies Prisma.QuestionAnswerFindManyArgs; +}; diff --git a/apps/api/modules/answers/answers.routes.ts b/apps/api/modules/answers/answers.routes.ts index 158799d9..cb62b472 100644 --- a/apps/api/modules/answers/answers.routes.ts +++ b/apps/api/modules/answers/answers.routes.ts @@ -1,10 +1,10 @@ import { TypeBoxTypeProvider } from "@fastify/type-provider-typebox"; import { Prisma } from "@prisma/client"; -import { Type } from "@sinclair/typebox"; import { FastifyPluginAsync, preHandlerAsyncHookHandler, preHandlerHookHandler } from "fastify"; import { PrismaErrorCode } from "../db/prismaErrors.js"; import { isPrismaError } from "../db/prismaErrors.util.js"; import { dbAnswerToDto } from "./answers.mapper.js"; +import { getAnswersPrismaParams } from "./answers.params.js"; import { getAnswersRelatedToPostSchema, createAnswerSchema, @@ -66,7 +66,9 @@ const answersPlugin: FastifyPluginAsync = async (fastify) => { method: "GET", schema: getAnswersSchema, async handler(request) { + const params = getAnswersPrismaParams(request.query); const answers = await fastify.db.questionAnswer.findMany({ + ...params, select: { id: true, content: true, diff --git a/apps/api/modules/answers/answers.schemas.ts b/apps/api/modules/answers/answers.schemas.ts index d8f60fee..45f293ed 100644 --- a/apps/api/modules/answers/answers.schemas.ts +++ b/apps/api/modules/answers/answers.schemas.ts @@ -1,4 +1,4 @@ -import { Type } from "@sinclair/typebox"; +import { Static, Type } from "@sinclair/typebox"; const answerSchema = Type.Object({ id: Type.Number(), @@ -90,7 +90,21 @@ export const downvoteAnswerSchema = { }, }; +const generateGetAnswersQuerySchema = Type.Partial( + Type.Object({ + limit: Type.Integer(), + offset: Type.Integer(), + orderBy: Type.Union([ + Type.Literal("createdAt"), + Type.Literal("updatedAt"), + Type.Literal("votesCount"), + ]), + order: Type.Union([Type.Literal("asc"), Type.Literal("desc")]), + }), +); + export const getAnswersSchema = { + querystring: generateGetAnswersQuerySchema, response: { 200: Type.Object({ data: Type.Array( @@ -112,3 +126,5 @@ export const getAnswersSchema = { }), }, }; + +export type GetAnswersQuery = Static; diff --git a/apps/api/modules/questions/questions.params.ts b/apps/api/modules/questions/questions.params.ts index fed3e525..8a1e6866 100644 --- a/apps/api/modules/questions/questions.params.ts +++ b/apps/api/modules/questions/questions.params.ts @@ -1,6 +1,6 @@ import { Prisma } from "@prisma/client"; import { kv } from "../../utils.js"; -import { GetQuestionsQuery } from "./questions.schemas.js"; +import { GetQuestionsQuery } from "./questions.schemas"; export const getQuestionsPrismaParams = ( { diff --git a/packages/openapi-types/types.ts b/packages/openapi-types/types.ts index 3bd75e52..242cdbab 100644 --- a/packages/openapi-types/types.ts +++ b/packages/openapi-types/types.ts @@ -301,6 +301,37 @@ export interface paths { }; }; }; + "/answers": { + get: { + responses: { + /** @description Default Response */ + 200: { + content: { + "application/json": { + data: { + id: number; + content: string; + sources: string[]; + /** Format: date-time */ + createdAt: string; + /** Format: date-time */ + updatedAt: string; + createdBy: { + id: number; + firstName: string | null; + lastName: string | null; + socialLogin: { + [key: string]: (string | number) | undefined; + }; + }; + votesCount: number; + }[]; + }; + }; + }; + }; + }; + }; "/questions/{id}/answers": { get: { parameters: { From 2c1116f7fac8c5251243bf0568d17d13c7e19cea Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Sun, 29 Jan 2023 18:39:10 +0100 Subject: [PATCH 16/36] feat(app): add '/answers' page skeleton --- .../app/(main-layout)/answers/[page]/page.tsx | 21 +++++++++++++++++++ .../src/app/(main-layout)/answers/head.tsx | 5 +++++ .../src/app/(main-layout)/answers/layout.tsx | 6 ++++++ .../src/components/CtaHeader/CtaHeader.tsx | 5 ++++- 4 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 apps/app/src/app/(main-layout)/answers/[page]/page.tsx create mode 100644 apps/app/src/app/(main-layout)/answers/head.tsx create mode 100644 apps/app/src/app/(main-layout)/answers/layout.tsx diff --git a/apps/app/src/app/(main-layout)/answers/[page]/page.tsx b/apps/app/src/app/(main-layout)/answers/[page]/page.tsx new file mode 100644 index 00000000..e95a4981 --- /dev/null +++ b/apps/app/src/app/(main-layout)/answers/[page]/page.tsx @@ -0,0 +1,21 @@ +import { redirect } from "next/navigation"; +import { PrivateRoute } from "../../../../components/PrivateRoute"; +import { Params, SearchParams } from "../../../../types"; + +export default function ManageQuestionsAnswers({ + params, + searchParams, +}: { + params: Params<"page">; + searchParams?: SearchParams<"technology" | "level">; +}) { + const page = Number.parseInt(params.page); + + if (Number.isNaN(page)) { + return redirect("/user/questions"); + } + + console.log(); + + return manage questions answers; +} diff --git a/apps/app/src/app/(main-layout)/answers/head.tsx b/apps/app/src/app/(main-layout)/answers/head.tsx new file mode 100644 index 00000000..0e83a998 --- /dev/null +++ b/apps/app/src/app/(main-layout)/answers/head.tsx @@ -0,0 +1,5 @@ +import { HeadTags } from "../../../components/HeadTags"; + +export default function Head() { + return ; +} diff --git a/apps/app/src/app/(main-layout)/answers/layout.tsx b/apps/app/src/app/(main-layout)/answers/layout.tsx new file mode 100644 index 00000000..7bfcf05f --- /dev/null +++ b/apps/app/src/app/(main-layout)/answers/layout.tsx @@ -0,0 +1,6 @@ +import { ReactNode } from "react"; +import { Container } from "../../../components/Container"; + +export default function UserPageLayout({ children }: { readonly children: ReactNode }) { + return {children}; +} diff --git a/apps/app/src/components/CtaHeader/CtaHeader.tsx b/apps/app/src/components/CtaHeader/CtaHeader.tsx index 8d75c2bb..958b5ca5 100644 --- a/apps/app/src/components/CtaHeader/CtaHeader.tsx +++ b/apps/app/src/components/CtaHeader/CtaHeader.tsx @@ -21,7 +21,7 @@ const CtaHeaderActiveLink = (props: CtaHeaderActiveLinkProps) => ( export const CtaHeader = () => (
-
); }; diff --git a/apps/app/src/components/AnswersDashboard/FilterableAnswersList.tsx b/apps/app/src/components/AnswersDashboard/FilterableAnswersList.tsx new file mode 100644 index 00000000..38ca72e1 --- /dev/null +++ b/apps/app/src/components/AnswersDashboard/FilterableAnswersList.tsx @@ -0,0 +1,21 @@ +import { ComponentProps, ReactNode } from "react"; +import { QuestionsPagination } from "../QuestionsPagination/QuestionsPagination"; + +type FilterableAnswersListProps = { + page: number; + children: ReactNode; +} & Omit, "current">; + +export const FilterableAnswersList = ({ + page, + total, + getHref, + children, +}: FilterableAnswersListProps) => { + return ( +
+ {children} + +
+ ); +}; From 4f007112d6964cc6eee1ae1864286cd3c398446e Mon Sep 17 00:00:00 2001 From: Grzegorz Pokorski Date: Mon, 30 Jan 2023 12:05:24 +0100 Subject: [PATCH 23/36] move 'sortByLabels' out of the 'SortBySelect' component --- .../AnswersDashboard/FilterableAnswersList.tsx | 2 ++ .../FilterableQuestionsListHeader.tsx | 9 +++++++-- apps/app/src/components/QuestionsHeader.tsx | 9 +++++++-- apps/app/src/components/SortBySelect.tsx | 3 ++- 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/apps/app/src/components/AnswersDashboard/FilterableAnswersList.tsx b/apps/app/src/components/AnswersDashboard/FilterableAnswersList.tsx index 38ca72e1..f23ff306 100644 --- a/apps/app/src/components/AnswersDashboard/FilterableAnswersList.tsx +++ b/apps/app/src/components/AnswersDashboard/FilterableAnswersList.tsx @@ -1,5 +1,6 @@ import { ComponentProps, ReactNode } from "react"; import { QuestionsPagination } from "../QuestionsPagination/QuestionsPagination"; +import { FilterableAnswersListHeader } from "./FilterableAnswersListHeader"; type FilterableAnswersListProps = { page: number; @@ -14,6 +15,7 @@ export const FilterableAnswersList = ({ }: FilterableAnswersListProps) => { return (
+ {children}
diff --git a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx index 7fd15e77..97161c4f 100644 --- a/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx +++ b/apps/app/src/components/FilterableQuestionsList/FilterableQuestionsListHeader.tsx @@ -2,7 +2,7 @@ import { useRouter } from "next/navigation"; import { ChangeEvent, ReactNode } from "react"; import { useDevFAQRouter } from "../../hooks/useDevFAQRouter"; import { levels } from "../../lib/level"; -import { Order, OrderBy } from "../../lib/order"; +import { Order, OrderBy, sortByLabels } from "../../lib/order"; import { QuestionStatus, statuses } from "../../lib/question"; import { technologies, technologiesLabels, Technology } from "../../lib/technologies"; import { Level } from "../QuestionItem/QuestionLevel"; @@ -70,7 +70,12 @@ export const FilterableQuestionsListHeader = ({
)} {order && orderBy && ( - + )} {status !== undefined && ( diff --git a/apps/app/src/components/QuestionsHeader.tsx b/apps/app/src/components/QuestionsHeader.tsx index 2b8f0ca0..1f63d1e9 100644 --- a/apps/app/src/components/QuestionsHeader.tsx +++ b/apps/app/src/components/QuestionsHeader.tsx @@ -4,7 +4,7 @@ import { ChangeEvent } from "react"; import { technologiesLabels, Technology } from "../lib/technologies"; import { pluralize } from "../utils/intl"; import { useQuestionsOrderBy } from "../hooks/useQuestionsOrderBy"; -import { parseQuerySortBy } from "../lib/order"; +import { parseQuerySortBy, sortByLabels } from "../lib/order"; import { SortBySelect } from "./SortBySelect"; const questionsPluralize = pluralize("pytanie", "pytania", "pytań"); @@ -30,7 +30,12 @@ export const QuestionsHeader = ({ technology, total }: QuestionsHeaderProps) => {total} {questionsPluralize(total)} {sort?.order && sort?.orderBy && ( - + )} ); diff --git a/apps/app/src/components/SortBySelect.tsx b/apps/app/src/components/SortBySelect.tsx index d8d8f952..195d8fe0 100644 --- a/apps/app/src/components/SortBySelect.tsx +++ b/apps/app/src/components/SortBySelect.tsx @@ -6,10 +6,11 @@ import { SelectLabel } from "./SelectLabel"; type SortBySelectProps = { order: Order; orderBy: OrderBy; + sortByLabels: typeof sortByLabels; onChange: (event: ChangeEvent) => void; }; -export const SortBySelect = ({ order, orderBy, onChange }: SortBySelectProps) => ( +export const SortBySelect = ({ order, orderBy, sortByLabels, onChange }: SortBySelectProps) => ( Sortowanie: