Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.objectcomputing.checkins.services.guild;

import com.objectcomputing.checkins.services.guild.member.GuildMemberResponseDTO;
import com.objectcomputing.checkins.services.memberprofile.MemberProfile;
import com.objectcomputing.checkins.services.memberprofile.MemberProfileResponseDTO;
import com.objectcomputing.checkins.services.memberprofile.MemberProfileServices;
import io.micronaut.core.annotation.Nullable;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.*;
import io.micronaut.scheduling.TaskExecutors;
import io.micronaut.scheduling.annotation.ExecuteOn;
Expand All @@ -15,6 +18,7 @@
import reactor.core.publisher.Mono;

import java.net.URI;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
Expand All @@ -26,9 +30,11 @@
public class GuildController {

private final GuildServices guildService;
private final MemberProfileServices profileServices;

public GuildController(GuildServices guildService) {
public GuildController(GuildServices guildService, MemberProfileServices profileServices) {
this.guildService = guildService;
this.profileServices = profileServices;
}

/**
Expand Down Expand Up @@ -57,6 +63,32 @@ public Mono<HttpResponse<GuildResponseDTO>> readGuild(@NotNull UUID id) {
.map(HttpResponse::ok);
}

/**
* Get guild leader based on guild id
*
* @param id of guild
* @return {@link GuildResponseDTO guild leader matching id}
*/

@Get("/leaders/{id}")
public Mono<HttpResponse<Set<MemberProfile>>> getGuildLeaders(@NotNull UUID id) {
return Mono.fromCallable(() -> {
GuildResponseDTO guild = guildService.read(id);
List<GuildMemberResponseDTO> members = guild.getGuildMembers();
Set<MemberProfile> newLeaders = new HashSet<>();
for (GuildMemberResponseDTO member : members) {
if (member.isLead()) {
MemberProfile memberProfile = profileServices.getById(member.getMemberId());
if (memberProfile != null) {
newLeaders.add(memberProfile);
}
}
}
return newLeaders;
})
.map(HttpResponse::ok);
}

/**
* Find guild(s) given a combination of the following parameters
*
Expand Down
10 changes: 9 additions & 1 deletion web-ui/src/api/guild.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { resolve } from './api.js';

const guildUrl = `/services/guilds`;
const guildMemberUrl = `/services/guilds/members`;
const guildMemberUrl = `${guildUrl}/members`;
const guildLeadersUrl = `${guildUrl}/leaders`;

export const getAllGuildMembers = async cookie => {
return resolve({
Expand All @@ -20,6 +21,13 @@ export const getMembersByGuild = async (id, cookie) => {
});
};

export const getGuildLeaders = async (id, cookie) => {
return resolve({
url: `${guildLeadersUrl}/${id}`,
headers: { 'X-CSRF-Header': cookie, Accept: 'application/json' }
});
}

export const updateGuild = async (guild, cookie) => {
return resolve({
method: 'PUT',
Expand Down
36 changes: 24 additions & 12 deletions web-ui/src/api/notifications.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { resolve } from './api.js';
import { getMember } from "./member.js";

const emailNotificationURL = '/services/email-notifications';
const emailUrl = '/services/email';
const emailURL = '/services/email';
const testEmailURL = import.meta.env.VITE_APP_API_URL
? import.meta.env.VITE_APP_URL + '/feedback/submit?request='
: 'http://localhost:8080/feedback/submit?request=';
Expand Down Expand Up @@ -33,7 +33,7 @@ export const sendReminderNotification = async (
export const sendEmail = async (subject, content, html, recipients, cookie) => {
return resolve({
method: 'POST',
url: emailUrl,
url: emailURL,
data: {
subject: subject,
content: content,
Expand All @@ -52,13 +52,13 @@ export const emailPDLAssignment = async (member, cookie) => {
if (member.pdlId && member.lastName && member.firstName && member.workEmail) {
let res = await getMember(member.pdlId)
let pdl = res.payload?.data && !res.error
? res.payload.data
: null;
? res.payload.data
: null;
if (pdl?.workEmail) {
await sendEmail("You have been assigned as the PDL of " + member.firstName + " " + member.lastName,
member.firstName + " " + member.lastName +
" will now report to you as their PDL. Please engage with them: " + member.workEmail,
false, [pdl.workEmail], cookie)
member.firstName + " " + member.lastName +
" will now report to you as their PDL. Please engage with them: " + member.workEmail,
false, [pdl.workEmail], cookie)
} else {
console.warn("Unable to send email regarding " + member.firstName + " " + member.lastName + "'s PDL update as the PDL was unable to be pulled up correctly")
}
Expand All @@ -70,17 +70,29 @@ export const emailSupervisorAssignment = async (member, cookie) => {
if (member.supervisorid && member.lastName && member.firstName && member.workEmail) {
let res = await getMember(member.supervisorid)
let supervisor = res.payload?.data && !res.error
? res.payload.data
: null;
? res.payload.data
: null;
if (supervisor?.workEmail) {
await sendEmail("You have been assigned as the supervisor of " + member.firstName + " " + member.lastName,
member.firstName + " " + member.lastName +
" will now report to you as their supervisor. Please engage with them: " + member.workEmail,
false, [supervisor.workEmail], cookie)
member.firstName + " " + member.lastName +
" will now report to you as their supervisor. Please engage with them: " + member.workEmail,
false, [supervisor.workEmail], cookie)
} else {
console.warn("Unable to send email regarding " + member.firstName + " " + member.lastName + "'s supervisor update as the supervisor was unable to be pulled up correctly")
}
} else {
console.warn("Unable to send email regarding as member was not valid and missing required fields", member)
}
}
export const emailGuildLeaders = async (members, guild, cookie) => {
members.forEach(member => {
if (!member.workEmail || !guild?.name) {
console.warn("Unable to send guild leader email as member is missing required fields", member);
return;
}

const subject = `You have been assigned as a guild leader of ${guild.name}`;
const body = `Congratulations, you have been assigned as a guild leader of ${guild.name}`;
sendEmail(subject, body, false, [member.workEmail], cookie);
});
}
12 changes: 8 additions & 4 deletions web-ui/src/components/admin/users/Users.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,11 +185,15 @@ const Users = () => {
type: UPDATE_MEMBER_PROFILES,
payload: [...memberProfiles, data]
});
if (member.pdlId) {
emailPDLAssignment(member, csrf).then()
try {
member.pdlId && await emailPDLAssignment(member, csrf)
} catch (e) {
console.error("Unable to send PDL assignment email", e)
}
if (member.supervisorid) {
emailSupervisorAssignment(member, csrf).then()
try {
member.supervisorid && await emailSupervisorAssignment(member, csrf)
} catch (e) {
console.error("Unable to send supervisor assignment email", e)
}
}
handleClose();
Expand Down
47 changes: 28 additions & 19 deletions web-ui/src/components/guild-results/GuildResults.jsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import PropTypes from 'prop-types';
import React, { useContext, useEffect, useState } from 'react';
import React, { useContext, useState } from 'react';
import GroupIcon from '@mui/icons-material/Group';
import { Button, TextField } from '@mui/material';
import { styled } from '@mui/material/styles';

import { createGuild } from '../../api/guild';
import { createGuild, getGuildLeaders } from '../../api/guild';
import { ADD_GUILD } from '../../context/actions';
import { AppContext } from '../../context/AppContext';
import AddGuildModal from './EditGuildModal';
import GuildSummaryCard from './GuildSummaryCard';
import SkeletonLoader from '../skeleton_loader/SkeletonLoader';
import { useQueryParameters } from '../../helpers/query-parameters';
import './GuildResults.css';
import { emailGuildLeaders } from "../../api/notifications.js";

const PREFIX = 'GuildResults';
const classes = {
Expand Down Expand Up @@ -93,13 +94,21 @@ const GuildResults = () => {
onClose={handleClose}
onSave={async guild => {
if (csrf) {
let res = await createGuild(guild, csrf);
let data =
res.payload && res.payload.data && !res.error
? res.payload.data
: null;
const res = await createGuild(guild, csrf);
const data = res.payload?.data && !res.error
? res.payload.data
: null;
if (data) {
dispatch({ type: ADD_GUILD, payload: data });
const resGuildLeader = await getGuildLeaders(data.id, csrf);
const guildLeaders = resGuildLeader.payload?.data && !resGuildLeader.error
? resGuildLeader.payload.data
: null;
try {
guildLeaders && await emailGuildLeaders(guildLeaders, data, csrf).then();
} catch (e) {
console.error("Unable to email guild leader assignment(s)", e)
}
}
handleClose();
}
Expand All @@ -113,19 +122,19 @@ const GuildResults = () => {
<div className="guilds">
{guilds?.length
? guilds?.map((guild, index) =>
guild.name.toLowerCase().includes(searchText.toLowerCase()) ? (
<GuildSummaryCard
key={`guild-summary-${guild.id}`}
index={index}
guild={guild}
isOpen={guild.id === openedGuildId}
onGuildSelect={setOpenedGuildId}
/>
) : null
)
guild.name.toLowerCase().includes(searchText.toLowerCase()) ? (
<GuildSummaryCard
key={`guild-summary-${guild.id}`}
index={index}
guild={guild}
isOpen={guild.id === openedGuildId}
onGuildSelect={setOpenedGuildId}
/>
) : null
)
: Array.from({ length: 20 }).map((_, index) => (
<SkeletonLoader key={index} type="guild" />
))}
<SkeletonLoader key={index} type="guild" />
))}
</div>
</Root>
);
Expand Down
31 changes: 25 additions & 6 deletions web-ui/src/components/guild-results/GuildSummaryCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import {
Tooltip
} from '@mui/material';
import PropTypes from 'prop-types';
import { deleteGuild, updateGuild } from '../../api/guild.js';
import { deleteGuild, getGuildLeaders, updateGuild } from '../../api/guild.js';
import SplitButton from '../split-button/SplitButton';
import { emailGuildLeaders } from "../../api/notifications.js";

const PREFIX = 'GuildSummaryCard';
const classes = {
Expand Down Expand Up @@ -263,18 +264,36 @@ const GuildSummaryCard = ({ guild, index, isOpen, onGuildSelect }) => {
open={open}
onClose={handleClose}
onSave={async editedGuild => {
let res = await updateGuild(editedGuild, csrf);
let data =
res.payload && res.payload.data && !res.error
? res.payload.data
: null;
const resGetOldGuildLeader = await getGuildLeaders(editedGuild.id, csrf);
const oldGuildLeaders = resGetOldGuildLeader.payload?.data && !resGetOldGuildLeader.error
? resGetOldGuildLeader.payload.data
: null;
const res = await updateGuild(editedGuild, csrf);
const data = res.payload?.data && !res.error
? res.payload.data
: null;
if (data) {
const copy = [...guilds];
copy[index] = data;
dispatch({
type: UPDATE_GUILDS,
payload: copy
});
const resGetGuildLeaders = await getGuildLeaders(editedGuild.id, csrf);
const guildLeaders = resGetGuildLeaders.payload?.data && !resGetGuildLeaders.error
? resGetGuildLeaders.payload.data
: null;
if (guildLeaders && oldGuildLeaders) {
// Filter out the new leaders that were not in the old leaders
const newLeaders = guildLeaders.filter(
newLeader => !oldGuildLeaders.some(oldLeader => oldLeader.id === newLeader.id)
);
try {
newLeaders.length > 0 && await emailGuildLeaders(newLeaders, guild, csrf).then();
} catch (e) {
console.error("Unable to email guild leader assignment(s)", e)
}
}
}
}}
headerText="Edit Your Guild"
Expand Down
17 changes: 11 additions & 6 deletions web-ui/src/components/member-directory/AdminMemberCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -217,8 +217,8 @@ const AdminMemberCard = ({ member, index }) => {
onSave={async member => {
const resGetMember = await getMember(member.id, csrf);
const oldMember = resGetMember.payload?.data && !resGetMember.error
? resGetMember.payload.data
: null;
? resGetMember.payload.data
: null;
const res = await updateMember(member, csrf);
const data =
res.payload?.data && !res.error
Expand All @@ -234,11 +234,16 @@ const AdminMemberCard = ({ member, index }) => {
type: UPDATE_MEMBER_PROFILES,
payload: copy
});
if (oldMember && oldMember.pdlId !== member.pdlId) {
emailPDLAssignment(member, csrf).then()
try {
oldMember.pdlId !== member.pdlId && await emailPDLAssignment(member, csrf);
} catch (e) {
console.error("Unable to email PDL assignment", e)
}
if (oldMember && oldMember.supervisorid !== member.supervisorid) {
emailSupervisorAssignment(member, csrf).then()
try {
oldMember.supervisorid !== member.supervisorid && await emailSupervisorAssignment(member, csrf);
} catch {
console.error("Unable to email supervisor assignment", e)

}
handleClose();
}
Expand Down