138 implement redesigned competitive teams - #178
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughAdds a Payload CMS collection for competitive teams, migrates legacy team data, exposes typed retrieval, and replaces the page with localized cards showing team details, events, contacts, galleries, and join links. ChangesCompetitive Teams
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant CompetitiveTeamsPage
participant getCompetitiveTeams
participant PayloadCMS
participant CompetitiveTeamCard
participant OrganizationCard
CompetitiveTeamsPage->>getCompetitiveTeams: request ordered teams
getCompetitiveTeams->>PayloadCMS: query competitive-teams
PayloadCMS-->>getCompetitiveTeams: return team documents
getCompetitiveTeams-->>CompetitiveTeamsPage: return CompetitiveTeam[]
CompetitiveTeamsPage->>CompetitiveTeamCard: pass team and locale
CompetitiveTeamCard->>OrganizationCard: pass localized card data
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (4)
src/app/(app)/[lang]/kozelet/versenycsapatok/competitiveTeamCard.utils.ts (1)
87-91: 🎯 Functional Correctness | 🔵 Trivial | 🏗️ Heavy liftUse a schema-backed umbrella flag.
Line 87 derives card semantics from the editable display name. Renaming MVK silently changes section labels and swaps activities/departments. Persist a stable
kindor boolean on the collection and use that instead.🤖 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]/kozelet/versenycsapatok/competitiveTeamCard.utils.ts around lines 87 - 91, Update isUmbrellaOrganization to use the schema-backed stable kind or boolean field on CompetitiveTeam instead of comparing or searching the editable team.name. Ensure the persisted umbrella classification continues to drive section labels and activity/department behavior even when the display name changes.src/collections/CompetitiveTeams.ts (2)
155-171: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAdmin labels render lowercase (
website URL,facebook URL).
name.replace("Url", " URL")yields un-capitalized labels that look out of place next to the other Hungarian labels. A small map is clearer and keeps thevalidatewiring.✏️ Suggested label map
- fields: [ - "websiteUrl", - "facebookUrl", - "instagramUrl", - "linkedinUrl", - "youtubeUrl", - ].map((name) => ({ - name, - label: name.replace("Url", " URL"), + fields: ( + [ + ["websiteUrl", "Weboldal URL"], + ["facebookUrl", "Facebook URL"], + ["instagramUrl", "Instagram URL"], + ["linkedinUrl", "LinkedIn URL"], + ["youtubeUrl", "YouTube URL"], + ] as const + ).map(([name, label]) => ({ + name, + label, type: "text" as const, validate: validateOptionalUrl, })),🤖 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/CompetitiveTeams.ts` around lines 155 - 171, Update the contacts field mapping in CompetitiveTeams to use an explicit label map with capitalized labels for website, Facebook, Instagram, LinkedIn, and YouTube while preserving each field name, text type, and validateOptionalUrl wiring.
44-201: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMerge the shared fields into a single factory.
CompetitiveTeamsandSpecializedCollegesdefine the same field names/structure, with only localized labels/placeholders differing. A parameterized factory would avoid duplicating field changes across both collections.🤖 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/CompetitiveTeams.ts` around lines 44 - 201, Extract the shared field definitions from CompetitiveTeams and SpecializedColleges into one parameterized field factory, using its argument to supply the differing localized labels or placeholders. Update both collection configs to consume the factory while preserving their existing field names, structure, validation, and collection-specific fields.src/migrations/20260726_203000_competitive_teams_collection.json (1)
4602-4607: 🩺 Stability & Availability | 🔵 Trivial
competitive_teams_gallery.image_idisNOT NULLbut its FK isON DELETE set null.Deleting a referenced
mediarow will raise a not-null violation rather than clearing the reference. This mirrors the existingclubs_gallery/specialized_colleges_gallerydefinitions, so it's a pre-existing Payload-generated pattern rather than something new here — worth tracking separately if media deletion is ever exercised in the admin.Also applies to: 4656-4669
🤖 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/20260726_203000_competitive_teams_collection.json` around lines 4602 - 4607, Track the schema inconsistency between competitive_teams_gallery.image_id being notNull and its foreign key using ON DELETE SET NULL as a separate follow-up; no change is required in this migration, and note that the same existing pattern appears in clubs_gallery and specialized_colleges_gallery.
🤖 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/migrations/20260726_203000_competitive_teams_collection.ts`:
- Around line 7-8: The competitive teams seed must be an immutable migration
snapshot rather than reading mutable dictionaries. In
src/migrations/20260726_203000_competitive_teams_collection.ts lines 7-8,
replace the enDictionary and huDictionary imports with inline seed data
containing the correct Formula Racing Team URL. In
src/migrations/20260726_204000_fix_frt_url.ts lines 20-28, remove the patch
migration if safe for existing environments; otherwise retain it but make its
down() method a no-op instead of restoring the invented URL.
- Around line 169-179: Normalize scheme-less HTTP-style URLs returned by
getContacts() before payload.create() seeds competitive-teams. Ensure
contacts.websiteUrl and the non-MVK joinUrl value are prefixed with an
appropriate scheme when needed, while preserving already valid URLs and existing
MVK behavior.
- Around line 163-167: Update the organization pairing loop to find the English
organization by matching its id rather than using the positional order index,
falling back to the Hungarian organization when no match exists. Also derive the
MVK flag from the organization id instead of assuming order zero, while
preserving the existing bme_motorsport id-based check.
In `@src/migrations/20260726_204000_fix_frt_url.ts`:
- Around line 7-8: The migration’s down() must not restore an invented
previousUrl or assume the seeded team name matches exactly. Make down() a no-op
if rollback fidelity cannot be guaranteed, and remove the hardcoded previousUrl
restoration path; otherwise, capture and restore the original social_links value
and use the exact seed identity.
---
Nitpick comments:
In `@src/app/`(app)/[lang]/kozelet/versenycsapatok/competitiveTeamCard.utils.ts:
- Around line 87-91: Update isUmbrellaOrganization to use the schema-backed
stable kind or boolean field on CompetitiveTeam instead of comparing or
searching the editable team.name. Ensure the persisted umbrella classification
continues to drive section labels and activity/department behavior even when the
display name changes.
In `@src/collections/CompetitiveTeams.ts`:
- Around line 155-171: Update the contacts field mapping in CompetitiveTeams to
use an explicit label map with capitalized labels for website, Facebook,
Instagram, LinkedIn, and YouTube while preserving each field name, text type,
and validateOptionalUrl wiring.
- Around line 44-201: Extract the shared field definitions from CompetitiveTeams
and SpecializedColleges into one parameterized field factory, using its argument
to supply the differing localized labels or placeholders. Update both collection
configs to consume the factory while preserving their existing field names,
structure, validation, and collection-specific fields.
In `@src/migrations/20260726_203000_competitive_teams_collection.json`:
- Around line 4602-4607: Track the schema inconsistency between
competitive_teams_gallery.image_id being notNull and its foreign key using ON
DELETE SET NULL as a separate follow-up; no change is required in this
migration, and note that the same existing pattern appears in clubs_gallery and
specialized_colleges_gallery.
🪄 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 Plus
Run ID: 411a63a9-bd7a-4fe0-a4f5-9707bd1218fa
📒 Files selected for processing (14)
src/app/(app)/[lang]/kozelet/versenycsapatok/CompetitiveTeamCard.tsxsrc/app/(app)/[lang]/kozelet/versenycsapatok/competitiveTeamCard.utils.tssrc/app/(app)/[lang]/kozelet/versenycsapatok/loading.tsxsrc/app/(app)/[lang]/kozelet/versenycsapatok/page.tsxsrc/collections/CompetitiveTeams.tssrc/dictionaries/en/competition_teams.jsonsrc/dictionaries/hu/competition_teams.jsonsrc/lib/payload-cms.tssrc/migrations/20260726_203000_competitive_teams_collection.jsonsrc/migrations/20260726_203000_competitive_teams_collection.tssrc/migrations/20260726_204000_fix_frt_url.tssrc/migrations/index.tssrc/payload-types.tssrc/payload.config.ts
| import enDictionary from "../dictionaries/en/competition_teams.json"; | ||
| import huDictionary from "../dictionaries/hu/competition_teams.json"; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift
Both migrations stem from one root cause: the seed's source of truth is mutable dictionary JSON containing a stale FRT URL. Because the seed reads src/dictionaries/{hu,en}/competition_teams.json at build time, the wrong Formula Racing Team URL had to be patched by a second migration whose down() can only guess the prior value.
src/migrations/20260726_203000_competitive_teams_collection.ts#L7-L8: inline the seed data as a literal constant in the migration (with the correct FRT URL) instead of importing the dictionaries, so the migration is an immutable snapshot.src/migrations/20260726_204000_fix_frt_url.ts#L20-L28: once the seed carries the correct URL, drop this patch migration, or keep it for already-migrated environments and makedown()a no-op rather than writing the inventedhttps://mvk.bme.huvalue.
📍 Affects 2 files
src/migrations/20260726_203000_competitive_teams_collection.ts#L7-L8(this comment)src/migrations/20260726_204000_fix_frt_url.ts#L20-L28
🤖 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/20260726_203000_competitive_teams_collection.ts` around lines
7 - 8, The competitive teams seed must be an immutable migration snapshot rather
than reading mutable dictionaries. In
src/migrations/20260726_203000_competitive_teams_collection.ts lines 7-8,
replace the enDictionary and huDictionary imports with inline seed data
containing the correct Formula Racing Team URL. In
src/migrations/20260726_204000_fix_frt_url.ts lines 20-28, remove the patch
migration if safe for existing environments; otherwise retain it but make its
down() method a no-op instead of restoring the invented URL.
| for (const [order, organization] of huOrganizations.entries()) { | ||
| const english = enOrganizations[order] ?? organization; | ||
| const contacts = getContacts(organization); | ||
| const isMvk = order === 0; | ||
| const isMotorsport = organization.id === "bme_motorsport"; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
HU/EN pairing relies on positional index and MVK on order === 0.
enOrganizations[order] assumes both dictionaries list the same organizations in the same order, and isMvk assumes source.mvk is always spread first. A reordered or shorter EN list silently seeds Hungarian text as the English presentation. Matching on organization.id (as already done for bme_motorsport) would be robust.
🔒 Suggested id-based pairing
- const english = enOrganizations[order] ?? organization;
+ const english =
+ (organization.id &&
+ enOrganizations.find((item) => item.id === organization.id)) ||
+ enOrganizations[order] ||
+ organization;📝 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.
| for (const [order, organization] of huOrganizations.entries()) { | |
| const english = enOrganizations[order] ?? organization; | |
| const contacts = getContacts(organization); | |
| const isMvk = order === 0; | |
| const isMotorsport = organization.id === "bme_motorsport"; | |
| for (const [order, organization] of huOrganizations.entries()) { | |
| const english = | |
| (organization.id && | |
| enOrganizations.find((item) => item.id === organization.id)) || | |
| enOrganizations[order] || | |
| organization; | |
| const contacts = getContacts(organization); | |
| const isMvk = order === 0; | |
| const isMotorsport = organization.id === "bme_motorsport"; |
🤖 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/20260726_203000_competitive_teams_collection.ts` around lines
163 - 167, Update the organization pairing loop to find the English organization
by matching its id rather than using the positional order index, falling back to
the Hungarian organization when no match exists. Also derive the MVK flag from
the organization id instead of assuming order zero, while preserving the
existing bme_motorsport id-based check.
| await payload.create({ | ||
| collection: "competitive-teams", | ||
| data: { | ||
| name: isMvk | ||
| ? "Műegyetemi Versenycsapat Közösség" | ||
| : organization.title, | ||
| presentation_hu: richTextDocument(organization.description), | ||
| presentation_en: richTextDocument(english.description), | ||
| contacts, | ||
| order, | ||
| ...(!isMvk && { joinUrl: contacts.websiteUrl }), |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
for f in src/dictionaries/hu/competition_teams.json src/dictionaries/en/competition_teams.json; do
echo "== $f"
jq -r '.competition_teams | [.mvk] + .teams | .[] | .social_links[]? | .url' "$f"
doneRepository: kir-dev/ehk
Length of output: 2651
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate file outline/size =="
wc -l src/migrations/20260726_203000_competitive_teams_collection.ts || true
ast-grep outline src/migrations/20260726_203000_competitive_teams_collection.ts --view expanded || true
echo "== migration relevant sections =="
sed -n '1,240p' src/migrations/20260726_203000_competitive_teams_collection.ts
echo "== locate collection and validateOptionalUrl =="
rg -n "competitive-teams|validateOptionalUrl|social_links|contacts|joinUrl" src -SRepository: kir-dev/ehk
Length of output: 41465
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== CompetitiveTeams field validation =="
sed -n '1,60p' src/collections/CompetitiveTeams.ts
echo "== competitiveTeams collection payload config usage =="
sed -n '230,260p' src/lib/payload-cms.ts
echo "== existing URL sanitizer/migrator if any =="
rg -n "validateOptionalUrl|sanitize|new URL\\(|protocol|joinUrl|contacts_website_url" src/collections src/migrations -SRepository: kir-dev/ehk
Length of output: 5956
🌐 Web query:
Payload CMS 3 create collection field hooks access hooks documentation
💡 Result:
In Payload CMS 3, hooks and access control are distinct features used for different purposes in your document lifecycle [1]. Hooks allow you to execute custom logic during specific lifecycle events [2][3], while access control defines which users have permission to perform operations on documents or fields [4][5]. Field-Level Hooks Field hooks run specifically on individual fields [3][6]. To implement them, use the hooks property within your field configuration [3][6]. Common field hooks include [3][7]: - beforeValidate: Used for data formatting or cleanup before validation [7]. - beforeChange: Useful for business logic modifications [7]. - afterChange: Appropriate for side effects after a field has been updated [3]. - afterRead: Frequently used for computed virtual fields [3][6]. Field-Level Access Control Field-level access control restricts specific fields within a document based on user roles or data criteria [5]. You define this using the access property inside a field's configuration [5]. Available operations include [5]: - create: Determines if a user can set the field value during creation [5]. - read: Controls if a user can view the field value [5]. - update: Controls if a user can modify the field value [5]. Collection-Level Hooks and Access Hooks and access can also be applied at the collection level. Collection hooks are defined in your CollectionConfig and trigger for the document lifecycle [2]. Collection access control defines global permissions for the entire collection (e.g., who can find, create, or update documents) [8]. Key Differences - Hooks: Execute code before or after operations (e.g., transforming data, triggering emails) [2][3]. - Access Control: Determine if an operation is permitted to proceed [4]. For implementation details, refer to the official Payload documentation for Collection Hooks [2], Field Hooks [3], and Access Control [4].
Citations:
- 1: https://payloadcms.com/docs/hooks/overview
- 2: https://payloadcms.com/docs/hooks/collections
- 3: https://payloadcms.com/docs/hooks/fields
- 4: https://payloadcms.com/docs/access-control/overview
- 5: https://github.com/payloadcms/payload/blob/v3.83.0/docs/access-control/fields.mdx
- 6: https://github.com/payloadcms/payload/blob/main/docs/fields/overview.mdx
- 7: https://github.com/payloadcms/skills/blob/main/skills/payload/reference/HOOKS.md
- 8: https://payloadcms.com/docs/access-control/collections
Pre-sanitize the URL values before seeding competitive-teams.
getContacts() copies values directly into contacts.websiteUrl, which later becomes joinUrl for non-MVK teams. For example, linktr.ee/bmefrt fails new URL() because it has no scheme, causing the validation error during payload.create() and leaving the DDL-applied collection partially populated. Normalize or prefix scheme-less HTTP-style URLs before writing fields validated by validateOptionalUrl.
🤖 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/20260726_203000_competitive_teams_collection.ts` around lines
169 - 179, Normalize scheme-less HTTP-style URLs returned by getContacts()
before payload.create() seeds competitive-teams. Ensure contacts.websiteUrl and
the non-MVK joinUrl value are prefixed with an appropriate scheme when needed,
while preserving already valid URLs and existing MVK behavior.
| const correctUrl = "https://frtbme.hu/"; | ||
| const previousUrl = "https://mvk.bme.hu"; |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
down() writes a guessed previous value rather than restoring the original.
previousUrl is hardcoded to https://mvk.bme.hu, which is the MVK umbrella site, not necessarily what the FRT row held before up() ran (the seed derives it from social_links, and it may have been NULL). Rolling back therefore corrupts the row instead of restoring it. Also note the WHERE "name" = 'BME Formula Racing Team' predicate silently matches zero rows if the seeded title differs by a character.
If a rollback fidelity guarantee isn't needed here, a no-op down() would be more honest than writing an invented URL.
Also applies to: 20-28
🤖 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/20260726_204000_fix_frt_url.ts` around lines 7 - 8, The
migration’s down() must not restore an invented previousUrl or assume the seeded
team name matches exactly. Make down() a no-op if rollback fidelity cannot be
guaranteed, and remove the hardcoded previousUrl restoration path; otherwise,
capture and restore the original social_links value and use the exact seed
identity.
Summary by CodeRabbit