Skip to content

Add DNS zones#528

Merged
mlsmaycon merged 1 commit intomainfrom
feature/dns-zones
Jan 16, 2026
Merged

Add DNS zones#528
mlsmaycon merged 1 commit intomainfrom
feature/dns-zones

Conversation

@heisbrot
Copy link
Copy Markdown
Contributor

@heisbrot heisbrot commented Jan 16, 2026

Summary by CodeRabbit

  • New Features

    • Added DNS Zones management with full CRUD operations and record management capabilities
    • Added Help and Support dropdown menu in the header with links to documentation and community resources
    • Added DNS Zones section to group management pages
  • Improvements

    • Enhanced dropdown menu to support link navigation
    • Improved peer expiration settings UI organization
    • Updated user avatar rendering for better performance
    • Extended input tooltip positioning options
  • UI/Style

    • Refreshed color palette with new gray tones
    • Updated header layout and spacing
    • Visual icon enhancements throughout the dashboard
  • Chores

    • Updated configuration and announcements
    • Added ip-address library dependency

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 16, 2026

📝 Walkthrough

Walkthrough

This PR introduces comprehensive DNS zone management functionality, including new DNS zone and record CRUD operations via a provider context, zone/record management tables and modals, integration of zones into group pages, and refactored DNS module paths. It also adds a help and support button, enhances peer expiration handling, and updates various UI components and styling.

Changes

Cohort / File(s) Summary
Configuration & Package config.json, package.json
DNS Interfaces & Types src/interfaces/DNS.ts
DNS Provider & Context src/modules/dns/zones/DNSZonesProvider.tsx
DNS Zone Modal & Logic src/modules/dns/zones/DNSZoneModal.tsx
DNS Record Modal & Logic src/modules/dns/zones/DNSRecordModal.tsx
DNS Records Table & Cells src/modules/dns/zones/records/DNSRecordsTable.tsx, src/modules/dns/zones/records/DNSRecord*Cell.tsx
DNS Zones Table & Cells src/modules/dns/zones/table/DNSZonesTable.tsx, src/modules/dns/zones/table/DNSZones*Cell.tsx
DNS Module Path Refactoring src/app/(dashboard)/dns/nameservers/page.tsx, src/modules/dns/nameservers/*, src/modules/groups/details/GroupNameserversSection.tsx
DNS Navigation & Layout src/app/(dashboard)/dns/zones/page.tsx, src/app/(dashboard)/dns/zones/layout.tsx, src/layouts/Navigation.tsx
Group Integration with DNS src/modules/groups/details/GroupDNSZonesSection.tsx, src/modules/groups/details/useGroupDetails.ts, src/modules/groups/useGroupsUsage.tsx, src/app/(dashboard)/group/page.tsx
Groups Table Updates src/modules/groups/table/GroupsTable.tsx
Help & Support UI src/components/ui/HelpAndSupportButton.tsx
Icon Assets src/assets/icons/DNSZoneIcon.tsx, src/assets/icons/SlackIcon.tsx
Component Enhancements src/components/Button.tsx, src/components/DropdownMenu.tsx, src/components/Input.tsx, src/components/table/Table.tsx, src/components/ui/NoResults.tsx
Header & Layout src/layouts/Header.tsx
Peer Expiration Management src/modules/peer/PeerExpirationSettings.tsx, src/modules/peer/PeerExpirationToggle.tsx, src/modules/peer/PeerSSHToggle.tsx, src/modules/settings/AuthenticationTab.tsx, src/app/(dashboard)/peer/page.tsx
User Avatar & Dropdown src/components/ui/UserAvatar.tsx, src/components/ui/UserDropdown.tsx
Provider & Context src/contexts/AnnouncementProvider.tsx, src/contexts/DialogProvider.tsx
Styling Updates tailwind.config.ts, src/modules/routes/RouteTable.tsx
Miscellaneous src/components/ui/PeerCountBadge.tsx

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant DNSZonesTable as DNS Zones Table
    participant DNSZonesProvider as DNS Zones Provider
    participant API as API
    participant Cache as SWR Cache
    participant Dialog as Dialog Context
    participant Toast as Toast Notification

    User->>DNSZonesTable: Click "Add Zone" or "Edit"
    DNSZonesTable->>DNSZonesProvider: openZoneModal(zone?)
    DNSZonesProvider->>DNSZonesProvider: Set modal open state
    DNSZonesProvider->>User: Render DNSZoneModal

    User->>User: Fill zone form<br/>(domain, groups, toggles)
    User->>DNSZonesProvider: Submit (Create or Update)
    
    alt Create Zone
        DNSZonesProvider->>API: POST /dns/zones
    else Update Zone
        DNSZonesProvider->>API: PUT /dns/zones/{id}
    end

    API-->>DNSZonesProvider: Success response
    DNSZonesProvider->>Cache: Trigger SWR mutation
    Cache-->>DNSZonesProvider: Refetch zones
    DNSZonesProvider->>Dialog: Close modal
    DNSZonesProvider->>Toast: Show success notification
    Toast-->>User: Display notification
    DNSZonesTable-->>User: Update table with new zones

    User->>DNSZonesTable: Expand zone (view records)
    DNSZonesTable->>DNSZonesTable: Render DNSRecordsTable

    User->>DNSZonesTable: Click "Add Record"
    DNSZonesTable->>DNSZonesProvider: openRecordModal(zone, record?)
    DNSZonesProvider->>User: Render DNSRecordModal

    User->>User: Fill record form<br/>(name, type, content, TTL)
    User->>DNSZonesProvider: Submit (Add or Update)
    DNSZonesProvider->>API: POST/PUT /dns/zones/{zoneId}/records
    API-->>DNSZonesProvider: Success response
    DNSZonesProvider->>Cache: Trigger zone cache mutation
    DNSZonesProvider->>Toast: Show success notification
    DNSZonesTable-->>User: Update records table
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • #522: Modifies src/modules/groups/details/useGroupDetails.ts which this PR also extends to include DNS zones data fetching and filtering by distribution groups.

Suggested reviewers

  • mlsmaycon
  • braginini

🐰 DNS zones sprout in gardens green,
Records dance in tables keen,
Providers manage, modals gleam,
A robust feature, a dev's dream!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title 'Add DNS zones' clearly and concisely summarizes the main change in the changeset, which introduces comprehensive DNS zones management features throughout the codebase.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

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

Caution

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

⚠️ Outside diff range comments (2)
src/components/Input.tsx (1)

20-168: Handle the new "bottom" position in layout + tooltip props.

errorTooltipPosition now allows "bottom", but the positioning classes and FullTooltip props still only support "top" / "top-right". This will likely render incorrectly or not at all for "bottom".

✅ Suggested fix
           <div
             className={cn(
               errorTooltipPosition == "top" &&
                 "absolute right-0 top-2 h-[0px] w-full flex items-center pr-3 justify-center",
               errorTooltipPosition == "top-right" &&
                 "absolute -right-6 top-2 h-[0px] w-full flex items-center pr-3 justify-end",
+              errorTooltipPosition == "bottom" &&
+                "absolute right-0 bottom-2 h-[0px] w-full flex items-center pr-3 justify-center",
             )}
           >
             <FullTooltip
               content={
                 <div className={"text-xs text-red-500 inline-flex"}>
                   <AlertCircle
                     size={13}
                     className={"top-[1px] relative mr-2"}
                   />
                   {error}
                 </div>
               }
               interactive={false}
-              align={errorTooltipPosition == "top" ? "center" : "end"}
-              side={"top"}
+              align={
+                errorTooltipPosition == "top" || errorTooltipPosition == "bottom"
+                  ? "center"
+                  : "end"
+              }
+              side={errorTooltipPosition == "bottom" ? "bottom" : "top"}
               keepOpen={true}
             >
               &nbsp;
             </FullTooltip>
           </div>
src/modules/peer/PeerExpirationToggle.tsx (1)

58-60: Grammar fix: "an setup-key" should be "a setup-key".

Proposed fix
              <span>
-               This setting is disabled for all peers added with an setup-key.
+               This setting is disabled for all peers added with a setup-key.
              </span>
🤖 Fix all issues with AI agents
In `@config.json`:
- Around line 1-9: This file contains hardcoded Auth0 credentials (fields like
"auth0Auth", "authAuthority", "authClientId", "authScopesSupported",
"authAudience", "apiOrigin", "grpcApiOrigin", "latestVersion"); replace the
committed secret config by either removing it and adding it to .gitignore (e.g.,
keep a .local-config.json out of repo) or rename it to config.example.json and
strip values into placeholders, then update README or local dev docs to instruct
developers to set AUTH0_CLIENT_ID, AUTH0_DOMAIN and other env vars (used by
docker/init_react_envs.sh) for local runs.

In `@src/components/ui/HelpAndSupportButton.tsx`:
- Around line 57-141: The DropdownMenuItem usages with external hrefs are
incorrectly using asChild which causes Radix to merge href into the first child
<div> (breaking links and excluding DropdownMenuShortcut); update each affected
instance of DropdownMenuItem (the ones that currently include asChild and
explicit target/rel: the blocks rendering Documentation, Troubleshooting,
NetBird Forum, NetBird Slack, and Feedback) by removing the asChild prop and the
manual target/rel attributes so the custom DropdownMenuItem can render its
internal <a> wrapper and handle target/rel correctly; keep the
DropdownMenuShortcut and internal <div> content unchanged and follow the same
pattern used by the support@netbird.io item and the existing correct example.

In `@src/modules/dns/zones/records/DNSRecordActionCell.tsx`:
- Around line 20-33: The action buttons in DNSRecordActionCell are causing
parent row click handlers to fire; update both button onClick handlers (the one
calling openRecordModal(zone, record) and the one calling deleteRecord(zone,
record)) to stop event propagation first (e.g., receive the click event, call
event.stopPropagation()) before invoking openRecordModal or deleteRecord so
row-level click-to-expand/selection isn’t triggered by the buttons.

In `@src/modules/dns/zones/records/DNSRecordContentCell.tsx`:
- Around line 11-16: The CopyToClipboardText wrapper isn't receiving the content
to copy; pass record.content into CopyToClipboardText so its internal
copyToClipboard(message) receives the record value—for example, provide
message={record.content} (or the prop name CopyToClipboardText expects) on the
CopyToClipboardText component that currently wraps the span displaying
{record.content}.

In `@src/modules/dns/zones/records/DNSRecordNameCell.tsx`:
- Around line 11-14: The CopyToClipboardText component is being rendered without
the required message prop in DNSRecordNameCell.tsx, which can cause the copied
value to be undefined; update the JSX where CopyToClipboardText is used (in
DNSRecordNameCell) to pass the record.name explicitly as the message prop (e.g.,
message={record.name}) so the component receives the text to copy.

In `@src/modules/dns/zones/records/DNSRecordsTable.tsx`:
- Around line 59-75: The DataTable is passed zone.records which is optional on
DNSZone; update the DNSRecordsTable component to guard by defaulting
zone.records to an empty array before passing into DataTable (e.g., use
zone.records ?? [] or Array.isArray check) so the DataTable's data prop always
receives an array; locate the DataTable usage in DNSRecordsTable and replace
data={zone.records} with a safe expression like data={zone.records ?? []} (or
validate and map if needed) and ensure any downstream uses of sorting/pagination
handle the empty array case.

In `@src/modules/dns/zones/table/DNSZonesGroupCell.tsx`:
- Line 44: The current check in DNSZonesGroupCell (where the code inspects
zone?.distribution_groups) only treats falsy values as empty, so an empty array
still proceeds to render GroupsRow; change the guard to treat an empty array as
empty by checking that distribution_groups is either falsy or has length === 0
and return EmptyRow in that case (update the conditional that references
zone?.distribution_groups to include a length check), ensuring GroupsRow only
renders when distribution_groups is a non-empty array.

In `@src/modules/dns/zones/table/DNSZonesRecordsCell.tsx`:
- Around line 22-27: In DNSZonesRecordsCell.tsx the Badge component is given a
pointer cursor, useHover and an onClick that does nothing which creates a
misleading interactive affordance; either remove the interactivity by deleting
the onClick handler and remove useHover and the "cursor-pointer" class from the
Badge, or wire the Badge to a real action (e.g., call an
openRecords/openZoneRecords function or router navigation) and implement that
handler instead so the visual affordance matches behavior.

In `@src/modules/dns/zones/table/DNSZonesTable.tsx`:
- Around line 125-135: zonesWithGroups useMemo can crash when groups is
undefined; update the mapping that builds groups_search to guard groups (and
distribution_groups) by defaulting to empty arrays. For example, in the useMemo
that defines zonesWithGroups, change the groups?.map(...) to (groups ??
[]).map(...) and ensure zone?.distribution_groups?.includes(...) is safe (e.g.,
(zone?.distribution_groups ?? []).includes(...)) so groups_search is produced
without throwing and still typed as DNSZone.
- Around line 76-85: The accessorFn for id "searchString" can crash when
row.records is undefined because calling .map(...).join("") on undefined throws;
update the accessorFn to default records to an empty array or guard before
mapping (e.g., use row?.records ?? [] or check if Array.isArray(row.records)) so
each records-based expression uses a safe array before .map(...).join("") and
returns an empty string when records are missing.

In `@src/modules/peer/PeerSSHToggle.tsx`:
- Around line 313-337: The modal/provider rendering is currently gated by
permission?.policies.create which prevents users with only policies.update from
opening AccessControlModalContent for edits; change the condition to check
either create or update (e.g., permission?.policies.create ||
permission?.policies.update) so PoliciesProvider, Modal,
AccessControlModalContent (with policy={currentPolicy} and onSuccess that calls
setPolicyModal and setCurrentPolicy) and PeerSSHPolicyModal render for users
with update permission as well.
🧹 Nitpick comments (14)
src/components/ui/UserAvatar.tsx (3)

15-20: Consider using an integer for the "medium" size.

The value 35.2 may cause subpixel rendering inconsistencies in some browsers. Consider rounding to 35 or 36 for cleaner rendering, unless the exact 2.2rem match with the fallback div is intentional.

💡 Suggested change
   const getAvatarSize = () => {
     if (size === "small") return 32;
     if (size === "default") return 40;
     if (size === "large") return 48;
-    return 35.2;
+    return 35;
   };

22-30: Improve accessibility with a meaningful alt attribute.

The empty alt="" marks the image as decorative, but a user avatar is meaningful content. Consider using a descriptive alt text.

♿ Suggested improvement
     <Image
       src={user?.picture}
-      alt={""}
+      alt={user?.name ? `${user.name}'s avatar` : "User avatar"}
       onError={() => setPictureLoaded(false)}
       width={getAvatarSize()}
       height={getAvatarSize()}
       className={"rounded-full"}
     />

24-24: Minor: Redundant optional chaining.

user?.picture is already verified on line 22, so the optional chaining on src={user?.picture} is unnecessary. Consider using user.picture for consistency.

src/modules/peer/PeerExpirationSettings.tsx (3)

18-23: Local state may become stale if peer data changes externally.

The local state is initialized from peer props but won't update if the peer data is refetched/mutated elsewhere. Consider resetting state when peer.id changes, or derive state directly from peer props.

Option: Sync state with peer changes
+ import { useEffect } from "react";

  const [peerLoginExpiration, setPeerLoginExpiration] = useState(
    peer.login_expiration_enabled,
  );
  const [peerInactivityExpiration, setPeerInactivityExpiration] = useState(
    peer.inactivity_expiration_enabled,
  );

+ useEffect(() => {
+   setPeerLoginExpiration(peer.login_expiration_enabled);
+   setPeerInactivityExpiration(peer.inactivity_expiration_enabled);
+ }, [peer.id, peer.login_expiration_enabled, peer.inactivity_expiration_enabled]);

25-49: Consider handling update failures to keep UI state consistent.

If update() fails, the local state (peerLoginExpiration, peerInactivityExpiration) will be out of sync with the server. The notify shows a loading message but doesn't revert state on error.

Proposed fix: Revert state on failure
  const updateExpiration = async ({
    loginExpiration,
    inactivityExpiration,
  }: {
    loginExpiration?: boolean;
    inactivityExpiration?: boolean;
  }) => {
    if (!permission?.peers.update) return;

+   const previousLogin = peerLoginExpiration;
+   const previousInactivity = peerInactivityExpiration;

    const promise = update({
      loginExpiration,
      inactivityExpiration,
    }).then(() => {
      mutate("/peers/" + peer.id);
+   }).catch((error) => {
+     setPeerLoginExpiration(previousLogin);
+     setPeerInactivityExpiration(previousInactivity);
+     throw error;
    });

    notify({
      title: peer.name,
      description: "Expiration was successfully updated",
      promise,
      loadingMessage: "Updating setting...",
    });

    return promise;
  };

71-101: Minor: Redundant disabled styling applied in multiple places.

The disabled styling (opacity-50 pointer-events-none) is applied both on the container div (lines 75-76) and on the inner PeerExpirationToggle via className (lines 97-98) when !peerLoginExpiration. This duplication is harmless but could be simplified.

src/components/DropdownMenu.tsx (2)

132-138: Anchor element doesn't fill the full clickable area.

The <a> element is rendered inline within the menu item, which means only the text portion is clickable for navigation—not the full padded area. Users clicking the padding around the text will trigger the Radix item behavior but won't navigate.

Consider applying styles to make the anchor fill the entire item area.

♻️ Proposed fix
       {href ? (
-          <a href={href} target={target} rel={rel}>
+          <a
+            href={href}
+            target={target}
+            rel={rel}
+            className="flex items-center gap-2 w-full h-full"
+          >
             {props.children}
           </a>
         ) : (
           props.children
         )}

124-129: Consider passing the original onClick through for href items.

Currently, onClick is completely ignored when href is set. This prevents consumers from performing side effects (e.g., analytics tracking, closing modals) alongside navigation.

♻️ Proposed enhancement
         onClick={(e) => {
-          if (href) return;
-          e.preventDefault();
-          e.stopPropagation();
-          onClick && onClick(e);
+          if (href) {
+            onClick?.(e);
+            return;
+          }
+          e.preventDefault();
+          e.stopPropagation();
+          onClick?.(e);
         }}
src/interfaces/DNS.ts (1)

12-20: Consider referencing DNSRecordType in DNSRecord interface.

The type field in DNSRecord duplicates the literal union that's already defined as DNSRecordType. Using the type alias improves maintainability.

♻️ Proposed fix
+export type DNSRecordType = "A" | "AAAA" | "CNAME";
+
 export interface DNSRecord {
   id?: string;
   name: string;
-  type: "A" | "AAAA" | "CNAME";
+  type: DNSRecordType;
   content: string;
   ttl: number;
 }
-
-export type DNSRecordType = "A" | "AAAA" | "CNAME";
src/modules/dns/zones/records/DNSRecordTypeCell.tsx (1)

2-2: Unused React import.

With modern JSX transform (React 17+), explicit React import isn't required for JSX. This import can be removed.

♻️ Proposed fix
 import Badge from "@components/Badge";
-import * as React from "react";
 import { DNSRecord } from "@/interfaces/DNS";
src/modules/dns/zones/records/DNSRecordTimeToLiveCell.tsx (1)

1-5: Move getTTLLabel to a shared DNS util.

Importing it from DNSRecordModal.tsx couples this cell to a modal and can bloat bundles or create circular dependencies. Consider relocating it (e.g., src/modules/dns/zones/utils.ts) and importing from there.

src/modules/groups/details/useGroupDetails.ts (1)

36-76: Consider server-side filtering for zones fetch.

Fetching all zones and filtering client-side may become costly at scale. If the API supports it, consider a group-scoped query (e.g., /dns/zones?group_id=...) or a dedicated endpoint to reduce payload and latency.

src/modules/dns/zones/table/DNSZonesActionCell.tsx (1)

32-34: Add an accessible label to the icon-only actions button.
Screen readers need a label for the kebab button.

♿ Suggested tweak
-          <Button variant={"secondary"} className={"!px-3"}>
+          <Button
+            variant={"secondary"}
+            className={"!px-3"}
+            aria-label="Zone actions"
+          >
src/components/ui/HelpAndSupportButton.tsx (1)

36-46: Add an accessible label to the help button.
Icon-only controls should expose an aria-label.

♿ Suggested tweak
-        <Button
-          size={"xs"}
-          variant={"default-outline"}
-          className={cn(
-            "!rounded-full h-[38px] w-[38px] !p-0",
-            dropdownOpen && "text-white",
-          )}
-        >
+        <Button
+          size={"xs"}
+          variant={"default-outline"}
+          className={cn(
+            "!rounded-full h-[38px] w-[38px] !p-0",
+            dropdownOpen && "text-white",
+          )}
+          aria-label="Help and support"
+        >
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3affa89 and c204d52.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (59)
  • config.json
  • package.json
  • src/app/(dashboard)/dns/nameservers/page.tsx
  • src/app/(dashboard)/dns/zones/layout.tsx
  • src/app/(dashboard)/dns/zones/page.tsx
  • src/app/(dashboard)/group/page.tsx
  • src/app/(dashboard)/peer/page.tsx
  • src/assets/icons/DNSZoneIcon.tsx
  • src/assets/icons/SlackIcon.tsx
  • src/components/Button.tsx
  • src/components/DropdownMenu.tsx
  • src/components/Input.tsx
  • src/components/table/Table.tsx
  • src/components/ui/HelpAndSupportButton.tsx
  • src/components/ui/NoResults.tsx
  • src/components/ui/PeerCountBadge.tsx
  • src/components/ui/UserAvatar.tsx
  • src/components/ui/UserDropdown.tsx
  • src/contexts/AnnouncementProvider.tsx
  • src/contexts/DialogProvider.tsx
  • src/interfaces/DNS.ts
  • src/layouts/Header.tsx
  • src/layouts/Navigation.tsx
  • src/modules/dns/nameservers/NameserverModal.tsx
  • src/modules/dns/nameservers/NameserverTemplateModal.tsx
  • src/modules/dns/nameservers/table/NameserverActionCell.tsx
  • src/modules/dns/nameservers/table/NameserverActiveCell.tsx
  • src/modules/dns/nameservers/table/NameserverDistributionGroupsCell.tsx
  • src/modules/dns/nameservers/table/NameserverGroupTable.tsx
  • src/modules/dns/nameservers/table/NameserverMatchDomainsCell.tsx
  • src/modules/dns/nameservers/table/NameserverNameCell.tsx
  • src/modules/dns/nameservers/table/NameserverNameserversCell.tsx
  • src/modules/dns/zones/DNSRecordModal.tsx
  • src/modules/dns/zones/DNSZoneModal.tsx
  • src/modules/dns/zones/DNSZonesProvider.tsx
  • src/modules/dns/zones/records/DNSRecordActionCell.tsx
  • src/modules/dns/zones/records/DNSRecordContentCell.tsx
  • src/modules/dns/zones/records/DNSRecordNameCell.tsx
  • src/modules/dns/zones/records/DNSRecordTimeToLiveCell.tsx
  • src/modules/dns/zones/records/DNSRecordTypeCell.tsx
  • src/modules/dns/zones/records/DNSRecordsTable.tsx
  • src/modules/dns/zones/table/DNSZonesActionCell.tsx
  • src/modules/dns/zones/table/DNSZonesActiveCell.tsx
  • src/modules/dns/zones/table/DNSZonesGroupCell.tsx
  • src/modules/dns/zones/table/DNSZonesNameCell.tsx
  • src/modules/dns/zones/table/DNSZonesRecordsCell.tsx
  • src/modules/dns/zones/table/DNSZonesSearchDomainCell.tsx
  • src/modules/dns/zones/table/DNSZonesTable.tsx
  • src/modules/groups/details/GroupDNSZonesSection.tsx
  • src/modules/groups/details/GroupNameserversSection.tsx
  • src/modules/groups/details/useGroupDetails.ts
  • src/modules/groups/table/GroupsTable.tsx
  • src/modules/groups/useGroupsUsage.tsx
  • src/modules/peer/PeerExpirationSettings.tsx
  • src/modules/peer/PeerExpirationToggle.tsx
  • src/modules/peer/PeerSSHToggle.tsx
  • src/modules/routes/RouteTable.tsx
  • src/modules/settings/AuthenticationTab.tsx
  • tailwind.config.ts
💤 Files with no reviewable changes (1)
  • src/modules/settings/AuthenticationTab.tsx
🧰 Additional context used
🧬 Code graph analysis (32)
src/components/ui/HelpAndSupportButton.tsx (3)
src/utils/helpers.ts (1)
  • cn (6-8)
src/utils/netbird.ts (1)
  • isNetBirdHosted (18-22)
src/assets/icons/SlackIcon.tsx (1)
  • SlackIcon (3-30)
src/modules/dns/zones/records/DNSRecordTypeCell.tsx (2)
src/interfaces/DNS.ts (1)
  • DNSRecord (12-18)
src/components/Badge.tsx (1)
  • Badge (56-78)
src/modules/dns/zones/table/DNSZonesGroupCell.tsx (7)
src/interfaces/DNS.ts (1)
  • DNSZone (1-10)
src/contexts/GroupsProvider.tsx (1)
  • useGroups (163-163)
src/modules/dns/zones/DNSZonesProvider.tsx (1)
  • useDNSZones (264-264)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/interfaces/Group.ts (1)
  • Group (1-12)
src/modules/common-table-rows/EmptyRow.tsx (1)
  • EmptyRow (7-9)
src/modules/common-table-rows/GroupsRow.tsx (1)
  • GroupsRow (40-101)
src/modules/dns/zones/records/DNSRecordActionCell.tsx (4)
src/interfaces/DNS.ts (1)
  • DNSRecord (12-18)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/modules/dns/zones/DNSZonesProvider.tsx (1)
  • useDNSZones (264-264)
src/modules/dns/zones/records/DNSRecordsTable.tsx (1)
  • useDNSZone (80-80)
src/components/ui/PeerCountBadge.tsx (1)
src/contexts/GroupsProvider.tsx (1)
  • useGroups (163-163)
src/modules/dns/zones/table/DNSZonesNameCell.tsx (3)
src/interfaces/DNS.ts (1)
  • DNSZone (1-10)
src/utils/helpers.ts (1)
  • cn (6-8)
src/modules/common-table-rows/ActiveInactiveRow.tsx (1)
  • ActiveInactiveRow (17-57)
src/modules/dns/zones/table/DNSZonesSearchDomainCell.tsx (4)
src/interfaces/DNS.ts (1)
  • DNSZone (1-10)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/modules/dns/zones/DNSZonesProvider.tsx (1)
  • useDNSZones (264-264)
src/components/ToggleSwitch.tsx (1)
  • ToggleSwitch (71-71)
src/layouts/Navigation.tsx (1)
src/components/SidebarItem.tsx (1)
  • SidebarItem (26-142)
src/app/(dashboard)/dns/nameservers/page.tsx (1)
src/modules/dns/nameservers/table/NameserverGroupTable.tsx (1)
  • NameserverGroupTable (101-298)
src/components/ui/NoResults.tsx (1)
src/utils/helpers.ts (1)
  • cn (6-8)
src/modules/peer/PeerSSHToggle.tsx (4)
src/utils/api.tsx (1)
  • useFetchApi (120-167)
src/interfaces/Policy.ts (1)
  • Policy (4-12)
src/contexts/PoliciesProvider.tsx (1)
  • PoliciesProvider (25-95)
src/modules/peer/PeerSSHPolicyModal.tsx (1)
  • PeerSSHPolicyModal (13-34)
src/modules/dns/zones/DNSRecordModal.tsx (5)
src/interfaces/DNS.ts (4)
  • DNSZone (1-10)
  • DNSRecord (12-18)
  • DNSRecordType (20-20)
  • DNS_RECORDS_DOCS_LINK (23-23)
src/modules/dns/zones/DNSZonesProvider.tsx (1)
  • useDNSZones (264-264)
src/components/Separator.tsx (1)
  • Separator (1-3)
src/components/HelpText.tsx (1)
  • HelpText (9-25)
src/components/InlineLink.tsx (1)
  • InlineLink (37-43)
src/assets/icons/SlackIcon.tsx (1)
src/assets/icons/IconProperties.tsx (2)
  • IconProps (1-5)
  • iconProperties (14-26)
src/app/(dashboard)/dns/zones/page.tsx (5)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/interfaces/DNS.ts (2)
  • DNSZone (1-10)
  • DNS_ZONE_DOCS_LINK (22-22)
src/hooks/usePortalElement.tsx (1)
  • usePortalElement (3-12)
src/layouts/PageContainer.tsx (1)
  • PageContainer (9-25)
src/modules/dns/zones/DNSZonesProvider.tsx (1)
  • DNSZonesProvider (33-262)
src/components/ui/UserAvatar.tsx (1)
src/utils/helpers.ts (1)
  • cn (6-8)
src/modules/dns/zones/records/DNSRecordTimeToLiveCell.tsx (2)
src/interfaces/DNS.ts (1)
  • DNSRecord (12-18)
src/modules/dns/zones/DNSRecordModal.tsx (1)
  • getTTLLabel (347-359)
src/modules/groups/details/useGroupDetails.ts (2)
src/interfaces/DNS.ts (1)
  • DNSZone (1-10)
src/utils/api.tsx (1)
  • useFetchApi (120-167)
src/assets/icons/DNSZoneIcon.tsx (1)
src/assets/icons/IconProperties.tsx (2)
  • IconProps (1-5)
  • iconProperties (14-26)
src/modules/peer/PeerExpirationSettings.tsx (6)
src/contexts/PeerProvider.tsx (1)
  • usePeer (193-193)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/modules/account/useAccount.tsx (1)
  • useAccount (6-21)
src/components/Notification.tsx (1)
  • notify (151-155)
src/modules/peer/PeerExpirationToggle.tsx (1)
  • PeerExpirationToggle (25-114)
src/utils/helpers.ts (1)
  • cn (6-8)
src/components/DropdownMenu.tsx (1)
src/utils/helpers.ts (1)
  • cn (6-8)
src/modules/groups/details/GroupDNSZonesSection.tsx (5)
src/interfaces/DNS.ts (1)
  • DNSZone (1-10)
src/contexts/GroupProvider.tsx (1)
  • useGroupContext (329-335)
src/modules/groups/details/GroupDetailsTableContainer.tsx (1)
  • GroupDetailsTableContainer (14-47)
src/modules/dns/zones/DNSZonesProvider.tsx (1)
  • DNSZonesProvider (33-262)
src/modules/dns/zones/table/DNSZonesTable.tsx (1)
  • DNSZonesTable (98-282)
src/modules/dns/zones/records/DNSRecordContentCell.tsx (2)
src/interfaces/DNS.ts (1)
  • DNSRecord (12-18)
src/components/CopyToClipboardText.tsx (1)
  • CopyToClipboardText (14-60)
src/app/(dashboard)/group/page.tsx (3)
src/assets/icons/DNSZoneIcon.tsx (1)
  • DNSZoneIcon (3-19)
src/utils/helpers.ts (1)
  • singularize (247-259)
src/modules/groups/details/GroupDNSZonesSection.tsx (1)
  • GroupDNSZonesSection (8-29)
src/modules/dns/zones/records/DNSRecordNameCell.tsx (2)
src/interfaces/DNS.ts (1)
  • DNSRecord (12-18)
src/components/CopyToClipboardText.tsx (1)
  • CopyToClipboardText (14-60)
src/layouts/Header.tsx (1)
src/components/ui/HelpAndSupportButton.tsx (1)
  • HelpAndSupportButton (27-145)
src/modules/peer/PeerExpirationToggle.tsx (5)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/modules/account/useAccount.tsx (1)
  • useAccount (6-21)
src/components/InlineLink.tsx (1)
  • InlineLink (37-43)
src/components/FullTooltip.tsx (1)
  • FullTooltip (33-108)
src/components/FancyToggleSwitch.tsx (1)
  • FancyToggleSwitch (50-105)
src/modules/dns/zones/table/DNSZonesTable.tsx (13)
src/interfaces/DNS.ts (2)
  • DNSZone (1-10)
  • DNS_ZONE_DOCS_LINK (22-22)
src/modules/dns/zones/table/DNSZonesNameCell.tsx (1)
  • DNSZonesNameCell (11-38)
src/modules/dns/zones/table/DNSZonesActiveCell.tsx (1)
  • DNSZonesActiveCell (11-32)
src/modules/dns/zones/table/DNSZonesGroupCell.tsx (1)
  • DNSZonesGroupCell (15-60)
src/modules/dns/zones/table/DNSZonesSearchDomainCell.tsx (1)
  • DNSZonesSearchDomainCell (11-32)
src/modules/dns/zones/table/DNSZonesActionCell.tsx (1)
  • DNSZonesActionCell (18-58)
src/contexts/GroupsProvider.tsx (1)
  • useGroups (163-163)
src/hooks/useLocalStorage.tsx (1)
  • useLocalStorage (20-124)
src/components/table/DataTable.tsx (1)
  • DataTable (184-635)
src/modules/dns/zones/records/DNSRecordsTable.tsx (1)
  • DNSRecordsTable (54-78)
src/assets/icons/DNSZoneIcon.tsx (1)
  • DNSZoneIcon (3-19)
src/components/ui/GetStartedTest.tsx (1)
  • GetStartedTest (16-79)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/modules/dns/zones/table/DNSZonesActiveCell.tsx (4)
src/interfaces/DNS.ts (1)
  • DNSZone (1-10)
src/contexts/PermissionsProvider.tsx (1)
  • usePermissions (39-39)
src/modules/dns/zones/DNSZonesProvider.tsx (1)
  • useDNSZones (264-264)
src/components/ToggleSwitch.tsx (1)
  • ToggleSwitch (71-71)
src/modules/dns/zones/DNSZonesProvider.tsx (7)
src/interfaces/DNS.ts (2)
  • DNSZone (1-10)
  • DNSRecord (12-18)
src/interfaces/Group.ts (1)
  • Group (1-12)
src/utils/api.tsx (1)
  • useApiCall (169-212)
src/contexts/DialogProvider.tsx (1)
  • useDialog (134-134)
src/components/Notification.tsx (1)
  • notify (151-155)
src/modules/dns/zones/DNSZoneModal.tsx (1)
  • DNSZoneModal (37-65)
src/modules/dns/zones/DNSRecordModal.tsx (1)
  • DNSRecordModal (43-74)
src/app/(dashboard)/peer/page.tsx (1)
src/modules/peer/PeerExpirationSettings.tsx (1)
  • PeerExpirationSettings (12-105)
src/modules/groups/useGroupsUsage.tsx (2)
src/utils/api.tsx (1)
  • useFetchApi (120-167)
src/interfaces/DNS.ts (1)
  • DNSZone (1-10)
src/modules/groups/table/GroupsTable.tsx (2)
src/assets/icons/DNSZoneIcon.tsx (1)
  • DNSZoneIcon (3-19)
src/modules/groups/table/GroupsCountCell.tsx (1)
  • GroupsCountCell (15-59)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build_n_push
🔇 Additional comments (61)
src/components/table/Table.tsx (1)

106-108: LGTM!

The hover background color adjustment for minimal table rows (from nb-gray-900/10 to nb-gray-910/[15%]) is a valid styling refinement. The Tailwind arbitrary value syntax is correct.

src/components/ui/UserAvatar.tsx (1)

31-46: LGTM!

The fallback UI correctly displays the user's initial with appropriate sizing that aligns with the Image component dimensions.

src/app/(dashboard)/peer/page.tsx (4)

67-67: LGTM!

The import of PeerExpirationSettings is correctly added to support the refactored expiration management.


100-102: LGTM!

Using peer?.id as the React key is appropriate here. The parent PeerProvider already uses key={peerId}, ensuring proper remounting when the peer changes.


146-149: LGTM!

The hasChanges tracking now correctly excludes expiration states since they are managed independently by PeerExpirationSettings. This separation of concerns keeps the save flow focused on group changes only.

Also applies to: 169-169


265-265: LGTM!

Clean delegation to PeerExpirationSettings component. This encapsulates expiration-related logic and UI, improving maintainability.

src/modules/peer/PeerExpirationSettings.tsx (2)

1-10: LGTM!

Imports are clean and appropriately organized, using the project's established alias patterns.


61-69: LGTM!

Good UX: when login expiration is disabled, inactivity expiration is automatically turned off as well, maintaining logical consistency between the two related settings.

src/modules/peer/PeerExpirationToggle.tsx (5)

6-12: LGTM!

New imports are appropriate for the enhanced functionality: ArrowUpRightIcon for the settings link, useMemo for memoized tooltip content, InlineLink for navigation, and useAccount for account-level settings.


22-22: LGTM!

The new type prop with a sensible default value enables this component to be reused for both login expiration and inactivity expiration scenarios.

Also applies to: 34-34


37-49: LGTM!

Account-level global setting checks are properly implemented, correctly mapping the type prop to the corresponding account setting. This ensures the toggle respects admin-configured global policies.


51-90: LGTM!

The useMemo for tooltip content is well-structured with correct dependencies. The conditional rendering provides clear, actionable feedback to users about why the toggle may be disabled.


92-113: LGTM!

The render logic correctly:

  • Disables the tooltip when no content is needed
  • Disables the toggle when global settings are off or user lacks permission
  • Forces value to false when global setting is disabled, preventing misleading UI state
package.json (1)

64-64: The ip-address@10.1.0 package is legitimate, maintained, and the latest version. No security vulnerabilities or audit issues were detected. This dependency is appropriate for IP address parsing in the DNS zone management functionality.

src/modules/peer/PeerSSHToggle.tsx (2)

40-45: Permission-gated fetch looks good.

Tying the policies fetch to permission?.policies.read is a clean, permission-aware optimization.


209-213: Button permission gating looks solid.

Disabling creation when policies.create is false matches expected access control.

src/modules/dns/nameservers/NameserverTemplateModal.tsx (1)

12-12: Import path update looks good.
Aligns with the refactor without affecting behavior.

src/components/ui/NoResults.tsx (1)

9-29: Nice, flexible styling hook for content.
This keeps defaults while enabling targeted overrides.

Also applies to: 71-73

tailwind.config.ts (1)

32-34: Palette update is straightforward.
Token update/addition is clear and low risk.

src/contexts/DialogProvider.tsx (1)

66-71: Confirm intended dismissal behavior.
Blocking outside interactions changes how users can exit the dialog; please confirm this is desired for all confirmation dialogs (including whether ESC should still close).

src/modules/routes/RouteTable.tsx (1)

146-151: Styling update is clean and consistent.
Switching to the new nb-gray-960 token looks good.

src/contexts/AnnouncementProvider.tsx (1)

10-11: Release note copy/link update looks good.

Content-only change with no functional impact.

src/layouts/Navigation.tsx (1)

146-151: DNS “Zones” nav entry is wired correctly.

Permission-gated child link integrates cleanly with the DNS group.

src/modules/dns/nameservers/table/NameserverGroupTable.tsx (1)

22-29: Import path flattening is consistent.

Looks clean and aligns with the new module structure.

src/app/(dashboard)/dns/nameservers/page.tsx (1)

10-19: Icon swap and lazy import path update look good.

No behavioral change; consistent with the new DNS module paths.

Also applies to: 43-43

src/assets/icons/DNSZoneIcon.tsx (1)

1-19: New DNSZoneIcon implementation looks solid.

Follows existing icon conventions and integrates with iconProperties.

src/modules/groups/details/GroupNameserversSection.tsx (1)

6-8: Lazy import path update looks good.

No issues spotted with the new module path.

src/assets/icons/SlackIcon.tsx (1)

1-29: Slack icon component looks consistent with existing icon pattern.

No concerns with the SVG composition or prop usage.

src/app/(dashboard)/dns/zones/layout.tsx (1)

1-8: Layout + metadata wiring is clean.

No issues found.

src/modules/dns/zones/table/DNSZonesSearchDomainCell.tsx (1)

11-28: Toggle cell behavior looks solid.

Permissions and update logic are applied consistently.

src/components/ui/UserDropdown.tsx (1)

44-44: LGTM!

Minor spacing adjustment for tighter vertical rhythm in the user label area.

src/components/ui/PeerCountBadge.tsx (1)

24-27: LGTM - fallback lookup improves robustness.

The fallback from dropdownOptions to groups ensures the component can resolve group data even when the dropdown options are filtered differently. The dependency array is correctly updated.

Minor note: matching by name assumes group names are unique. If that invariant could break, consider matching by id instead.

src/interfaces/DNS.ts (1)

22-23: Both documentation links point to the same URL.

DNS_ZONE_DOCS_LINK and DNS_RECORDS_DOCS_LINK are identical. If there's a separate documentation page for DNS records, update DNS_RECORDS_DOCS_LINK accordingly. Otherwise, consider consolidating to a single constant.

src/modules/dns/zones/records/DNSRecordTypeCell.tsx (1)

9-19: LGTM!

Clean, focused component for rendering DNS record types. The uppercase styling with tracking provides good visual distinction for the record type labels.

src/components/Button.tsx (1)

76-80: Open-state styling for default-outline looks good.

src/app/(dashboard)/group/page.tsx (1)

134-331: Zones tab integration matches existing tab patterns.

src/layouts/Header.tsx (1)

14-69: Header updates integrate Help & Support cleanly.

src/modules/dns/zones/table/DNSZonesActiveCell.tsx (1)

11-29: LGTM: permission-aware toggle wiring.

Clean and readable toggle behavior with permission gating and safe click handling.

src/modules/groups/details/useGroupDetails.ts (1)

17-21: Good addition to GroupDetails.

Exposing zones on the group details object keeps the group view cohesive.

src/modules/dns/zones/table/DNSZonesNameCell.tsx (1)

11-35: LGTM: clear accordion/active-state presentation.

The conditional chevrons and ActiveInactiveRow integration look solid.

src/modules/dns/zones/DNSZoneModal.tsx (2)

37-63: Clean modal wiring.

The open/close and success callbacks are composed cleanly.


86-88: No changes needed — the hook correctly handles string IDs.

The useGroupHelper hook explicitly accepts both Group[] and string[] for its initial prop (see lines 20–28 of src/modules/groups/useGroupHelper.tsx). When given string IDs, it automatically resolves them to Group objects by looking them up in the groups context. The current code at lines 86–88 is correct: passing zone?.distribution_groups (which is string[]) is the intended use case and requires no changes.

src/modules/dns/zones/records/DNSRecordsTable.tsx (1)

16-50: Column definitions look consistent and well-factored.

src/app/(dashboard)/dns/zones/page.tsx (1)

23-67: Page composition is clean and well-scoped.

src/modules/groups/details/GroupDNSZonesSection.tsx (1)

8-27: Nice, compact section wiring for group-level zones.

src/modules/dns/zones/table/DNSZonesActionCell.tsx (1)

22-55: DNS zone action wiring looks solid.
Edit/delete hooks and permission gating are clear and consistent.

src/components/ui/HelpAndSupportButton.tsx (1)

1-35: Controlled dropdown state is wired correctly.
The open state and onOpenChange handling are straightforward.

src/modules/groups/table/GroupsTable.tsx (3)

1-23: Import updates align with the new Zones column.
Looks consistent with the new icon usage.


182-203: Zones column integration looks good.
The header, icon, and link target are consistent with other count columns.


234-244: In-use calculation update is correct.
Including zones in the “in use” signal is consistent with the new column.

src/modules/groups/useGroupsUsage.tsx (3)

1-20: Type updates for zones_count look good.
Imports and GroupUsage shape align with the new zones data.


29-70: Zones fetch and loading integration are consistent.
The new zones request and loading flag are wired into the memo flow as expected.

Also applies to: 88-106


121-170: zones_count aggregation is correctly added to GroupUsage.
The count computation and dependency list look solid.

src/modules/dns/zones/DNSZonesProvider.tsx (4)

1-31: Context surface and setup look clean.
The provider API is clearly defined.


45-105: Zone CRUD flow with notifications looks solid.
Mutations and success toasts are consistently wired.


107-190: Record CRUD flow is well-structured.
Confirmations and SWR revalidation are handled consistently.


192-259: Modal orchestration and cleanup look good.
State resets on close are handled cleanly.

src/modules/dns/zones/DNSRecordModal.tsx (3)

43-73: Nice modal orchestration and post-add UX.
Clean open/close handling and the auto-expand + scroll behavior is a good touch.


83-345: Solid validation + type-specific inputs.
The state/validation wiring and conditional inputs look robust and easy to follow.


347-359: Clear TTL labeling helper.
Readable and well-scoped utility for display labels.

src/modules/dns/zones/table/DNSZonesTable.tsx (1)

288-301: Permission-gated Add Zone button looks good.
Simple and consistent with the rest of the permission model.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment thread config.json
Comment thread src/components/ui/HelpAndSupportButton.tsx
Comment thread src/modules/dns/zones/records/DNSRecordActionCell.tsx
Comment thread src/modules/dns/zones/records/DNSRecordContentCell.tsx
Comment thread src/modules/dns/zones/records/DNSRecordNameCell.tsx
Comment thread src/modules/dns/zones/table/DNSZonesGroupCell.tsx
Comment thread src/modules/dns/zones/table/DNSZonesRecordsCell.tsx
Comment thread src/modules/dns/zones/table/DNSZonesTable.tsx
Comment thread src/modules/dns/zones/table/DNSZonesTable.tsx
Comment thread src/modules/peer/PeerSSHToggle.tsx
@mlsmaycon mlsmaycon merged commit 92676b6 into main Jan 16, 2026
4 checks passed
@mlsmaycon mlsmaycon deleted the feature/dns-zones branch January 16, 2026 16:33
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.

3 participants