Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[promote production]: 28 April 2024 3:45 PM #18

Merged
merged 1 commit into from
Apr 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
- [Mogoose](https://mongoosejs.com/) - Database ODM
- [Axios](https://axios-http.com/) - Promise based Http Client
- [Formik](https://formik.org/) - Form handler
- [Zustand](https://zustand-demo.pmnd.rs/) - State Management

## Getting Started

Expand Down
6 changes: 3 additions & 3 deletions client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import HomePage from "@/pages/HomePage";
import { AuthProvider } from "@/providers";
import { Route, Routes } from "react-router-dom";

const LoginPage = lazy(() => import("@/pages/AuthPages/Login"));
const RegisterPage = lazy(() => import("@/pages/AuthPages/Register"));
const LoginPage = lazy(() => import("@/pages/AuthPages/LoginPage"));
const RegisterPage = lazy(() => import("@/pages/AuthPages/RegisterPage"));
const ForgotPasswordPage = lazy(
() => import("@/pages/AuthPages/ForgotPassword")
() => import("@/pages/AuthPages/ForgotPasswordPage")
);

function App() {
Expand Down
115 changes: 115 additions & 0 deletions client/src/components/core-ui/Alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import {
CheckCircleIcon,
ExclamationTriangleIcon,
XMarkIcon,
} from "@heroicons/react/24/outline";
import { FC, useEffect, useState } from "react";

type Props = {
message: string;
type: "success" | "error" | "warning" | "info";
};

const Alert: FC<Props> = ({ message, type }) => {
const [show, setShow] = useState(true);

useEffect(() => {
const timer = setTimeout(() => {
setShow(false);
}, 2000);

return () => {
clearTimeout(timer);
};
}, []);

const getAlertColor = () => {
switch (type) {
case "success":
return "bg-green-50";
case "error":
return "bg-red-50";
case "warning":
return "bg-yellow-50";
case "info":
return "bg-blue-50";
default:
return "bg-red-50";
}
};

const getCloserColor = () => {
switch (type) {
case "success":
return "text-green-500 hover:bg-green-100 focus:ring-green-600 focus:ring-offset-green-50";
case "error":
return "text-red-500 hover:bg-red-100 focus:ring-red-600 focus:ring-offset-red-50";
case "warning":
return "text-yellow-500 hover:bg-yellow-100 focus:ring-yellow-600 focus:ring-offset-yellow-50";
case "info":
return "text-blue-500 hover:bg-blue-100 focus:ring-blue-600 focus:ring-offset-blue-50";
default:
return "text-red-500 hover:bg-red-100 focus:ring-red-600 focus:ring-offset-red-50";
}
};

const getIcon = () => {
switch (type) {
case "success":
return (
<CheckCircleIcon
className="h-5 w-5 text-green-400"
aria-hidden="true"
/>
);
case "error":
return (
<ExclamationTriangleIcon
className="h-5 w-5 text-red-400"
aria-hidden="true"
/>
);
case "warning":
return (
<XMarkIcon className="h-5 w-5 text-yellow-400" aria-hidden="true" />
);
case "info":
return (
<XMarkIcon className="h-5 w-5 text-blue-400" aria-hidden="true" />
);
default:
return (
<XMarkIcon className="h-5 w-5 text-red-400" aria-hidden="true" />
);
}
};

if (!show) {
return null;
}

return (
<div className={`rounded-md ${getAlertColor()} p-4`}>
<div className="flex">
<div className="flex-shrink-0">{getIcon()}</div>
<div className="ml-3">
<p className="text-sm font-medium">{message}</p>
</div>
<div className="ml-auto pl-3">
<div className="-mx-1.5 -my-1.5">
<button
type="button"
className={`inline-flex rounded-md ${getCloserColor()} p-1.5 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-green-50`}
onClick={() => setShow(false)}
>
<span className="sr-only">Dismiss</span>
<XMarkIcon className="h-5 w-5" aria-hidden="true" />
</button>
</div>
</div>
</div>
</div>
);
};

export default Alert;
1 change: 1 addition & 0 deletions client/src/components/core-ui/Alert/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./Alert";
130 changes: 130 additions & 0 deletions client/src/components/core-ui/Dialog/Dialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { FC, Fragment } from "react";
import { Dialog as DialogCore, Transition } from "@headlessui/react";
import { XMarkIcon } from "@heroicons/react/24/outline";

type Props = {
open: boolean;
title?: string;
children: React.ReactNode;
acceptLabel?: string;
cancelLabel?: string;
enableClose?: boolean;
enableAccept?: boolean;
enableCancel?: boolean;
renderLeftIcon?: () => React.ReactNode;
onClose: () => void;
onAccept?: () => void;
onCancel?: () => void;
};

const Dialog: FC<Props> = ({
open,
children,
title,
acceptLabel = "OK",
cancelLabel = "Cancel",
enableClose = true,
enableAccept = true,
enableCancel = true,
onClose,
onAccept,
onCancel,
renderLeftIcon,
}) => {
const handleOnClose = () => {
onClose();
};

const handleOnAccept = () => {
onAccept && onAccept();
onClose();
};

const handleOnCancel = () => {
onCancel && onCancel();
onClose();
};

return (
<Transition.Root show={open} as={Fragment}>
<DialogCore as="div" className="relative z-10" onClose={handleOnClose}>
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in duration-200"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>

<div className="fixed inset-0 z-10 w-screen overflow-y-auto">
<div className="flex min-h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
<Transition.Child
as={Fragment}
enter="ease-out duration-300"
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
enterTo="opacity-100 translate-y-0 sm:scale-100"
leave="ease-in duration-200"
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
<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">
{enableClose && (
<div className="absolute right-0 top-0 hidden pr-4 pt-4 sm:block">
<button
type="button"
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"
onClick={onClose}
>
<span className="sr-only">Close</span>
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
)}
<div className="sm:flex sm:items-start">
{renderLeftIcon && renderLeftIcon()}
<div className="mt-3 text-center sm:ml-4 sm:mt-0 sm:text-left">
{title && (
<DialogCore.Title
as="h3"
className="text-base font-semibold leading-6 text-gray-900"
>
{title}
</DialogCore.Title>
)}
<div className="mt-2">{children}</div>
</div>
</div>
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
{enableAccept && (
<button
type="button"
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"
onClick={handleOnAccept}
>
{acceptLabel}
</button>
)}
{enableCancel && (
<button
type="button"
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"
onClick={handleOnCancel}
>
{cancelLabel}
</button>
)}
</div>
</DialogCore.Panel>
</Transition.Child>
</div>
</div>
</DialogCore>
</Transition.Root>
);
};

export default Dialog;
1 change: 1 addition & 0 deletions client/src/components/core-ui/Dialog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./Dialog";
11 changes: 11 additions & 0 deletions client/src/components/core-ui/FieldError/FieldError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { FC } from "react";

type Props = {
error: string;
};

const FieldError: FC<Props> = ({ error }) => {
return <div className="mt-1 text-sm text-red-600">{error}</div>;
};

export default FieldError;
1 change: 1 addition & 0 deletions client/src/components/core-ui/FieldError/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./FieldError";
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Dialog from "@/components/core-ui/Dialog";
import { ShieldCheckIcon } from "@heroicons/react/24/outline";
import { FC } from "react";

type Props = {
open: boolean;
onClose: () => void;
};

const TermsAndConditionsDialog: FC<Props> = ({ open, onClose }) => {
const renderLeftIcon = () => {
return (
<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">
<ShieldCheckIcon
className="h-6 w-6 text-indigo-600"
aria-hidden="true"
/>
</div>
);
};

return (
<Dialog
title="Terms and Conditions"
enableCancel={false}
{...{ open, onClose, renderLeftIcon }}
>
<p className="text-sm text-gray-700">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod
tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim
veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
commodo consequat. Duis aute irure dolor in reprehenderit in voluptate
velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint
occaecat cupidatat non proident, sunt in culpa qui officia deserunt
mollit anim id est laborum.
</p>
</Dialog>
);
};

export default TermsAndConditionsDialog;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./TermsAndConditionsDialog";
6 changes: 6 additions & 0 deletions client/src/enums/RequestStatus.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export enum RequestStatus {
IDLE = "idle",
LOADING = "loading",
SUCCESS = "success",
ERROR = "error",
}
1 change: 1 addition & 0 deletions client/src/enums/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from "./HttpMethod";
export * from "./RequestStatus";
Loading