diff --git a/back/src/oidc-client/oidc-client.controller.ts b/back/src/oidc-client/oidc-client.controller.ts index 75d9477..9d4b3b8 100644 --- a/back/src/oidc-client/oidc-client.controller.ts +++ b/back/src/oidc-client/oidc-client.controller.ts @@ -1,8 +1,10 @@ import { Body, Controller, + Get, HttpException, HttpStatus, + Param, Post, } from '@nestjs/common'; import { CreateOidcClientDto } from './oidc-client.dto'; @@ -13,6 +15,35 @@ export class OidcClientController { constructor(private readonly oidcClientSaver: OidcClientSaver) { this.oidcClientSaver = oidcClientSaver; } + + @Get() + async findAll() { + return [ + { + clientDescription: 'Description', + clientId: 'clientId', + clientSecret: 'ClientSecret', + clientName: 'Rebecca Project 123', + redirectUris: ['http://example.com/callback'], + postLogoutRedirectUris: ['http://example.com/logout'], + scope: ['firstname', 'lastname'], + }, + ] satisfies CreateOidcClientDto[]; + } + + @Get('/:id') + async find_by_id(@Param('id') id: string) { + return { + clientDescription: 'Description', + clientId: 'clientId', + clientSecret: 'ClientSecret', + clientName: 'Rebecca Project 123', + redirectUris: ['http://example.com/callback'], + postLogoutRedirectUris: ['http://example.com/logout'], + scope: ['firstname', 'lastname'], + } satisfies CreateOidcClientDto; + } + @Post() async create(@Body() createOidcClientDto: CreateOidcClientDto) { try { @@ -26,7 +57,7 @@ export class OidcClientController { HttpStatus.BAD_REQUEST, { cause: error, - }, + } ); } } diff --git a/front/src/clients/back-client.ts b/front/src/clients/back-client.ts index 3b3c1aa..36108b4 100644 --- a/front/src/clients/back-client.ts +++ b/front/src/clients/back-client.ts @@ -6,6 +6,8 @@ export const backendClient = { getKeys, getList, postOidcClient, + getDashboard, + getDashboardItem, }; async function getKeys() { @@ -18,6 +20,16 @@ async function getList() { return response.data; } +async function getDashboard() { + const response = await axios.get(VITE_BASE_URL + '/oidc-clients/'); + return response.data; +} + +async function getDashboardItem(id: string) { + const response = await axios.get(VITE_BASE_URL + '/oidc-clients/' + id); + return response.data; +} + async function postOidcClient(data: OidcClient) { return await axios.post(VITE_BASE_URL + '/oidc-clients', data); } diff --git a/front/src/headers/Header.tsx b/front/src/headers/Header.tsx index 1437f97..73a43a1 100644 --- a/front/src/headers/Header.tsx +++ b/front/src/headers/Header.tsx @@ -20,7 +20,10 @@ function Header() { { iconId: 'fr-icon-user-fill', linkProps: { - to: '/connexion', + // NOTE(douglasduteil): use mocked data for now + // Will be replaced by + // to: '/connexion', + to: '/dashboard', }, text: 'Se connecter', }, diff --git a/front/src/providers/connectedSpaces/EspaceConnected.tsx b/front/src/providers/connectedSpaces/EspaceConnected.tsx index 393a6ce..1aa131b 100644 --- a/front/src/providers/connectedSpaces/EspaceConnected.tsx +++ b/front/src/providers/connectedSpaces/EspaceConnected.tsx @@ -1,34 +1,21 @@ +import { useContext } from 'react'; import Title1 from '../../titles/Title1'; import { KeyProductionData } from '../details/KeyProductionData'; import { ProviderKey } from '../details/ProviderKey'; import { ProviderName } from '../details/ProviderName'; import { ProviderScope } from '../details/ProviderScope'; +import { SideMenu } from '../details/ProviderSideMenu'; import { ProviderUrl } from '../details/ProviderUrl'; import { ProviderUrlDeco } from '../details/ProviderUrlDeco'; import { ProviderValidation } from '../details/ProviderValidation'; import { OidcClientFormContext } from '../details/oidc-client-form.context'; -import { OidcClient } from '../../types'; -import { useState } from 'react'; -import { SideMenu } from '../details/ProviderSideMenu'; export const EspaceConnected = () => { - const [oidcClientForm, setOidcClientForm] = useState({ - clientName: '', - clientDescription: '', - clientId: '', - clientSecret: '', - redirectUris: [], - postLogoutRedirectUris: [], - scope: [], - }); + const { oidcClientForm: {clientName} } = useContext(OidcClientFormContext); return ( - + <>
- + {clientName}
@@ -47,6 +34,6 @@ export const EspaceConnected = () => {
-
+ ); }; diff --git a/front/src/providers/details/ProviderDetails.tsx b/front/src/providers/details/ProviderDetails.tsx index 6f385d9..bad0114 100644 --- a/front/src/providers/details/ProviderDetails.tsx +++ b/front/src/providers/details/ProviderDetails.tsx @@ -1,18 +1,18 @@ -import { SideMenu } from './ProviderSideMenu'; -import { ProviderKey } from './ProviderKey'; -import { ProviderUrl } from './ProviderUrl'; -import { ProviderScope } from './ProviderScope'; -import { KeyProductionData } from './KeyProductionData'; -import { ProviderUrlDeco } from './ProviderUrlDeco'; -import Title1 from '../../titles/Title1'; -import { OidcClientFormContext } from './oidc-client-form.context'; -import { OidcClient } from '../../types'; import { useState } from 'react'; import CardInfos from '../../cards/CardInfos'; import monImage from '../../images/test-image.png'; import monImage2 from '../../images/test-image2.png'; -import { ProviderValidation } from './ProviderValidation'; +import Title1 from '../../titles/Title1'; +import { OidcClient } from '../../types'; +import { KeyProductionData } from './KeyProductionData'; +import { ProviderKey } from './ProviderKey'; import { ProviderName } from './ProviderName'; +import { ProviderScope } from './ProviderScope'; +import { SideMenu } from './ProviderSideMenu'; +import { ProviderUrl } from './ProviderUrl'; +import { ProviderUrlDeco } from './ProviderUrlDeco'; +import { ProviderValidation } from './ProviderValidation'; +import { OidcClientFormContext } from './oidc-client-form.context'; export function ProviderDetails() { const [oidcClientForm, setOidcClientForm] = useState({ @@ -28,11 +28,12 @@ export function ProviderDetails() { return (
- + Implémentation
{ }; export const ProviderKey = () => { - const [clientID, setClientID] = useState(''); - const [clientSecret, setClientSecret] = useState(''); - const [isShown, setIsShown] = useState(false); - - const { setOidcClientForm } = useContext(OidcClientFormContext); + const { setOidcClientForm, oidcClientForm } = useContext( + OidcClientFormContext + ); + const [clientID, setClientID] = useState(oidcClientForm.clientId); + const [clientSecret, setClientSecret] = useState(oidcClientForm.clientSecret); + const [isShown, setIsShown] = useState( + Boolean(oidcClientForm.clientId && oidcClientForm.clientSecret) + ); const toggleShowClientId = () => { setIsShown((current) => !current); }; React.useEffect(() => { + if (isShown) { + return; + } + async function fetchData() { try { const data = await backendClient.getKeys(); diff --git a/front/src/providers/details/ProviderName.tsx b/front/src/providers/details/ProviderName.tsx index d10faab..609ebf8 100644 --- a/front/src/providers/details/ProviderName.tsx +++ b/front/src/providers/details/ProviderName.tsx @@ -1,16 +1,31 @@ import { Input } from '@codegouvfr/react-dsfr/Input'; +import { ChangeEvent, useCallback, useContext } from 'react'; +import { OidcClientFormContext } from './oidc-client-form.context'; export const ProviderName = () => { + const { oidcClientForm, setOidcClientForm } = useContext( + OidcClientFormContext + ); + const onChange = useCallback((event: ChangeEvent) => { + const value = event.target.value; + setOidcClientForm((prevState) => ({ ...prevState, clientName: value })); + }, []); + return (
diff --git a/front/src/providers/details/ProviderScope.tsx b/front/src/providers/details/ProviderScope.tsx index 54345b5..7ff1f5d 100644 --- a/front/src/providers/details/ProviderScope.tsx +++ b/front/src/providers/details/ProviderScope.tsx @@ -1,13 +1,15 @@ import { Checkbox } from '@codegouvfr/react-dsfr/Checkbox'; import React, { ChangeEvent, useContext, useState } from 'react'; +import { COLORS } from '../../constants'; import Title2 from '../../titles/Title2'; -import { OidcClientFormContext } from './oidc-client-form.context'; import { OidcClient } from '../../types'; -import { COLORS } from '../../constants'; +import { OidcClientFormContext } from './oidc-client-form.context'; export const ProviderScope = () => { - const [scope, setScope] = useState([]); - const { setOidcClientForm } = useContext(OidcClientFormContext); + const { setOidcClientForm, oidcClientForm } = useContext( + OidcClientFormContext + ); + const [scope, setScope] = useState(oidcClientForm.scope); const getScopes = (e: ChangeEvent) => { if (e.target.checked) { @@ -49,6 +51,7 @@ export const ProviderScope = () => { nativeInputProps: { name: 'checkboxes-1', value: 'firstname', + checked: scope.includes('firstname'), onChange: getScopes, }, }, @@ -57,6 +60,7 @@ export const ProviderScope = () => { nativeInputProps: { name: 'checkboxes-1', value: 'lastname', + checked: scope.includes('lastname'), onChange: getScopes, }, }, @@ -65,6 +69,7 @@ export const ProviderScope = () => { nativeInputProps: { name: 'checkboxes-1', value: 'function-in-organization', + checked: scope.includes('function-in-organization'), onChange: getScopes, }, }, @@ -73,6 +78,7 @@ export const ProviderScope = () => { nativeInputProps: { name: 'checkboxes-1', value: 'email', + checked: scope.includes('email'), onChange: getScopes, }, }, diff --git a/front/src/providers/details/ProviderUrl.tsx b/front/src/providers/details/ProviderUrl.tsx index ff3926e..6ca470a 100644 --- a/front/src/providers/details/ProviderUrl.tsx +++ b/front/src/providers/details/ProviderUrl.tsx @@ -1,14 +1,18 @@ -import { Input } from '@codegouvfr/react-dsfr/Input'; import { Button } from '@codegouvfr/react-dsfr/Button'; +import { Input } from '@codegouvfr/react-dsfr/Input'; import { ChangeEvent, useContext, useState } from 'react'; import Title2 from '../../titles/Title2'; import { OidcClientFormContext } from './oidc-client-form.context'; export const ProviderUrl = () => { - const [inputUrl, setInputUrl] = useState(''); - const [contents, setContents] = useState([]); - const { setOidcClientForm } = useContext(OidcClientFormContext); + const { setOidcClientForm, oidcClientForm } = useContext( + OidcClientFormContext + ); + const [inputUrl, setInputUrl] = useState(''); + const [contents, setContents] = useState( + oidcClientForm.redirectUris + ); const handleInputChange = (e: ChangeEvent): void => { setInputUrl(e.target.value); }; diff --git a/front/src/providers/details/ProviderUrlDeco.tsx b/front/src/providers/details/ProviderUrlDeco.tsx index 31ca9f3..8ff66a7 100644 --- a/front/src/providers/details/ProviderUrlDeco.tsx +++ b/front/src/providers/details/ProviderUrlDeco.tsx @@ -1,12 +1,16 @@ -import { Input } from '@codegouvfr/react-dsfr/Input'; import { Button } from '@codegouvfr/react-dsfr/Button'; +import { Input } from '@codegouvfr/react-dsfr/Input'; import { ChangeEvent, useContext, useState } from 'react'; import { OidcClientFormContext } from './oidc-client-form.context'; export const ProviderUrlDeco = () => { + const { setOidcClientForm, oidcClientForm } = useContext( + OidcClientFormContext + ); const [inputUrl, setInputUrl] = useState(''); - const [contents, setContents] = useState([]); - const { setOidcClientForm } = useContext(OidcClientFormContext); + const [contents, setContents] = useState( + oidcClientForm.postLogoutRedirectUris + ); const handleInputChange = (e: ChangeEvent): void => { setInputUrl(e.target.value); @@ -34,7 +38,7 @@ export const ProviderUrlDeco = () => {
>; -}>({ - setOidcClientForm: (oidcClient) => oidcClient, -}); diff --git a/front/src/providers/details/oidc-client-form.context.tsx b/front/src/providers/details/oidc-client-form.context.tsx new file mode 100644 index 0000000..2d350e3 --- /dev/null +++ b/front/src/providers/details/oidc-client-form.context.tsx @@ -0,0 +1,39 @@ +import { PropsWithChildren, createContext, useState } from 'react'; +import { useLoaderData } from 'react-router-dom'; +import { OidcClient } from '../../types'; + +export const OidcClientFormContext = createContext<{ + oidcClientForm: OidcClient; + setOidcClientForm: React.Dispatch>; +}>({ + oidcClientForm: {} as OidcClient, + setOidcClientForm: (oidcClient) => oidcClient, +}); + +export function OidcClientFormProvider({ + children, +}: PropsWithChildren<{ id?: string }>) { + const item = useLoaderData() as OidcClient; + const [oidcClientForm, setOidcClientForm] = useState( + item ?? { + clientName: 'Test ' + new Date().toLocaleDateString(), + clientDescription: '', + clientId: '', + clientSecret: '', + redirectUris: [], + postLogoutRedirectUris: [], + scope: [], + } + ); + + return ( + + {children} + + ); +} diff --git a/front/src/routes/Router.tsx b/front/src/routes/Router.tsx index bdfdda6..a3d5c5b 100644 --- a/front/src/routes/Router.tsx +++ b/front/src/routes/Router.tsx @@ -1,9 +1,15 @@ -import { RouterProvider, createBrowserRouter } from 'react-router-dom'; -import { PageLayout } from '../layouts/PageLayout'; +import { + RouterProvider, + createBrowserRouter, + redirect, +} from 'react-router-dom'; +import { backendClient } from '../clients/back-client'; import HomeLayout from '../layouts/HomeLayout'; +import { PageLayout } from '../layouts/PageLayout'; +import { EspaceConnected } from '../providers/connectedSpaces/EspaceConnected'; import { ProviderDetails } from '../providers/details/ProviderDetails'; +import { OidcClientFormProvider } from '../providers/details/oidc-client-form.context'; import { EspaceDocumentation } from '../providers/documentation/EspaceDocumentation'; -import { EspaceConnected } from '../providers/connectedSpaces/EspaceConnected'; const router = createBrowserRouter([ { @@ -30,11 +36,45 @@ const router = createBrowserRouter([ ), }, + { + path: '/dashboard', + loader: async () => { + return backendClient.getDashboard(); + }, + element: ( + + <> + NEW +
+ Rebecca Project 123 + +
+ ), + }, { path: '/dashboard/:id', + loader: async ({ params }) => { + const { id } = params; + if (!id) { + return redirect('/dashboard'); + } + return backendClient.getDashboardItem(id); + }, + element: ( + + + + + + ), + }, + { + path: '/dashboard/new', element: ( - + + + ), }, diff --git a/front/src/titles/Title1.tsx b/front/src/titles/Title1.tsx index 9de5c02..1dad753 100644 --- a/front/src/titles/Title1.tsx +++ b/front/src/titles/Title1.tsx @@ -1,4 +1,6 @@ -function Title1(props: { title: string }) { - return

{props.title}

; +import { PropsWithChildren } from 'react'; + +function Title1({ children }: PropsWithChildren) { + return

{children}

; } export default Title1;