Skip to content

Board level sharing#830

Closed
tsahil01 wants to merge 10 commits intoantiwork:mainfrom
tsahil01:board-level-sharing
Closed

Board level sharing#830
tsahil01 wants to merge 10 commits intoantiwork:mainfrom
tsahil01:board-level-sharing

Conversation

@tsahil01
Copy link

@tsahil01 tsahil01 commented Oct 6, 2025

fixes #822

Board-Level Sharing Permissions

Implemented granular board sharing controls that allow organization admins to share specific boards with team members, rather than giving access to all boards.

Approach

  • Added board-level sharing toggles in organization settings with Share all boards option
  • Included granular board selection via View (Eye) button for precise control
  • Implemented board settings with member search and individual sharing toggles
  • Ensured synchronization between organization and board-level settings
gumboard.board.sharing.mp4

Changes

  • Database: Added BoardShare model for granular permissions
  • API: Created board sharing management endpoints with authorization logic
  • UI: Built sharing dialogs in organization and board settings
  • Authorization: Updated access control to check board-level permissions
  • Tests: Added comprehensive e2e tests covering all sharing scenarios

AI Disclosure

  • used claude sonnet 4.5

Self Review

  • added self review comments

Screenshots

Org Settings

Board Settings

@tsahil01
Copy link
Author

tsahil01 commented Oct 6, 2025

My preferred (implemented) solution to the issue:

  • In the organization settings (where all members are listed), we can have the same toggle for each member to share all boards or not.
    • Next to this toggle, we can add a “View/Eye” button that allows selecting specific boards to share or not share using checkboxes.
    • If all checkboxes are selected, it means all boards are shared.
  • In the board settings, we can include a search box to find organization members, along with a toggle for each member to specify whether that particular board is shared with them or not.
  • These toggles should stay synchronized with the toggles in the organization settings.
  • If all boards are shared (as per the toggle in settings), then in the specific board settings, the toggle for that user should appear ON for that board.
  • If all boards are shared, the toggle in settings should remain ON. However, if I change the toggle to OFF for a specific board, it should automatically update in the organization settings to reflect that this board is no longer shared.

@tsahil01
Copy link
Author

tsahil01 commented Oct 6, 2025

adding self review comments next.

Copy link
Author

@tsahil01 tsahil01 left a comment

Choose a reason for hiding this comment

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

added self review comments

invitedOrganizations OrganizationInvite[]
createdSelfServeInvites OrganizationSelfServeInvite[]
notes Note[]
boardShares BoardShare[]
Copy link
Author

Choose a reason for hiding this comment

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

links users to boards they can access

createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
notes Note[]
shares BoardShare[]
Copy link
Author

Choose a reason for hiding this comment

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

links boards to users who have access

Comment on lines +142 to +155
model BoardShare {
id String @id @default(cuid())
boardId String
userId String
board Board @relation(fields: [boardId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
createdAt DateTime @default(now())

@@unique([boardId, userId])
@@map("board_shares")
@@index([boardId])
@@index([userId])
}

Copy link
Author

Choose a reason for hiding this comment

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

Added BoardShare table to track which users can access which boards like a permission list between users and boards

Comment on lines 27 to +40
const boards = await db.board.findMany({
where: { organizationId: user.organizationId },
where: {
OR: [
{ isPublic: true },
{
shares: {
some: {
userId: session.user.id,
},
},
},
{ createdBy: session.user.id }, // Boards created by the user
],
},
Copy link
Author

Choose a reason for hiding this comment

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

update board listing. now include three access patterns - public boards (anyone), explicitly shared boards (has BoardShare record), and user created boards (createdBy matches user)

ensures creators never lose access to their own content while maintaining granular control

Comment on lines +49 to +54
organization: {
select: {
id: true,
name: true,
},
},
Copy link
Author

Choose a reason for hiding this comment

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

add organization info to response. needed for UI to show org context, was missing before and caused display issues

Comment on lines +773 to +780
<p className="text-sm text-zinc-600 dark:text-zinc-400">{member.email}</p>
{memberSharing && (
<div className="flex items-center space-x-2 mt-1">
<span className="text-xs text-zinc-500 dark:text-zinc-400">
{memberSharing.shareAllBoards
? `All ${memberSharing.totalBoards} boards shared`
: `${memberSharing.sharedBoardIds.length} of ${memberSharing.totalBoards} boards shared`}
</span>
Copy link
Author

Choose a reason for hiding this comment

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

shows either all boards are shared or what number of boards are shared

Comment on lines +786 to +816
{/* Board sharing toggle - only for admins and not for yourself */}
{user?.isAdmin && member.id !== user.id && (
<div className="flex items-center space-x-2">
<Switch
checked={memberSharing?.shareAllBoards || false}
onCheckedChange={(checked) =>
handleToggleShareAllBoards(member.id, checked)
}
disabled={updatingSharing === member.id}
className="data-[state=checked]:bg-green-600"
/>
<span className="text-sm text-zinc-600 dark:text-zinc-400">
Share all boards
</span>
<Button
onClick={() =>
openBoardDialog(
member.id,
member.name || member.email,
memberSharing?.sharedBoardIds || []
)
}
variant="outline"
size="sm"
className="text-zinc-500 dark:text-zinc-400 hover:text-zinc-600 hover:bg-zinc-50 dark:hover:text-zinc-300 dark:hover:bg-zinc-800"
title="View and edit shared boards"
>
<Eye className="w-4 h-4" />
</Button>
</div>
)}
Copy link
Author

Choose a reason for hiding this comment

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

board sharing controls section,
only shows for org admins and not for the current user, provides toggle for "Share all boards" and "View (Eye)" button for granular control

Comment on lines +818 to +851
{/* Admin toggle - only for admins and not for yourself */}
{user?.isAdmin && member.id !== user.id && (
<>
<Button
onClick={() => handleToggleAdmin(member.id, !!member.isAdmin)}
variant="outline"
size="sm"
className={`${
member.isAdmin
? "text-purple-600 hover:text-purple-700 hover:bg-purple-50 dark:text-purple-400 dark:hover:text-purple-300 dark:hover:bg-purple-900"
: "text-zinc-500 dark:text-zinc-400 hover:text-purple-600 hover:bg-purple-50 dark:hover:text-purple-300 dark:hover:bg-purple-900"
}`}
title={member.isAdmin ? "Remove admin role" : "Make admin"}
>
{member.isAdmin ? (
<ShieldCheck className="w-4 h-4" />
) : (
<Shield className="w-4 h-4" />
)}
</Button>
<Button
onClick={() => handleRemoveMember(member.id, member.name || member.email)}
variant="outline"
size="sm"
className="text-red-600 hover:text-red-700 hover:bg-red-50 dark:text-red-400 dark:hover:text-red-300 dark:hover:bg-red-900"
>
<Trash2 className="w-4 h-4" />
</Button>
</>
)}
</div>
</div>
<div className="flex items-center space-x-2">
{/* Only show admin toggle to current admins and not for yourself */}
{user?.isAdmin && member.id !== user.id && (
<Button
onClick={() => handleToggleAdmin(member.id, !!member.isAdmin)}
variant="outline"
size="sm"
className={`${
member.isAdmin
? "text-purple-600 hover:text-purple-700 hover:bg-purple-50 dark:text-purple-400 dark:hover:text-purple-300 dark:hover:bg-purple-900"
: "text-zinc-500 dark:text-zinc-400 hover:text-purple-600 hover:bg-purple-50 dark:hover:text-purple-300 dark:hover:bg-purple-900"
}`}
title={member.isAdmin ? "Remove admin role" : "Make admin"}
>
{member.isAdmin ? (
<ShieldCheck className="w-4 h-4" />
) : (
<Shield className="w-4 h-4" />
)}
</Button>
)}
{user?.isAdmin && member.id !== user.id && (
<Button
onClick={() => handleRemoveMember(member.id, member.name || member.email)}
variant="outline"
size="sm"
className="text-red-600 hover:text-red-700 hover:bg-red-50 dark:text-red-400 dark:hover:text-red-300 dark:hover:bg-red-900"
>
<Trash2 className="w-4 h-4" />
</Button>
)}
</div>
</div>
))}
);
})}
Copy link
Author

Choose a reason for hiding this comment

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

admin controls section.
existing admin toggle and remove member functionality,
moved into conditional rendering block

</AlertDialog>

{/* Board Sharing Dialog */}
<Dialog
Copy link
Author

Choose a reason for hiding this comment

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

board sharing dialog:

  • displays all org boards as checkboxes, allows selecting/deselecting individual boards for sharing
  • update btn saves sharing changes, calls handleUpdateBoardSharing function

Copy link
Author

Choose a reason for hiding this comment

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

unable to select full dialog component, github error

Copy link
Author

Choose a reason for hiding this comment

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

add e2e tests for board sharing:

  1. Board Creation & Creator Access:
  • admin creates board with auto-access
  • creator can access own board
  1. Board Sharing Dialog
  • sharing dialog shows creator identification
  • sharing dialog shows all members
  • dialog persists during member toggles
  • dialog closes on Done button
  1. Access Controls
  • shared user can access board
  • creator has access without explicit sharing
  1. Organization Integration
  • org sharing API works
  1. Edge Cases
  • public boards are accessible
  • board deletion removes sharing

@tsahil01 tsahil01 marked this pull request as ready for review October 7, 2025 05:50
@tsahil01
Copy link
Author

@slavingia can you review this now?
I have self reviewed everything as you asked in #829 (comment)

@slavingia slavingia closed this Jan 10, 2026
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.

Feature Request: Board-level Sharing Permissions

2 participants