Skip to content

Commit

Permalink
Merge pull request #59 from brandonnorsworthy/more-features
Browse files Browse the repository at this point in the history
More features
  • Loading branch information
brandonnorsworthy authored Sep 15, 2024
2 parents 37c07ac + 9c8a176 commit 39934cd
Show file tree
Hide file tree
Showing 20 changed files with 4,199 additions and 3,232 deletions.
6,836 changes: 3,816 additions & 3,020 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

39 changes: 8 additions & 31 deletions public/assets/text/news.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,11 @@
# Latest News

## September 21, 2024
- **New Release:** Version 1.0.0 is out!

this is a test link to [google](https://www.google.com)

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed ac purus sit amet nisl sollicitudin bibendum ac nec purus [link](https://www.google.com). Nulla facilisi cras fermentum odio eu feugiat. fusce id velit ut tortor pretium viverra suspendisse potenti.

bullet list:
- item 1
- item 2
- item 3

numbered list:
1. item 1
1. item 2
1. item 3

```css
body {
background-color: lightblue;
}
```

```html
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>
</html>
```
**New Release:** Version 1.0.0 is out!
- Major updates to the user interface
- Users can login/register to track their progress across devices
- Settings persist across sessions
- Guest users can keep track of their progress without an account on a single device

## Future Updates
- Light/Dark mode toggle
14 changes: 8 additions & 6 deletions src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,31 @@ interface MenuButtonProps {
type?: 'info' | 'cancel' | 'confirm';
htmlType?: 'button' | 'submit' | 'reset';
onClick?: () => void;
disabled?: boolean;
}

const Button: React.FC<MenuButtonProps> = ({ text, onClick, type, htmlType = "button" }) => {
const Button: React.FC<MenuButtonProps> = ({ text, onClick, type, htmlType = "button", disabled }) => {
let typeStyles = "";
switch (type) {
case "info":
typeStyles = `bg-buttonBackground-info text-buttonText-info`;
typeStyles = `bg-buttonBackground-info disabled:bg-buttonBackground text-buttonText-info hover:bg-buttonBackground-info-hover`;
break;
case "cancel":
typeStyles = `bg-buttonBackground-cancel text-buttonText-cancel`;
typeStyles = `bg-buttonBackground-cancel disabled:bg-buttonBackground text-buttonText-cancel hover:bg-buttonBackground-cancel-hover`;
break;
case "confirm":
typeStyles = `bg-buttonBackground-confirm text-buttonText-confirm`;
typeStyles = `bg-buttonBackground-confirm disabled:bg-buttonBackground text-buttonText-confirm hover:bg-buttonBackground-confirm-hover`;
break;
default:
typeStyles = "bg-buttonBackground text-buttonText";
typeStyles = "bg-buttonBackground disabled:bg-buttonBackground text-buttonText hover:bg-buttonBackground-hover";
break;
}

return (
<button
className={[`text-xl font-bold font-roboto py-3 px-6 min-w-full sm:min-w-36`, typeStyles].join(" ")}
className={`text-xl font-bold font-roboto py-3 px-6 min-w-full sm:min-w-36 ${typeStyles}`}
onClick={onClick}
disabled={disabled}
type={htmlType}
>
{text.toUpperCase()}
Expand Down
176 changes: 96 additions & 80 deletions src/components/LoginPanel/LoginPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import React, { useEffect, useState } from "react";
import { AxiosError } from "axios";

import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { Input } from "../ui/input";

import AuthService from "@/service/authService";
import { useAuth } from "@/context/useAuth";
import Button from "../Button";
import { toast } from "../Toaster";
import authService from "@/service/authService";

interface LoginPanelProps {
onLoginSuccess: () => void;
Expand Down Expand Up @@ -66,101 +59,124 @@ const LoginPanel: React.FC<LoginPanelProps> = (props) => {
catch (error: unknown) {
console.error(error);
if (error instanceof AxiosError) {
if (error.response?.status === 429) {
setError("Too many requests. Please try again later.");
return;
}
setError(error.response?.data.error || "An error occurred");
return;
}
setError("An unkown error occurred");
}
}

const loginAsGuest = async () => {
try {
const loginGuestResponse = await authService.guestLogin();
saveToken(loginGuestResponse.token);
onRegistrationSuccess();
} catch (error: unknown) {
toast.error("Failed to log in as guest", error);
}
};

return (
<Dialog open={true} modal>
<DialogContent
className="bg-slate-100"
>
<DialogHeader>
<DialogTitle className='text-muted-foreground'>{isRegistering ? "Register" : "Login"}</DialogTitle>
<DialogDescription>
Please enter your credentials to continue
</DialogDescription>
</DialogHeader>
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div className="w-full max-w-md p-2 shadow-lg bg-secondary">
<div className="p-2 text-text">
<h2 className="text-lg font-semibold text-muted-foreground">{isRegistering ? "Register" : "Login"}</h2>
<p className="mt-2 text-sm text-text-secondary">Please enter your credentials to continue or&nbsp;
<span
onClick={loginAsGuest}
className="underline cursor-pointer text-buttonText-info"
aria-label="Continue as a guest without registration"
>
continue as Guest
</span>
</p>
</div>
<form className="flex flex-col" onSubmit={handleSubmit}>
<div className="p-2">
<label htmlFor="username" className="sr-only">
Username
</label>
<Input
id="username"
type="text"
placeholder="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
aria-label="Username"
/>
</div>
<div className="p-2">
<label htmlFor="password" className="sr-only">
Password
</label>
<Input
id="password"
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
aria-label="Password"
/>
</div>
<label htmlFor="username" className="sr-only">
Username
</label>
<input
id="username"
type="text"
placeholder="username"
value={username}
onChange={(e) => setUsername(e.target.value)}
className="w-full p-2 mt-4 border-gray-300 font-semi text-text placeholder:text-text/50 bg-white/25"
aria-label="Username"
/>

<label htmlFor="password" className="sr-only">
Password
</label>
<input
id="password"
type="password"
placeholder="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full p-2 mt-2 border-gray-300 font-semi text-text placeholder:text-text/50 bg-white/25"
aria-label="Password"
/>

{
isRegistering && (
<div className="p-2">
<>
<label htmlFor="confirmPassword" className="sr-only">
Confirm Password
</label>
<Input
<input
id="confirmPassword"
type="password"
placeholder="confirm password"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
className="w-full p-2 mt-2 border-gray-300 font-semi text-text placeholder:text-text/50 bg-white/25"
aria-label="confirmPassword"
/>
</div>
</>
)
}
<div className="flex justify-end">
<div className="flex flex-col items-end">
{error && (
<p className="text-right text-red-500">
{error}
</p>
)}
<button
type="submit"
className="p-2 mt-2 text-white transition-colors rounded-md bg-slate-500 hover:bg-slate-400"
aria-label={isRegistering ? "Register" : "Login"}
>
{isRegistering ? "Register" : "Login"}
</button>
</div>

<div className="flex flex-col items-end justify-center w-full p-2 text-text text-end">
<span
onClick={() => setIsRegistering(!isRegistering)}
className="underline cursor-pointer"
aria-label={isRegistering ? "Click here to log in" : "Click here to register"}
>
{isRegistering ? "Already have an account? Click to Log In" : "New here? Click to Register"}
</span>

</div>

{
error ?
<div className="w-full mt-2 text-red-500 text-end">
{error}
</div> :
<div className="w-full mt-2 text-red-500 text-end">
&nbsp;
</div>
}

<div className="flex justify-between mt-2">
<Button
text="cancel"
htmlType="button"
/>
<Button
text={isRegistering ? "Register" : "Login"}
htmlType="submit"
type="confirm"
/>
</div>
</form>
<DialogFooter>
<button
className="border-none text-muted-foreground bg-none test-sm"
onClick={() => setIsRegistering(!isRegistering)}
aria-label={isRegistering ? "Switch to Login" : "Switch to Register"}
>
{
isRegistering ?
"Click here to Login instead" :
"Click here to Register instead"
}
</button>
</DialogFooter>
</DialogContent>
</Dialog>
);
</div>
</div>
)
};

export default LoginPanel;
10 changes: 5 additions & 5 deletions src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ interface ModalProps {
}

const QuestModal: React.FC<ModalProps> = ({ onClose, isOpen = true, children }) => {
const modal = useRef<HTMLDivElement>(null);
const modalRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const handleEscape = (event: KeyboardEvent) => {
Expand All @@ -17,7 +17,7 @@ const QuestModal: React.FC<ModalProps> = ({ onClose, isOpen = true, children })
};

const handleClickOutside = (event: MouseEvent) => {
if (modal.current && !modal.current.contains(event.target as Node)) {
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
onClose();
}
};
Expand All @@ -34,9 +34,9 @@ const QuestModal: React.FC<ModalProps> = ({ onClose, isOpen = true, children })
if (!isOpen) return null;

return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black bg-opacity-50">
<div ref={modal} className="p-2 bg-secondary max-h-[90vh] shadow-lg">
<div className="max-w-full m-0 h-full max-h-[85vh] overflow-y-auto sm:m-2 md:m-0 md:max-w-[42rem] text-text">
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
<div ref={modalRef} className="p-2 bg-secondary max-h-[90dvh] shadow-lg">
<div className="max-w-full m-0 h-full max-h-[85dvh] overflow-y-auto sm:m-2 md:m-0 md:max-w-[42rem] text-text scrollbar-modern">
{children}
</div>
</div>
Expand Down
Loading

0 comments on commit 39934cd

Please sign in to comment.