Skip to content

118 implement redesigned educational regulations oktatási szabályzatok page#155

Open
DankaMarci wants to merge 7 commits into
mainfrom
118-implement-redesigned-educational-regulations-oktatási-szabályzatok-page

Hidden character warning

The head ref may contain hidden characters: "118-implement-redesigned-educational-regulations-oktat\u00e1si-szab\u00e1lyzatok-page"
Open

118 implement redesigned educational regulations oktatási szabályzatok page#155
DankaMarci wants to merge 7 commits into
mainfrom
118-implement-redesigned-educational-regulations-oktatási-szabályzatok-page

Conversation

@DankaMarci

@DankaMarci DankaMarci commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • News articles now display representative contact information and email address
    • Permissions can now include external submission links
    • Permissions display submission process details in rich text format
  • Style

    • Improved news article detail layout with simplified header and contact section
    • Enhanced permission list UI with better empty state display
    • Redesigned representative modal with improved responsive layout
    • Updated file and attachment card styling across pages
    • Refined empty state messages with clearer visual hierarchy

- 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.
@DankaMarci DankaMarci requested a review from peterlipt June 19, 2026 17:09
@DankaMarci DankaMarci self-assigned this Jun 19, 2026
@DankaMarci DankaMarci linked an issue Jun 19, 2026 that may be closed by this pull request
4 tasks
@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 5:09pm

Request Review

@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR extends the News collection with representative/contactEmail fields and the Permissions collection with submissionProcess_hu/en richtext and externalLink fields, backed by two database migrations. It rebuilds the news detail, permissions list, regulations list, decisions archive, and representatives grid/card/modal UIs to use new helpers, EmptyState, an extended FileCard (adding actionType="open" and extensionLabel), and updated dictionary keys.

Changes

Schema, Migrations, and FileCard Extension

Layer / File(s) Summary
News and Permissions collection schema and generated types
src/collections/News.ts, src/collections/Permissions.ts, src/payload-types.ts
News gains optional representative relation and contactEmail; Permissions gains submissionProcess_hu/en richtext and externalLink; payload-types regenerated with new fields and select projections.
Database migrations
src/migrations/20260619_105937_*.ts, src/migrations/20260619_141957_*.ts, src/migrations/index.ts
Migrations add representative_id (FK + index) and contact_email to news, and two JSONB plus one varchar column to permissions; both registered in the migrations index.
FileCard: actionType="open" and extensionLabel
src/components/common/FileCard.tsx
Props expanded with "open" action type and extensionLabel override; getFileInfo routes extension text through the override; "open" renders an anchor with target="_blank".

Representatives Helpers, Card, Modal, Grid, and Skeletons

Layer / File(s) Summary
representatives.helpers module
src/app/(app)/[lang]/kepviselok/components/representatives.helpers.ts
New module exports RepresentativeLang, facultyStyles, getPrimaryPosition, getPositionText, getRepresentativePicture, getRepresentativeInitials, getFileInfo, and formatFileSize.
RepresentativeCard redesign
src/app/(app)/[lang]/kepviselok/components/RepresentativeCard.tsx
Rewritten as accessible article with role="button", keyboard handling, helper-driven picture/initials/faculty/position rendering, and a static ArrowRight details footer.
RepresentativeModal redesign
src/app/(app)/[lang]/kepviselok/components/RepresentativeModal.tsx
Rewritten with custom back-button header, mobile-first and desktop image/initials/name/position/email/faculty blocks, and direct anchor links for file reports replacing FileCard.
RepresentativesGrid/GridClient and page layout
src/app/(app)/[lang]/kepviselok/components/RepresentativesGrid.tsx, ...RepresentativesGridClient.tsx, ...page.tsx
Grid and client accept new title/description props; client renders back button, styled header, restyled search, and responsive grid; page removes PageHeader and passes dictionary-derived props.
Representatives skeleton updates
src/app/(app)/[lang]/kepviselok/components/skeletons/Loading*.tsx
LoadingRepresentativeCard and LoadingRepresentativesGrid rebuilt to match new layouts with hex colors and a reduced card count of 6.

News Detail Page Redesign and Contacts Sidebar

Layer / File(s) Summary
News dictionary additions
src/dictionaries/en/news.json, src/dictionaries/hu/news.json
EN/HU news dictionaries gain "back" and "contacts" keys.
Removed news-detail files
src/app/(app)/[lang]/hirek/[slug]/components/NewsDetail.tsx
BackNav import and grid layout removed; layout changed to flex/aside structure.
NewsDetailMain redesign
src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx
Replaces Card/share/reading-time layout with back-button bar, dark header, UTC-aware date formatter, uppercase tag spans, RichText body, and FileCard attachments with actionType="view".
NewsDetailSidebar as contacts panel
src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailSidebar.tsx
Rewritten to derive representative/contactEmail from article, returns null when absent, renders a contacts section with position text and mailto links.
RelatedNewsClient redesign
src/app/(app)/[lang]/hirek/[slug]/components/RelatedNewsClient.tsx
Removes card/badge/routing dependencies, rewrites formatDate to use UTC components, simplifies tags to translated labels, and drops the "view all news" CTA.

Permissions, Regulations, and Decisions UI Updates

Layer / File(s) Summary
Permissions and regulations dictionaries
src/dictionaries/*/permissions.json, src/dictionaries/*/regulations.json
EN/HU permissions gain submission_process key; EN/HU regulations rename decisions.title.
PermissionsListClient and page
src/app/(app)/[lang]/engedelyek/components/PermissionsListClient.tsx, ...page.tsx
Rebuilt with hasRichText helper, EmptyState, article-based items with RichText and conditional submissionProcess section, and attachment rendering preferring externalLink (actionType="open") over media file.
RegulationsListClient and skeleton
src/components/regulations/RegulationsListClient.tsx, .../skeletons/LoadingRegulationsGrid.tsx, src/app/(app)/[lang]/oktatasi-szabalyzatok/...
RegulationsListClient uses EmptyState and article elements with RichText/FileCard; skeleton drops spinner and renders static cards; page updates background to bg-[#f9f4f0].
DecisionsArchiveClient update
src/app/(app)/[lang]/hatarozatok-tara/components/DecisionsArchiveClient.tsx
Switches to useLanguage for language-aware title, uses EmptyState, changes PageHeader to subtitle prop, removes secondaryText from FileCard, and updates list to flex flex-col gap-4.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • kir-dev/ehk#150: Both PRs modify src/components/common/FileCard.tsx — the retrieved PR rewrites view/download behavior while this PR adds actionType="open" and extensionLabel consumed by the permissions and regulations UIs.

Suggested reviewers

  • peterlipt

Poem

🐰 Hop hop, new fields appear,
migrations dig the columns clear.
FileCard opens links anew,
with "open" type and label too.
Cards redesigned, old code is gone —
the EHK repo hops right on! 🌸

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.33% 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 title accurately reflects the main objective of this PR: implementing a redesigned educational regulations page (oktatási szabályzatok). However, the change scope extends significantly beyond just the regulations page.
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 118-implement-redesigned-educational-regulations-oktatási-szabályzatok-page

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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/(app)/[lang]/engedelyek/page.tsx (1)

14-19: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate lang before dictionary/provider usage.

Line 14–19 currently trust-casts lang; an unsupported locale can break dictionary loading and provider language state at runtime. Mirror the validLang guard pattern used in the regulations page.

Suggested fix
 export default async function PermissionsPage({
   params }: { params: Promise<{ lang: string }> }) {
   const { lang } = await params;
-  const dictionary = await getDictionary(lang as "hu" | "en", "permissions");
+  const validLang =
+    i18n.locales.includes(lang as "hu" | "en")
+      ? (lang as "hu" | "en")
+      : i18n.defaultLocale;
+  const dictionary = await getDictionary(validLang, "permissions");
 
   return (
-    <LanguageProvider defaultLang={(lang as string).toUpperCase() as "HU" | "EN"} dictionary={dictionary}>
+    <LanguageProvider defaultLang={validLang.toUpperCase() as "HU" | "EN"} dictionary={dictionary}>
       <div className="min-h-screen bg-gray-50">
🤖 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]/engedelyek/page.tsx around lines 14 - 19, The lang
parameter is being trust-cast without validation before being used in
getDictionary and LanguageProvider, which could break dictionary loading and
language state if an unsupported locale is passed. Add a validLang guard check
(mirroring the pattern used in the regulations page) to validate that lang is
either "hu" or "en" before passing it to getDictionary and LanguageProvider.
This should validate lang as soon as it's destructured from params and handle
invalid values appropriately.
🧹 Nitpick comments (1)
src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailMain.tsx (1)

29-38: 💤 Low value

Consider extracting formatDate to a shared utility.

This date formatting logic is duplicated in RelatedNewsClient.tsx (lines 12-21). Extracting it to a shared utility (e.g., in src/lib/utils.ts alongside translateTags) would improve maintainability and ensure consistent date formatting across the news detail components.

🤖 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, which reduces maintainability. Extract the formatDate
function from NewsDetailMain.tsx and move it to src/lib/utils.ts alongside other
shared utilities like translateTags. Then import this shared formatDate function
in both NewsDetailMain.tsx and RelatedNewsClient.tsx, removing the duplicate
function definitions from both files. This ensures consistent date formatting
logic across components and makes future updates easier.
🤖 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]/hatarozatok-tara/components/DecisionsArchiveClient.tsx:
- Around line 37-41: The getDecisions() function in src/lib/payload-cms.ts is
missing the depth: 1 parameter in its payload.find() query, which causes the
file relation to remain unpopulated as a numeric ID instead of a full object.
This breaks the FileCard component in DecisionsArchiveClient.tsx which expects a
properly populated file object. Add depth: 1 to the payload.find() call in the
getDecisions() function, following the same pattern used in similar functions
like getRepresentatives(), getPermissions(), and getAcademicRegulations() which
all include this parameter to properly populate their relations.

In `@src/app/`(app)/[lang]/kepviselok/components/representatives.helpers.ts:
- Around line 82-99: The formatFileSize function does not validate for negative
byte values, which causes Math.log(bytes) to return NaN, resulting in malformed
output. Add a check in the formatFileSize function to handle negative bytes
values (bytes < 0) before the logarithm calculation, returning null as a safe
fallback for invalid negative inputs, similar to how the function handles
undefined, null, and NaN values.

In `@src/collections/Permissions.ts`:
- Around line 100-108: The externalLink field in the Permissions collection
accepts free-form text without validation, which creates a potential XSS
vulnerability when the URL is rendered as an href attribute in anchors. Add a
validate function to the externalLink field configuration that checks the URL
scheme at write-time, allowing only safe schemes like http and https while
rejecting javascript:, data:, and other dangerous protocols. This validation
should be applied before the data is persisted to prevent malicious payloads
from being stored.

In
`@src/migrations/20260619_141957_add_permissions_submission_and_external_link.ts`:
- Around line 5-7: The migration file contains column names that do not follow
the ORM's camelCase-to-snake_case mapping convention. The columns
submissionprocess_hu and submissionprocess_en are missing the underscore
separator between "submission" and "process", while the corresponding fields in
Permissions.ts are defined as submissionProcess_hu and submissionProcess_en. The
ORM adapter will convert these camelCase field names to submission_process_hu
and submission_process_en (with underscore), so the migration columns must match
this pattern. Rename both submissionprocess_hu and submissionprocess_en to
submission_process_hu and submission_process_en respectively in both the up and
down migration statements.

---

Outside diff comments:
In `@src/app/`(app)/[lang]/engedelyek/page.tsx:
- Around line 14-19: The lang parameter is being trust-cast without validation
before being used in getDictionary and LanguageProvider, which could break
dictionary loading and language state if an unsupported locale is passed. Add a
validLang guard check (mirroring the pattern used in the regulations page) to
validate that lang is either "hu" or "en" before passing it to getDictionary and
LanguageProvider. This should validate lang as soon as it's destructured from
params and handle invalid values appropriately.

---

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, which reduces maintainability. Extract the formatDate
function from NewsDetailMain.tsx and move it to src/lib/utils.ts alongside other
shared utilities like translateTags. Then import this shared formatDate function
in both NewsDetailMain.tsx and RelatedNewsClient.tsx, removing the duplicate
function definitions from both files. This ensures consistent date formatting
logic across components and makes future updates easier.
🪄 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: cce014b9-cbb8-4f9e-bbb1-1e077aa2b47a

📥 Commits

Reviewing files that changed from the base of the PR and between 6844674 and 5e0dd23.

📒 Files selected for processing (39)
  • 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]/oktatasi-szabalyzatok/loading.tsx
  • src/app/(app)/[lang]/oktatasi-szabalyzatok/page.tsx
  • src/collections/News.ts
  • src/collections/Permissions.ts
  • src/components/common/FileCard.tsx
  • src/components/regulations/RegulationsListClient.tsx
  • src/components/regulations/skeletons/LoadingRegulationsGrid.tsx
  • src/dictionaries/en/news.json
  • src/dictionaries/en/permissions.json
  • src/dictionaries/en/regulations.json
  • src/dictionaries/hu/news.json
  • src/dictionaries/hu/permissions.json
  • src/dictionaries/hu/regulations.json
  • 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/index.ts
  • src/payload-types.ts
💤 Files with no reviewable changes (5)
  • src/app/(app)/[lang]/hirek/[slug]/lib/news-utils.ts
  • src/app/(app)/[lang]/hirek/[slug]/components/BackNav.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/ShareButton.tsx
  • src/app/(app)/[lang]/hirek/[slug]/components/NewsDetailClient.ts
  • src/app/(app)/[lang]/hirek/[slug]/components/FileIcon.tsx

Comment on lines 37 to +41
<FileCard
key={decision.id}
file={decision.file}
title={title}
secondaryText={secondary}
actionType="view"
actionType="view"

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

echo "== Find Decisions list/server loader files =="
fd -i 'decisions|hatarozat|archive' src app

echo
echo "== Locate Decision queries and check relation depth/select =="
rg -n -C3 'find\\(|collection:\\s*["'\'']decisions["'\'']|get.*Decision|loader\\s*=|file\\s*:' src app

echo
echo "== Inspect where DecisionsArchiveClient is fed data =="
rg -n -C3 'DecisionsArchiveClient\\s*\\(|<DecisionsArchiveClient' src app

Repository: kir-dev/ehk

Length of output: 664


🏁 Script executed:

cat -n src/collections/Decisions.ts

Repository: kir-dev/ehk

Length of output: 1281


🏁 Script executed:

cat -n src/app/(app)/[lang]/hatarozatok-tara/components/DecisionsArchive.tsx

Repository: kir-dev/ehk

Length of output: 219


🏁 Script executed:

cat -n src/app/(app)/[lang]/hatarozatok-tara/components/DecisionsArchiveClient.tsx

Repository: kir-dev/ehk

Length of output: 225


🏁 Script executed:

cat -n 'src/app/(app)/[lang]/hatarozatok-tara/components/DecisionsArchive.tsx'

Repository: kir-dev/ehk

Length of output: 411


🏁 Script executed:

cat -n 'src/app/(app)/[lang]/hatarozatok-tara/components/DecisionsArchiveClient.tsx'

Repository: kir-dev/ehk

Length of output: 1913


🏁 Script executed:

find src -name '*payload-cms*' -o -name '*payload*' | grep -E '\.(ts|tsx)$' | head -20

Repository: kir-dev/ehk

Length of output: 121


🏁 Script executed:

rg -l 'getDecisions' src

Repository: kir-dev/ehk

Length of output: 148


🏁 Script executed:

cat -n 'src/components/common/FileCard.tsx'

Repository: kir-dev/ehk

Length of output: 6669


🏁 Script executed:

cat -n src/lib/payload-cms.ts

Repository: kir-dev/ehk

Length of output: 6790


🏁 Script executed:

head -100 src/payload-types.ts

Repository: kir-dev/ehk

Length of output: 2709


Add depth: 1 to getDecisions() query to populate the file relation.

The getDecisions() function in src/lib/payload-cms.ts (line 75-82) is missing the depth: 1 parameter. Without it, the file relation remains unpopulated as a numeric ID, causing FileCard to render a disabled link. Compare with similar functions like getRepresentatives() (line 58), getPermissions() (line 90), and getAcademicRegulations() (line 100), which all include depth: 1 to populate relations. Update the query to:

const decisions = await payload.find({
  collection: "decisions",
  depth: 1,
  limit: 1000 });
🤖 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]/hatarozatok-tara/components/DecisionsArchiveClient.tsx
around lines 37 - 41, The getDecisions() function in src/lib/payload-cms.ts is
missing the depth: 1 parameter in its payload.find() query, which causes the
file relation to remain unpopulated as a numeric ID instead of a full object.
This breaks the FileCard component in DecisionsArchiveClient.tsx which expects a
properly populated file object. Add depth: 1 to the payload.find() call in the
getDecisions() function, following the same pattern used in similar functions
like getRepresentatives(), getPermissions(), and getAcademicRegulations() which
all include this parameter to properly populate their relations.

Comment on lines +82 to +99
export function formatFileSize(bytes?: number | null) {
if (bytes === undefined || bytes === null || Number.isNaN(bytes)) {
return null;
}

if (bytes === 0) {
return "0 B";
}

const units = ["B", "KB", "MB", "GB"];
const unitIndex = Math.min(
Math.floor(Math.log(bytes) / Math.log(1024)),
units.length - 1,
);
const size = bytes / 1024 ** unitIndex;

return `${parseFloat(size.toFixed(1))} ${units[unitIndex]}`;
}

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 | 🟡 Minor | ⚡ Quick win

Handle invalid negative byte values before logarithm.

Math.log(bytes) on negative values yields NaN, which renders malformed output (NaN undefined) instead of a safe fallback.

💡 Suggested fix
 export function formatFileSize(bytes?: number | null) {
-  if (bytes === undefined || bytes === null || Number.isNaN(bytes)) {
+  if (
+    bytes === undefined ||
+    bytes === null ||
+    !Number.isFinite(bytes) ||
+    bytes < 0
+  ) {
     return null;
   }
 
   if (bytes === 0) {
     return "0 B";
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function formatFileSize(bytes?: number | null) {
if (bytes === undefined || bytes === null || Number.isNaN(bytes)) {
return null;
}
if (bytes === 0) {
return "0 B";
}
const units = ["B", "KB", "MB", "GB"];
const unitIndex = Math.min(
Math.floor(Math.log(bytes) / Math.log(1024)),
units.length - 1,
);
const size = bytes / 1024 ** unitIndex;
return `${parseFloat(size.toFixed(1))} ${units[unitIndex]}`;
}
export function formatFileSize(bytes?: number | null) {
if (
bytes === undefined ||
bytes === null ||
!Number.isFinite(bytes) ||
bytes < 0
) {
return null;
}
if (bytes === 0) {
return "0 B";
}
const units = ["B", "KB", "MB", "GB"];
const unitIndex = Math.min(
Math.floor(Math.log(bytes) / Math.log(1024)),
units.length - 1,
);
const size = bytes / 1024 ** unitIndex;
return `${parseFloat(size.toFixed(1))} ${units[unitIndex]}`;
}
🤖 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/representatives.helpers.ts around
lines 82 - 99, The formatFileSize function does not validate for negative byte
values, which causes Math.log(bytes) to return NaN, resulting in malformed
output. Add a check in the formatFileSize function to handle negative bytes
values (bytes < 0) before the logarithm calculation, returning null as a safe
fallback for invalid negative inputs, similar to how the function handles
undefined, null, and NaN values.

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 URL scheme at write-time.

externalLink is stored as free-form text but later rendered into an anchor href via FileCard open mode. Without validation, javascript:/data: payloads can become stored XSS vectors.

🔒 Suggested fix
         {
             name: "externalLink",
             type: "text",
             required: false,
             label: "Külső link (űrlap URL)",
+            validate: (value: string | null | undefined) => {
+                if (!value) return true
+                try {
+                    const parsed = new URL(value.trim())
+                    return ["http:", "https:"].includes(parsed.protocol)
+                        ? true
+                        : "Csak http/https URL engedélyezett."
+                } catch {
+                    return "Érvényes URL-t adj meg (http/https)."
+                }
+            },
             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 accepts free-form text without validation,
which creates a potential XSS vulnerability when the URL is rendered as an href
attribute in anchors. Add a validate function to the externalLink field
configuration that checks the URL scheme at write-time, allowing only safe
schemes like http and https while rejecting javascript:, data:, and other
dangerous protocols. This validation should be applied before the data is
persisted to prevent malicious payloads from being stored.

Comment on lines +5 to +7
ALTER TABLE "permissions" ADD COLUMN "submissionprocess_hu" jsonb;
ALTER TABLE "permissions" ADD COLUMN "submissionprocess_en" jsonb;
ALTER TABLE "permissions" ADD COLUMN "external_link" varchar;`)

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

echo "== Field names defined in collections =="
rg -n --type ts 'name:\s*"submissionProcess_(hu|en)"|name:\s*"externalLink"|name:\s*"contactEmail"' src/collections

echo
echo "== Column names used in migrations =="
rg -n --type ts '"submissionprocess_(hu|en)"|"submission_process_(hu|en)"|"external_link"|"contact_email"' src/migrations

echo
echo "Expected: consistent camelCase->snake_case mapping across migrations."

Repository: kir-dev/ehk

Length of output: 1637


Column names in migration must match ORM field mapping convention.

The migration uses submissionprocess_hu/en while the field definitions in Permissions.ts are submissionProcess_hu/en. This breaks the camelCase-to-snake_case mapping pattern—the adapter will map submissionProcess_* to submission_process_* (with underscore), not submissionprocess_*. The same migration correctly maps externalLink to external_link and the prior migration maps contactEmail to contact_email. Rename the columns in both up and down migrations to submission_process_hu/en.

Suggested fix
-  ALTER TABLE "permissions" ADD COLUMN "submissionprocess_hu" jsonb;
-  ALTER TABLE "permissions" ADD COLUMN "submissionprocess_en" jsonb;
+  ALTER TABLE "permissions" ADD COLUMN "submission_process_hu" jsonb;
+  ALTER TABLE "permissions" ADD COLUMN "submission_process_en" jsonb;
   ALTER TABLE "permissions" ADD COLUMN "external_link" varchar;`)
...
-  ALTER TABLE "permissions" DROP COLUMN "submissionprocess_hu";
-  ALTER TABLE "permissions" DROP COLUMN "submissionprocess_en";
+  ALTER TABLE "permissions" DROP COLUMN "submission_process_hu";
+  ALTER TABLE "permissions" DROP COLUMN "submission_process_en";
   ALTER TABLE "permissions" DROP COLUMN "external_link";`)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ALTER TABLE "permissions" ADD COLUMN "submissionprocess_hu" jsonb;
ALTER TABLE "permissions" ADD COLUMN "submissionprocess_en" jsonb;
ALTER TABLE "permissions" ADD COLUMN "external_link" varchar;`)
ALTER TABLE "permissions" ADD COLUMN "submission_process_hu" jsonb;
ALTER TABLE "permissions" ADD COLUMN "submission_process_en" jsonb;
ALTER TABLE "permissions" ADD COLUMN "external_link" varchar;`)
🤖 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/migrations/20260619_141957_add_permissions_submission_and_external_link.ts`
around lines 5 - 7, The migration file contains column names that do not follow
the ORM's camelCase-to-snake_case mapping convention. The columns
submissionprocess_hu and submissionprocess_en are missing the underscore
separator between "submission" and "process", while the corresponding fields in
Permissions.ts are defined as submissionProcess_hu and submissionProcess_en. The
ORM adapter will convert these camelCase field names to submission_process_hu
and submission_process_en (with underscore), so the migration columns must match
this pattern. Rename both submissionprocess_hu and submissionprocess_en to
submission_process_hu and submission_process_en respectively in both the up and
down migration statements.

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 Educational Regulations (Oktatási Szabályzatok) Page

1 participant