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

More features #59

Merged
merged 6 commits into from
Sep 15, 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
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