Skip to content

feat(dashboard): add delete button to Skills page - #247

Merged
cnlangzi merged 3 commits into
mainfrom
fix/dashboard-skills
May 11, 2026
Merged

feat(dashboard): add delete button to Skills page#247
cnlangzi merged 3 commits into
mainfrom
fix/dashboard-skills

Conversation

@xiajiexia

Copy link
Copy Markdown
Collaborator

Summary

Adds delete functionality to the Skills page in the dashboard.

Changes

  • Add trash icon delete button that appears on hover for each skill card
  • Calls DELETE /api/skills/{id} with confirmation dialog
  • Uses existing handleSkillDelete backend endpoint
  • Button visible only on hover to prevent accidental clicks

Testing

  • Manual testing: delete button appears on hover
  • Manual testing: confirmation dialog shows before deletion
  • Manual testing: skill is removed after successful deletion

Related Issue

Fixes #246

Adds a trash icon delete button that appears on hover for each skill card.
- Calls DELETE /api/skills/{id} with confirmation dialog
- Uses existing handleSkillDelete backend endpoint
- Button visible only on hover to avoid accidental clicks

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • Previously the whole skill card was a clickable link, but now only the text area is wrapped in the <a> while the padding/hover background is on the outer <div>; if preserving the full-card click target is desired, consider moving the href back to the outer container and handling delete via event.stopPropagation() instead.
  • Instead of using an inline onclick="deleteSkill(this)", consider attaching the click listener in JavaScript (e.g., via event delegation on the container) to keep behavior separate from markup and make future JS changes easier to manage.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Previously the whole skill card was a clickable link, but now only the text area is wrapped in the `<a>` while the padding/hover background is on the outer `<div>`; if preserving the full-card click target is desired, consider moving the `href` back to the outer container and handling delete via `event.stopPropagation()` instead.
- Instead of using an inline `onclick="deleteSkill(this)"`, consider attaching the click listener in JavaScript (e.g., via event delegation on the container) to keep behavior separate from markup and make future JS changes easier to manage.

## Individual Comments

### Comment 1
<location path="cmd/app/dashboard/pages/skills.html" line_range="148-157" />
<code_context>
     container.innerHTML = html;
 }

+async function deleteSkill(btn) {
+    const id = btn.dataset.skillId;
+    const name = btn.dataset.skillName;
+    if (!confirm(`Delete skill "${name}"? This cannot be undone.`)) {
+        return;
+    }
+    
+    try {
+        const response = await fetch(`/api/skills/${id}`, {
+            method: 'DELETE'
+        });
+        if (!response.ok) {
+            alert('Failed to delete skill: HTTP ' + response.status);
+            return;
+        }
+        const data = await response.json();
+        
+        if (data.success) {
</code_context>
<issue_to_address>
**issue:** Handle DELETE responses that might not include a JSON body (e.g. 204 No Content) to avoid runtime errors.

The function currently calls `await response.json()` unconditionally. Many DELETE endpoints return `204 No Content`, where this will throw. Guard the JSON parsing (e.g., based on status or `content-type`/`content-length`), or wrap it in try/catch and treat 204 as a success with no body.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread cmd/app/dashboard/pages/skills.html Outdated
@coderabbitai

coderabbitai Bot commented May 11, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Rate limit exceeded

@xiajiexia has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 43 minutes and 6 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 30505dc7-a08a-4fc0-82a7-668fd592aea8

📥 Commits

Reviewing files that changed from the base of the PR and between d198461 and 9f36a55.

📒 Files selected for processing (1)
  • cmd/app/dashboard/pages/skills.html

Walkthrough

This pull request adds deletion support to the skills management dashboard. The skills list page now displays a trash icon button for each skill row that triggers an inline delete handler. When clicked, the handler prompts the user to confirm deletion by skill name, then sends a DELETE request to the backend API endpoint. Upon successful deletion, the skills list automatically refreshes to reflect the removal. The changes are contained to the client-side HTML template and include both the new delete confirmation logic and the updated markup that wires the delete buttons to the handler function.

Poem

🐰 A skill once made can now find rest,
With just a click, the trash does test.
"Confirm?" it asks with gentle care,
Then whoosh—it's gone into thin air! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR partially addresses issue #246 by implementing delete functionality on the skills list page but omits the required delete button on the edit page (/skills/edit/{id}). Add delete functionality to the skill edit page including a Delete Skill button near the title with confirmation dialog as specified in issue #246 acceptance criteria.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a delete button feature to the Skills page dashboard component.
Description check ✅ Passed The description is directly related to the changeset, outlining the delete functionality added including the delete button, confirmation dialog, and API call implementation.
Out of Scope Changes check ✅ Passed All changes in the PR are directly in scope for adding the delete button to the Skills list page as part of issue #246 implementation.
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.

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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.

@cnlangzi

Copy link
Copy Markdown
Owner

@codex review

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d198461e63

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


if (data.success) {
// Reload the list
loadSkillsList();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid reloading before watcher removes deleted skills

In the normal delete flow this reload can race the backend: handleSkillDelete only removes the file and explicitly leaves removal from memory to the file watcher (cmd/app/skills.go:628-636), while /api/skills/list is built from the in-memory skill list (plugin/manager.go:161-164). The watcher polls once per second and then waits another 500ms debounce before calling Skills.Delete (plugin/sql/plugin.go:201-226, plugin/sql/plugin.go:270-289), so an immediate loadSkillsList() often re-renders the just-deleted skill and makes the delete appear to have failed until a manual refresh/later reload. Consider removing the card optimistically or delaying/polling until the list no longer contains the id.

Useful? React with 👍 / 👎.

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

🧹 Nitpick comments (2)
cmd/app/dashboard/pages/skills.html (2)

163-163: ⚡ Quick win

Add defensive error handling for JSON parsing.

response.json() can throw if the response body is not valid JSON. While your backend should return JSON, defensive coding improves robustness, especially if the server returns HTML error pages for 5xx errors or the response Content-Type is unexpected.

🛡️ Proposed fix with try-catch for JSON parsing
         if (!response.ok) {
             alert('Failed to delete skill: HTTP ' + response.status);
             return;
         }
-        const data = await response.json();
+        
+        let data;
+        try {
+            data = await response.json();
+        } catch (parseErr) {
+            alert('Failed to parse server response: ' + parseErr.message);
+            return;
+        }
         
         if (data.success) {
🤖 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 `@cmd/app/dashboard/pages/skills.html` at line 163, The call to response.json()
can throw on non-JSON responses; wrap the JSON parsing in a try-catch and handle
non-OK or non-JSON responses gracefully: first check response.ok (or status) and
then attempt await response.json() inside a try block, catch parsing errors,
optionally call await response.text() for debugging, and log or surface a clear
error before returning/throwing. Update the code around the existing
response.json() usage so that failures don’t crash the page and provide a useful
error path.

151-151: Consider custom dialogs for consistency with design system.

The native confirm() and alert() dialogs work but don't match your polished UI design (custom colors, rounded corners, transitions). A custom modal component would provide a more consistent user experience.

This is a minor polish item that could be deferred to a future refactor.

Also applies to: 160-160, 169-169, 172-172

🤖 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 `@cmd/app/dashboard/pages/skills.html` at line 151, Replace the native
confirm() and alert() usages with the app's custom modal/dialog component to
match the design system: create or reuse an async confirmDialog(showMessage,
options) that returns a boolean and call it in place of confirm(`Delete skill
"${name}"?...`), and replace alert() calls with a styled infoDialog/showToast;
update the deleteSkill handler and the other places where confirm()/alert() are
used so they await the modal result and proceed only if true, preserving
existing message text and success/error flows.
🤖 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 `@cmd/app/dashboard/pages/skills.html`:
- Line 107: The current code builds HTML using unescaped user data (skill.name,
skill.description, skill.error) and assigns it via container.innerHTML,
introducing an XSS risk; change rendering to set safe text content instead of
injecting raw HTML: keep the existing HTML shell but remove direct interpolation
of skill fields, assign container.innerHTML = html; then iterate the rendered
blocks (use container.querySelectorAll('.block') or the same selector used now),
and for each block set its h4.textContent = skills[i].name, set the description
paragraph's textContent = skills[i].description, and set the error paragraph's
textContent and title via Element.textContent and Element.setAttribute
respectively so no user string is interpreted as HTML or script; alternatively
perform server-side HTML-escaping of skill.name/description/error before they
reach this script.
- Around line 148-174: The deleteSkill function allows multiple rapid clicks;
fix it by early-return if btn.disabled is true, then set btn.disabled = true
immediately after extracting id/name to block further clicks, perform the async
fetch as before, and on non-success paths (response not ok, data.success false,
or catch error) re-enable the button (btn.disabled = false) so the user can
retry; do not re-enable on success since loadSkillsList() will re-render the
list. Reference: deleteSkill, btn, loadSkillsList.
- Around line 129-135: The delete button (the <button> with title "Delete skill"
containing the trash SVG) is fully hidden via "opacity-0
group-hover:opacity-100", which prevents touch, keyboard, and screen-reader
users from accessing it; change its utility classes so it is visible by default
at low opacity and becomes fully opaque on hover/focus — e.g., replace the base
"opacity-0" with a subtle visible state like "opacity-60", keep
"hover:opacity-100", and add keyboard/touch accessibility classes such as
"focus:opacity-100", "focus-visible:opacity-100" and
"group-focus-within:opacity-100" (or a responsive rule to always show on small
screens) so the button is discoverable for keyboard, touch, and assistive tech
users.

---

Nitpick comments:
In `@cmd/app/dashboard/pages/skills.html`:
- Line 163: The call to response.json() can throw on non-JSON responses; wrap
the JSON parsing in a try-catch and handle non-OK or non-JSON responses
gracefully: first check response.ok (or status) and then attempt await
response.json() inside a try block, catch parsing errors, optionally call await
response.text() for debugging, and log or surface a clear error before
returning/throwing. Update the code around the existing response.json() usage so
that failures don’t crash the page and provide a useful error path.
- Line 151: Replace the native confirm() and alert() usages with the app's
custom modal/dialog component to match the design system: create or reuse an
async confirmDialog(showMessage, options) that returns a boolean and call it in
place of confirm(`Delete skill "${name}"?...`), and replace alert() calls with a
styled infoDialog/showToast; update the deleteSkill handler and the other places
where confirm()/alert() are used so they await the modal result and proceed only
if true, preserving existing message text and success/error flows.
🪄 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 UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 903ee4b5-50b2-48a4-9210-b4c1205e485e

📥 Commits

Reviewing files that changed from the base of the PR and between 4c0385a and d198461.

📒 Files selected for processing (1)
  • cmd/app/dashboard/pages/skills.html

Comment thread cmd/app/dashboard/pages/skills.html Outdated
Comment thread cmd/app/dashboard/pages/skills.html Outdated
Comment thread cmd/app/dashboard/pages/skills.html Outdated

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 1 file

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/app/dashboard/pages/skills.html">

<violation number="1" location="cmd/app/dashboard/pages/skills.html:128">
P1: Escape `skill.name` before inserting it into `data-skill-name` to prevent attribute injection from crafted skill names.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread cmd/app/dashboard/pages/skills.html Outdated
Address code review comments:
- Restore full card as clickable link (whole card is now <a>)
- Event delegation for delete button instead of inline onclick
- Handle 204 No Content responses without JSON body
- Use content-type header check before parsing JSON response
- Use event.stopPropagation() to prevent card link navigation when clicking delete

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/app/dashboard/pages/skills.html">

<violation number="1" location="cmd/app/dashboard/pages/skills.html:110">
P2: This introduces a nested interactive element (`button` inside `<a>`), which is invalid markup and can cause inconsistent interaction behavior.</violation>

<violation number="2" location="cmd/app/dashboard/pages/skills.html:148">
P1: `container` is out of scope here, causing a runtime `ReferenceError` and breaking page script execution.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread cmd/app/dashboard/pages/skills.html Outdated
Comment thread cmd/app/dashboard/pages/skills.html Outdated
Address multiple code review issues:

1. Fix nested interactive elements (button in <a>): use overlay <a> pattern instead
2. Fix delete button accessibility: change opacity-0 to opacity-60 with focus support
3. Fix XSS vulnerability: add escapeHtml() function for skill.name, description, error
4. Fix double-click prevention: add deletingSkills Set to track in-flight deletions
5. Fix container scope issue: use document.getElementById('skills-list') explicitly

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="cmd/app/dashboard/pages/skills.html">

<violation number="1" location="cmd/app/dashboard/pages/skills.html:125">
P2: The edit link is hidden from keyboard and assistive tech, making skill editing inaccessible for non-pointer users.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

<div class="flex items-start justify-between">
<div class="flex-1 min-w-0">
<div class="block p-4 hover:bg-surfaceHover transition-colors group relative">
<a href="/skills/edit/${skill.id}" class="absolute inset-0" tabindex="-1" aria-hidden="true"></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.

P2: The edit link is hidden from keyboard and assistive tech, making skill editing inaccessible for non-pointer users.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cmd/app/dashboard/pages/skills.html, line 125:

<comment>The edit link is hidden from keyboard and assistive tech, making skill editing inaccessible for non-pointer users.</comment>

<file context>
@@ -102,19 +113,23 @@ <h2 class="text-lg font-semibold text-text">Loaded Skills</h2>
-                <div class="flex items-start justify-between">
-                    <div class="flex-1 min-w-0">
+            <div class="block p-4 hover:bg-surfaceHover transition-colors group relative">
+                <a href="/skills/edit/${skill.id}" class="absolute inset-0" tabindex="-1" aria-hidden="true"></a>
+                <div class="flex items-start justify-between relative z-10">
+                    <div class="flex-1 min-w-0 pr-16">
</file context>
Suggested change
<a href="/skills/edit/${skill.id}" class="absolute inset-0" tabindex="-1" aria-hidden="true"></a>
<a href="/skills/edit/${skill.id}" class="absolute inset-0" aria-label="Edit skill ${escapedName}"></a>

Tip: Review your code locally with the cubic CLI to iterate faster.

@cnlangzi
cnlangzi merged commit 93289cb into main May 11, 2026
5 checks passed
@cnlangzi
cnlangzi deleted the fix/dashboard-skills branch May 11, 2026 10:11
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.

[Dashboard] Skills 页面缺少删除功能

2 participants