diff --git a/server/__tests__/fileUpload.test.ts b/server/__tests__/fileUpload.test.ts
index 6bf0dd4fa..f4cc3070f 100644
--- a/server/__tests__/fileUpload.test.ts
+++ b/server/__tests__/fileUpload.test.ts
@@ -3,6 +3,7 @@ import fs from 'fs';
import path from 'path';
import { uploadFiles } from '../controllers/filesController';
+import type { User } from '../entities/user';
import { AwsS3 } from '../fileUpload/s3';
import { AppError } from '../middlewares/handleErrors';
import { appDataSource, fakeUser } from './mock';
@@ -12,9 +13,6 @@ beforeAll(() => {
});
beforeEach(() => {});
afterAll(() => {
- const directory = path.join(__dirname, '../fileUpload/images/1/');
- // cleqning dev images files
- fs.rmSync(directory, { recursive: true, force: true });
return appDataSource.destroy();
});
@@ -29,7 +27,7 @@ describe('Upload files', () => {
});
const mockRequest: Partial = {
- user: fakeUser,
+ user: fakeUser as User,
files: [
{
buffer: dummyPdf,
@@ -54,7 +52,7 @@ describe('Upload files', () => {
});
test('Should throw - files are missing -', async () => {
const mockRequest: Partial = {
- user: fakeUser,
+ user: fakeUser as User,
files: [],
};
@@ -85,7 +83,7 @@ describe('Upload files', () => {
});
const mockRequest: Partial = {
- user: fakeUser,
+ user: fakeUser as User,
files: [
{
buffer: dummyPdf,
@@ -114,7 +112,7 @@ describe('Upload files', () => {
});
const mockRequest: Partial = {
- user: fakeUser,
+ user: fakeUser as User,
files: [
{
buffer: dummyPdf,
diff --git a/server/__tests__/mock.ts b/server/__tests__/mock.ts
index d6c19bf5a..193f68547 100644
--- a/server/__tests__/mock.ts
+++ b/server/__tests__/mock.ts
@@ -3,7 +3,6 @@ import supertest from 'supertest';
import { DataSource } from 'typeorm';
import { getApp } from '../app';
-import type { User } from '../entities/user';
export const appDataSource = new DataSource({
type: 'sqlite',
@@ -54,6 +53,6 @@ export const fakeUser = {
firstLogin: 3,
type: 0,
villageId: 1,
- country: { id: 0, isoCode: 'FR', name: 'France' },
+ country: { isoCode: 'FR', name: 'France' },
position: { lat: 48.8863442, lng: 2.380321 },
-} as unknown as User;
+};
diff --git a/server/app-staging.ts b/server/app-staging.ts
index a1ccf9fcb..95ffc3161 100644
--- a/server/app-staging.ts
+++ b/server/app-staging.ts
@@ -5,7 +5,7 @@ import morgan from 'morgan';
import next from 'next';
import path from 'path';
-import { UserType } from '../types/user.type';
+import { UserType } from './entities/user';
import { handleErrors } from './middlewares/handleErrors';
import { removeTrailingSlash } from './middlewares/trailingSlash';
diff --git a/server/app.ts b/server/app.ts
index cebd7d037..92779842b 100644
--- a/server/app.ts
+++ b/server/app.ts
@@ -8,9 +8,9 @@ import morgan from 'morgan';
import next from 'next';
import path from 'path';
-import { UserType } from '../types/user.type';
import { authRouter } from './authentication';
import { controllerRouter } from './controllers';
+import { UserType } from './entities/user';
import { getH5pRouter } from './h5p';
import { authenticate } from './middlewares/authenticate';
import { crsfProtection } from './middlewares/csrfCheck';
diff --git a/server/authentication/login.ts b/server/authentication/login.ts
index 3acf07c1e..b975168fe 100644
--- a/server/authentication/login.ts
+++ b/server/authentication/login.ts
@@ -2,8 +2,7 @@ import type { JSONSchemaType } from 'ajv';
import * as argon2 from 'argon2';
import type { NextFunction, Request, Response } from 'express';
-import { UserType } from '../../types/user.type';
-import { User } from '../entities/user';
+import { User, UserType } from '../entities/user';
import { UserToStudent } from '../entities/userToStudent';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { AppDataSource } from '../utils/data-source';
diff --git a/server/controllers/activity.ts b/server/controllers/activity.ts
index 6056f96e2..92aa11dec 100644
--- a/server/controllers/activity.ts
+++ b/server/controllers/activity.ts
@@ -7,11 +7,11 @@ import { EPhase1Steps, ActivityStatus, ActivityType, EPhase2Steps, EPhase3Steps
import type { GameData, GamesData } from '../../types/game.type';
import type { StoriesData, StoryElement } from '../../types/story.type';
import { ImageType } from '../../types/story.type';
-import { UserType } from '../../types/user.type';
-import { VillagePhase } from '../../types/village.type';
import { Activity } from '../entities/activity';
import { Game } from '../entities/game';
import { Image } from '../entities/image';
+import { UserType } from '../entities/user';
+import { VillagePhase } from '../entities/village';
import { getActivities, getActivitiesCommentCount } from '../manager/activity';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { getQueryString } from '../utils';
@@ -25,11 +25,17 @@ const activityController = new Controller('/activities');
// --- Get all activities. ---
activityController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Request, res: Response) => {
if (!req.user) throw new AppError('Forbidden', ErrorCode.UNKNOWN);
+
const activities = await getActivities({
limit: req.query.limit ? Number(getQueryString(req.query.limit)) || 200 : undefined,
page: req.query.page ? Number(getQueryString(req.query.page)) || 0 : undefined,
villageId: req.query.villageId ? Number(getQueryString(req.query.villageId)) || 0 : undefined,
- countries: req.query.countries as string[] | undefined,
+ countries:
+ req.query.countries !== undefined
+ ? req.query.countries.length === 0
+ ? []
+ : (getQueryString(req.query.countries) || '').split(',')
+ : undefined,
pelico: req.query.pelico ? req.query.pelico !== 'false' : undefined,
type: req.query.type ? (getQueryString(req.query.type) || '').split(',') : undefined,
subType: req.query.subType ? Number(getQueryString(req.query.subType)) || 0 : undefined,
diff --git a/server/controllers/analytic.ts b/server/controllers/analytic.ts
index bcad3b2f1..69835daeb 100644
--- a/server/controllers/analytic.ts
+++ b/server/controllers/analytic.ts
@@ -3,8 +3,8 @@ import useragent from 'express-useragent';
import { Between } from 'typeorm';
import type { AnalyticData, NavigationPerf, BrowserPerf } from '../../types/analytics.type';
-import { UserType } from '../../types/user.type';
import { AnalyticSession, AnalyticPageView, AnalyticPerformance } from '../entities/analytic';
+import { UserType } from '../entities/user';
import { AppError, ErrorCode, handleErrors } from '../middlewares/handleErrors';
import { generateTemporaryToken, getQueryString } from '../utils';
import { AppDataSource } from '../utils/data-source';
diff --git a/server/controllers/archive.ts b/server/controllers/archive.ts
index d6897f939..0bdaa1e5f 100644
--- a/server/controllers/archive.ts
+++ b/server/controllers/archive.ts
@@ -1,6 +1,6 @@
import type { Request, Response, NextFunction } from 'express';
-import { UserType } from '../../types/user.type';
+import { UserType } from '../entities/user';
import { streamFile } from '../fileUpload/streamFile';
import { Controller } from './controller';
diff --git a/server/controllers/audio.ts b/server/controllers/audio.ts
index 453be1bdb..3a186cdd7 100644
--- a/server/controllers/audio.ts
+++ b/server/controllers/audio.ts
@@ -3,7 +3,7 @@ import fs from 'fs-extra';
import path from 'path';
import { v4 as uuidv4 } from 'uuid';
-import { UserType } from '../../types/user.type';
+import { UserType } from '../entities/user';
import { deleteFile, uploadFile } from '../fileUpload';
import { streamFile } from '../fileUpload/streamFile';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
diff --git a/server/controllers/classroom.ts b/server/controllers/classroom.ts
index d0cbf9887..e78a00193 100644
--- a/server/controllers/classroom.ts
+++ b/server/controllers/classroom.ts
@@ -1,10 +1,8 @@
import type { JSONSchemaType } from 'ajv';
import type { NextFunction, Request, Response } from 'express';
-import { UserType } from '../../types/user.type';
import { Classroom } from '../entities/classroom';
-import { Country } from '../entities/country';
-import { User } from '../entities/user';
+import { User, UserType } from '../entities/user';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { AppDataSource } from '../utils/data-source';
import { ajv, sendInvalidDataError } from '../utils/jsonSchemaValidator';
@@ -26,7 +24,6 @@ classroomController.get({ path: '/:id', userType: UserType.TEACHER }, async (req
const classroom = await AppDataSource.getRepository(Classroom).findOne({
relations: {
user: true,
- country: true,
},
where: { user: { id } },
});
@@ -88,13 +85,12 @@ classroomController.post({ path: '', userType: UserType.TEACHER }, async (req: R
where: { user: { id: req.user.id } },
});
if (verification.length !== 0) return res.status(303).send('Classroom already exit');
- const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: data.countryCode } });
- const classroom = new Classroom();
- classroom.user.id = data.userId;
- classroom.country = countryFound;
- classroom.id = data.villageId;
- await AppDataSource.getRepository(Classroom).save(classroom);
+ const classroom = await AppDataSource.createQueryBuilder()
+ .insert()
+ .into(Classroom)
+ .values([{ user: { id: data.userId }, village: { id: data.villageId }, countryCode: data.countryCode }])
+ .execute();
res.json(classroom);
});
diff --git a/server/controllers/comment.ts b/server/controllers/comment.ts
index cf76d5cac..22e6d2732 100644
--- a/server/controllers/comment.ts
+++ b/server/controllers/comment.ts
@@ -1,9 +1,9 @@
import type { JSONSchemaType } from 'ajv';
import type { NextFunction, Request, Response } from 'express';
-import { UserType } from '../../types/user.type';
import { Activity } from '../entities/activity';
import { Comment } from '../entities/comment';
+import { UserType } from '../entities/user';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { AppDataSource } from '../utils/data-source';
import { ajv, sendInvalidDataError } from '../utils/jsonSchemaValidator';
diff --git a/server/controllers/controller.ts b/server/controllers/controller.ts
index 3c3400098..d04b5d067 100644
--- a/server/controllers/controller.ts
+++ b/server/controllers/controller.ts
@@ -4,7 +4,7 @@ import fs from 'fs-extra';
import multer from 'multer';
import path from 'path';
-import type { UserType } from '../../types/user.type';
+import type { UserType } from '../entities/user';
import { authenticate } from '../middlewares/authenticate';
import { handleErrors } from '../middlewares/handleErrors';
import { diskStorage } from '../middlewares/multer';
diff --git a/server/controllers/countries.ts b/server/controllers/countries.ts
index 452662c6e..e43a15174 100644
--- a/server/controllers/countries.ts
+++ b/server/controllers/countries.ts
@@ -1,13 +1,11 @@
-import { UserType } from '../../types/user.type';
-import { Country } from '../entities/country';
-import { AppDataSource } from '../utils/data-source';
+import { UserType } from '../entities/user';
+import { countries } from '../utils/iso-3166-countries-french';
import { Controller } from './controller';
const countryController = new Controller('/countries');
//--- Get all countries ---
countryController.get({ path: '', userType: UserType.TEACHER }, async (_req, res) => {
- const countries = await AppDataSource.getRepository(Country).find();
res.sendJSON(countries);
});
diff --git a/server/controllers/currencies.ts b/server/controllers/currencies.ts
index b29051995..5e07df679 100644
--- a/server/controllers/currencies.ts
+++ b/server/controllers/currencies.ts
@@ -1,4 +1,4 @@
-import { UserType } from '../../types/user.type';
+import { UserType } from '../entities/user';
import { currencies } from '../utils/iso-4217-currencies-french';
import { Controller } from './controller';
diff --git a/server/controllers/featureFlag.ts b/server/controllers/featureFlag.ts
index fe393123d..5a079d3d3 100644
--- a/server/controllers/featureFlag.ts
+++ b/server/controllers/featureFlag.ts
@@ -2,9 +2,8 @@ import type { NextFunction, Request, Response } from 'express';
import { In } from 'typeorm';
import type { FeatureFlagsNames } from '../../types/featureFlag.constant';
-import { UserType } from '../../types/user.type';
import { FeatureFlag } from '../entities/featureFlag';
-import { User } from '../entities/user';
+import { User, UserType } from '../entities/user';
import { AppDataSource } from '../utils/data-source';
import { Controller } from './controller';
diff --git a/server/controllers/game.ts b/server/controllers/game.ts
index bd9f6af5b..6f0f4ca34 100644
--- a/server/controllers/game.ts
+++ b/server/controllers/game.ts
@@ -1,9 +1,9 @@
import type { JSONSchemaType } from 'ajv';
import type { NextFunction, Request, Response } from 'express';
-import { UserType } from '../../types/user.type';
import { Game } from '../entities/game';
import { GameResponse } from '../entities/gameResponse';
+import { UserType } from '../entities/user';
import { getQueryString } from '../utils';
import { AppDataSource } from '../utils/data-source';
import { ajv, sendInvalidDataError } from '../utils/jsonSchemaValidator';
diff --git a/server/controllers/image.ts b/server/controllers/image.ts
index 8f9eafdda..b123c75c8 100644
--- a/server/controllers/image.ts
+++ b/server/controllers/image.ts
@@ -4,7 +4,7 @@ import path from 'path';
import sharp from 'sharp';
import { v4 as uuidv4 } from 'uuid';
-import { UserType } from '../../types/user.type';
+import { UserType } from '../entities/user';
import { deleteFile, uploadFile } from '../fileUpload';
import { streamFile } from '../fileUpload/streamFile';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
diff --git a/server/controllers/story.ts b/server/controllers/story.ts
index 65da7b86d..d9da026f2 100644
--- a/server/controllers/story.ts
+++ b/server/controllers/story.ts
@@ -1,8 +1,8 @@
import type { Request, Response, NextFunction } from 'express';
import { ImageType } from '../../types/story.type';
-import { UserType } from '../../types/user.type';
import { Image } from '../entities/image';
+import { UserType } from '../entities/user';
import { getQueryString } from '../utils';
import { AppDataSource } from '../utils/data-source';
import { Controller } from './controller';
diff --git a/server/controllers/student.ts b/server/controllers/student.ts
index 2569d10e7..d2c75a893 100644
--- a/server/controllers/student.ts
+++ b/server/controllers/student.ts
@@ -1,10 +1,8 @@
import type { JSONSchemaType } from 'ajv';
import type { NextFunction, Request, Response } from 'express';
-import { UserType } from '../../types/user.type';
-import { Classroom } from '../entities/classroom';
import { Student } from '../entities/student';
-import { User } from '../entities/user';
+import { User, UserType } from '../entities/user';
import { UserToStudent } from '../entities/userToStudent';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { getQueryString } from '../utils';
@@ -120,10 +118,7 @@ studentController.post({ path: '', userType: UserType.TEACHER }, async (req: Req
throw new AppError('Forbidden', ErrorCode.UNKNOWN);
}
const student = new Student();
- const classroomFound = await AppDataSource.getRepository(Classroom).findOne({ where: { id: data.classroomId } });
- if (classroomFound) {
- student.classroom = classroomFound;
- }
+ student.classroom = data.classroomId;
student.firstname = data.firstname ?? null;
student.lastname = data.lastname ?? null;
student.hashedCode = inviteCodeGenerator(10);
@@ -181,7 +176,7 @@ studentController.post({ path: '/link-student', userType: UserType.FAMILY }, asy
villageId: student.classroom.villageId,
hasStudentLinked: true,
firstLogin: student.classroom.village?.activePhase, //TEST THIS LINE
- country: student.classroom.country,
+ countryCode: student.classroom.countryCode as string,
})
.where('id = :id', { id: req.user.id })
.execute();
diff --git a/server/controllers/teacher.ts b/server/controllers/teacher.ts
index c313036d5..db09c9cae 100644
--- a/server/controllers/teacher.ts
+++ b/server/controllers/teacher.ts
@@ -1,7 +1,7 @@
import type { NextFunction, Request, Response } from 'express';
-import { UserType } from '../../types/user.type';
import { Activity } from '../entities/activity';
+import { UserType } from '../entities/user';
import { UserToStudent } from '../entities/userToStudent';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { AppDataSource } from '../utils/data-source';
diff --git a/server/controllers/user.ts b/server/controllers/user.ts
index b989078c3..e9b71c021 100644
--- a/server/controllers/user.ts
+++ b/server/controllers/user.ts
@@ -5,15 +5,13 @@ import type { FindOperator } from 'typeorm';
import { In, IsNull, LessThan } from 'typeorm';
import { ActivityStatus, ActivityType } from '../../types/activity.type';
-import { UserType } from '../../types/user.type';
import { getAccessToken } from '../authentication/lib/tokens';
import { Email, sendMail } from '../emails';
import { Activity } from '../entities/activity';
import { Classroom } from '../entities/classroom';
-import { Country } from '../entities/country';
import { FeatureFlag } from '../entities/featureFlag';
import { Student } from '../entities/student';
-import { User } from '../entities/user';
+import { User, UserType } from '../entities/user';
import { UserToStudent } from '../entities/userToStudent';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { generateTemporaryToken, valueOrDefault, isPasswordValid, getQueryString } from '../utils';
@@ -41,7 +39,6 @@ userController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Requ
// eslint-disable-next-line @typescript-eslint/no-explicit-any
{ villageId: IsNull(), type: LessThan(`${UserType.TEACHER}`) as FindOperator },
],
- relations: { country: true },
});
const ids = users.map((u) => u.id);
const mascottes = (
@@ -65,7 +62,6 @@ userController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Requ
users = await AppDataSource.getRepository(User).find({
relations: {
userToStudents: true,
- country: true,
},
});
const studentsToSchool = (
@@ -91,10 +87,9 @@ userController.get({ path: '', userType: UserType.OBSERVATOR }, async (req: Requ
});
// --- Get one user. ---
-userController.get({ path: '/:id', userType: UserType.TEACHER }, async (req: Request, res: Response, next: NextFunction) => {
+userController.get({ path: '/:id(\\d+)', userType: UserType.TEACHER }, async (req: Request, res: Response, next: NextFunction) => {
const id = parseInt(req.params.id, 10) || 0;
-
- const user = await AppDataSource.getRepository(User).findOne({ where: { id }, relations: { country: true } });
+ const user = await AppDataSource.getRepository(User).findOne({ where: { id } });
const isSelfProfile = req.user && req.user.id === id;
const isAdmin = req.user && req.user.type <= UserType.ADMIN;
if (user === null || (!isSelfProfile && !isAdmin)) {
@@ -134,8 +129,7 @@ userController.get({ path: '/pseudo/:pseudo' }, async (req: Request, res: Respon
res.sendJSON({ available: true });
}
res.sendJSON({
- relations: { country: true },
- available: (await AppDataSource.getRepository(User).count({ where: { pseudo }, relations: { country: true } })) === 0,
+ available: (await AppDataSource.getRepository(User).count({ where: { pseudo } })) === 0,
});
});
@@ -258,10 +252,7 @@ userController.post({ path: '' }, async (req: Request, res: Response) => {
user.hasAcceptedNewsletter = data.hasAcceptedNewsletter || false;
user.language = data.language || 'français';
user.hasStudentLinked = data.hasStudentLinked || false;
- const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: data.countryCode } });
- if (countryFound) {
- user.country = countryFound;
- }
+ user.countryCode = data.countryCode || '';
user.type = data.type || UserType.TEACHER || UserType.FAMILY;
if (user.type === UserType.TEACHER) {
user.isVerified = true;
@@ -311,8 +302,7 @@ type EditUserData = {
type?: UserType;
villageId?: number | null;
firstLogin?: number;
- positionLat?: number;
- positionLon?: number;
+ position?: { lat: number; lng: number };
isVerified?: boolean;
accountRegistration?: number;
hasAcceptedNewsletter?: boolean;
@@ -342,8 +332,16 @@ const EDIT_SCHEMA: JSONSchemaType = {
villageId: { type: 'number', nullable: true },
accountRegistration: { type: 'number', nullable: true },
firstLogin: { type: 'number', nullable: true },
- positionLat: { type: 'number', nullable: true },
- positionLon: { type: 'number', nullable: true },
+ position: {
+ type: 'object',
+ nullable: true,
+ properties: {
+ lat: { type: 'number', nullable: false },
+ lng: { type: 'number', nullable: false },
+ },
+ required: ['lat', 'lng'],
+ additionalProperties: false,
+ },
hasAcceptedNewsletter: { type: 'boolean', nullable: true },
isVerified: { type: 'boolean', nullable: true },
language: { type: 'string', nullable: true },
@@ -379,10 +377,7 @@ userController.put({ path: '/:id', userType: UserType.OBSERVATOR }, async (req:
user.postalCode = valueOrDefault(data.postalCode, user.postalCode);
user.level = valueOrDefault(data.level, user.level);
user.school = valueOrDefault(data.school, user.school);
- const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: data.countryCode } });
- if (countryFound) {
- user.country = countryFound;
- }
+ user.countryCode = valueOrDefault(data.countryCode, user.countryCode);
user.avatar = valueOrDefault(data.avatar, user.avatar) || null;
user.displayName = valueOrDefault(data.displayName, user.displayName) || null;
user.firstLogin = valueOrDefault(data.firstLogin, user.firstLogin);
@@ -390,35 +385,34 @@ userController.put({ path: '/:id', userType: UserType.OBSERVATOR }, async (req:
user.type = valueOrDefault(data.type, user.type);
user.villageId = valueOrDefault(data.villageId, user.villageId, true);
}
- if (data.positionLat && data.positionLon) {
- user.positionLat = data.positionLat;
- user.positionLon = data.positionLon;
+ if (data.position) {
+ user.position = data.position;
}
if (data.villageId) {
if (user.type === UserType.TEACHER) {
const classroom = await AppDataSource.getRepository(Classroom).findOne({ where: { user: { id: user.id } } });
- if (classroom) {
- const students = await AppDataSource.getRepository(Student).find({ where: { classroom: { id: classroom.id } } });
- const studentsId = students.map((student) => student.id);
+ if (!classroom) return;
- const families = await AppDataSource.getRepository(UserToStudent).find({
- where: { student: { id: In(studentsId) } },
- relations: ['user', 'student'],
- });
+ const students = await AppDataSource.getRepository(Student).find({ where: { classroom: { id: classroom.id } } });
+ const studentsId = students.map((student) => student.id);
- const familiesId = families.map((family) => family.user.id);
+ const families = await AppDataSource.getRepository(UserToStudent).find({
+ where: { student: { id: In(studentsId) } },
+ relations: ['user', 'student'],
+ });
- const promises = [];
+ const familiesId = families.map((family) => family.user.id);
- promises.push(
- AppDataSource.getRepository(Classroom).update({ user: { id: user.id } }, { villageId: data.villageId }),
- AppDataSource.getRepository(Activity).update({ userId: user.id }, { villageId: data.villageId }),
- AppDataSource.getRepository(User).update({ id: In(familiesId) }, { villageId: data.villageId }),
- );
+ const promises = [];
- await Promise.all(promises);
- }
+ promises.push(
+ AppDataSource.getRepository(Classroom).update({ user: { id: user.id } }, { villageId: data.villageId }),
+ AppDataSource.getRepository(Activity).update({ userId: user.id }, { villageId: data.villageId }),
+ AppDataSource.getRepository(User).update({ id: In(familiesId) }, { villageId: data.villageId }),
+ );
+
+ await Promise.all(promises);
}
}
user.hasAcceptedNewsletter = valueOrDefault(data.hasAcceptedNewsletter, user.hasAcceptedNewsletter);
@@ -843,7 +837,7 @@ userController.get({ path: '/get-classroom/:id', userType: UserType.OBSERVATOR }
firstname: student.firstname,
lastname: student.lastname,
classroom: {
- user: classroom.user.country?.isoCode,
+ user: classroom.user.countryCode,
},
},
});
@@ -900,7 +894,7 @@ userController.get({ path: '/:id/visibility-params/', userType: UserType.FAMILY
throw new AppError('Forbidden', ErrorCode.UNKNOWN);
}
const id = parseInt(req.params.id, 10) || 0;
- const user = await AppDataSource.getRepository(User).findOne({ where: { id }, relations: ['userToStudents', 'userToStudents.student', 'country'] });
+ const user = await AppDataSource.getRepository(User).findOne({ where: { id }, relations: ['userToStudents', 'userToStudents.student'] });
if (user && user.type === UserType.TEACHER) {
const classroom = [];
diff --git a/server/controllers/video.ts b/server/controllers/video.ts
index 38291ec2d..511743d75 100644
--- a/server/controllers/video.ts
+++ b/server/controllers/video.ts
@@ -1,4 +1,4 @@
-import { UserType } from '../../types/user.type';
+import { UserType } from '../entities/user';
import { Video } from '../entities/video';
import { deleteVideo, getVideoLink, uploadVideo, getPictureForVideo } from '../fileUpload';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
diff --git a/server/controllers/village.ts b/server/controllers/village.ts
index d9cfd6526..9361d954d 100644
--- a/server/controllers/village.ts
+++ b/server/controllers/village.ts
@@ -1,9 +1,7 @@
import type { JSONSchemaType } from 'ajv';
import type { NextFunction, Request, Response } from 'express';
-import { In } from 'typeorm';
-import { UserType } from '../../types/user.type';
-import { Country } from '../entities/country';
+import { UserType } from '../entities/user';
import { Village } from '../entities/village';
import { createVillagesFromPLM } from '../legacy-plm/api';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
@@ -16,7 +14,7 @@ const villageController = new Controller('/villages');
//--- Get all villages ---
villageController.get({ path: '', userType: UserType.OBSERVATOR }, async (_req: Request, res: Response) => {
- const villages = await AppDataSource.getRepository(Village).find({ relations: { countries: true } });
+ const villages = await AppDataSource.getRepository(Village).find();
res.sendJSON(villages);
});
@@ -27,8 +25,7 @@ villageController.get({ path: '/:id', userType: UserType.OBSERVATOR }, async (re
return;
}
const id = parseInt(req.params.id, 10) || 0;
- const village = await AppDataSource.getRepository(Village).findOne({ where: { id }, relations: { countries: true } });
-
+ const village = await AppDataSource.getRepository(Village).findOne({ where: { id } });
if (!village || (req.user.type === UserType.TEACHER && req.user.villageId !== village.id)) {
next();
return;
@@ -59,12 +56,7 @@ villageController.post({ path: '', userType: UserType.ADMIN }, async (req: Reque
}
const village = new Village();
village.name = data.name;
- const countries = await AppDataSource.getRepository(Country).find({
- where: { isoCode: In(data.countries) },
- });
- if (countries.length) {
- village.countries = countries;
- }
+ village.countryCodes = data.countries;
await AppDataSource.getRepository(Village).save(village);
res.sendJSON(village);
});
@@ -95,26 +87,17 @@ villageController.put({ path: '/:id', userType: UserType.ADMIN }, async (req: Re
return;
}
const id = parseInt(req.params.id, 10) || 0;
- const village = await AppDataSource.getRepository(Village).findOne({
- where: { id },
- relations: {
- countries: true,
- },
- });
+ const village = await AppDataSource.getRepository(Village).findOne({ where: { id } });
if (!village) {
next();
return;
}
+
village.name = valueOrDefault(data.name, village.name);
+ village.countryCodes = valueOrDefault(data.countries, village.countryCodes);
village.activePhase = valueOrDefault(data.activePhase, village.activePhase);
village.anthemId = valueOrDefault(data.anthemId, village.anthemId);
- const countries = await AppDataSource.getRepository(Country).find({
- where: { isoCode: In(data.countries ?? village.countries.map((country) => country.isoCode)) },
- });
- if (countries.length) {
- village.countries = countries;
- }
await AppDataSource.getRepository(Village).save(village);
res.sendJSON(village);
});
diff --git a/server/controllers/weather.ts b/server/controllers/weather.ts
index e64ccce95..64edbf8e7 100644
--- a/server/controllers/weather.ts
+++ b/server/controllers/weather.ts
@@ -1,8 +1,8 @@
/* eslint-disable camelcase */
import axios from 'axios';
-import { UserType } from '../../types/user.type';
import type { Weather } from '../../types/weather.type';
+import { UserType } from '../entities/user';
import { AppError, ErrorCode } from '../middlewares/handleErrors';
import { getQueryString, serializeToQueryUrl } from '../utils';
import { logger } from '../utils/logger';
diff --git a/server/entities/classroom.ts b/server/entities/classroom.ts
index e7c654560..83fa591d0 100644
--- a/server/entities/classroom.ts
+++ b/server/entities/classroom.ts
@@ -1,6 +1,7 @@
import { Entity, PrimaryGeneratedColumn, Column, OneToOne, OneToMany, ManyToOne, JoinColumn } from 'typeorm';
-import { Country } from './country';
+import type { Country } from '../../types/country.type';
+import { countriesMap } from '../utils/countries-map';
import { Student } from './student';
import { User } from './user';
import { Village } from './village';
@@ -19,8 +20,14 @@ export class Classroom {
@Column({ nullable: true, default: 0 })
public delayedDays: number;
- @ManyToOne(() => Country, (country: Country) => country)
- public country: Country | null;
+ @Column({ type: 'varchar', length: 2, nullable: true })
+ set countryCode(newCountryCode: string) {
+ this.country = countriesMap[newCountryCode] || countriesMap['FR'];
+ }
+ get countryCode() {
+ return this.country?.isoCode;
+ }
+ public country: Country;
@Column({
type: 'boolean',
diff --git a/server/entities/user.ts b/server/entities/user.ts
index b0c9e8f2c..f6c7c5376 100644
--- a/server/entities/user.ts
+++ b/server/entities/user.ts
@@ -1,8 +1,10 @@
import { Column, Entity, PrimaryGeneratedColumn, ManyToOne, JoinColumn, OneToMany, ManyToMany, JoinTable } from 'typeorm';
+import type { Country } from '../../types/country.type';
import { UserType } from '../../types/user.type';
+import type { User as UserInterface } from '../../types/user.type';
+import { countriesMap } from '../utils/countries-map';
import { Activity } from './activity';
-import { Country } from './country';
import { FeatureFlag } from './featureFlag';
import { Game } from './game';
import { GameResponse } from './gameResponse';
@@ -10,8 +12,10 @@ import { Image } from './image';
import { UserToStudent } from './userToStudent';
import { Village } from './village';
+export { UserType };
+
@Entity()
-export class User {
+export class User implements UserInterface {
@PrimaryGeneratedColumn()
public id: number;
@@ -78,14 +82,38 @@ export class User {
@Column({ nullable: true })
public villageId: number | null;
- @ManyToOne(() => Country, (country: Country) => country)
- public country: Country | null;
+ @Column({ type: 'varchar', length: 2, nullable: true })
+ set countryCode(newCountryCode: string) {
+ this.country = countriesMap[newCountryCode] || countriesMap['FR'];
+ }
+ get countryCode() {
+ return this.country?.isoCode;
+ }
+ public country: Country;
@Column({ type: 'decimal', precision: 11, scale: 8, nullable: false, default: 0 })
- public positionLat: number;
+ set positionLat(newLat: string) {
+ if (!this.position) {
+ this.position = { lat: 0, lng: 0 };
+ }
+ this.position.lat = parseFloat(newLat) || 0;
+ }
+ get positionLat() {
+ return `${this.position?.lat || 0}`;
+ }
@Column({ type: 'decimal', precision: 11, scale: 8, nullable: false, default: 0 })
- public positionLon: number;
+ set positionLon(newLon: string) {
+ if (!this.position) {
+ this.position = { lat: 0, lng: 0 };
+ }
+ this.position.lng = parseFloat(newLon) || 0;
+ }
+ get positionLon() {
+ return `${this.position?.lng || 0}`;
+ }
+
+ public position: { lat: number; lng: number };
public mascotteId?: number;
diff --git a/server/entities/userToStudent.ts b/server/entities/userToStudent.ts
index d9b693ee0..c5391ced6 100644
--- a/server/entities/userToStudent.ts
+++ b/server/entities/userToStudent.ts
@@ -53,7 +53,7 @@ export class UserToStudent {
remainingUserToStudent.student.classroom.village
) {
userToStudent.user.villageId = remainingUserToStudent.student.classroom.village.id;
- userToStudent.user.country = remainingUserToStudent.student.classroom.country;
+ userToStudent.user.countryCode = remainingUserToStudent.student.classroom.countryCode;
await entityManager.save(userToStudent.user);
}
}
diff --git a/server/entities/village.ts b/server/entities/village.ts
index 7d2154200..9081e9f51 100644
--- a/server/entities/village.ts
+++ b/server/entities/village.ts
@@ -1,10 +1,11 @@
-import { Column, Entity, JoinColumn, PrimaryGeneratedColumn, OneToMany, OneToOne, ManyToMany, JoinTable } from 'typeorm';
+import { Column, Entity, JoinColumn, PrimaryGeneratedColumn, OneToMany, OneToOne } from 'typeorm';
+import type { Country } from '../../types/country.type';
import type { Village as VillageInterface } from '../../types/village.type';
import { VillagePhase } from '../../types/village.type';
+import { countriesMap } from '../utils/countries-map';
import { Activity } from './activity';
import { Classroom } from './classroom';
-import { Country } from './country';
import { Game } from './game';
import { GameResponse } from './gameResponse';
import { Image } from './image';
@@ -23,8 +24,13 @@ export class Village implements VillageInterface {
@Column({ type: 'varchar', length: 80, nullable: false })
public name: string;
- @ManyToMany(() => Country)
- @JoinTable()
+ @Column('simple-array')
+ set countryCodes(newCountryCodes: string[]) {
+ this.countries = newCountryCodes.map((isoCode) => countriesMap[isoCode]).filter((c) => c !== undefined);
+ }
+ get countryCodes() {
+ return this.countries.map((c) => c.isoCode);
+ }
public countries: Country[];
@Column({
diff --git a/server/legacy-plm/user.ts b/server/legacy-plm/user.ts
index f2b0f04d5..f402186b9 100644
--- a/server/legacy-plm/user.ts
+++ b/server/legacy-plm/user.ts
@@ -2,9 +2,7 @@
import stringSimilarity from 'string-similarity';
import { In } from 'typeorm';
-import { UserType } from '../../types/user.type';
-import { Country } from '../entities/country';
-import { User } from '../entities/user';
+import { User, UserType } from '../entities/user';
import { Village } from '../entities/village';
import { AppDataSource } from '../utils/data-source';
import { setUserPosition } from '../utils/get-pos';
@@ -73,8 +71,8 @@ export async function createPLMUserToDB(plmUser: PLM_User): Promise {
countryCode = matchs[c.bestMatchIndex].isoCode;
}
}
- if (village !== null && !village.countries.map((e) => e.isoCode).includes(countryCode)) {
- countryCode = village.countries[0].isoCode;
+ if (village !== null && !village.countryCodes.includes(countryCode)) {
+ countryCode = village.countryCodes[0];
}
// 3- Add user
@@ -87,14 +85,12 @@ export async function createPLMUserToDB(plmUser: PLM_User): Promise {
user.postalCode = plmUser.postalCode || '';
user.address = plmUser.address || '';
user.villageId = village?.id || null;
- const countryFound = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: countryCode } });
- user.country = countryFound ?? null;
+ user.countryCode = countryCode;
user.type = userType;
user.passwordHash = '';
user.verificationHash = '';
user.accountRegistration = 10;
- user.positionLat = 0;
- user.positionLon = 0;
+ user.position = { lat: 0, lng: 0 };
await setUserPosition(user);
await AppDataSource.getRepository(User).save(user);
diff --git a/server/legacy-plm/village.ts b/server/legacy-plm/village.ts
index e54623311..99424f02c 100644
--- a/server/legacy-plm/village.ts
+++ b/server/legacy-plm/village.ts
@@ -1,9 +1,9 @@
/* eslint-disable camelcase */
import stringSimilarity from 'string-similarity';
-import { Country } from '../entities/country';
import { Village } from '../entities/village';
import { AppDataSource } from '../utils/data-source';
+import { countries } from '../utils/iso-3166-countries-french';
import { logger } from '../utils/logger';
export type PLM_Village = {
@@ -38,7 +38,6 @@ async function createVillage(plmVillage: PLM_Village): Promise {
// get countries and create village
logger.info(`Try to create village with slug: ${slug}`);
- const countries = await AppDataSource.getRepository(Country).find();
const villageCountries = (slug.startsWith('village-monde-') ? slug.slice(14) : slug).split(/[-–]/).filter((s) => s.length > 0);
if (villageCountries.length === 2) {
const c1 = stringSimilarity.findBestMatch(
@@ -53,7 +52,7 @@ async function createVillage(plmVillage: PLM_Village): Promise {
logger.info(`c2: ${JSON.stringify(c2.bestMatch)}`);
if (c1.bestMatch.rating > 0.88 && c2.bestMatch.rating > 0.88) {
const newVillage = new Village();
- newVillage.countries = [countries[c1.bestMatchIndex], countries[c2.bestMatchIndex]];
+ newVillage.countryCodes = [countries[c1.bestMatchIndex].isoCode, countries[c2.bestMatchIndex].isoCode];
newVillage.name = name;
newVillage.plmId = plmId;
await AppDataSource.getRepository(Village).save(newVillage);
@@ -62,8 +61,7 @@ async function createVillage(plmVillage: PLM_Village): Promise {
}
const newVillage = new Village();
- const frCountry = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: 'FR' } });
- if (frCountry) newVillage.countries = [frCountry, frCountry];
+ newVillage.countryCodes = ['FR', 'FR'];
newVillage.name = name;
newVillage.plmId = plmId;
await AppDataSource.getRepository(Village).save(newVillage);
diff --git a/server/manager/activity.ts b/server/manager/activity.ts
index d6e218dda..3ea43e393 100644
--- a/server/manager/activity.ts
+++ b/server/manager/activity.ts
@@ -1,9 +1,8 @@
import { In } from 'typeorm';
-import { UserType } from '../../types/user.type';
import { Activity } from '../entities/activity';
import { Comment } from '../entities/comment';
-import { Country } from '../entities/country';
+import { UserType } from '../entities/user';
import { AppDataSource } from '../utils/data-source';
type ActivityGetter = {
@@ -79,15 +78,10 @@ export const getActivities = async ({
userId,
});
} else if (pelico && countries !== undefined && countries.length > 0) {
- const countriesId = await AppDataSource.getRepository(Country).find({
- where: {
- isoCode: In(countries),
- },
- });
subQueryBuilder = subQueryBuilder
.innerJoin('activity.user', 'user')
- .andWhere('((user.countryId IN (:countriesId) AND user.type >= :userType) OR user.type <= :userType2)', {
- countriesId,
+ .andWhere('((user.countryCode IN (:countries) AND user.type >= :userType) OR user.type <= :userType2)', {
+ countries,
userType: UserType.TEACHER,
userType2: UserType.MEDIATOR,
});
@@ -96,13 +90,8 @@ export const getActivities = async ({
userType2: UserType.MEDIATOR,
});
} else if (!pelico && countries !== undefined && countries.length > 0) {
- const countriesId = await AppDataSource.getRepository(Country).find({
- where: {
- isoCode: In(countries),
- },
- });
- subQueryBuilder = subQueryBuilder.innerJoin('activity.user', 'user').andWhere('user.countryCode IN (:countriesId) AND user.type >= :userType', {
- countriesId,
+ subQueryBuilder = subQueryBuilder.innerJoin('activity.user', 'user').andWhere('user.countryCode IN (:countries) AND user.type >= :userType', {
+ countries,
userType: UserType.TEACHER,
});
} else if (!pelico && countries !== undefined) {
diff --git a/server/middlewares/authenticate.ts b/server/middlewares/authenticate.ts
index 057e8ce60..758938c33 100644
--- a/server/middlewares/authenticate.ts
+++ b/server/middlewares/authenticate.ts
@@ -1,8 +1,8 @@
import type { NextFunction, Request, RequestHandler, Response } from 'express';
import jwt from 'jsonwebtoken';
-import type { UserType } from '../../types/user.type';
import { getNewAccessToken } from '../authentication/lib/tokens';
+import type { UserType } from '../entities/user';
import { User } from '../entities/user';
import { getHeader } from '../utils';
import { AppDataSource } from '../utils/data-source';
diff --git a/server/middlewares/setVillage.ts b/server/middlewares/setVillage.ts
index 2295171ca..68a50f047 100644
--- a/server/middlewares/setVillage.ts
+++ b/server/middlewares/setVillage.ts
@@ -1,6 +1,6 @@
import type { NextFunction, Request, Response } from 'express';
-import { UserType } from '../../types/user.type';
+import { UserType } from '../entities/user';
import { Village } from '../entities/village';
import { AppDataSource } from '../utils/data-source';
@@ -13,9 +13,7 @@ export async function setVillage(req: Request, res: Response, next: NextFunction
villageId = req.cookies?.['village-id'] || -1;
}
if (villageId !== -1 || (user && user.type !== UserType.TEACHER)) {
- const villages = await AppDataSource.getRepository(Village).find(
- villageId !== -1 ? { where: { id: villageId }, relations: { countries: true } } : { order: { id: 'ASC' } },
- );
+ const villages = await AppDataSource.getRepository(Village).find(villageId !== -1 ? { where: { id: villageId } } : { order: { id: 'ASC' } });
req.village = villages[0];
}
if (villageId === -1 && req.village !== undefined) {
diff --git a/server/migrations/1712732757287-New-Country-Table.ts b/server/migrations/1712732757287-New-Country-Table.ts
index 9e8fbccf5..267ae846e 100644
--- a/server/migrations/1712732757287-New-Country-Table.ts
+++ b/server/migrations/1712732757287-New-Country-Table.ts
@@ -13,7 +13,7 @@ export class NewCountryTable1712732757287 implements MigrationInterface {
for (const country of countries) {
// try catch here to prevent if country is already set in db for some reason
try {
- await queryRunner.query(`INSERT INTO country (isoCode, name) VALUES ('${country.isoCode}', "${country.name}");`);
+ await queryRunner.query(`INSERT INTO country (isoCode, name) VALUES ("${country.isoCode}", "${country.name}");`);
} catch (error) {
console.error(error);
}
diff --git a/server/migrations/1713446226165-user-village-country-relation.ts b/server/migrations/1713446226165-user-village-country-relation.ts
deleted file mode 100644
index 301427085..000000000
--- a/server/migrations/1713446226165-user-village-country-relation.ts
+++ /dev/null
@@ -1,172 +0,0 @@
-import type { MigrationInterface, QueryRunner } from 'typeorm';
-
-export class UserVillageCountryRelation1713446226165 implements MigrationInterface {
- name = 'UserVillageCountryRelation1713446226165';
-
- public async up(queryRunner: QueryRunner): Promise {
- // VILLAGE county relation
- const villageCountriesCountryTable = await queryRunner.getTable('village_countries_country');
- if (!villageCountriesCountryTable) {
- await queryRunner.query(
- `CREATE TABLE village_countries_country (
- villageId int NOT NULL,
- countryId int NOT NULL,
- INDEX IDX_df9756b7b0058adb584d3a8c75 (villageId),
- INDEX IDX_870cdc22399cffb6327c914b79 (countryId),
- CONSTRAINT FK_df9756b7b0058adb584d3a8c75e FOREIGN KEY (villageId) REFERENCES village(id) ON DELETE CASCADE ON UPDATE CASCADE,
- CONSTRAINT FK_870cdc22399cffb6327c914b790 FOREIGN KEY (countryId) REFERENCES country(id) ON DELETE CASCADE ON UPDATE CASCADE,
- PRIMARY KEY (villageId, countryId))
- ENGINE=InnoDB;`,
- );
- }
-
- // save data stored
- const villageTable = await queryRunner.getTable('village');
- const villageCountryCodesColumn = villageTable?.columns.find((c) => c.name === 'countryCodes');
- if (villageCountryCodesColumn) {
- const allVillages: { id: number; countryCodes: string }[] = await queryRunner.query(`SELECT id, countryCodes FROM village;`);
- for (const village of allVillages) {
- if (village.countryCodes) {
- const countryCodes = village.countryCodes
- .split(',')
- .map((cc) => `'${cc}'`)
- .join(',');
- const countryIds: { id: number }[] = await queryRunner.query(`SELECT id FROM country WHERE isoCode IN(${countryCodes})`);
- for (const countryId of countryIds) {
- await queryRunner.query(`INSERT INTO village_countries_country (villageId, countryId) VALUES (${village.id}, ${countryId.id});`);
- }
- }
- }
- await queryRunner.query(`ALTER TABLE village DROP COLUMN countryCodes;`);
- }
-
- // USER country relation
- const userTable = await queryRunner.getTable('user');
- const userCountryId = userTable?.columns.find((c) => c.name === 'countryId');
- const userCountryConstraint = userTable?.foreignKeys.find((fk) => fk.name === 'FK_4aaf6d02199282eb8d3931bff31');
- if (!userCountryId && !userCountryConstraint) {
- await queryRunner.query(`ALTER TABLE user
- ADD COLUMN countryId int,
- ADD CONSTRAINT FK_4aaf6d02199282eb8d3931bff31 FOREIGN KEY (countryId) REFERENCES country(id) ON DELETE NO ACTION ON UPDATE NO ACTION`);
- }
- const userCountryCode = userTable?.columns.find((c) => c.name === 'countryCode');
- if (userCountryCode) {
- const usersCountry: { id: number; countryCode: string | null }[] = await queryRunner.query(`SELECT id, countryCode FROM user;`);
- // save data stored
- for (const userCountry of usersCountry) {
- const countryId: { id: number }[] = await queryRunner.query(`SELECT id FROM country WHERE isoCode='${userCountry.countryCode}';`);
- if (countryId.length && countryId[0].id) {
- await queryRunner.query(`UPDATE user SET countryId=${countryId[0].id} WHERE id=${userCountry.id};`);
- }
- }
- await queryRunner.query(`ALTER TABLE user DROP COLUMN countryCode;`);
- }
-
- // CLASSROOM relation
- const classroomTable = await queryRunner.getTable('classroom');
- const classroomCountryId = classroomTable?.columns.find((c) => c.name === 'countryId');
- const classroomCountryConstraint = classroomTable?.foreignKeys.find((fk) => fk.name === 'FK_650c574da06eaf08695ed7a7bcd');
- if (!classroomCountryId && !classroomCountryConstraint) {
- await queryRunner.query(
- `ALTER TABLE classroom
- ADD countryId int NULL,
- ADD CONSTRAINT FK_650c574da06eaf08695ed7a7bcd FOREIGN KEY (countryId) REFERENCES country(id) ON DELETE NO ACTION ON UPDATE NO ACTION;`,
- );
- }
- const classroomCountryCode = classroomTable?.columns.find((c) => c.name === 'countryCode');
- if (classroomCountryCode) {
- const classroomCountryCodes: { id: number; countryCode: string | null }[] = await queryRunner.query(`SELECT id, countryCode FROM classroom`);
- for (const classroom of classroomCountryCodes) {
- if (classroom.countryCode) {
- const country: { id: number }[] = await queryRunner.query(`SELECT id FROM country WHERE isoCode='${classroom.countryCode}';`);
- await queryRunner.query(`UPDATE classroom SET countryId=${country[0].id} WHERE id=${classroom.id};`);
- }
- }
- await queryRunner.query(`ALTER TABLE classroom DROP COLUMN countryCode`);
- }
- }
-
- public async down(queryRunner: QueryRunner): Promise {
- // recreate VILLAGE data
- const villageTable = await queryRunner.getTable('village');
- const villageCountryCodes = villageTable?.columns.find((c) => c.name === 'countryCodes');
- if (!villageCountryCodes) {
- await queryRunner.query(`ALTER TABLE village ADD COLUMN countryCodes TINYTEXT;`);
- }
- const villageCountriesCountryTable = await queryRunner.getTable('village_countries_country');
- if (villageCountriesCountryTable) {
- const villageCountryRelation: { villageId: number; countryId: number }[] = await queryRunner.query(`SELECT * FROM village_countries_country;`);
- // create countryCodes column
- const allVillages: { id: number }[] = await queryRunner.query(`SELECT id FROM village;`);
- for (const village of allVillages) {
- // build string query from country id
- const countryIds = villageCountryRelation
- .reduce((acc, relation) => {
- if (relation.villageId === village.id) {
- acc += `${relation.countryId}, `;
- }
- return acc;
- }, '')
- .slice(0, -2);
-
- // get isoCodes from country table
- const countryIsoCodes: { isoCode: string }[] = await queryRunner.query(`SELECT isoCode FROM country WHERE id IN(${countryIds});`);
- // build original countryCodes and set them in village.countryCodes
- const countryCodes = countryIsoCodes
- .reduce((acc, countryIso) => {
- acc += `${countryIso.isoCode},`;
- return acc;
- }, '')
- .slice(0, -1);
- await queryRunner.query(`UPDATE village SET countryCodes = '${countryCodes}' WHERE id = ${village.id};`);
- }
- // removing relation village countries table
- if (villageTable?.foreignKeys.find((fk) => fk.name === 'FK_870cdc22399cffb6327c914b790')) {
- await queryRunner.query(`ALTER TABLE village_countries_country DROP FOREIGN KEY FK_870cdc22399cffb6327c914b790`);
- }
- if (villageTable?.foreignKeys.find((fk) => fk.name === 'FK_df9756b7b0058adb584d3a8c75e')) {
- await queryRunner.query(`ALTER TABLE village_countries_country DROP FOREIGN KEY FK_df9756b7b0058adb584d3a8c75e`);
- }
- await queryRunner.dropTable('village_countries_country');
- }
-
- // recreate USER data
- const userTable = await queryRunner.getTable('user');
- const userCountryId = userTable?.columns.find((c) => c.name === 'countryId');
- if (userCountryId) {
- const usersCountryId: { id: number; countryId: number }[] = await queryRunner.query(`SELECT id, countryId FROM user;`);
- const userCountryCode = userTable?.columns.find((c) => c.name === 'countryCode');
- if (!userCountryCode) {
- await queryRunner.query(`ALTER TABLE user ADD COLUMN countryCode TINYTEXT;`);
- for (const userData of usersCountryId) {
- const isoCode: [{ isoCode: string }] = await queryRunner.query(`SELECT isoCode FROM country WHERE id=${userData.countryId}`);
- if (isoCode.length) {
- await queryRunner.query(`UPDATE user SET countryCode = '${isoCode[0].isoCode}' WHERE id = ${userData.id};`);
- }
- }
- }
- // drop key constraint + column
- await queryRunner.query(`ALTER TABLE user DROP FOREIGN KEY FK_4aaf6d02199282eb8d3931bff31`);
- await queryRunner.query(`ALTER TABLE user DROP COLUMN countryId`);
- }
-
- // recreate CLASSROOM data
- const classroomTable = await queryRunner.getTable('classroom');
- const classroomCountryId = classroomTable?.columns.find((c) => c.name === 'countryId');
- if (classroomCountryId) {
- const classroomsCountryId: { id: number; countryId: number }[] = await queryRunner.query(`SELECT id, countryId FROM classroom;`);
- if (!classroomTable?.columns.find((c) => c.name === 'countryCode')) {
- await queryRunner.query(`ALTER TABLE classroom ADD COLUMN countryCode TINYTEXT;`);
- for (const classroom of classroomsCountryId) {
- const isoCode: [{ isoCode: string }] = await queryRunner.query(`SELECT isoCode FROM country WHERE id=${classroom.countryId}`);
- if (isoCode.length) {
- await queryRunner.query(`UPDATE classroom SET countryCode = '${isoCode[0].isoCode}' WHERE id = ${classroom.id};`);
- }
- }
- }
- // drop key contraint and relation table
- await queryRunner.query(`ALTER TABLE classroom DROP FOREIGN KEY FK_650c574da06eaf08695ed7a7bcd`);
- await queryRunner.query(`ALTER TABLE classroom DROP COLUMN countryId`);
- }
- }
-}
diff --git a/server/utils/countries-map.ts b/server/utils/countries-map.ts
new file mode 100644
index 000000000..43789d4aa
--- /dev/null
+++ b/server/utils/countries-map.ts
@@ -0,0 +1,7 @@
+import type { Country } from '../../types/country.type';
+import { countries } from './iso-3166-countries-french';
+
+export const countriesMap = countries.reduce>((acc, c) => {
+ acc[c.isoCode] = c;
+ return acc;
+}, {});
diff --git a/server/utils/database.ts b/server/utils/database.ts
index 8b943567d..2ca60eb65 100644
--- a/server/utils/database.ts
+++ b/server/utils/database.ts
@@ -1,9 +1,7 @@
import * as argon2 from 'argon2';
import mysql from 'mysql2';
-import { UserType } from '../../types/user.type';
-import { Country } from '../entities/country';
-import { User } from '../entities/user';
+import { User, UserType } from '../entities/user';
import { AppDataSource, DEFAULT_NAME } from './data-source';
import { sleep } from './index';
import { logger } from './logger';
@@ -68,10 +66,9 @@ async function createSuperAdminUser(): Promise {
user.type = UserType.SUPER_ADMIN;
user.passwordHash = await argon2.hash(adminPassword);
user.accountRegistration = 0;
- const frCountry = await AppDataSource.getRepository(Country).findOne({ where: { isoCode: 'FR' } });
- if (frCountry) user.country = frCountry;
- user.positionLat = 0;
- user.positionLon = 0;
+ user.countryCode = 'fr';
+ user.positionLat = '0';
+ user.positionLon = '0';
await AppDataSource.getRepository(User).save(user);
logger.info('Super user Admin created!');
}
diff --git a/server/utils/get-pos.ts b/server/utils/get-pos.ts
index c275a78e4..c5d6f1135 100644
--- a/server/utils/get-pos.ts
+++ b/server/utils/get-pos.ts
@@ -38,11 +38,10 @@ export async function setUserPosition(user: User): Promise {
const pos =
(await getPosition({ q: query })) ||
- (await getPosition({ city: user.city, country: user.country?.name ?? 'France' })) ||
- (await getPosition({ country: user.country?.name ?? 'France' }));
+ (await getPosition({ city: user.city, country: user.country?.name })) ||
+ (await getPosition({ country: user.country?.name }));
if (pos !== null) {
- user.positionLat = pos.lat;
- user.positionLon = pos.lng;
+ user.position = pos;
return;
}
}
diff --git a/server/utils/iso-3166-countries-french.ts b/server/utils/iso-3166-countries-french.ts
index d81937c8b..026ae1640 100644
--- a/server/utils/iso-3166-countries-french.ts
+++ b/server/utils/iso-3166-countries-french.ts
@@ -1,6 +1,6 @@
import type { Country } from '../../types/country.type';
-export const countries: Omit[] = [
+export const countries: Country[] = [
{
isoCode: 'AF',
name: 'Afghanistan',
diff --git a/src/api/countries/countries.get.ts b/src/api/countries/countries.get.ts
deleted file mode 100644
index 9c123f92a..000000000
--- a/src/api/countries/countries.get.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { useQuery } from 'react-query';
-import type { Country } from 'server/entities/country';
-
-import { axiosRequest } from 'src/utils/axiosRequest';
-
-async function getCountries(): Promise {
- return (
- await axiosRequest({
- method: 'GET',
- baseURL: '/api',
- url: '/countries',
- })
- ).data;
-}
-
-export const useGetCountries = () => {
- return useQuery(['countries'], () => getCountries());
-};
diff --git a/src/api/user/user.get.ts b/src/api/user/user.get.ts
index ef4b99aa2..9fd078f38 100644
--- a/src/api/user/user.get.ts
+++ b/src/api/user/user.get.ts
@@ -34,6 +34,7 @@ export const getUserVisibilityFamilyParams = async (user: User) => {
url: `/users/${user.id}/visibility-params`,
});
if (response.error) return null;
+ // console.log('User visibility params: ', response.data);
return response.data;
}
return [];
diff --git a/src/components/Flag.tsx b/src/components/Flag.tsx
index 0c3e2dbbe..8c23ca027 100644
--- a/src/components/Flag.tsx
+++ b/src/components/Flag.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-import type { Country } from 'server/entities/country';
import MysteryFlag from 'src/svg/mystery-flag.svg';
@@ -10,7 +9,7 @@ const sizes = {
interface FlagProps {
isMistery?: boolean;
- country?: Country;
+ country?: string;
size?: 'small' | 'medium';
style?: React.CSSProperties;
}
@@ -21,9 +20,6 @@ export const Flag = ({ country, isMistery = false, size = 'medium', style = {} }
return (
// Small SVG, no need of improvments
// eslint-disable-next-line @next/next/no-img-element
-
+
);
};
diff --git a/src/components/Navigation.tsx b/src/components/Navigation.tsx
index 5f66aca36..984d654e9 100644
--- a/src/components/Navigation.tsx
+++ b/src/components/Navigation.tsx
@@ -257,12 +257,12 @@ export const Navigation = (): JSX.Element => {
);
@@ -271,7 +271,7 @@ export const Navigation = (): JSX.Element => {
{
displayName: newUser.displayName || '',
};
if (position !== null) {
- updatedValues.positionLat = position.lat;
- updatedValues.positionLon = position.lng;
+ updatedValues.position = position;
}
const response = await axiosRequest({
method: 'PUT',
@@ -227,7 +226,7 @@ export const FirstPhase = () => {
{user ? user.country?.name : ''}
- {user && }
+ {user && }
)}
diff --git a/src/components/activities/ActivityView/index.tsx b/src/components/activities/ActivityView/index.tsx
index 6ef79f58a..f564aa36d 100644
--- a/src/components/activities/ActivityView/index.tsx
+++ b/src/components/activities/ActivityView/index.tsx
@@ -54,7 +54,7 @@ export const ActivityView = ({ activity, user }: ActivityViewProps) => {
{isPelico ? (
) : (
-
+
)}
diff --git a/src/components/activities/GameStats.tsx b/src/components/activities/GameStats.tsx
index 25059fba7..9bff9d612 100644
--- a/src/components/activities/GameStats.tsx
+++ b/src/components/activities/GameStats.tsx
@@ -2,7 +2,6 @@ import React, { useMemo } from 'react';
import { AvatarImg } from '../Avatar';
import { Flag } from '../Flag';
-import type { Country } from 'types/country.type';
import type { GameResponse } from 'types/gameResponse.type';
import { UserType } from 'types/user.type';
import type { User } from 'types/user.type';
@@ -10,7 +9,7 @@ import type { User } from 'types/user.type';
type GameStatsProps = {
gameResponses: GameResponse[];
choices: number[];
- country?: Country;
+ country: string;
userMap: { [key: number]: number };
users: User[];
position: number;
@@ -23,7 +22,7 @@ const POSITION = [
];
const GameStats = ({ gameResponses, choices, country, userMap, users, position }: GameStatsProps) => {
const countryResponses = useMemo(() => {
- return gameResponses.filter((responseGame) => users[userMap[responseGame.userId]]?.country?.isoCode === country?.isoCode);
+ return gameResponses.filter((responseGame) => users[userMap[responseGame.userId]]?.country?.isoCode === country);
}, [country, gameResponses, userMap, users]);
const responseCount = countryResponses.length;
diff --git a/src/contexts/countryContext.tsx b/src/contexts/countryContext.tsx
deleted file mode 100644
index 3e81b31a4..000000000
--- a/src/contexts/countryContext.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import type { ReactNode } from 'react';
-import React, { useEffect, createContext, useState } from 'react';
-import type { Country } from 'server/entities/country';
-
-import { CircularProgress } from '@mui/material';
-
-import { useGetCountries } from 'src/api/countries/countries.get';
-
-interface CountryContextValue {
- countries: Country[];
-}
-export const CountryContext = createContext({
- countries: [],
-});
-
-interface Props {
- children?: ReactNode;
-}
-export function CountryContextProvider({ children }: Props) {
- const [countries, setCountries] = useState([]);
- const countriesFetch = useGetCountries();
- useEffect(() => {
- if (countriesFetch.data) setCountries(countriesFetch.data);
- }, [countriesFetch.data]);
- if (countriesFetch.isLoading || countriesFetch.isIdle) {
- return ;
- }
- return {children};
-}
diff --git a/src/contexts/userContext.tsx b/src/contexts/userContext.tsx
index 914c68209..d06a98940 100644
--- a/src/contexts/userContext.tsx
+++ b/src/contexts/userContext.tsx
@@ -1,5 +1,5 @@
import { useRouter } from 'next/router';
-import React from 'react';
+import React, { useEffect } from 'react';
import { axiosRequest } from 'src/utils/axiosRequest';
import type { Student } from 'types/student.type';
@@ -340,6 +340,10 @@ export const UserContextProvider = ({ user, setUser, children }: React.PropsWith
[user],
);
+ useEffect(() => {
+ // console.log('user===', user);
+ }, [user]);
+
const value = React.useMemo(
() => ({
user,
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
index c79b50f89..10fc19676 100644
--- a/src/pages/_app.tsx
+++ b/src/pages/_app.tsx
@@ -37,7 +37,6 @@ import { NewAdminHeader } from 'src/components/admin/NewAdminHeader';
import { NewAdminNavigation } from 'src/components/admin/NewAdminNavigation';
import { ActivityContextProvider } from 'src/contexts/activityContext';
import { ClassroomContextProvider } from 'src/contexts/classroomContext';
-import { CountryContextProvider } from 'src/contexts/countryContext';
import { UserContextProvider } from 'src/contexts/userContext';
import { VillageContextProvider } from 'src/contexts/villageContext';
import { useAnalytics } from 'src/hooks/useAnalytics';
@@ -137,51 +136,49 @@ const MyApp: React.FunctionComponent & {
-
-
- {isOnAdmin ? (
- router.pathname.startsWith('/admin/newportal') ? (
-
-
-
-
-
-
-
-
+
+ {isOnAdmin ? (
+ router.pathname.startsWith('/admin/newportal') ? (
+
+
+
+
+
+
+
- ) : (
-
-
-
+ ) : (
+
+
+
- )
- ) : user !== null &&
- router.pathname !== '/inscription' &&
- router.pathname !== '/connexion' &&
- router.pathname !== '/login' &&
- router.pathname !== '/user-verified' &&
- router.pathname !== '/reset-password' &&
- router.pathname !== '/update-password' &&
- router.pathname !== '/404' ? (
-
-
-
-
- ) : (
+ )
+ ) : user !== null &&
+ router.pathname !== '/inscription' &&
+ router.pathname !== '/connexion' &&
+ router.pathname !== '/login' &&
+ router.pathname !== '/user-verified' &&
+ router.pathname !== '/reset-password' &&
+ router.pathname !== '/update-password' &&
+ router.pathname !== '/404' ? (
+
+
- )}
-
-
+
+
+ ) : (
+
+ )}
+
diff --git a/src/pages/admin/users/edit/[id].tsx b/src/pages/admin/users/edit/[id].tsx
index 2f289bc68..e365604fd 100644
--- a/src/pages/admin/users/edit/[id].tsx
+++ b/src/pages/admin/users/edit/[id].tsx
@@ -1,7 +1,7 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useSnackbar } from 'notistack';
-import React, { useContext } from 'react';
+import React from 'react';
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
import Breadcrumbs from '@mui/material/Breadcrumbs';
@@ -15,7 +15,6 @@ import TextField from '@mui/material/TextField';
import { AdminTile } from 'src/components/admin/AdminTile';
import { CountrySelector } from 'src/components/selectors/CountrySelector';
-import { CountryContext } from 'src/contexts/countryContext';
import { useUserRequests } from 'src/services/useUsers';
import { useVillages } from 'src/services/useVillages';
import { getQueryString } from 'src/utils';
@@ -35,7 +34,6 @@ const Required = (label: string) => (
const EditUser = () => {
const router = useRouter();
- const { countries } = useContext(CountryContext);
const { villages } = useVillages();
const { editUser } = useUserRequests();
@@ -104,7 +102,7 @@ const EditUser = () => {
return;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
- const { positionLat: _ignore, positionLon: _ignoreToo, ...updatedValues } = user;
+ const { position: _ignore, ...updatedValues } = user;
const result = await editUser({ ...updatedValues, villageId: user.villageId || null });
if (result !== null) {
router.push('/admin/users');
@@ -230,10 +228,7 @@ const EditUser = () => {
label={Required('Pays')}
value={user.country?.isoCode}
onChange={(countryCode) => {
- const foundCoundry = countries.find((c) => c.isoCode === countryCode);
- if (foundCoundry) {
- setUser((u) => (!u ? null : { ...u, country: foundCoundry }));
- }
+ setUser((u) => (!u ? null : { ...u, country: { isoCode: countryCode, name: '' } }));
}}
filterCountries={
user.villageId ? villages.find((v) => v.id === user.villageId)?.countries?.map((c) => c.isoCode) || undefined : undefined
diff --git a/src/pages/admin/users/new.tsx b/src/pages/admin/users/new.tsx
index 688c62c5e..ba22bd668 100644
--- a/src/pages/admin/users/new.tsx
+++ b/src/pages/admin/users/new.tsx
@@ -1,7 +1,7 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
import { useSnackbar } from 'notistack';
-import React, { useContext } from 'react';
+import React from 'react';
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
import Breadcrumbs from '@mui/material/Breadcrumbs';
@@ -15,7 +15,6 @@ import TextField from '@mui/material/TextField';
import { AdminTile } from 'src/components/admin/AdminTile';
import { CountrySelector } from 'src/components/selectors/CountrySelector';
-import { CountryContext } from 'src/contexts/countryContext';
import { useUserRequests } from 'src/services/useUsers';
import { useVillages } from 'src/services/useVillages';
import { defaultOutlinedButtonStyle } from 'src/styles/variables.const';
@@ -33,7 +32,6 @@ const Required = (label: string) => (
);
const NewUser = () => {
- const { countries } = useContext(CountryContext);
const router = useRouter();
const { villages } = useVillages();
const { addUser } = useUserRequests();
@@ -49,7 +47,6 @@ const NewUser = () => {
type: UserType.TEACHER,
villageId: 0,
country: {
- id: -1,
isoCode: '',
name: '',
},
@@ -214,8 +211,7 @@ const NewUser = () => {
label={Required('Pays')}
value={newUser.country?.isoCode || ''}
onChange={(countryCode) => {
- const countryFound = countries.find((c) => c.isoCode === countryCode);
- if (countryFound) setNewUser((u) => ({ ...u, country: countryFound }));
+ setNewUser((u) => ({ ...u, country: { isoCode: countryCode, name: '' } }));
}}
filterCountries={
newUser.villageId ? villages.find((v) => v.id === newUser.villageId)?.countries?.map((c) => c.isoCode) || undefined : undefined
diff --git a/src/pages/admin/villages/edit/[id].tsx b/src/pages/admin/villages/edit/[id].tsx
index fd2f0089c..8f7bb6544 100644
--- a/src/pages/admin/villages/edit/[id].tsx
+++ b/src/pages/admin/villages/edit/[id].tsx
@@ -1,6 +1,6 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
-import React, { useContext } from 'react';
+import React from 'react';
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
import { Button, TextField } from '@mui/material';
@@ -9,7 +9,6 @@ import MaterialLink from '@mui/material/Link';
import { AdminTile } from 'src/components/admin/AdminTile';
import { CountrySelector } from 'src/components/selectors/CountrySelector';
-import { CountryContext } from 'src/contexts/countryContext';
import { useVillageRequests } from 'src/services/useVillages';
import { defaultOutlinedButtonStyle } from 'src/styles/variables.const';
import { getQueryString } from 'src/utils';
@@ -18,7 +17,6 @@ import type { Village } from 'types/village.type';
const EditVillage = () => {
const router = useRouter();
- const { countries } = useContext(CountryContext);
const { editVillage } = useVillageRequests();
const villageId = React.useMemo(() => parseInt(getQueryString(router.query.id), 10) || 0, [router]);
@@ -82,8 +80,7 @@ const EditVillage = () => {
{
- const foundCountry = countries.find((c) => c.isoCode === newValue);
- if (foundCountry) setVillage((v) => (!v ? null : { ...v, countries: [foundCountry, village.countries[1]] }));
+ setVillage((v) => (!v ? null : { ...v, countries: [{ isoCode: newValue, name: '' }, village.countries[1]] }));
}}
label="Pays 1"
style={{ width: '100%', marginBottom: '1rem' }}
@@ -91,8 +88,7 @@ const EditVillage = () => {
{
- const foundCountry = countries.find((c) => c.isoCode === newValue);
- if (foundCountry) setVillage((v) => (!v ? null : { ...v, countries: [village.countries[0], foundCountry] }));
+ setVillage((v) => (!v ? null : { ...v, countries: [village.countries[0], { isoCode: newValue, name: '' }] }));
}}
label="Pays 2"
style={{ width: '100%', marginBottom: '1rem' }}
diff --git a/src/pages/admin/villages/new.tsx b/src/pages/admin/villages/new.tsx
index e18bf9d6e..a8feb2d87 100644
--- a/src/pages/admin/villages/new.tsx
+++ b/src/pages/admin/villages/new.tsx
@@ -1,7 +1,6 @@
import Link from 'next/link';
import { useRouter } from 'next/router';
-import React, { useContext } from 'react';
-import type { Country } from 'server/entities/country';
+import React from 'react';
import NavigateNextIcon from '@mui/icons-material/NavigateNext';
import { Button, TextField } from '@mui/material';
@@ -10,17 +9,15 @@ import MaterialLink from '@mui/material/Link';
import { AdminTile } from 'src/components/admin/AdminTile';
import { CountrySelector } from 'src/components/selectors/CountrySelector';
-import { CountryContext } from 'src/contexts/countryContext';
import { useVillageRequests } from 'src/services/useVillages';
const NewVillage = () => {
const router = useRouter();
- const { countries } = useContext(CountryContext);
const { addVillage } = useVillageRequests();
- const [village, setVillage] = React.useState<{ name: string; countries: Country[] }>({
+ const [village, setVillage] = React.useState<{ name: string; countries: string[] }>({
name: '',
- countries: [],
+ countries: ['', ''],
});
const onSubmit = async (event: React.FormEvent) => {
@@ -28,7 +25,7 @@ const NewVillage = () => {
if (!village.name || !village.countries[0] || !village.countries[1]) {
return;
}
- const result = await addVillage({ name: village.name, countries: village.countries });
+ const result = await addVillage({ name: village.name, countries: village.countries.map((c) => ({ isoCode: c, name: c })) });
if (result !== null) {
router.push('/admin/villages');
}
@@ -59,19 +56,17 @@ const NewVillage = () => {
style={{ marginBottom: '1rem' }}
/>
{
- const foundCountry = countries.find((c) => c.isoCode === newValue);
- if (foundCountry) setVillage((v) => ({ ...v, countries: [foundCountry, village.countries[1]] }));
+ setVillage((v) => ({ ...v, countries: [newValue, village.countries[1]] }));
}}
label="Pays 1"
style={{ width: '100%', marginBottom: '1rem' }}
/>
{
- const foundCountry = countries.find((c) => c.isoCode === newValue);
- if (foundCountry) setVillage((v) => ({ ...v, countries: [village.countries[0], foundCountry] }));
+ setVillage((v) => ({ ...v, countries: [village.countries[0], newValue] }));
}}
label="Pays 1"
style={{ width: '100%', marginBottom: '1rem' }}
diff --git a/src/pages/creer-un-jeu/mimique/jouer.tsx b/src/pages/creer-un-jeu/mimique/jouer.tsx
index d7e41fcd3..54b989d56 100644
--- a/src/pages/creer-un-jeu/mimique/jouer.tsx
+++ b/src/pages/creer-un-jeu/mimique/jouer.tsx
@@ -343,8 +343,7 @@ const PlayMimic = () => {
{gameCreatorIsPelico ? (
) : (
- gameCreator &&
- gameCreator.country &&
+ gameCreator && gameCreator.country &&
)}
)}
@@ -419,7 +418,7 @@ const PlayMimic = () => {
{
gameResponses={gameResponses}
choices={choices}
position={1}
- country={userIsPelico ? village.countries[1] : village.countries.find((c) => c.id !== user.country?.id)}
+ country={
+ userIsPelico
+ ? village.countries[1].isoCode
+ : village.countries.map((c) => c.isoCode).find((i) => i !== user.country?.isoCode) || ''
+ }
userMap={userMap}
users={users}
/>
diff --git a/src/pages/familles/1.tsx b/src/pages/familles/1.tsx
index 69456f2c6..3de5195b5 100644
--- a/src/pages/familles/1.tsx
+++ b/src/pages/familles/1.tsx
@@ -2,7 +2,6 @@
// import debounce from 'lodash.debounce';
import { useRouter } from 'next/router';
import React, { useContext, useEffect, useReducer } from 'react';
-import type { Country } from 'server/entities/country';
import { Button, Card, CircularProgress, FormControl, FormControlLabel, Radio, RadioGroup } from '@mui/material';
@@ -29,11 +28,24 @@ import type { Activity } from 'types/activity.type';
import type { Classroom, InitialStateOptionsProps } from 'types/classroom.type';
import { UserType } from 'types/user.type';
+// const content1 = {
+// text1: 'les familles peuvent voir toutes les activités publiées sur 1Village, mais',
+// text2: 'jours après leurs publication',
+// };
+// const content2 = {
+// text1: 'les familles peuvent voir toutes les activités publiées sur 1Village, mais seulement celles publiées par notre classe et',
+// text2: 'jours après leurs publication',
+// };
+
type StateType = {
delayedDays: number;
hasVisibilitySetToClass: boolean;
};
+//TODO: ouvrir un nouvel onglet pour les activités
+//TODO: factoriser le code méthode SOLID
+//TODO: traiter les erreurs de react forward avec les activityCard
+
function reducer(
state: InitialStateOptionsProps,
action: {
@@ -88,6 +100,12 @@ const ClassroomParamStep1 = () => {
ownClassTimeDelay: { delayedDays: classroom?.delayedDays || 1, hasVisibilitySetToClass: true },
});
+ // const [isDisabled, setIsDisabled] = React.useState({
+ // default: false,
+ // timeDelay: false,
+ // ownClass: false,
+ // ownClassTimeDelay: false,
+ // });
const [radioValue, setRadioValue] = React.useState('default');
const [modalStep, setModalStep] = React.useState(0);
const modalStepTimeout = React.useRef(undefined);
@@ -114,21 +132,24 @@ const ClassroomParamStep1 = () => {
user !== null &&
(user.type === UserType.MEDIATOR || user.type === UserType.ADMIN || user.type === UserType.SUPER_ADMIN || user.type === UserType.FAMILY);
- const filterCountries: Country[] = React.useMemo(() => {
- if (!village || (selectedPhase === 1 && !isMediatorOrFamily)) {
- return user && user.country && user.country !== null ? [user.country] : [];
- } else {
- return village.countries;
- }
- }, [selectedPhase, village, user, isMediatorOrFamily]);
+ const filterCountries = React.useMemo(
+ () =>
+ !village || (selectedPhase === 1 && !isMediatorOrFamily)
+ ? user && user.country !== null
+ ? [user.country?.isoCode.toUpperCase()]
+ : []
+ : village.countries.map((c) => c.isoCode),
+ [selectedPhase, village, user, isMediatorOrFamily],
+ );
+ //TODO: may be filterCountries should be with country form student > teacher
const [filters, setFilters] = React.useState({
selectedType: 0,
selectedPhase: 0,
types: 'all',
status: 0,
countries: filterCountries.reduce<{ [key: string]: boolean }>((acc, c) => {
- acc[c.isoCode] = true;
+ acc[c] = true;
return acc;
}, {}),
pelico: true,
@@ -151,6 +172,26 @@ const ClassroomParamStep1 = () => {
}, {}),
[users],
);
+ // const handleDaysDelay = (key: string, event: React.ChangeEvent) => {
+ // const value = Number(event.target.value);
+ // const days = value >= 1 ? value : 1;
+
+ // switch (key) {
+ // case 'timeDelay':
+ // dispatch({ type: 'timeDelay', data: days });
+ // break;
+ // case 'ownClassTimeDelay':
+ // dispatch({ type: 'ownClassTimeDelay', data: days });
+ // break;
+ // }
+ // };
+
+ // TODO handle delayed days with this debounce function
+ // const debouncedhandleRadioSelect = React.useRef(
+ // debounce((value: any) => {
+ // updateClassroomParameters(value);
+ // }, 1500),
+ // ).current;
const handleRadioSelect = (key: string): void => {
setRadioValue(key);
@@ -225,6 +266,9 @@ const ClassroomParamStep1 = () => {
refetch();
});
};
+ // const toggleInput = (key: string, bool: boolean) => {
+ // setIsDisabled({ ...isDisabled, [key]: bool });
+ // };
const onNext = () => {
router.push('/familles/2');
@@ -281,14 +325,59 @@ const ClassroomParamStep1 = () => {
// onFocus={() => handleSelectionVisibility('default')}
style={radioValue !== 'default' ? { color: '#CCC' } : {}}
/>
-
+ {/* }
+ label={
+ {
+ handleDaysDelay('timeDelay', event);
+ debouncedhandleRadioSelect('timeDelay');
+ }}
+ onChange={(event) => {
+ handleDaysDelay('timeDelay', event);
+ debouncedhandleRadioSelect('timeDelay');
+ }}
+ onBlur={() => handleSelectionVisibility('timeDelay')}
+ value={state.timeDelay.delayedDays}
+ disabled={radioValue !== 'timeDelay'}
+ />
+ }
+ onFocus={(event: unknown) => {
+ handleDaysDelay('timeDelay', event as unknown as React.ChangeEvent);
+ debouncedhandleSelVisibility('timeDelay')
+ }}
+ onClick={() => toggleInput('timeDelay', false)}
+ disabled={isDisabled?.timeDelay}
+ style={radioValue !== 'timeDelay' ? { color: '#CCC' } : {}}
+ /> */}
}
label="les familles peuvent voir toutes les activités publiées sur 1Village, dès leur publication, mais seulement celles publiées par notre classe"
+ // onFocus={() => handleSelectionVisibility('ownClass')}
style={radioValue !== 'ownClass' ? { color: '#CCC' } : {}}
/>
+ {/* }
+ label={
+ handleDaysDelay('ownClassTimeDelay', event)}
+ onBlur={() => handleSelectionVisibility('ownClassTimeDelay')}
+ value={state.ownClassTimeDelay.delayedDays}
+ disabled={radioValue !== 'ownClassTimeDelay'}
+ />
+ }
+ onClick={() => toggleInput('ownClassTimeDelay', false)}
+ disabled={isDisabled?.ownClassTimeDelay}
+ style={radioValue !== 'ownClassTimeDelay' ? { color: '#CCC' } : {}}
+ /> */}
@@ -371,4 +460,45 @@ const ClassroomParamStep1 = () => {
);
};
+// type TextInputContainerProps = {
+// text1: string;
+// text2?: string;
+// onChange: (event: React.ChangeEvent
) => void;
+// onBlur: () => void;
+// value: number;
+// disabled?: boolean;
+// };
+
+/**
+ * Container to display text and input inline
+ * @param onChange function to handle changes
+ * @param value value of the input
+ * @param object text object containing text to display
+ */
+// const TextnInputContainer = ({ onChange, onBlur, value, disabled, ...props }: TextInputContainerProps) => {
+// const { text1, text2 } = props;
+// const spanStyle = { flexShrink: 0, marginRight: '0.5rem' };
+// return (
+//
+// {text1}
+//
+// {text2}
+//
+// );
+// };
+
export default ClassroomParamStep1;
diff --git a/src/services/useActivities.ts b/src/services/useActivities.ts
index fcf39bf4d..e1f8ed19f 100644
--- a/src/services/useActivities.ts
+++ b/src/services/useActivities.ts
@@ -5,6 +5,7 @@ import { useQuery } from 'react-query';
import { getUserVisibilityFamilyParams } from 'src/api/user/user.get';
import { UserContext } from 'src/contexts/userContext';
import { VillageContext } from 'src/contexts/villageContext';
+import { serializeToQueryUrl } from 'src/utils';
import { axiosRequest } from 'src/utils/axiosRequest';
import type { Activity } from 'types/activity.type';
import type { Classroom } from 'types/classroom.type';
@@ -38,12 +39,12 @@ export const useActivities = ({ pelico, countries = [], userId, type, ...args }:
const isFamily = user.type === UserType.FAMILY;
const query: {
- [key: string]: string | string[] | number | boolean | undefined;
+ [key: string]: string | number | boolean | undefined;
} = {
...args,
type: Array.isArray(type) ? type.join(',') : type,
villageId: user.villageId !== null ? user.villageId : villageId !== null ? villageId : undefined,
- countries: countries,
+ countries: countries.join(','),
pelico: pelico ? 'true' : 'false',
delayedDays: isFamily ? userClassroomData[0]?.delayedDays : undefined,
hasVisibilitySetToClass: isFamily ? (userClassroomData[0]?.hasVisibilitySetToClass ? true : false) : undefined,
@@ -55,10 +56,7 @@ export const useActivities = ({ pelico, countries = [], userId, type, ...args }:
const response = await axiosRequest({
method: 'GET',
- url: `/activities`,
- params: {
- ...query,
- },
+ url: `/activities${serializeToQueryUrl(query)}`,
});
if (response.error) {
return [];
diff --git a/types/country.type.ts b/types/country.type.ts
index f6b3efcc3..3fa9a16d8 100644
--- a/types/country.type.ts
+++ b/types/country.type.ts
@@ -1,5 +1,4 @@
export interface Country {
- id: number;
isoCode: string;
name: string;
}
diff --git a/types/user.type.ts b/types/user.type.ts
index 2890ad0bc..e23e5d2e6 100644
--- a/types/user.type.ts
+++ b/types/user.type.ts
@@ -29,8 +29,10 @@ export interface User {
city: string;
postalCode: string;
address: string;
- positionLat: number;
- positionLon: number;
+ position: {
+ lat: number;
+ lng: number;
+ };
hasAcceptedNewsletter: boolean;
language: string;