diff --git a/package-lock.json b/package-lock.json
index 5f65aea4..397cf321 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,6 +10,7 @@
"license": "MIT",
"dependencies": {
"@heroicons/react": "^2.0.17",
+ "@tanstack/react-query": "^5.17.8",
"@types/offscreencanvas": "^2019.7.1",
"leaflet": "^1.9.4",
"prop-types": "^15.8.1",
@@ -256,6 +257,30 @@
"node": ">=14.0.0"
}
},
+ "node_modules/@tanstack/query-core": {
+ "version": "5.17.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.17.8.tgz",
+ "integrity": "sha512-V4hQv4jmRwbji9wo3F6/JQEjbWLUlv2sE2K5R49girEyok71ksDnVHbQiAvp4+FbovMY8A3IbP3cH3jqAUe/BQ==",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/react-query": {
+ "version": "5.17.8",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.17.8.tgz",
+ "integrity": "sha512-J+QMBoQiAZglwVB/bEgmf9IczLPg0YwFaqmn4aTW72ZYkUYyBU9dj6OMQk3RzmD9RzUz2m7pPBCkcT0Z1i0acg==",
+ "dependencies": {
+ "@tanstack/query-core": "5.17.8"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^18.0.0"
+ }
+ },
"node_modules/@trysound/sax": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
diff --git a/package.json b/package.json
index c8611279..3278e600 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
},
"dependencies": {
"@heroicons/react": "^2.0.17",
+ "@tanstack/react-query": "^5.17.8",
"@types/offscreencanvas": "^2019.7.1",
"leaflet": "^1.9.4",
"prop-types": "^15.8.1",
diff --git a/src/Components/AppShell/AppShell.tsx b/src/Components/AppShell/AppShell.tsx
index 13d8124d..e9f78914 100644
--- a/src/Components/AppShell/AppShell.tsx
+++ b/src/Components/AppShell/AppShell.tsx
@@ -6,31 +6,37 @@ import { QuestsProvider } from '../Gaming/hooks/useQuests'
import { AssetsProvider, useSetAssetApi } from './hooks/useAssets'
import { SetAssetsApi } from './SetAssetsApi'
import { AssetsApi } from '../../types'
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
-export function AppShell({ appName, nameWidth, children, assetsApi } : {appName: string, nameWidth?: number, children: React.ReactNode, assetsApi: AssetsApi}) {
+export function AppShell({ appName, nameWidth, children, assetsApi }: { appName: string, nameWidth?: number, children: React.ReactNode, assetsApi: AssetsApi }) {
+
+ // Create a client
+ const queryClient = new QueryClient()
return (
-
-
-
-
-
-
-
- {children}
-
-
-
-
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+
+
)
}
diff --git a/src/Components/AppShell/Content.tsx b/src/Components/AppShell/Content.tsx
index 31db7f6b..4c463756 100644
--- a/src/Components/AppShell/Content.tsx
+++ b/src/Components/AppShell/Content.tsx
@@ -6,7 +6,7 @@ type ContentProps = {
export function Content({children} : ContentProps) {
return (
-
+
{children}
diff --git a/src/Components/AppShell/NavBar.tsx b/src/Components/AppShell/NavBar.tsx
index 36699ebb..11978f15 100644
--- a/src/Components/AppShell/NavBar.tsx
+++ b/src/Components/AppShell/NavBar.tsx
@@ -10,63 +10,11 @@ import DialogModal from "../Templates/DialogModal";
export default function NavBar({ appName, nameWidth = 200}: { appName: string, nameWidth?: number }) {
-
- const [signupOpen, setSignupOpen] = useState(false);
- const [loginOpen, setLoginOpen] = useState(false);
-
-
- const [email, setEmail] = useState
("");
- const [userName, setUserName] = useState("");
- const [password, setPassword] = useState("");
-
-
- const { isAuthenticated, user, login, register, loading, logout, token } = useAuth();
-
const navigate = useNavigate();
- const onRegister = async () => {
- await toast.promise(
- register({ email: email, password: password }, userName),
- {
- success: {
- render({ data }) {
- navigate(`/`);
- return `Hi ${data?.first_name}`
- },
- // other options
- icon: "✌️",
- },
- error: {
- render( {data} ) {
- return `${data}`
- },
- },
- pending: 'creating new user ...'
- });
- setSignupOpen(false);
- }
- const onLogin = async () => {
- await toast.promise(
- login({ email: email, password: password }),
- {
- success: {
- render({ data }) {
- navigate(`/`);
- return `Hi ${data?.first_name}`
- },
- // other options
- icon: "✌️",
- },
- error: {
- render( {data} ) {
- return `${data}`
- },
- },
- pending: 'logging in ...'
- });
- setLoginOpen(false);
- }
+
+ const { isAuthenticated, user, logout, token } = useAuth();
const onLogout = () => {
toast.promise(
@@ -133,11 +81,11 @@ export default function NavBar({ appName, nameWidth = 200}: { appName: string, n
-
setLoginOpen(true)} className="tw-btn tw-btn-ghost tw-mr-2">
+
navigate("/login")} className="tw-btn tw-btn-ghost tw-mr-2">
Login
-
setSignupOpen(true)} className="tw-btn tw-btn-ghost tw-mr-2">
+
navigate("/signup")} className="tw-btn tw-btn-ghost tw-mr-2">
Sign Up
@@ -150,35 +98,15 @@ export default function NavBar({ appName, nameWidth = 200}: { appName: string, n
}
- setLoginOpen(false)}>
- setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
- setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
-
-
-
-
- setSignupOpen(false)}>
- setUserName(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
- setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
- setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
-
-
-
-
+
>
)
}
diff --git a/src/Components/Auth/LoginPage.tsx b/src/Components/Auth/LoginPage.tsx
index a26a78ab..b2c90a85 100644
--- a/src/Components/Auth/LoginPage.tsx
+++ b/src/Components/Auth/LoginPage.tsx
@@ -1,66 +1,53 @@
-import {useState} from 'react'
-import {Link} from 'react-router-dom'
+import { useRef, useState } from 'react'
+import { Link, useNavigate } from 'react-router-dom'
import ErrorText from '../Typography/ErrorText'
-import {TextInput} from '../Input/TextInput'
+import { TextInput } from '../Input/TextInput'
import * as React from 'react'
+import { toast } from 'react-toastify'
+import { useAuth } from './useAuth'
+import { MapOverlayPage} from '../Templates'
-export function LoginPage(){
+export function LoginPage() {
- const INITIAL_LOGIN_OBJ = {
- password : "",
- emailId : ""
+ const [email, setEmail] = useState("");
+ const [password, setPassword] = useState("");
+
+ const { login, loading } = useAuth();
+
+ const navigate = useNavigate();
+
+ const onLogin = async () => {
+ await toast.promise(
+ login({ email: email, password: password }),
+ {
+ success: {
+ render({ data }) {
+ navigate(`/`);
+ return `Hi ${data?.first_name}`
+ },
+ // other options
+ icon: "✌️",
+ },
+ error: {
+ render({ data }) {
+ return `${data}`
+ },
+ },
+ pending: 'logging in ...'
+ });
}
- const [loading, setLoading] = useState(false)
- const [errorMessage, setErrorMessage] = useState("")
- const [loginObj, setLoginObj] = useState(INITIAL_LOGIN_OBJ)
-
- const submitForm = (e) =>{
- e.preventDefault()
- setErrorMessage("")
-
- if(loginObj.emailId.trim() === "")return setErrorMessage("Email Id is required! (use any value)")
- if(loginObj.password.trim() === "")return setErrorMessage("Password is required! (use any value)")
- else{
- setLoading(true)
- // Call API to check user credentials and save token in localstorage
- localStorage.setItem("token", "DumyTokenHere")
- setLoading(false)
- window.location.href = '/app/welcome'
- }
- }
-
- const updateFormValue = (val: string) => {
- console.log(val)
- }
-
- return(
-
-
-
+
)
}
+
diff --git a/src/Components/Auth/RequestPasswordPage.tsx b/src/Components/Auth/RequestPasswordPage.tsx
new file mode 100644
index 00000000..d297f8a8
--- /dev/null
+++ b/src/Components/Auth/RequestPasswordPage.tsx
@@ -0,0 +1,48 @@
+import { useState } from 'react'
+import { useNavigate } from 'react-router-dom'
+
+import * as React from 'react'
+import { toast } from 'react-toastify'
+import { useAuth } from './useAuth'
+import { MapOverlayPage} from '../Templates'
+
+export function RequestPasswordPage({reset_url}) {
+
+ const [email, setEmail] = useState
("");
+
+ const { requestPasswordReset, loading } = useAuth();
+
+ const navigate = useNavigate();
+
+ const onReset = async () => {
+ await toast.promise(
+ requestPasswordReset( email, reset_url),
+ {
+ success: {
+ render() {
+ navigate(`/`);
+ return `Check your mailbox`
+ },
+ // other options
+ icon: "📬",
+ },
+ error: {
+ render({ data }) {
+ return `${data}`
+ },
+ },
+ pending: 'sending email ...'
+ });
+ }
+
+ return (
+
+ Reset Password
+ setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
+
+
+
+
+ )
+}
+
diff --git a/src/Components/Auth/SetNewPasswordPage.tsx b/src/Components/Auth/SetNewPasswordPage.tsx
new file mode 100644
index 00000000..676404db
--- /dev/null
+++ b/src/Components/Auth/SetNewPasswordPage.tsx
@@ -0,0 +1,50 @@
+import { useRef, useState } from 'react'
+import { Link, useNavigate } from 'react-router-dom'
+import ErrorText from '../Typography/ErrorText'
+import { TextInput } from '../Input/TextInput'
+import * as React from 'react'
+import { toast } from 'react-toastify'
+import { useAuth } from './useAuth'
+import { MapOverlayPage} from '../Templates'
+
+export function SetNewPasswordPage() {
+
+ const [password, setPassword] = useState("");
+
+ const { passwordReset, loading } = useAuth();
+
+ const navigate = useNavigate();
+
+ const onReset = async () => {
+ const token = window.location.search.split("token=")[1];
+ console.log(token);
+
+ await toast.promise(
+ passwordReset(token, password),
+ {
+ success: {
+ render() {
+ navigate(`/`);
+ return `New password set`
+ },
+ },
+ error: {
+ render({ data }) {
+ return `${data}`
+ },
+ },
+ pending: 'setting password ...'
+ });
+ }
+
+ return (
+
+ Set new Password
+ setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
+
+
+
+
+ )
+}
+
diff --git a/src/Components/Auth/SignupPage.tsx b/src/Components/Auth/SignupPage.tsx
index 38879502..64b54e78 100644
--- a/src/Components/Auth/SignupPage.tsx
+++ b/src/Components/Auth/SignupPage.tsx
@@ -1,63 +1,55 @@
-import { useState } from 'react'
-import { Link } from 'react-router-dom'
+import { useRef, useState } from 'react'
+import { Link, useNavigate } from 'react-router-dom'
import ErrorText from '../Typography/ErrorText'
-import {TextInput} from '../Input/TextInput'
+import { TextInput } from '../Input/TextInput'
import * as React from 'react'
+import { toast } from 'react-toastify'
+import { useAuth } from './useAuth'
+import { MapOverlayPage } from '../Templates'
export function SignupPage() {
- const INITIAL_REGISTER_OBJ = {
- name : "",
- password : "",
- emailId : ""
+ const [email, setEmail] = useState("");
+ const [userName, setUserName] = useState("");
+
+ const [password, setPassword] = useState("");
+
+ const { register, loading } = useAuth();
+
+ const navigate = useNavigate();
+
+ const onRegister = async () => {
+ await toast.promise(
+ register({ email: email, password: password }, userName),
+ {
+ success: {
+ render({ data }) {
+ navigate(`/`);
+ return `Hi ${data?.first_name}`
+ },
+ // other options
+ icon: "✌️",
+ },
+ error: {
+ render({ data }) {
+ return `${data}`
+ },
+ },
+ pending: 'creating new user ...'
+ });
}
- const [loading, setLoading] = useState(false)
- const [errorMessage, setErrorMessage] = useState("")
- const [registerObj, setRegisterObj] = useState(INITIAL_REGISTER_OBJ)
-
- const submitForm = (e) =>{
- e.preventDefault()
- setErrorMessage("")
-
- if(registerObj.name.trim() === "")return setErrorMessage("Name is required! (use any value)")
- if(registerObj.emailId.trim() === "")return setErrorMessage("Email Id is required! (use any value)")
- if(registerObj.password.trim() === "")return setErrorMessage("Password is required! (use any value)")
- else{
- setLoading(true)
- // Call API to check user credentials and save token in localstorage
- localStorage.setItem("token", "DumyTokenHere")
- setLoading(false)
- window.location.href = '/app/welcome'
- }
- }
-
- const updateFormValue = (val: string) => {
- console.log(val)
- }
return (
-
-
+
)
}
+
diff --git a/src/Components/Auth/index.tsx b/src/Components/Auth/index.tsx
index 0142c432..1e3aee63 100644
--- a/src/Components/Auth/index.tsx
+++ b/src/Components/Auth/index.tsx
@@ -1,3 +1,5 @@
export {AuthProvider, useAuth} from "./useAuth"
export {LoginPage} from "./LoginPage"
-export {SignupPage} from "./SignupPage"
\ No newline at end of file
+export {SignupPage} from './SignupPage'
+export {RequestPasswordPage} from './RequestPasswordPage'
+export {SetNewPasswordPage} from './SetNewPasswordPage'
\ No newline at end of file
diff --git a/src/Components/Auth/useAuth.tsx b/src/Components/Auth/useAuth.tsx
index 8b1c924f..043b3e06 100644
--- a/src/Components/Auth/useAuth.tsx
+++ b/src/Components/Auth/useAuth.tsx
@@ -24,7 +24,9 @@ type AuthContextProps = {
loading: Boolean,
logout: () => Promise
,
updateUser: (user: UserItem) => any,
- token: String | null
+ token: String | null,
+ requestPasswordReset: (email:string, reset_url: string) => Promise,
+ passwordReset: (token:string, new_password:string) => Promise
}
const AuthContext = createContext({
@@ -35,7 +37,9 @@ const AuthContext = createContext({
loading: false,
logout: () => Promise.reject(),
updateUser: () => Promise.reject(),
- token: ""
+ token: "",
+ requestPasswordReset: () => Promise.reject(),
+ passwordReset: () => Promise.reject()
});
export const AuthProvider = ({ userApi, children }: AuthProviderProps) => {
@@ -118,10 +122,34 @@ export const AuthProvider = ({ userApi, children }: AuthProviderProps) => {
};
}
+ const requestPasswordReset = async (email: string, reset_url?: string): Promise => {
+ setLoading(true);
+ try {
+ await userApi.requestPasswordReset(email, reset_url);
+ return setLoading(false);
+ } catch (error: any) {
+ setLoading(false);
+ throw error;
+ };
+ }
+
+
+ const passwordReset = async (token: string, new_password:string): Promise => {
+ setLoading(true);
+ try {
+ await userApi.passwordReset(token, new_password);
+ return setLoading(false);
+ } catch (error: any) {
+ setLoading(false);
+ throw error;
+ };
+ }
+
+
return (
{children}
diff --git a/src/Components/Map/UtopiaMap.tsx b/src/Components/Map/UtopiaMap.tsx
index 7012bb52..7c1f440e 100644
--- a/src/Components/Map/UtopiaMap.tsx
+++ b/src/Components/Map/UtopiaMap.tsx
@@ -18,6 +18,7 @@ import { LeafletRefsProvider } from "./hooks/useLeafletRefs";
import { LayerControl } from "./Subcomponents/LayerControl";
import { QuestControl } from "./Subcomponents/QuestControl";
import { Control } from "./Subcomponents/Control";
+import { Outlet } from "react-router-dom";
export interface MapEventListenerProps {
@@ -71,6 +72,7 @@ function UtopiaMap({
return (
+ <>
@@ -114,6 +116,8 @@ function UtopiaMap({
+
+ >
);
}
diff --git a/src/Components/Profile/ColorPicker.tsx b/src/Components/Profile/ColorPicker.tsx
index 49a941ab..d18afb5e 100644
--- a/src/Components/Profile/ColorPicker.tsx
+++ b/src/Components/Profile/ColorPicker.tsx
@@ -4,7 +4,7 @@ import { HexColorPicker } from "react-colorful";
import "./ColorPicker.css"
import useClickOutside from "./useClickOutside";
-export const ColorPicker = ({ color, onChange, className }) => {
+export const ColorPicker = ({ color = "#000", onChange, className }) => {
const popover = useRef(null);
const [isOpen, toggle] = useState(false);
diff --git a/src/Components/Profile/ProfileSettings.tsx b/src/Components/Profile/ProfileSettings.tsx
index 5ac14884..ba729d08 100644
--- a/src/Components/Profile/ProfileSettings.tsx
+++ b/src/Components/Profile/ProfileSettings.tsx
@@ -158,8 +158,8 @@ export function ProfileSettings() {
return (
- <>
-
+
+
@@ -219,6 +219,6 @@ export function ProfileSettings() {
renderCrop();
}}>Select
- >
+
)
}
diff --git a/src/Components/Templates/MapOverlayPage.tsx b/src/Components/Templates/MapOverlayPage.tsx
new file mode 100644
index 00000000..5b7b2e2e
--- /dev/null
+++ b/src/Components/Templates/MapOverlayPage.tsx
@@ -0,0 +1,30 @@
+
+import * as React from 'react'
+import { useNavigate } from 'react-router-dom';
+
+export function MapOverlayPage({children} : {children: React.ReactNode}) {
+
+
+ const closeScreen = () => {
+ navigate(`/`);
+ }
+
+ const navigate = useNavigate();
+
+
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+
+ )
+}
+
diff --git a/src/Components/Templates/index.tsx b/src/Components/Templates/index.tsx
index 14848776..9c08cb10 100644
--- a/src/Components/Templates/index.tsx
+++ b/src/Components/Templates/index.tsx
@@ -1,2 +1,3 @@
export {CardPage} from './CardPage'
export {TitleCard} from './TitleCard'
+export {MapOverlayPage} from './MapOverlayPage'
\ No newline at end of file
diff --git a/src/index.tsx b/src/index.tsx
index 651044e2..22f77e17 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,6 +1,6 @@
export { UtopiaMap, Layer, Tags, Permissions, ItemForm, ItemView, PopupTextAreaInput, PopupStartEndInput, PopupTextInput, PopupButton, TextView, StartEndView } from './Components/Map';
export {AppShell, Content, SideBar} from "./Components/AppShell"
-export {AuthProvider, useAuth, LoginPage, SignupPage} from "./Components/Auth"
+export {AuthProvider, useAuth, LoginPage, SignupPage, RequestPasswordPage, SetNewPasswordPage} from "./Components/Auth"
export {UserSettings, ProfileSettings} from './Components/Profile'
export {Quests, Modal} from './Components/Gaming'
export {TitleCard, CardPage} from './Components/Templates'
diff --git a/src/types.ts b/src/types.ts
index 8da2a863..c6af7179 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -87,7 +87,9 @@ export interface UserApi {
logout(): Promise,
getUser(): Promise,
getToken(): Promise,
- updateUser(user: UserItem): Promise
+ updateUser(user: UserItem): Promise,
+ requestPasswordReset(email:string, reset_url?:string),
+ passwordReset(token:string,new_password:string)
}
export type UserItem = {