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