mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-26 19:12:50 +00:00
use iam module login page for backoffice
This commit is contained in:
@@ -1,5 +1 @@
|
|||||||
import { LoginForm } from '../components/LoginForm';
|
export { Login as LoginPage } from '@tria-plc/iamui-common';
|
||||||
|
|
||||||
export function LoginPage() {
|
|
||||||
return <LoginForm />;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,137 +1 @@
|
|||||||
import {
|
export { SetPasswordPage } from '@tria-plc/iamui-common';
|
||||||
Paper,
|
|
||||||
TextInput,
|
|
||||||
PasswordInput,
|
|
||||||
Button,
|
|
||||||
Stack,
|
|
||||||
Title,
|
|
||||||
Text,
|
|
||||||
} from '@mantine/core';
|
|
||||||
import { useForm } from 'react-hook-form';
|
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
|
||||||
import { z } from 'zod';
|
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../../store/hooks';
|
|
||||||
import {
|
|
||||||
setPasswordStart,
|
|
||||||
setPasswordSuccess,
|
|
||||||
setPasswordFailure,
|
|
||||||
} from '../store/set-password.slice';
|
|
||||||
import { notify } from '@ema-platform/ui';
|
|
||||||
|
|
||||||
const BASE_API_URL =
|
|
||||||
(import.meta as { env?: Record<string, string> }).env?.['VITE_BASE_API_URL'] ??
|
|
||||||
'http://localhost:3001/api';
|
|
||||||
|
|
||||||
const schema = z
|
|
||||||
.object({
|
|
||||||
userId: z.string().min(1, 'User ID is required'),
|
|
||||||
email: z.string().email('Enter a valid email'),
|
|
||||||
verificationCode: z.string().min(1, 'Verification code is required'),
|
|
||||||
newPassword: z.string().min(6, 'Password must be at least 6 characters'),
|
|
||||||
confirmPassword: z.string().min(6, 'Confirm your password'),
|
|
||||||
})
|
|
||||||
.refine((data) => data.newPassword === data.confirmPassword, {
|
|
||||||
message: 'Passwords do not match',
|
|
||||||
path: ['confirmPassword'],
|
|
||||||
});
|
|
||||||
|
|
||||||
type FormValues = z.infer<typeof schema>;
|
|
||||||
|
|
||||||
export function SetPasswordPage() {
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const location = useLocation();
|
|
||||||
const { loading } = useAppSelector((s) => s.setPassword);
|
|
||||||
const signupEmail = (location.state as { email?: string } | null)?.email ?? '';
|
|
||||||
|
|
||||||
const {
|
|
||||||
register,
|
|
||||||
handleSubmit,
|
|
||||||
formState: { errors },
|
|
||||||
} = useForm<FormValues>({
|
|
||||||
resolver: zodResolver(schema),
|
|
||||||
defaultValues: { email: signupEmail },
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSubmit = async (values: FormValues) => {
|
|
||||||
dispatch(setPasswordStart());
|
|
||||||
try {
|
|
||||||
const res = await fetch(`${BASE_API_URL}/auth/set-password`, {
|
|
||||||
method: 'PATCH',
|
|
||||||
headers: { 'Content-Type': 'application/json' },
|
|
||||||
body: JSON.stringify({
|
|
||||||
userId: values.userId,
|
|
||||||
email: values.email,
|
|
||||||
verificationCode: values.verificationCode,
|
|
||||||
newPassword: values.newPassword,
|
|
||||||
confirmPassword: values.confirmPassword,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
const errorData = await res.json().catch(() => null);
|
|
||||||
throw new Error(errorData?.message ?? 'Failed to set password');
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(setPasswordSuccess());
|
|
||||||
notify.success('Password set successfully');
|
|
||||||
navigate('/dashboard');
|
|
||||||
} catch (err) {
|
|
||||||
const msg = err instanceof Error ? err.message : 'Something went wrong';
|
|
||||||
dispatch(setPasswordFailure(msg));
|
|
||||||
notify.error(msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper p="xl" shadow="md" radius="md">
|
|
||||||
<Stack gap="lg">
|
|
||||||
<Stack gap={4}>
|
|
||||||
<Title order={2}>Set your password</Title>
|
|
||||||
<Text c="dimmed" size="sm">
|
|
||||||
A verification code has been sent to your email. Enter it below along
|
|
||||||
with your new password to complete registration.
|
|
||||||
</Text>
|
|
||||||
</Stack>
|
|
||||||
<form onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<Stack gap="md">
|
|
||||||
<TextInput
|
|
||||||
label="User ID"
|
|
||||||
placeholder="Enter your user ID"
|
|
||||||
error={errors.userId?.message}
|
|
||||||
{...register('userId')}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label="Email"
|
|
||||||
placeholder="you@example.com"
|
|
||||||
error={errors.email?.message}
|
|
||||||
{...register('email')}
|
|
||||||
/>
|
|
||||||
<TextInput
|
|
||||||
label="Verification Code"
|
|
||||||
placeholder="Enter the code from your email"
|
|
||||||
error={errors.verificationCode?.message}
|
|
||||||
{...register('verificationCode')}
|
|
||||||
/>
|
|
||||||
<PasswordInput
|
|
||||||
label="New Password"
|
|
||||||
placeholder="Enter a new password"
|
|
||||||
error={errors.newPassword?.message}
|
|
||||||
{...register('newPassword')}
|
|
||||||
/>
|
|
||||||
<PasswordInput
|
|
||||||
label="Confirm Password"
|
|
||||||
placeholder="Confirm your new password"
|
|
||||||
error={errors.confirmPassword?.message}
|
|
||||||
{...register('confirmPassword')}
|
|
||||||
/>
|
|
||||||
<Button type="submit" loading={loading} fullWidth mt="sm">
|
|
||||||
Set Password
|
|
||||||
</Button>
|
|
||||||
</Stack>
|
|
||||||
</form>
|
|
||||||
</Stack>
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -29,3 +29,13 @@ export function IamProviders({ children }: IamProvidersProps) {
|
|||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AuthProviders({ children }: { children: ReactNode }) {
|
||||||
|
return (
|
||||||
|
<AuthProvider>
|
||||||
|
<BrandingProvider>
|
||||||
|
{children}
|
||||||
|
</BrandingProvider>
|
||||||
|
</AuthProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,12 +1,5 @@
|
|||||||
import { Center, Box } from '@mantine/core';
|
|
||||||
import { Outlet } from 'react-router-dom';
|
import { Outlet } from 'react-router-dom';
|
||||||
|
|
||||||
export function AuthLayout() {
|
export function AuthLayout() {
|
||||||
return (
|
return <Outlet />;
|
||||||
<Center h="100vh" bg="gray.0">
|
|
||||||
<Box w={420}>
|
|
||||||
<Outlet />
|
|
||||||
</Box>
|
|
||||||
</Center>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { Group, Text, ActionIcon, Burger } from '@mantine/core';
|
import { Group, Text, ActionIcon, Burger } from '@mantine/core';
|
||||||
import { IconLogout } from '@tabler/icons-react';
|
import { IconLogout } from '@tabler/icons-react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import { useAuth } from '../../features/auth/hooks/useAuth';
|
import { useAuthUser } from '@tria-plc/iamui-common';
|
||||||
|
|
||||||
interface AppHeaderProps {
|
interface AppHeaderProps {
|
||||||
onToggle: () => void;
|
onToggle: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function AppHeader({ onToggle }: AppHeaderProps) {
|
export function AppHeader({ onToggle }: AppHeaderProps) {
|
||||||
const { logout } = useAuth();
|
const { logout } = useAuthUser();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleLogout = () => {
|
const handleLogout = () => {
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom';
|
import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom';
|
||||||
import {
|
import {
|
||||||
|
Login,
|
||||||
|
SetPasswordPage,
|
||||||
UserManagementLayout,
|
UserManagementLayout,
|
||||||
UserManagementPage,
|
UserManagementPage,
|
||||||
BulkUploadPage,
|
BulkUploadPage,
|
||||||
@@ -11,17 +13,18 @@ import {
|
|||||||
import { ProtectedRoute } from './ProtectedRoute';
|
import { ProtectedRoute } from './ProtectedRoute';
|
||||||
import { BackofficeLayout } from '../layouts/BackofficeLayout';
|
import { BackofficeLayout } from '../layouts/BackofficeLayout';
|
||||||
import { AuthLayout } from '../layouts/AuthLayout';
|
import { AuthLayout } from '../layouts/AuthLayout';
|
||||||
import { LoginPage } from '../features/auth/pages/LoginPage';
|
|
||||||
import { SignupPage } from '../features/auth/pages/SignupPage';
|
|
||||||
import { SetPasswordPage } from '../features/auth/pages/SetPasswordPage';
|
|
||||||
import { DashboardPage } from '../features/dashboard/pages/DashboardPage';
|
import { DashboardPage } from '../features/dashboard/pages/DashboardPage';
|
||||||
import { ItemPage } from '../features/item/pages/ItemPage';
|
import { ItemPage } from '../features/item/pages/ItemPage';
|
||||||
import { AuditLogPageWrapper } from '../iam/pages/AuditLogPage';
|
import { AuditLogPageWrapper } from '../iam/pages/AuditLogPage';
|
||||||
import { IamProviders } from '../iam/IamProviders';
|
import { IamProviders, AuthProviders } from '../iam/IamProviders';
|
||||||
|
|
||||||
const router = createBrowserRouter([
|
const router = createBrowserRouter([
|
||||||
{
|
{
|
||||||
element: <ProtectedRoute />,
|
element: (
|
||||||
|
<AuthProviders>
|
||||||
|
<ProtectedRoute />
|
||||||
|
</AuthProviders>
|
||||||
|
),
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
element: <BackofficeLayout />,
|
element: <BackofficeLayout />,
|
||||||
@@ -51,10 +54,13 @@ const router = createBrowserRouter([
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
element: <AuthLayout />,
|
element: (
|
||||||
|
<AuthProviders>
|
||||||
|
<AuthLayout />
|
||||||
|
</AuthProviders>
|
||||||
|
),
|
||||||
children: [
|
children: [
|
||||||
{ path: '/login', element: <LoginPage /> },
|
{ path: '/login', element: <Login /> },
|
||||||
{ path: '/signup', element: <SignupPage /> },
|
|
||||||
{ path: '/set-password', element: <SetPasswordPage /> },
|
{ path: '/set-password', element: <SetPasswordPage /> },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user