4 Commits

59 changed files with 377 additions and 438 deletions

View File

@ -1,12 +0,0 @@
from django.test import TestCase
from .models import Profile
class ProfileModelTest(TestCase):
def test_create_profile(self):
user = Profile.objects.create_user(
username="testuser",
email="test@example.com",
password="testpass123"
)
self.assertEqual(user.email, "test@example.com")
self.assertTrue(user.check_password("testpass123"))

View File

@ -1,11 +1,3 @@
from django.test import TestCase
from .models import Domain
# Create your tests here.
class DomainModelTest(TestCase):
def test_create_domain(self):
domain = Domain.objects.create(name="Mathématiques", cycle=1)
self.assertEqual(domain.name, "Mathématiques")
self.assertEqual(domain.cycle, 1)
self.assertIsNotNone(domain.id)

View File

@ -1,17 +0,0 @@
from django.test import TestCase
from .models import Establishment, StructureType, EvaluationFrequency
class EstablishmentModelTest(TestCase):
def test_create_establishment(self):
est = Establishment.objects.create(
name="École Test",
address="1 rue de l'École",
total_capacity=100,
establishment_type=[StructureType.PRIMAIRE],
evaluation_frequency=EvaluationFrequency.TRIMESTER,
licence_code="ABC123"
)
self.assertEqual(est.name, "École Test")
self.assertEqual(est.establishment_type, [StructureType.PRIMAIRE])
self.assertTrue(est.is_active)
self.assertIsNotNone(est.created_at)

View File

@ -1,13 +0,0 @@
from django.test import TestCase
from .models import Conversation
class ConversationModelTest(TestCase):
def test_create_conversation(self):
conv = Conversation.objects.create(
name="Groupe Test",
conversation_type="group"
)
self.assertEqual(conv.name, "Groupe Test")
self.assertEqual(conv.conversation_type, "group")
self.assertTrue(conv.is_active)
self.assertIsNotNone(conv.id)

View File

@ -1,22 +0,0 @@
from django.test import TestCase
from .models import Notification, TypeNotif
from Auth.models import Profile
class NotificationModelTest(TestCase):
def setUp(self):
self.user = Profile.objects.create_user(
username="notifuser",
email="notif@example.com",
password="testpass123"
)
def test_create_notification(self):
notif = Notification.objects.create(
user=self.user,
message="Un message a été reçu",
typeNotification=TypeNotif.NOTIF_MESSAGE
)
self.assertEqual(notif.user, self.user)
self.assertEqual(notif.message, "Un message a été reçu")
self.assertFalse(notif.is_read)
self.assertEqual(notif.typeNotification, TypeNotif.NOTIF_MESSAGE)

View File

@ -1,27 +0,0 @@
from django.test import TestCase
from .models import Planning
from Establishment.models import Establishment
from School.models import SchoolClass
class PlanningModelTest(TestCase):
def setUp(self):
self.establishment = Establishment.objects.create(
name="École Test",
address="1 rue de l'École",
total_capacity=100,
establishment_type=[1],
evaluation_frequency=1,
licence_code="ABC123"
)
self.school_class = SchoolClass.objects.create(
atmosphere_name="Classe Test",
establishment=self.establishment
)
def test_create_planning(self):
planning = Planning.objects.create(
school_class=self.school_class,
establishment=self.establishment
)
self.assertEqual(planning.school_class, self.school_class)
self.assertEqual(planning.establishment, self.establishment)

View File

@ -1,77 +1,3 @@
from django.test import TestCase
from .models import Speciality, Teacher, SchoolClass, Planning, Discount, Fee, PaymentPlan, PaymentMode, Competency, EstablishmentCompetency
from Establishment.models import Establishment
from Common.models import Category, PaymentPlanType, PaymentModeType
from datetime import date
class SpecialityModelTest(TestCase):
def test_create_speciality(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
speciality = Speciality.objects.create(name="Maths", establishment=est)
self.assertEqual(speciality.name, "Maths")
self.assertEqual(speciality.establishment, est)
class TeacherModelTest(TestCase):
def test_create_teacher(self):
teacher = Teacher.objects.create(last_name="Martin", first_name="Paul")
self.assertEqual(teacher.last_name, "Martin")
self.assertEqual(teacher.first_name, "Paul")
class SchoolClassModelTest(TestCase):
def test_create_school_class(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
school_class = SchoolClass.objects.create(atmosphere_name="Classe A", establishment=est)
self.assertEqual(school_class.atmosphere_name, "Classe A")
self.assertEqual(school_class.establishment, est)
class PlanningModelTest(TestCase):
def test_create_planning(self):
school_class = SchoolClass.objects.create(atmosphere_name="Classe B", establishment=Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A"))
planning = Planning.objects.create(school_class=school_class)
self.assertEqual(planning.school_class, school_class)
class DiscountModelTest(TestCase):
def test_create_discount(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
discount = Discount.objects.create(name="Réduction", establishment=est)
self.assertEqual(discount.name, "Réduction")
self.assertEqual(discount.establishment, est)
class FeeModelTest(TestCase):
def test_create_fee(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
fee = Fee.objects.create(name="Frais", establishment=est)
self.assertEqual(fee.name, "Frais")
self.assertEqual(fee.establishment, est)
class PaymentPlanModelTest(TestCase):
def test_create_payment_plan(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
plan_type = PaymentPlanType.objects.create(code="A", label="Plan A")
payment_plan = PaymentPlan.objects.create(plan_type=plan_type, establishment=est)
self.assertEqual(payment_plan.plan_type, plan_type)
self.assertEqual(payment_plan.establishment, est)
class PaymentModeModelTest(TestCase):
def test_create_payment_mode(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
mode_type = PaymentModeType.objects.create(label="Espèces")
payment_mode = PaymentMode.objects.create(mode=mode_type, establishment=est)
self.assertEqual(payment_mode.mode, mode_type)
self.assertEqual(payment_mode.establishment, est)
class CompetencyModelTest(TestCase):
def test_create_competency(self):
cat = Category.objects.create(name="Maths", domain_id=1)
comp = Competency.objects.create(name="Compétence 1", category=cat)
self.assertEqual(comp.name, "Compétence 1")
self.assertEqual(comp.category, cat)
class EstablishmentCompetencyModelTest(TestCase):
def test_create_establishment_competency(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
cat = Category.objects.create(name="Maths", domain_id=1)
comp = Competency.objects.create(name="Compétence 2", category=cat)
est_comp = EstablishmentCompetency.objects.create(establishment=est, competency=comp)
self.assertEqual(est_comp.establishment, est)
self.assertEqual(est_comp.competency, comp)
# Create your tests here.

View File

@ -1,97 +0,0 @@
from django.test import TestCase
from .models import Language, Guardian, Sibling, BilanCompetence, Student, RegistrationFileGroup, RegistrationForm, RegistrationSchoolFileMaster, RegistrationParentFileMaster, RegistrationSchoolFileTemplate, StudentCompetency, RegistrationParentFileTemplate, AbsenceManagement
from django.utils import timezone
from datetime import date
from Establishment.models import Establishment
class LanguageModelTest(TestCase):
def test_create_language(self):
lang = Language.objects.create(label="Français")
self.assertEqual(lang.label, "Français")
self.assertIsNotNone(lang.id)
class GuardianModelTest(TestCase):
def test_create_guardian(self):
guardian = Guardian.objects.create(last_name="Dupont", first_name="Jean")
self.assertEqual(guardian.last_name, "Dupont")
self.assertEqual(guardian.first_name, "Jean")
class SiblingModelTest(TestCase):
def test_create_sibling(self):
sibling = Sibling.objects.create(last_name="Martin", first_name="Julie")
self.assertEqual(sibling.last_name, "Martin")
self.assertEqual(sibling.first_name, "Julie")
class BilanCompetenceModelTest(TestCase):
def test_create_bilan(self):
student = Student.objects.create(last_name="Test", first_name="Eleve")
bilan = BilanCompetence.objects.create(student=student, period="T1-2024_2025")
self.assertEqual(bilan.student, student)
self.assertEqual(bilan.period, "T1-2024_2025")
class StudentModelTest(TestCase):
def test_create_student(self):
student = Student.objects.create(last_name="Durand", first_name="Paul")
self.assertEqual(student.last_name, "Durand")
self.assertEqual(student.first_name, "Paul")
class RegistrationFileGroupModelTest(TestCase):
def test_create_group(self):
group = RegistrationFileGroup.objects.create(name="Groupe A")
self.assertEqual(group.name, "Groupe A")
class RegistrationFormModelTest(TestCase):
def test_create_registration_form(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
student = Student.objects.create(last_name="Test", first_name="Eleve")
group = RegistrationFileGroup.objects.create(name="Groupe B", establishment=est)
form = RegistrationForm.objects.create(student=student, fileGroup=group, establishment=est)
self.assertEqual(form.student, student)
self.assertEqual(form.fileGroup, group)
self.assertEqual(form.establishment, est)
class RegistrationSchoolFileMasterModelTest(TestCase):
def test_create_school_file_master(self):
master = RegistrationSchoolFileMaster.objects.create(id=1, name="Doc école")
self.assertEqual(master.name, "Doc école")
class RegistrationParentFileMasterModelTest(TestCase):
def test_create_parent_file_master(self):
master = RegistrationParentFileMaster.objects.create(name="Doc parent")
self.assertEqual(master.name, "Doc parent")
class RegistrationSchoolFileTemplateModelTest(TestCase):
def test_create_school_file_template(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
master = RegistrationSchoolFileMaster.objects.create(id=2, name="Doc école 2")
student = Student.objects.create(last_name="Test", first_name="Eleve")
group = RegistrationFileGroup.objects.create(name="Groupe C", establishment=est)
form = RegistrationForm.objects.create(student=student, fileGroup=group, establishment=est)
template = RegistrationSchoolFileTemplate.objects.create(id=2, master=master, name="Fichier école", registration_form=form)
self.assertEqual(template.name, "Fichier école")
self.assertEqual(template.master, master)
class StudentCompetencyModelTest(TestCase):
def test_create_student_competency(self):
student = Student.objects.create(last_name="Test", first_name="Eleve")
# Pour le test, on suppose qu'un modèle School.EstablishmentCompetency existe et est importable
# Ici, on ne peut pas créer l'objet sans ce modèle, donc ce test est à adapter selon la base réelle
pass
class RegistrationParentFileTemplateModelTest(TestCase):
def test_create_parent_file_template(self):
est = Establishment.objects.create(name="École Test", address="1 rue", total_capacity=10, establishment_type=[1], evaluation_frequency=1, licence_code="A")
master = RegistrationParentFileMaster.objects.create(name="Doc parent 2")
student = Student.objects.create(last_name="Test", first_name="Eleve")
group = RegistrationFileGroup.objects.create(name="Groupe D", establishment=est)
form = RegistrationForm.objects.create(student=student, fileGroup=group, establishment=est)
template = RegistrationParentFileTemplate.objects.create(master=master, registration_form=form)
self.assertEqual(template.master, master)
self.assertEqual(template.registration_form, form)
class AbsenceManagementModelTest(TestCase):
def test_create_absence(self):
student = Student.objects.create(last_name="Test", first_name="Eleve")
absence = AbsenceManagement.objects.create(student=student, day=date.today())
self.assertEqual(absence.student, student)
self.assertEqual(absence.day, date.today())

View File

@ -1,3 +0,0 @@
[pytest]
DJANGO_SETTINGS_MODULE = N3wtSchool.settings
python_files = tests.py test_*.py *_tests.py

View File

@ -66,10 +66,8 @@ urllib3==2.2.3
vine==5.1.0
wcwidth==0.2.13
webencodings==0.5.1
watchfiles
xhtml2pdf==0.2.16
channels==4.0.0
channels-redis==4.1.0
daphne==4.1.0
pytest
pytest-django
pytest-json-report

View File

@ -1,5 +1,6 @@
import subprocess
import os
from watchfiles import run_process
def run_command(command):
process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
@ -11,6 +12,7 @@ def run_command(command):
return process.returncode
test_mode = os.getenv('test_mode', 'false').lower() == 'true'
watch_mode = os.getenv('DJANGO_WATCH', 'false').lower() == 'true'
commands = [
["python", "manage.py", "collectstatic", "--noinput"],
@ -32,23 +34,55 @@ test_commands = [
["python", "manage.py", "init_mock_datas"]
]
for command in commands:
def run_daphne():
try:
result = subprocess.run([
"daphne", "-b", "0.0.0.0", "-p", "8080", "N3wtSchool.asgi:application"
])
return result.returncode
except KeyboardInterrupt:
print("Arrêt de Daphne (KeyboardInterrupt)")
return 0
if __name__ == "__main__":
for command in commands:
if run_command(command) != 0:
exit(1)
#if test_mode:
# for test_command in test_commands:
# if run_command(test_command) != 0:
# exit(1)
#if test_mode:
# for test_command in test_commands:
# if run_command(test_command) != 0:
# exit(1)
# Lancer les processus en parallèle
processes = [
subprocess.Popen(["daphne", "-b", "0.0.0.0", "-p", "8080", "N3wtSchool.asgi:application"]),
if watch_mode:
celery_worker = subprocess.Popen(["celery", "-A", "N3wtSchool", "worker", "--loglevel=info"])
celery_beat = subprocess.Popen(["celery", "-A", "N3wtSchool", "beat", "--loglevel=info", "--scheduler", "django_celery_beat.schedulers:DatabaseScheduler"])
try:
run_process(
'.',
target=run_daphne
)
except KeyboardInterrupt:
print("Arrêt demandé (KeyboardInterrupt)")
finally:
celery_worker.terminate()
celery_beat.terminate()
celery_worker.wait()
celery_beat.wait()
else:
processes = [
subprocess.Popen([
"daphne", "-b", "0.0.0.0", "-p", "8080", "N3wtSchool.asgi:application"
]),
subprocess.Popen(["celery", "-A", "N3wtSchool", "worker", "--loglevel=info"]),
subprocess.Popen(["celery", "-A", "N3wtSchool", "beat", "--loglevel=info", "--scheduler", "django_celery_beat.schedulers:DatabaseScheduler"])
]
# Attendre la fin des processus
for process in processes:
]
try:
for process in processes:
process.wait()
except KeyboardInterrupt:
print("Arrêt demandé (KeyboardInterrupt)")
for process in processes:
process.terminate()
for process in processes:
process.wait()

View File

@ -1,12 +1,12 @@
{
"name": "n3wt-school-front-end",
"version": "0.0.1",
"version": "0.0.3",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "n3wt-school-front-end",
"version": "0.0.1",
"version": "0.0.3",
"dependencies": {
"@docuseal/react": "^1.0.56",
"@radix-ui/react-dialog": "^1.1.2",
@ -29,6 +29,7 @@
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18",
"react-hook-form": "^7.62.0",
"react-international-phone": "^4.5.0",
"react-quill": "^2.0.0",
"react-tooltip": "^5.28.0"
@ -8834,6 +8835,21 @@
"react": "^18.3.1"
}
},
"node_modules/react-hook-form": {
"version": "7.62.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz",
"integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==",
"engines": {
"node": ">=18.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/react-hook-form"
},
"peerDependencies": {
"react": "^16.8.0 || ^17 || ^18 || ^19"
}
},
"node_modules/react-international-phone": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/react-international-phone/-/react-international-phone-4.5.0.tgz",
@ -17160,6 +17176,12 @@
"scheduler": "^0.23.2"
}
},
"react-hook-form": {
"version": "7.62.0",
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.62.0.tgz",
"integrity": "sha512-7KWFejc98xqG/F4bAxpL41NB3o1nnvQO1RWZT3TqRZYL8RryQETGfEdVnJN2fy1crCiBLLjkRBVK05j24FxJGA==",
"requires": {}
},
"react-international-phone": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/react-international-phone/-/react-international-phone-4.5.0.tgz",

View File

@ -35,19 +35,20 @@
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18",
"react-hook-form": "^7.62.0",
"react-international-phone": "^4.5.0",
"react-quill": "^2.0.0",
"react-tooltip": "^5.28.0"
},
"devDependencies": {
"@testing-library/react": "^13.4.0",
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^14.4.3",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"autoprefixer": "^10.4.20",
"eslint": "^8",
"eslint-config-next": "14.2.11",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"postcss": "^8.4.47",
"tailwindcss": "^3.4.14"
}

View File

@ -1,6 +1,6 @@
'use client';
import React, { useState, useEffect } from 'react';
import SelectChoice from '@/components/SelectChoice';
import SelectChoice from '@/components/Form/SelectChoice';
import AcademicResults from '@/components/Grades/AcademicResults';
import Attendance from '@/components/Grades/Attendance';
import Remarks from '@/components/Grades/Remarks';
@ -9,7 +9,7 @@ import Homeworks from '@/components/Grades/Homeworks';
import SpecificEvaluations from '@/components/Grades/SpecificEvaluations';
import Orientation from '@/components/Grades/Orientation';
import GradesStatsCircle from '@/components/Grades/GradesStatsCircle';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import logger from '@/utils/logger';
import {
FE_ADMIN_GRADES_STUDENT_COMPETENCIES_URL,
@ -29,7 +29,7 @@ import { useClasses } from '@/context/ClassesContext';
import { Award, FileText } from 'lucide-react';
import SectionHeader from '@/components/SectionHeader';
import GradesDomainBarChart from '@/components/Grades/GradesDomainBarChart';
import InputText from '@/components/InputText';
import InputText from '@/components/Form/InputText';
import dayjs from 'dayjs';
import { useCsrfToken } from '@/context/CsrfContext';

View File

@ -2,7 +2,7 @@
import React, { useState, useEffect } from 'react';
import { useSearchParams, useRouter } from 'next/navigation';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import GradeView from '@/components/Grades/GradeView';
import {
fetchStudentCompetencies,

View File

@ -2,8 +2,8 @@
import React, { useState, useEffect } from 'react';
import Tab from '@/components/Tab';
import TabContent from '@/components/TabContent';
import Button from '@/components/Button';
import InputText from '@/components/InputText';
import Button from '@/components/Form/Button';
import InputText from '@/components/Form/InputText';
import CheckBox from '@/components/CheckBox'; // Import du composant CheckBox
import logger from '@/utils/logger';
import {

View File

@ -8,8 +8,8 @@ import { fetchClasse } from '@/app/actions/schoolAction';
import { useSearchParams } from 'next/navigation';
import logger from '@/utils/logger';
import { useClasses } from '@/context/ClassesContext';
import Button from '@/components/Button';
import SelectChoice from '@/components/SelectChoice';
import Button from '@/components/Form/Button';
import SelectChoice from '@/components/Form/SelectChoice';
import CheckBox from '@/components/CheckBox';
import {
fetchAbsences,

View File

@ -2,17 +2,17 @@
import React, { useState, useRef, useEffect } from 'react';
import { User, Mail } from 'lucide-react';
import InputTextIcon from '@/components/InputTextIcon';
import ToggleSwitch from '@/components/ToggleSwitch';
import Button from '@/components/Button';
import InputTextIcon from '@/components/Form/InputTextIcon';
import ToggleSwitch from '@/components/Form/ToggleSwitch';
import Button from '@/components/Form/Button';
import Table from '@/components/Table';
import FeesSection from '@/components/Structure/Tarification/FeesSection';
import DiscountsSection from '@/components/Structure/Tarification/DiscountsSection';
import SectionTitle from '@/components/SectionTitle';
import InputPhone from '@/components/InputPhone';
import InputPhone from '@/components/Form/InputPhone';
import CheckBox from '@/components/CheckBox';
import RadioList from '@/components/RadioList';
import SelectChoice from '@/components/SelectChoice';
import RadioList from '@/components/Form/RadioList';
import SelectChoice from '@/components/Form/SelectChoice';
import Loader from '@/components/Loader';
import { getCurrentSchoolYear, getNextSchoolYear } from '@/utils/Date';
import logger from '@/utils/logger';

View File

@ -40,8 +40,8 @@ import {
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import { useCsrfToken } from '@/context/CsrfContext';
import logger from '@/utils/logger';
import { PhoneLabel } from '@/components/PhoneLabel';
import FileUpload from '@/components/FileUpload';
import { PhoneLabel } from '@/components/Form/PhoneLabel';
import FileUpload from '@/components/Form/FileUpload';
import FilesModal from '@/components/Inscription/FilesModal';
import { getCurrentSchoolYear, getNextSchoolYear } from '@/utils/Date';
@ -250,7 +250,12 @@ export default function Page({ params: { locale } }) {
}, 500); // Debounce la recherche
return () => clearTimeout(timeoutId);
}
}, [searchTerm, selectedEstablishmentId, currentSchoolYearPage, itemsPerPage]);
}, [
searchTerm,
selectedEstablishmentId,
currentSchoolYearPage,
itemsPerPage,
]);
/**
* UseEffect to update page count of tab

View File

@ -1,8 +1,9 @@
'use client';
import { useTranslations } from 'next-intl';
import React from 'react';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import Logo from '@/components/Logo'; // Import du composant Logo
import FormRenderer from '@/components/Form/FormRenderer';
export default function Home() {
const t = useTranslations('homePage');
@ -13,6 +14,7 @@ export default function Home() {
<h1 className="text-4xl font-bold mb-4">{t('welcomeParents')}</h1>
<p className="text-lg mb-8">{t('pleaseLogin')}</p>
<Button text={t('loginButton')} primary href="/users/login" />
<FormRenderer />
</div>
);
}

View File

@ -11,7 +11,7 @@ import {
CalendarDays,
} from 'lucide-react';
import StatusLabel from '@/components/StatusLabel';
import FileUpload from '@/components/FileUpload';
import FileUpload from '@/components/Form/FileUpload';
import { FE_PARENTS_EDIT_SUBSCRIPTION_URL } from '@/utils/Url';
import {
fetchChildren,

View File

@ -1,7 +1,7 @@
'use client';
import React, { useState } from 'react';
import Button from '@/components/Button';
import InputText from '@/components/InputText';
import Button from '@/components/Form/Button';
import InputText from '@/components/Form/InputText';
import logger from '@/utils/logger';
import { useNotification } from '@/context/NotificationContext';

View File

@ -3,9 +3,9 @@ import React, { useState } from 'react';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import Logo from '@/components/Logo';
import { useRouter } from 'next/navigation';
import InputTextIcon from '@/components/InputTextIcon';
import InputTextIcon from '@/components/Form/InputTextIcon';
import Loader from '@/components/Loader'; // Importez le composant Loader
import Button from '@/components/Button'; // Importez le composant Button
import Button from '@/components/Form/Button'; // Importez le composant Button
import { User, KeySquare } from 'lucide-react'; // Importez directement les icônes nécessaires
import { FE_USERS_NEW_PASSWORD_URL, getRedirectUrlFromRole } from '@/utils/Url';
import { login } from '@/app/actions/authAction';
@ -35,11 +35,7 @@ export default function Page() {
logger.debug('Sign In Result', result);
if (result.error) {
showNotification(
result.error,
'error',
'Erreur'
);
showNotification(result.error, 'error', 'Erreur');
setIsLoading(false);
} else {
// On initialise le contexte establishement avec la session
@ -50,11 +46,7 @@ export default function Page() {
if (url) {
router.push(url);
} else {
showNotification(
'Type de rôle non géré',
'error',
'Erreur'
);
showNotification('Type de rôle non géré', 'error', 'Erreur');
}
});
setIsLoading(false);

View File

@ -3,9 +3,9 @@
import React, { useState } from 'react';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import Logo from '@/components/Logo';
import InputTextIcon from '@/components/InputTextIcon';
import InputTextIcon from '@/components/Form/InputTextIcon';
import Loader from '@/components/Loader';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import { User } from 'lucide-react';
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
import { useCsrfToken } from '@/context/CsrfContext';
@ -25,25 +25,13 @@ export default function Page() {
.then((data) => {
logger.debug('Success:', data);
if (data.message !== '') {
showNotification(
data.message,
'success',
'Succès'
);
showNotification(data.message, 'success', 'Succès');
router.push(`${FE_USERS_LOGIN_URL}`);
} else {
if (data.errorMessage) {
showNotification(
data.errorMessage,
'error',
'Erreur'
);
showNotification(data.errorMessage, 'error', 'Erreur');
} else if (data.errorFields) {
showNotification(
data.errorFields.email,
'error',
'Erreur'
);
showNotification(data.errorFields.email, 'error', 'Erreur');
}
}
setIsLoading(false);

View File

@ -5,9 +5,9 @@ import React, { useState, useEffect } from 'react';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import Logo from '@/components/Logo';
import { useSearchParams, useRouter } from 'next/navigation';
import InputTextIcon from '@/components/InputTextIcon';
import InputTextIcon from '@/components/Form/InputTextIcon';
import Loader from '@/components/Loader';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
import { KeySquare } from 'lucide-react';
import { useCsrfToken } from '@/context/CsrfContext';
@ -33,21 +33,12 @@ export default function Page() {
resetPassword(uuid, data, csrfToken)
.then((data) => {
if (data.message !== '') {
logger.debug('Success:', data);
showNotification(
data.message,
'success',
'Succès'
);
showNotification(data.message, 'success', 'Succès');
router.push(`${FE_USERS_LOGIN_URL}`);
} else {
if (data.errorMessage) {
showNotification(
data.errorMessage,
'error',
'Erreur'
);
showNotification(data.errorMessage, 'error', 'Erreur');
} else if (data.errorFields) {
showNotification(
data.errorFields.password1 || data.errorFields.password2,

View File

@ -4,9 +4,9 @@ import React, { useState, useEffect } from 'react';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import Logo from '@/components/Logo';
import { useSearchParams, useRouter } from 'next/navigation';
import InputTextIcon from '@/components/InputTextIcon';
import InputTextIcon from '@/components/Form/InputTextIcon';
import Loader from '@/components/Loader';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import { User, KeySquare } from 'lucide-react';
import { FE_USERS_LOGIN_URL } from '@/utils/Url';
import { useCsrfToken } from '@/context/CsrfContext';
@ -36,22 +36,16 @@ export default function Page() {
.then((data) => {
logger.debug('Success:', data);
if (data.message !== '') {
showNotification(
data.message,
'success',
'Succès'
);
showNotification(data.message, 'success', 'Succès');
router.push(`${FE_USERS_LOGIN_URL}`);
} else {
if (data.errorMessage) {
showNotification(
data.errorMessage,
'error',
'Erreur'
);
showNotification(data.errorMessage, 'error', 'Erreur');
} else if (data.errorFields) {
showNotification(
data.errorFields.email || data.errorFields.password1 || data.errorFields.password2,
data.errorFields.email ||
data.errorFields.password1 ||
data.errorFields.password2,
'error',
'Erreur'
);

View File

@ -9,10 +9,10 @@ import { useEstablishment } from '@/context/EstablishmentContext';
import AlertMessage from '@/components/AlertMessage';
import RecipientInput from '@/components/RecipientInput';
import { useRouter } from 'next/navigation'; // Ajoute cette ligne
import WisiwigTextArea from '@/components/WisiwigTextArea';
import WisiwigTextArea from '@/components/Form/WisiwigTextArea';
import logger from '@/utils/logger';
import InputText from '@/components/InputText';
import Button from '@/components/Button';
import InputText from '@/components/Form/InputText';
import Button from '@/components/Form/Button';
export default function EmailSender({ csrfToken }) {
const [recipients, setRecipients] = useState([]);

View File

@ -0,0 +1,194 @@
import logger from '@/utils/logger';
import { useForm, Controller } from 'react-hook-form';
import SelectChoice from './SelectChoice';
import InputTextIcon from './InputTextIcon';
import * as LucideIcons from 'lucide-react';
import Button from './Button';
import DjangoCSRFToken from '../DjangoCSRFToken';
import WisiwigTextArea from './WisiwigTextArea';
/*
* Récupère une icône Lucide par son nom.
*/
export function getIcon(name) {
if (Object.keys(LucideIcons).includes(name)) {
const Icon = LucideIcons[name];
return Icon ?? null;
} else {
return null;
}
}
const formConfigTest = {
id: 0,
title: 'Mon formulaire dynamique',
submitLabel: 'Envoyer',
fields: [
{ id: 'name', label: 'Nom', type: 'text', required: true },
{ id: 'email', label: 'Email', type: 'email' },
{
id: 'email2',
label: 'Email',
type: 'text',
icon: 'Mail',
},
{
id: 'role',
label: 'Rôle',
type: 'select',
options: ['Admin', 'Utilisateur', 'Invité'],
required: true,
},
{
type: 'paragraph',
text: "Bonjour, Bienvenue dans ce formulaire d'inscription haha",
},
{
id: 'birthdate',
label: 'Date de naissance',
type: 'date',
icon: 'Calendar',
},
{
id: 'textarea',
label: 'toto',
type: 'textarea',
},
],
};
export default function FormRenderer({
formConfig = formConfigTest,
csrfToken,
}) {
const {
handleSubmit,
control,
formState: { errors },
reset,
} = useForm();
const onSubmit = (data) => {
logger.debug('=== DÉBUT onSubmit ===');
logger.debug('Réponses :', data);
const formattedData = {
//TODO: idDossierInscriptions: 123,
formId: formConfig.id,
responses: { ...data },
};
//TODO: ENVOYER LES DONNÉES AU BACKEND
alert('Données reçues : ' + JSON.stringify(formattedData, null, 2));
reset(); // Réinitialiser le formulaire après soumission
logger.debug('=== FIN onSubmit ===');
};
const onError = (errors) => {
logger.error('=== ERREURS DE VALIDATION ===');
logger.error('Erreurs :', errors);
alert('Erreurs de validation : ' + JSON.stringify(errors, null, 2));
};
return (
<form
onSubmit={handleSubmit(onSubmit, onError)}
className="max-w-md mx-auto"
>
{csrfToken ? <DjangoCSRFToken csrfToken={csrfToken} /> : null}
<h2 className="text-2xl font-bold text-center mb-4">
{formConfig.title}
</h2>
{formConfig.fields.map((field) => (
<div key={field.id} className="flex flex-col mt-4">
{field.type === 'paragraph' && <p>{field.text}</p>}
{(field.type === 'text' ||
field.type === 'email' ||
field.type === 'date') && (
<Controller
name={field.id}
control={control}
rules={{ required: field.required }}
render={({ field: { onChange, value, name } }) => (
<InputTextIcon
label={field.label}
required={field.required}
IconItem={field.icon ? getIcon(field.icon) : null}
type={field.type}
name={name}
value={value || ''}
onChange={onChange}
errorMsg={
errors[field.id]
? field.required
? `${field.label} est requis`
: 'Champ invalide'
: ''
}
/>
)}
/>
)}
{field.type === 'select' && (
<Controller
name={field.id}
control={control}
rules={{ required: field.required }}
render={({ field: { onChange, value, name } }) => (
<SelectChoice
label={field.label}
required={field.required}
name={name}
selected={value || ''}
callback={onChange}
choices={field.options.map((e) => ({ label: e, value: e }))}
placeHolder={`Sélectionner ${field.label.toLowerCase()}`}
errorMsg={
errors[field.id]
? field.required
? `${field.label} est requis`
: 'Champ invalide'
: ''
}
/>
)}
/>
)}
{field.type === 'textarea' && (
<Controller
name={field.id}
control={control}
rules={{ required: field.required }}
render={({ field: { onChange, value } }) => (
<WisiwigTextArea
label={field.label}
placeholder={field.placeholder}
value={value || ''}
onChange={onChange}
required={field.required}
errorMsg={
errors[field.id]
? field.required
? `${field.label} est requis`
: 'Champ invalide'
: ''
}
/>
)}
/>
)}
</div>
))}
<div className="form-group-submit mt-4">
<Button
type="submit"
primary
text={formConfig.submitLabel ? formConfig.submitLabel : 'Envoyer'}
className="mb-1 px-4 py-2 rounded-md shadow bg-emerald-500 text-white hover:bg-emerald-600 w-full"
/>
</div>
</form>
);
}

View File

@ -1,3 +1,5 @@
import React from 'react';
export default function InputTextIcon({
name,
type,
@ -31,9 +33,11 @@ export default function InputTextIcon({
!enable ? 'bg-gray-100 cursor-not-allowed' : ''
}`}
>
{IconItem ? (
<span className="inline-flex min-h-9 items-center px-3 rounded-l-md bg-gray-50 text-gray-500 text-sm">
{IconItem && <IconItem />}
<IconItem />
</span>
) : null}
<input
type={type}
id={name}

View File

@ -4,10 +4,10 @@ import 'react-quill/dist/quill.snow.css';
const ReactQuill = dynamic(() => import('react-quill'), { ssr: false });
export default function WisiwigTextArea({
label = 'Mail',
label = 'Zone de Texte',
value,
onChange,
placeholder = 'Ecrivez votre mail ici...',
placeholder = 'Ecrivez votre texte ici...',
className = 'h-64',
required = false,
errorMsg,

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { Trash2 } from 'lucide-react';
import ToggleSwitch from '@/components/ToggleSwitch';
import Button from '@/components/Button';
import ToggleSwitch from '@/components/Form/ToggleSwitch';
import Button from '@/components/Form/Button';
import Popup from '@/components/Popup';
import { useNotification } from '@/context/NotificationContext';

View File

@ -1,6 +1,6 @@
import React, { useState, useMemo, useEffect } from 'react';
import { BookOpen, CheckCircle, AlertCircle, Clock } from 'lucide-react';
import RadioList from '@/components/RadioList';
import RadioList from '@/components/Form/RadioList';
const LEVELS = [
{ value: 0, label: 'Non évalué' },

View File

@ -1,6 +1,6 @@
import React, { useState } from 'react';
import Table from '@/components/Table';
import FileUpload from '@/components/FileUpload';
import FileUpload from '@/components/Form/FileUpload';
import { Upload, Eye, Trash2, FileText } from 'lucide-react';
import { BASE_URL } from '@/utils/Url';
import Popup from '@/components/Popup';

View File

@ -1,6 +1,6 @@
// Import des dépendances nécessaires
import React, { useState, useEffect } from 'react';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import DjangoCSRFToken from '@/components/DjangoCSRFToken';
import {
fetchSchoolFileTemplatesFromRegistrationFiles,
@ -220,9 +220,7 @@ export default function InscriptionFormShared({
.then((data) => {
setProfiles(data);
})
.catch((error) =>
logger.error('Error fetching profiles : ', error)
);
.catch((error) => logger.error('Error fetching profiles : ', error));
if (selectedEstablishmentId) {
// Fetch data for registration payment modes

View File

@ -1,6 +1,6 @@
import React, { useEffect } from 'react';
import SelectChoice from '@/components/SelectChoice';
import RadioList from '@/components/RadioList';
import SelectChoice from '@/components/Form/SelectChoice';
import RadioList from '@/components/Form/RadioList';
import logger from '@/utils/logger';
export default function PaymentMethodSelector({

View File

@ -1,5 +1,5 @@
import InputText from '@/components/InputText';
import InputPhone from '@/components/InputPhone';
import InputText from '@/components/Form/InputText';
import InputPhone from '@/components/Form/InputPhone';
import React, { useEffect } from 'react';
import { useTranslations } from 'next-intl';
import { Trash2, Plus, Users } from 'lucide-react';

View File

@ -1,4 +1,4 @@
import InputText from '@/components/InputText';
import InputText from '@/components/Form/InputText';
import React, { useEffect } from 'react';
import { Trash2, Plus, Users } from 'lucide-react';
import SectionHeader from '@/components/SectionHeader';

View File

@ -1,12 +1,12 @@
import React, { useState, useEffect } from 'react';
import InputText from '@/components/InputText';
import SelectChoice from '@/components/SelectChoice';
import InputText from '@/components/Form/InputText';
import SelectChoice from '@/components/Form/SelectChoice';
import Loader from '@/components/Loader';
import { fetchRegisterForm } from '@/app/actions/subscriptionAction';
import logger from '@/utils/logger';
import SectionHeader from '@/components/SectionHeader';
import { User } from 'lucide-react';
import FileUpload from '@/components/FileUpload';
import FileUpload from '@/components/Form/FileUpload';
import { BASE_URL } from '@/utils/Url';
import { levels, genders } from '@/utils/constants';
@ -112,13 +112,10 @@ export default function StudentInfoForm({
(field === 'birth_place' &&
(!formData.birth_place || formData.birth_place.trim() === '')) ||
(field === 'birth_postal_code' &&
(
!formData.birth_postal_code ||
(!formData.birth_postal_code ||
String(formData.birth_postal_code).trim() === '' ||
isNaN(Number(formData.birth_postal_code)) ||
!Number.isInteger(Number(formData.birth_postal_code))
)
) ||
!Number.isInteger(Number(formData.birth_postal_code)))) ||
(field === 'address' &&
(!formData.address || formData.address.trim() === '')) ||
(field === 'attending_physician' &&

View File

@ -1,7 +1,7 @@
'use client';
import React, { useState, useEffect } from 'react';
import ToggleSwitch from '@/components/ToggleSwitch';
import SelectChoice from '@/components/SelectChoice';
import ToggleSwitch from '@/components/Form/ToggleSwitch';
import SelectChoice from '@/components/Form/SelectChoice';
import { BASE_URL } from '@/utils/Url';
import {
fetchSchoolFileTemplatesFromRegistrationFiles,
@ -10,7 +10,7 @@ import {
import logger from '@/utils/logger';
import { School, CheckCircle, Hourglass, FileText } from 'lucide-react';
import SectionHeader from '@/components/SectionHeader';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
export default function ValidateSubscription({
studentId,

View File

@ -2,9 +2,9 @@ import React, { useState, useRef, useCallback } from 'react';
import TreeView from '@/components/Structure/Competencies/TreeView';
import SectionHeader from '@/components/SectionHeader';
import { Award, CheckCircle } from 'lucide-react';
import SelectChoice from '@/components/SelectChoice';
import SelectChoice from '@/components/Form/SelectChoice';
import CheckBox from '@/components/CheckBox';
import Button from '@/components/Button';
import Button from '@/components/Form/Button';
import { useEstablishment } from '@/context/EstablishmentContext';
import {
fetchEstablishmentCompetencies,

View File

@ -2,10 +2,10 @@ import { Trash2, Edit3, ZoomIn, Users, Check, X, Hand } from 'lucide-react';
import React, { useState, useEffect } from 'react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
import InputText from '@/components/InputText';
import SelectChoice from '@/components/SelectChoice';
import InputText from '@/components/Form/InputText';
import SelectChoice from '@/components/Form/SelectChoice';
import TeacherItem from '@/components/Structure/Configuration/TeacherItem';
import MultiSelect from '@/components/MultiSelect';
import MultiSelect from '@/components/Form/MultiSelect';
import LevelLabel from '@/components/CustomLabels/LevelLabel';
import { DndProvider, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';

View File

@ -2,7 +2,7 @@ import { Trash2, Edit3, Check, X, BookOpen } from 'lucide-react';
import { useState } from 'react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
import InputTextWithColorIcon from '@/components/InputTextWithColorIcon';
import InputTextWithColorIcon from '@/components/Form/InputTextWithColorIcon';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';

View File

@ -2,11 +2,11 @@ import React, { useState, useEffect } from 'react';
import { Edit3, Trash2, GraduationCap, Check, X, Hand } from 'lucide-react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
import ToggleSwitch from '@/components/ToggleSwitch';
import ToggleSwitch from '@/components/Form/ToggleSwitch';
import { useCsrfToken } from '@/context/CsrfContext';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import InputText from '@/components/InputText';
import InputText from '@/components/Form/InputText';
import SpecialityItem from '@/components/Structure/Configuration/SpecialityItem';
import TeacherItem from './TeacherItem';
import logger from '@/utils/logger';

View File

@ -7,7 +7,7 @@ import {
} from '@/app/actions/registerFileGroupAction';
import { DocusealBuilder } from '@docuseal/react';
import logger from '@/utils/logger';
import MultiSelect from '@/components/MultiSelect'; // Import du composant MultiSelect
import MultiSelect from '@/components/Form/MultiSelect'; // Import du composant MultiSelect
import { useCsrfToken } from '@/context/CsrfContext';
import { useEstablishment } from '@/context/EstablishmentContext';
import Popup from '@/components/Popup';
@ -121,7 +121,13 @@ export default function FileUploadDocuSeal({
guardianDetails.forEach((guardian, index) => {
logger.debug('creation du clone avec required : ', is_required);
cloneTemplate(templateMaster?.id, guardian.email, is_required, selectedEstablishmentId, apiDocuseal)
cloneTemplate(
templateMaster?.id,
guardian.email,
is_required,
selectedEstablishmentId,
apiDocuseal
)
.then((clonedDocument) => {
// Sauvegarde des schoolFileTemplates clonés dans la base de données
const data = {

View File

@ -1,14 +1,14 @@
import React, { useState } from 'react';
import { Plus, Edit3, Trash2, Check, X, FileText } from 'lucide-react';
import Table from '@/components/Table';
import InputText from '@/components/InputText';
import MultiSelect from '@/components/MultiSelect';
import InputText from '@/components/Form/InputText';
import MultiSelect from '@/components/Form/MultiSelect';
import Popup from '@/components/Popup';
import logger from '@/utils/logger';
import { createRegistrationParentFileTemplate } from '@/app/actions/registerFileGroupAction';
import { useCsrfToken } from '@/context/CsrfContext';
import SectionHeader from '@/components/SectionHeader';
import ToggleSwitch from '@/components/ToggleSwitch';
import ToggleSwitch from '@/components/Form/ToggleSwitch';
import { useNotification } from '@/context/NotificationContext';
import AlertMessage from '@/components/AlertMessage';

View File

@ -3,7 +3,7 @@ import { Trash2, Edit3, Check, X, Percent, EuroIcon, Tag } from 'lucide-react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
import CheckBox from '@/components/CheckBox';
import InputText from '@/components/InputText';
import InputText from '@/components/Form/InputText';
import logger from '@/utils/logger';
import SectionHeader from '@/components/SectionHeader';
import { useEstablishment } from '@/context/EstablishmentContext';

View File

@ -3,7 +3,7 @@ import { Trash2, Edit3, Check, X, EyeOff, Eye, CreditCard } from 'lucide-react';
import Table from '@/components/Table';
import Popup from '@/components/Popup';
import CheckBox from '@/components/CheckBox';
import InputText from '@/components/InputText';
import InputText from '@/components/Form/InputText';
import logger from '@/utils/logger';
import SectionHeader from '@/components/SectionHeader';
import { useEstablishment } from '@/context/EstablishmentContext';

View File

@ -36,20 +36,6 @@ services:
- database
command: python start.py
backend-test:
build:
context: ./Back-End
volumes:
- ./Back-End:/Back-End
env_file: "./conf/backend.env"
links:
- "database:database"
- "redis:redis"
depends_on:
- redis
- database
command: python manage.py test
volumes:
postgres-data:
redis-data: