enable teams for self-hosted (#137)
* enable teams for self-hosted * remove console
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
import { useTeam } from "~/providers/team-context";
|
||||
import { SettingsNavButton } from "../dev-settings/settings-nav-button";
|
||||
import { isCloud } from "~/utils/common";
|
||||
|
||||
export const dynamic = "force-static";
|
||||
|
||||
@@ -16,8 +17,10 @@ export default function ApiKeysPage({
|
||||
<div>
|
||||
<h1 className="font-bold text-lg">Settings</h1>
|
||||
<div className="flex gap-4 mt-4">
|
||||
<SettingsNavButton href="/settings">Usage</SettingsNavButton>
|
||||
{currentIsAdmin ? (
|
||||
{isCloud() ? (
|
||||
<SettingsNavButton href="/settings">Usage</SettingsNavButton>
|
||||
) : null}
|
||||
{currentIsAdmin && isCloud() ? (
|
||||
<SettingsNavButton href="/settings/billing">
|
||||
Billing
|
||||
</SettingsNavButton>
|
||||
|
@@ -1,10 +1,24 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@unsend/ui/src/button";
|
||||
import { api } from "~/trpc/react";
|
||||
import { isCloud } from "~/utils/common";
|
||||
import UsagePage from "./usage/usage";
|
||||
import InviteTeamMember from "./team/invite-team-member";
|
||||
import TeamMembersList from "./team/team-members-list";
|
||||
|
||||
export default function SettingsPage() {
|
||||
if (!isCloud()) {
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
<div className="flex justify-end ">
|
||||
<InviteTeamMember />
|
||||
</div>
|
||||
<TeamMembersList />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<UsagePage />
|
||||
|
@@ -33,6 +33,7 @@ import {
|
||||
FormMessage,
|
||||
} from "@unsend/ui/src/form";
|
||||
import { useTeam } from "~/providers/team-context";
|
||||
import { isCloud, isSelfHosted } from "~/utils/common";
|
||||
|
||||
const inviteTeamMemberSchema = z.object({
|
||||
email: z
|
||||
@@ -47,6 +48,7 @@ type FormData = z.infer<typeof inviteTeamMemberSchema>;
|
||||
|
||||
export default function InviteTeamMember() {
|
||||
const { currentIsAdmin } = useTeam();
|
||||
const { data: domains } = api.domain.domains.useQuery();
|
||||
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
@@ -60,23 +62,53 @@ export default function InviteTeamMember() {
|
||||
|
||||
const utils = api.useUtils();
|
||||
|
||||
const createInvite = api.team.createTeamInvite.useMutation({
|
||||
onSuccess: () => {
|
||||
form.reset();
|
||||
setOpen(false);
|
||||
void utils.team.getTeamInvites.invalidate();
|
||||
toast.success("Invitation sent successfully");
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message || "Failed to send invitation");
|
||||
},
|
||||
});
|
||||
const createInvite = api.team.createTeamInvite.useMutation();
|
||||
|
||||
function onSubmit(values: FormData) {
|
||||
createInvite.mutate({
|
||||
email: values.email,
|
||||
role: values.role,
|
||||
});
|
||||
createInvite.mutate(
|
||||
{
|
||||
email: values.email,
|
||||
role: values.role,
|
||||
sendEmail: true,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
form.reset();
|
||||
setOpen(false);
|
||||
void utils.team.getTeamInvites.invalidate();
|
||||
toast.success("Invitation sent successfully");
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error);
|
||||
toast.error(error.message || "Failed to send invitation");
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function onCopyLink() {
|
||||
createInvite.mutate(
|
||||
{
|
||||
email: form.getValues("email"),
|
||||
role: form.getValues("role"),
|
||||
sendEmail: false,
|
||||
},
|
||||
{
|
||||
onSuccess: (invite) => {
|
||||
void utils.team.getTeamInvites.invalidate();
|
||||
navigator.clipboard.writeText(
|
||||
`${location.origin}/join-team?inviteId=${invite.id}`
|
||||
);
|
||||
form.reset();
|
||||
setOpen(false);
|
||||
toast.success("Invitation link copied to clipboard");
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error(error);
|
||||
toast.error(error.message || "Failed to copy invitation link");
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (!currentIsAdmin) {
|
||||
@@ -91,7 +123,7 @@ export default function InviteTeamMember() {
|
||||
Invite Member
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent className="sm:max-w-[425px]">
|
||||
<DialogContent className=" max-w-lg">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Invite Team Member</DialogTitle>
|
||||
</DialogHeader>
|
||||
@@ -152,6 +184,13 @@ export default function InviteTeamMember() {
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{isSelfHosted() && domains?.length ? (
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Will use{" "}
|
||||
<span className="font-bold">hello@{domains[0]?.name}</span> to
|
||||
send invitation
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex justify-end gap-2 pt-4">
|
||||
<Button
|
||||
type="button"
|
||||
@@ -161,14 +200,23 @@ export default function InviteTeamMember() {
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={createInvite.isPending}
|
||||
showSpinner
|
||||
isLoading={createInvite.isPending}
|
||||
className="w-[150px]"
|
||||
onClick={form.handleSubmit(onCopyLink)}
|
||||
>
|
||||
{createInvite.isPending ? "Sending..." : "Send Invitation"}
|
||||
Copy Invitation
|
||||
</Button>
|
||||
{isCloud() || domains?.length ? (
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={createInvite.isPending}
|
||||
isLoading={createInvite.isPending}
|
||||
className="w-[150px]"
|
||||
>
|
||||
Send Invitation
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
|
@@ -3,13 +3,14 @@
|
||||
import { Button } from "@unsend/ui/src/button";
|
||||
import { api } from "~/trpc/react";
|
||||
import { toast } from "@unsend/ui/src/toaster";
|
||||
import { RotateCw } from "lucide-react";
|
||||
import { Copy, RotateCw } from "lucide-react";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@unsend/ui/src/tooltip";
|
||||
import { isSelfHosted } from "~/utils/common";
|
||||
|
||||
export const ResendTeamInvite: React.FC<{
|
||||
invite: { id: string; email: string };
|
||||
@@ -44,6 +45,28 @@ export const ResendTeamInvite: React.FC<{
|
||||
<p>Resend invite</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
{isSelfHosted() ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
`${location.origin}/join-team?inviteId=${invite.id}`
|
||||
);
|
||||
toast.success(`Invite link copied to clipboard`);
|
||||
}}
|
||||
>
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>Copy invite link</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
|
Reference in New Issue
Block a user