diff --git a/src/components/CreateCruisePage.tsx b/src/components/CreateCruisePage.tsx new file mode 100644 index 0000000..9fdcac8 --- /dev/null +++ b/src/components/CreateCruisePage.tsx @@ -0,0 +1,8 @@ +export default function CreateCruisePage() { + return ( +
+

Create Cruise

+ +
+ ); +} \ No newline at end of file diff --git a/src/components/DashboardPage.tsx b/src/components/DashboardPage.tsx new file mode 100644 index 0000000..a3d9c5e --- /dev/null +++ b/src/components/DashboardPage.tsx @@ -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( +
+

Welcome to NWS, {account?.name}!

+
+
+

Your NWS Cruise™ Services

+ +
+ {/*

Your NWS Write™ Blogs

*/} +
+ {services.map((e)=>{ + return ( +
+
+

{e.serviceName}

+

Application Id

+

{e.serviceId}

+

Deployment Key

+ Regenerate Deploy Key +
+
); + })} +
+
+ ); +} \ No newline at end of file diff --git a/src/components/Login.css b/src/components/Login.css new file mode 100644 index 0000000..bf840ac --- /dev/null +++ b/src/components/Login.css @@ -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 +} \ No newline at end of file diff --git a/src/components/LoginPage.tsx b/src/components/LoginPage.tsx new file mode 100644 index 0000000..27cff4a --- /dev/null +++ b/src/components/LoginPage.tsx @@ -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(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + 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( +
+
+

Login to NWS Dashboard

+ { errorMessage != "" && +
+

{errorMessage}

+
+ } +

E-Mail Address

+ {setEmail(e.target.value)}} className={"login-input"}/> +

Password

+ {setPassword(e.target.value)}}className={"login-input"} type={"password"}/> + +

No account? Register Here!

+
+
+ ); +} \ No newline at end of file diff --git a/src/components/NotFoundPage.tsx b/src/components/NotFoundPage.tsx new file mode 100644 index 0000000..47ffbf7 --- /dev/null +++ b/src/components/NotFoundPage.tsx @@ -0,0 +1,10 @@ +export default function NotFoundPage() { + return( +
+
+

Not Found :(

+ Home +
+
+ ); +} \ No newline at end of file diff --git a/src/components/RegisterPage.css b/src/components/RegisterPage.css new file mode 100644 index 0000000..b03563c --- /dev/null +++ b/src/components/RegisterPage.css @@ -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 +} \ No newline at end of file diff --git a/src/components/RegisterPage.tsx b/src/components/RegisterPage.tsx new file mode 100644 index 0000000..971bb47 --- /dev/null +++ b/src/components/RegisterPage.tsx @@ -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(""); + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [cpassword, setCpassword] = useState(""); + const [inviteCode, setInviteCode] = useState(""); + const [didRegister, setDidRegister] = useState(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( +
+
+

Create an NWS Account

+ + { errorMessage != "" && +
+

{errorMessage}

+
+ } + +

Name

+ setName(e.target.value)} className={"reg-input"}/> + +

E-Mail Address

+ setEmail(e.target.value)} className={"reg-input"}/> + +

Password

+ setPassword(e.target.value)} className={"reg-input"} type={"password"}/> + +

Confirm

+ setCpassword(e.target.value)} className={"reg-input"} type={"password"}/> + +
+

Invite Code

+ setInviteCode(e.target.value)} style={{width: "100%"}}/> + Currently, NWS is invite only. Email me to get an invite code. +
+ + +

Already have an account? Login Here!

+
+ +
+

Verify your E-Mail address.

+ +

Please verify your E-Mail by clicking the link we sent to you at: {email}

+
+
+ ); +} \ No newline at end of file diff --git a/src/components/VerifyPage.css b/src/components/VerifyPage.css new file mode 100644 index 0000000..3fc96a3 --- /dev/null +++ b/src/components/VerifyPage.css @@ -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 +} \ No newline at end of file diff --git a/src/components/VerifyPage.tsx b/src/components/VerifyPage.tsx new file mode 100644 index 0000000..0cfb64a --- /dev/null +++ b/src/components/VerifyPage.tsx @@ -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(""); + 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( +
+
+

Uh Oh!

+ +

Looks like the verification code you provided didn't work!

+ +

Try to click on the link in the E-Mail sent to you instead of copying it.

+
+
+

Expired Link

+ +

It looks like the link you used to verify your account has expired.

+ +

We've sent a new link to your email that is valid for 30 minutes.

+
+
+ + ); +} \ No newline at end of file diff --git a/src/nws-api/hooks.ts b/src/nws-api/hooks.ts new file mode 100644 index 0000000..9f4ad83 --- /dev/null +++ b/src/nws-api/hooks.ts @@ -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([]); + + 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(); + + 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; +} \ No newline at end of file