Skip to content

Commit

Permalink
feat(imap-server): add imap server
Browse files Browse the repository at this point in the history
  • Loading branch information
Mihoub2 committed Oct 2, 2024
1 parent 6c85106 commit ca679c1
Show file tree
Hide file tree
Showing 25 changed files with 466 additions and 95 deletions.
Binary file modified bun.lockb
Binary file not shown.
3 changes: 1 addition & 2 deletions client/main.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { ReactNode } from "react";
import ReactDOM from "react-dom/client";

import { BrowserRouter, Link } from "react-router-dom";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
Expand Down Expand Up @@ -47,8 +46,8 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
}}
/>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
<Router />
<ReactQueryDevtools initialIsOpen={false} />
</QueryClientProvider>
</DSFRConfig>
</BrowserRouter>
Expand Down
5 changes: 2 additions & 3 deletions client/src/api/send-mail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ function EmailSender({ contribution, refetch }: EmailSenderProps) {
collectionName: collectionName,
to: contribution.email,
name: contribution.name,
subject: `Réponse à votre contribution, référence ${contribution.id}`,
subject: `Réponse à votre contribution, référence ${collectionName}-${contribution.id}`,
userResponse: formattedResponse,
selectedProfile,
message: contribution.message,
Expand Down Expand Up @@ -117,8 +117,7 @@ function EmailSender({ contribution, refetch }: EmailSenderProps) {
<Row gutters>
<Col>
<p>
De:{" "}
{`${selectedProfile} de l'équipe scanR <scanr@recherche.gouv.fr>`}
De: {`${selectedProfile} de l'équipe scanR <support@scanr.fr>`}
</p>
<p>À: {`${contribution?.name} <${contribution?.email}>`}</p>
<p>
Expand Down
8 changes: 2 additions & 6 deletions client/src/components/items/contribution-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const ContributionItem: React.FC<ContributionItemProps> = ({
highlightedQuery,
refetch,
allTags,
url,
}) => {
const [copiedId, setCopiedId] = useState<string | null>(null);

Expand All @@ -41,11 +42,6 @@ const ContributionItem: React.FC<ContributionItemProps> = ({
{data.tags.join(", ")}
</Badge>
)}
{data?.fromSubApp && (
<Badge size="sm" color="green-menthe" className="fr-mr-1w fr-mb-1w">
{data.fromSubApp}
</Badge>
)}
{data?.status && (
<Badge
size="sm"
Expand Down Expand Up @@ -109,7 +105,7 @@ const ContributionItem: React.FC<ContributionItemProps> = ({
refetch={refetch}
allTags={allTags}
/>
<StaffActions refetch={refetch} data={data} />
<StaffActions url={url} refetch={refetch} data={data} />
</Col>
</>
);
Expand Down
169 changes: 156 additions & 13 deletions client/src/components/items/staff-action.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,171 @@
import { Col, Text } from "@dataesr/dsfr-plus";
import { Col, Text, Toggle } from "@dataesr/dsfr-plus";
import EmailSender from "../../api/send-mail";
import type { Contribution } from "../../types";
import { useLocation } from "react-router-dom";

const StaffActions = ({ data, refetch }: { data: Contribution; refetch }) => {
import { useMutation, useQueryClient } from "@tanstack/react-query";
import "./styles.scss";
import { useState, useEffect } from "react";
const StaffActions = ({
data,
refetch,
url,
}: {
data: Contribution;
refetch: () => void;
url: string;
}) => {
const location = useLocation();

let baseUrl = "contacts";
if (location?.pathname?.includes("scanr-contributionpage")) {
baseUrl = "contribute";
} else if (location?.pathname?.includes("removeuser")) {
baseUrl = "remove-user";
} else if (location?.pathname?.includes("namechange")) {
baseUrl = "update-user-data";
} else if (location?.pathname?.includes("apioperations")) {
baseUrl = "production";
}

const contributorClassName = location.pathname.includes("contributionpage")
? "staffSide"
: "staffSideContact";

const displayedDates = new Set<string>();
const queryClient = useQueryClient();

const [isAllRead, setIsAllRead] = useState(() => {
return (
data?.threads.every((thread) =>
thread.responses.every((response) => response.read)
) || false
);
});
const mutation = useMutation(
async (isRead: boolean) => {
return fetch(`api/${baseUrl}/${data.id}`, {
method: "PATCH",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
threads: data.threads.map((thread) => ({
...thread,
responses: thread.responses.map((response) => ({
...response,
read: isRead,
})),
})),
}),
});
},
{
onMutate: async (isRead: boolean) => {
await queryClient.cancelQueries([url]);

const previousData = queryClient.getQueryData<{ data: Contribution[] }>(
[url]
) || { data: [] };

if (Array.isArray(previousData.data)) {
const updatedThreads = previousData.data.map((thread) => ({
...thread,
responses: Array.isArray(thread.responses)
? thread.responses.map((response) => ({
...response,
read: isRead,
}))
: [],
}));

queryClient.setQueryData([url], {
...previousData,
data: updatedThreads,
});
} else {
console.error("previousData.data n'est pas un tableau", previousData);
}

return { previousData };
},

onSuccess: () => {
queryClient.invalidateQueries([url]);
const updatedData = queryClient.getQueryData<{ data: Contribution[] }>([
url,
]);

if (updatedData?.data) {
const allRead = updatedData.data.every((thread) =>
thread.responses.every((response) => response.read)
);
setIsAllRead(allRead);
}
},
}
);

const handleToggleChange = () => {
const newState = !isAllRead;
setIsAllRead(newState);
mutation.mutate(newState);
};

useEffect(() => {
const allRead =
data?.threads.every((thread) =>
thread.responses.every((response) => response.read)
) || false;
setIsAllRead(allRead);
}, [data]);

return (
<>
{data?.threads?.length > 0 && (
<Col className={contributorClassName}>
{data.threads.map((thread) =>
thread.responses.map((response, index) => (
<Text size="sm" key={index}>
Réponse apportée par {response.team.join(", ")} le{" "}
{new Date(response.timestamp).toLocaleDateString()}
{" à "}
{new Date(response.timestamp).toLocaleTimeString()} :<br />
{response.responseMessage}
</Text>
))
<Toggle
label="Marquer toutes les réponses comme lues"
checked={isAllRead}
onChange={handleToggleChange}
/>

{data.threads.map((thread, threadIndex) =>
thread.responses.map((response, index) => {
const responseDate = new Date(
response.timestamp
).toLocaleDateString();
const responseTime = new Date(
response.timestamp
).toLocaleTimeString();

if (displayedDates.has(responseDate + responseTime)) {
return null;
}

displayedDates.add(responseDate + responseTime);

const responder = response.team.includes("user")
? data.name
: response.team.join(", ");

return (
<div
key={`${threadIndex}-${index}`}
className="response-container"
>
<Text
size="sm"
className={
response.team.includes("user") ? "user-side" : "staffSide"
}
>
Réponse apportée par {responder} le {responseDate} à{" "}
{responseTime} :<br />
{response.responseMessage}
</Text>
</div>
);
})
)}
</Col>
)}
Expand Down
9 changes: 7 additions & 2 deletions client/src/components/items/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,22 @@
padding: 15px;
}

.user-side {
text-align: left;
background-color: var(--background-alt-pink-macaron);
border-radius: 20px;
padding: 20px;
}

.staffSide {
text-align: right;
background-color: var(--background-alt-pink-macaron);
border-radius: 20px;
padding: 20px;
margin-left: 140px;
}

.staffSideContact {
text-align: right;
background-color: var(--background-alt-blue-cumulus);
border-radius: 20px;
padding: 20px;
margin-left: 140px;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,7 @@ const ContactAndContributionPage: React.FC<ContributionPageProps> = ({
)}
refetch={refetch}
highlightedQuery={highlightedQuery}
url={url}
/>
)}
</Col>
Expand Down
2 changes: 2 additions & 0 deletions client/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface ContributionItemProps {
highlightedQuery: string;
refetch: () => void;
allTags: string[];
url: string;
}
export interface ChangeNameProps {
id: string;
Expand Down Expand Up @@ -89,6 +90,7 @@ export interface Thread {
}

export interface Response {
read: boolean;
responseMessage: string;
timestamp: string;
team?: string[];
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,17 @@
"client"
],
"devDependencies": {
"@types/imapflow": "^1.0.19",
"@types/mailparser": "^3.4.4",
"concurrently": "^8.2.2"
},
"scripts": {
"start:client": "cd client && npm run dev",
"start:server": "cd server && bun run --watch server.ts",
"start": "concurrently \"npm run start:client\" \"bun run start:server\""
},
"dependencies": {
"imapflow": "^1.0.164",
"mailparser": "^3.7.1"
}
}
}
4 changes: 3 additions & 1 deletion server/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import removeUserRoutes from "./routes/remove-user";
import updateUserDataRoutes from "./routes/update-user-data";
import contactsRoutes from "./routes/contacts";
import sendMail from "./routes/reply/replyRoutes";
import { fetchEmails } from "./routes/receive-email";
// import connectToImapServer from "./routes/receive-email";

dotenv.config();

const app = new Elysia();
fetchEmails();
app
.use(
cors({
Expand All @@ -37,7 +39,7 @@ app
{ name: "Contacts", description: "Gestion des contacts" },
{ name: "Contributions", description: "Gestion des contributions" },
{ name: "Productions", description: "Gestion des productions" },
{ name: "Envoi de mails", description: "Gestion de emails" },
{ name: "Envoi de mails", description: "Envoi de mails" },
{
name: "Supressions de profil",
description: "Gestion des demandes supression de profil",
Expand Down
2 changes: 1 addition & 1 deletion server/libs/mongo.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { MongoClient } from "mongodb";

const mongoUri = process.env.MONGO_URI || "";
const client = new MongoClient(mongoUri);
const client = new MongoClient(mongoUri, { directConnection: true });
const db = client.db("ticket-office-api");

export default db;
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"@babel/preset-env": "^7.25.3",
"@types/imap-simple": "^4.2.9",
"@types/node": "^22.4.1",
"@types/quoted-printable": "^1.0.2",
"@types/swagger-jsdoc": "^6.0.4",
"@types/swagger-ui-express": "^4.1.6",
"@types/uuid": "^10.0.0",
Expand Down
14 changes: 13 additions & 1 deletion server/routes/contacts/patch/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ contactPutRoutes.patch(
}
}

if (body.threads) {
body.threads = body.threads.map((thread) => {
thread.responses = thread.responses?.map((response) => {
if (response.read === false) {
response.read = true;
}
return response;
});
return thread;
});
}

const { acknowledged } = await db
.collection("contacts")
.updateOne({ id }, { $set: { ...body, updatedAt: new Date() } });
Expand Down Expand Up @@ -63,7 +75,7 @@ contactPutRoutes.patch(
detail: {
summary: "Modifier une contribution via formulaire de contact par ID",
description:
"Cette route permet de mettre à jour une contribution spécifique via l'ID fourni. Elle permet de modifier le statut, l'idref, d'ajouter la personne modifiant dans l'équipe, et de mettre à jour la date de traitement et de modification.",
"Cette route permet de mettre à jour une contribution spécifique via l'ID fourni. Elle permet de modifier le statut, d'ajouter la personne modifiant dans l'équipe, et de mettre à jour la date de traitement et de modification.",
tags: ["Contacts"],
},
}
Expand Down
Loading

0 comments on commit ca679c1

Please sign in to comment.