diff --git a/apps/app/.env.local b/apps/app/.env.local-example similarity index 100% rename from apps/app/.env.local rename to apps/app/.env.local-example diff --git a/apps/app/.gitignore b/apps/app/.gitignore index 2068bd05..de8cee3b 100644 --- a/apps/app/.gitignore +++ b/apps/app/.gitignore @@ -1,3 +1,4 @@ .vscode .vercel storybook-static +.env*.local diff --git a/apps/app/public/select.svg b/apps/app/public/select.svg new file mode 100644 index 00000000..7b6b73ca --- /dev/null +++ b/apps/app/public/select.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/app/src/app/(main-layout)/questions/[technology]/[page]/page.tsx b/apps/app/src/app/(main-layout)/questions/[technology]/[page]/page.tsx index a0b732f1..621715e1 100644 --- a/apps/app/src/app/(main-layout)/questions/[technology]/[page]/page.tsx +++ b/apps/app/src/app/(main-layout)/questions/[technology]/[page]/page.tsx @@ -1,16 +1,21 @@ import { redirect } from "next/navigation"; import { QuestionItem } from "../../../../../components/QuestionItem/QuestionItem"; +import { QuestionsHeader } from "../../../../../components/QuestionsHeader/QuestionsHeader"; import { QuestionsPagination } from "../../../../../components/QuestionsPagination"; import { PAGE_SIZE } from "../../../../../lib/constants"; +import { getQuerySortBy, DEFAULT_SORT_BY_QUERY } from "../../../../../lib/order"; import { technologies } from "../../../../../lib/technologies"; import { getAllQuestions } from "../../../../../services/questions.service"; export default async function QuestionsPage({ params, + searchParams, }: { params: { technology: string; page: string }; + searchParams?: { sortBy?: string }; }) { const page = parseInt(params.page); + const querySortBy = getQuerySortBy(searchParams?.sortBy || DEFAULT_SORT_BY_QUERY); if (!technologies.includes(params.technology) || isNaN(page)) { return redirect("/"); @@ -20,10 +25,13 @@ export default async function QuestionsPage({ category: params.technology, limit: PAGE_SIZE, offset: (page - 1) * PAGE_SIZE, + orderBy: querySortBy?.orderBy, + order: querySortBy?.order, }); return (
+ {data.data.map(({ id, question, _levelId, acceptedAt, votesCount }) => ( ) => {
- - diff --git a/apps/app/src/components/QuestionsHeader/QuestionsHeader.tsx b/apps/app/src/components/QuestionsHeader/QuestionsHeader.tsx new file mode 100644 index 00000000..ea738b62 --- /dev/null +++ b/apps/app/src/components/QuestionsHeader/QuestionsHeader.tsx @@ -0,0 +1,41 @@ +"use client"; + +import { ChangeEvent } from "react"; +import { technologiesLabel, Technology } from "../../lib/technologies"; +import { pluralize } from "../../utils/intl"; +import { Select } from "../Select/Select"; +import { useQuestionsOrderBy } from "../../hooks/useQuestionsOrderBy"; + +const questionsPluralize = pluralize("pytanie", "pytania", "pytań"); + +type QuestionsHeaderProps = Readonly<{ + technology: Technology; + total: number; +}>; + +export const QuestionsHeader = ({ technology, total }: QuestionsHeaderProps) => { + const { sortBy, setSortByFromString } = useQuestionsOrderBy(); + + const handleSelectChange = (event: ChangeEvent) => { + event.preventDefault(); + setSortByFromString(event.target.value); + }; + + return ( +
+ + {technologiesLabel[technology]}: + {total} {questionsPluralize(total)} + + +
+ ); +}; diff --git a/apps/app/src/components/Select/Select.stories.tsx b/apps/app/src/components/Select/Select.stories.tsx index 95be4282..58ae45dd 100644 --- a/apps/app/src/components/Select/Select.stories.tsx +++ b/apps/app/src/components/Select/Select.stories.tsx @@ -19,4 +19,14 @@ export default meta; type Story = StoryObj; -export const Default: Story = {}; +export const Default: Story = { + args: { + variant: "default", + }, +}; + +export const Purple: Story = { + args: { + variant: "purple", + }, +}; diff --git a/apps/app/src/components/Select/Select.tsx b/apps/app/src/components/Select/Select.tsx index f6d73ee2..675801d8 100644 --- a/apps/app/src/components/Select/Select.tsx +++ b/apps/app/src/components/Select/Select.tsx @@ -1,10 +1,23 @@ import { twMerge } from "tailwind-merge"; import type { SelectHTMLAttributes } from "react"; -export const Select = ({ className, ...props }: SelectHTMLAttributes) => ( +const variants = { + default: + "select bg-white py-1 pl-1 pr-6 text-neutral-700 dark:bg-white-dark dark:text-neutral-200", + purple: + "select-purple border-b border-primary bg-transparent py-2 pr-6 pl-1 capitalize text-primary transition-shadow duration-100 focus:shadow-[0_0_10px] focus:shadow-primary dark:border-neutral-200 dark:text-neutral-200 dark:focus:shadow-white", +}; + +type SelectProps = Readonly<{ + variant: keyof typeof variants; +}> & + SelectHTMLAttributes; + +export const Select = ({ variant, className, ...props }: SelectProps) => (