Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion frontend/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,12 @@
}
}

.qa-accordion-item:hover,
.qa-accordion-item:focus-within,
.qa-accordion-item[data-state='open'] {
border-color: var(--qa-accent);
}

.no-select {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
Expand Down Expand Up @@ -244,4 +250,4 @@
/* Зупинка при наведенні мишкою, щоб роздивитися */
.hover\:pause:hover .animate-marquee-vertical {
animation-play-state: paused;
}
}
12 changes: 11 additions & 1 deletion frontend/components/header/MainSwitcher.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ function isBlogPath(pathname: string): boolean {
return segments[0] === 'blog' || segments[1] === 'blog';
}

function isQaPath(pathname: string): boolean {
const segments = pathname.split('/').filter(Boolean);
return segments[0] === 'q&a' || segments[1] === 'q&a';
}

type MainSwitcherProps = {
children: ReactNode;
userExists: boolean;
Expand All @@ -28,6 +33,7 @@ export function MainSwitcher({
blogCategories = [],
}: MainSwitcherProps) {
const pathname = usePathname();
const isQa = isQaPath(pathname);

if (isShopPath(pathname)) return <>{children}</>;

Expand All @@ -45,5 +51,9 @@ export function MainSwitcher({
);
}

return <main className="mx-auto px-6 min-h-[80vh]">{children}</main>;
return (
<main className={isQa ? 'mx-auto min-h-[80vh]' : 'mx-auto px-6 min-h-[80vh]'}>
{children}
</main>
);
}
22 changes: 18 additions & 4 deletions frontend/components/q&a/AccordionList.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
'use client';

import { ReactNode } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import {
Accordion,
AccordionItem,
AccordionTrigger,
AccordionContent,
} from '@/components/ui/accordion';
import { qaTabStyles } from '@/data/qaTabs';
Comment thread
coderabbitai[bot] marked this conversation as resolved.

import CodeBlock from '@/components/q&a/CodeBlock';
import type {
Expand Down Expand Up @@ -239,10 +240,23 @@ export default function AccordionList({ items }: { items: QuestionEntry[] }) {
<Accordion type="single" collapsible className="w-full">
{items.map((q, idx) => {
const key = q.id ?? idx;
const accent =
qaTabStyles[q.category as keyof typeof qaTabStyles]?.accent;
return (
<AccordionItem key={key} value={String(key)}>
<AccordionTrigger>{q.question}</AccordionTrigger>
<AccordionContent>
<AccordionItem
key={key}
value={String(key)}
className="qa-accordion-item mb-3 last:mb-0 rounded-xl border border-black/5 bg-white/90 shadow-sm transition-colors last:border-b dark:border-white/10 dark:bg-neutral-900/80"
style={
accent
? ({ '--qa-accent': accent } as CSSProperties)
: undefined
}
>
<AccordionTrigger className="px-4 hover:no-underline">
{q.question}
</AccordionTrigger>
<AccordionContent className="px-4">
<div className="space-y-3 pt-2">
{q.answerBlocks.map((block, i) => renderBlock(block, i))}
</div>
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/q&a/CodeBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export default function CodeBlock({ code, language }: Props) {
const { resolvedTheme } = useTheme();
const [copied, setCopied] = useState(false);

const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.vsLight;
const theme = resolvedTheme === 'dark' ? themes.nightOwl : themes.github;

const handleCopy = async () => {
await navigator.clipboard.writeText(code);
Expand Down
6 changes: 3 additions & 3 deletions frontend/components/q&a/Pagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ export function Pagination({
disabled={currentPage === 1}
className={cn(
'px-2 py-2 text-sm font-medium rounded-lg transition-colors sm:px-3',
'border border-gray-300 dark:border-gray-700',
'border border-gray-300 bg-white/90 dark:border-gray-700 dark:bg-neutral-900/80',
currentPage === 1
? 'text-gray-400 dark:text-gray-600 cursor-not-allowed'
: 'text-gray-700 dark:text-gray-300 hover:bg-[var(--qa-accent-soft)]'
Expand All @@ -122,7 +122,7 @@ export function Pagination({
onClick={() => onPageChange(page)}
disabled={page === currentPage}
className={cn(
'min-w-[40px] px-3 py-2 text-sm font-medium rounded-lg transition-colors border border-transparent overflow-hidden',
'min-w-[40px] px-3 py-2 text-sm font-medium rounded-lg transition-colors border border-transparent overflow-hidden bg-white/90 dark:bg-neutral-900/80',
page === currentPage
? 'shadow-sm text-gray-700 dark:text-gray-300'
: 'text-gray-700 dark:text-gray-300 hover:bg-[var(--qa-accent-soft)]'
Expand Down Expand Up @@ -150,7 +150,7 @@ export function Pagination({
disabled={currentPage === totalPages}
className={cn(
'px-2 py-2 text-sm font-medium rounded-lg transition-colors sm:px-3',
'border border-gray-300 dark:border-gray-700',
'border border-gray-300 bg-white/90 dark:border-gray-700 dark:bg-neutral-900/80',
currentPage === totalPages
? 'text-gray-400 dark:text-gray-600 cursor-not-allowed'
: 'text-gray-700 dark:text-gray-300 hover:bg-[var(--qa-accent-soft)]'
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/q&a/QaTabButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export function QaTabButton({
<TabsTrigger
value={value}
className={cn(
'group relative h-full min-w-[96px] !flex !w-fit !flex-none !shrink-0 !items-center !justify-start gap-2 overflow-hidden rounded-xl border border-black/5 bg-transparent px-4 py-3 text-left text-xs font-semibold text-black shadow-sm transition-all duration-300 ease-out hover:-translate-y-0.5 hover:shadow-lg dark:border-white/20 dark:bg-transparent dark:text-white',
'group relative h-full min-w-[96px] !flex !w-fit !flex-none !shrink-0 !items-center !justify-start gap-2 overflow-hidden rounded-xl border border-black/5 bg-white/90 px-4 py-3 text-left text-xs font-semibold text-black shadow-sm transition-all duration-300 ease-out hover:-translate-y-0.5 hover:shadow-lg dark:border-white/20 dark:bg-neutral-900/80 dark:text-white',
'data-[state=active]:-translate-y-0.5 data-[state=active]:shadow-lg data-[state=active]:border-2 dark:data-[state=active]:border-2',
style.color
)}
Expand Down
15 changes: 3 additions & 12 deletions frontend/components/q&a/useQaTabs.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use client';

import { useState, useEffect, useCallback } from 'react';
import { useSearchParams, useParams } from 'next/navigation';
import { useSearchParams } from 'next/navigation';
import { useLocale } from 'next-intl';
import { useRouter } from '@/i18n/routing';
import { categoryData } from '@/data/category';
import {
qaConstants,
type CategorySlug,
type Locale,
type PaginatedResponse,
Expand All @@ -15,11 +15,6 @@ import {

const CATEGORY_SLUGS = categoryData.map(category => category.slug);
const DEFAULT_CATEGORY = CATEGORY_SLUGS[0] || 'html';
function resolveLocale(value: string): Locale {
return qaConstants.supportedLocales.includes(value as Locale)
? (value as Locale)
: 'en';
}

function isCategorySlug(value: string): value is CategorySlug {
return CATEGORY_SLUGS.includes(value);
Expand All @@ -28,11 +23,7 @@ function isCategorySlug(value: string): value is CategorySlug {
export function useQaTabs() {
const router = useRouter();
const searchParams = useSearchParams();
const params = useParams();

const locale =
typeof params.locale === 'string' ? params.locale : params.locale?.[0] ?? '';
const localeKey = resolveLocale(locale);
const localeKey = useLocale() as Locale;

const rawPage = searchParams.get('page');
const pageFromUrl = rawPage ? Number(rawPage) : 1;
Expand Down