first commit
This commit is contained in:
186
src/Components/Account/AccountPage.js
Normal file
186
src/Components/Account/AccountPage.js
Normal file
@@ -0,0 +1,186 @@
|
||||
import { useState, useContext, useEffect } from "react";
|
||||
import { db } from "../../firebase";
|
||||
import { collection, onSnapshot, query, where } from "firebase/firestore";
|
||||
import Button from "../../pageElements/Button";
|
||||
import { AppContext } from "../../Hooks/useContext/appContext";
|
||||
import { AuthContext } from "../../Context/AuthProvider";
|
||||
import ProgressBar from "../../pageElements/ProgressBar";
|
||||
|
||||
const AccountPage = () => {
|
||||
const { currentUserCreatedAt } = useContext(AuthContext);
|
||||
const { appState } = useContext(AppContext);
|
||||
const { group } = appState;
|
||||
const [docsGenerated, setDocsGenerated] = useState(null);
|
||||
const [docsAllowed, setDocsAllowed] = useState(200);
|
||||
const appUserId = group ? group.appUserId : null;
|
||||
|
||||
function getDocumentsCount() {
|
||||
if (!appUserId) {
|
||||
return;
|
||||
}
|
||||
return onSnapshot(
|
||||
query(collection(db, "documents"), where("ownerId", "==", appUserId)),
|
||||
(snapshot) => setDocsGenerated(snapshot.docs.length)
|
||||
);
|
||||
}
|
||||
|
||||
useEffect(getDocumentsCount, [appUserId]);
|
||||
|
||||
const telNumberFormat = (num) => {
|
||||
return `(${num.slice(0, 3)}) ${num.slice(4, 7)}-${num.slice(6, 10)}`;
|
||||
};
|
||||
|
||||
if (!group) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="account-container">
|
||||
<div className="account-left-column">
|
||||
<div className="account-menu">
|
||||
<div className="account-menu-item">
|
||||
<a href="#">Personal Settings</a>
|
||||
</div>
|
||||
<div className="account-menu-item">
|
||||
<a href="#">Payment Settings</a>
|
||||
</div>
|
||||
<div className="account-menu-item">
|
||||
<a href="#">Membership Settings</a>
|
||||
</div>
|
||||
<div className="account-menu-item">
|
||||
<a href="#">Order History</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-column">
|
||||
{docsGenerated !== null ? (
|
||||
<div className="account-card account-docs-generated">
|
||||
<div>
|
||||
You have generated {docsGenerated} out of {docsAllowed} response
|
||||
documents this billing period
|
||||
</div>
|
||||
<div className="account-card-title">Upgrade to get more</div>
|
||||
<div className="account-docs-progress-bar">
|
||||
<ProgressBar value={docsGenerated} total={docsAllowed} />
|
||||
</div>
|
||||
<div className="account-card-actions">
|
||||
<Button
|
||||
className="primary-button"
|
||||
labelText="Upgrade to add more"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="accound-section">
|
||||
Take full advantage of the features included in your account
|
||||
</div>
|
||||
<div className="account-group">
|
||||
<div className="account-card">
|
||||
<div className="account-card-title">Give the gift of Novodraft</div>
|
||||
<div className="account-card-description">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
<div className="account-card-actions">
|
||||
<Button className="primary-button" labelText="Upgrade" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-card">
|
||||
<div className="account-card-title">Give the gift of Novodraft</div>
|
||||
<div className="account-card-description">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
<div className="account-card-actions">
|
||||
<Button className="primary-button" labelText="Upgrade" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-card">
|
||||
<div className="account-card-title">Give the gift of Novodraft</div>
|
||||
<div className="account-card-description">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
<div className="account-card-actions">
|
||||
<Button className="primary-button" labelText="Upgrade" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-card">
|
||||
<div className="account-card-title">Give the gift of Novodraft</div>
|
||||
<div className="account-card-description">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
|
||||
eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
<div className="account-card-actions">
|
||||
<Button className="primary-button" labelText="Upgrade" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-card account-support">
|
||||
<div className="account-support-info">
|
||||
<div className="account-support-title account-support-info-title">
|
||||
Need Support?
|
||||
</div>
|
||||
<div>
|
||||
<a href="tel:+12223334444">1-222-333-4444</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="mailto:test@example.com">test@example.com</a>
|
||||
</div>
|
||||
<div>
|
||||
Or use our <a href="#">contact form</a>
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-support-links">
|
||||
<div className="account-support-title account-support-links-title">
|
||||
FAQ
|
||||
</div>
|
||||
<div>
|
||||
<a href="#">Support</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#">Billing</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#">Privacy</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="accound-section">Active Subscriptions</div>
|
||||
<div className="account-card account-subscriptions">
|
||||
<div className="account-subscriptions-title">
|
||||
30 day free trial - 1 allowed document
|
||||
</div>
|
||||
<div className="account-subscriptions-item">
|
||||
<div className="account-subscriptions-item-label">Amount:</div>
|
||||
<div className="account-subscriptions-item-value">Free!</div>
|
||||
</div>
|
||||
{typeof currentUserCreatedAt !== "undefined" ? (
|
||||
<div className="account-subscriptions-item">
|
||||
<div className="account-subscriptions-item-label">Activated:</div>
|
||||
<div className="account-subscriptions-item-value">
|
||||
{currentUserCreatedAt.toLocaleDateString("en-US")}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="account-subscriptions-actions">
|
||||
<Button className="primary-button" labelText="Cancel Account" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="account-column">
|
||||
<div className="account-card account-card-right-account account-info">
|
||||
<div className="account-info-name">
|
||||
{group.firstName} {group.lastName}
|
||||
</div>
|
||||
<div>
|
||||
<a href={`mailto:${group.email}`}>{group.email}</a>
|
||||
</div>
|
||||
<div>{telNumberFormat(group.telephone)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountPage;
|
||||
200
src/Components/Account/PasswordReset.js
Normal file
200
src/Components/Account/PasswordReset.js
Normal file
@@ -0,0 +1,200 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { checkActionCode, confirmPasswordReset, getAuth, sendPasswordResetEmail } from "firebase/auth";
|
||||
import Button from "../../pageElements/Button";
|
||||
import { useSearchParams } from "react-router-dom";
|
||||
|
||||
const PasswordResetPage = () => {
|
||||
const auth = getAuth();
|
||||
const [isBusy, setIsBusy] = useState(false);
|
||||
const [isCodeValid, setIsCodeValid] = useState(null);
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [done, setDone] = useState(false);
|
||||
const [notice, setNotice] = useState("");
|
||||
const [searchParams] = useSearchParams();
|
||||
const mode = searchParams.get("mode");
|
||||
const oobCode = searchParams.get("oobCode");
|
||||
|
||||
useEffect(() => {
|
||||
async function handleCheckCode() {
|
||||
if (!oobCode) {
|
||||
setIsCodeValid(false);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await checkActionCode(auth, oobCode);
|
||||
setIsCodeValid(true);
|
||||
} catch (error) {
|
||||
setIsCodeValid(false);
|
||||
}
|
||||
}
|
||||
handleCheckCode();
|
||||
}, [auth, oobCode]);
|
||||
|
||||
const handleResetPassword = async (e) => {
|
||||
e.preventDefault();
|
||||
if (isBusy) {
|
||||
return;
|
||||
}
|
||||
setIsBusy(true);
|
||||
setNotice("");
|
||||
try {
|
||||
await sendPasswordResetEmail(auth, email);
|
||||
setDone(true);
|
||||
} catch (error) {
|
||||
if (error.code === "auth/invalid-email") {
|
||||
setNotice("Invalid email.");
|
||||
} else {
|
||||
setNotice(`Error occured (${error.code}).`);
|
||||
}
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmPasswordReset = async (e) => {
|
||||
e.preventDefault();
|
||||
if (isBusy) {
|
||||
return;
|
||||
}
|
||||
if (password !== confirmPassword) {
|
||||
setNotice("Passwords do not match");
|
||||
return;
|
||||
}
|
||||
setIsBusy(true);
|
||||
try {
|
||||
await confirmPasswordReset(auth, oobCode, password);
|
||||
setDone(true);
|
||||
} catch (error) {
|
||||
if (error.code === "auth/weak-password") {
|
||||
setNotice("Weak password.");
|
||||
} else {
|
||||
setNotice(`Error occured (${error.code}).`);
|
||||
}
|
||||
} finally {
|
||||
setIsBusy(false);
|
||||
}
|
||||
}
|
||||
|
||||
const resetMode = mode === "resetPassword" ? "resetPassword" : "enterEmail";
|
||||
return (
|
||||
<div className="password-reset-container">
|
||||
<div className="password-reset-form-wrapper">
|
||||
<div className="password-reset-form">
|
||||
{resetMode === "enterEmail" ? <>
|
||||
<div className="password-reset-header">
|
||||
<h2 className="password-reset-header-text">
|
||||
Enter Account Email
|
||||
</h2>
|
||||
</div>
|
||||
{!done ? (
|
||||
<form>
|
||||
<div className="form-floating">
|
||||
<input
|
||||
type="email"
|
||||
className="form-control"
|
||||
id="emailInput"
|
||||
placeholder="Email"
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
disabled={isBusy}
|
||||
></input>
|
||||
<label htmlFor="emailInput" className="form-label">
|
||||
Email
|
||||
</label>
|
||||
</div>
|
||||
<div className="alert-box">
|
||||
{"" !== notice && (
|
||||
<div className="password-reset-alert" role="alert">
|
||||
{notice}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="password-reset-button-wrapper">
|
||||
<Button
|
||||
className="primary-button"
|
||||
onClick={(e) => handleResetPassword(e)}
|
||||
labelText="Submit"
|
||||
disabled={isBusy}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
) : null}
|
||||
{done && (
|
||||
<div className="success-container">
|
||||
<p className="reset-sent-text">A password reset link was sent.</p>
|
||||
</div>
|
||||
)}
|
||||
</> : null}
|
||||
{resetMode === "resetPassword" ? <>
|
||||
<div className="password-reset-header">
|
||||
<h2 className="password-reset-header-text">
|
||||
Enter New Password
|
||||
</h2>
|
||||
</div>
|
||||
{!done && isCodeValid !== null && isCodeValid ? (
|
||||
<form>
|
||||
<div className="form-floating mb-3">
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
id="passwordInput"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
disabled={isBusy}
|
||||
></input>
|
||||
<label htmlFor="emailInput" className="form-label">
|
||||
Enter new password
|
||||
</label>
|
||||
</div>
|
||||
<div className="form-floating">
|
||||
<input
|
||||
type="password"
|
||||
className="form-control"
|
||||
id="confirmPasswordInput"
|
||||
placeholder="Confirm Password"
|
||||
value={confirmPassword}
|
||||
onChange={(e) => setConfirmPassword(e.target.value)}
|
||||
disabled={isBusy}
|
||||
></input>
|
||||
<label htmlFor="confirmPasswordInput" className="form-label">
|
||||
Confirm password
|
||||
</label>
|
||||
</div>
|
||||
<div className="alert-box">
|
||||
{"" !== notice && (
|
||||
<div className="password-reset-alert" role="alert">
|
||||
{notice}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="password-reset-button-wrapper">
|
||||
<Button
|
||||
className="primary-button"
|
||||
onClick={(e) => handleConfirmPasswordReset(e)}
|
||||
labelText="Submit"
|
||||
disabled={isBusy}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
) : null}
|
||||
{!done && isCodeValid !== null && !isCodeValid && (
|
||||
<div className="success-container">
|
||||
<p className="reset-sent-text">Bad or already used password reset code.</p>
|
||||
</div>
|
||||
)}
|
||||
{done && (
|
||||
<div className="success-container">
|
||||
<p className="reset-sent-text">Password changed.</p>
|
||||
</div>
|
||||
)}
|
||||
</> : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default PasswordResetPage;
|
||||
8
src/Components/Account/SettingsPage.js
Normal file
8
src/Components/Account/SettingsPage.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import { useState, useContext, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const SettingsPage = () => {
|
||||
return <div>Settings Page</div>;
|
||||
};
|
||||
|
||||
export default SettingsPage;
|
||||
Reference in New Issue
Block a user