Skip to content

Commit

Permalink
Merge pull request #333 from ReseauEntourage/epic/EN-7374-Messagerie
Browse files Browse the repository at this point in the history
WIP - Epic/messagerie
  • Loading branch information
guillobits authored Oct 10, 2024
2 parents 70647a9 + 6ce27f7 commit b7b484f
Show file tree
Hide file tree
Showing 73 changed files with 1,835 additions and 231 deletions.
6 changes: 4 additions & 2 deletions assets/icons/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import Location from './location.svg';
import LogOut from './log-out.svg';
import LogoEntourage from './logo-entourage.svg';
import Menu from './menu.svg';
import Messages from './messages.svg';
import More from './more.svg';
import OrienterCarteSolidaire from './orienter-carte-solidaire.svg';
import OrienterSablier from './orienter-sablier.svg';
Expand Down Expand Up @@ -165,5 +166,6 @@ UserEmpty,
User,
Warning,
Whatsapp,
Youtube
};
Youtube,
Messages
};
2 changes: 1 addition & 1 deletion assets/icons/illu-conversation.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions assets/icons/messages.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 1 addition & 28 deletions cypress/e2e/test/user/candidat.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,33 +112,6 @@ describe('Candidat', () => {
cy.intercept('/message/internal', {}).as('postInternalMessage');
});

it("should open a user's public profile and contact him", () => {
cy.fixture('public-profile-res').then((userProfile) => {
cy.visit(`/backoffice/profile/${userProfile.id}`);
cy.get('[data-testid="app-splash-screen"]').should('not.visible');
cy.url().should('include', userProfile.id);
});

cy.get('[data-testid="form-contact-internal-message-subject"]')
.click()
.find('.Select__option')
.contains('Conseils')
.click();

cy.get('[data-testid="form-contact-internal-message-message"]')
.scrollIntoView()
.type('test');

cy.get('[data-testid="form-confirm-form-contact-internal-message"]')
.scrollIntoView()
.click();

cy.get('[data-testid="profile-contact-form-confirm"]').should(
'contain',
'Votre message a été envoyé'
);
});

it('should open backoffice public offers', () => {
// to be done: use automatic generation and not static data
cy.fixture('auth-current-candidat-onboarding3-res').then((user) => {
Expand Down Expand Up @@ -382,7 +355,7 @@ describe('Candidat', () => {
cy.wait('@changePwd');

// to be done with automatic generation
let newHelpList = [
const newHelpList = [
{
id: '352a7dde-c4ad-410f-86cf-506cdc9eb624',
name: 'cv',
Expand Down
23 changes: 23 additions & 0 deletions src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ContactCompany,
ContactContactUs,
ContactNewsletter,
ConversationReportDto,
CV,
ExternalCv,
ExternalMessage,
Expand Down Expand Up @@ -566,6 +567,28 @@ export class APIHandler {
return this.post(`/message/internal/${internalMessageId}/send`, {});
}

// ////////////
// messaging //
// ////////////
getConversations(query?: string): Promise<AxiosResponse> {
return this.get(`/messaging/conversations?query=${encodeURI(query || '')}`);
}

getConversationById(conversationId: string): Promise<AxiosResponse> {
return this.get(`/messaging/conversations/${conversationId}`);
}

postMessage(params: { content: string }): Promise<AxiosResponse> {
return this.post('/messaging/messages', params);
}

reportMessage(conversationId: string, params: ConversationReportDto) {
return this.post(
`/messaging/conversations/${conversationId}/report`,
params
);
}

/// /////////////////
// read documents //
/// /////////////////
Expand Down
36 changes: 36 additions & 0 deletions src/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export const APIRoutes = {
MESSAGE: 'message',
READ_DOCUMENTS: 'readDocuments',
EXTERNAL_CVS: 'external-cv',
MESSAGING: 'messaging',
} as const;

export type APIRoute = (typeof APIRoutes)[keyof typeof APIRoutes];
Expand Down Expand Up @@ -118,6 +119,11 @@ export type UserReportDto = {
comment: string;
};

export type ConversationReportDto = {
reason: string;
comment: string;
};

export type User = {
coach: User;
id: string;
Expand Down Expand Up @@ -603,6 +609,36 @@ export type InternalMessage = {
id?: string;
};

export type Message = {
id: string;
content: string;
authorId: string;
createdAt: string;
updatedAt: string;
conversationId: string;
author: User;
};

export type ConversationParticipants = (User & {
ConversationParticipant: {
id: string;
seenAt: string;
};
})[];

export type Conversation = {
id: string;
createdAt?: string;
updatedAt?: string;
messages: Message[];
participants: ConversationParticipants;
seenAt?: string;
};

export type MessageWithConversation = Message & {
conversation: Conversation;
};

export type PublicProfile = {
id: string;
firstName: string;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { OpportunitiesContainer } from 'src/components/backoffice/opportunities/
import { AdminOpportunitiesList } from 'src/components/backoffice/opportunities/OpportunitiesContainer/OpportunitiesList/AdminOpportunitiesList';
import { AdminOpportunityDetailsContainer } from 'src/components/backoffice/opportunities/OpportunitiesContainer/OpportunityDetails/AdminOpportunityDetails/AdminOpportunityDetailsContainer';
import { OpportunityError } from 'src/components/backoffice/opportunities/OpportunityError';
import { SearchBar } from 'src/components/filters/SearchBar';
import { SearchBar } from 'src/components/filters/SearchBar/SearchBar';
import { formAddExternalOpportunityAsAdmin } from 'src/components/forms/schemas/formAddExternalOpportunity';
import { HeaderBackoffice } from 'src/components/headers/HeaderBackoffice';
import { openModal } from 'src/components/modals/Modal';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Api } from 'src/api';
import { UserWithUserCandidate } from 'src/api/types';
import { LoadingScreen } from 'src/components/backoffice/LoadingScreen';
import { AdminCreationButtons } from 'src/components/backoffice/admin/AdminCreationButtons';
import { SearchBar } from 'src/components/filters/SearchBar';
import { SearchBar } from 'src/components/filters/SearchBar/SearchBar';
import { HeaderBackoffice } from 'src/components/headers/HeaderBackoffice';
import { BackToTop, Button, Section, Typography } from 'src/components/utils';
import { ContainerWithTextCentered } from 'src/components/utils/Containers';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { Api } from 'src/api';
import { Organization as OrganizationType } from 'src/api/types';
import { LoadingScreen } from 'src/components/backoffice/LoadingScreen';
import { AdminCreationButtons } from 'src/components/backoffice/admin/AdminCreationButtons';
import { SearchBar } from 'src/components/filters/SearchBar';
import { SearchBar } from 'src/components/filters/SearchBar/SearchBar';
import { HeaderBackoffice } from 'src/components/headers/HeaderBackoffice';
import {
Section,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { NoOpportunities } from 'src/components/backoffice/opportunities/Opportu
import { CandidateOpportunitiesList } from 'src/components/backoffice/opportunities/OpportunitiesContainer/OpportunitiesList/CandidateOpportunitiesList';
import { CandidateOpportunityDetailsContainer } from 'src/components/backoffice/opportunities/OpportunitiesContainer/OpportunityDetails/CandidateOpportunityDetails';
import { OpportunityError } from 'src/components/backoffice/opportunities/OpportunityError';
import { SearchBar } from 'src/components/filters/SearchBar';
import { SearchBar } from 'src/components/filters/SearchBar/SearchBar';
import { HeaderBackoffice } from 'src/components/headers/HeaderBackoffice';
import { openModal } from 'src/components/modals/Modal';
import { ModalExternalOffer } from 'src/components/modals/Modal/ModalGeneric/OfferModals/ModalOffer';
Expand Down
2 changes: 2 additions & 0 deletions src/components/backoffice/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { DashboardAlertWhatsappCoach } from './DashboardAlertWhatsappCoach/DashboardAlertWhatsappCoach';
import { DashboardAvailabilityCard } from './DashboardAvailabilityCard';
import { DashboardLinkedUserCard } from './DashboardLinkedUserCard';
import { DashboardMessagingConversation } from './DashboardMessagingConversation';
import { DashboardOpportunitiesCard } from './DashboardOpportunitiesCard';
import { DashboardProfileCard } from './DashboardProfileCard';
import { DashboardReadDocumentsCard } from './DashboardReadDocumentsCard';
Expand Down Expand Up @@ -63,6 +64,7 @@ export const Dashboard = () => {
) && (
<>
<DashboardReadDocumentsCard />
<DashboardMessagingConversation />
<DashboardStepsCard />
<DashboardOpportunitiesCard />
<DashboardRecommendationsCard />
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import styled from 'styled-components';
import { COLORS } from 'src/constants/styles';

export const StyledContainer = styled.div`
display: flex;
background: ${COLORS.hoverBlue};
padding: 18px 20px;
gap: 15px;
flex: 1;
cursor: pointer;
`;

export const StyledConversationParticipants = styled.div`
display: flex;
gap: 10px;
align-items: center;
font-weight: bold;
width: 275px;
`;

export const StyledMessagePreview = styled.div<{ hasSeen: boolean }>`
display: flex;
align-items: center;
flex: auto;
${({ hasSeen }) => !hasSeen && `font-weight: 700;`}
`;

export const StyledMessageDate = styled.div`
display: flex;
align-items: center;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import moment from 'moment';
import { useRouter } from 'next/router';
import React from 'react';
import { useSelector } from 'react-redux';
import { Conversation, User } from 'src/api/types';
import { ImgProfile } from 'src/components/utils';
import { selectCurrentUserId } from 'src/use-cases/current-user';
import {
StyledContainer,
StyledConversationParticipants,
StyledMessageDate,
StyledMessagePreview,
} from './ConversationItem.styles';

export interface ConversationItemProps {
conversation: Conversation;
}

export const ConversationItem = ({ conversation }: ConversationItemProps) => {
const router = useRouter();
const currentUserId = useSelector(selectCurrentUserId);
const addresee = conversation.participants.find(
(participant) => participant.id !== currentUserId
) as User;
const userParticipantConversation = conversation.participants.find(
(participant) => participant.id === currentUserId
);
const lastMessage = conversation.messages[conversation.messages.length - 1];
const seenAt = userParticipantConversation?.ConversationParticipant.seenAt;
const userHasSeenConversation =
seenAt && moment(seenAt).isSameOrAfter(lastMessage.createdAt);

const openConversation = () => {
router.push(`/backoffice/messaging?userId=${addresee.id}`);
};

return (
<StyledContainer onClick={openConversation}>
<StyledConversationParticipants>
<ImgProfile user={addresee} size={25} />
{`${addresee.firstName} ${addresee.lastName}`}
</StyledConversationParticipants>
<StyledMessagePreview hasSeen={userHasSeenConversation}>
{conversation.messages[0].content}
</StyledMessagePreview>
<StyledMessageDate>
{moment(conversation.messages[0].createdAt).format('DD/MM/YYYY')}
</StyledMessageDate>
</StyledContainer>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import styled from 'styled-components';

export const CardContent = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 20px;
`;

export const ConversationList = styled.div`
width: 100%;
display: flex;
flex-direction: column;
gap: 20px;
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useRouter } from 'next/router';
import React, { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Button, Card } from 'src/components/utils';
import { messagingActions, selectConversations } from 'src/use-cases/messaging';
import { ConversationItem } from './ConversationItem/ConversationItem';
import {
CardContent,
ConversationList,
} from './DashboardMessagingConversation.styles';

export const DashboardMessagingConversation = () => {
const router = useRouter();
const dispatch = useDispatch();
const conversations = useSelector(selectConversations);

useEffect(() => {
dispatch(messagingActions.getConversationsRequested());
}, [dispatch]);

if (!conversations || conversations.length === 0) {
return null;
}

const openMessaging = () => {
router.push('/backoffice/messaging');
};

return (
<Card title="Mes derniers messages">
<CardContent>
<ConversationList>
{conversations &&
conversations.slice(0, 3).map((conversation) => {
return <ConversationItem conversation={conversation} />;
})}
</ConversationList>
<Button onClick={openMessaging}>Accéder à la messagerie</Button>
</CardContent>
</Card>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './DashboardMessagingConversation';
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useRouter } from 'next/router';
import React, { useMemo } from 'react';
import { DirectoryList } from '../DirectoryList';
import { useDirectoryQueryParams } from '../useDirectoryQueryParams';
import { SearchBar } from 'src/components/filters/SearchBar';
import { SearchBar } from 'src/components/filters/SearchBar/SearchBar';
import { Button, Section } from 'src/components/utils';
import { BUSINESS_LINES, DirectoryFilters } from 'src/constants';
import { DEPARTMENTS_FILTERS } from 'src/constants/departements';
Expand Down
Loading

0 comments on commit b7b484f

Please sign in to comment.