Skip to content

Commit 6059fa3

Browse files
authored
fix: show invite link toggle on self hosted environments (#198)
1 parent 60355c7 commit 6059fa3

File tree

3 files changed

+62
-23
lines changed

3 files changed

+62
-23
lines changed

apps/web/src/components/Toggle.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ const Toggle = ({
55
isChecked,
66
onChange,
77
label,
8+
disabled,
89
}: {
910
isChecked: boolean;
1011
onChange: () => void;
1112
label: string;
13+
disabled?: boolean;
1214
}) => (
1315
<div className="mr-4 flex items-center justify-end">
1416
<span className="mr-2 text-xs text-light-900 dark:text-dark-900">
@@ -17,6 +19,7 @@ const Toggle = ({
1719
<Switch
1820
checked={isChecked}
1921
onChange={onChange}
22+
disabled={disabled}
2023
className={twMerge(
2124
"relative inline-flex h-4 w-6 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent bg-light-800 transition-colors duration-200 ease-in-out focus:outline-none dark:bg-dark-800",
2225
isChecked && "bg-indigo-600 dark:bg-indigo-600",

apps/web/src/views/members/components/InviteMemberForm.tsx

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export function InviteMemberForm({
3939
const [isShareInviteLinkEnabled, setIsShareInviteLinkEnabled] =
4040
useState(false);
4141
const [inviteLink, setInviteLink] = useState<string>("");
42-
const [isLoadingInviteLink, setIsLoadingInviteLink] = useState(false);
42+
const [_isLoadingInviteLink, setIsLoadingInviteLink] = useState(false);
4343
const [copied, setCopied] = useState(false);
4444
const { closeModal } = useModal();
4545
const { workspace } = useWorkspace();
@@ -68,7 +68,7 @@ export function InviteMemberForm({
6868
const refetchBoards = () => utils.board.all.refetch();
6969

7070
// Fetch active invite link on component mount
71-
const { data: activeInviteLink, refetch: refetchInviteLink } =
71+
const { data: activeInviteLink, refetch: _refetchInviteLink } =
7272
api.member.getActiveInviteLink.useQuery(
7373
{ workspacePublicId: workspace.publicId || "" },
7474
{ enabled: !!workspace.publicId },
@@ -78,7 +78,7 @@ export function InviteMemberForm({
7878
useEffect(() => {
7979
if (activeInviteLink) {
8080
setIsShareInviteLinkEnabled(activeInviteLink.isActive);
81-
setInviteLink(activeInviteLink.inviteLink || "");
81+
setInviteLink(activeInviteLink.inviteLink ?? "");
8282
}
8383
}, [activeInviteLink]);
8484

@@ -113,13 +113,23 @@ export function InviteMemberForm({
113113
setInviteLink(data.inviteLink);
114114
setIsLoadingInviteLink(false);
115115
},
116-
onError: () => {
116+
onError: (error) => {
117117
setIsLoadingInviteLink(false);
118-
showPopup({
119-
header: t`Error creating invite link`,
120-
message: t`Please try again later.`,
121-
icon: "error",
122-
});
118+
setIsShareInviteLinkEnabled(false);
119+
120+
if (error.data?.code === "FORBIDDEN") {
121+
showPopup({
122+
header: t`Subscription Required`,
123+
message: t`Invite links require a Team or Pro subscription. Please upgrade your workspace.`,
124+
icon: "error",
125+
});
126+
} else {
127+
showPopup({
128+
header: t`Error creating invite link`,
129+
message: t`Please try again later.`,
130+
icon: "error",
131+
});
132+
}
123133
},
124134
});
125135

@@ -148,7 +158,7 @@ export function InviteMemberForm({
148158
let price = t`$10/month`;
149159
let billingType = t`monthly billing`;
150160

151-
if (teamSubscription?.periodStart && teamSubscription?.periodEnd) {
161+
if (teamSubscription?.periodStart && teamSubscription.periodEnd) {
152162
const periodStartDate = new Date(teamSubscription.periodStart);
153163
const periodEndDate = new Date(teamSubscription.periodEnd);
154164
const diffInDays = Math.round(
@@ -193,7 +203,7 @@ export function InviteMemberForm({
193203
message: t`Invite link copied to clipboard`,
194204
icon: "success",
195205
});
196-
} catch (error) {
206+
} catch {
197207
showPopup({
198208
header: t`Error`,
199209
message: t`Failed to copy invite link`,
@@ -325,18 +335,20 @@ export function InviteMemberForm({
325335
</div>
326336

327337
<div className="mt-12 flex items-center justify-end border-t border-light-600 px-5 pb-5 pt-5 dark:border-dark-600">
328-
{(hasTeamSubscription || hasProSubscription) &&
329-
env("NEXT_PUBLIC_KAN_ENV") === "cloud" && (
330-
<Toggle
331-
label={
332-
isShareInviteLinkEnabled
333-
? t`Deactivate invite link`
334-
: t`Create invite link`
335-
}
336-
isChecked={isShareInviteLinkEnabled}
337-
onChange={handleInviteLinkToggle}
338-
/>
339-
)}
338+
<Toggle
339+
label={
340+
isShareInviteLinkEnabled
341+
? t`Deactivate invite link`
342+
: t`Create invite link`
343+
}
344+
isChecked={isShareInviteLinkEnabled}
345+
disabled={
346+
env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
347+
!hasTeamSubscription &&
348+
!hasProSubscription
349+
}
350+
onChange={handleInviteLinkToggle}
351+
/>
340352
<div>
341353
{env("NEXT_PUBLIC_KAN_ENV") === "cloud" &&
342354
!hasTeamSubscription &&

packages/api/src/routers/member.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,30 @@ export const memberRouter = createTRPCRouter({
362362
// Check if user is in workspace
363363
await assertUserInWorkspace(ctx.db, userId, workspace.id, "admin");
364364

365+
// Check subscription for cloud environment
366+
if (process.env.NEXT_PUBLIC_KAN_ENV === "cloud") {
367+
const subscriptions = await subscriptionRepo.getByReferenceId(
368+
ctx.db,
369+
workspace.publicId,
370+
);
371+
372+
const activeTeamSubscription = getSubscriptionByPlan(
373+
subscriptions,
374+
"team",
375+
);
376+
const activeProSubscription = getSubscriptionByPlan(
377+
subscriptions,
378+
"pro",
379+
);
380+
381+
if (!activeTeamSubscription && !activeProSubscription) {
382+
throw new TRPCError({
383+
message: `Invite links require a Team or Pro subscription`,
384+
code: "FORBIDDEN",
385+
});
386+
}
387+
}
388+
365389
// Deactivate any existing active invite links
366390
await inviteLinkRepo.deactivateAllActiveForWorkspace(ctx.db, {
367391
workspaceId: workspace.id,

0 commit comments

Comments
 (0)