Skip to content

Commit 0538e1b

Browse files
authored
Merge pull request #306 from Alt-Org/joni/refactor/296-refactor-memberspage-to-integrate-with-directus
joni/refactor/296-refactor-memberspage-to-integrate-with-directus
2 parents 789f9ec + 00772e3 commit 0538e1b

File tree

14 files changed

+418
-306
lines changed

14 files changed

+418
-306
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export { useGetTeamsQuery } from './membersApi';
1+
export { useGetMembersQuery } from './membersApi';
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { organizeMembers } from './mappers';
2+
import { Member, Team, Department } from '@/entities/Member/model/types/types';
3+
4+
const mockTeams: Team[] = [
5+
{
6+
id: 1,
7+
name: 'Development',
8+
translations: [
9+
{ id: 101, teams_id: 1, languages_code: 'en-US', team: 'Development' },
10+
{ id: 102, teams_id: 1, languages_code: 'fi-FI', team: 'Ohjelmistokehitys' },
11+
],
12+
members: [],
13+
departments: [],
14+
},
15+
{
16+
id: 2,
17+
name: 'Game Design',
18+
translations: [
19+
{ id: 103, teams_id: 2, languages_code: 'en-US', team: 'Game Design' },
20+
{ id: 104, teams_id: 2, languages_code: 'fi-FI', team: 'Pelisuunnittelu' },
21+
],
22+
members: [],
23+
departments: [],
24+
},
25+
];
26+
27+
describe('organizeMembers', () => {
28+
const mockMembers: Member[] = [
29+
{
30+
id: 1,
31+
name: 'Joni Roine',
32+
33+
github: 'https://github.com/Jonroi',
34+
linkedin: 'https://www.linkedin.com/in/joni-roine/',
35+
website: 'https://jonroi.netlify.app/',
36+
team: mockTeams[0],
37+
department: {
38+
id: 10,
39+
name: 'Website Developer',
40+
translations: [
41+
{
42+
id: 110,
43+
departments_id: 10,
44+
languages_code: 'en-US',
45+
department: 'Website Developer',
46+
},
47+
{
48+
id: 111,
49+
departments_id: 10,
50+
languages_code: 'fi-FI',
51+
department: 'Verkkosivukehittäjä',
52+
},
53+
],
54+
members: [],
55+
} as Department,
56+
translations: [],
57+
},
58+
{
59+
id: 2,
60+
name: 'Alice Smith',
61+
62+
github: 'https://github.com/AliceSmith',
63+
linkedin: 'https://www.linkedin.com/in/alice-smith/',
64+
website: 'https://alicesmith.com/',
65+
team: mockTeams[1],
66+
department: {
67+
id: 20,
68+
name: 'Game Developer',
69+
translations: [
70+
{
71+
id: 120,
72+
departments_id: 20,
73+
languages_code: 'en-US',
74+
department: 'Game Developer',
75+
},
76+
{
77+
id: 121,
78+
departments_id: 20,
79+
languages_code: 'fi-FI',
80+
department: 'Pelikehittäjä',
81+
},
82+
],
83+
members: [],
84+
} as Department,
85+
translations: [],
86+
},
87+
];
88+
89+
it('should organize members into teams and departments with correct English translations', () => {
90+
const result = organizeMembers(mockMembers, 'en');
91+
92+
const developmentTeam = result.teamsMap.get(1);
93+
expect(developmentTeam).toBeDefined();
94+
if (developmentTeam) {
95+
expect(developmentTeam.name).toBe('Development');
96+
expect(developmentTeam.departments[0].name).toBe('Website Developer');
97+
expect(developmentTeam.departments[0].members[0].name).toBe('Joni Roine');
98+
}
99+
100+
const designTeam = result.teamsMap.get(2);
101+
expect(designTeam).toBeDefined();
102+
if (designTeam) {
103+
expect(designTeam.name).toBe('Game Design');
104+
expect(designTeam.departments[0].name).toBe('Game Developer');
105+
expect(designTeam.departments[0].members[0].name).toBe('Alice Smith');
106+
}
107+
});
108+
109+
it('should organize members into teams and departments with correct Finnish translations', () => {
110+
const result = organizeMembers(mockMembers, 'fi');
111+
112+
const developmentTeam = result.teamsMap.get(1);
113+
expect(developmentTeam).toBeDefined();
114+
if (developmentTeam) {
115+
expect(developmentTeam.name).toBe('Ohjelmistokehitys');
116+
expect(developmentTeam.departments[0].name).toBe('Verkkosivukehittäjä');
117+
expect(developmentTeam.departments[0].members[0].name).toBe('Joni Roine');
118+
}
119+
120+
const designTeam = result.teamsMap.get(2);
121+
expect(designTeam).toBeDefined();
122+
if (designTeam) {
123+
expect(designTeam.name).toBe('Pelisuunnittelu');
124+
expect(designTeam.departments[0].name).toBe('Pelikehittäjä');
125+
expect(designTeam.departments[0].members[0].name).toBe('Alice Smith');
126+
}
127+
});
128+
129+
it('should return an empty teams map if no members are provided', () => {
130+
const result = organizeMembers([], 'en');
131+
expect(result.teamsMap.size).toBe(0);
132+
});
133+
});
Lines changed: 99 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,105 +1,122 @@
11
import { faGithub, faLinkedin, faInstagram, faFacebook } from '@fortawesome/free-brands-svg-icons';
22
import { faGlobe, faEnvelope } from '@fortawesome/free-solid-svg-icons';
3-
import { envHelper } from '@/shared/const/envHelper';
4-
import { Member, Department } from '../model/types/types';
3+
import { Member, Team } from '@/entities/Member/model/types/types';
4+
import { getDepartmentTranslation, getTeamTranslation, getLanguageCode } from './translations';
55

66
/**
7-
* LinksMap provides a mapping of link types to FontAwesome icons.
7+
* Provides a set of icon links for various platforms.
8+
*
9+
* @returns {object} An object containing font-awesome icons for different links.
810
*/
9-
export const getLinks = () => ({
10-
website: faGlobe,
11-
github: faGithub,
12-
linkedin: faLinkedin,
13-
facebook: faFacebook,
14-
instagram: faInstagram,
15-
email: faEnvelope,
16-
});
11+
export const getLinks = () => {
12+
return {
13+
website: faGlobe,
14+
github: faGithub,
15+
linkedin: faLinkedin,
16+
facebook: faFacebook,
17+
instagram: faInstagram,
18+
email: faEnvelope,
19+
};
20+
};
1721

1822
/**
19-
* Maps raw member data from the API response to the Member type used in the application.
20-
* Sorts members alphabetically by name.
21-
* @param membersData An array of raw member data from the API.
22-
* @returns An array of members mapped to the Member type.
23+
* Represents the organized data structure containing teams and departments.
24+
*
25+
* @typedef {Object} OrganizedData
26+
* @property {Map<number, Team>} teamsMap - A map associating team IDs with team objects,
27+
* organized according to their translation and language preference.
2328
*/
24-
export const getMembers = (membersData: any[]): Member[] => {
25-
return (
26-
membersData
27-
.map((member: any) => {
28-
const logoUrl = member.attributes.Logo?.data?.attributes?.url
29-
? `${envHelper.strapiHost}${member.attributes.Logo.data.attributes.url}`
30-
: null;
31-
32-
return {
33-
id: member.id,
34-
name: member.attributes.Name,
35-
task: member.attributes.Task,
36-
email: member.attributes.Email,
37-
linkedin: member.attributes.Linkedin,
38-
website: member.attributes.Website,
39-
github: member.attributes.Github,
40-
logo: logoUrl,
41-
facebook: member.attributes.Facebook,
42-
instagram: member.attributes.Instagram,
43-
createdAt: member.attributes.createdAt,
44-
updatedAt: member.attributes.updatedAt,
45-
locale: member.attributes.locale,
46-
};
47-
})
48-
.sort((a, b) => a.name.localeCompare(b.name)) || []
49-
);
50-
};
5129

5230
/**
53-
* Maps raw department data from the API response to the Department type used in the application.
54-
* Sorts departments based on a predefined order depending on the locale.
55-
* @param departmentsData An array of raw department data from the API.
56-
* @param locale The language locale used to find the localized department name.
57-
* @returns An array of departments mapped to the Department type.
31+
* Organizes members into teams and departments based on their properties,
32+
* and sorts both the members alphabetically within their teams and departments,
33+
* as well as the teams based on a predefined order dictated by language.
34+
*
35+
* @param {Member[]} members - An array of member objects, each containing associated team and department data.
36+
* @param {string} lng - The language code used to determine which language to use for translations and sorting.
37+
* @returns {OrganizedData} The organized data containing teams mapped by their IDs.
5838
*/
59-
export const getDepartments = (departmentsData: any[], locale: string): Department[] => {
60-
const orderEn = [
61-
'Lead Developers',
62-
'Game Developer',
63-
'Website Developer',
64-
'Developers',
39+
export const organizeMembers = (members: Member[], lng: string) => {
40+
const teamsMap = new Map<number, Team>();
41+
const fullLanguageCode = getLanguageCode(lng);
42+
43+
const enOrder = [
44+
'Game Design',
45+
'Mentoring',
46+
'Development',
6547
'Graphics',
66-
'Graphical Game Development',
67-
'Sound Design & Composition',
68-
'Sound Design-Oriented Game Development',
48+
'Sounds',
49+
'Comic Book',
50+
'Production',
51+
'Analysis',
52+
'Game Art Education Package',
53+
'Other Participants',
54+
'Special Thanks',
6955
];
7056

71-
const orderFi = [
72-
'Vastaava Ohjelmistokehittäjä',
73-
'Pelikehittäjä',
74-
'Verkkosivukehittäjä',
75-
'Ohjelmistokehittäjät',
57+
const fiOrder = [
58+
'Pelisuunnittelu',
59+
'Mentorointi',
60+
'Ohjelmistokehitys',
7661
'Grafiikka',
77-
'Graafinen pelikehitys',
78-
'Äänisuunnittelu ja Sävellys',
79-
'Äänisuunnittelullinen Pelikehitys',
62+
'Äänet',
63+
'Sarjakuva',
64+
'Tuotanto',
65+
'Analyysi',
66+
'Pelitaiteen Opetuspaketti',
67+
'Muut Mukana Olleet',
68+
'Erityiskiitokset',
8069
];
8170

82-
const order = locale === 'fi' ? orderFi : orderEn;
71+
const order = lng === 'fi' ? fiOrder : enOrder;
72+
73+
members.forEach((member: Member) => {
74+
const memberTeam = member.team;
75+
const memberDepartment = member.department;
76+
77+
if (memberTeam) {
78+
let team = teamsMap.get(memberTeam.id);
79+
if (!team) {
80+
const teamName = getTeamTranslation(
81+
memberTeam.translations || [],
82+
fullLanguageCode,
83+
);
84+
85+
team = { ...memberTeam, name: teamName, members: [], departments: [] };
86+
teamsMap.set(memberTeam.id, team);
87+
}
8388

84-
return (
85-
departmentsData
86-
.map((dept: any) => {
87-
const localizedDept = dept.attributes.localizations?.data.find(
88-
(loc: any) => loc.attributes.locale === locale,
89+
if (memberDepartment) {
90+
let department = team.departments.find(
91+
(departmentItem) => departmentItem.id === memberDepartment.id,
8992
);
93+
if (!department) {
94+
const departmentName = getDepartmentTranslation(
95+
memberDepartment.translations || [],
96+
fullLanguageCode,
97+
);
9098

91-
const localizedDeptName = localizedDept
92-
? localizedDept.attributes.Name
93-
: dept.attributes.Name;
99+
department = { ...memberDepartment, name: departmentName, members: [] };
100+
team.departments.push(department);
101+
}
94102

95-
const members = getMembers(dept.attributes.members?.data || []);
103+
department.members.push(member);
104+
} else {
105+
team.members.push(member);
106+
}
107+
}
108+
});
109+
teamsMap.forEach((team) => {
110+
team.members.sort((a, b) => a.name.localeCompare(b.name));
111+
team.departments.forEach((department) => {
112+
department.members.sort((a, b) => a.name.localeCompare(b.name));
113+
});
114+
});
115+
const sortedTeams = Array.from(teamsMap.values()).sort((a, b) => {
116+
const indexA = order.indexOf(a.name);
117+
const indexB = order.indexOf(b.name);
118+
return indexA - indexB;
119+
});
96120

97-
return {
98-
id: dept.id,
99-
name: localizedDeptName,
100-
members,
101-
};
102-
})
103-
.sort((a, b) => order.indexOf(a.name) - order.indexOf(b.name)) || []
104-
);
121+
return { teamsMap: new Map(sortedTeams.map((team) => [team.id, team])) };
105122
};

0 commit comments

Comments
 (0)