mirror of
https://git.v0id.ovh/n3wt-innov/n3wt-school.git
synced 2026-01-28 15:33:22 +00:00
feat: Ajout du logo de l'école
This commit is contained in:
@ -223,14 +223,29 @@ def makeToken(user):
|
||||
"""
|
||||
try:
|
||||
# Récupérer tous les rôles de l'utilisateur actifs
|
||||
roles = ProfileRole.objects.filter(profile=user, is_active=True).values('role_type', 'establishment__id', 'establishment__name', 'establishment__evaluation_frequency', 'establishment__total_capacity', 'establishment__api_docuseal')
|
||||
roles_qs = ProfileRole.objects.filter(profile=user, is_active=True).select_related('establishment')
|
||||
roles = []
|
||||
for role in roles_qs:
|
||||
logo_url = ""
|
||||
if role.establishment.logo:
|
||||
# Construit l'URL complète pour le logo
|
||||
logo_url = f"{role.establishment.logo.url}"
|
||||
roles.append({
|
||||
"role_type": role.role_type,
|
||||
"establishment__id": role.establishment.id,
|
||||
"establishment__name": role.establishment.name,
|
||||
"establishment__evaluation_frequency": role.establishment.evaluation_frequency,
|
||||
"establishment__total_capacity": role.establishment.total_capacity,
|
||||
"establishment__api_docuseal": role.establishment.api_docuseal,
|
||||
"establishment__logo": logo_url,
|
||||
})
|
||||
|
||||
# Générer le JWT avec la bonne syntaxe datetime
|
||||
access_payload = {
|
||||
'user_id': user.id,
|
||||
'email': user.email,
|
||||
'roleIndexLoginDefault':user.roleIndexLoginDefault,
|
||||
'roles': list(roles),
|
||||
'roleIndexLoginDefault': user.roleIndexLoginDefault,
|
||||
'roles': roles,
|
||||
'type': 'access',
|
||||
'exp': datetime.utcnow() + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'],
|
||||
'iat': datetime.utcnow(),
|
||||
|
||||
@ -2,6 +2,12 @@ from django.db import models
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
import os
|
||||
|
||||
def registration_logo_upload_to(instance, filename):
|
||||
ext = os.path.splitext(filename)[1]
|
||||
return f"logos/school_{instance.pk}/logo{ext}"
|
||||
|
||||
class StructureType(models.IntegerChoices):
|
||||
MATERNELLE = 1, _('Maternelle')
|
||||
PRIMAIRE = 2, _('Primaire')
|
||||
@ -22,6 +28,11 @@ class Establishment(models.Model):
|
||||
is_active = models.BooleanField(default=True)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
api_docuseal = models.CharField(max_length=255, blank=True, null=True)
|
||||
logo = models.FileField(
|
||||
upload_to=registration_logo_upload_to,
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@ -1,7 +1,7 @@
|
||||
from django.http.response import JsonResponse
|
||||
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
|
||||
from django.utils.decorators import method_decorator
|
||||
from rest_framework.parsers import JSONParser
|
||||
from rest_framework.parsers import JSONParser, MultiPartParser, FormParser
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework import status
|
||||
from .models import Establishment
|
||||
@ -12,6 +12,8 @@ from django.db.models import Q
|
||||
from Auth.models import Profile, ProfileRole, Directeur
|
||||
from Settings.models import SMTPSettings
|
||||
import N3wtSchool.mailManager as mailer
|
||||
import os
|
||||
from N3wtSchool import settings
|
||||
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
@ -42,6 +44,8 @@ class EstablishmentListCreateView(APIView):
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(ensure_csrf_cookie, name='dispatch')
|
||||
class EstablishmentDetailView(APIView):
|
||||
parser_classes = [MultiPartParser, FormParser]
|
||||
|
||||
def get(self, request, id=None):
|
||||
try:
|
||||
establishment = Establishment.objects.get(id=id)
|
||||
@ -51,15 +55,20 @@ class EstablishmentDetailView(APIView):
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def put(self, request, id):
|
||||
establishment_data = JSONParser().parse(request)
|
||||
"""
|
||||
Met à jour un établissement existant.
|
||||
Accepte les données en multipart/form-data pour permettre l'upload de fichiers (ex : logo).
|
||||
"""
|
||||
try:
|
||||
establishment = Establishment.objects.get(id=id)
|
||||
except Establishment.DoesNotExist:
|
||||
return JsonResponse({'error': 'No object found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
establishment_serializer = EstablishmentSerializer(establishment, data=establishment_data, partial=True)
|
||||
|
||||
# Utilise request.data pour supporter multipart/form-data (fichiers et champs classiques)
|
||||
establishment_serializer = EstablishmentSerializer(establishment, data=request.data, partial=True)
|
||||
if establishment_serializer.is_valid():
|
||||
establishment_serializer.save()
|
||||
return JsonResponse(establishment_serializer.data, safe=False)
|
||||
return JsonResponse(establishment_serializer.data, safe=False, status=status.HTTP_200_OK)
|
||||
return JsonResponse(establishment_serializer.errors, safe=False, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
def delete(self, request, id):
|
||||
@ -67,6 +76,7 @@ class EstablishmentDetailView(APIView):
|
||||
|
||||
def create_establishment_with_directeur(establishment_data):
|
||||
# Extraction des sous-objets
|
||||
# school_name = establishment_data.get("name")
|
||||
directeur_data = establishment_data.pop("directeur", None)
|
||||
smtp_settings_data = establishment_data.pop("smtp_settings", {})
|
||||
|
||||
@ -91,6 +101,8 @@ def create_establishment_with_directeur(establishment_data):
|
||||
# Création de l'établissement
|
||||
establishment_serializer = EstablishmentSerializer(data=establishment_data)
|
||||
establishment_serializer.is_valid(raise_exception=True)
|
||||
# base_dir = os.path.join(settings.MEDIA_ROOT, f"logo/school_{school_name}")
|
||||
# os.makedirs(base_dir, exist_ok=True)
|
||||
establishment = establishment_serializer.save()
|
||||
|
||||
# Création ou récupération du ProfileRole ADMIN pour ce profil et cet établissement
|
||||
|
||||
@ -18,6 +18,11 @@ const nextConfig = {
|
||||
protocol: 'https',
|
||||
hostname: 'www.gravatar.com',
|
||||
},
|
||||
{
|
||||
protocol: 'http',
|
||||
hostname: 'localhost',
|
||||
port: '8080',
|
||||
},
|
||||
],
|
||||
},
|
||||
env: {
|
||||
|
||||
@ -9,6 +9,9 @@ import { usePopup } from '@/context/PopupContext';
|
||||
import { getRightStr } from '@/utils/rights';
|
||||
import { ChevronDown } from 'lucide-react'; // Import de l'icône
|
||||
import Image from 'next/image'; // Import du composant Image
|
||||
import {
|
||||
BASE_URL,
|
||||
} from '@/utils/Url';
|
||||
|
||||
const ProfileSelector = ({ onRoleChange, className = '' }) => {
|
||||
const {
|
||||
@ -20,6 +23,8 @@ const ProfileSelector = ({ onRoleChange, className = '' }) => {
|
||||
user,
|
||||
setSelectedEstablishmentEvaluationFrequency,
|
||||
setSelectedEstablishmentTotalCapacity,
|
||||
selectedEstablishmentLogo,
|
||||
setSelectedEstablishmentLogo
|
||||
} = useEstablishment();
|
||||
const { isConnected, connectionStatus } = useChatConnection();
|
||||
const [dropdownOpen, setDropdownOpen] = useState(false);
|
||||
@ -33,12 +38,15 @@ const ProfileSelector = ({ onRoleChange, className = '' }) => {
|
||||
user.roles[roleId].establishment__evaluation_frequency;
|
||||
const establishmentTotalCapacity =
|
||||
user.roles[roleId].establishment__total_capacity;
|
||||
const establishmentLogo =
|
||||
user.roles[roleId].establishment__logo;
|
||||
setProfileRole(role);
|
||||
setSelectedEstablishmentId(establishmentId);
|
||||
setSelectedEstablishmentEvaluationFrequency(
|
||||
establishmentEvaluationFrequency
|
||||
);
|
||||
setSelectedEstablishmentTotalCapacity(establishmentTotalCapacity);
|
||||
setSelectedEstablishmentLogo(establishmentLogo);
|
||||
setSelectedRoleId(roleId);
|
||||
if (onRoleChange) {
|
||||
onRoleChange(roleId);
|
||||
@ -99,14 +107,14 @@ const ProfileSelector = ({ onRoleChange, className = '' }) => {
|
||||
<div className={`relative ${className}`}>
|
||||
<DropdownMenu
|
||||
buttonContent={
|
||||
<div className="h-16 flex items-center gap-2 cursor-pointer px-4 bg-white">
|
||||
<div className="h-16 flex items-center gap-2 cursor-pointer px-4 bg-white h-24">
|
||||
<div className="relative">
|
||||
<Image
|
||||
src={getGravatarUrl(user?.email)}
|
||||
src={selectedEstablishmentLogo ? `${BASE_URL}${selectedEstablishmentLogo}` : getGravatarUrl(user?.email)}
|
||||
alt="Profile"
|
||||
className="w-10 h-10 rounded-full object-cover shadow-md"
|
||||
width={32}
|
||||
height={32}
|
||||
className="w-16 h-16 rounded-full object-cover shadow-md"
|
||||
width={64}
|
||||
height={64}
|
||||
/>
|
||||
{/* Bulle de statut de connexion au chat */}
|
||||
<div
|
||||
@ -116,7 +124,7 @@ const ProfileSelector = ({ onRoleChange, className = '' }) => {
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div
|
||||
className="font-bold text-left truncate max-w-full"
|
||||
className="font-bold text-left truncate max-w-full text-sm"
|
||||
title={user?.email}
|
||||
>
|
||||
{user?.email}
|
||||
@ -125,11 +133,16 @@ const ProfileSelector = ({ onRoleChange, className = '' }) => {
|
||||
className="text-sm text-gray-500 text-left truncate max-w-full"
|
||||
title={`${getRightStr(selectedEstablishment?.role_type) || ''}${selectedEstablishment?.name ? ', ' + selectedEstablishment.name : ''}`}
|
||||
>
|
||||
{getRightStr(selectedEstablishment?.role_type) || ''}
|
||||
{selectedEstablishment?.name
|
||||
? `, ${selectedEstablishment.name}`
|
||||
? `${selectedEstablishment.name}`
|
||||
: ''}
|
||||
</div>
|
||||
<div
|
||||
className="italic text-sm text-gray-500 text-left truncate max-w-full"
|
||||
title={`${getRightStr(selectedEstablishment?.role_type) || ''}${selectedEstablishment?.name ? ', ' + selectedEstablishment.name : ''}`}
|
||||
>
|
||||
{getRightStr(selectedEstablishment?.role_type) || ''}
|
||||
</div>
|
||||
</div>
|
||||
<ChevronDown
|
||||
className={`w-5 h-5 transition-transform duration-200 ${dropdownOpen ? 'rotate-180' : 'rotate-0'}`}
|
||||
|
||||
@ -35,7 +35,7 @@ function Sidebar({ currentPage, items, onCloseMobile }) {
|
||||
return (
|
||||
<div className="w-64 bg-stone-50 border-r h-full border-gray-200">
|
||||
<div className="border-b border-gray-200 ">
|
||||
<ProfileSelector className="border-none" />
|
||||
<ProfileSelector className="border-none h-24" />
|
||||
</div>
|
||||
<nav className="space-y-1 px-4 py-6">
|
||||
{items.map((item) => (
|
||||
|
||||
@ -50,6 +50,10 @@ export const EstablishmentProvider = ({ children }) => {
|
||||
const storedApiDocuseal = sessionStorage.getItem('apiDocuseal');
|
||||
return storedApiDocuseal ? JSON.parse(storedApiDocuseal) : null;
|
||||
});
|
||||
const [selectedEstablishmentLogo, setSelectedEstablishmentLogoState] = useState(() => {
|
||||
const storedLogo = sessionStorage.getItem('selectedEstablishmentLogo');
|
||||
return storedLogo ? JSON.parse(storedLogo) : null;
|
||||
});
|
||||
|
||||
// Sauvegarder dans sessionStorage à chaque mise à jour
|
||||
const setSelectedEstablishmentId = (id) => {
|
||||
@ -95,6 +99,11 @@ export const EstablishmentProvider = ({ children }) => {
|
||||
sessionStorage.setItem('apiDocuseal', JSON.stringify(api));
|
||||
};
|
||||
|
||||
const setSelectedEstablishmentLogo = (logo) => {
|
||||
setSelectedEstablishmentLogoState(logo);
|
||||
sessionStorage.setItem('selectedEstablishmentLogo', JSON.stringify(logo));
|
||||
};
|
||||
|
||||
/**
|
||||
* Fonction d'initialisation du contexte avec la session (appelée lors du login)
|
||||
* @param {*} session
|
||||
@ -114,6 +123,7 @@ export const EstablishmentProvider = ({ children }) => {
|
||||
evaluation_frequency: role.establishment__evaluation_frequency,
|
||||
total_capacity: role.establishment__total_capacity,
|
||||
api_docuseal: role.establishment__api_docuseal,
|
||||
logo: role.establishment__logo,
|
||||
role_id: i,
|
||||
role_type: role.role_type,
|
||||
}));
|
||||
@ -136,6 +146,9 @@ export const EstablishmentProvider = ({ children }) => {
|
||||
setApiDocuseal(
|
||||
userEstablishments[roleIndexDefault].api_docuseal
|
||||
);
|
||||
setSelectedEstablishmentLogo(
|
||||
userEstablishments[roleIndexDefault].logo
|
||||
);
|
||||
setProfileRole(userEstablishments[roleIndexDefault].role_type);
|
||||
}
|
||||
if (endInitFunctionHandler) {
|
||||
@ -156,6 +169,7 @@ export const EstablishmentProvider = ({ children }) => {
|
||||
setSelectedEstablishmentEvaluationFrequencyState(null);
|
||||
setSelectedEstablishmentTotalCapacityState(null);
|
||||
setApiDocusealState(null);
|
||||
setSelectedEstablishmentLogoState(null);
|
||||
sessionStorage.clear();
|
||||
};
|
||||
|
||||
@ -172,6 +186,8 @@ export const EstablishmentProvider = ({ children }) => {
|
||||
setSelectedEstablishmentTotalCapacity,
|
||||
apiDocuseal,
|
||||
setApiDocuseal,
|
||||
selectedEstablishmentLogo,
|
||||
setSelectedEstablishmentLogo,
|
||||
selectedRoleId,
|
||||
setSelectedRoleId,
|
||||
profileRole,
|
||||
|
||||
@ -6,11 +6,11 @@ export const RIGHTS = {
|
||||
|
||||
export function getRightStr(right) {
|
||||
if (right === RIGHTS.ADMIN) {
|
||||
return 'ADMINISTRATEUR';
|
||||
return 'Administrateur';
|
||||
} else if (right === RIGHTS.TEACHER) {
|
||||
return 'PROFESSEUR';
|
||||
return 'Professeur';
|
||||
} else if (right === RIGHTS.PARENT) {
|
||||
return 'PARENT';
|
||||
return 'Parent';
|
||||
} else {
|
||||
return 'NON DEFINI';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user