Skip to content

Commit 826b22c

Browse files
authored
feat: create organisation (#2254)
* feat: refactor dashboard UI frame * refactor: router. use router with context * refactor: remove unused tailwind classes * fix: sidebar appearing over main content * feat: use auth hook * feat: use auth hook * refactor: auth service * fix: stop caching useAuth return values * fix: login button not enable on request completion on error * feat: set overlay background colour for dialog component * feat: update sidebar background colour * feat: create organisation
1 parent 8ba08aa commit 826b22c

28 files changed

+1213
-887
lines changed
Loading
Loading
Loading
+9-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { lazy, Suspense } from 'react';
2-
import { createRootRoute, Outlet } from '@tanstack/react-router';
2+
import { createRootRouteWithContext, Outlet } from '@tanstack/react-router';
33
import { isProductionMode } from '@/lib/env';
44

5+
import type { AuthContext } from '@/hooks/use-auth';
6+
57
const TanStackRouterDevTools = isProductionMode
68
? () => null
79
: lazy(() =>
@@ -10,13 +12,15 @@ const TanStackRouterDevTools = isProductionMode
1012
})),
1113
);
1214

13-
export const Route = createRootRoute({
15+
type RouterContext = {
16+
auth: AuthContext | null
17+
};
18+
19+
export const Route = createRootRouteWithContext<RouterContext>()({
1420
component: () => (
1521
<>
1622
<Outlet />
17-
<Suspense>
18-
{/* <TanStackRouterDevTools /> */}
19-
</Suspense>
23+
<Suspense>{/* <TanStackRouterDevTools /> */}</Suspense>
2024
</>
2125
),
2226
});

web/ui/dashboard-react/src/app/index.tsx

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { createFileRoute } from '@tanstack/react-router';
2+
23
import { router } from '@/lib/router';
34
import { ensureCanAccessPrivatePages } from '@/lib/auth';
45

56
export const Route = createFileRoute('/')({
6-
beforeLoad() {
7-
ensureCanAccessPrivatePages();
7+
beforeLoad({context}) {
8+
ensureCanAccessPrivatePages(context.auth?.getTokens().isLoggedIn);
89
router.navigate({ to: '/projects' });
910
},
1011
component: Index,

web/ui/dashboard-react/src/app/login.tsx

+29-37
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
import { z } from 'zod';
2+
import { useForm } from 'react-hook-form';
3+
import { EyeIcon, EyeOffIcon } from 'lucide-react';
4+
import { zodResolver } from '@hookform/resolvers/zod';
5+
import { useEffect, useReducer, useState } from 'react';
6+
import { createFileRoute, useNavigate } from '@tanstack/react-router';
7+
8+
import { Form } from '@/components/ui/form';
9+
import { Input } from '@/components/ui/input';
110
import { Button } from '@/components/ui/button';
211
import {
312
FormField,
@@ -6,20 +15,13 @@ import {
615
FormControl,
716
FormMessageWithErrorIcon,
817
} from '@/components/ui/form';
9-
import { Input } from '@/components/ui/input';
10-
import { Form } from '@/components/ui/form';
11-
import { cn } from '@/lib/utils';
12-
import { zodResolver } from '@hookform/resolvers/zod';
13-
import { EyeIcon, EyeOffIcon } from 'lucide-react';
14-
import { useEffect, useReducer, useState } from 'react';
15-
import { useForm } from 'react-hook-form';
16-
import { createFileRoute, useNavigate } from '@tanstack/react-router';
17-
import { z } from 'zod';
1818
import { ConvoyLoader } from '@/components/convoy-loader';
19-
import * as loginService from '@/services/login.service';
20-
import * as signUpService from '@/services/signup.service';
21-
import * as organisationService from '@/services/organisations.service';
19+
20+
import { cn } from '@/lib/utils';
21+
22+
import * as authService from '@/services/auth.service';
2223
import * as licensesService from '@/services/licenses.service';
24+
import * as organisationService from '@/services/organisations.service';
2325

2426
import type { UseFormReturn } from 'react-hook-form';
2527

@@ -182,7 +184,7 @@ function LoginWithSAMLButton() {
182184
localStorage.setItem('AUTH_TYPE', 'login');
183185

184186
try {
185-
const res = await loginService.loginWithSAML();
187+
const res = await authService.loginWithSAML();
186188
const { redirectUrl } = res.data;
187189
window.open(redirectUrl);
188190
} catch (error) {
@@ -229,18 +231,18 @@ function SignUpButton() {
229231

230232
type ReducerPayload = Partial<{
231233
isSignUpEnabled: boolean;
232-
isFetchingConfig: boolean;
233234
isLoadingProject: boolean;
234235
hasCreateUserLicense: boolean;
235236
isLoginButtonEnabled: boolean;
237+
isFetchingSignUpConfig: boolean;
236238
}>;
237239

238240
const initialReducerState = {
239241
isSignUpEnabled: false,
240-
isFetchingConfig: false,
241242
isLoadingProject: false,
242243
isLoginButtonEnabled: true,
243244
hasCreateUserLicense: false,
245+
isFetchingSignUpConfig: false,
244246
};
245247

246248
function reducer(state: ReducerPayload, payload: ReducerPayload) {
@@ -255,8 +257,8 @@ function LoginPage() {
255257
const [state, dispatchState] = useReducer(reducer, initialReducerState);
256258

257259
useEffect(function () {
258-
getSignUpConfig();
259-
licensesService.setLicenses();
260+
getSignUpConfig().then();
261+
licensesService.setLicenses().then();
260262
const hasCreateUserLicense = licensesService.hasLicense('CREATE_USER');
261263
dispatchState({ hasCreateUserLicense });
262264
}, []);
@@ -274,39 +276,29 @@ function LoginPage() {
274276
dispatchState({ isLoginButtonEnabled: false });
275277

276278
try {
277-
await loginService.login(values);
279+
await authService.login(values);
278280
dispatchState({ isLoadingProject: true });
279-
await getOrganisations();
281+
await organisationService.getOrganisations({ refresh: true });
280282
dispatchState({ isLoginButtonEnabled: true, isLoadingProject: false });
281-
282-
navigate({
283-
to: '/',
284-
from: '/login',
285-
});
283+
navigate({ to: '/projects', from: '/login' });
286284
} catch (err) {
287285
// TODO notify user using the UI
288286
console.error(login.name, err);
287+
dispatchState({ isLoginButtonEnabled: true, isLoadingProject: false });
289288
}
290289
}
291290

292291
async function getSignUpConfig() {
293-
dispatchState({ isFetchingConfig: true });
292+
dispatchState({ isFetchingSignUpConfig: true });
293+
294294
try {
295-
const { data } = await signUpService.getSignUpConfig();
295+
const { data } = await authService.getSignUpConfig();
296296
dispatchState({ isSignUpEnabled: data });
297297
} catch (err) {
298298
// TODO notify user using the UI
299299
console.error(getSignUpConfig.name, err);
300300
} finally {
301-
dispatchState({ isFetchingConfig: false });
302-
}
303-
}
304-
305-
async function getOrganisations() {
306-
try {
307-
await organisationService.getOrganisations({ refresh: true });
308-
} catch (err) {
309-
console.error(getOrganisations.name, err);
301+
dispatchState({ isFetchingSignUpConfig: false });
310302
}
311303
}
312304

@@ -345,7 +337,7 @@ function LoginPage() {
345337

346338
<ConvoyLoader
347339
isTransparent={false}
348-
isVisible={state.isLoadingProject || state.isFetchingConfig}
340+
isVisible={state.isLoadingProject || state.isFetchingSignUpConfig}
349341
/>
350342
</>
351343
);
@@ -355,5 +347,5 @@ export const Route = createFileRoute('/login')({
355347
component: LoginPage,
356348
});
357349

358-
// TODO loginService and other impure extraneous deps should be injected as a
350+
// TODO authService and other impure extraneous deps should be injected as a
359351
// dependency for testing and flexibility/maintainability
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ensureCanAccessPrivatePages } from '@/lib/auth';
2+
import { Outlet, createFileRoute } from '@tanstack/react-router';
3+
4+
import { DashboardLayout } from '@/components/dashboard';
5+
6+
export const Route = createFileRoute('/projects/_layout')({
7+
beforeLoad({ context }) {
8+
ensureCanAccessPrivatePages(context.auth?.getTokens().isLoggedIn);
9+
},
10+
component: ProjectsLayout,
11+
});
12+
13+
function ProjectsLayout() {
14+
return (
15+
<DashboardLayout>
16+
<Outlet />
17+
</DashboardLayout>
18+
);
19+
}

0 commit comments

Comments
 (0)