Skip to content

feat: add manager profile pages#618

Draft
Seranged wants to merge 10 commits into
developmentfrom
feature/lite-256-dedicated-profile-page-for-curators-risk-managers
Draft

feat: add manager profile pages#618
Seranged wants to merge 10 commits into
developmentfrom
feature/lite-256-dedicated-profile-page-for-curators-risk-managers

Conversation

@Seranged

@Seranged Seranged commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add dedicated manager profile pages so curator and risk manager entities have an app-native destination.
  • Link single-entity curator/risk-manager displays from vault surfaces into the profile page.

Changes

  • Adds a /managers/[slug] route with entity details, socials, managed products, and managed vault lists.
  • Adds shared manager profile helpers and a reusable manager entity link component.
  • Keeps collapsed multi-manager labels as plain text to avoid routing ambiguous labels to only one profile.
  • Waits for vault loading state before showing empty managed-vault results.

Test plan

  • npm run test:run -- tests/utils/manager-profile.test.ts
  • npx eslint components/entities/manager/ManagerEntityLink.vue composables/useEulerManagerProfile.ts
  • npm run typecheck
  • npm run build
  • curl -sSI 'http://127.0.0.1:3000/managers/k3?network=ethereum'

Summary by CodeRabbit

  • New Features

    • Added manager profile pages with summaries, social links, managed products, and managed vault lists.
    • Introduced a reusable manager/entity link component for consistent linking and display across the app.
    • Added manager profile helper utilities for display names, profile paths, and entity matching.
  • Bug Fixes

    • Updated risk manager and curator displays to use a single, consistent link experience instead of mixed avatar/text rendering.
  • Tests

    • Added coverage for manager profile helper behavior and URL generation.

Add profile pages for label entities and link risk manager/curator displays into the new route.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 24, 2026 11:59 Destroyed
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2cdf5ac9-f455-4bb9-8f18-443680b4b95e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces a manager profile feature: new utility helpers (utils/manager-profile.ts), a useEulerManagerProfile composable, a ManagerEntityLink Vue component, and a pages/managers/[slug].vue profile page. Simultaneously refactors all vault item and overview components to replace inline BaseAvatar+text/link risk-manager rendering with ManagerEntityLink.

Changes

Manager Profile Feature

Layer / File(s) Summary
Manager profile utility helpers and tests
utils/manager-profile.ts, tests/utils/manager-profile.test.ts
Adds five exported helpers: entity key normalization, managed-by detection, slug resolution, display name formatting, and profile path construction; covered by a Vitest suite with five test cases.
useEulerManagerProfile composable
composables/useEulerManagerProfile.ts
New composable that accepts a reactive slug, computes the target entity, filtered/sorted EVault/Securitize/EarnVault lists and product entries via store lookups, aggregates vaultCount, and exposes isLoading.
ManagerEntityLink component
components/entities/manager/ManagerEntityLink.vue
New link/label component accepting entities, label, avatar/text/linking toggles, and data attributes; derives primarySlug and isLinked, then renders BaseAvatar plus a conditional span, NuxtLink, or plain span.
Manager profile page
pages/managers/[slug].vue
New Nuxt page driven by useEulerManagerProfile; renders manager identity, social links, product entries with navigation, and tiered vault subsections (lending: VaultItem/SecuritizeVaultItem; earn: VaultEarnItem) with empty-state handling.
Vault item components switched to ManagerEntityLink
components/entities/vault/VaultItem.vue, components/entities/vault/VaultBorrowItem.vue, components/entities/vault/VaultEarnItem.vue, components/entities/vault/SecuritizeVaultItem.vue
Removes getEulerLabelEntityLogo imports and entityLogos computed values; updates entityDisplay shape from logos to entities; replaces BaseAvatar+span risk-manager/capital-allocator markup with ManagerEntityLink in desktop and mobile layouts.
Vault overview components switched to ManagerEntityLink
components/entities/vault/overview/SecuritizeVaultOverview.vue, components/entities/vault/overview/VaultOverviewBlockGeneral.vue, components/entities/vault/overview/earn/VaultOverviewEarnBlockGeneral.vue
Removes getEulerLabelEntityLogo imports; replaces per-entity avatar+external-link/name rendering in Risk manager and curator sections with single ManagerEntityLink invocations.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as Browser / Router
  participant ManagerPage as pages/managers/[slug].vue
  participant Composable as useEulerManagerProfile
  participant Utils as manager-profile utils
  participant Stores as Euler Stores

  Browser->>ManagerPage: navigate to /managers/:slug
  ManagerPage->>Composable: useEulerManagerProfile(slug)
  Composable->>Stores: read label entities, vault lists, product lists
  Composable->>Utils: getEulerLabelEntitySlug(), isEulerLabelProductManagedBy()
  Utils-->>Composable: resolved slugs, filtered products/vaults
  Composable-->>ManagerPage: entity, productEntries, evaults, securitizeVaults, earnVaults, vaultCount, isLoading
  ManagerPage->>ManagerPage: render identity, metrics, products, vault sections
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • euler-xyz/euler-lite#167: Modifies VaultOverviewBlockGeneral.vue Risk manager section layout, which this PR also changes to use ManagerEntityLink.
  • euler-xyz/euler-lite#187: Changes risk-manager entity name/link rendering in VaultOverviewBlockGeneral.vue and VaultOverviewEarnBlockGeneral.vue, the same sections replaced here by ManagerEntityLink.
  • euler-xyz/euler-lite#401: Modifies the "Capital allocator/Curator" branding UI in VaultEarnItem.vue and VaultOverviewEarnBlockGeneral.vue, both of which this PR refactors to use ManagerEntityLink.

Suggested reviewers

  • kasperpawlowski

Poem

🐰 Hoppity hop down the manager lane,
A slug in the route, a profile to claim!
No more raw avatars scattered around—
ManagerEntityLink is the new sound.
Vaults list their curators, products line up neat,
The rabbit typed utils and called it complete! 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding manager profile pages.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/lite-256-dedicated-profile-page-for-curators-risk-managers

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

@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: 2

🧹 Nitpick comments (2)
composables/useEulerManagerProfile.ts (2)

35-61: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Consider precomputing an entity→slug map to avoid repeated O(n) scans.

getEulerLabelEntitySlug runs Object.entries(entities).find(...) (up to twice) for every manager of every vault, so each list recompute is roughly O(vaults · managers · entities). Building a reverse map once per recompute keeps the filters near-linear.

🤖 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 `@composables/useEulerManagerProfile.ts` around lines 35 - 61, The filtering in
useEulerManagerProfile.ts repeatedly calls getEulerLabelEntitySlug for every
manager, causing nested O(n) scans over entities during evaults,
securitizeVaults, and earnVaults recomputation. Precompute a reverse
entity-to-slug lookup once per recompute in the same composable, then have
managesEntity and managesEarnEntity read from that map instead of calling
getEulerLabelEntitySlug repeatedly. Keep the existing computed lists and
filter/sort flow, but make the slug resolution constant-time by keying off the
same entities source used by getEulerLabelEntitySlug.

35-43: 🗄️ Data Integrity & Integration | 🔵 Trivial

Cache entity slug lookups in useEulerManagerProfile
getEulerLabelEntitySlug scans entities with Object.entries for every manager check, so these computed filters repeat the same work. A reverse lookup map would avoid the extra passes.

🤖 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 `@composables/useEulerManagerProfile.ts` around lines 35 - 43, Cache the
repeated entity slug resolution in useEulerManagerProfile by introducing a
reverse lookup from entity id to slug instead of calling getEulerLabelEntitySlug
inside every managesEntity and managesEarnEntity check. Build the lookup once
from entities, then use it in the managers.some callbacks so the computed
filters no longer repeatedly scan Object.entries. Keep the changes localized
around getEulerLabelEntitySlug, managesEntity, and managesEarnEntity.
🤖 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 `@components/entities/manager/ManagerEntityLink.vue`:
- Around line 59-67: The clickable `span` in `ManagerEntityLink.vue` is
mouse-only and needs keyboard support. Update the `spanLink && isLinked` branch
on the `<span>` used for `goToManager` to be accessible by adding link semantics
and keyboard activation, such as `role="link"`, `tabindex="0"`, and a keydown
handler for Enter/Space, while keeping the existing click behavior and avoiding
nested anchors.

In `@components/entities/vault/VaultItem.vue`:
- Around line 301-308: The manager-profile link is using the click-only span
branch in ManagerEntityLink, so it is not keyboard accessible from the vault
cards. Update the shared span-link path in ManagerEntityLink so it renders a
tabbable, keyboard-activatable control with proper link semantics, and make the
VaultItem usage continue through that fixed path rather than relying on the
non-accessible span behavior.

---

Nitpick comments:
In `@composables/useEulerManagerProfile.ts`:
- Around line 35-61: The filtering in useEulerManagerProfile.ts repeatedly calls
getEulerLabelEntitySlug for every manager, causing nested O(n) scans over
entities during evaults, securitizeVaults, and earnVaults recomputation.
Precompute a reverse entity-to-slug lookup once per recompute in the same
composable, then have managesEntity and managesEarnEntity read from that map
instead of calling getEulerLabelEntitySlug repeatedly. Keep the existing
computed lists and filter/sort flow, but make the slug resolution constant-time
by keying off the same entities source used by getEulerLabelEntitySlug.
- Around line 35-43: Cache the repeated entity slug resolution in
useEulerManagerProfile by introducing a reverse lookup from entity id to slug
instead of calling getEulerLabelEntitySlug inside every managesEntity and
managesEarnEntity check. Build the lookup once from entities, then use it in the
managers.some callbacks so the computed filters no longer repeatedly scan
Object.entries. Keep the changes localized around getEulerLabelEntitySlug,
managesEntity, and managesEarnEntity.
🪄 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: Repository: euler-xyz/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2f674161-5228-4d81-a9d8-4e6637ca87c5

📥 Commits

Reviewing files that changed from the base of the PR and between c61a9af and 1138f42.

📒 Files selected for processing (12)
  • components/entities/manager/ManagerEntityLink.vue
  • components/entities/vault/SecuritizeVaultItem.vue
  • components/entities/vault/VaultBorrowItem.vue
  • components/entities/vault/VaultEarnItem.vue
  • components/entities/vault/VaultItem.vue
  • components/entities/vault/overview/SecuritizeVaultOverview.vue
  • components/entities/vault/overview/VaultOverviewBlockGeneral.vue
  • components/entities/vault/overview/earn/VaultOverviewEarnBlockGeneral.vue
  • composables/useEulerManagerProfile.ts
  • pages/managers/[slug].vue
  • tests/utils/manager-profile.test.ts
  • utils/manager-profile.ts

Comment on lines +59 to +67
<span
v-if="spanLink && isLinked"
:class="textClass"
:data-id="dataKey && dataField ? 'data-point' : undefined"
:data-key="dataKey || undefined"
:data-field="dataField || undefined"
:data-value="displayName"
@click.stop.prevent="goToManager"
>{{ displayName }}</span>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Clickable span is not keyboard accessible.

In the spanLink && isLinked branch the navigation lives on a <span> with only a mouse @click handler, so keyboard users can't focus or activate it. Since this branch exists to avoid nested anchors inside link rows, consider adding role="link", tabindex="0", and a keydown handler (Enter/Space).

♿ Suggested accessibility attributes
     <span
       v-if="spanLink && isLinked"
       :class="textClass"
+      role="link"
+      tabindex="0"
       :data-id="dataKey && dataField ? 'data-point' : undefined"
       :data-key="dataKey || undefined"
       :data-field="dataField || undefined"
       :data-value="displayName"
       `@click.stop.prevent`="goToManager"
+      `@keydown.enter.stop.prevent`="goToManager"
+      `@keydown.space.stop.prevent`="goToManager"
     >{{ displayName }}</span>
📝 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
<span
v-if="spanLink && isLinked"
:class="textClass"
:data-id="dataKey && dataField ? 'data-point' : undefined"
:data-key="dataKey || undefined"
:data-field="dataField || undefined"
:data-value="displayName"
@click.stop.prevent="goToManager"
>{{ displayName }}</span>
<span
v-if="spanLink && isLinked"
:class="textClass"
role="link"
tabindex="0"
:data-id="dataKey && dataField ? 'data-point' : undefined"
:data-key="dataKey || undefined"
:data-field="dataField || undefined"
:data-value="displayName"
`@click.stop.prevent`="goToManager"
`@keydown.enter.stop.prevent`="goToManager"
`@keydown.space.stop.prevent`="goToManager"
>{{ displayName }}</span>
🤖 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 `@components/entities/manager/ManagerEntityLink.vue` around lines 59 - 67, The
clickable `span` in `ManagerEntityLink.vue` is mouse-only and needs keyboard
support. Update the `spanLink && isLinked` branch on the `<span>` used for
`goToManager` to be accessible by adding link semantics and keyboard activation,
such as `role="link"`, `tabindex="0"`, and a keydown handler for Enter/Space,
while keeping the existing click behavior and avoiding nested anchors.

Comment on lines +301 to +308
<ManagerEntityLink
:entities="entities"
:label="entityName"
:src="entityLogos"
/>
<span
class="text-p2 text-content-primary truncate"
data-id="data-point"
span-link
text-class="text-p2 text-content-primary hover:text-accent-600 underline transition-colors truncate"
:data-key="vault.address.toLowerCase()"
data-field="risk-manager"
:data-value="entityName"
>{{ entityName }}</span>
/>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Make the manager-profile affordance keyboard accessible.

Using span-link here routes through ManagerEntityLink’s click-only <span> branch, so the new profile destination is not tabbable or activatable from the keyboard inside these vault cards. Please fix the shared span-link path before rolling this pattern out across the other list items in this PR.

🤖 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 `@components/entities/vault/VaultItem.vue` around lines 301 - 308, The
manager-profile link is using the click-only span branch in ManagerEntityLink,
so it is not keyboard accessible from the vault cards. Update the shared
span-link path in ManagerEntityLink so it renders a tabbable,
keyboard-activatable control with proper link semantics, and make the VaultItem
usage continue through that fixed path rather than relying on the non-accessible
span behavior.

@LeonardEulerXYZ LeonardEulerXYZ left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed current head 1138f42baffd32f7f6d04e479131efc869a2e887.

Verdict: COMMENT — one non-blocking accessibility/UX suggestion inline; no correctness or security blocker found.

Scope reviewed:

  • New /managers/[slug] page and useEulerManagerProfile data path.
  • Shared ManagerEntityLink and profile helper utilities.
  • Refactor of curator/risk-manager display across vault cards and overview blocks.
  • Active bot feedback: CodeRabbit had a walkthrough/pre-merge summary only; no material inline findings to verify.

Validation performed:

  • git diff --check origin/development...HEAD
  • npm run test:run -- tests/utils/manager-profile.test.ts
  • npx eslint components/entities/manager/ManagerEntityLink.vue composables/useEulerManagerProfile.ts pages/managers/[slug].vue utils/manager-profile.ts tests/utils/manager-profile.test.ts
  • npm run typecheck
  • npm run build
  • Headed Chromium/Xvfb browser smoke on a local production build:
    • Desktop /managers/k3?network=ethereum: rendered manager identity, social links, product cards, and managed vault list.
    • Mobile /managers/k3?network=ethereum: same content rendered in the narrow viewport.
    • /managers/not-a-real-manager?network=ethereum: rendered the not-found state.

Smoke coverage: browser visual smoke + mobile smoke; no wallet/signing coverage, as this PR is read-only navigation/profile UI.

Screenshots: captured during local smoke, but not attached here because the available GitHub CLI path in this environment does not support binary PNG upload. No sensitive content was captured.

Scalability / maintainability hygiene pass:

  • Good direction overall: the repeated avatar/name rendering was centralized in ManagerEntityLink, and helper logic has focused utility tests.
  • Sibling surfaces checked: lend/borrow/earn cards, Securitize cards, and vault overview blocks now use the shared component where this PR touches manager display. Explore/list filters remain name-based filters and are out of scope for profile navigation.
  • Remaining refinement is the spanLink accessibility point inline; because most card call sites need to avoid nested anchors, the shared component is the right place to make that behavior reusable and keyboard-accessible.

:class="avatarClass"
:label="displayName"
:src="entityLogos"
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Suggestion: spanLink makes the manager destination mouse-clickable, but it renders as a plain <span> with no href, role="link", tabindex, or keyboard handler. Most of the new vault-card call sites use this mode to avoid nested anchors, so keyboard and screen-reader users do not get the same navigation affordance as mouse users. Consider making the span mode an accessible link-like control (for example role="link", tabindex="0", and Enter/Space handling) or otherwise restructuring the card/link relationship so the shared component stays accessible everywhere.

@railway-app

railway-app Bot commented Jun 24, 2026

Copy link
Copy Markdown

🚅 Deployed to the euler-lite-pr-618 environment in euler-lite

Service Status Web Updated (UTC)
dev-build ✅ Success (View Logs) Web Jun 25, 2026 at 12:09 pm

@Seranged Seranged marked this pull request as draft June 24, 2026 13:19
Add keyboard semantics to the span-based manager profile affordance used inside vault cards.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 24, 2026 14:00 Destroyed
Normalize manager profile links and surface labeled governance addresses plus product metadata from labels.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 24, 2026 14:16 Destroyed
Align manager profiles with the review notes by removing governance addresses and individual lending vault lists, using market cards, and keeping flexible profile links.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 10:48 Destroyed
Drop the low-value links stat card now that profile links render directly below the summary.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 10:52 Destroyed
Remove the extra manager-profile label and place the back button with the manager identity header.
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 10:54 Destroyed
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 11:04 Destroyed
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 11:05 Destroyed
@railway-app railway-app Bot temporarily deployed to euler-lite / euler-lite-pr-618 June 25, 2026 11:11 Destroyed
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.

2 participants