password reset implemented

This commit is contained in:
Anton 2024-01-08 22:21:35 +01:00
parent f5dd097228
commit 3be1718022
18 changed files with 323 additions and 219 deletions

25
package-lock.json generated
View File

@ -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",

View File

@ -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",

View File

@ -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 (
<BrowserRouter>
<AssetsProvider>
<SetAssetsApi assetsApi={assetsApi}></SetAssetsApi>
<QuestsProvider initialOpen={true}>
<ToastContainer position="top-right"
autoClose={2000}
hideProgressBar
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
theme="light" />
<NavBar appName={appName} nameWidth={nameWidth}></NavBar>
<div id="app-content" className="tw-flex tw-!pl-[77px]">
{children}
</div>
</QuestsProvider>
</AssetsProvider>
</BrowserRouter>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<AssetsProvider>
<SetAssetsApi assetsApi={assetsApi}></SetAssetsApi>
<QuestsProvider initialOpen={true}>
<ToastContainer position="top-right"
autoClose={2000}
hideProgressBar
newestOnTop={false}
closeOnClick
rtl={false}
pauseOnFocusLoss
draggable
pauseOnHover
theme="light" />
<NavBar appName={appName} nameWidth={nameWidth}></NavBar>
<div id="app-content" className="tw-flex tw-!pl-[77px]">
{children}
</div>
</QuestsProvider>
</AssetsProvider>
</BrowserRouter>
</QueryClientProvider>
)
}

View File

@ -6,7 +6,7 @@ type ContentProps = {
export function Content({children} : ContentProps) {
return (
<div className='tw-flex tw-flex-col tw-w-full tw-relative'>
<div className='tw-flex tw-flex-col tw-w-full tw-bg-base-200 tw-relative'>
{children}
</div>

View File

@ -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<string>("");
const [userName, setUserName] = useState<string>("");
const [password, setPassword] = useState<string>("");
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
<div>
<div className="tw-hidden md:tw-flex">
<div onClick={() => setLoginOpen(true)} className="tw-btn tw-btn-ghost tw-mr-2">
<div onClick={() => navigate("/login")} className="tw-btn tw-btn-ghost tw-mr-2">
Login
</div>
<div onClick={() => setSignupOpen(true)} className="tw-btn tw-btn-ghost tw-mr-2">
<div onClick={() => navigate("/signup")} className="tw-btn tw-btn-ghost tw-mr-2">
Sign Up
</div>
</div>
@ -150,35 +98,15 @@ export default function NavBar({ appName, nameWidth = 200}: { appName: string, n
</label>
<ul tabIndex={1} className="tw-menu tw-dropdown-content tw-mt-3 tw-p-2 tw-shadow tw-bg-base-100 tw-rounded-box tw-w-52 !tw-z-[10000]">
<li><a onClick={() => {setLoginOpen(true)}}>Login</a></li>
<li><a onClick={() => setSignupOpen(true)}>Sign Up</a></li>
<li><a onClick={() => {() => navigate("/login")}}>Login</a></li>
<li><a onClick={() => () => navigate("/signup")}>Sign Up</a></li>
</ul>
</div>
</div>
}
</div>
<DialogModal
title="Login"
isOpened={loginOpen}
onClose={() => setLoginOpen(false)}>
<input type="email" placeholder="E-Mail" value={email} onChange={e => setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<input type="password" placeholder="Password" onChange={e => setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<div className="tw-card-actions">
<button className={loading ? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' : 'tw-btn tw-btn-primary tw-btn-block'} onClick={() => onLogin()}>{loading ? <span className="tw-loading tw-loading-spinner"></span> : 'Login'}</button>
</div>
</DialogModal>
<DialogModal
title="Sign Up"
isOpened={signupOpen}
onClose={() => setSignupOpen(false)}>
<input type="text" placeholder="Name" value={userName} onChange={e => setUserName(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<input type="email" placeholder="E-Mail" value={email} onChange={e => setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<input type="password" placeholder="Password" onChange={e => setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<div className="tw-card-actions">
<button className={loading ? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' : 'tw-btn tw-btn-primary tw-btn-block'} onClick={() => onRegister()}>{loading ? <span className="tw-loading tw-loading-spinner"></span> : 'Sign Up'}</button>
</div>
</DialogModal>
</>
)
}

View File

@ -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<string>("");
const [password, setPassword] = useState<string>("");
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(
<div className="tw-flex-1 tw-bg-base-200 tw-flex tw-items-center">
<div className="tw-card tw-mx-auto tw-w-full tw-max-w-md tw-shadow-xl">
<div className="tw-grid md:tw-grid-cols-1 tw-grid-cols-1 tw-bg-base-100 tw-rounded-xl">
<div className='tw-py-10 tw-px-10'>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Login</h2>
<form onSubmit={(e) => submitForm(e)}>
<div className="tw-mb-4">
<TextInput type="email" defaultValue={loginObj.emailId} containerStyle="tw-mt-4" labelTitle="E-Mail" updateFormValue={(v) => updateFormValue(v)}/>
<TextInput defaultValue={loginObj.password} type="password" containerStyle="tw-mt-4" labelTitle="Password" updateFormValue={(v) => updateFormValue(v)}/>
return (
<MapOverlayPage>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Login</h2>
<input type="email" placeholder="E-Mail" value={email} onChange={e => setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<input type="password" placeholder="Password" onChange={e => setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<div className='tw-text-right tw-text-primary'><Link to="/reset-password"><span className="tw-text-sm tw-inline-block hover:tw-text-primary hover:tw-underline hover:tw-cursor-pointer tw-transition tw-duration-200">Forgot Password?</span></Link>
</div>
<div className='tw-text-right tw-text-primary'><Link to="/forgot-password"><span className="tw-text-sm tw-inline-block hover:tw-text-primary hover:tw-underline hover:tw-cursor-pointer tw-transition tw-duration-200">Forgot Password?</span></Link>
</div>
<ErrorText styleClass="mt-8">{errorMessage}</ErrorText>
<button type="submit" className={"tw-btn tw-mt-2 tw-w-full tw-btn-primary" + (loading ? " tw-loading" : "")}>Login</button>
<div className='tw-text-center tw-mt-4'>Don't have an account yet? <Link to="/signup"><span className=" tw-inline-block hover:tw-text-primary hover:tw-underline hover:tw-cursor-pointer tw-transition tw-duration-200">Sign Up</span></Link></div>
</form>
</div>
<div className="tw-card-actions">
<button className={loading ? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' : 'tw-btn tw-btn-primary tw-btn-block'} onClick={() => onLogin()}>{loading ? <span className="tw-loading tw-loading-spinner"></span> : 'Login'}</button>
</div>
</div>
</div>
</MapOverlayPage>
)
}

View File

@ -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<string>("");
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 (
<MapOverlayPage>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Reset Password</h2>
<input type="email" placeholder="E-Mail" value={email} onChange={e => setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<div className="tw-card-actions tw-mt-4">
<button className={loading ? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' : 'tw-btn tw-btn-primary tw-btn-block'} onClick={() => onReset()}>{loading ? <span className="tw-loading tw-loading-spinner"></span> : 'Send'}</button>
</div>
</MapOverlayPage>
)
}

View File

@ -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<string>("");
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 (
<MapOverlayPage>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Set new Password</h2>
<input type="password" placeholder="Password" onChange={e => setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<div className="tw-card-actions tw-mt-4">
<button className={loading ? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' : 'tw-btn tw-btn-primary tw-btn-block'} onClick={() => onReset()}>{loading ? <span className="tw-loading tw-loading-spinner"></span> : 'Set'}</button>
</div>
</MapOverlayPage>
)
}

View File

@ -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<string>("");
const [userName, setUserName] = useState<string>("");
const [password, setPassword] = useState<string>("");
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 (
<div className="tw-flex-1 tw-bg-base-200 tw-flex tw-items-center">
<div className="tw-card tw-mx-auto tw-w-full tw-max-w-md tw-shadow-xl">
<div className="tw-grid md:tw-grid-cols-1 tw-grid-cols-1 tw-bg-base-100 tw-rounded-xl">
<div className='tw-py-10 tw-px-10'>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Sign Up</h2>
<form onSubmit={(e) => submitForm(e)}>
<div className="mb-4">
<TextInput defaultValue={registerObj.name} containerStyle="tw-mt-4" labelTitle="Name" updateFormValue={updateFormValue} />
<TextInput defaultValue={registerObj.emailId} containerStyle="tw-mt-4" labelTitle="E-Mail" updateFormValue={updateFormValue} />
<TextInput defaultValue={registerObj.password} type="password" containerStyle="tw-mt-4" labelTitle="Password" updateFormValue={updateFormValue} />
</div>
<ErrorText styleClass="tw-mt-8">{errorMessage}</ErrorText>
<button type="submit" className={"tw-btn tw-mt-2 tw-w-full tw-btn-primary" + (loading ? " tw-loading" : "")}>Register</button>
<div className='tw-text-center tw-mt-4'>Already have an account? <Link to="/login"><span className=" tw-inline-block hover:tw-text-primary hover:tw-underline hover:tw-cursor-pointer tw-transition tw-duration-200">Login</span></Link></div>
</form>
</div>
</div>
<MapOverlayPage>
<h2 className='tw-text-2xl tw-font-semibold tw-mb-2 tw-text-center'>Sign Up</h2>
<input type="text" placeholder="Name" value={userName} onChange={e => setUserName(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<input type="email" placeholder="E-Mail" value={email} onChange={e => setEmail(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<input type="password" placeholder="Password" onChange={e => setPassword(e.target.value)} className="tw-input tw-input-bordered tw-w-full tw-max-w-xs" />
<div className="tw-card-actions tw-mt-4">
<button className={loading ? 'tw-btn tw-btn-disabled tw-btn-block tw-btn-primary' : 'tw-btn tw-btn-primary tw-btn-block'} onClick={() => onRegister()}>{loading ? <span className="tw-loading tw-loading-spinner"></span> : 'Sign Up'}</button>
</div>
</div>
</MapOverlayPage>
)
}

View File

@ -1,3 +1,5 @@
export {AuthProvider, useAuth} from "./useAuth"
export {LoginPage} from "./LoginPage"
export {SignupPage} from "./SignupPage"
export {SignupPage} from './SignupPage'
export {RequestPasswordPage} from './RequestPasswordPage'
export {SetNewPasswordPage} from './SetNewPasswordPage'

View File

@ -24,7 +24,9 @@ type AuthContextProps = {
loading: Boolean,
logout: () => Promise<any>,
updateUser: (user: UserItem) => any,
token: String | null
token: String | null,
requestPasswordReset: (email:string, reset_url: string) => Promise<any>,
passwordReset: (token:string, new_password:string) => Promise<any>
}
const AuthContext = createContext<AuthContextProps>({
@ -35,7 +37,9 @@ const AuthContext = createContext<AuthContextProps>({
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<any> => {
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<any> => {
setLoading(true);
try {
await userApi.passwordReset(token, new_password);
return setLoading(false);
} catch (error: any) {
setLoading(false);
throw error;
};
}
return (
<AuthContext.Provider
value={{ isAuthenticated, user, login, register, loading, logout, updateUser, token }}
value={{ isAuthenticated, user, login, register, loading, logout, updateUser, token, requestPasswordReset, passwordReset }}
>
{children}
</AuthContext.Provider>

View File

@ -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 (
<>
<LayersProvider initialLayers={[]}>
<TagsProvider initialTags={[]}>
<PermissionsProvider initialPermissions={[]}>
@ -114,6 +116,8 @@ function UtopiaMap({
</PermissionsProvider>
</TagsProvider>
</LayersProvider>
<Outlet></Outlet>
</>
);
}

View File

@ -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<HTMLDivElement>(null);
const [isOpen, toggle] = useState(false);

View File

@ -158,8 +158,8 @@ export function ProfileSettings() {
return (
<>
<main className="tw-flex-1 tw-overflow-y-auto tw-overflow-x-hidden tw-pt-8 tw-px-6 tw-bg-base-200 tw-min-w-80 tw-flex tw-justify-center" >
<div className='tw-backdrop-contrast-50 tw-h-full tw-w-full'>
<main className="tw-flex-1 tw-overflow-y-auto tw-overflow-x-hidden tw-pt-8 tw-px-6 tw-min-w-80 tw-flex tw-justify-center" >
<div className='tw-w-full xl:tw-max-w-6xl'>
<TitleCard title="Profile" topMargin="tw-mt-2" className='tw-mb-6'>
<div className="tw-flex">
@ -219,6 +219,6 @@ export function ProfileSettings() {
renderCrop();
}}>Select</button>
</DialogModal>
</>
</div>
)
}

View File

@ -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 (
<div className="tw-absolute tw-z-1000 tw-h-full tw-w-full tw-m-auto">
<div className='tw-backdrop-brightness-75 tw-h-full tw-w-full tw-grid tw-place-items-center tw-m-auto'
>
<div className='tw-card tw-shadow-xl tw-bg-base-100 tw-p-4 tw-max-w-xs tw-absolute tw-top-0 tw-bottom-0 tw-right-0 tw-left-0 tw-m-auto tw-h-fit '>
<div className="tw-card-body tw-p-2">
{children}
<button className="tw-btn tw-btn-sm tw-btn-circle tw-btn-ghost tw-absolute tw-right-2 tw-top-2" onClick={() => closeScreen()}></button>
</div>
</div>
</div>
</div>
)
}

View File

@ -1,2 +1,3 @@
export {CardPage} from './CardPage'
export {TitleCard} from './TitleCard'
export {MapOverlayPage} from './MapOverlayPage'

View File

@ -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'

View File

@ -87,7 +87,9 @@ export interface UserApi {
logout(): Promise<void>,
getUser(): Promise<UserItem>,
getToken(): Promise<any>,
updateUser(user: UserItem): Promise<void>
updateUser(user: UserItem): Promise<void>,
requestPasswordReset(email:string, reset_url?:string),
passwordReset(token:string,new_password:string)
}
export type UserItem = {