Skip to content

feat: add dynamic links page to replace Linktree#108

Merged
finxol merged 18 commits into
mainfrom
linktree-refactor-cherry-pick
Jun 11, 2026
Merged

feat: add dynamic links page to replace Linktree#108
finxol merged 18 commits into
mainfrom
linktree-refactor-cherry-pick

Conversation

@GreenGKH

@GreenGKH GreenGKH commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

La bonne pr c'est celle là

@dokploy-fare-project

dokploy-fare-project Bot commented Jun 8, 2026

Copy link
Copy Markdown

Dokploy Preview Deployment

Name Status Preview Updated (UTC)
front ✅ Done Preview URL 2026-06-11T23:29:01.423Z

@GreenGKH GreenGKH linked an issue Jun 9, 2026 that may be closed by this pull request
@finxol

finxol commented Jun 10, 2026

Copy link
Copy Markdown
Member

j’adore le nouveau style de la page 🔥

Comment thread src/components/dashboard/links/linksManager.tsx Outdated
@finxol

finxol commented Jun 11, 2026

Copy link
Copy Markdown
Member

Attention au responsive. Tu peux enlever des choses (par exemple le "x liens") quand c'est trop petit et les afficher uniquement à partir d'une certaine taille de container (hidden @md:block), regarde les container queries (normalement le parent contient déjà un @container)

Screenshot 2026-06-11 at 11 03 37

Comment thread src/components/dashboard/links/linksManager.tsx Outdated
Comment thread src/components/footer/socialLinks.tsx Outdated
@finxol

finxol commented Jun 11, 2026

Copy link
Copy Markdown
Member

je sais je suis chiant mais je suis pas entièrement satisfait du design côté dashboard. Je te fais une propal plus tard

@finxol

finxol commented Jun 11, 2026

Copy link
Copy Markdown
Member

@claude review this

@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown

Claude finished @finxol's task in 2m 36s —— View job


Code Review — Dynamic Links Page

Nice work — the feature is well structured and the server actions follow the repo conventions closely (withServerAction, tryCatch, permission gating, discriminated-union returns, revalidatePath on both /dashboard/liens and /liens). The PBAC wiring and the add:liens permission seeds are all in place. A few things below worth addressing.

🐛 Correctness

1. URL schema rejects relative internal links it claims to supportsrc/schemas/link.ts:14
url is validated with string.url, which requires an absolute URL (scheme + host). But the feature is clearly designed to also accept internal paths:

  • addLinkButton.tsx:96 placeholder: "ex. https://instagram.com/... ou /projets/agorae"
  • the public page (src/app/(public)/liens/page.tsx:56-73) has dedicated isExternal / newTabProps / getLinkIcon handling that returns the internal LinkIcon and omits target="_blank" precisely for relative URLs.

So a value like /projets/agorae will fail validation in both AddLinkSchema and EditLinkSchema, contradicting the placeholder and the public rendering logic. Either loosen the rule to accept absolute URLs or root-relative paths (and mailto:/tel:), or drop the relative-path affordance everywhere. Fix this →

⚠️ Tests required (per CLAUDE.md)

This PR adds 8 new server actions, a new schema (link.ts), and several form components, but ships no tests. The testing policy is explicit: "A PR that adds or changes behaviour without corresponding tests is incomplete." At minimum:

  • Node action tests in src/actions/links/__tests__/ covering the branch/IO matrix (invalid payload, auth denial, each permission denial, DB-failure branch, happy path + exact payload + revalidatePath). The category === null not-found branches in addLinkAction/editLinkAction are good things to assert.
  • Schema test in src/schemas/__tests__/link.test.ts — especially once the URL rule above is fixed (absolute vs relative vs invalid).
  • Browser test for at least one form (e.g. addLinkButton) — render, client validation blocks submit, valid submit payload, error UI.

🔎 Minor

  • Double space in labeladdLinkButton.tsx:65: Ajouter un {first && "premier"} lien renders "Ajouter un lien" (two spaces) when first is false. Consider Ajouter un {first ? "premier " : ""}lien.
  • Stale aria-labelmoveLinkCategoryButtons.tsx:50: aria-label="Media controls" looks like a copy-paste leftover; should describe reordering categories (e.g. "Réordonner la catégorie").
  • Row actions unreachable on touchsortableLinkCard.tsx:72: edit/delete are hidden ... group-hover:flex group-focus-within:flex. On touch devices (no hover) and with the drag handle capturing the pointer, these may never appear. Consider making them always visible at small container sizes.
  • Anonymous default componentsortableLinkCard.tsx:18: export default function (...) has no name; other components in this PR are named. A name improves React DevTools/stack traces.
  • isPending unusedaddLinkButton.tsx:22 destructures isPending from useTransition but only passes it to DialogForm… it does use it (line 74). Disregard — verified it's used.

✅ Looks good

  • All actions check getCurrentUserWithPermissions() + hasPermission before any IO, and validation/permission failures are early returns (not captureActionError-ed) — exactly per the guide.
  • Optimistic reorder with rollback on failure in sortableLinkList.tsx and moveLinkCategoryButtons.tsx is clean.
  • onDelete: Cascade on LinkItem.categoryId (migration + schema) correctly removes links when a category is deleted.
  • Public page filters out empty categories and degrades gracefully on DB error.

Re: the open thread on responsive (hidden @md:block container queries) and the dashboard design propal from @finxol — those are still pending and not addressed here, but they're stylistic follow-ups rather than blockers.
linktree-refactor-cherry-pick

@finxol

finxol commented Jun 11, 2026

Copy link
Copy Markdown
Member

@claude review this again

@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown

Claude finished @finxol's task in 2m 3s —— View job


Code Review — Dynamic Links Page (re-review)

Re-reviewed against the previous round. Almost everything I flagged before has been addressed cleanly, and the test coverage that was missing is now comprehensive and idiomatic. 👍

✅ Resolved since last review

  • URL validation is now consistent. Rather than loosening the rule, the relative-path affordance was removed everywhere — the placeholder is now just "ex. https://instagram.com/..." (addLinkButton.tsx:154, editLinkButton.tsx:96), and the public page keys "external" off !url.startsWith("https://fare-asso.fr/") (liens/page.tsx:56-57), so absolute internal links + mailto: still get the right icon/target. link.ts:14 keeps string.url with a clean custom message (URL requise vs URL invalide). Coherent end-to-end.
  • Tests are in and they're good. Node action tests use the shared itIsGatedBy gate helper + @/test/mocks + @/test/factories/links, and cover the full matrix: invalid payload, auth/permission denial, the category === null not-found branch, both DB-failure branches (lookup + insert) with captureActionError asserted once, and a happy path asserting exact findUnique/create payloads + both revalidatePath calls (addLinkAction.test.ts). Schema tests cover absolute/mailto/tel/relative/empty and that Edit shares the rule. A browser test exists for addLinkButton. This matches the CLAUDE.md policy.
  • Double space fixedaddLinkButton.tsx:57 now Ajouter un {first && "premier "}lien (single space when not first).
  • aria-label fixedmoveLinkCategoryButtons.tsx:50 now "Réordonner la catégorie".
  • Component is namedsortableLinkCard.tsx:22 is now function SortableLinkCard(...).
  • Dashboard card-stacking fixed (finxol's thread) — linksManager.tsx dropped the nested bg-card border ... wrapper; the h2 is now m-0 text-lg font-semibold and aligned.

🔎 Minor (non-blocking)

  • Row actions still hover-only on touchsortableLinkCard.tsx:76: edit/delete remain hidden ... group-hover:flex group-focus-within:flex. On touch devices there's no hover and the drag handle captures the pointer, so these can be unreachable. Worth making them always-visible below a container breakpoint (ties into finxol's container-query note). Fix this →
  • Permission model is intentional but worth a confirm — category create/edit/delete reuse create:lien / edit:lien / delete:lien (there's no separate *:lien-category permission). That's a fine simplification, just flagging it's deliberate. All four permissions (access:liens, create:lien, edit:lien, delete:lien) are seeded (prisma/seed.ts:346-367) and used consistently across actions, the dashboard page, and the sidebar.

⏳ Still open from the discussion threads (stylistic, your call)

  • finxol's responsive note (hidden @md:block container queries for the "x liens" count, screenshot at small widths) and the dashboard design propal — not blockers for correctness, but they're the remaining items on this PR.

Nothing here is a correctness blocker. Solid round. ✅
linktree-refactor-cherry-pick

@finxol

finxol commented Jun 11, 2026

Copy link
Copy Markdown
Member

@claude one last review before we merge

@claude

claude Bot commented Jun 11, 2026

Copy link
Copy Markdown

Claude finished @finxol's task in 1m 26s —— View job


Final review before merge — Dynamic Links Page

Re-read the current state of the branch (actions, schema, public page, dashboard components). Everything flagged in the previous two rounds is resolved, conventions are followed throughout, and the test matrix is comprehensive. No correctness blockers — this is good to merge.

✅ Verified clean

  • Server actions (addLinkAction.tsx, updateLinkOrderAction.tsx, …) all follow the guide exactly: getCurrentUserWithPermissions() + hasPermission guards as early returns, arktype validation, risky IO via tryCatch, captureActionError only on real exceptions, discriminated-union returns, and both revalidatePath("/dashboard/liens") + revalidatePath("/liens") after mutation. The category === null not-found branch in addLinkAction is handled and asserted in tests.
  • Schema/public-page coherence is consistent end-to-end: link.ts keeps string.url with clean custom messages, and liens/page.tsx keys external off !url.startsWith("https://fare-asso.fr/") so internal absolute links, mailto:, and PDFs get the right icon/target.
  • Permissions (access:liens, create:lien, edit:lien, delete:lien) seeded and used consistently across actions, dashboard page redirects, and the sidebar.
  • Optimistic reorder with rollback-on-error in both sortableLinkList.tsx and linksManager.tsx is clean; onDelete: Cascade on LinkItem.categoryId is correct.
  • Dashboard card-stacking (finxol's thread) resolved — linksManager.tsx:97 is now m-0 text-lg font-semibold with no nested card wrapper.

🔎 Minor (non-blocking, your call before/after merge)

  • Row actions still hover-only on touchsortableLinkCard.tsx:76 keeps hidden … group-hover:flex group-focus-within:flex. On touch devices (no hover, drag handle captures the pointer) edit/delete can be unreachable. Worth making them always-visible below a container breakpoint — this ties into finxol's container-query note. Fix this →
  • Anonymous default exportaddLinkButton.tsx:40 is export default function ({ … }) with no name. Other components in the PR (e.g. SortableLinkCard) were given names; naming this one improves DevTools/stack traces. Fix this →
  • Dead propaddLinkButton.tsx:59 passes disabled={false} to the trigger Button; it's a no-op and can be dropped.
  • isExternal host assumptionliens/page.tsx:57 treats only https://fare-asso.fr/ as internal, so a https://www.fare-asso.fr/… link would open in a new tab. Edge case, fine to leave.

⏳ Still open from the threads (stylistic)

  • finxol's responsive note — hide the "x liens" count etc. at small container widths via hidden @md:block container queries — remains the main pending polish item, not a blocker.

Nothing here blocks the merge. 🚀
linktree-refactor-cherry-pick

@finxol finxol merged commit 26c917e into main Jun 11, 2026
3 checks passed
@finxol finxol deleted the linktree-refactor-cherry-pick branch June 11, 2026 23:27
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.

Ajouter une page "liens"

2 participants