Skip to content

Commit

Permalink
Minor UX improvments (QuivrHQ#717)
Browse files Browse the repository at this point in the history
* feat: display user rights on invitation page

* feat: add brain name in invitation email
  • Loading branch information
mamadoudicko authored Jul 20, 2023
1 parent d7ca11f commit eb779f9
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 18 deletions.
7 changes: 3 additions & 4 deletions backend/core/models/brains_subscription_invitations.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from typing import Optional
from uuid import UUID

import resend
from logger import get_logger
from models.settings import BrainSettings, CommonsDep, common_dependencies
from pydantic import BaseModel

from models.settings import CommonsDep, common_dependencies

logger = get_logger(__name__)


Expand Down Expand Up @@ -64,4 +63,4 @@ def create_or_update_subscription_invitation(self):
else:
response = self.create_subscription_invitation()

return response
return response
Original file line number Diff line number Diff line change
@@ -1,31 +1,43 @@
import resend
from logger import get_logger
from models.brains import Brain
from models.brains_subscription_invitations import BrainSubscription
from models.settings import BrainSettings

from repository.brain_subscription.get_brain_url import get_brain_url

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
resend.api_key = brains_settings.resend_api_key

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"""
<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>
"""

try:
r = resend.Emails.send({
"from": brains_settings.resend_email_address,
"to": brain_subscription.email,
"subject": "Quivr - Brain Shared With You",
"html": html_body
})
logger.info('Resend response', r)
r = resend.Emails.send(
{
"from": brains_settings.resend_email_address,
"to": brain_subscription.email,
"subject": "Quivr - Brain Shared With You",
"html": html_body,
}
)
logger.info("Resend response", r)
except Exception as e:
logger.error(f"Error sending email: {e}")
return
Expand Down
2 changes: 1 addition & 1 deletion backend/core/routes/subscription_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
)

return {"name": brain_details[0]["name"]}
return {"name": brain_details[0]["name"], "rights": invitation["rights"]}


@subscription_router.post(
Expand Down
10 changes: 7 additions & 3 deletions frontend/app/invitation/[brainId]/hooks/useInvitation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useParams, useRouter } from "next/navigation";
import { useEffect, useState } from "react";

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 { useToast } from "@/lib/hooks";
import { useEventTracking } from "@/services/analytics/useEventTracking";
Expand All @@ -16,6 +17,7 @@ export const useInvitation = () => {
const brainId = params?.brainId as UUID | undefined;
const [isLoading, setIsLoading] = useState(false);
const [brainName, setBrainName] = useState<string>("");
const [rights, setRights] = useState<BrainRoleType | undefined>();
const [isProcessingRequest, setIsProcessingRequest] = useState(false);

const { publish } = useToast();
Expand All @@ -35,8 +37,9 @@ export const useInvitation = () => {

const checkInvitationValidity = async () => {
try {
const invitationBrain = await getInvitation(brainId);
setBrainName(invitationBrain.name);
const { name, rights: assignedRights } = await getInvitation(brainId);
setBrainName(name);
setRights(assignedRights);
} catch (error) {
if (axios.isAxiosError(error) && error.response?.status === 404) {
publish({
Expand Down Expand Up @@ -126,8 +129,9 @@ export const useInvitation = () => {
return {
handleAccept,
handleDecline,
isLoading,
brainName,
rights,
isLoading,
isProcessingRequest,
};
};
7 changes: 6 additions & 1 deletion frontend/app/invitation/[brainId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const InvitationPage = (): JSX.Element => {
handleDecline,
isLoading,
brainName,
rights,
} = useInvitation();
const { session } = useSupabase();

Expand All @@ -26,11 +27,15 @@ const InvitationPage = (): JSX.Element => {
redirectToLogin();
}

if (rights === undefined) {
throw new Error("Rights are undefined");
}

return (
<main className="pt-10">
<PageHeading
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 ? (
<div className="flex flex-col items-center justify-center mt-5">
Expand Down
3 changes: 3 additions & 0 deletions frontend/lib/api/subscription/subscription.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AxiosInstance } from "axios";
import { UUID } from "crypto";

import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";

export const acceptInvitation = async (
brainId: UUID,
axiosInstance: AxiosInstance
Expand Down Expand Up @@ -31,6 +33,7 @@ export const declineInvitation = async (

export type InvitationBrain = {
name: string;
rights: BrainRoleType;
};

export const getInvitation = async (
Expand Down

0 comments on commit eb779f9

Please sign in to comment.