Skip to content

121 implement redesigned academic scholarship tanulmányi ösztöndíj page and cms integration#159

Open
DankaMarci wants to merge 10 commits into
mainfrom
121-implement-redesigned-academic-scholarship-tanulmányi-ösztöndíj-page-and-cms-integration

Hidden character warning

The head ref may contain hidden characters: "121-implement-redesigned-academic-scholarship-tanulm\u00e1nyi-\u00f6szt\u00f6nd\u00edj-page-and-cms-integration"
Open

121 implement redesigned academic scholarship tanulmányi ösztöndíj page and cms integration#159
DankaMarci wants to merge 10 commits into
mainfrom
121-implement-redesigned-academic-scholarship-tanulmányi-ösztöndíj-page-and-cms-integration

Conversation

@DankaMarci

@DankaMarci DankaMarci commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added FAQ accordion for academic scholarships
    • Added submission process information display
    • Added support for external forms/links in permissions
    • Added contact information to news articles
  • Bug Fixes & Improvements

    • Enhanced empty state messaging across multiple sections
    • Improved layout consistency and styling throughout
    • Restructured detail pages for better readability
    • Updated background colors and visual design elements

- Updated RepresentativeCard component to enhance accessibility and visual design.
- Refactored RepresentativeModal to streamline layout and improve user experience.
- Modified RepresentativesGrid to accept title and description props for better context.
- Enhanced RepresentativesGridClient with a back button and improved loading states.
- Created a new helpers file for common representative functions and styles.
- Improved loading skeletons for representative cards and grid.
- Updated page structure in RepresentativesPage for better layout and responsiveness.
… table

- Created a new migration to add `representative_id` and `contact_email` columns to the `news` table.
- Added foreign key constraint for `representative_id` referencing the `representatives` table.
- Created an index on `representative_id` for improved query performance.
- Updated the payload types to include optional `representative` and `contactEmail` fields in the News interface.
…rnal link

- Created a new migration to add columns for submission process in Hungarian and English, and an external link to the permissions table.
- Updated the migrations index to include the new migration.
- Extended the Permission interface in payload-types.ts to include types for the new submission process fields and external link.
- Created migration for academic scholarship FAQ with accordion items.
- Updated migrations index to include the new academic scholarship FAQ migration.
- Defined AcademicScholarshipFaq and AcademicScholarshipFaqSelect interfaces in payload-types.
- Configured global settings to include AcademicScholarshipFAQ in payload configuration.
@vercel

vercel Bot commented Jun 19, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ehk Ready Ready Preview, Comment Jun 19, 2026 6:16pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extends the Payload CMS schema with an AcademicScholarshipFAQ global and new fields on Permissions (submission process rich-text, external link) and News (representative relation, contact email). It adds a shared Accordion component and extends FileCard, then rewires multiple feature pages (scholarship, permissions, news article, representatives, decisions, help, language education, regulations) to use the new data and updated UI layouts.

Changes

Full-stack feature additions and UI redesign

Layer / File(s) Summary
CMS schema extensions and database migrations
src/globals/AcademicScholarshipFAQ.ts, src/collections/Permissions.ts, src/collections/News.ts, src/payload.config.ts, src/payload-types.ts, src/migrations/..., src/migrations/index.ts
Adds the AcademicScholarshipFAQ Payload global with bilingual accordion items, extends Permissions with submissionProcess_hu/en rich-text and externalLink fields, adds representative relation and contactEmail to News, registers the global, and provides three new up/down migrations with updated generated types.
Shared Accordion component, FileCard extension, richtext CSS
src/components/common/Accordion.tsx, src/components/common/FileCard.tsx, src/app/(app)/globals.css
Introduces a new accessible Accordion/AccordionItem component with controlled/uncontrolled open state and single-open mode. Extends FileCard with externalUrl, extensionLabel, and "open" action type. Adds .richtext blockquote and heading/list typography to global CSS.
Academic Scholarship FAQ page
src/lib/payload-cms.ts, src/app/(app)/[lang]/tanulmanyi-osztondij/components/StudyScholarshipContent.tsx, src/app/(app)/[lang]/tanulmanyi-osztondij/page.tsx, src/dictionaries/*/scholarships.json
Adds getAcademicScholarshipFAQ() data helper. Rewrites StudyScholarshipContent as an async component fetching FAQ items and rendering them via Accordion with localized RichText, or showing EmptyState. Page validates locale and passes dictionary-derived empty-state strings.
Permissions page: submission process and external link rendering
src/app/(app)/[lang]/engedelyek/components/PermissionsListClient.tsx, src/app/(app)/[lang]/engedelyek/page.tsx, src/dictionaries/*/permissions.json
Rewrites PermissionsListClient with a hasRichText type guard, EN/HU field fallback logic, conditional submission-process section, and FileCard rendering for external links or files. Page layout removes outer container and PageHeader.
News article page redesign
src/app/(app)/[lang]/hirek/[slug]/components/NewsDetail.tsx, .../NewsDetailMain.tsx, .../NewsDetailSidebar.tsx, .../RelatedNewsClient.tsx, .../BackNav.tsx, .../FileIcon.tsx, .../NewsDetailClient.ts, .../ShareButton.tsx, .../lib/news-utils.ts, src/dictionaries/*/news.json
Removes BackNav, FileIcon, ShareButton, NewsDetailClient, and news-utils. Refactors layout to full-height flex. Rewrites NewsDetailMain with router.back() and FileCard attachments. Replaces sidebar with a contacts section (representative + email). Updates RelatedNewsClient with custom tag pills and UTC date formatting.
Representatives page redesign
src/app/(app)/[lang]/kepviselok/components/representatives.helpers.ts, .../RepresentativeCard.tsx, .../RepresentativeModal.tsx, .../RepresentativesGrid.tsx, .../RepresentativesGridClient.tsx, .../skeletons/... , src/app/(app)/[lang]/kepviselok/page.tsx
Adds representatives.helpers.ts with facultyStyles, position helpers, picture/initials extraction, and file size formatting. Redesigns RepresentativeCard as an accessible article element. Rewrites RepresentativeModal to use direct file anchor links and single primary email. Adds title/description props to the grid chain and back-navigation button. Updates skeletons and page layout.
Decisions, Help, Language Education, Regulations updates
src/app/(app)/[lang]/hatarozatok-tara/..., src/app/(app)/[lang]/kisokosok/..., src/app/(app)/[lang]/nyelvoktatas/..., src/app/(app)/[lang]/oktatasi-szabalyzatok/..., src/components/regulations/..., src/dictionaries/*/language_education.json, src/dictionaries/*/regulations.json
Refactors DecisionsArchiveClient to use useLanguage() and EmptyState. Adds LoadingHelpPageGrid skeleton; converts help/language-education/regulations pages to bg-[#f9f4f0] background. Rewrites LanguageEducationContent to a two-column dictionary-driven layout. Converts RegulationsListClient and LoadingRegulationsGrid to article-based layout and pulse skeleton.

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • kir-dev/ehk#150: Previously modified FileCard API and attachment rendering patterns that this PR extends with externalUrl, extensionLabel, and the "open" action type.

Suggested reviewers

  • peterlipt

Poem

🐇 Hop, hop, the rabbit codes away,
New accordions spring up today!
News contacts bloom, permissions grow,
Skeletons pulse with animated glow.
The representatives shine with style —
Every page redesigned with a smile! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.98% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately captures the main scope: redesigning the academic scholarship page with CMS integration, which aligns with the substantial changes across the scholarship component, CMS globals, and related infrastructure.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 121-implement-redesigned-academic-scholarship-tanulmányi-ösztöndíj-page-and-cms-integration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx (1)

29-38: ⚡ Quick win

Extract duplicated date formatting into a shared helper.

This formatter is duplicated in NewsDetailMain.tsx and RelatedNewsClient.tsx; centralizing it avoids locale/date drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx around lines
29 - 38, The formatDate function in NewsDetailMain.tsx is duplicated in
RelatedNewsClient.tsx. Create a shared utility helper file (e.g., in a utils or
helpers directory) and move the formatDate function there, ensuring it accepts
both the dateString and lang parameters. Then update both NewsDetailMain.tsx and
RelatedNewsClient.tsx to import and use this shared formatDate helper instead of
maintaining duplicate implementations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/app/`(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx:
- Around line 46-49: The onClick handler in the button element that calls
router.back() lacks a proper fallback route, and the hardcoded fallback to
/{lang}/hirek is invalid since that page doesn't exist in the routing structure.
To fix this, implement a fallback mechanism by either: (1) accepting a
parentRoute prop in the NewsDetailMain component and using it as the fallback
destination when router.back() fails, (2) using useSearchParams() to capture and
preserve referrer information from the parent route during navigation, or (3)
gracefully handling the case where no valid parent route is available by
redirecting to a valid fallback like the homepage or a category index. The
onClick handler should check window.history.length to determine if back
navigation is safe, and use the configured fallback route instead of the invalid
/{lang}/hirek path.

In `@src/app/`(app)/[lang]/hirek/[slug]/components/RelatedNewsClient.tsx:
- Around line 61-63: The Link component's href attribute is missing the locale
prefix which causes unintended locale switching when navigating to related
articles. In the RelatedNewsClient component where the Link element contains
href={`/hirek/${article.id}`}, modify the href to include the locale segment by
using the lang variable obtained from the useTranslate() hook. Update the href
to construct the path as href={`/${lang.toLowerCase()}/hirek/${article.id}`} to
ensure users remain on their current language version when clicking related news
links.

In `@src/app/`(app)/[lang]/kepviselok/components/RepresentativeCard.tsx:
- Around line 27-41: The article element in RepresentativeCard has role="button"
but contains nested interactive email links (lines 86-94), creating conflicting
accessibility semantics. Remove the role="button" attribute from the article
element and refactor the click handling by moving the onClickAction logic to a
wrapper div or restructuring the component so that the card container is not a
button, allowing the nested email links to be the primary interactive elements.
This will resolve the semantic conflict and improve keyboard and screen-reader
accessibility.

In `@src/app/`(app)/[lang]/nyelvoktatas/page.tsx:
- Around line 6-9: The `lang` parameter extracted from `params` in the page
component is being passed directly to getDictionary without validation, which
can cause the page to crash if an unsupported locale is provided since
loaders[locale] would be undefined. Before calling getDictionary(lang,
'language_education'), validate that the lang value is a supported locale and
apply a fallback pattern (consistent with other pages in the application) to
handle cases where an invalid locale is provided. This ensures that
getDictionary receives only valid locales from the loaders object.

In `@src/collections/Permissions.ts`:
- Around line 100-108: The externalLink field in the Permissions collection is
defined with type "text" which allows arbitrary text input without URL
validation. Modify this field definition to add URL validation by either
changing the type to support URL validation or by adding a validate function
that checks if the provided value is a valid URL format before allowing it to be
saved, ensuring only proper URLs can be set for this external link field.

---

Nitpick comments:
In `@src/app/`(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx:
- Around line 29-38: The formatDate function in NewsDetailMain.tsx is duplicated
in RelatedNewsClient.tsx. Create a shared utility helper file (e.g., in a utils
or helpers directory) and move the formatDate function there, ensuring it
accepts both the dateString and lang parameters. Then update both
NewsDetailMain.tsx and RelatedNewsClient.tsx to import and use this shared
formatDate helper instead of maintaining duplicate implementations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7655705-e0f1-4d8e-9b3a-8887777855fc

📥 Commits

Reviewing files that changed from the base of the PR and between 6844674 and 5845af1.

📒 Files selected for processing (57)
  • src/app/(app)/[lang]/engedelyek/components/PermissionsListClient.tsx
  • src/app/(app)/[lang]/engedelyek/page.tsx
  • src/app/(app)/[lang]/hatarozatok-tara/components/DecisionsArchiveClient.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/BackNav.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/FileIcon.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/NewsDetail.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailClient.ts
  • src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailSidebar.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/RelatedNewsClient.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/ShareButton.tsx
  • src/app/(app)/[lang]/hirek/[slug]/lib/news-utils.ts
  • src/app/(app)/[lang]/kepviselok/components/RepresentativeCard.tsx
  • src/app/(app)/[lang]/kepviselok/components/RepresentativeModal.tsx
  • src/app/(app)/[lang]/kepviselok/components/RepresentativesGrid.tsx
  • src/app/(app)/[lang]/kepviselok/components/RepresentativesGridClient.tsx
  • src/app/(app)/[lang]/kepviselok/components/representatives.helpers.ts
  • src/app/(app)/[lang]/kepviselok/components/skeletons/LoadingRepresentativeCard.tsx
  • src/app/(app)/[lang]/kepviselok/components/skeletons/LoadingRepresentativesGrid.tsx
  • src/app/(app)/[lang]/kepviselok/page.tsx
  • src/app/(app)/[lang]/kisokosok/components/HelpPageList.tsx
  • src/app/(app)/[lang]/kisokosok/components/skeletons/LoadingHelpPageGrid.tsx
  • src/app/(app)/[lang]/kisokosok/page.tsx
  • src/app/(app)/[lang]/nyelvoktatas/components/LanguageEducationContent.tsx
  • src/app/(app)/[lang]/nyelvoktatas/page.tsx
  • src/app/(app)/[lang]/oktatasi-szabalyzatok/loading.tsx
  • src/app/(app)/[lang]/oktatasi-szabalyzatok/page.tsx
  • src/app/(app)/[lang]/tanulmanyi-osztondij/components/StudyScholarshipContent.tsx
  • src/app/(app)/[lang]/tanulmanyi-osztondij/page.tsx
  • src/app/(app)/globals.css
  • src/collections/News.ts
  • src/collections/Permissions.ts
  • src/components/common/Accordion.tsx
  • src/components/common/FileCard.tsx
  • src/components/regulations/RegulationsListClient.tsx
  • src/components/regulations/skeletons/LoadingRegulationsGrid.tsx
  • src/dictionaries/en/language_education.json
  • src/dictionaries/en/news.json
  • src/dictionaries/en/permissions.json
  • src/dictionaries/en/regulations.json
  • src/dictionaries/en/scholarships.json
  • src/dictionaries/hu/language_education.json
  • src/dictionaries/hu/news.json
  • src/dictionaries/hu/permissions.json
  • src/dictionaries/hu/regulations.json
  • src/dictionaries/hu/scholarships.json
  • src/globals/AcademicScholarshipFAQ.ts
  • src/lib/payload-cms.ts
  • src/migrations/20260619_105937_add_news_representative_and_contact_email.json
  • src/migrations/20260619_105937_add_news_representative_and_contact_email.ts
  • src/migrations/20260619_141957_add_permissions_submission_and_external_link.json
  • src/migrations/20260619_141957_add_permissions_submission_and_external_link.ts
  • src/migrations/20260619_173736_academic_scholarship_faq.json
  • src/migrations/20260619_173736_academic_scholarship_faq.ts
  • src/migrations/index.ts
  • src/payload-types.ts
  • src/payload.config.ts
💤 Files with no reviewable changes (5)
  • src/app/(app)/[lang]/hirek/[slug]/components/BackNav.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailClient.ts
  • src/app/(app)/[lang]/hirek/[slug]/components/ShareButton.tsx
  • src/app/(app)/[lang]/hirek/[slug]/lib/news-utils.ts
  • src/app/(app)/[lang]/hirek/[slug]/components/FileIcon.tsx

Comment on lines +46 to +49
<button
type="button"
onClick={() => router.back()}
className="group inline-flex items-center gap-2 bg-[#862633] hover:bg-[#9e2d3e] text-white border border-[#e9e2d6] rounded-full px-4 py-2 transition-colors duration-200 active:scale-95 cursor-pointer"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Verify whether other pages using router.back() already implement a fallback.
rg -nP --type=tsx '\brouter\.back\s*\(' src -C3

Repository: kir-dev/ehk

Length of output: 83


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for router.back usage without tsx type filter
rg -n 'router\.back' src -A 3 -B 3

# Also get the file structure to understand the layout
echo "=== File structure ===" 
find src -name "*.tsx" -o -name "*.ts" | head -30

Repository: kir-dev/ehk

Length of output: 4802


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the specific file mentioned in the review
cat -n src/app/\(app\)/\[lang\]/hirek/\[slug\]/components/NewsDetailMain.tsx | head -60

Repository: kir-dev/ehk

Length of output: 2885


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for router usage patterns in hirek-related files
rg -n 'router\.' src -t js -t ts --iglob '*hirek*' -C 2

Repository: kir-dev/ehk

Length of output: 37


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find the hirek page to confirm the list route
find src -path "*hirek*" -name "page.tsx" | head -10
cat -n src/app/\(app\)/\[lang\]/hirek/page.tsx 2>/dev/null | head -30

Repository: kir-dev/ehk

Length of output: 504


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check RepresentativesGridClient for similar context
cat -n src/app/\(app\)/\[lang\]/kepviselok/components/RepresentativesGridClient.tsx | sed -n '40,65p'

Repository: kir-dev/ehk

Length of output: 2170


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the main hirek page that corresponds to NewsDetailMain
ls -la src/app/\(app\)/\[lang\]/hirek/ 2>/dev/null
cat -n src/app/\(app\)/\[lang\]/hirek/page.tsx 2>/dev/null | head -40

Repository: kir-dev/ehk

Length of output: 222


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check PageHeader to see how it handles the fallback
cat -n src/components/common/PageHeader.tsx | head -50

Repository: kir-dev/ehk

Length of output: 1658


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check which hirek page imports NewsDetailMain
rg -n "NewsDetailMain" src --type ts --type tsx -B 2 -A 2

Repository: kir-dev/ehk

Length of output: 83


🏁 Script executed:

#!/bin/bash
set -euo pipefetch
# Check the news detail slug page to understand the routing context
cat -n src/app/\(app\)/\[lang\]/hirek/\[slug\]/page.tsx

Repository: kir-dev/ehk

Length of output: 110


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check which hirek page imports NewsDetailMain
rg -n "NewsDetailMain" src -B 2 -A 2

Repository: kir-dev/ehk

Length of output: 2064


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the news detail slug page to understand the routing context
cat -n "src/app/(app)/[lang]/hirek/[slug]/page.tsx"

Repository: kir-dev/ehk

Length of output: 1178


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if there's ANY hirek page at the root level
find src/app -name "*hirek*" -type d

Repository: kir-dev/ehk

Length of output: 407


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check the routing structure - are there any pages that list news?
rg -n "export.*function.*News" src --type ts -A 5 | head -50

Repository: kir-dev/ehk

Length of output: 3296


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check what the actual news list route is - look at all hirek pages
for f in src/app/*/\[lang\]/*/hirek/page.tsx src/app/\(app\)/\[lang\]/*/hirek/page.tsx; do
  if [ -f "$f" ]; then
    echo "=== $f ==="
    head -20 "$f"
  fi
done

Repository: kir-dev/ehk

Length of output: 19445


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if there's a main hirek page (not a category-specific one)
ls -la "src/app/(app)/[lang]/hirek/"
cat -n "src/app/(app)/[lang]/hirek/page.tsx" 2>/dev/null || echo "No main hirek page found"

Repository: kir-dev/ehk

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how NewsDetailMain is instantiated - does it receive any props about parent route?
cat -n "src/app/(app)/[lang]/hirek/[slug]/components/NewsDetail.tsx"

Repository: kir-dev/ehk

Length of output: 1345


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if there's referrer detection or any context management
rg -n "useSearchParams\|usePathname\|referrer" src/app/\(app\)/\[lang\]/hirek -A 2 -B 2

Repository: kir-dev/ehk

Length of output: 37


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify: does the news detail page have a way to know its source/category?
# Check what params are available in the slug page
cat -n "src/app/(app)/[lang]/hirek/[slug]/page.tsx" | tail -20

# Also check if there's any navigation/breadcrumb info passed
rg -n "searchParams\|params" "src/app/(app)/[lang]/hirek/[slug]/page.tsx" -A 2

Repository: kir-dev/ehk

Length of output: 665


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check if router.back() is used anywhere with a fallback pattern
rg -n "window.history" src -A 3 -B 3

Repository: kir-dev/ehk

Length of output: 37


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Understand the real concern: can users direct-enter news pages?
# Check if there are any navigation links TO the detail pages
rg -n "hirek.*slug\|hirek.*\[slug\]" src --type ts -B 2 -A 2 | head -40

Repository: kir-dev/ehk

Length of output: 37


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Let's understand: can we detect what category/section we came from?
# Check how news articles are linked from category pages
rg -n "Link.*hirek\|href.*hirek.*slug" src --type ts -B 2 -A 2 | head -60

Repository: kir-dev/ehk

Length of output: 37


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify if there are ANY valid fallback routes for news
find src/app -path "*hirek*" -name "page.tsx" -exec grep -l "NewsGrid\|News" {} \;

Repository: kir-dev/ehk

Length of output: 504


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Most importantly: check if window.history.length is actually a reliable method in Next.js
# and what best practices recommend for fallback navigation
rg -n "history.length\|history.back" src

Repository: kir-dev/ehk

Length of output: 37


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify the actual routing structure - what's the proper parent route for news?
# Check if there are any breadcrumbs or navigation structure that hints at parent
rg -n "breadcrumb\|Breadcrumb\|parent" src/app/\(app\)/\[lang\]/hirek -i

Repository: kir-dev/ehk

Length of output: 37


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Check how news is actually discoverable - what's the entry point?
# Look for navigation menus or site structure
rg -n "hirek" src/components/common -i -B 1 -A 1 | head -30

Repository: kir-dev/ehk

Length of output: 37


Back button fallback route is incorrect and will cause 404 errors.

Line 48 uses router.back() without a fallback, which can fail for direct entries. However, the proposed fallback to /{lang}/hirek is invalid—that page doesn't exist in the routing structure. The news detail page at /[lang]/hirek/[slug] is generic and doesn't know which category the article came from (kollegium, sport, etc.).

A viable fix requires either:

  • Passing the parent route as a prop to NewsDetailMain
  • Using useSearchParams() to preserve referrer information
  • Handling the case where no valid parent route exists (redirect to homepage or category index)

The window.history.length approach is sound, but the destination route must be valid or configurable.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx around lines
46 - 49, The onClick handler in the button element that calls router.back()
lacks a proper fallback route, and the hardcoded fallback to /{lang}/hirek is
invalid since that page doesn't exist in the routing structure. To fix this,
implement a fallback mechanism by either: (1) accepting a parentRoute prop in
the NewsDetailMain component and using it as the fallback destination when
router.back() fails, (2) using useSearchParams() to capture and preserve
referrer information from the parent route during navigation, or (3) gracefully
handling the case where no valid parent route is available by redirecting to a
valid fallback like the homepage or a category index. The onClick handler should
check window.history.length to determine if back navigation is safe, and use the
configured fallback route instead of the invalid /{lang}/hirek path.

Comment on lines +61 to +63
<Link
href={`/hirek/${article.id}`}
className="inline-flex items-center justify-between gap-4 text-xs font-bold text-[#862633]"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# 1) Confirm route shape for news detail pages
fd -a "page.tsx" src/app | rg "/hirek/"

# 2) Inspect how `slug` param is read in news detail page(s)
rg -nP --type=tsx "params.*slug|\\[slug\\]" src/app -C2

# 3) Compare all generated hrefs targeting /hirek/*
rg -nP --type=tsx 'href=\{`/hirek/\$\{[^}]+\}`\}|href="\/hirek\/' src -C2

Repository: kir-dev/ehk

Length of output: 740


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Read the RelatedNewsClient component around the flagged lines
echo "=== RelatedNewsClient.tsx context (lines 50-70) ==="
sed -n '50,70p' src/app/\(app\)/\[lang\]/hirek/\[slug\]/components/RelatedNewsClient.tsx

# 2) Read the slug/page.tsx to understand how slug is used
echo -e "\n=== [slug]/page.tsx content ==="
cat src/app/\(app\)/\[lang\]/hirek/\[slug\]/page.tsx

# 3) Check for middleware or i18n setup
echo -e "\n=== Middleware files ==="
find src -name "middleware.*" -o -name "*i18n*" -o -name "*locale*" | head -20

# 4) Search for other /hirek/ hrefs to see pattern
echo -e "\n=== Other /hirek/ href patterns ==="
rg 'href.*hirek' src/app --type ts --type tsx -B2 -A2 2>/dev/null | head -50

Repository: kir-dev/ehk

Length of output: 1909


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Read middleware to understand lang prefix handling
echo "=== middleware.ts ==="
cat src/middleware.ts

# 2) Read i18n-config to understand locale setup
echo -e "\n=== i18n-config.ts ==="
cat src/i18n-config.ts

# 3) Check the full RelatedNewsClient component for type info
echo -e "\n=== RelatedNewsClient.tsx full file ==="
cat src/app/\(app\)/\[lang\]/hirek/\[slug\]/components/RelatedNewsClient.tsx

# 4) Look for other news/article links with href patterns
echo -e "\n=== Search for other article/news hrefs ==="
rg 'href.*article|href.*news' src/app -A1 2>/dev/null | head -60

Repository: kir-dev/ehk

Length of output: 5112


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Verify useTranslate hook to confirm lang variable format
echo "=== useTranslate hook implementation ==="
find src -name "*useTranslate*" -type f
xargs cat 2>/dev/null | head -40

# 2) Verify lang variable case in RelatedNewsClient (is it 'EN'/'HU' or 'en'/'hu'?)
echo -e "\n=== Check i18n context/context for lang format ==="
rg "defaultLang|Lang" src/components/common/LanguageProvider.tsx -A3 -B3

Repository: kir-dev/ehk

Length of output: 2650


Fix missing locale segment in related news link to prevent unintended locale switching.

The href on line 62 omits the locale prefix: href={/hirek/${article.id}} should be href={/${lang.toLowerCase()}/hirek/${article.id}}.

When a user navigates from a localized page (e.g., /en/hirek/456) to a related article without a locale segment, the middleware auto-prepends the default locale (hu), causing the user to be redirected to /hu/hirek/... instead of /en/hirek/.... This unintentionally switches the user's language preference.

The lang variable is already available in the component via the useTranslate() hook; use it to construct the correct localized path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(app)/[lang]/hirek/[slug]/components/RelatedNewsClient.tsx around
lines 61 - 63, The Link component's href attribute is missing the locale prefix
which causes unintended locale switching when navigating to related articles. In
the RelatedNewsClient component where the Link element contains
href={`/hirek/${article.id}`}, modify the href to include the locale segment by
using the lang variable obtained from the useTranslate() hook. Update the href
to construct the path as href={`/${lang.toLowerCase()}/hirek/${article.id}`} to
ensure users remain on their current language version when clicking related news
links.

Comment on lines +27 to +41
<article
role="button"
tabIndex={0}
onClick={onClickAction}
onKeyDown={(event) => {
// Ignore key events bubbling up from nested interactive elements (e.g. the email link)
if (event.target !== event.currentTarget) return
if (event.key === "Enter" || event.key === " ") {
event.preventDefault()
onClickAction()
}
}}
className="group relative min-h-[31.6rem] cursor-pointer pt-16 focus-visible:outline-none"
aria-label={`${detailsLabel}: ${representative.name}`}
>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Avoid nested interactive semantics in the card container.

Line 28 makes the whole <article> a button role, but Lines 86-94 include nested email links. This creates conflicting interaction semantics and is an accessibility blocker for keyboard/screen-reader users.

Proposed fix
-        <article
-            role="button"
-            tabIndex={0}
-            onClick={onClickAction}
-            onKeyDown={(event) => {
-                // Ignore key events bubbling up from nested interactive elements (e.g. the email link)
-                if (event.target !== event.currentTarget) return
-                if (event.key === "Enter" || event.key === " ") {
-                    event.preventDefault()
-                    onClickAction()
-                }
-            }}
-            className="group relative min-h-[31.6rem] cursor-pointer pt-16 focus-visible:outline-none"
-            aria-label={`${detailsLabel}: ${representative.name}`}
-        >
+        <article
+            onClick={onClickAction}
+            className="group relative min-h-[31.6rem] cursor-pointer pt-16"
+        >
@@
-                <div className="w-full border-t border-[`#e9e2d6`] pt-4">
-                    <div className="flex h-[22px] items-center justify-between gap-4 font-open-sans text-sm font-semibold leading-[1.6] text-[`#862633`]">
+                <div className="w-full border-t border-[`#e9e2d6`] pt-4">
+                    <button
+                        type="button"
+                        onClick={(event) => {
+                            event.stopPropagation()
+                            onClickAction()
+                        }}
+                        aria-label={`${detailsLabel}: ${representative.name}`}
+                        className="flex h-[22px] w-full items-center justify-between gap-4 font-open-sans text-sm font-semibold leading-[1.6] text-[`#862633`] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[`#862633`] focus-visible:ring-offset-2"
+                    >
                         <span>{detailsLabel}</span>
                         <ArrowRight className="h-6 w-6 shrink-0 transition-transform duration-200 group-hover:translate-x-1 group-focus-visible:translate-x-1" />
-                    </div>
+                    </button>
                 </div>

Also applies to: 83-95, 100-104

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(app)/[lang]/kepviselok/components/RepresentativeCard.tsx around
lines 27 - 41, The article element in RepresentativeCard has role="button" but
contains nested interactive email links (lines 86-94), creating conflicting
accessibility semantics. Remove the role="button" attribute from the article
element and refactor the click handling by moving the onClickAction logic to a
wrapper div or restructuring the component so that the card container is not a
button, allowing the nested email links to be the primary interactive elements.
This will resolve the semantic conflict and improve keyboard and screen-reader
accessibility.

Comment on lines +6 to 9
params }: { params: Promise<{ lang: Locale }> }) {
const { lang } = await params;
const dictionary = await getDictionary(lang, 'language_education');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate route locale before dictionary lookup

At Line 8, getDictionary(lang, 'language_education') uses raw route params. An unsupported locale can crash the page (loaders[locale] is undefined). Align with the fallback pattern used in other pages.

Suggested fix
 import LanguageEducationContent from "`@/app/`(app)/[lang]/nyelvoktatas/components/LanguageEducationContent";
 import { getDictionary } from "`@/get-dictionary`";
-import { Locale } from "`@/i18n-config`";
+import { i18n, Locale } from "`@/i18n-config`";
 
 export default async function LanguageEducationPage({
-  params }: { params: Promise<{ lang: Locale }> }) {
+  params }: { params: Promise<{ lang: string }> }) {
   const { lang } = await params;
-  const dictionary = await getDictionary(lang, 'language_education');
+  const validLang = i18n.locales.includes(lang as Locale) ? (lang as Locale) : i18n.defaultLocale;
+  const dictionary = await getDictionary(validLang, "language_education");
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/`(app)/[lang]/nyelvoktatas/page.tsx around lines 6 - 9, The `lang`
parameter extracted from `params` in the page component is being passed directly
to getDictionary without validation, which can cause the page to crash if an
unsupported locale is provided since loaders[locale] would be undefined. Before
calling getDictionary(lang, 'language_education'), validate that the lang value
is a supported locale and apply a fallback pattern (consistent with other pages
in the application) to handle cases where an invalid locale is provided. This
ensures that getDictionary receives only valid locales from the loaders object.

Comment on lines +100 to +108
{
name: "externalLink",
type: "text",
required: false,
label: "Külső link (űrlap URL)",
admin: {
description: "Külső webes űrlaphoz tartozó URL (pl. \"Rendezvénybejelentő űrlap\"). Ha meg van adva, a kártya ezt nyitja meg fájl helyett.",
},
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate externalLink as a URL, not arbitrary text.

Line 102 defines a URL-intended field as plain text without validation. This allows invalid or unsafe values to reach the UI click target and can break navigation behavior.

Suggested schema hardening
         {
             name: "externalLink",
-            type: "text",
+            type: "text",
             required: false,
             label: "Külső link (űrlap URL)",
+            validate: (value: unknown) => {
+                if (value == null || value === "") return true;
+                if (typeof value !== "string") return "Érvénytelen URL.";
+                try {
+                    const url = new URL(value);
+                    if (!["http:", "https:"].includes(url.protocol)) {
+                        return "Csak http/https URL engedélyezett.";
+                    }
+                    return true;
+                } catch {
+                    return "Érvénytelen URL.";
+                }
+            },
             admin: {
                 description: "Külső webes űrlaphoz tartozó URL (pl. \"Rendezvénybejelentő űrlap\"). Ha meg van adva, a kártya ezt nyitja meg fájl helyett.",
             },
         },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/collections/Permissions.ts` around lines 100 - 108, The externalLink
field in the Permissions collection is defined with type "text" which allows
arbitrary text input without URL validation. Modify this field definition to add
URL validation by either changing the type to support URL validation or by
adding a validate function that checks if the provided value is a valid URL
format before allowing it to be saved, ensuring only proper URLs can be set for
this external link field.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement Redesigned Academic Scholarship (Tanulmányi ösztöndíj) Page and CMS Integration

1 participant