Skip to content

Commit bb348ea

Browse files
authored
[Client]: Refactoring Register UI & Adding more UI components (#17) (#18)
* [client]: refactoring register ui * Add Zustand state management library
1 parent f1fe169 commit bb348ea

File tree

34 files changed

+551
-156
lines changed

34 files changed

+551
-156
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
- [Mogoose](https://mongoosejs.com/) - Database ODM
4040
- [Axios](https://axios-http.com/) - Promise based Http Client
4141
- [Formik](https://formik.org/) - Form handler
42+
- [Zustand](https://zustand-demo.pmnd.rs/) - State Management
4243

4344
## Getting Started
4445

client/src/App.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ import HomePage from "@/pages/HomePage";
44
import { AuthProvider } from "@/providers";
55
import { Route, Routes } from "react-router-dom";
66

7-
const LoginPage = lazy(() => import("@/pages/AuthPages/Login"));
8-
const RegisterPage = lazy(() => import("@/pages/AuthPages/Register"));
7+
const LoginPage = lazy(() => import("@/pages/AuthPages/LoginPage"));
8+
const RegisterPage = lazy(() => import("@/pages/AuthPages/RegisterPage"));
99
const ForgotPasswordPage = lazy(
10-
() => import("@/pages/AuthPages/ForgotPassword")
10+
() => import("@/pages/AuthPages/ForgotPasswordPage")
1111
);
1212

1313
function App() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
import {
2+
CheckCircleIcon,
3+
ExclamationTriangleIcon,
4+
XMarkIcon,
5+
} from "@heroicons/react/24/outline";
6+
import { FC, useEffect, useState } from "react";
7+
8+
type Props = {
9+
message: string;
10+
type: "success" | "error" | "warning" | "info";
11+
};
12+
13+
const Alert: FC<Props> = ({ message, type }) => {
14+
const [show, setShow] = useState(true);
15+
16+
useEffect(() => {
17+
const timer = setTimeout(() => {
18+
setShow(false);
19+
}, 2000);
20+
21+
return () => {
22+
clearTimeout(timer);
23+
};
24+
}, []);
25+
26+
const getAlertColor = () => {
27+
switch (type) {
28+
case "success":
29+
return "bg-green-50";
30+
case "error":
31+
return "bg-red-50";
32+
case "warning":
33+
return "bg-yellow-50";
34+
case "info":
35+
return "bg-blue-50";
36+
default:
37+
return "bg-red-50";
38+
}
39+
};
40+
41+
const getCloserColor = () => {
42+
switch (type) {
43+
case "success":
44+
return "text-green-500 hover:bg-green-100 focus:ring-green-600 focus:ring-offset-green-50";
45+
case "error":
46+
return "text-red-500 hover:bg-red-100 focus:ring-red-600 focus:ring-offset-red-50";
47+
case "warning":
48+
return "text-yellow-500 hover:bg-yellow-100 focus:ring-yellow-600 focus:ring-offset-yellow-50";
49+
case "info":
50+
return "text-blue-500 hover:bg-blue-100 focus:ring-blue-600 focus:ring-offset-blue-50";
51+
default:
52+
return "text-red-500 hover:bg-red-100 focus:ring-red-600 focus:ring-offset-red-50";
53+
}
54+
};
55+
56+
const getIcon = () => {
57+
switch (type) {
58+
case "success":
59+
return (
60+
<CheckCircleIcon
61+
className="h-5 w-5 text-green-400"
62+
aria-hidden="true"
63+
/>
64+
);
65+
case "error":
66+
return (
67+
<ExclamationTriangleIcon
68+
className="h-5 w-5 text-red-400"
69+
aria-hidden="true"
70+
/>
71+
);
72+
case "warning":
73+
return (
74+
<XMarkIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" />
75+
);
76+
case "info":
77+
return (
78+
<XMarkIcon className="h-5 w-5 text-blue-400" aria-hidden="true" />
79+
);
80+
default:
81+
return (
82+
<XMarkIcon className="h-5 w-5 text-red-400" aria-hidden="true" />
83+
);
84+
}
85+
};
86+
87+
if (!show) {
88+
return null;
89+
}
90+
91+
return (
92+
<div className={`rounded-md ${getAlertColor()} p-4`}>
93+
<div className="flex">
94+
<div className="flex-shrink-0">{getIcon()}</div>
95+
<div className="ml-3">
96+
<p className="text-sm font-medium">{message}</p>
97+
</div>
98+
<div className="ml-auto pl-3">
99+
<div className="-mx-1.5 -my-1.5">
100+
<button
101+
type="button"
102+
className={`inline-flex rounded-md ${getCloserColor()} p-1.5 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-green-50`}
103+
onClick={() => setShow(false)}
104+
>
105+
<span className="sr-only">Dismiss</span>
106+
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
107+
</button>
108+
</div>
109+
</div>
110+
</div>
111+
</div>
112+
);
113+
};
114+
115+
export default Alert;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "./Alert";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { FC, Fragment } from "react";
2+
import { Dialog as DialogCore, Transition } from "@headlessui/react";
3+
import { XMarkIcon } from "@heroicons/react/24/outline";
4+
5+
type Props = {
6+
open: boolean;
7+
title?: string;
8+
children: React.ReactNode;
9+
acceptLabel?: string;
10+
cancelLabel?: string;
11+
enableClose?: boolean;
12+
enableAccept?: boolean;
13+
enableCancel?: boolean;
14+
renderLeftIcon?: () => React.ReactNode;
15+
onClose: () => void;
16+
onAccept?: () => void;
17+
onCancel?: () => void;
18+
};
19+
20+
const Dialog: FC<Props> = ({
21+
open,
22+
children,
23+
title,
24+
acceptLabel = "OK",
25+
cancelLabel = "Cancel",
26+
enableClose = true,
27+
enableAccept = true,
28+
enableCancel = true,
29+
onClose,
30+
onAccept,
31+
onCancel,
32+
renderLeftIcon,
33+
}) => {
34+
const handleOnClose = () => {
35+
onClose();
36+
};
37+
38+
const handleOnAccept = () => {
39+
onAccept && onAccept();
40+
onClose();
41+
};
42+
43+
const handleOnCancel = () => {
44+
onCancel && onCancel();
45+
onClose();
46+
};
47+
48+
return (
49+
<Transition.Root show={open} as={Fragment}>
50+
<DialogCore as="div" className="relative z-10" onClose={handleOnClose}>
51+
<Transition.Child
52+
as={Fragment}
53+
enter="ease-out duration-300"
54+
enterFrom="opacity-0"
55+
enterTo="opacity-100"
56+
leave="ease-in duration-200"
57+
leaveFrom="opacity-100"
58+
leaveTo="opacity-0"
59+
>
60+
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
61+
</Transition.Child>
62+
63+
<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
64+
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
65+
<Transition.Child
66+
as={Fragment}
67+
enter="ease-out duration-300"
68+
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
69+
enterTo="opacity-100 translate-y-0 sm:scale-100"
70+
leave="ease-in duration-200"
71+
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
72+
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
73+
>
74+
<DialogCore.Panel className="relative transform overflow-hidden rounded-lg bg-white px-4 pb-4 pt-5 text-left shadow-xl transition-all sm:my-8 sm:w-full sm:max-w-lg sm:p-6">
75+
{enableClose && (
76+
<div className="absolute right-0 top-0 hidden pr-4 pt-4 sm:block">
77+
<button
78+
type="button"
79+
className="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2"
80+
onClick={onClose}
81+
>
82+
<span className="sr-only">Close</span>
83+
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
84+
</button>
85+
</div>
86+
)}
87+
<div className="sm:flex sm:items-start">
88+
{renderLeftIcon && renderLeftIcon()}
89+
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
90+
{title && (
91+
<DialogCore.Title
92+
as="h3"
93+
className="text-base font-semibold leading-6 text-gray-900"
94+
>
95+
{title}
96+
</DialogCore.Title>
97+
)}
98+
<div className="mt-2">{children}</div>
99+
</div>
100+
</div>
101+
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
102+
{enableAccept && (
103+
<button
104+
type="button"
105+
className="inline-flex w-full justify-center rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 sm:ml-3 sm:w-auto"
106+
onClick={handleOnAccept}
107+
>
108+
{acceptLabel}
109+
</button>
110+
)}
111+
{enableCancel && (
112+
<button
113+
type="button"
114+
className="mt-3 inline-flex w-full justify-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 hover:bg-gray-50 sm:mt-0 sm:w-auto"
115+
onClick={handleOnCancel}
116+
>
117+
{cancelLabel}
118+
</button>
119+
)}
120+
</div>
121+
</DialogCore.Panel>
122+
</Transition.Child>
123+
</div>
124+
</div>
125+
</DialogCore>
126+
</Transition.Root>
127+
);
128+
};
129+
130+
export default Dialog;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "./Dialog";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { FC } from "react";
2+
3+
type Props = {
4+
error: string;
5+
};
6+
7+
const FieldError: FC<Props> = ({ error }) => {
8+
return <div className="mt-1 text-sm text-red-600">{error}</div>;
9+
};
10+
11+
export default FieldError;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "./FieldError";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Dialog from "@/components/core-ui/Dialog";
2+
import { ShieldCheckIcon } from "@heroicons/react/24/outline";
3+
import { FC } from "react";
4+
5+
type Props = {
6+
open: boolean;
7+
onClose: () => void;
8+
};
9+
10+
const TermsAndConditionsDialog: FC<Props> = ({ open, onClose }) => {
11+
const renderLeftIcon = () => {
12+
return (
13+
<div className="mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-indigo-100 sm:mx-0 sm:h-10 sm:w-10">
14+
<ShieldCheckIcon
15+
className="h-6 w-6 text-indigo-600"
16+
aria-hidden="true"
17+
/>
18+
</div>
19+
);
20+
};
21+
22+
return (
23+
<Dialog
24+
title="Terms and Conditions"
25+
enableCancel={false}
26+
{...{ open, onClose, renderLeftIcon }}
27+
>
28+
<p className="text-sm text-gray-700">
29+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
30+
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
31+
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
32+
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
33+
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
34+
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
35+
mollit anim id est laborum.
36+
</p>
37+
</Dialog>
38+
);
39+
};
40+
41+
export default TermsAndConditionsDialog;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "./TermsAndConditionsDialog";

client/src/enums/RequestStatus.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export enum RequestStatus {
2+
IDLE = "idle",
3+
LOADING = "loading",
4+
SUCCESS = "success",
5+
ERROR = "error",
6+
}

client/src/enums/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
export * from "./HttpMethod";
2+
export * from "./RequestStatus";

0 commit comments

Comments
 (0)