Skip to content

Commit 026da38

Browse files
authored
feat: relax admin role (#1019)
1 parent ced468d commit 026da38

File tree

3 files changed

+44
-31
lines changed

3 files changed

+44
-31
lines changed

frontend/components/workspace/purchase-seats-dialog.tsx

Lines changed: 40 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import { Loader2 } from "lucide-react";
2-
import { useState } from "react";
2+
import { useCallback, useState } from "react";
33

44
import { Button } from "@/components/ui/button";
5-
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog";
5+
import {
6+
Dialog,
7+
DialogContent,
8+
DialogDescription,
9+
DialogFooter,
10+
DialogHeader,
11+
DialogTitle,
12+
DialogTrigger,
13+
} from "@/components/ui/dialog";
614

715
import { Input } from "../ui/input";
816
import { Label } from "../ui/label";
@@ -23,6 +31,20 @@ export default function PurchaseSeatsDialog({
2331
const [quantity, setQuantity] = useState<number | null>(0);
2432
const [isLoading, setIsLoading] = useState(false);
2533
const [isOpen, setIsOpen] = useState(false);
34+
35+
const handleUpdateSeats = useCallback(async () => {
36+
setIsLoading(true);
37+
await fetch(`/api/workspaces/${workspaceId}/update-seats`, {
38+
method: "POST",
39+
body: JSON.stringify({
40+
quantity: quantity,
41+
}),
42+
});
43+
setIsLoading(false);
44+
setIsOpen(false);
45+
onUpdate?.();
46+
}, [onUpdate, quantity, workspaceId]);
47+
2648
return (
2749
<Dialog
2850
onOpenChange={(open) => {
@@ -38,38 +60,29 @@ export default function PurchaseSeatsDialog({
3860
Add seats
3961
</Button>
4062
</DialogTrigger>
41-
<DialogContent>
63+
<DialogContent className="sm:max-w-md">
4264
<DialogHeader>
4365
<DialogTitle>Add seats</DialogTitle>
66+
<DialogDescription>Purchase additional seats for your workspace.</DialogDescription>
4467
</DialogHeader>
45-
<p>Purchase additional seats for your workspace.</p>
46-
<Label className="text-sm text-secondary-foreground">
47-
Your current tier includes {seatsIncludedInTier} seats. You have {currentQuantity - seatsIncludedInTier}{" "}
48-
additional seats. Purchasing seats will charge your payment method on file.
49-
</Label>
50-
<Input
51-
type="number"
52-
onChange={(e) =>
53-
e.target.value === "" ? setQuantity(null) : setQuantity(Math.max(0, parseInt(e.target.value)))
54-
}
55-
value={quantity?.toString() ?? ""}
56-
/>
68+
<div className="grid gap-2">
69+
<Label className="text-secondary-foreground text-xs">
70+
Your current tier includes {seatsIncludedInTier} seats. You have {currentQuantity - seatsIncludedInTier}{" "}
71+
additional seats. Purchasing seats will charge your payment method on file.
72+
</Label>
73+
<Input
74+
type="number"
75+
onChange={(e) =>
76+
e.target.value === "" ? setQuantity(null) : setQuantity(Math.max(0, parseInt(e.target.value)))
77+
}
78+
value={quantity?.toString() ?? ""}
79+
/>
80+
</div>
5781
<DialogFooter>
5882
<Button
5983
className="flex flex-row items-center"
6084
disabled={isLoading || quantity === null || quantity <= 0}
61-
onClick={async () => {
62-
setIsLoading(true);
63-
await fetch(`/api/workspaces/${workspaceId}/update-seats`, {
64-
method: "POST",
65-
body: JSON.stringify({
66-
quantity: quantity,
67-
}),
68-
});
69-
setIsLoading(false);
70-
setIsOpen(false);
71-
onUpdate?.();
72-
}}
85+
onClick={handleUpdateSeats}
7386
>
7487
{isLoading && <Loader2 className="w-4 h-4 mr-2 animate-spin" />}
7588
Purchase

frontend/components/workspace/workspace-users.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export default function WorkspaceUsers({
108108

109109
const renderRoleCell = useCallback(
110110
(user: WorkspaceUser) => {
111-
if (isOwner && user.role !== "owner" && !isCurrentUser(user)) {
111+
if (canManageUsers && user.role !== "owner" && !isCurrentUser(user)) {
112112
return (
113113
<Select
114114
value={user.role}
@@ -128,7 +128,7 @@ export default function WorkspaceUsers({
128128

129129
return <span className="text-sm">{user.role}</span>;
130130
},
131-
[handleRoleChange, isCurrentUser, isOwner, updatingRoleUserId]
131+
[canManageUsers, handleRoleChange, isCurrentUser, updatingRoleUserId]
132132
);
133133

134134
const renderActionCell = useCallback(
@@ -193,7 +193,7 @@ export default function WorkspaceUsers({
193193
<>
194194
<SettingsSectionHeader title="Members" description="Manage workspace members and their roles" />
195195

196-
{isOwner && (
196+
{canManageUsers && (
197197
<SettingsSection>
198198
<SettingsSectionHeader
199199
size="sm"

frontend/lib/actions/workspace/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ export const getWorkspaceUsage = async (workspaceId: string): Promise<WorkspaceU
249249
export const updateRole = async (input: z.infer<typeof UpdateRoleSchema>) => {
250250
const { workspaceId, userId, role } = UpdateRoleSchema.parse(input);
251251

252-
const currentRole = await checkUserWorkspaceRole({ workspaceId, roles: ["owner"] });
252+
await checkUserWorkspaceRole({ workspaceId, roles: ["owner", "admin"] });
253253

254254
const [targetUser] = await db
255255
.select({ memberRole: membersOfWorkspaces.memberRole })

0 commit comments

Comments
 (0)