Skip to content

feat(email): enhance email ui#176

Merged
urjitc merged 3 commits into
mainfrom
feature/no-ref/email-share-ui-improvements
Feb 7, 2026
Merged

feat(email): enhance email ui#176
urjitc merged 3 commits into
mainfrom
feature/no-ref/email-share-ui-improvements

Conversation

@ibraheemshaikh5

@ibraheemshaikh5 ibraheemshaikh5 commented Feb 6, 2026

Copy link
Copy Markdown
Collaborator

Important

Enhances email UI for workspace invitations with redesigned templates, permission level display, and improved URL handling.

  • Email UI Enhancements:
    • Redesigned invitation emails with branded HTML layouts in invite-email.tsx.
    • Added display of workspace color and assigned permission level in emails.
    • Added logo attachments to pending user invitations.
  • Functionality:
    • Improved workspace URL identification with enhanced slug handling in route.ts.
    • Updated handlePOST in route.ts to include permissionLevel in email data.

This description was created by Ellipsis for 22ab824. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features
    • Redesigned invitation emails with a branded HTML layout, rounded CTA, fallback link, and branded header/footer.
    • Invitation emails now show workspace name, inviter name, workspace color/branding, and the assigned permission level/role.
    • Logo included in pending-user invitations.
    • Direct invitation flow now passes the selected permission level so emails reflect the assigned role.

@vercel

vercel Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
thinkex Ready Ready Preview, Comment Feb 7, 2026 3:13am

@ellipsis-dev ellipsis-dev Bot 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.

Important

Looks good to me! 👍

Reviewed everything up to 7b2f98b in 6 seconds. Click for details.
  • Reviewed 242 lines of code in 2 files
  • Skipped 2 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_PQecnjIPEClxEoqv

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@coderabbitai

coderabbitai Bot commented Feb 6, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Adds a permissionLevel field to the workspace invitation flow and updates the invite email template to accept and render that permission level; the API now passes permissionLevel into the email template call and the email component was redesigned with a branded HTML layout and new props.

Changes

Cohort / File(s) Summary
API change
src/app/api/workspaces/[id]/collaborators/route.ts
Passes permissionLevel into the invite email payload for direct invitations by forwarding it into the InviteEmailTemplate call. No other behavioral changes.
Email template redesign
src/components/email/invite-email.tsx
Introduces theming constants and a new optional prop permissionLevel?: string; computes roleText from permissionLevel and conditionally renders it; replaces the prior simple layout with a full branded table-based HTML email (inline styles, CTA button, header/footer); adds export default InviteEmailTemplate and updates the component signature/interface.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped in to tweak an invite so bright,
Permission baked in, the role now in sight,
A button, a banner — the email feels right,
I nibble a carrot and vanish from light. 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'feat(email): enhance email ui' is vague and doesn't clearly convey the specific changes; 'enhance email ui' is too generic and doesn't describe what was actually improved. Use a more descriptive title that specifies the key improvement, such as 'feat(email): add permission level and branding to workspace invitation emails' or 'feat(email): redesign invitation email with role and workspace details'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/no-ref/email-share-ui-improvements

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.

@greptile-apps

greptile-apps Bot commented Feb 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Overview

Greptile Summary

  • Updates workspace collaborator invite flow to include permissionLevel in the invitation email payload and constructs workspace URLs using the workspace slug when present.
  • Replaces the old plain invite email with a branded HTML/table-based template, adding optional permission level copy.
  • Adds new email logo assets (public/email-logo.png and .svg) for use in invitation emails.
  • Main API changes are concentrated in src/app/api/workspaces/[id]/collaborators/route.ts, which is responsible for creating collaborators/invites and sending Resend emails.

Confidence Score: 3/5

  • This PR has a couple of correctness issues in the collaborator invite route that should be addressed before merge.
  • Most changes are email-template/UI and static assets. However, handlePOST can throw when ws is missing (undefined dereference), and the existing-user path directly adds collaborators while still sending an invitation email, which is likely incorrect behavior. Fixing those would make the PR substantially safer.
  • src/app/api/workspaces/[id]/collaborators/route.ts

Important Files Changed

Filename Overview
public/email-logo.png Adds a new PNG email logo asset; no code-path changes.
public/email-logo.svg Adds a new SVG email logo asset; no code-path changes.
src/app/api/workspaces/[id]/collaborators/route.ts Includes permissionLevel in invite email data and uses workspace slug for URLs; however, it can throw when ws is undefined and the existing-user path emails despite directly adding collaborators.
src/components/email/invite-email.tsx Redesigns the invite email HTML and adds optional permission level copy; logic appears safe.

Sequence Diagram

sequenceDiagram
  autonumber
  participant C as Client
  participant N as Next.js API Route
  participant A as Auth (requireAuthWithUserInfo)
  participant DB as Drizzle DB
  participant R as Resend
  participant E as InviteEmailTemplate

  C->>N: POST /api/workspaces/:id/collaborators {email, permissionLevel}
  N->>A: requireAuthWithUserInfo()
  A-->>N: currentUser
  N->>DB: verifyWorkspaceAccess(workspaceId, currentUser, editor)
 DB-->>N: ok
  N->>DB: SELECT user by email  DB-->>N: invitedUser? (may be null)
  N->>DB: SELECT workspace (ws)
  DB-->>N: ws

  alt invitedUser exists
    N->>DB: INSERT workspaceCollaborators
    DB-->>N: newCollaborator
    N->>E: Render email (inviterName, workspaceName, workspaceUrl, permissionLevel)
    E-->>N: React email
    N->>R: resend.emails.send(react email)
    R-->>N: sent/err
    N-->>C: 201 {collaborator}
  else invitedUser missing
    N->>DB: DELETE existing workspaceInvites
    DB-->>N: ok
    N->>DB: INSERT workspaceInvites(token, expiresAt, permissionLevel)
    DB-->>N: ok
    N->>E: Render email (inviterName, workspaceName, workspaceUrl?invite=token)
    E-->>N: React email
    N->>R: resend.emails.send(react email)
    R-->>N: sent/err
    N-->>C: 201 {pending: true}
  end
Loading

@greptile-apps greptile-apps Bot 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.

2 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

workspaceColor: ws.color || undefined,
permissionLevel,
}),
attachments: [logoAttachment],

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.

logoAttachment is undefined - this will cause a runtime error when sending emails to non-existing users

Suggested change
attachments: [logoAttachment],
// attachments: [logoAttachment], // TODO: Define logoAttachment if needed
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/app/api/workspaces/[id]/collaborators/route.ts
Line: 279:279

Comment:
`logoAttachment` is undefined - this will cause a runtime error when sending emails to non-existing users

```suggestion
            // attachments: [logoAttachment], // TODO: Define logoAttachment if needed
```

How can I resolve this? If you propose a fix, please make it concise.

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

1 issue found across 4 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="src/components/email/invite-email.tsx">

<violation number="1" location="src/components/email/invite-email.tsx:13">
P3: `workspaceColor` was added to the template props but is never used in the component, so the email UI can’t reflect the workspace color. Either use it in the template or remove it until it’s wired in.</violation>
</file>

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

Comment thread src/components/email/invite-email.tsx Outdated

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/api/workspaces/[id]/collaborators/route.ts (2)

194-214: ⚠️ Potential issue | 🟠 Major

ws can be undefined — no null guard before property access.

If the workspace row is missing (e.g., deleted between the access check and this query), ws will be undefined and accessing ws.slug, ws.name, ws.color on Lines 196, 201, 203–207 will throw a TypeError. The same issue exists on Line 264. Add a guard after the query on Line 155.

Proposed guard
     const [ws] = await db
         .select({ ... })
         .from(workspaces)
         .where(eq(workspaces.id, workspaceId))
         .limit(1);

+    if (!ws) {
+        return NextResponse.json({ error: "Workspace not found" }, { status: 404 });
+    }
+
     // If user exists, add as collaborator directly

242-260: ⚠️ Potential issue | 🟡 Minor

Delete + insert is not atomic — concurrent invites can race.

Two simultaneous invite requests for the same email could both delete and then both insert, resulting in duplicate rows, or one delete could remove the other's freshly inserted row. Wrap these in a transaction.

Wrap in a transaction
-    await db
-        .delete(workspaceInvites)
-        .where(
-            and(
-                eq(workspaceInvites.workspaceId, workspaceId),
-                eq(workspaceInvites.email, email.trim().toLowerCase())
-            )
-        );
-
-    await db.insert(workspaceInvites).values({
-        workspaceId,
-        email: email.trim().toLowerCase(),
-        token,
-        inviterId: currentUser.userId,
-        permissionLevel: permissionLevel === "viewer" ? "viewer" : "editor",
-        expiresAt: expiresAt.toISOString(),
-    });
+    await db.transaction(async (tx) => {
+        await tx
+            .delete(workspaceInvites)
+            .where(
+                and(
+                    eq(workspaceInvites.workspaceId, workspaceId),
+                    eq(workspaceInvites.email, email.trim().toLowerCase())
+                )
+            );
+
+        await tx.insert(workspaceInvites).values({
+            workspaceId,
+            email: email.trim().toLowerCase(),
+            token,
+            inviterId: currentUser.userId,
+            permissionLevel: permissionLevel === "viewer" ? "viewer" : "editor",
+            expiresAt: expiresAt.toISOString(),
+        });
+    });
🤖 Fix all issues with AI agents
In `@src/app/api/workspaces/`[id]/collaborators/route.ts:
- Line 279: The code references logoAttachment in the pending-invite email path
(attachments: [logoAttachment]) but logoAttachment is not declared or imported,
causing a runtime ReferenceError; either remove the attachments field from the
email payload in the send-invite code path (so invitations send without an
attachment) or properly define/import logoAttachment by creating the attachment
object (e.g., reading the logo file and constructing the { filename,
content/type, cid } object) and use that variable; update the code paths that
build/send the pending-invite email (where attachments: [logoAttachment]
appears) to use the chosen fix and ensure the variable name logoAttachment is
defined in the same module or imported.

In `@src/components/email/invite-email.tsx`:
- Around line 17-22: The prop workspaceColor declared on
InviteEmailTemplateProps is passed by callers but not used in
InviteEmailTemplate; either destructure workspaceColor in the
InviteEmailTemplate function signature and apply it (e.g., set the CTA button
background or heading accent using workspaceColor) so the passed color affects
the rendered template, or remove workspaceColor from InviteEmailTemplateProps
and stop passing it from the route to eliminate dead code; update
InviteEmailTemplate (and any callers) accordingly to keep the prop and usage
consistent.
🧹 Nitpick comments (5)
src/components/email/invite-email.tsx (2)

23-23: roleText silently maps unexpected permission levels to "an editor".

If permissionLevel is anything other than 'viewer' (e.g., 'owner', 'admin', or a typo), the email will say "as an editor." Consider an explicit allowlist or a fallback that omits the role text for unknown values.

Proposed safer mapping
-    const roleText = permissionLevel === 'viewer' ? 'a viewer' : 'an editor';
+    const roleMap: Record<string, string> = { viewer: 'a viewer', editor: 'an editor' };
+    const roleText = permissionLevel ? roleMap[permissionLevel] : undefined;

Then on Line 71:

-                                        {permissionLevel ? ` as ${roleText}` : ''}.
+                                        {roleText ? ` as ${roleText}` : ''}.

26-170: Consider wrapping with <html>, <head>, and <body> for broader email client compatibility.

Many email clients (especially Outlook and older webmail) strip or re-wrap content that doesn't include a proper HTML document skeleton. Starting the template from a bare <div> can lead to inconsistent rendering. A minimal <!DOCTYPE html><html><head><meta charset="utf-8"></head><body>…</body></html> wrapper improves deliverability and rendering consistency.

src/app/api/workspaces/[id]/collaborators/route.ts (3)

1-13: Duplicate doc comment block.

The file header comment (Lines 1–6) is repeated verbatim at Lines 8–13. Remove one of them.

Remove duplicate
 /**
  * Collaborators API - List and invite collaborators
  * 
  * GET /api/workspaces/[id]/collaborators - List collaborators
  * POST /api/workspaces/[id]/collaborators - Invite a new collaborator
  */
-
-/**
- * Collaborators API - List and invite collaborators
- * 
- * GET /api/workspaces/[id]/collaborators - List collaborators
- * POST /api/workspaces/[id]/collaborators - Invite a new collaborator
- */

226-233: Math.random fallback produces guessable invite tokens.

The crypto global is available in all Node.js ≥ 19 and in the Web/Edge runtime, so the fallback should never be reached in Next.js 16. However, if it ever does, Math.random tokens are trivially predictable and an attacker could forge invite links. Consider removing the fallback entirely and letting it fail loudly, or importing node:crypto as a guaranteed server-side alternative.

Remove insecure fallback
     const generateToken = () => {
-        if (typeof crypto !== 'undefined') {
-            const array = new Uint8Array(24);
-            crypto.getRandomValues(array);
-            return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
-        } else {
-            return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
-        }
+        const array = new Uint8Array(24);
+        crypto.getRandomValues(array);
+        return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join('');
     };

136-152: The "non-existent" sentinel is fragile — prefer skipping the query when user is not found.

When invitedUser is null, passing "non-existent" to the equality check works by accident but is unclear and could theoretically collide (however unlikely). It's cleaner to skip the query entirely.

Proposed improvement
-    const [existing] = await db
-        .select({ id: workspaceCollaborators.id })
-        .from(workspaceCollaborators)
-        .where(
-            and(
-                eq(workspaceCollaborators.workspaceId, workspaceId),
-                eq(workspaceCollaborators.userId, invitedUser?.id || "non-existent")
-            )
-        )
-        .limit(1);
-
-    if (existing) {
-        return NextResponse.json(
-            { message: "User is already a collaborator" },
-            { status: 409 }
-        );
-    }
+    if (invitedUser) {
+        const [existing] = await db
+            .select({ id: workspaceCollaborators.id })
+            .from(workspaceCollaborators)
+            .where(
+                and(
+                    eq(workspaceCollaborators.workspaceId, workspaceId),
+                    eq(workspaceCollaborators.userId, invitedUser.id)
+                )
+            )
+            .limit(1);
+
+        if (existing) {
+            return NextResponse.json(
+                { message: "User is already a collaborator" },
+                { status: 409 }
+            );
+        }
+    }

workspaceColor: ws.color || undefined,
permissionLevel,
}),
attachments: [logoAttachment],

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:

rg -n 'logoAttachment' src/app/api/workspaces/[id]/collaborators/route.ts

Repository: ThinkEx-OSS/thinkex

Length of output: 109


🏁 Script executed:

cat -n src/app/api/workspaces/[id]/collaborators/route.ts | head -30

Repository: ThinkEx-OSS/thinkex

Length of output: 1245


🏁 Script executed:

cat -n src/app/api/workspaces/[id]/collaborators/route.ts | sed -n '260,290p'

Repository: ThinkEx-OSS/thinkex

Length of output: 1366


🏁 Script executed:

wc -l src/app/api/workspaces/[id]/collaborators/route.ts

Repository: ThinkEx-OSS/thinkex

Length of output: 117


🏁 Script executed:

cat -n src/app/api/workspaces/[id]/collaborators/route.ts | sed -n '1,50p'

Repository: ThinkEx-OSS/thinkex

Length of output: 1978


logoAttachment is undefined — this will throw a ReferenceError at runtime.

logoAttachment is referenced on line 279 but never declared or imported in this file. When the pending-invite email path executes, this will crash the email send. Although the error is caught (line 281-284), the invitation email will silently fail to send, leaving users without notification of their invite. Either define/import logoAttachment (e.g., read a logo file and construct the attachment object), or remove the attachments field until it's implemented.

Quick fix: remove until properly implemented
             react: InviteEmailTemplate({
                 inviterName: currentUser.name || 'A user',
                 workspaceName: ws.name || 'Workspace',
                 workspaceUrl,
                 workspaceColor: ws.color || undefined,
                 permissionLevel,
             }),
-            attachments: [logoAttachment],
         });
📝 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
attachments: [logoAttachment],
react: InviteEmailTemplate({
inviterName: currentUser.name || 'A user',
workspaceName: ws.name || 'Workspace',
workspaceUrl,
workspaceColor: ws.color || undefined,
permissionLevel,
}),
});
🤖 Prompt for AI Agents
In `@src/app/api/workspaces/`[id]/collaborators/route.ts at line 279, The code
references logoAttachment in the pending-invite email path (attachments:
[logoAttachment]) but logoAttachment is not declared or imported, causing a
runtime ReferenceError; either remove the attachments field from the email
payload in the send-invite code path (so invitations send without an attachment)
or properly define/import logoAttachment by creating the attachment object
(e.g., reading the logo file and constructing the { filename, content/type, cid
} object) and use that variable; update the code paths that build/send the
pending-invite email (where attachments: [logoAttachment] appears) to use the
chosen fix and ensure the variable name logoAttachment is defined in the same
module or imported.

Comment thread src/components/email/invite-email.tsx

@ellipsis-dev ellipsis-dev Bot 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.

Important

Looks good to me! 👍

Reviewed 22ab824 in 9 seconds. Click for details.
  • Reviewed 54 lines of code in 2 files
  • Skipped 0 files when reviewing.
  • Skipped posting 0 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.

Workflow ID: wflow_Q9TDtUIQKZvR10JB

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

@ibraheemshaikh5

Copy link
Copy Markdown
Collaborator Author

@greptile-ai

@greptile-apps greptile-apps Bot 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.

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@greptile-apps

greptile-apps Bot commented Feb 7, 2026

Copy link
Copy Markdown
Contributor
Additional Comments (2)

src/app/api/workspaces/[id]/collaborators/route.ts
Missing workspace guard

ws can be undefined (workspace not found), but the code unconditionally reads ws.slug/ws.name when building workspaceUrl and subject in both the existing-user and pending-invite email paths. This will throw at runtime for invalid/nonexistent workspace IDs. Add an explicit if (!ws) return 404 (or similar) before any ws.* access.


src/app/api/workspaces/[id]/collaborators/route.ts
Duplicate invite emails

When invitedUser exists, this endpoint inserts a workspaceCollaborators row and also sends an invite email. That results in users receiving an “invitation” even though they’ve already been added as a collaborator (and no acceptance step exists on this path). This is likely unintended and can confuse recipients. Either only email on the pending-invite flow, or change the existing-user flow to create an invite/acceptance mechanism instead of directly inserting the collaborator row.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/app/api/workspaces/[id]/collaborators/route.ts (2)

266-275: ⚠️ Potential issue | 🟡 Minor

permissionLevel is not passed to the pending-invite email template.

The direct-invite path (Line 205) passes permissionLevel to InviteEmailTemplate, but the pending-invite path here does not. Pending invitees will see the email without their assigned role, producing an inconsistent experience.

Proposed fix
             react: InviteEmailTemplate({
                 inviterName: currentUser.name || 'A user',
                 workspaceName: ws.name || 'Workspace',
                 workspaceUrl,
+                permissionLevel,
             }),

155-163: ⚠️ Potential issue | 🟡 Minor

ws could be undefined — unguarded access on Line 195 and 262.

If no workspace is found (e.g., deleted between the access check and this query), ws will be undefined. Line 195 (ws.slug) and Line 262 (ws.slug) would throw. Consider adding a guard or returning a 404 if ws is missing.

🧹 Nitpick comments (1)
src/app/api/workspaces/[id]/collaborators/route.ts (1)

1-13: Duplicate doc comment block.

The file-level JSDoc comment (lines 1–6) is repeated verbatim at lines 8–13.

@ibraheemshaikh5

Copy link
Copy Markdown
Collaborator Author

@urjitc

@urjitc
urjitc merged commit 601775f into main Feb 7, 2026
3 of 5 checks passed
@urjitc
urjitc deleted the feature/no-ref/email-share-ui-improvements branch February 7, 2026 03:05
@github-project-automation github-project-automation Bot moved this from Backlog to Done in Dev Board Feb 7, 2026
@sonarqubecloud

sonarqubecloud Bot commented Feb 7, 2026

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants