Skip to content

Feat/my stuff#26

Merged
BretRen merged 4 commits intomainfrom
feat/my-stuff
Apr 20, 2026
Merged

Feat/my stuff#26
BretRen merged 4 commits intomainfrom
feat/my-stuff

Conversation

@BretRen
Copy link
Copy Markdown
Member

@BretRen BretRen commented Apr 20, 2026

Summary by CodeRabbit

  • New Features

    • Added "My Stuff" page with tabbed interface for browsing and managing personal items
    • New "My Stuff" button in user dropdown menu for quick access
  • Style

    • Enhanced user dropdown menu layout and spacing

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 20, 2026

📝 Walkthrough

Walkthrough

This PR removes ID-immutability validation from the global record update handler in main.go, adds a "My Stuff" navigation link to the authenticated user dropdown in the layout with adjusted styling, and introduces a new "My Stuff" page featuring a tabbed interface for browsing user-owned CPS and character records from PocketBase.

Changes

Cohort / File(s) Summary
Backend Middleware Update
main.go
Removed the validateIdImmutable middleware from the OnRecordUpdateRequest() hook, restricting ID-immutability validation to creation requests only.
Frontend Navigation & Styling
website/src/routes/+layout.svelte
Added goto import and wired "My Stuff" button in authenticated user dropdown to navigate to /mystuff; adjusted dropdown menu layout with flexbox classes (flex flex-col gap-2).
New Feature Page
website/src/routes/mystuff/+page.svelte
Added new Svelte route page with reactive state management for two view modes ("cps" and "characters"), async data fetching from PocketBase filtered by user ownership, loading/empty states, and tabbed grid interface with mode-dependent action links.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Feat/create and list cps #16: Modifies PocketBase record request hooks in main.go by changing middleware bindings to OnRecordCreateRequest and OnRecordUpdateRequest, directly related to the middleware management pattern in this PR's backend changes.

Poem

A rabbit hops through the garden of change,
Removing old guards from the update exchange,
"My Stuff" now gleams in a shiny new page,
With CPS and characters—a data-filled stage! 🐰✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title 'Feat/my stuff' is vague and generic, using non-descriptive terms that don't clearly convey the main changes (ID immutability validation removal, navigation UI updates, and new user data dashboard page). Use a more specific title like 'Add My Stuff page with user-owned content management' or 'Implement My Stuff dashboard and adjust ID validation scope' to better reflect the primary feature being added.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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 feat/my-stuff

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.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new "My Stuff" page for users to manage their created CPs and characters, updates the navigation layout, and modifies backend hooks. Feedback includes a security recommendation to use the pb.filter() helper to prevent potential injection, a suggestion to restore the validateIdImmutable hook in the backend to maintain record integrity, and a best-practice recommendation to move authentication guards to SvelteKit load functions to improve the user experience.

Comment thread website/src/routes/mystuff/+page.svelte
Comment thread main.go
Comment thread website/src/routes/mystuff/+page.svelte
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@website/src/routes/mystuff/`+page.svelte:
- Around line 29-33: Guard against stale responses by tracking each load with a
request id and only updating state if the response corresponds to the most
recent load: add a component-scoped counter (e.g., currentLoadId) and, inside
the function before calling pb.collection(...).getFullList, capture const
myLoadId = ++currentLoadId and const myMode = mode; after the await (and in the
catch), only assign items, clear items on error, or set isLoading = false if
myLoadId === currentLoadId and mode === myMode; otherwise ignore the result so a
slower previous response cannot overwrite items or isLoading for a different
tab. Ensure references to items, mode, and isLoading are guarded by that check.
- Around line 48-58: The tab buttons in +page.svelte use role="tab" but don't
expose selection to AT; update both button elements (the ones using class "tab
{mode === 'cps' ? 'tab-active' : ''}" and "tab {mode === 'characters' ?
'tab-active' : ''}") to include aria-selected attributes such that
aria-selected="true" when mode === 'cps' (for the My CPs button) or mode ===
'characters' (for the My Characters button) and aria-selected="false" otherwise;
keep the existing onclick handlers (onclick={() => mode = 'cps'} / onclick={()
=> mode = 'characters'}) and class logic unchanged so visual and programmatic
state remain synchronized.
- Line 82: The Edit anchor in +page.svelte uses a dynamic href
"/edit/{mode}/{item.id}" which points to non-existent routes (/edit/cps/[id] and
/edit/characters/[id]) and will 404; either implement route handlers for those
paths (create +page.svelte or route files for /edit/cps/[id] and
/edit/characters/[id] to accept mode and id and render the edit forms) or change
the anchor href in +page.svelte (the <a href="/edit/{mode}/{item.id}"> link) to
an existing route pattern used by the app (e.g., the current item edit route) so
clicks resolve to a real page.
🪄 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: 16217073-6b77-4e6e-b06d-4c5eaebcb6bd

📥 Commits

Reviewing files that changed from the base of the PR and between b902c65 and 4c95b85.

📒 Files selected for processing (3)
  • main.go
  • website/src/routes/+layout.svelte
  • website/src/routes/mystuff/+page.svelte
💤 Files with no reviewable changes (1)
  • main.go

Comment on lines +29 to +33
items = await pb.collection(collection).getFullList(options);
} catch (err) {
console.error("Fetch error:", err);
} finally {
isLoading = false;
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

Guard against stale responses when switching tabs.

Line 39 can start overlapping requests; a slower previous response can still assign items after mode changed, rendering CPs under the Characters tab or vice versa. Errors also keep the previous items visible.

🐛 Proposed fix to ignore stale loads
     let mode = $state<ViewMode>('cps');
     let items = $state<any[]>([]);
     let isLoading = $state(true);
+    let fetchVersion = 0;
 
     async function fetchMyData(currentMode: ViewMode) {
+        const version = ++fetchVersion;
+
         // 安全守卫:如果没有登录,直接跳走
         if (!pb.authStore.isValid || !pb.authStore.record) {
-            goto('/login');
+            isLoading = false;
+            await goto('/login');
             return;
         }
 
         isLoading = true;
         const userId = pb.authStore.record.id;
@@
-            items = await pb.collection(collection).getFullList(options);
+            const nextItems = await pb.collection(collection).getFullList(options);
+            if (version === fetchVersion) {
+                items = nextItems;
+            }
         } catch (err) {
             console.error("Fetch error:", err);
+            if (version === fetchVersion) {
+                items = [];
+            }
         } finally {
-            isLoading = false;
+            if (version === fetchVersion) {
+                isLoading = false;
+            }
         }
     }

Also applies to: 37-40

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@website/src/routes/mystuff/`+page.svelte around lines 29 - 33, Guard against
stale responses by tracking each load with a request id and only updating state
if the response corresponds to the most recent load: add a component-scoped
counter (e.g., currentLoadId) and, inside the function before calling
pb.collection(...).getFullList, capture const myLoadId = ++currentLoadId and
const myMode = mode; after the await (and in the catch), only assign items,
clear items on error, or set isLoading = false if myLoadId === currentLoadId and
mode === myMode; otherwise ignore the result so a slower previous response
cannot overwrite items or isLoading for a different tab. Ensure references to
items, mode, and isLoading are guarded by that check.

Comment on lines +48 to +58
<div role="tablist" class="tabs tabs-boxed mt-6 w-fit">
<button
role="tab"
class="tab {mode === 'cps' ? 'tab-active' : ''}"
onclick={() => mode = 'cps'}
>My CPs</button>
<button
role="tab"
class="tab {mode === 'characters' ? 'tab-active' : ''}"
onclick={() => mode = 'characters'}
>My Characters</button>
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

Expose the active tab state to assistive technology.

These buttons declare role="tab", but the selected state is only represented by CSS. Add aria-selected so screen readers can identify the active tab.

♿ Proposed tab accessibility fix
             <button 
+                type="button"
                 role="tab" 
+                aria-selected={mode === 'cps'}
                 class="tab {mode === 'cps' ? 'tab-active' : ''}" 
                 onclick={() => mode = 'cps'}
             >My CPs</button>
             <button 
+                type="button"
                 role="tab" 
+                aria-selected={mode === 'characters'}
                 class="tab {mode === 'characters' ? 'tab-active' : ''}" 
                 onclick={() => mode = 'characters'}
             >My Characters</button>
📝 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
<div role="tablist" class="tabs tabs-boxed mt-6 w-fit">
<button
role="tab"
class="tab {mode === 'cps' ? 'tab-active' : ''}"
onclick={() => mode = 'cps'}
>My CPs</button>
<button
role="tab"
class="tab {mode === 'characters' ? 'tab-active' : ''}"
onclick={() => mode = 'characters'}
>My Characters</button>
<div role="tablist" class="tabs tabs-boxed mt-6 w-fit">
<button
type="button"
role="tab"
aria-selected={mode === 'cps'}
class="tab {mode === 'cps' ? 'tab-active' : ''}"
onclick={() => mode = 'cps'}
>My CPs</button>
<button
type="button"
role="tab"
aria-selected={mode === 'characters'}
class="tab {mode === 'characters' ? 'tab-active' : ''}"
onclick={() => mode = 'characters'}
>My Characters</button>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@website/src/routes/mystuff/`+page.svelte around lines 48 - 58, The tab
buttons in +page.svelte use role="tab" but don't expose selection to AT; update
both button elements (the ones using class "tab {mode === 'cps' ? 'tab-active' :
''}" and "tab {mode === 'characters' ? 'tab-active' : ''}") to include
aria-selected attributes such that aria-selected="true" when mode === 'cps' (for
the My CPs button) or mode === 'characters' (for the My Characters button) and
aria-selected="false" otherwise; keep the existing onclick handlers (onclick={()
=> mode = 'cps'} / onclick={() => mode = 'characters'}) and class logic
unchanged so visual and programmatic state remain synchronized.

<div class="card-body p-5">
<h2 class="card-title">{item.name}</h2>
<div class="card-actions justify-end mt-4">
<a href="/edit/{mode}/{item.id}" class="btn btn-sm btn-ghost">Edit</a>
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
# Description: Verify that SvelteKit route files exist for the edit URLs generated by My Stuff.
# Expected: route files/directories matching /edit/cps/[id] and /edit/characters/[id], or update the hrefs to the actual edit route shape.

printf 'Generated edit hrefs in Svelte files:\n'
rg -nP 'href="/edit/\{mode\}/\{item\.id\}"' -C2 --glob '*.svelte' || true

printf '\nRelevant SvelteKit route files:\n'
fd -p '\+page\.svelte$' website/src/routes \
  | sort \
  | rg '/edit/|/cps/|/characters/' || true

Repository: pdnode-team/cp

Length of output: 790


🏁 Script executed:

#!/bin/bash
# Check if /edit directory exists and list all route files
printf 'Checking for /edit directory:\n'
ls -la website/src/routes/ | grep edit || echo "No /edit directory found"

printf '\nAll +page.svelte files in website/src/routes:\n'
find website/src/routes -name '+page.svelte' -type f | sort

printf '\nChecking for any edit route patterns:\n'
find website/src/routes -type d -name edit || echo "No /edit directory found"

Repository: pdnode-team/cp

Length of output: 555


🏁 Script executed:

#!/bin/bash
# Search for any edit route references or edit functionality
printf 'Searching for edit-related functionality:\n'
rg -i 'edit.*route|route.*edit' website/src/routes --type svelte -l || echo "No edit route references found"

printf '\nSearching for +layout or server files that might handle edit:\n'
find website/src/routes/edit website/src/routes/cps website/src/routes/characters -name '+layout*' -o -name '+server*' 2>/dev/null || echo "No edit layout/server files found"

printf '\nChecking mystuff page for edit functionality context:\n'
grep -n -i 'edit' website/src/routes/mystuff/+page.svelte

Repository: pdnode-team/cp

Length of output: 432


Update the Edit link destination or create the edit routes.

The edit routes at /edit/cps/[id] and /edit/characters/[id] do not exist in the codebase. The Edit button on line 82 will generate 404 errors. Either create these routes to handle edit functionality or change the href to an existing route.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@website/src/routes/mystuff/`+page.svelte at line 82, The Edit anchor in
+page.svelte uses a dynamic href "/edit/{mode}/{item.id}" which points to
non-existent routes (/edit/cps/[id] and /edit/characters/[id]) and will 404;
either implement route handlers for those paths (create +page.svelte or route
files for /edit/cps/[id] and /edit/characters/[id] to accept mode and id and
render the edit forms) or change the anchor href in +page.svelte (the <a
href="/edit/{mode}/{item.id}"> link) to an existing route pattern used by the
app (e.g., the current item edit route) so clicks resolve to a real page.

@BretRen BretRen merged commit d345ae5 into main Apr 20, 2026
1 check passed
@BretRen BretRen deleted the feat/my-stuff branch April 20, 2026 03:34
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.

1 participant