Skip to content

Commit

Permalink
feat: introduit auth-store zustand
Browse files Browse the repository at this point in the history
  • Loading branch information
ocruze committed Nov 20, 2023
1 parent 93d5a8b commit 0d232b7
Show file tree
Hide file tree
Showing 26 changed files with 249 additions and 98 deletions.
25 changes: 16 additions & 9 deletions assets/App.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,31 @@
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import { FC } from "react";

import ErrorBoundary from "./components/Utils/ErrorBoundary";
import { UserContextProvider } from "./contexts/UserContext";
import RouterRenderer from "./router/RouterRenderer";
import { RouteProvider } from "./router/router";
import { FC } from "react";
import { useAuthStore } from "./stores/AuthStore";

const userFromTwig = (document.getElementById("user") as HTMLDivElement)?.dataset?.user ?? null;
const user = userFromTwig === null ? null : JSON.parse(userFromTwig);
useAuthStore.getState().setUser(user);

const queryClient = new QueryClient();

const App: FC = () => {
const authStore = useAuthStore();

console.log("authStore.sessionExpired", authStore.sessionExpired);

return (
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<UserContextProvider>
<RouteProvider>
<ErrorBoundary>
<RouterRenderer />
</ErrorBoundary>
</RouteProvider>
</UserContextProvider>
<RouteProvider>
<ErrorBoundary>
<RouterRenderer />
</ErrorBoundary>
</RouteProvider>
</QueryClientProvider>
);
};
Expand Down
4 changes: 2 additions & 2 deletions assets/components/Layout/AppHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import Header, { HeaderProps } from "@codegouvfr/react-dsfr/Header";
import { MainNavigationProps } from "@codegouvfr/react-dsfr/MainNavigation/MainNavigation";
import { FC, ReactNode } from "react";

import useUser from "../../hooks/useUser";
// import { useLang } from "../../i18n/i18n";
import SymfonyRouting from "../../modules/Routing";
import { routes } from "../../router/router";
import { useAuthStore } from "../../stores/AuthStore";
// import LanguageSelector from "../Utils/LanguageSelector";

import "../../sass/components/header.scss";
Expand All @@ -16,7 +16,7 @@ type AppHeaderProps = {
navItems?: NavigationProps;
};
const AppHeader: FC<AppHeaderProps> = ({ navItems = [] }) => {
const { user } = useUser();
const { user } = useAuthStore();
// const { lang, setLang } = useLang();

const quickAccessItems: (HeaderProps.QuickAccessItem | ReactNode)[] = [];
Expand Down
8 changes: 7 additions & 1 deletion assets/components/Layout/AppLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ import { defaultNavItems } from "../../config/navItems";
import useDocumentTitle from "../../hooks/useDocumentTitle";
import { useTranslation } from "../../i18n/i18n";
import Translator from "../../modules/Translator";
import AlertsContainer from "../Utils/AlertsContainer";
import AppFooter from "./AppFooter";
import AppHeader, { NavigationProps } from "./AppHeader";
import SessionExpiredAlert from "../Utils/SessionExpiredAlert";

type AppLayoutProps = {
navItems?: NavigationProps;
Expand Down Expand Up @@ -50,7 +52,11 @@ const AppLayout: FC<PropsWithChildren<AppLayoutProps>> = ({ children, navItems,
{/* doit être le premier élément atteignable après le lien d'évitement (Accessibilité) : https://www.systeme-de-design.gouv.fr/elements-d-interface/composants/bandeau-d-information-importante */}
{infoBannerMsg && <Notice title={infoBannerMsg} isClosable={true} />}

<div className={fr.cx("fr-container", "fr-py-2w")}>{children}</div>
<div className={fr.cx("fr-container", "fr-py-2w")}>
<SessionExpiredAlert />
<AlertsContainer />
{children}
</div>
</main>
<AppFooter />
</>
Expand Down
22 changes: 22 additions & 0 deletions assets/components/Utils/AlertsContainer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { fr } from "@codegouvfr/react-dsfr";
import Alert from "@codegouvfr/react-dsfr/Alert";
import { FC } from "react";

import { useAlertsStore } from "../../stores/AlertsStore";

/**
* Composant qui rend les Alertes dans la vue
*/
const AlertsContainer: FC = () => {
const alerts = useAlertsStore((state) => state.alerts);

return (
<div className={fr.cx("fr-my-2v")}>
{alerts.map((alert) => (
<Alert key={alert.id} {...alert} />
))}
</div>
);
};

export default AlertsContainer;
30 changes: 30 additions & 0 deletions assets/components/Utils/SessionExpiredAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import Alert from "@codegouvfr/react-dsfr/Alert";
import { FC } from "react";

import SymfonyRouting from "../../modules/Routing";
import { useAuthStore } from "../../stores/AuthStore";

const SessionExpiredAlert: FC = () => {
const { sessionExpired } = useAuthStore();

return (
sessionExpired && (
<Alert
severity="error"
title="Session expirée"
description={
<>
Veuillez{" "}
<a href={SymfonyRouting.generate("cartesgouvfr_security_login")} rel="noreferrer" target="_blank">
vous-reconnecter
</a>{" "}
dans un nouvel onglet
</>
}
closable={true}
/>
)
);
};

export default SessionExpiredAlert;
41 changes: 0 additions & 41 deletions assets/contexts/UserContext.tsx

This file was deleted.

6 changes: 3 additions & 3 deletions assets/hooks/useDatastoreList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,16 @@ import { useQuery } from "@tanstack/react-query";
import api from "../api";
import RQKeys from "../modules/RQKeys";
import { CartesApiException } from "../modules/jsonFetch";
import { useAuthStore } from "../stores/AuthStore";
import { Datastore } from "../types/app";
import useUser from "./useUser";

export const useDatastoreList = () => {
const { user } = useUser();
const { isAuthenticated } = useAuthStore();

return useQuery<Datastore[], CartesApiException>({
queryKey: RQKeys.datastore_list(),
queryFn: ({ signal }) => api.user.getDatastoresList({ signal }),
staleTime: 300000,
enabled: !!user,
enabled: isAuthenticated(),
});
};
11 changes: 0 additions & 11 deletions assets/hooks/useUser.tsx

This file was deleted.

10 changes: 10 additions & 0 deletions assets/modules/jsonFetch.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useAuthStore } from "../stores/AuthStore";

/** doit avoir la même structure que l'erreur renvoyée par CartesApiExceptionSubscriber de Symfony */
export type CartesApiException = {
code: number;
Expand Down Expand Up @@ -46,8 +48,12 @@ export async function jsonFetch<T>(
const data = await response.json().catch(() => ({}));

if (response.ok) {
useAuthStore.getState().setSessionExpired(false);
resolve(data);
} else {
if (hasSessionExpired(data)) {
useAuthStore.getState().setSessionExpired(true);
}
reject(data);
}
} catch (error) {
Expand All @@ -60,3 +66,7 @@ export async function jsonFetch<T>(
})();
});
}

const hasSessionExpired = (error) => {
return error.code === 401 && error?.details?.controller === "App\\Controller\\Api\\ApiControllerInterface" && error?.details?.session_expired === true;
};
25 changes: 25 additions & 0 deletions assets/pages/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,37 @@ import { appRoot, useRoute } from "../router/router";

import hp from "../img/home/home.png";
import "../sass/pages/home.scss";
import Button from "@codegouvfr/react-dsfr/Button";
import { useAlertsStore } from "../stores/AlertsStore";

const Home = () => {
const { params } = useRoute();

// const { pushAlert, removeAlert } = useAlerts();
const alertsStore = useAlertsStore();

return (
<AppLayout documentTitle="Le service public des cartes et données du territoire">
<Button
onClick={() => {
const alertId = `test-alert-${Date.now()}`;
console.log("alertId", alertId);

alertsStore.pushAlert({
id: alertId,
severity: "error",
title: "Le titre de l'erreur",
description: "La description de l'erreur",
closable: true,
onClose() {
alertsStore.removeAlert(alertId);
},
});
}}
>
Test Alert
</Button>

{params?.["authentication_failed"] === 1 && (
<Alert
severity="error"
Expand Down
31 changes: 16 additions & 15 deletions assets/pages/communities/CommunityList.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,32 @@
import { FC, useEffect, useMemo, useRef, useState } from "react";
import api from "../../api";
import { useMutation, useQuery } from "@tanstack/react-query";
import LoadingText from "../../components/Utils/LoadingText";
import { fr } from "@codegouvfr/react-dsfr";
import { Table } from "@codegouvfr/react-dsfr/Table";
import Alert from "@codegouvfr/react-dsfr/Alert";
import { Pagination } from "@codegouvfr/react-dsfr/Pagination";
import Button from "@codegouvfr/react-dsfr/Button";
import Input from "@codegouvfr/react-dsfr/Input";
import { createModal } from "@codegouvfr/react-dsfr/Modal";
import Translator from "../../modules/Translator";
import { datastoreNavItems } from "../../config/datastoreNavItems";
import { Pagination } from "@codegouvfr/react-dsfr/Pagination";
import { Table } from "@codegouvfr/react-dsfr/Table";
import { useMutation, useQuery } from "@tanstack/react-query";
import { FC, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";

import api from "../../api";
import { type CommunitiesReponse } from "../../api/catalogs";
import AppLayout from "../../components/Layout/AppLayout";
import LoadingText from "../../components/Utils/LoadingText";
import Wait from "../../components/Utils/Wait";
import { datastoreNavItems } from "../../config/datastoreNavItems";
import Translator from "../../modules/Translator";
import { routes, useRoute } from "../../router/router";
import { type CommunitiesReponse } from "../../api/catalogs";
import useUser from "../../hooks/useUser";
import { createPortal } from "react-dom";
import Button from "@codegouvfr/react-dsfr/Button";
import { useAuthStore } from "../../stores/AuthStore";
import { CommunityListResponseDto } from "../../types/entrepot";
import Input from "@codegouvfr/react-dsfr/Input";
import Wait from "../../components/Utils/Wait";

const explainModal = createModal({
id: "explain-modal",
isOpenedByDefault: false,
});

const CommunityList: FC = () => {
const { user } = useUser();
const { user } = useAuthStore();
const route = useRoute();

const refMsg = useRef<HTMLTextAreaElement>(null);
Expand Down
4 changes: 2 additions & 2 deletions assets/pages/contact/Contact.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { TranslationFunction } from "i18nifty/typeUtils/TranslationFunction";

import AppLayout from "../../components/Layout/AppLayout";
import Wait from "../../components/Utils/Wait";
import useUser from "../../hooks/useUser";
import { declareComponentKeys, useTranslation, type Translations, ComponentKey } from "../../i18n/i18n";
import SymfonyRouting from "../../modules/Routing";
import { jsonFetch } from "../../modules/jsonFetch";
import { routes } from "../../router/router";
import { useAuthStore } from "../../stores/AuthStore";
import { regex } from "../../utils";

import "../../sass/components/spinner.scss";
Expand All @@ -41,7 +41,7 @@ const schema = (t: TranslationFunction<"Contact", ComponentKey>) =>

const Contact = () => {
const { t } = useTranslation({ Contact });
const { user } = useUser();
const { user } = useAuthStore();

const [isSending, setIsSending] = useState(false);
const [error, setError] = useState(null);
Expand Down
4 changes: 2 additions & 2 deletions assets/pages/dashboard/DashboardPro.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import AppLayout from "../../components/Layout/AppLayout";
import LoadingText from "../../components/Utils/LoadingText";
import { datastoreNavItems } from "../../config/datastoreNavItems";
import { useDatastoreList } from "../../hooks/useDatastoreList";
import useUser from "../../hooks/useUser";
import Translator from "../../modules/Translator";
import { routes } from "../../router/router";
import { useAuthStore } from "../../stores/AuthStore";

const DashboardPro = () => {
const datastoreListQuery = useDatastoreList();
const navItems = datastoreNavItems();
const { user } = useUser();
const { user } = useAuthStore();

return (
<AppLayout navItems={navItems} documentTitle="Tableau de bord professionnel">
Expand Down
4 changes: 2 additions & 2 deletions assets/pages/users/Me.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import AppLayout from "../../components/Layout/AppLayout";
import BtnBackToHome from "../../components/Utils/BtnBackToHome";
import functions from "../../functions";
import useUser from "../../hooks/useUser";
import { useAuthStore } from "../../stores/AuthStore";

const Me = () => {
const { user } = useUser();
const { user } = useAuthStore();

return (
<AppLayout documentTitle="Mon compte">
Expand Down
Loading

0 comments on commit 0d232b7

Please sign in to comment.