Compare commits
5 Commits
modal
...
poc-invitation
| Author | SHA1 | Date | |
|---|---|---|---|
| d9b9f32e44 | |||
| b25a0065c2 | |||
| bc9143096b | |||
| 45908042c6 | |||
| ed3b9ad50d |
@@ -1,11 +1,17 @@
|
||||
"""API endpoints"""
|
||||
import json
|
||||
import smtplib
|
||||
import uuid
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core import mail
|
||||
from django.db.models import Q
|
||||
from django.http import Http404
|
||||
from django.http import Http404, JsonResponse
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.template.loader import render_to_string
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from rest_framework import (
|
||||
decorators,
|
||||
@@ -276,3 +282,43 @@ class ResourceAccessViewSet(
|
||||
permission_classes = [permissions.ResourceAccessPermission]
|
||||
queryset = models.ResourceAccess.objects.all()
|
||||
serializer_class = serializers.ResourceAccessSerializer
|
||||
|
||||
|
||||
def invite(request):
|
||||
"""PoC of sending email invitation."""
|
||||
|
||||
if request.method != "POST":
|
||||
return JsonResponse({"error": "Method not allowed"}, status=405)
|
||||
if not request.user.is_authenticated:
|
||||
return JsonResponse({"error": "Authentication required"}, status=401)
|
||||
|
||||
data = json.loads(request.body)
|
||||
emails = data.get("emails")
|
||||
room = data.get("room")
|
||||
|
||||
if not emails or not room:
|
||||
return JsonResponse(
|
||||
{"error": "Emails and room are required fields."}, status=400
|
||||
)
|
||||
|
||||
try:
|
||||
template_vars = {
|
||||
"title": _("Invitation to join a room!"),
|
||||
"site": Site.objects.get_current(),
|
||||
"room": room,
|
||||
"sender": str(request.user),
|
||||
}
|
||||
msg_html = render_to_string("mail/html/invitation.html", template_vars)
|
||||
msg_plain = render_to_string("mail/text/invitation.txt", template_vars)
|
||||
mail.send_mail(
|
||||
_("Invitation to join a room!"),
|
||||
msg_plain,
|
||||
settings.EMAIL_FROM,
|
||||
emails,
|
||||
html_message=msg_html,
|
||||
fail_silently=False,
|
||||
)
|
||||
except smtplib.SMTPException:
|
||||
return JsonResponse({"error": "Failed to send invitation emails"}, status=500)
|
||||
|
||||
return JsonResponse({"msg": "invitation sent."}, status=200)
|
||||
|
||||
@@ -22,6 +22,7 @@ urlpatterns = [
|
||||
[
|
||||
*router.urls,
|
||||
*oidc_urls,
|
||||
path("invite/", viewsets.invite, name="invite")
|
||||
]
|
||||
),
|
||||
),
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
import {fetchApi} from '@/api/fetchApi'
|
||||
|
||||
export const inviteToRoom = (roomId, emails) => {
|
||||
return fetchApi(
|
||||
`/invite/`,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(
|
||||
{
|
||||
emails: emails,
|
||||
room: roomId
|
||||
}
|
||||
)
|
||||
}
|
||||
).then((ee) => {
|
||||
console.log(ee)
|
||||
alert("invitation envoyée")
|
||||
}).catch((ee) => {
|
||||
console.log(ee)
|
||||
alert("invitation failed")
|
||||
})
|
||||
}
|
||||
@@ -9,6 +9,162 @@ import { keys } from '@/api/queryKeys'
|
||||
import { QueryAware } from '@/layout/QueryAware'
|
||||
import { navigateToHome } from '@/navigation/navigateToHome'
|
||||
import { fetchRoom } from '../api/fetchRoom'
|
||||
import {Box, Button, Div, H, P, Text} from "@/primitives";
|
||||
|
||||
import { cva } from '@/styled-system/css'
|
||||
import {styled} from "@/styled-system/jsx";
|
||||
import {useRef, useState} from "react";
|
||||
import {inviteToRoom} from "@/features/rooms/api/inviteToRoom";
|
||||
|
||||
|
||||
const popin = cva({
|
||||
base: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "start",
|
||||
justifyContent: "space-between",
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'box.border',
|
||||
backgroundColor: 'box.bg',
|
||||
color: 'box.text',
|
||||
boxShadow: 'box',
|
||||
borderRadius: 8,
|
||||
padding: 'boxPadding',
|
||||
flex: 1,
|
||||
position: "absolute",
|
||||
zIndex: 100,
|
||||
}
|
||||
})
|
||||
|
||||
const Popin = styled('div', popin)
|
||||
|
||||
const fakeInput = cva({
|
||||
base: {
|
||||
height: "40px",
|
||||
display: "flex",
|
||||
borderRadius: "4px",
|
||||
background: "rgb(241,243,244)",
|
||||
alignItems: "center",
|
||||
marginTop: ".5rem",
|
||||
paddingLeft: ".75rem",
|
||||
width: "100%",
|
||||
justifyContent: "space-between"
|
||||
}
|
||||
})
|
||||
|
||||
const FakeInput = styled('div', fakeInput)
|
||||
|
||||
const getLocation = () => {
|
||||
return window.location.hostname + window.location.pathname;
|
||||
}
|
||||
|
||||
const CopyIcon = ({size = 16}) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} fill="currentColor" className="bi bi-copy" viewBox="0 0 16 16">
|
||||
<path fill-rule="evenodd" d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const CloseIcon = ({size = 16}) => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={size} height={size} fill="currentColor" className="bi bi-x" viewBox="0 0 16 16">
|
||||
<path d="M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708"/>
|
||||
</svg>
|
||||
)
|
||||
|
||||
const Invitation = ({onClose, onOpenEmailModal}) => (
|
||||
<Popin style={{ bottom: "100px", left: "24px", maxWidth: "350px" }}>
|
||||
<Div style={{display: "flex", justifyContent: "space-between", width: '100%'}}>
|
||||
<H lvl={2} style={{ marginBottom: "0.75rem"}}>Votre réunion est prête</H>
|
||||
<Div style={{cursor: "pointer"}} onClick={onClose}>
|
||||
<CloseIcon size={25} />
|
||||
</Div>
|
||||
</Div>
|
||||
<Button variant="primary" onClick={onOpenEmailModal}>Ajouter des participants</Button>
|
||||
<P style={{fontSize: "14px", marginBottom: "0", marginTop: "0.5rem"}}>
|
||||
Ou partagez ce lien avec les personnes que vous souhaitez inviter à la réunion
|
||||
</P>
|
||||
<FakeInput>
|
||||
<Text as="span" style={{fontSize: '0.75rem', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: '70%'}}>{getLocation()}</Text>
|
||||
<Div style={{paddingRight: ".75rem", cursor: "pointer"}} onClick={() => navigator.clipboard.writeText(getLocation())}><CopyIcon size={20} /></Div>
|
||||
</FakeInput>
|
||||
<P style={{fontSize: "12px", marginBottom: "0", marginTop: "0.5rem"}}>
|
||||
Les personnes utilisant le lien de cette réunion pourront rejoindre la réunion sans demander votre autorisation.
|
||||
</P>
|
||||
</Popin>
|
||||
)
|
||||
|
||||
|
||||
const modalContainer = cva({
|
||||
base: {
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
position: "absolute",
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
zIndex: 200,
|
||||
}
|
||||
})
|
||||
|
||||
const ModalContainer = styled('div', modalContainer)
|
||||
|
||||
const EmailModal = ({onClose, onSubmit, closeOnSubmit = false}) => {
|
||||
const emailRef = useRef(null);
|
||||
return (
|
||||
<ModalContainer>
|
||||
<InnerModal>
|
||||
<Div style={{display: "flex", justifyContent: "space-between", width: '100%'}}>
|
||||
<H lvl={2} style={{ marginBottom: "0.75rem"}}>Inviter des participants</H>
|
||||
<Div style={{cursor: "pointer"}} onClick={onClose}>
|
||||
<CloseIcon size={25} />
|
||||
</Div>
|
||||
</Div>
|
||||
<P>
|
||||
Inviter un participant par email.
|
||||
</P>
|
||||
<Div style={{width: "100%"}}>
|
||||
<input ref={emailRef} type="email" placeholder="sam@meet.co" name="email" style={{border: '1px solid black', borderRadius: '4px', minWidth: '250px', padding: '0 .5rem'}}/>
|
||||
<Button onClick={() => {
|
||||
const emailValue = emailRef.current.value;
|
||||
if(!emailValue || !/\S+@\S+\.\S+/.test(emailValue)) {
|
||||
return
|
||||
}
|
||||
onSubmit(emailValue)
|
||||
if (closeOnSubmit) {
|
||||
onClose()
|
||||
}
|
||||
}}>inviter</Button>
|
||||
</Div>
|
||||
</InnerModal>
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
const innerModal = cva({
|
||||
base: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "start",
|
||||
justifyContent: "space-between",
|
||||
borderWidth: '1px',
|
||||
borderStyle: 'solid',
|
||||
borderColor: 'box.border',
|
||||
backgroundColor: 'box.bg',
|
||||
color: 'box.text',
|
||||
boxShadow: 'box',
|
||||
borderRadius: 8,
|
||||
padding: 'boxPadding',
|
||||
flex: 1,
|
||||
maxWidth: "400px"
|
||||
}
|
||||
})
|
||||
|
||||
const InnerModal = styled('div', innerModal)
|
||||
|
||||
export const Conference = ({
|
||||
userConfig,
|
||||
@@ -25,8 +181,23 @@ export const Conference = ({
|
||||
}),
|
||||
})
|
||||
|
||||
const [isInvitationOpened, setIsInvitationOpened] = useState(true)
|
||||
const [isEmailModalOpened, setIsEmailModalOpened] = useState(false)
|
||||
|
||||
return (
|
||||
<QueryAware status={status}>
|
||||
{
|
||||
isInvitationOpened && (
|
||||
<Invitation onClose={() => setIsInvitationOpened(false)} onOpenEmailModal={() => setIsEmailModalOpened(true)} />
|
||||
)
|
||||
}
|
||||
{
|
||||
isEmailModalOpened && (
|
||||
<EmailModal onClose={() => setIsEmailModalOpened(false)} onSubmit={(email) => {
|
||||
inviteToRoom(roomId, [email])
|
||||
}} closeOnSubmit />
|
||||
)
|
||||
}
|
||||
<LiveKitRoom
|
||||
serverUrl={data?.livekit?.url}
|
||||
token={data?.livekit?.token}
|
||||
|
||||
@@ -11,39 +11,14 @@
|
||||
<mj-section mj-class="bg--white-100" padding="30px 20px 60px 20px">
|
||||
<mj-column>
|
||||
<mj-text font-size="14px">
|
||||
<p>{% trans "Invitation to join a team" %}</p>
|
||||
</mj-text>
|
||||
|
||||
<!-- Welcome Message -->
|
||||
<mj-text>
|
||||
<h1>{% blocktrans %}Welcome to <strong>Meet</strong>{% endblocktrans %}</h1>
|
||||
</mj-text>
|
||||
<mj-divider border-width="1px" border-style="solid" border-color="#DDDDDD" width="30%" align="left"/>
|
||||
|
||||
<mj-image src="{% base64_static 'images/logo.svg' %}" width="157px" align="left" alt="{%trans 'Logo' %}" />
|
||||
|
||||
<!-- Main Message -->
|
||||
<mj-text>{% trans "We are delighted to welcome you to our community on Meet, your new companion to collaborate on documents efficiently, intuitively, and securely." %}</mj-text>
|
||||
<mj-text>{% trans "Our application is designed to help you organize, collaborate, and manage permissions." %}</mj-text>
|
||||
<mj-text>
|
||||
{% trans "With Meet, you will be able to:" %}
|
||||
<ul>
|
||||
<li>{% trans "Create documents."%}</li>
|
||||
<li>{% trans "Invite members of your document or community in just a few clicks."%}</li>
|
||||
</ul>
|
||||
</mj-text>
|
||||
<mj-button href="//{{site.domain}}" background-color="#000091" color="white" padding-bottom="30px">
|
||||
{% trans "Visit Meet"%}
|
||||
</mj-button>
|
||||
<mj-text>{% trans "We are confident that Meet will help you increase efficiency and productivity while strengthening the bond among members." %}</mj-text>
|
||||
<mj-text>{% trans "Feel free to explore all the features of the application and share your feedback and suggestions with us. Your feedback is valuable to us and will enable us to continually improve our service." %}</mj-text>
|
||||
<mj-text>{% trans "Once again, welcome aboard! We are eager to accompany you on you collaboration adventure." %}</mj-text>
|
||||
|
||||
<!-- Signature -->
|
||||
<mj-text>
|
||||
<p>{% trans "Sincerely," %}</p>
|
||||
<p>{% trans "The La Suite Numérique Team" %}</p>
|
||||
</mj-text>
|
||||
<p>{{sender}} {% trans "invite you to join a meeting" %}</p>
|
||||
</mj-text>
|
||||
<mj-button href="//{{site.domain}}/{{room}}" background-color="#000091" color="white" padding-bottom="30px">
|
||||
{% trans "Join meeting"%}
|
||||
</mj-button>
|
||||
<mj-text font-size="14px">
|
||||
<p>{{site.domain}}/{{room}}</p>
|
||||
</mj-text>
|
||||
</mj-column>
|
||||
</mj-section>
|
||||
</mj-wrapper>
|
||||
|
||||
Reference in New Issue
Block a user