diff --git a/public/LICENSE.txt b/public/LICENSE.txt new file mode 100644 index 0000000..44c9efc --- /dev/null +++ b/public/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Pete Pongpeauk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/public/images/LICENSE.txt b/public/images/LICENSE.txt new file mode 100644 index 0000000..44c9efc --- /dev/null +++ b/public/images/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Pete Pongpeauk + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/components/CheckActivationCodeModal.tsx b/src/components/CheckActivationCodeModal.tsx index 355a94e..35f03c6 100644 --- a/src/components/CheckActivationCodeModal.tsx +++ b/src/components/CheckActivationCodeModal.tsx @@ -116,15 +116,15 @@ export default function CheckActivationCodeModal({ + - diff --git a/src/components/Footer.tsx b/src/components/Footer.tsx index 684f707..4117aae 100644 --- a/src/components/Footer.tsx +++ b/src/components/Footer.tsx @@ -47,8 +47,9 @@ export default function Footer({ type = 'platform' }: { type?: 'public' | 'platf fontWeight={'bold'} letterSpacing={'tight'} > - PT Amperra Sambung Semesta Sentosa + PT Amperra Sambung Semesta Sentosa{' '} + under the MIT License. diff --git a/src/components/nav/Nav.tsx b/src/components/nav/Nav.tsx index 45f74fc..7ce6896 100644 --- a/src/components/nav/Nav.tsx +++ b/src/components/nav/Nav.tsx @@ -122,6 +122,7 @@ export default function Nav({ type }: { type?: string }) { h={'6rem'} align={'center'} bg={useColorModeValue('white', 'gray.800')} + borderBottom={'1px solid'} borderColor={useColorModeValue('gray.300', 'gray.700')} zIndex={50} > @@ -149,7 +150,7 @@ export default function Nav({ type }: { type?: string }) { > + + {/* Reset Password Modal */} + + + + Reset Password + + { + sendPasswordResetEmail(auth, values.email) + .then(() => { + toast({ + title: 'Password reset email sent.', + description: 'Please check your inbox to reset your password.', + status: 'success', + duration: 9000, + isClosable: true, + }); + onResetModalClose(); + }) + .catch((error) => { + const errorCode = error.code; + let errorMessage = error.message; + switch (errorCode) { + case 'auth/invalid-email': + errorMessage = "The email address you've entered is invalid. Please try again."; + break; + case 'auth/user-not-found': + errorMessage = "No account found with that email address."; + break; + default: + errorMessage = 'An unknown error occurred. Please try again.'; + } + toast({ + title: 'Error sending reset email', + description: errorMessage, + status: 'error', + duration: 5000, + isClosable: true, + }); + }) + .finally(() => { + actions.setSubmitting(false); + }); + }} + > + {(props) => ( +
+ + Enter your email address below and we'll send you a link to reset your password. + + {({ field, form }: any) => ( + + Email address + + + + + + + + )} + + + + + + +
+ )} +
+
+
+
- + {/* Background Image */} + {'Background'} + + {/* Login Panel (Left Side) */} { - !loading && !user ? - ( + ) : !user ? ( + - {'Login'} - {/* - - */} - - + - - Welcome! - - Please present your credentials to continue. - -
- - { + Welcome! + + Please present your credentials to continue. + +
+ + { + if (isRegisterMode) { + // Registration Logic + if (values.password !== values.confirmPassword) { + toast({ + title: 'Passwords do not match.', + description: 'Please ensure your password and confirmation match.', + status: 'error', + duration: 5000, + isClosable: true + }); + actions.setSubmitting(false); + return; + } + if (!values.activationCode) { + toast({ + title: 'Activation code required.', + description: 'Please enter your activation code.', + status: 'error', + duration: 5000, + isClosable: true + }); + actions.setSubmitting(false); + return; + } + fetch(`/api/v1/activation/${values.activationCode}`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + displayName: values.displayName, + email: values.email, + username: values.username, + password: values.password + }) + }) + .then((res) => { + if (res.status === 200) { + return res.json(); + } else { + return res.json().then((json) => { + throw new Error(json.message || 'Registration failed'); + }); + } + }) + .then(() => { + toast({ + title: 'Account created.', + description: 'You can now log in.', + status: 'success', + duration: 5000, + isClosable: true + }); + setIsRegisterMode(false); // Switch back to login mode + actions.resetForm(); + }) + .catch((error) => { + toast({ + title: 'There was an error while creating your account.', + description: error.message, + status: 'error', + duration: 5000, + isClosable: true + }); + }) + .finally(() => { + actions.setSubmitting(false); + }); + } else { + // Login Logic signInWithEmailAndPassword(auth, values.email, values.password) .then(() => { redirectOnAuth(); @@ -212,20 +385,17 @@ export default function Login() { case 'auth/invalid-email': errorMessage = 'The email address you provided is invalid.'; break; - case 'auth/invalid-password': + case 'auth/invalid-credential': + case 'auth/user-not-found': + case 'auth/wrong-password': errorMessage = "Invalid email address or password. Please try again."; break; case 'auth/user-disabled': errorMessage = 'Your account has been disabled.'; break; - case 'auth/user-not-found': - errorMessage = "Invalid email address or password. Please try again."; - break; - case 'auth/wrong-password': - errorMessage = "Invalid email address or password. Please try again."; - break; case 'auth/too-many-requests': errorMessage = 'Too many attempts. Please try again later.'; + break; default: errorMessage = 'An unknown error occurred.'; } @@ -239,98 +409,219 @@ export default function Login() { .finally(() => { actions.setSubmitting(false); }); - }} + } + }} + > + {(props) => ( +
+ + {({ field, form }: any) => ( + + Email + + + + + + + + )} + + + {({ field, form }: any) => ( + + Password + + + + + + + + )} + + + + + + {({ field, form }: any) => ( + + Confirm Password + + + + + + + + )} + + + + {({ field, form }: any) => ( + + Display Name + + + + + + + + )} + + + {({ field, form }: any) => ( + + Username + + + + + + + + )} + + + + + {({ field, form }: any) => ( + + Activation Code + + + + + + + + )} + + + + + +
+ )} +
+ + {isRegisterMode ? ( + + Already have an account?{' '} + setIsRegisterMode(false)} cursor="pointer" textDecor={'underline'} textUnderlineOffset={4}> + Login + + + ) : ( + - - )} - + Create an Account + + )} + {isRegisterMode && ( - - Forgot your password? - + By creating an account, you agree to our{' '} + + + Terms of Use + + {' '} + and{' '} + + + Privacy Policy + + + . - - - Have an activation code? - - - - Need help?{' '} - - View the FAQ. - - -
-
+ )} + + + Forgot your password? + + + + Need help?{' '} + + View the FAQ. + + + + +
- ) : <> - - - - + ) : ( + // User is logged in, redirectOnAuth has been called. Show spinner during transition. + + ) } - +
);