add files

This commit is contained in:
Nicholas Orlowsky 2022-12-20 18:34:12 -06:00
parent 10f347ea0c
commit 7de0d9fc5e
10 changed files with 475 additions and 0 deletions

View file

@ -0,0 +1,8 @@
export default function CreateCruisePage() {
return (
<div>
<h1>Create Cruise</h1>
</div>
);
}

View file

@ -0,0 +1,35 @@
import {Account, Service} from "../nws-api/types";
import {useGetAccountServices, useLoggedInRedirect, useNWSAccount} from "../nws-api/hooks";
export default function DashboardPage() {
useLoggedInRedirect();
let account: Account | undefined = useNWSAccount();
let services: Service[] = useGetAccountServices();
return(
<div style={{minHeight: "100vh", padding: "50px"}}>
<h1>Welcome to NWS, {account?.name}!</h1>
<hr/>
<div className={"d-flex justify-content-between"}>
<h2>Your NWS Cruise Services</h2>
<button onClick={(e) => {window.location.href = "/cruise/new"}}>Create Cruise Service</button>
</div>
{/*<h2>Your NWS Write™ Blogs</h2>*/}
<div className={"row"}>
{services.map((e)=>{
return (
<div className={"col-4"} style={{ padding: 5}}>
<div style={{backgroundColor: "#eee", borderRadius: 20, padding: 5}}>
<h3>{e.serviceName}</h3>
<p><b>Application Id</b></p>
<p>{e.serviceId}</p>
<p><b>Deployment Key</b></p>
<a href={"#regen"}>Regenerate Deploy Key</a>
</div>
</div>);
})}
</div>
</div>
);
}

32
src/components/Login.css Normal file
View file

@ -0,0 +1,32 @@
.login-box {
border-style: solid;
border-width: 1px;
border-color: #aaa;
border-radius: 10px;
align-self: center;
justify-self: center;
width: 500px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
}
.login-label {
margin: 0;
}
.login-button {
border-radius: 10px;
border-width: 0px;
background-color: cornflowerblue;
color: white;
margin-top: 10px;
margin-bottom: 10px;
padding: 3px
}

View file

@ -0,0 +1,64 @@
import "./Login.css";
import {useEffect, useState} from "react";
import {Account, ApiError, SessionKey} from "../nws-api/types";
import {useNonLoggedInRedirect} from "../nws-api/hooks";
export default function LoginPage() {
useNonLoggedInRedirect();
const [errorMessage, setErrorMessage] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
function loginUser() {
if(email == "" || password == "") {
setErrorMessage("Please enter an email and password");
return;
}
let acc: Account = {
email: email,
password: password
};
fetch("https://api-nws.nickorlow.com/Account/session",{
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(acc)
}).then((result) => {
if(result.status == 200) {
result.json().then((o: SessionKey)=>{
localStorage.setItem("session_key", JSON.stringify(o));
window.location.href = '/dashboard';
});
} else {
setErrorMessage("Server Error (This is NWS' fault)");
}
}).catch((e) =>{
setErrorMessage("Server Error (This is NWS' fault)");
});
}
return(
<div style={{minHeight: "100vh", display: "grid", width: "100%"}}>
<div className={"login-box"}>
<h3>Login to NWS Dashboard</h3>
{ errorMessage != "" &&
<div className={"error-banner"}>
<p style={{color: "black"}}>{errorMessage}</p>
</div>
}
<p className={"login-label"}>E-Mail Address</p>
<input onChange={(e)=>{setEmail(e.target.value)}} className={"login-input"}/>
<p className={"login-label"}>Password</p>
<input onChange={(e)=>{setPassword(e.target.value)}}className={"login-input"} type={"password"}/>
<button className={"login-button"} onClick={loginUser}>Login</button>
<p>No account? <a href={"/register"}>Register Here!</a></p>
</div>
</div>
);
}

View file

@ -0,0 +1,10 @@
export default function NotFoundPage() {
return(
<div style={{width: "100vw", height: "100vh", display: "flex", justifyContent: "center", alignContent: "center", alignItems: "center"}}>
<div>
<h1>Not Found :(</h1>
<a href={"/"}>Home</a>
</div>
</div>
);
}

View file

@ -0,0 +1,32 @@
.reg-box {
border-style: solid;
border-width: 1px;
border-color: #aaa;
border-radius: 10px;
align-self: center;
justify-self: center;
width: 500px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
}
.reg-label {
margin: 0;
}
.reg-button {
border-radius: 10px;
border-width: 0px;
background-color: cornflowerblue;
color: white;
margin-top: 10px;
margin-bottom: 10px;
padding: 3px
}

View file

@ -0,0 +1,105 @@
import "./RegisterPage.css";
import {useState} from "react";
import {Account, ApiError} from "../nws-api/types";
import {useNonLoggedInRedirect} from "../nws-api/hooks";
export default function RegisterPage() {
const [errorMessage, setErrorMessage] = useState<String>("");
const [name, setName] = useState<string>("");
const [email, setEmail] = useState<string>("");
const [password, setPassword] = useState<string>("");
const [cpassword, setCpassword] = useState<string>("");
const [inviteCode, setInviteCode] = useState<string>("");
const [didRegister, setDidRegister] = useState<Boolean>(false);
async function createAccount() {
if(name == "" || email == "" || password == "" || cpassword == "" || inviteCode == "") {
setErrorMessage("You must fill out all information before registering.")
return;
}
if(cpassword != password) {
setErrorMessage("Passwords don't match!")
return;
}
if(!email
.toLowerCase()
.match(
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
)) {
setErrorMessage("You have entered an invalid E-Mail address.")
return;
}
let newAcc: Account = {
email: email,
name: name,
password: password
};
fetch("https://api-nws.nickorlow.com/Account",{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Invite-Code': inviteCode
},
body: JSON.stringify(newAcc)
}).then((result) => {
if(result.status == 200) {
setDidRegister(true);
} else {
result.json().then((o: ApiError) => {
setErrorMessage(o.ErrorMessage);
});
}
}).catch((e) =>{
setErrorMessage("Server Error (This is NWS' fault)")
});
}
useNonLoggedInRedirect();
return(
<div style={{minHeight: "100vh", display: "grid", width: "100%"}}>
<div className={"reg-box"} style={{display: didRegister ? "none" : "flex"}}>
<h3>Create an NWS Account</h3>
{ errorMessage != "" &&
<div className={"error-banner"}>
<p style={{color: "black"}}>{errorMessage}</p>
</div>
}
<p className={"reg-label"}>Name</p>
<input onChange={(e)=>setName(e.target.value)} className={"reg-input"}/>
<p className={"reg-label"}>E-Mail Address</p>
<input onChange={(e)=>setEmail(e.target.value)} className={"reg-input"}/>
<p className={"reg-label"}>Password</p>
<input onChange={(e)=>setPassword(e.target.value)} className={"reg-input"} type={"password"}/>
<p className={"reg-label"}>Confirm</p>
<input onChange={(e)=>setCpassword(e.target.value)} className={"reg-input"} type={"password"}/>
<div style={{width: "100%", marginTop: 10, marginBottom: 10, padding: 10, backgroundColor: "#eee", borderRadius: 10}}>
<p className={"reg-label"}>Invite Code</p>
<input onChange={(e)=>setInviteCode(e.target.value)} style={{width: "100%"}}/>
<small>Currently, NWS is invite only. Email me to get an invite code.</small>
</div>
<button onClick={createAccount} className={"reg-button"}>Create Account</button>
<p>Already have an account? <a href={"/login"}>Login Here!</a></p>
</div>
<div className={"reg-box"} style={{display: didRegister ? "flex" : "none"}}>
<h3>Verify your E-Mail address.</h3>
<p>Please verify your E-Mail by clicking the link we sent to you at: <b>{email}</b></p>
</div>
</div>
);
}

View file

@ -0,0 +1,32 @@
.verify-box {
border-style: solid;
border-width: 1px;
border-color: #aaa;
border-radius: 10px;
align-self: center;
justify-self: center;
width: 500px;
padding: 20px;
display: flex;
flex-direction: column;
justify-content: center;
}
.verify-label {
margin: 0;
}
.verify-button {
border-radius: 10px;
border-width: 0px;
background-color: cornflowerblue;
color: white;
margin-top: 10px;
margin-bottom: 10px;
padding: 3px
}

View file

@ -0,0 +1,67 @@
import "./RegisterPage.css";
import {useEffect, useState} from "react";
import {Account, SessionKey} from "../nws-api/types";
import {useSearchParams} from "react-router-dom";
import {Session} from "inspector";
export default function VerifyPage() {
const [pageState, setPageState] = useState<string>("");
const [searchParams, setSearchParams] = useSearchParams();
useEffect(()=>{
let verificationKey: string | null = searchParams.get("key");
if(verificationKey == null) {
setPageState("invalid_code");
return;
} else {
fetch("https://api-nws.nickorlow.com/Account/verify?verificationKey=" + verificationKey, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
}
}).then((result) => {
if (result.status == 200) {
result.json().then((o: SessionKey)=>{
localStorage.setItem("session_key", JSON.stringify(o));
window.location.href = '/dashboard';
});
}
if (result.status == 500) {
setPageState("server_error");
} else {
result.json().then((o) => {
if (o.ErrorMessage == "Invalid verification key.") {
setPageState("invalid_code");
} else {
setPageState("expired_code");
}
});
}
}).catch((e) => {
setPageState("invalid_code");
});
}
}, [])
return(
<div style={{minHeight: "100vh", display: "grid", width: "100%"}}>
<div className={"reg-box"} style={{display: pageState == "invalid_code" ? "flex" : "none"}}>
<h3>Uh Oh!</h3>
<p>Looks like the verification code you provided didn't work!</p>
<p className={"mt-2"}>Try to click on the link in the E-Mail sent to you instead of copying it.</p>
</div>
<div className={"reg-box"} style={{display: pageState == "expired_code" ? "flex" : "none"}}>
<h3>Expired Link</h3>
<p>It looks like the link you used to verify your account has expired.</p>
<p className={"mt-2"}>We've sent a new link to your email that is valid for 30 minutes.</p>
</div>
</div>
);
}

90
src/nws-api/hooks.ts Normal file
View file

@ -0,0 +1,90 @@
import {useEffect, useState} from "react";
import {Account, Service, SessionKey} from "./types";
export function useNonLoggedInRedirect() {
useEffect(()=>{
let rawSession: string | null = localStorage.getItem("session_key");
if(rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
if(session.expiry < new Date()) {
localStorage.removeItem("session_key");
} else {
window.location.href = "/dashboard";
}
}
}, []);
return true;
}
export function useLoggedInRedirect() {
useEffect(()=>{
let rawSession: string | null = localStorage.getItem("session_key");
if(rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
if(session.expiry > new Date()) {
window.location.href = "/login";
}
} else {
window.location.href = "/login";
}
}, []);
return true;
}
export function useGetAccountServices() {
const [services, setService] = useState<Service[]>([]);
useEffect(() => {
let rawSession: string | null = localStorage.getItem("session_key");
if(rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
fetch("https://api-nws.nickorlow.com/Account/services?accountId=" + session.accountId,
{
headers: {
"Authorization": btoa(session.accountId + ":" + session.id)
}
}).then((response)=>{
response.json().then((svcs: Service[]) => {
console.log(svcs)
setService(svcs);
});
});
}
}, []);
return services;
}
export function useNWSAccount() {
const [accountInfo, setAccountInfo] = useState<Account>();
useEffect(()=>{
let rawSession: string | null = localStorage.getItem("session_key");
if(rawSession != null) {
let session: SessionKey = JSON.parse(rawSession);
fetch("https://api-nws.nickorlow.com/Account/"+session.accountId, {
headers: {
"Authorization": btoa(session.accountId+":"+session.id)
}
}).then((e)=>{
if(e.status == 200) {
e.json().then((o: Account) => {
setAccountInfo(o)
})
} else {
localStorage.removeItem("session_key");
window.location.href = "/login";
}
});
} else {
localStorage.removeItem("session_key");
window.location.href = "/login";
}
}, []);
return accountInfo;
}