feat: customisable workspace role permissions (#345)

* feat: setup schema for workspace roles

* chore: regen migration

* feat: add publicId to workspace roles

* feat: setup default permissions

* feat: add repo funcs

* feat: setup basic router interactions

* feat: add card permissions

* feat: assert permissions for lists

* feat: assert board permissions

* feat: assert permission for remaining routes

* feat: add permissions page to settings

* feat: enable updating member roles

* feat: order members by role and createdAt

* feat: allow editing individual permissions

* feat: reset role defaults

* feat: clear all permission overrides

* feat: allow users to delete entities they have created

* feat: set roleId when inviting new members

* feat: disable UI elements if user does not have permissions

* feat: allow admins to assign the admin role to other users

* feat: allow delete:list as default

* refactor: centre permissions modal

* chore: translations
This commit is contained in:
Henry
2026-02-01 21:17:18 +00:00
committed by GitHub
parent 5f2d409773
commit 7f5a1ab513
83 changed files with 10568 additions and 1442 deletions

View File

@@ -12,6 +12,7 @@ import FeedbackModal from "~/components/FeedbackModal";
import Modal from "~/components/modal";
import { NewWorkspaceForm } from "~/components/NewWorkspaceForm";
import { PageHead } from "~/components/PageHead";
import { usePermissions } from "~/hooks/usePermissions";
import { useModal } from "~/providers/modal";
import { useWorkspace } from "~/providers/workspace";
import { api } from "~/utils/api";
@@ -25,6 +26,7 @@ import { UpgradeToProConfirmation } from "./components/UpgradeToProConfirmation"
export default function WorkspaceSettings() {
const { modalContentType, openModal, isOpen } = useModal();
const { workspace } = useWorkspace();
const { canEditWorkspace } = usePermissions();
const router = useRouter();
const { data } = api.user.getUser.useQuery();
const [hasOpenedUpgradeModal, setHasOpenedUpgradeModal] = useState(false);
@@ -61,6 +63,7 @@ export default function WorkspaceSettings() {
<UpdateWorkspaceNameForm
workspacePublicId={workspace.publicId}
workspaceName={workspace.name}
disabled={!canEditWorkspace}
/>
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
@@ -70,6 +73,7 @@ export default function WorkspaceSettings() {
workspacePublicId={workspace.publicId}
workspaceUrl={workspace.slug ?? ""}
workspacePlan={workspace.plan ?? "free"}
disabled={!canEditWorkspace}
/>
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
@@ -78,6 +82,7 @@ export default function WorkspaceSettings() {
<UpdateWorkspaceDescriptionForm
workspacePublicId={workspace.publicId}
workspaceDescription={workspace.description ?? ""}
disabled={!canEditWorkspace}
/>
<h2 className="mb-4 mt-8 text-[14px] font-bold text-neutral-900 dark:text-dark-1000">
@@ -85,7 +90,10 @@ export default function WorkspaceSettings() {
</h2>
<UpdateWorkspaceEmailVisibilityForm
workspacePublicId={workspace.publicId}
showEmailsToMembers={workspaceData?.showEmailsToMembers ?? false}
showEmailsToMembers={Boolean(
workspaceData?.showEmailsToMembers ?? false,
)}
disabled={!canEditWorkspace}
/>
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&