160 lines
4.7 KiB
TypeScript
160 lines
4.7 KiB
TypeScript
"use client";
|
|
|
|
import { Button } from "@unsend/ui/src/button";
|
|
import { Spinner } from "@unsend/ui/src/spinner";
|
|
import { api } from "~/trpc/react";
|
|
import { useRouter, useSearchParams } from "next/navigation";
|
|
import { toast } from "@unsend/ui/src/toaster";
|
|
import {
|
|
Dialog,
|
|
DialogContent,
|
|
DialogDescription,
|
|
DialogHeader,
|
|
DialogTitle,
|
|
} from "@unsend/ui/src/dialog";
|
|
import { useState } from "react";
|
|
import type { AppRouter } from "~/server/api/root";
|
|
import type { inferRouterOutputs } from "@trpc/server";
|
|
|
|
type RouterOutputs = inferRouterOutputs<AppRouter>;
|
|
type Invite = NonNullable<
|
|
RouterOutputs["invitation"]["getUserInvites"]
|
|
>[number];
|
|
|
|
export default function JoinTeam({
|
|
showCreateTeam = false,
|
|
}: {
|
|
showCreateTeam?: boolean;
|
|
}) {
|
|
const searchParams = useSearchParams();
|
|
const inviteId = searchParams.get("inviteId");
|
|
|
|
const { data: invites, status: invitesStatus } =
|
|
api.invitation.getUserInvites.useQuery({
|
|
inviteId,
|
|
});
|
|
const joinTeamMutation = api.invitation.acceptTeamInvite.useMutation();
|
|
const [selectedInvite, setSelectedInvite] = useState<Invite | null>(null);
|
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
|
|
const utils = api.useUtils();
|
|
const router = useRouter();
|
|
|
|
const handleAcceptInvite = (invite: Invite) => {
|
|
setSelectedInvite(invite);
|
|
setDialogOpen(true);
|
|
};
|
|
|
|
const confirmAcceptInvite = () => {
|
|
if (!selectedInvite) return;
|
|
|
|
joinTeamMutation.mutate(
|
|
{
|
|
inviteId: selectedInvite.id,
|
|
},
|
|
{
|
|
onSuccess: async () => {
|
|
toast.success(`Successfully joined ${selectedInvite.team.name}`);
|
|
await Promise.all([
|
|
utils.invitation.getUserInvites.invalidate(),
|
|
utils.team.getTeams.invalidate(),
|
|
]);
|
|
setDialogOpen(false);
|
|
router.replace("/dashboard");
|
|
},
|
|
onError: (error) => {
|
|
toast.error(`Failed to join team: ${error.message}`);
|
|
setDialogOpen(false);
|
|
},
|
|
}
|
|
);
|
|
};
|
|
|
|
if (!invites?.length) {
|
|
return !showCreateTeam ? (
|
|
<div className="text-center text-xl">No invites found</div>
|
|
) : null;
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<div>You have been invited to join team</div>
|
|
<div className="space-y-2 mt-4">
|
|
{invites.map((invite) => (
|
|
<div
|
|
key={invite.id}
|
|
className="flex items-center gap-2 border rounded-lg p-2 px-4 shadow justify-between"
|
|
>
|
|
<div>
|
|
<div className="text-sm">{invite.team.name}</div>
|
|
<div className="flex items-center gap-2">
|
|
<div className="text-muted-foreground text-xs capitalize">
|
|
{invite.role.toLowerCase()}
|
|
</div>
|
|
<div className="text-muted-foreground text-xs">
|
|
{invite.createdAt.toLocaleDateString()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Button
|
|
onClick={() => handleAcceptInvite(invite)}
|
|
disabled={joinTeamMutation.isPending}
|
|
size="sm"
|
|
variant="ghost"
|
|
>
|
|
{joinTeamMutation.isPending ? (
|
|
<Spinner className="w-5 h-5" />
|
|
) : (
|
|
"Accept"
|
|
)}
|
|
</Button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
{showCreateTeam ? (
|
|
<div className="mt-8 text-muted-foreground text-sm font-mono text-center">
|
|
OR
|
|
</div>
|
|
) : null}
|
|
|
|
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>Accept Team Invitation</DialogTitle>
|
|
<DialogDescription>
|
|
Are you sure you want to join{" "}
|
|
<span className="font-semibold text-primary">
|
|
{selectedInvite?.team.name}
|
|
</span>
|
|
? You will be added as a{" "}
|
|
<span className="font-semibold text-primary lowercase">
|
|
{selectedInvite?.role.toLowerCase()}
|
|
</span>
|
|
.
|
|
</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="flex justify-end gap-3 mt-4">
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => setDialogOpen(false)}
|
|
disabled={joinTeamMutation.isPending}
|
|
>
|
|
Cancel
|
|
</Button>
|
|
<Button
|
|
onClick={confirmAcceptInvite}
|
|
disabled={joinTeamMutation.isPending}
|
|
>
|
|
{joinTeamMutation.isPending ? (
|
|
<Spinner className="w-5 h-5" />
|
|
) : (
|
|
"Accept Invitation"
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
</div>
|
|
);
|
|
}
|