add files
This commit is contained in:
parent
10f347ea0c
commit
7de0d9fc5e
8
src/components/CreateCruisePage.tsx
Normal file
8
src/components/CreateCruisePage.tsx
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export default function CreateCruisePage() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Create Cruise</h1>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
35
src/components/DashboardPage.tsx
Normal file
35
src/components/DashboardPage.tsx
Normal 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
32
src/components/Login.css
Normal 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
|
||||||
|
}
|
64
src/components/LoginPage.tsx
Normal file
64
src/components/LoginPage.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
10
src/components/NotFoundPage.tsx
Normal file
10
src/components/NotFoundPage.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
32
src/components/RegisterPage.css
Normal file
32
src/components/RegisterPage.css
Normal 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
|
||||||
|
}
|
105
src/components/RegisterPage.tsx
Normal file
105
src/components/RegisterPage.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
32
src/components/VerifyPage.css
Normal file
32
src/components/VerifyPage.css
Normal 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
|
||||||
|
}
|
67
src/components/VerifyPage.tsx
Normal file
67
src/components/VerifyPage.tsx
Normal 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
90
src/nws-api/hooks.ts
Normal 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;
|
||||||
|
}
|
Loading…
Reference in a new issue