Minor UX improvments (#717)

* feat: display user rights on invitation page

* feat: add brain name in invitation email
This commit is contained in:
Mamadou DICKO 2023-07-20 15:15:43 +02:00 committed by GitHub
parent d7ca11f5d1
commit eb779f9e58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 41 additions and 18 deletions

View File

@ -1,11 +1,10 @@
from typing import Optional
from uuid import UUID from uuid import UUID
import resend
from logger import get_logger from logger import get_logger
from models.settings import BrainSettings, CommonsDep, common_dependencies
from pydantic import BaseModel from pydantic import BaseModel
from models.settings import CommonsDep, common_dependencies
logger = get_logger(__name__) logger = get_logger(__name__)
@ -64,4 +63,4 @@ class BrainSubscription(BaseModel):
else: else:
response = self.create_subscription_invitation() response = self.create_subscription_invitation()
return response return response

View File

@ -1,31 +1,43 @@
import resend import resend
from logger import get_logger from logger import get_logger
from models.brains import Brain
from models.brains_subscription_invitations import BrainSubscription from models.brains_subscription_invitations import BrainSubscription
from models.settings import BrainSettings from models.settings import BrainSettings
from repository.brain_subscription.get_brain_url import get_brain_url from repository.brain_subscription.get_brain_url import get_brain_url
logger = get_logger(__name__) logger = get_logger(__name__)
def resend_invitation_email(brain_subscription: BrainSubscription, inviter_email: str, origin: str = "https://www.quivr.app"): def resend_invitation_email(
brain_subscription: BrainSubscription,
inviter_email: str,
origin: str = "https://www.quivr.app",
):
brains_settings = BrainSettings() # pyright: ignore reportPrivateUsage=none brains_settings = BrainSettings() # pyright: ignore reportPrivateUsage=none
resend.api_key = brains_settings.resend_api_key resend.api_key = brains_settings.resend_api_key
brain_url = get_brain_url(origin, brain_subscription.brain_id) brain_url = get_brain_url(origin, brain_subscription.brain_id)
invitation_brain_client = Brain(id=brain_subscription.brain_id)
invitation_brain = invitation_brain_client.get_brain_details()[0]
brain_name = invitation_brain["name"]
html_body = f""" html_body = f"""
<p>This brain has been shared with you by {inviter_email}.</p> <p>Brain {brain_name} has been shared with you by {inviter_email}.</p>
<p><a href='{brain_url}'>Click here</a> to access your brain.</p> <p><a href='{brain_url}'>Click here</a> to access your brain.</p>
""" """
try: try:
r = resend.Emails.send({ r = resend.Emails.send(
"from": brains_settings.resend_email_address, {
"to": brain_subscription.email, "from": brains_settings.resend_email_address,
"subject": "Quivr - Brain Shared With You", "to": brain_subscription.email,
"html": html_body "subject": "Quivr - Brain Shared With You",
}) "html": html_body,
logger.info('Resend response', r) }
)
logger.info("Resend response", r)
except Exception as e: except Exception as e:
logger.error(f"Error sending email: {e}") logger.error(f"Error sending email: {e}")
return return

View File

@ -179,7 +179,7 @@ def get_user_invitation(brain_id: UUID, current_user: User = Depends(get_current
detail="Brain not found while trying to get invitation", detail="Brain not found while trying to get invitation",
) )
return {"name": brain_details[0]["name"]} return {"name": brain_details[0]["name"], "rights": invitation["rights"]}
@subscription_router.post( @subscription_router.post(

View File

@ -6,6 +6,7 @@ import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useSubscriptionApi } from "@/lib/api/subscription/useSubscriptionApi"; import { useSubscriptionApi } from "@/lib/api/subscription/useSubscriptionApi";
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext"; import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { useToast } from "@/lib/hooks"; import { useToast } from "@/lib/hooks";
import { useEventTracking } from "@/services/analytics/useEventTracking"; import { useEventTracking } from "@/services/analytics/useEventTracking";
@ -16,6 +17,7 @@ export const useInvitation = () => {
const brainId = params?.brainId as UUID | undefined; const brainId = params?.brainId as UUID | undefined;
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [brainName, setBrainName] = useState<string>(""); const [brainName, setBrainName] = useState<string>("");
const [rights, setRights] = useState<BrainRoleType | undefined>();
const [isProcessingRequest, setIsProcessingRequest] = useState(false); const [isProcessingRequest, setIsProcessingRequest] = useState(false);
const { publish } = useToast(); const { publish } = useToast();
@ -35,8 +37,9 @@ export const useInvitation = () => {
const checkInvitationValidity = async () => { const checkInvitationValidity = async () => {
try { try {
const invitationBrain = await getInvitation(brainId); const { name, rights: assignedRights } = await getInvitation(brainId);
setBrainName(invitationBrain.name); setBrainName(name);
setRights(assignedRights);
} catch (error) { } catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) { if (axios.isAxiosError(error) && error.response?.status === 404) {
publish({ publish({
@ -126,8 +129,9 @@ export const useInvitation = () => {
return { return {
handleAccept, handleAccept,
handleDecline, handleDecline,
isLoading,
brainName, brainName,
rights,
isLoading,
isProcessingRequest, isProcessingRequest,
}; };
}; };

View File

@ -15,6 +15,7 @@ const InvitationPage = (): JSX.Element => {
handleDecline, handleDecline,
isLoading, isLoading,
brainName, brainName,
rights,
} = useInvitation(); } = useInvitation();
const { session } = useSupabase(); const { session } = useSupabase();
@ -26,11 +27,15 @@ const InvitationPage = (): JSX.Element => {
redirectToLogin(); redirectToLogin();
} }
if (rights === undefined) {
throw new Error("Rights are undefined");
}
return ( return (
<main className="pt-10"> <main className="pt-10">
<PageHeading <PageHeading
title={`Welcome to ${brainName}!`} title={`Welcome to ${brainName}!`}
subtitle="You have been invited to join this brain and start exploring. Do you accept this exciting journey?" subtitle={`You have been invited to join this brain as a ${rights} and start exploring. Do you accept this exciting journey?`}
/> />
{isProcessingRequest ? ( {isProcessingRequest ? (
<div className="flex flex-col items-center justify-center mt-5"> <div className="flex flex-col items-center justify-center mt-5">

View File

@ -1,6 +1,8 @@
import { AxiosInstance } from "axios"; import { AxiosInstance } from "axios";
import { UUID } from "crypto"; import { UUID } from "crypto";
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
export const acceptInvitation = async ( export const acceptInvitation = async (
brainId: UUID, brainId: UUID,
axiosInstance: AxiosInstance axiosInstance: AxiosInstance
@ -31,6 +33,7 @@ export const declineInvitation = async (
export type InvitationBrain = { export type InvitationBrain = {
name: string; name: string;
rights: BrainRoleType;
}; };
export const getInvitation = async ( export const getInvitation = async (