-
-
- {children}
-
+
);
diff --git a/resources/ts/back/Pages/Auth/ForgotPassword.tsx b/resources/ts/back/Pages/Auth/ForgotPassword.tsx
new file mode 100644
index 0000000..bf83fec
--- /dev/null
+++ b/resources/ts/back/Pages/Auth/ForgotPassword.tsx
@@ -0,0 +1,87 @@
+// * Packages.
+import { Head, Link, useForm } from "@inertiajs/react";
+import { route } from "ziggy-js";
+import { LoaderCircleIcon, MoveLeftIcon, SendIcon } from "lucide-react";
+import { SubmitEvent } from "react";
+// * Components shadcn.
+import { Button } from "@/components/ui/button";
+import {
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+// * Other.
+import GuestLayout from "@/back/Layouts/GuestLayout";
+import SimpleString from "@/back/Components/Inputs/SimpleString";
+import useTrans from "@/hooks/use-translations";
+import { toast } from "@/hooks/use-toast";
+
+export default function ForgotPassword() {
+ const { post, setData, data, processing } = useForm({
+ email: "",
+ });
+
+ const mailSent = useTrans("auth.mail_sent");
+ const mailSentDesc = useTrans("auth.mail_sent_desc", {
+ params: { email: data.email },
+ });
+
+ const submit = (event: SubmitEvent) => {
+ event.preventDefault();
+ post(route("back.forgot-password.post"), {
+ onSuccess: () => {
+ toast({
+ title: mailSent,
+ description: mailSentDesc,
+ });
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+ {useTrans("auth.forgot_password")}
+
+
+
+ {useTrans("auth.forgot_password_desc")}
+
+
+
+
+
+
+ );
+}
diff --git a/resources/ts/back/Pages/Auth/Login.tsx b/resources/ts/back/Pages/Auth/Login.tsx
index c82ef21..e0a549f 100644
--- a/resources/ts/back/Pages/Auth/Login.tsx
+++ b/resources/ts/back/Pages/Auth/Login.tsx
@@ -1,8 +1,9 @@
-// * Packages
-import { Head, usePage } from "@inertiajs/react";
-import { SquareArrowOutUpRightIcon } from "lucide-react";
+// * Packages.
+import { Head, Link, useForm, usePage } from "@inertiajs/react";
import { route } from "ziggy-js";
-// * Components shadcn
+import { SubmitEvent } from "react";
+import { LoaderCircleIcon, LogInIcon, MoveRightIcon } from "lucide-react";
+// * Components shadcn.
import { Button } from "@/components/ui/button";
import {
CardContent,
@@ -10,14 +11,40 @@ import {
CardHeader,
CardTitle,
} from "@/components/ui/card";
-// * Other
+// * Other.
import GuestLayout from "@/back/Layouts/GuestLayout";
-import InputError from "@/back/Components/InputError";
+import SimpleString from "@/back/Components/Inputs/SimpleString";
import useTrans from "@/hooks/use-translations";
import { AppEnv } from "@/types/global";
+import { toast } from "@/hooks/use-toast";
+import SimpleSwitch from "@/back/Components/Inputs/SimpleSwitch";
-export default function Login({ tenantUUID }: { tenantUUID: string }) {
+export default function Login() {
const appName = (usePage().props.appEnv as AppEnv).name;
+
+ /** Translations. */
+ const transConnected = useTrans("auth.connected");
+ const transConnectedDesc = useTrans("auth.connected_desc");
+
+ /** Form section. */
+ const { post, setData, data, processing } = useForm({
+ email: null,
+ password: null,
+ remember: false,
+ });
+
+ const submit = (event: SubmitEvent) => {
+ event.preventDefault();
+ post(route("back.login.post"), {
+ onSuccess: () => {
+ toast({
+ title: transConnected,
+ description: transConnectedDesc,
+ });
+ },
+ });
+ };
+
return (
@@ -27,25 +54,72 @@ export default function Login({ tenantUUID }: { tenantUUID: string }) {
{useTrans("uwil-backend::back.login_title")}
-
-
-
+
-
-
+
);
diff --git a/resources/ts/back/Pages/Auth/ResetPassword.tsx b/resources/ts/back/Pages/Auth/ResetPassword.tsx
new file mode 100644
index 0000000..cab5f64
--- /dev/null
+++ b/resources/ts/back/Pages/Auth/ResetPassword.tsx
@@ -0,0 +1,114 @@
+// * Packages.
+import { SubmitEvent } from "react";
+import { Head, useForm } from "@inertiajs/react";
+import { route } from "ziggy-js";
+import { LoaderCircleIcon, ShieldCheckIcon } from "lucide-react";
+// * Components shadcn.
+import { Button } from "@/components/ui/button";
+import {
+ CardContent,
+ CardDescription,
+ CardHeader,
+ CardTitle,
+} from "@/components/ui/card";
+// * Other.
+import GuestLayout from "@/back/Layouts/GuestLayout";
+import SimpleString from "@/back/Components/Inputs/SimpleString";
+import useTrans from "@/hooks/use-translations";
+import { toast } from "@/hooks/use-toast";
+
+type ResetPasswordProps = {
+ token: string;
+ email: string;
+};
+
+export default function ResetPassword({ token, email }: ResetPasswordProps) {
+ const resetSent = useTrans("auth.reset_sent");
+ const resetSentDesc = useTrans("auth.reset_sent_desc");
+
+ const { post, setData, processing } = useForm({
+ token,
+ email,
+ password: "",
+ password_confirmation: "",
+ });
+
+ const submit = (event: SubmitEvent) => {
+ event.preventDefault();
+ post(route("back.reset-password.post"), {
+ onSuccess: () => {
+ toast({
+ title: resetSent,
+ description: resetSentDesc,
+ });
+ },
+ });
+ };
+
+ return (
+
+
+
+
+
+ {useTrans("auth.reset_password")}
+
+
+
+ {useTrans("auth.reset_password_desc")}
+
+
+
+
+
+
+ );
+}
diff --git a/resources/ts/back/Pages/Errors/Default.tsx b/resources/ts/back/Pages/Errors/Default.tsx
index 7d99860..473ef2d 100644
--- a/resources/ts/back/Pages/Errors/Default.tsx
+++ b/resources/ts/back/Pages/Errors/Default.tsx
@@ -15,7 +15,7 @@ export default function Default({ status }: { status: number }) {
-
+
diff --git a/resources/ts/back/Pages/Users/Form.tsx b/resources/ts/back/Pages/Users/Form.tsx
index e6c4985..e94c2de 100644
--- a/resources/ts/back/Pages/Users/Form.tsx
+++ b/resources/ts/back/Pages/Users/Form.tsx
@@ -9,7 +9,6 @@ import FieldsetWrapper from "@/back/Components/FieldsetWrapper";
import useTrans from "@/hooks/use-translations";
import str from "@/hooks/use-string";
import InputError from "@/back/Components/InputError";
-import { LaravelEnum } from "@/types/global";
import { AuthProp, UserModel } from "@/back/types/global";
import SubmitButton from "@/back/Components/SubmitButton";
import useCrudSubmit from "@/back/hooks/use-crud-submit";
@@ -28,8 +27,8 @@ export default function Form({
method: number;
}) {
const userModel = (usePage().props.auth as AuthProp).user;
- const enumUserRole = usePage().props.enumUserRole as LaravelEnum;
- const isConceptor = userModel.role === enumUserRole.conceptor.value;
+ const enumUserRole = usePage().props.enumUserRole as UserModel["role"][];
+ const isConceptor = enumUserRole.find((role) => role.name === "conceptor");
const filterRoles = (value: number) => {
return (
Object.entries(enumUserRole)
@@ -46,7 +45,7 @@ export default function Form({
email: model?.email,
published: model?.published ?? false,
published_at: model?.published_at,
- role: model?.role,
+ role: model && model.role ? model.role.value : undefined,
});
const submit = useCrudSubmit({
@@ -141,26 +140,26 @@ export default function Form({
.ucFirst()
.value()}
placeholder="ex: john.doe@gmail.com"
- disabled={!isConceptor}
- wordCounter={isConceptor}
- value={!isConceptor ? model?.email : data.email}
+ disabled={!isConceptor!.value}
+ wordCounter={Boolean(isConceptor!.value)}
+ value={!isConceptor!.value ? model?.email : data.email}
setData={setData}
/>
- {isConceptor && (
+ {Boolean(isConceptor!.value) && (
setData(name, value?.value ?? value)}
/>
)}
- {isConceptor && (
+ {userModel.role.value === isConceptor!.value && (
{
+const getColumns = () => {
return [
{
accessorKey: "first_name",
@@ -80,19 +79,14 @@ const getColumns = (enumUserRole: LaravelEnum) => {
field={column.id}
/>
),
- cell: ({ row }: { row: Row }) => (
-
- {str(
- String(
- Object.values(enumUserRole).find(
- (role) => role.value === row.getValue("role"),
- )?.label,
- ),
- )
- .ucFirst()
- .value()}
-
- ),
+ cell: ({ row }: { row: Row }) => {
+ const rowRole = row.getValue("role") as UserModel["role"];
+ return (
+
+ {str(String(rowRole?.label ?? '')).ucFirst().value()}
+
+ );
+ },
},
{
accessorKey: "published",
@@ -160,9 +154,8 @@ export default function Index({
userModels: Resource;
searchFields: string;
}) {
- const { auth, enumUserRole } = usePage().props as unknown as {
+ const { auth } = usePage().props as unknown as {
auth: AuthProp;
- enumUserRole: LaravelEnum;
};
const { create } = auth.policies.users;
const modelName = usePage().props.modelName as string;
@@ -173,7 +166,7 @@ export default function Index({
Views.DELETION,
];
- const columns = getColumns(enumUserRole);
+ const columns = getColumns();
const transCrudAddEntry = useTrans("uwil-backend::back.crud_add_entry");
const afterNode = () => {
return (
diff --git a/resources/ts/back/Pages/Users/Show.tsx b/resources/ts/back/Pages/Users/Show.tsx
index 71662bc..0fb58ff 100644
--- a/resources/ts/back/Pages/Users/Show.tsx
+++ b/resources/ts/back/Pages/Users/Show.tsx
@@ -11,12 +11,11 @@ import ModelActions from "@/back/Components/ModelActions";
import BtnOptionnalLinkWithTooltip from "@/back/Components/BtnOptionnalLinkWithTooltip";
import Views from "@/back/lib/actions";
import { GetFormatedDate, GetTranslation } from "@/lib/utils";
-import { LaravelEnum } from "@/types/global";
import { AuthProp, SingleResource, UserModel } from "@/back/types/global";
import str from "@/hooks/use-string";
import useTrans from "@/hooks/use-translations";
-const getColumns = (enumUserRole: LaravelEnum) => {
+const getColumns = () => {
return [
{
accessorKey: "first_name",
@@ -54,16 +53,9 @@ const getColumns = (enumUserRole: LaravelEnum) => {
header: str(GetTranslation(`uwil-backend::validation.attributes.role`))
.ucFirst()
.value(),
- cell: ({ value }: { value: number }) => (
+ cell: ({ value }: { value: UserModel["role"] }) => (
- {str(
- String(
- Object.values(enumUserRole).find((role) => role.value === value)
- ?.label,
- ),
- )
- .ucFirst()
- .value()}
+ {str(String(value?.label ?? '')).ucFirst().value()}
),
},
@@ -133,15 +125,14 @@ const getColumns = (enumUserRole: LaravelEnum) => {
};
export default function Show({ data }: { data: SingleResource }) {
- const { auth, enumUserRole } = usePage().props as unknown as {
+ const { auth } = usePage().props as unknown as {
auth: AuthProp;
- enumUserRole: LaravelEnum;
};
const { viewAny } = auth.policies.users;
const userModel = data.data as UserModel;
const actionModels = [Views.EDITION, Views.DUPLICATION, Views.DELETION];
- const columns = getColumns(enumUserRole);
+ const columns = getColumns();
const transCrudBackList = useTrans("uwil-backend::back.crud_back_to_list");
const beforeNode = () => {
diff --git a/resources/ts/back/types/global.d.ts b/resources/ts/back/types/global.d.ts
index 48726a3..133ec52 100644
--- a/resources/ts/back/types/global.d.ts
+++ b/resources/ts/back/types/global.d.ts
@@ -25,7 +25,7 @@ type UserModel = {
first_name: string;
last_name: string;
email: string;
- role: number;
+ role: { value: number; label: string; name: string };
published: boolean;
published_at: Date;
created_at: Date;
diff --git a/resources/views/back/mails/link-reset-password.blade.php b/resources/views/back/mails/link-reset-password.blade.php
new file mode 100644
index 0000000..a961281
--- /dev/null
+++ b/resources/views/back/mails/link-reset-password.blade.php
@@ -0,0 +1,23 @@
+{{-- blade-formatter-disable --}}
+@component('mail::message')
+
+# {{ __('auth.email_hello') }}
+
+{{ __('auth.email_get_reset_password') }}
+{{ __('auth.email_reset_link_expire', ['count' => config('auth.passwords.users.expire')]) }}
+
+{{ __('auth.email_button_redirect') }}
+@component('mail::button', ['url' => $url])
+{{ __('auth.email_reset_password') }}
+@endcomponent
+
+{{ __('auth.email_wrong_destination') }}
+
+{{ config('app.name') }}.
+
+
+
+{{ __('auth.email_cant_visualise', ['actionText' => __('auth.email_reset_password')]) }}
+{{ $url }}
+
+@endcomponent
\ No newline at end of file
diff --git a/resources/views/back/mails/reset-password.blade.php b/resources/views/back/mails/reset-password.blade.php
new file mode 100644
index 0000000..51026b9
--- /dev/null
+++ b/resources/views/back/mails/reset-password.blade.php
@@ -0,0 +1,20 @@
+{{-- blade-formatter-disable --}}
+@component('mail::message')
+
+# {{ __('auth.email_hello') }}
+
+{{ __('auth.email_reset_complete') }}
+
+{{ __('auth.email_new_connection') }}
+@component('mail::button', ['url' => route('back.login')])
+{{ __('auth.login') }}
+@endcomponent
+
+{{ config('app.name') }}.
+
+
+
+{{ __('auth.email_cant_visualise', ['actionText' => __('auth.login')]) }}
+{{ route('back.login') }}
+
+@endcomponent
\ No newline at end of file
diff --git a/routes/auth.php b/routes/auth.php
new file mode 100644
index 0000000..5541b5a
--- /dev/null
+++ b/routes/auth.php
@@ -0,0 +1,30 @@
+name(config('uwil-backend.routes.name'))
+ ->middleware(['web', 'admin-secure'])
+ ->group(function (): void {
+ Route::middleware('guest:backend')->group(function (): void {
+ Route::get('login', [AuthenticatedSessionController::class, 'login'])
+ ->name('login');
+ Route::post('login', [AuthenticatedSessionController::class, 'postLogin'])
+ ->name('login.post');
+
+ Route::get('forgot-password', [AuthenticatedSessionController::class, 'forgotPassword'])
+ ->name('forgot-password');
+ Route::post('forgot-password', [AuthenticatedSessionController::class, 'postForgotPassword'])
+ ->name('forgot-password.post');
+
+ Route::get('reset-password/{token}', [AuthenticatedSessionController::class, 'resetPassword'])
+ ->name('reset-password');
+ Route::post('reset-password', [AuthenticatedSessionController::class, 'postResetPassword'])
+ ->name('reset-password.post');
+ });
+ Route::middleware(['auth:backend', 'verified'])->group(function (): void {
+ Route::get('logout', [AuthenticatedSessionController::class, 'logout'])
+ ->name('logout');
+ });
+ });