diff --git a/.eslintrc b/.eslintrc
index 11936e2..3d4feaf 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -56,6 +56,7 @@
"react/destructuring-assignment": 0, // Dec 2018: We should do this! But right now we have 3990 warnings/errors if enabled.
"react/forbid-prop-types": 0, // Dec 2018: Should consider someday
"react/indent-prop": 0,
+ "react/jsx-curly-newline": 0, // Feb 2025, started showing up with latest eslint
"react/jsx-first-prop-new-line": 0,
"react/jsx-indent-props": 0,
"react/jsx-no-bind": 1, // Dec 2018: Should these be errors?
diff --git a/README.md b/README.md
index ee1962d..40f62a9 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,309 @@
-# WeConnect
+# WeConnect Client
-This WeConnect repository contains a Node/React/Flux Javascript application.
+This WeConnect Client repository (weconnect-client) contains a React Javascript application, that is used to manage WeVote
+volunteer teams.
+
+
Interested in [volunteering or applying for an internship](https://wevote.applytojob.com/apply)? [Starting presentation here](https://prezi.com/p/6iu9aks7zqvs/?present=1).
Please also [read about our values](https://docs.google.com/document/d/12qBXevI3mVKUsGmXL8mrDMPnWJ1SYw9zX9LGW5cozgg/edit) and
-[see our Code of Conduct](CODE_OF_CONDUCT.md)
+[see our Code of Conduct](https://github.com/wevote/WebApp/blob/435304bc1edd7a8d4d0abdae8c46a533a0ecf52c/CODE_OF_CONDUCT.md)
To join us, please [review our openings here](https://wevote.applytojob.com/apply), and apply for a volunteer position through that page.
-Our current version is here [https://WeVote.US](https://WeVote.US) and we are working on a new version now!
+The current version of our flagship voter app is here [https://WeVote.US](https://WeVote.US), and we are working on a new version now!
-## Installing WeConnect - WORK IN PROGRESS
Our installation process is built to allow engineers all over America to contribute.
It may seem complicated, but it allows anyone to be in a position to make suggestions, and get involved.
-Installation on MacOs and Linux
-1. [Preparing the Environment on Your Machine (MacOS and Linux)](docs/installing/ENVIRONMENT.md)
+## Installing the React weconnect-client
+
+These instruction assume that you are installing on a Mac. If you use Windows or Linux, the installation procedure should be similar.
+
+This procedure is based on using the free Community edition of WebStorm, which has great Git integration,
+a great integrated Node debugger, and has an excellent editor.
+
+If you have the paid version of WebStorm the instructions should be the same.
+
+**If you have some other preferred editor, we recommend that you still do this install, and then use your other editor as you wish!**
+
+
+### If you don't already have one, create an account in [GitHub](https://github.com/)
+[GitHub](https://github.com/) is where WeVote stores the source code for our various projects.
+
+
+### Download and install WebStorm
+The free Community edition is at https://www.jetbrains.com/webstorm/
+
+License it as a free Community installation.
+
+Once installed, start WebStorm from Launch Pad or Spotlight
+
+
+
+The first step is to press that "Clone Repository" button to clone the https://github.com/wevote/weconnect-client repository.
+Enter the URL and press the Clone button.
+
+
+
+Now the latest code is on your machine.
+
+
+
+
+### Configure the WebStorm display mode
+If you like the default white characters on a black background, skip this step.
+
+Access the settings dialog from the gear icon in the upper right hand side of the WebStorm app.
+Set the Theme as you would like, or have it "Sync with OS" (which is my preference), and then save.
+
+
+
+
+
+### About WebStorm plugins
+Plugins extend the capability of WebStorm and are worth exploring. If it sounds good, we usually install them, unless the suggestions are for off-topic for what we are useing WebStorm for today.
+Plugins suggested by WebStorm are safe to install, and easy to remove if you don't like them.
+
+
+### Run 'npm install'
+If WebStorm prompts you to run "npm install" feel free to do it anytime.
+
+
+
+"npm install" updates all the dependencies (node_modules i.e. libraries) that we import from other projects, and should never be a problem.
+
+"npm install" will often show lots of outdated libraries, and vulnerabilities ... WeVote gets
+reports on these issues, and we resolve them periodically. Don't worry about the warnings at this point.
+
+
+
+### Install Homebrew (If it is not already installed)
+If you are reading this file in WebStorm, click the green double arrow to the left of the following command,
+to install [Homebrew](https://brew.sh/). Otherwise, open a terminal window at the bottom of Webstorm, and
+paste the following command into the terminal and run it.
+
+```bash
+/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+```
+The following output will appear in the terminal window. You will need your login password to allow macOS
+to install Homebrew. When it prompts you for an "ENTER" you can click in the terminal, and press Enter.
+
+```
+/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+stevepodell@Steves-MBP-M1-Dec2021 weconnect-client % /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
+==> Checking for `sudo` access (which may request your password)...
+Password:
+==> This script will install:
+/opt/homebrew/bin/brew
+/opt/homebrew/share/doc/homebrew
+/opt/homebrew/share/man/man1/brew.1
+/opt/homebrew/share/zsh/site-functions/_brew
+/opt/homebrew/etc/bash_completion.d/brew
+/opt/homebrew
+
+Press RETURN/ENTER to continue or any other key to abort:
+```
+Homebrew will load many many "formulae" (their cutesy name for scripts that install all sorts of
+free open source programs). If you already had Homebrew installed, it will update
+any ("taps" which are "formulae" that were already installed).
+
+When Homebrew completes it will leave the terminal window at a macOS (Linux) prompt.
+
+You will now use Homebrew to install some more applications.
+
+
+### Install Node
+This project has a few scripts that run in Node, get the latest version of at least 22.0
+If you have an earlier version of Node installed, you will need to reinstall it.
+
+Check your node version via the terminal (This computer was at V18, and needed to be upgraded. Node had been previously installed with
+Homebrew. "homebrew" is in the path to the Node executable (`/opt/homebrew/bin/node`), so we know it was installed with Homebrew.)
+
+```
+stevepodell@Steves-MacBook-Air weconnect-client % which node
+/opt/homebrew/bin/node
+stevepodell@Steves-MacBook-Air weconnect-client % node -v
+v18.10.0
+stevepodell@Steves-MacBook-Air weconnect-client %
+```
+
+If your computer did not have Node installed with Homebrew, you will have to research how to upgrade your installation of Node.
+
+If Node was installed with Homebrew or you have never installed Node, continue...
+
+```bash
+ stevepodell@Steves-MacBook-Air weconnect-client % brew install node@22
+ ...
+ ==> Caveats
+ node@22 is keg-only, which means it was not symlinked into /usr/local,
+ because this is an alternate version of another formula.
+
+ If you need to have node@22 first in your PATH, run:
+ echo 'export PATH="/usr/local/opt/node@22/bin:$PATH"' >> ~/.zshrc
+
+ For compilers to find node@22 you may need to set:
+ export LDFLAGS="-L/usr/local/opt/node@22/lib"
+ export CPPFLAGS="-I/usr/local/opt/node@22/include"
+ ==> Summary
+🍺 /usr/local/Cellar/node@22/22.11.0: 2,628 files, 83.7MB
+ ==> Running `brew cleanup node@22`...
+ Disable this behaviour by setting HOMEBREW_NO_INSTALL_CLEANUP.
+ Hide these hints with HOMEBREW_NO_ENV_HINTS (see `man brew`).
+ stevepodell@Steves-MacBook-Air weconnect-client %
+```
+If Homebrew asks you to make the following 4 manual changes to link in Node. Execute these 4 lines in your terminal.
+```bash
+stevepodell@Steves-MacBook-Air weconnect-client % echo 'export PATH="/usr/local/opt/node@22/bin:$PATH"' >> ~/.zshrc
+stevepodell@Steves-MacBook-Air weconnect-client % echo 'export PATH="/usr/local/opt/node@22/bin:$P
+stevepodell@Steves-MacBook-Air weconnect-client % export LDFLAGS="-L/usr/local/opt/node@22/lib"
+stevepodell@Steves-MacBook-Air weconnect-client % export CPPFLAGS="-I/usr/local/opt/node@22/include"
+stevepodell@Steves-MacBook-Air weconnect-client %
+```
+Then confirm the version of Node is greater than V22, open a new terminal window (with the "+" icon) and type
+
+```
+stevepodell@Steves-MacBook-Air weconnect-client % node -v
+v22.11.0
+stevepodell@Steves-MacBook-Air weconnect-client %
+```
+
+
+### Set up your Git remotes within WebStorm
+
+
+
+At WeVote we use different naming conventions for `origin` and `upstream` than you might be familiar with from other projects, so you
+will need to rename the default git origin (which at WeVote is your private branch in GitHub
+)
+
+
+
+Edit the origin line, and change the name to upstream, then press OK
+
+
+
+Then press the + button and set up the new value for “origin”.
+(DON’T USE SailingSteve — use your GitHub handle — the GitHub username that is in the URL after you sign in to GitHub .)
+
+
+
+You may have to authenticate with GitHub at this point. After completing the GitHub authentication, continue...
+
+When done, your remotes will look something like this (with your GitHub handle instead of SailingSteve!)
+
+
+
+At this point you are poised to make Git branches and pull requests.
+
+
+### Load all the Node packages that we use in the weconnect-client
+If you haven't already done this via a prompt from Webstorm, type
+```
+stevepodell@Steves-MacBook-Air weconnect-client % npm install
+```
+You can run this command as often as you want, and it will cause no harm.
+
+
+### Make a live copy of config-template.js to the config.js file
+
+Right-click on the `config-template.js` file in Webstorm, and copy it, then paste it as `config.js`
+
+
+
+
+If WebStorm did not automatically open `config.js` in a tab, then double click it in the Project pane to open it
+for editing.
+
+
+
+### Setup the connection to a weconnect-server (API server)
+
+**Option 1:** Use the weconnect-server running in Amazon AWS
+
+ Note: As of March 5, 2025 the weconnect-server is not yet running in Amazon AWS cloud, so this option is not available.
+
+This is all you need if initially you will not be developing new API endpoints, and you won't initially need to debug
+aPI server side code.
+
+Uncomment these four STAFF_API_SERVER_ lines, and make sure the other two sets of lines like this are commented out.
+
+```
+ //// Connecting to live WeConnect APIs ////
+ // STAFF_API_SERVER_ROOT_URL: 'https://weconnectserver.org/',
+ // STAFF_API_SERVER_ADMIN_ROOT_URL: 'https://weconnectserver.org/admin/',
+ // STAFF_API_SERVER_API_ROOT_URL: 'https://weconnectserver.org/apis/v1/',
+ // STAFF_API_SERVER_API_CDN_ROOT_URL: 'https://cdn.weconnectserver.org/apis/v1/',
+```
+
+
+**Option 2:** Run a weconnect-server instance on your computer using https SSL/TLS
+
+In this case you will have already setup the [weconnect-server](https://github.com/wevote/weconnect-server) and postgres on your computer and have it running.
+
+For this option you will need to get the wevotedeveloper.com certificates from Dale.
+
+Uncomment these four STAFF_API_SERVER_ lines, and make sure the other two sets of lines like this are commented out.
+
+```
+ //// weconnect-server local server running SSL/TLS HTTPS
+ // STAFF_API_SERVER_ROOT_URL: 'https://wevotedeveloper.com:4500/',
+ // STAFF_API_SERVER_ADMIN_ROOT_URL: 'https://wevotedeveloper.com:4500/admin/',
+ // STAFF_API_SERVER_API_ROOT_URL: 'https://wevotedeveloper.com:4500/apis/v1/',
+ // STAFF_API_SERVER_API_CDN_ROOT_URL: 'https://wevotedeveloper.com:4500/apis/v1/',
+```
+
+
+**Option 3:** (Not recommended) Run a non-secure weconnect-server instance on your computer using http
+
+This may work for some limited purposes, but your session may not be maintained as you navigate to the React app in
+different tabs on your browser, and you may have authentication problems, or API query response problems.
+
+Uncomment these four STAFF_API_SERVER_ lines, and make sure the other two sets of lines like this are commented out.
+```
+ //// Connecting to local WeConnect "weconnect-server" APIs ////
+ // STAFF_API_SERVER_ROOT_URL: 'http://localhost:4500/',
+ // STAFF_API_SERVER_ADMIN_ROOT_URL: 'http://localhost:4500/admin/',
+ // STAFF_API_SERVER_API_ROOT_URL: 'http://localhost:4500/apis/v1/',
+ // STAFF_API_SERVER_API_CDN_ROOT_URL: 'http://localhost:4500/apis/v1/',
+```
+
+Not in this project, but in the **weconnect-server project** on your computer, change the PROTOCOL line in the .env file from
+`PROTOCOL=https://` to `PROTOCOL=http://` and restart the weconnect-server, and then it will come up in http mode.
+
+### Add a Run Configuration in WebStorm to start the weconnect-client
+
+Open the pull-down that initially says "Current File", and select Edit Configurations
+
+
+
+In the Run/Debug Configurations dialog, press the "+" button and then select "Node.js"
+
+Then fill in the run configuration...
+1) Enter `Start weconnect-client` in the Scripts field.
+2) And press "OK" to save
+
+
+
+### Start the app!
+First start postgres via the run configuration
+
+As you can see when you press the Green start arrow, the server starts up in a terminal window where you can see
+some logging from the Webpack server program. The `run start` starts up the Webpack server, which listens for any changes to the files in your IDE and then
+automatically 'recompiles' and 'redeploys' the changes to your browser.
+
+Webpack takes a minute or two to start up, but then it recompiles in less that a second. As you make changes in your code
+you will see the line that says something like `webpack 5.97.1 compiled successfully in 795 ms` change for each compile.
+Webpack will eventually see the changes to you code when Webstorm automatically saves the file, but you can get those
+recompiles to happen instantly by either pressing the save floppy-disk icon on the top row, or by pressing Command+S
+
+In your browser you can press Control+Shift+R to reload the page with the latest from Webpack (although this also will
+happen automatically after a few seconds). Control+Shift+R guranatees a full reload, which sometimes does not happen
+if modal dialogs or drawers are open.
+
+Debugging of the code is done with Chrome Dev-tools, which can be invoked by right-clicking anywhere on the page, and
+selecting 'Inspect' at the bottom of the right-click menu.
+
+### View the app in the Chrome browser
+
+When you navigate in Chrome to `http://localhost:4000/` you will see the client login page.
+
diff --git a/docs/images/AppScreenShot.png b/docs/images/AppScreenShot.png
new file mode 100644
index 0000000..509ee1a
Binary files /dev/null and b/docs/images/AppScreenShot.png differ
diff --git a/docs/images/CodeInstalledInWebStorm.png b/docs/images/CodeInstalledInWebStorm.png
new file mode 100644
index 0000000..dd19f5f
Binary files /dev/null and b/docs/images/CodeInstalledInWebStorm.png differ
diff --git a/docs/images/GitOriginMine.png b/docs/images/GitOriginMine.png
new file mode 100644
index 0000000..2010c2b
Binary files /dev/null and b/docs/images/GitOriginMine.png differ
diff --git a/docs/images/GitRemoteOriginBefore.png b/docs/images/GitRemoteOriginBefore.png
new file mode 100644
index 0000000..3b41427
Binary files /dev/null and b/docs/images/GitRemoteOriginBefore.png differ
diff --git a/docs/images/GitRemoteUpstreamAfter.png b/docs/images/GitRemoteUpstreamAfter.png
new file mode 100644
index 0000000..f891cce
Binary files /dev/null and b/docs/images/GitRemoteUpstreamAfter.png differ
diff --git a/docs/images/GitRemotesDone.png b/docs/images/GitRemotesDone.png
new file mode 100644
index 0000000..61f3042
Binary files /dev/null and b/docs/images/GitRemotesDone.png differ
diff --git a/docs/images/ManageRemotesMenu.png b/docs/images/ManageRemotesMenu.png
new file mode 100644
index 0000000..3d4254b
Binary files /dev/null and b/docs/images/ManageRemotesMenu.png differ
diff --git a/docs/images/NpmConfigFilledIn.png b/docs/images/NpmConfigFilledIn.png
new file mode 100644
index 0000000..701f4f3
Binary files /dev/null and b/docs/images/NpmConfigFilledIn.png differ
diff --git a/docs/images/NpmInstall.png b/docs/images/NpmInstall.png
new file mode 100644
index 0000000..dc26a5e
Binary files /dev/null and b/docs/images/NpmInstall.png differ
diff --git a/docs/images/RunConfigMenu.png b/docs/images/RunConfigMenu.png
new file mode 100644
index 0000000..2fea02b
Binary files /dev/null and b/docs/images/RunConfigMenu.png differ
diff --git a/docs/images/RunInHTTP.png b/docs/images/RunInHTTP.png
new file mode 100644
index 0000000..133916c
Binary files /dev/null and b/docs/images/RunInHTTP.png differ
diff --git a/docs/images/SelectNpmFromList.png b/docs/images/SelectNpmFromList.png
new file mode 100644
index 0000000..fd8ade7
Binary files /dev/null and b/docs/images/SelectNpmFromList.png differ
diff --git a/docs/images/WebStormAppearanceSettings.png b/docs/images/WebStormAppearanceSettings.png
new file mode 100644
index 0000000..2ab4f9e
Binary files /dev/null and b/docs/images/WebStormAppearanceSettings.png differ
diff --git a/docs/images/WebstormCloneRepository.png b/docs/images/WebstormCloneRepository.png
new file mode 100644
index 0000000..c5022d7
Binary files /dev/null and b/docs/images/WebstormCloneRepository.png differ
diff --git a/docs/images/WebstormCopySelection.png b/docs/images/WebstormCopySelection.png
new file mode 100644
index 0000000..d080d57
Binary files /dev/null and b/docs/images/WebstormCopySelection.png differ
diff --git a/docs/images/WebstormEditConfig.png b/docs/images/WebstormEditConfig.png
new file mode 100644
index 0000000..6cf18e5
Binary files /dev/null and b/docs/images/WebstormEditConfig.png differ
diff --git a/docs/images/WebstormPaste.png b/docs/images/WebstormPaste.png
new file mode 100644
index 0000000..7f5a003
Binary files /dev/null and b/docs/images/WebstormPaste.png differ
diff --git a/docs/images/WelcomeToWebStorm.png b/docs/images/WelcomeToWebStorm.png
new file mode 100644
index 0000000..36c9b76
Binary files /dev/null and b/docs/images/WelcomeToWebStorm.png differ
diff --git a/src/js/common/utils/logging.js b/src/js/common/utils/logging.js
index 31dc669..14cdad3 100644
--- a/src/js/common/utils/logging.js
+++ b/src/js/common/utils/logging.js
@@ -43,7 +43,7 @@ export function httpLog (text, res) {
}
}
-// Log oAuth steps
+// Log auth steps
export function authLog (text, res) {
if (webAppConfig.LOG_AUTHENTICATION) {
if (res) {
diff --git a/src/js/components/Login/ResetYourPassword.jsx b/src/js/components/Login/ResetYourPassword.jsx
index 5f52dfd..79aa3c2 100644
--- a/src/js/components/Login/ResetYourPassword.jsx
+++ b/src/js/components/Login/ResetYourPassword.jsx
@@ -8,11 +8,11 @@ import TextField from '@mui/material/TextField';
import PropTypes from 'prop-types';
import * as React from 'react';
import { useEffect, useRef, useState } from 'react';
+import styled from 'styled-components';
import validator from 'validator';
import { renderLog } from '../../common/utils/logging';
import { useConnectAppContext } from '../../contexts/ConnectAppContext';
-import makeRequestParams from '../../react-query/makeRequestParams';
-import { useLogoutMutation, usePersonRetrieveByEmailMutation, usePersonSaveForAuthMutation } from '../../react-query/mutations';
+import { useLogoutMutation, usePasswordSaveMutation, usePersonRetrieveByEmailMutation } from '../../react-query/mutations';
import weConnectQueryFn, { METHOD } from '../../react-query/WeConnectQuery';
import { ErrorMessage } from '../Style/sharedStyles';
import VerifySecretCodeModal from '../VerifySecretCodeModal';
@@ -20,19 +20,22 @@ import VerifySecretCodeModal from '../VerifySecretCodeModal';
const ResetYourPassword = ({ openDialog, closeDialog }) => {
renderLog('ResetYourPassword');
const { mutate: mutateRetrievePersonByEmail } = usePersonRetrieveByEmailMutation();
- const { mutate: mutatePersonSaveForAuth } = usePersonSaveForAuthMutation();
+ const { mutate: mutatePasswordSave } = usePasswordSaveMutation();
const { mutate: mutateLogout } = useLogoutMutation();
const { getAppContextValue, setAppContextValue } = useConnectAppContext();
+ // console.log('ResetYourPassword ', getAppContextData());
const [open, setOpen] = React.useState(openDialog);
const [displayEmailAddress, setDisplayEmailAddress] = useState(true);
const [warningLine, setWarningLine] = useState('');
const [errorMessage, setErrorMessage] = useState('');
+ const [personId, setPersonId] = useState('');
const emailRef = useRef('');
+ const emailDisabledRef = useRef('');
const password1Ref = useRef('');
const password2Ref = useRef('');
- const authPerson = useRef(undefined);
+ const authPersonRef = useRef(undefined);
useEffect(() => {
setOpen(openDialog);
@@ -43,6 +46,7 @@ const ResetYourPassword = ({ openDialog, closeDialog }) => {
if (secretCodeVerified === true) {
console.log('received new secretCodeVerifiedForReset', secretCodeVerified);
setDisplayEmailAddress(false);
+ emailDisabledRef.current = authPersonRef.current?.emailPersonal || '';
emailRef.current = '';
password1Ref.current = '';
password2Ref.current = '';
@@ -52,11 +56,9 @@ const ResetYourPassword = ({ openDialog, closeDialog }) => {
const auth = getAppContextValue('authenticatedPerson');
useEffect(() => {
const authP = getAppContextValue('authenticatedPerson');
+ authPersonRef.current = authP;
if (authP && open) {
- console.log('received new authP', authP);
- authPerson.current = authP;
- console.log('authPerson.personId in Login useEffect [auth] id: ', authP.personId);
- console.log('authPerson.personId in Login useEffect [auth] open: ', open);
+ setPersonId(authP.personId);
weConnectQueryFn('send-email-code', { personId: authP.personId }, METHOD.POST)
.then(setAppContextValue('openVerifySecretCodeModalDialog', true));
}
@@ -64,21 +66,6 @@ const ResetYourPassword = ({ openDialog, closeDialog }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [auth]);
- const sendEmail = async () => {
- const email = emailRef.current.value;
- setAppContextValue('resetEmail', email);
- console.log('email in sendEmail: ', emailRef.current.value);
- if (!validator.isEmail(email)) {
- setWarningLine('Please enter a valid email address.');
- return;
- }
- setWarningLine('');
- setAppContextValue('openVerifySecretCodeModalDialog', true);
- // Logout so that the current sessionID will not be reused when resetting password for a potentially differnt staff member
- await mutateLogout();
- // This retrieve will set the 'authenticatedPerson' app context value, and bring back a new sessionID (without touching the cookie)
- await mutateRetrievePersonByEmail({ emailPersonal: email });
- };
const handleClose = () => {
setOpen(false);
@@ -88,23 +75,41 @@ const ResetYourPassword = ({ openDialog, closeDialog }) => {
const changePassword = async () => {
const pass1 = password1Ref.current.value;
const pass2 = password2Ref.current.value;
- const person = authPerson.current;
+ const person = authPersonRef.current;
+ let id;
+ if (person?.id) {
+ id = person.id;
+ } else {
+ id = personId;
+ }
- console.log('password in changePassword: ', pass1, pass2);
if (pass1 !== pass2) {
setErrorMessage('Your password entries do not match.');
} else {
setErrorMessage('');
- // const person = getAppContextValue('authenticatedPerson');
- await mutatePersonSaveForAuth(makeRequestParams({ id: person.id }, { password: pass1 }));
+ await mutatePasswordSave({ personId: id, password: pass1 });
setAppContextValue('isAuthenticated', true);
- console.log('ResetYourPassword changePassword pass1, pass2: ', pass1, pass2);
+ setAppContextValue('authenticatedPerson', person);
setAppContextValue('resetPassword', pass1);
handleClose();
}
};
- console.log('ResetYourPassword incoming authPerson: ', authPerson);
+ const sendEmail = async () => {
+ const email = emailRef.current.value;
+ setAppContextValue('resetEmail', email);
+ if (!validator.isEmail(email)) {
+ setWarningLine('Please enter a valid email address.');
+ return;
+ }
+ setWarningLine('');
+ setAppContextValue('openVerifySecretCodeModalDialog', true);
+ // Logout so that the current sessionID will not be reused when resetting password for a potentially differnt staff member
+ await mutateLogout();
+ // This retrieve will set the 'authenticatedPerson' app context value, and bring back a new sessionID (without touching the cookie)
+ await mutateRetrievePersonByEmail({ emailPersonal: email });
+ };
+
return (
<>
{
name="email"
required
type="email"
- variant="standard"
+ variant="outlined"
/>
) : (
-
+ >
)}
-
+
>
);
};
ResetYourPassword.propTypes = {
- openDialog: PropTypes.func,
+ openDialog: PropTypes.bool,
closeDialog: PropTypes.func,
};
+const EmailDiv = styled('div')`
+ padding: 0 0 20px 0;
+ font-family: "Roboto", "Helvetica", "Arial", sans-serif;
+ font-weight: 400;
+ font-size: 1rem;
+ line-height: 1.5;
+ letter-spacing: 0.00938em;
+ color: rgba(0, 0, 0, 0.6);
+`;
+
+
export default ResetYourPassword;
diff --git a/src/js/components/Navigation/HeaderBar.jsx b/src/js/components/Navigation/HeaderBar.jsx
index 03c35fd..dd29daa 100644
--- a/src/js/components/Navigation/HeaderBar.jsx
+++ b/src/js/components/Navigation/HeaderBar.jsx
@@ -2,12 +2,12 @@ import { Button, Tab, Tabs } from '@mui/material';
import { withStyles } from '@mui/styles';
import PropTypes from 'prop-types';
import React, { useEffect, useState } from 'react';
-import { useNavigate } from 'react-router';
+import { useLocation, useNavigate } from 'react-router';
import styled from 'styled-components';
import standardBoxShadow from '../../common/components/Style/standardBoxShadow';
import { hasIPhoneNotch } from '../../common/utils/cordovaUtils';
import { normalizedHrefPage } from '../../common/utils/hrefUtils';
-import { authLog, renderLog } from '../../common/utils/logging';
+import { authLog, renderLog, routingLog } from '../../common/utils/logging';
import { useConnectAppContext } from '../../contexts/ConnectAppContext';
import { clearSignedInGlobals } from '../../contexts/contextFunctions';
import { viewerCanSeeOrDo } from '../../models/AuthModel';
@@ -23,26 +23,35 @@ const HeaderBar = ({ hideTabs }) => {
renderLog('HeaderBar');
const navigate = useNavigate();
const { apiDataCache, getAppContextValue, setAppContextValue, getAppContextData } = useConnectAppContext();
- const { viewerAccessRights } = apiDataCache;
const { mutate: mutateLogout } = useLogoutMutation();
const [scrolledDown] = useState(false);
const [tabsValue, setTabsValue] = useState('1');
const [showTabs, setShowTabs] = useState(true);
const [isAuthenticated, setIsAuthenticated] = useState(false);
+ const [viewerAccessRights, setViewerAccessRights] = useState(apiDataCache.viewerAccessRights);
const isAuth = getAppContextValue('isAuthenticated');
useEffect(() => {
if (isAuth !== null) {
authLog('HeaderBar isAuthenticated changed =', isAuth);
+ setViewerAccessRights(apiDataCache.viewerAccessRights);
setIsAuthenticated(isAuth);
}
}, [isAuth]);
+ useEffect(() => {
+ // console.log('HeaderBar detected apiDataCache change', apiDataCache.viewerAccessRights, isAuth);
+ const isAuth2 = getAppContextValue('isAuthenticated');
+ if (Object.keys(apiDataCache.viewerAccessRights).length > 0 || !isAuth2) {
+ setViewerAccessRights(apiDataCache.viewerAccessRights);
+ }
+ }, [apiDataCache]);
+
const logoutApi = async () => {
// I don't think we want to make the weConnectQueryFn call here since we are about to call mutateLogout
const data = await weConnectQueryFn('logout', {}, METHOD.POST);
- console.log(`/logout response in HeaderBar -- status: '${'status'}', data: ${JSON.stringify(data)}`);
+ // console.log(`/logout response in HeaderBar -- status: '${'status'}', data: ${JSON.stringify(data)}`);
clearSignedInGlobals(setAppContextValue, getAppContextData);
navigate('/login');
mutateLogout();
@@ -73,8 +82,16 @@ const HeaderBar = ({ hideTabs }) => {
}
};
+ const authP = getAppContextValue('authenticatedPerson');
+ useEffect(() => {
+ // Track new user logging in, possibly after a reset password, and display the resulting appropriate tabs
+ // console.log('useEffect authenticatedPerson changed');
+ setViewerAccessRights(apiDataCache.viewerAccessRights);
+ initializeTabValue();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [authP]);
+
const handleTabChange = (event, newValue) => {
- // console.log(`handleTabChange newValue: ${newValue}`);
// setTabsValue(newValue);
if (newValue) {
switch (newValue) {
@@ -124,6 +141,17 @@ const HeaderBar = ({ hideTabs }) => {
initializeTabValue();
}, []);
+ // useNavigate() called from anywhere, will update the ReactRouter, and will call initializeTabValue()
+ const loc = useLocation();
+ React.useEffect(() => {
+ routingLog('HeaderBar useLocation detected a url change to: ', loc.pathname);
+ setViewerAccessRights(apiDataCache.viewerAccessRights);
+ initializeTabValue();
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [loc]);
+
+ // console.log('HeaderBar viewerCanSeeOrDo(canViewSystemSettings, viewerAccessRights): ', viewerCanSeeOrDo('canViewSystemSettings', viewerAccessRights));
+
return (
{
// TODO: Need to deal with preferred name searching and display, very possible but it will get more complicated
let addToTeamListTemp = searchResultsList || remainingPeopleToAdd || [];
addToTeamListTemp = addToTeamListTemp.filter((person) => person.firstName.length || person.lastName.length);
- setAddToTeamList(addToTeamListTemp);
+ const sorted = alphabetizePeoplesObject(addToTeamListTemp);
+ setAddToTeamList(sorted);
}, [searchResultsList, remainingPeopleToAdd]);
useEffect(() => {
@@ -174,6 +176,7 @@ const AddPersonDrawerMainContent = () => {
};
const AddPersonDrawerMainContentWrapper = styled('div')`
+ min-width: 30px;
`;
const AddPersonWrapper = styled('div')`
diff --git a/src/js/components/Person/PersonSummaryHeader.jsx b/src/js/components/Person/PersonSummaryHeader.jsx
index d0fb52b..03eb840 100644
--- a/src/js/components/Person/PersonSummaryHeader.jsx
+++ b/src/js/components/Person/PersonSummaryHeader.jsx
@@ -9,17 +9,17 @@ const PersonSummaryHeader = () => {
return (
{/* Width (below) of this PersonHeaderCell comes from the combined widths of the first x columns in PersonMemberList */}
-
+
-
+
Location
-
+
Title / Volunteering Love
{/* Edit icon */}
-
+
);
};
@@ -37,9 +37,9 @@ const PersonHeaderCell = styled.div`
height: 22px;
font-size: ${(props) => (props?.$largefont ? '1.1em;' : '.8em;')};
font-weight: ${(props) => (props?.$titleCell ? ';' : '550;')}
- min-width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
- max-width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
- width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
+ min-width: ${(props) => (props.$cellwidth ? `${props.$cellwidth}px;` : ';')};
+ max-width: ${(props) => (props.$cellwidth ? `${props.$cellwidth}px;` : ';')};
+ width: ${(props) => (props.$cellwidth ? `${props.$cellwidth}px;` : ';')};
overflow: hidden;
white-space: nowrap;
`;
diff --git a/src/js/components/Person/PersonSummaryRow.jsx b/src/js/components/Person/PersonSummaryRow.jsx
index 019c673..2271fe4 100644
--- a/src/js/components/Person/PersonSummaryRow.jsx
+++ b/src/js/components/Person/PersonSummaryRow.jsx
@@ -54,7 +54,7 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => {
{rowNumberForDisplay && (
{rowNumberForDisplay}
@@ -69,23 +69,22 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => {
textDecoration: 'underline',
color: DesignTokenColors.primary500,
}}
- // cellwidth="200"
- cellwidth={200}
+ $cellwidth={200}
>
{/* {`${person.firstName} ${person.lastName}`} */}
{getFullNamePreferredPerson(person)} {/* 2/6/25 currently if you save a first name preferred, it shows up here, but will not be searchable on add team member If you */}
{person.location}
{person.jobTitle}
@@ -94,15 +93,13 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => {
id={`editPerson-personId-${person.personId}`}
onClick={() => editPersonClick(hasEditRights)}
style={{ cursor: 'pointer' }}
- // cellwidth="20"
- cellwidth={20}
+ $cellwidth={20}
>
) : (
@@ -114,14 +111,13 @@ const PersonSummaryRow = ({ person, rowNumberForDisplay, teamId }) => {
id={`removeMember-personId-${person.personId}`}
onClick={() => removeTeamMemberClick(person)}
style={{ cursor: 'pointer' }}
- cellwidth={20}
+ $cellwidth={20}
>
) : (
@@ -167,17 +163,15 @@ const fontSz = (smallfont, smallestfont) => {
}
return ';';
};
-// ${(smallfont && !smallestfont) ? 'font-size: .9em;' : ''}
-// ${(smallestfont && !smallfont) ? 'font-size: .8em;' : ''}
const PersonCell = styled.div`
align-content: center;
border-bottom: 1px solid #ccc;
font-size: ${(props) => (fontSz(props?.$smallfont, props?.$smallestfont))}
height: 22px;
- min-width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
- max-width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';;')};
- width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
+ min-width: ${(props) => (props.$cellwidth ? `${props.$cellwidth}px;` : ';')};
+ max-width: ${(props) => (props.$cellwidth ? `${props.$cellwidth}px;` : ';;')};
+ width: ${(props) => (props.$cellwidth ? `${props.$cellwidth}px;` : ';')};
overflow: hidden;
white-space: nowrap;
`;
diff --git a/src/js/components/PrivateRoute.jsx b/src/js/components/PrivateRoute.jsx
index 5a49563..24d0437 100644
--- a/src/js/components/PrivateRoute.jsx
+++ b/src/js/components/PrivateRoute.jsx
@@ -17,7 +17,6 @@ const PrivateRoute = () => {
if (isSuccessAuth) {
authLog('useFetchData in PrivateRoute useEffect dataAuth good:', dataAuth, isSuccessAuth);
setIsAuthenticated(dataAuth.isAuthenticated);
- setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin);
captureAccessRightsData(dataAuth, isSuccessAuth, apiDataCache, dispatch);
}
}, [dataAuth, isSuccessAuth]);
diff --git a/src/js/components/Style/sharedStyles.js b/src/js/components/Style/sharedStyles.js
index fe94170..7c1b7a8 100644
--- a/src/js/components/Style/sharedStyles.js
+++ b/src/js/components/Style/sharedStyles.js
@@ -1,8 +1,9 @@
import styled from 'styled-components';
export const SearchBarWrapper = styled('div')`
+ display:inline-block;
+ vertical-align:top;
margin-bottom: 16px;
- display: flex;
`;
export const MatchingPerson = styled('div')`
diff --git a/src/js/components/Team/TeamHeader.jsx b/src/js/components/Team/TeamHeader.jsx
index 33348cf..a2ae227 100644
--- a/src/js/components/Team/TeamHeader.jsx
+++ b/src/js/components/Team/TeamHeader.jsx
@@ -30,24 +30,24 @@ const TeamHeader = ({ showHeaderLabels, showIcons, team }) => {
return (
{/* Width (below) of this TeamHeaderCell comes from the combined widths of the first x columns in TeamMemberList */}
-
+
{teamLocal && (
{teamLocal.teamName}
)}
-
+
{showHeaderLabels ? 'Location' : ''}
-
+
{showHeaderLabels ? 'Title / Volunteering Love' : ''}
{/* Edit icon */}
{showIcons && (
<>
{viewerCanSeeOrDo('canEditTeamAnyTeam', viewerAccessRights) && (
-
+
)}
@@ -91,9 +91,9 @@ const TeamHeaderCell = styled.div`
font-size: ${(props) => (props?.$largefont ? '1.1em;' : '.8em;')};
font-weight: ${(props) => (props?.$titleCell ? ';' : '550;')}
height: 22px;
- max-width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
- min-width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
- width: ${(props) => (props.cellwidth ? `${props.cellwidth}px;` : ';')};
+ max-width: ${(props) => (props.$cellwidth ? `${ props.$cellwidth}px;` : ';')};
+ min-width: ${(props) => (props.$cellwidth ? `${ props.$cellwidth}px;` : ';')};
+ width: ${(props) => (props.$cellwidth ? `${ props.$cellwidth}px;` : ';')};
overflow: hidden;
white-space: nowrap;
`;
diff --git a/src/js/components/VerifySecretCodeModal.jsx b/src/js/components/VerifySecretCodeModal.jsx
index bcdce62..0494c62 100644
--- a/src/js/components/VerifySecretCodeModal.jsx
+++ b/src/js/components/VerifySecretCodeModal.jsx
@@ -37,6 +37,7 @@ const VerifySecretCodeModal = ({ classes, person }) => {
useEffect(() => {
setOpenDialogMutable(getAppContextValue('openVerifySecretCodeModalDialog'));
setAppContextValue('secretCodeVerified', false);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [open]);
const handleClose = () => {
@@ -54,7 +55,8 @@ const VerifySecretCodeModal = ({ classes, person }) => {
code += refDigit.current.value.toString();
}
- const data = await weConnectQueryFn('verify-email-code', { personId: person.personId, code }, METHOD.POST);
+ const id = person.personId || person.id;
+ const data = await weConnectQueryFn('verify-email-code', { personId: id, code }, METHOD.POST);
console.log(`/verify-email-code response: data: ${JSON.stringify(data)}`);
if (data.emailVerified) {
setAppContextValue('secretCodeVerified', true);
@@ -85,6 +87,7 @@ const VerifySecretCodeModal = ({ classes, person }) => {
}, 50);
}
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [nextFocus]);
const extractDigits = (str) => {
diff --git a/src/js/config-template.js b/src/js/config-template.js
index c771d6d..2f78ae1 100644
--- a/src/js/config-template.js
+++ b/src/js/config-template.js
@@ -1,7 +1,7 @@
/* eslint-disable */
// Note that we import these values where needed as "webAppConfig"
module.exports = {
- // weconnect React server for the "weconnect" web app
+ // weconnect React server for the "weconnect-client" web app
NAME_FOR_BROWSER_TAB_TITLE: 'WeConnect',
ORGANIZATION_NAME: 'WeVote',
PROTOCOL: 'http://', // 'http://' for local dev (if not using SSL), or 'https://' for live server
diff --git a/src/js/contexts/ConnectAppContext.jsx b/src/js/contexts/ConnectAppContext.jsx
index d24b74f..9ec2be2 100644
--- a/src/js/contexts/ConnectAppContext.jsx
+++ b/src/js/contexts/ConnectAppContext.jsx
@@ -96,12 +96,14 @@ export const ConnectAppContextProvider = ({ children }) => {
authLog('useFetchData in ConnectAppContext useEffect dataAuth good:', dataAuth, isSuccessAuth, isFetchingAuth);
const { isAuthenticated } = dataAuth;
setAppContextValue('authenticatedPerson', dataAuth.person);
- setAppContextValue('isAuthenticated', isAuthenticated);
- setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin);
+ if (dataAuth.person) {
+ setAppContextValue('isAuthenticated', isAuthenticated);
+ }
captureAccessRightsData(dataAuth, isSuccessAuth, apiDataCache, dispatch);
console.log('=============== ConnectAppContextProvider ======= isAuthenticated: ', isAuthenticated, ' ===========');
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataAuth, isSuccessAuth]);
return (
diff --git a/src/js/contexts/contextFunctions.jsx b/src/js/contexts/contextFunctions.jsx
index 0a007f2..ab4e4d4 100644
--- a/src/js/contexts/contextFunctions.jsx
+++ b/src/js/contexts/contextFunctions.jsx
@@ -1,7 +1,6 @@
const clearSignedInGlobals = (setAppContextValue, getAppContextData) => {
setAppContextValue('authenticatedPerson', {});
setAppContextValue('isAuthenticated', false);
- setAppContextValue('loggedInPersonIsAdmin', false);
setAppContextValue('secretCodeVerifiedForReset', false);
setAppContextValue('secretCodeVerified', false);
console.log('appContextData in clearSignedInGlobals after clear: ', getAppContextData());
diff --git a/src/js/pages/Login.jsx b/src/js/pages/Login.jsx
index 3f4555e..9ee86ef 100644
--- a/src/js/pages/Login.jsx
+++ b/src/js/pages/Login.jsx
@@ -1,12 +1,12 @@
import { Button, TextField } from '@mui/material';
import { withStyles } from '@mui/styles';
+import { useQueryClient } from '@tanstack/react-query';
import PropTypes from 'prop-types';
import React, { useEffect, useRef, useState } from 'react';
import { Helmet } from 'react-helmet-async';
-import { useNavigate } from 'react-router';
import styled from 'styled-components';
import validator from 'validator';
-import { renderLog } from '../common/utils/logging';
+import { authLog, reactQueryLog, renderLog } from '../common/utils/logging';
import compileDate from '../compileDate';
import ResetYourPassword from '../components/Login/ResetYourPassword';
import { PageContentContainer } from '../components/Style/pageLayoutStyles';
@@ -14,7 +14,7 @@ import VerifySecretCodeModal from '../components/VerifySecretCodeModal';
import webAppConfig from '../config';
import { useConnectAppContext, useConnectDispatch } from '../contexts/ConnectAppContext';
import { clearSignedInGlobals } from '../contexts/contextFunctions';
-import { captureAccessRightsData } from '../models/AuthModel';
+import { captureAccessRightsData, viewerCanSeeOrDo } from '../models/AuthModel';
import { getFullNamePreferredPerson } from '../models/PersonModel';
import { useLogoutMutation } from '../react-query/mutations';
import weConnectQueryFn, { METHOD, useFetchData } from '../react-query/WeConnectQuery';
@@ -22,9 +22,9 @@ import weConnectQueryFn, { METHOD, useFetchData } from '../react-query/WeConnect
const Login = ({ classes }) => {
renderLog('Login');
- const navigate = useNavigate();
- const { apiDataCache, getAppContextValue, setAppContextValue, getAppContextData } = useConnectAppContext();
+ const { apiDataCache, apiDataCache: { viewerAccessRights }, getAppContextValue, setAppContextValue, getAppContextData } = useConnectAppContext();
const dispatch = useConnectDispatch();
+ const queryClient = useQueryClient();
const { mutate: mutateLogout } = useLogoutMutation();
const firstNameFldRef = useRef('');
@@ -39,38 +39,51 @@ const Login = ({ classes }) => {
const authPerson = useRef(undefined);
const [loginAttempted, setLoginAttempted] = useState(false);
+ const [isForSomeOneElse, setIsForSomeOneElse] = useState(false);
const [openResetPasswordDialog, setOpenResetPasswordDialog] = useState(false);
const [showCreateStuff, setShowCreateStuff] = useState(false);
const [successLine, setSuccessLine] = useState('');
const [warningLine, setWarningLine] = useState('');
+ const [loginCount, setLoginCount] = useState(0);
const { data: dataAuth, isSuccess: isSuccessAuth, isFetching: isFetchingAuth } = useFetchData(['get-auth'], {}, METHOD.POST);
useEffect(() => {
if (isSuccessAuth) {
- console.log('useFetchData in Login useEffect dataAuth good:', dataAuth, isSuccessAuth, isFetchingAuth);
+ authLog('useFetchData get-auth in Login dataAuth:', dataAuth, isSuccessAuth, isFetchingAuth);
const { isAuthenticated, person: authenticatedPerson, emailVerified: emailVerifiedFromAPI, personId } = dataAuth;
authPerson.current = authenticatedPerson;
+ if (authenticatedPerson) {
+ setAppContextValue('isAuthenticated', isAuthenticated);
+ }
captureAccessRightsData(dataAuth, isSuccessAuth, apiDataCache, dispatch);
- console.log('appContextData in Login [dataAuth, isSuccessAuth]: ', getAppContextData());
if (!emailVerifiedFromAPI && personId > 0) {
setWarningLine('');
setSuccessLine('A verification email has been sent to your address');
setAppContextValue('openVerifySecretCodeModalDialog', true);
} else if (isAuthenticated && authenticatedPerson) {
setSuccessLine(`Signed in as ${getFullNamePreferredPerson(authenticatedPerson)}`);
- setAppContextValue('loggedInPersonIsAdmin', dataAuth.loggedInPersonIsAdmin);
if (loginAttempted) { // if we navigate to here directly, not as a result of a loginAPI
- setTimeout(() => {
- navigate('/tasks');
- }, 2000);
+ // setTimeout(() => {
+ // navigate('/tasks');
+ setAppContextValue('navigatedFromLogin', true);
+ // }, 2000);
}
- } else {
+ } else if (!getAppContextValue('openVerifySecretCodeModalDialog')) {
+ // console.log('======== appContextData in Login: Please sign in');
setSuccessLine('Please sign in');
}
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataAuth, isSuccessAuth]);
+ const isAuth = getAppContextValue('isAuthenticated');
+ useEffect(() => {
+ // rerender if logged out from HeaderBar
+ setLoginCount(loginCount + 1);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isAuth]);
+
const loginApi = async (email, password) => {
if (!validator.isEmail(email)) {
setWarningLine('Please enter a valid email address.');
@@ -85,16 +98,24 @@ const Login = ({ classes }) => {
setLoginAttempted(true); // so we know when to timeout to /tasks
const data = await weConnectQueryFn('login', { email, password }, METHOD.POST);
- console.log(`/login response -- status: '${'status'}', data: ${JSON.stringify(data)}`);
- if (data.personId > 0) {
+ // console.log(`/login response -- status: '${'status'}', data: ${JSON.stringify(data)}`);
+ // console.log('appContextData in Login after /login response: ', getAppContextData());
+ if (data?.personId > 0) {
setAppContextValue('isAuthenticated', data.emailVerified);
+ setAppContextValue('authenticatedPerson', data.person);
+ queryClient.invalidateQueries('get-auth');
if (data.emailVerified) {
+ passwordFldRef.current.value = ''; // Blank the email field after signing in
setWarningLine('');
+ setAppContextValue('secretCodeVerified', true);
+ setAppContextValue('openVerifySecretCodeModalDialog', false);
+ setAppContextValue('secretCodeVerified', false);
+ setAppContextValue('secretCodeVerifiedForReset', false);
+ setAppContextValue('resetPassword', '');
setSuccessLine(`${getFullNamePreferredPerson(data.person)}, you are signed in!`);
- setAppContextValue('authenticatedPerson', data);
- setTimeout(() => {
- navigate('/tasks');
- }, 2000);
+ // setTimeout(() => {
+ // navigate('/tasks');
+ // }, 4000);
} else {
authPerson.current = { // just enough data for VerifySecretCodeModal
personId: data.personId,
@@ -104,32 +125,48 @@ const Login = ({ classes }) => {
setSuccessLine('A verification email has been sent to your address');
}
} else {
- setWarningLine(data.error.msg);
+ setWarningLine(data?.error?.msg || 'error message with bad data');
setSuccessLine('');
}
};
+ const clearOnCreate = () => {
+ // console.log('clearOnCreate -------------- 1 ------------ ', openResetPasswordDialog);
+ if (!openResetPasswordDialog) {
+ // console.log('clearOnCreate -------------- 2 ------------ ', openResetPasswordDialog);
+ setAppContextValue('resetEmail', '');
+ setAppContextValue('resetPassword', '');
+ setAppContextValue('openVerifySecretCodeModalDialog', false);
+ setAppContextValue('secretCodeVerified', false);
+ setAppContextValue('secretCodeVerifiedForReset', false);
+ setOpenResetPasswordDialog(false);
+ setShowCreateStuff(false);
+ const per = authPerson.current ? authPerson.current : getAppContextValue('authenticatedPerson');
+ setSuccessLine(`${getFullNamePreferredPerson(per)}, you are signed in!`);
+ passwordFldRef.current.value = ''; // Blank the email field after signing in
+ }
+ };
+
const secretCodeVerified = getAppContextValue('secretCodeVerified');
- const resetPassword = getAppContextValue('resetPassword');
+ const resetPassword = getAppContextValue('resetPassword') || '';
useEffect(() => {
- if (secretCodeVerified === true && resetPassword && resetPassword.length) {
+ if (secretCodeVerified === true) {
+ loginApi(emailPersonalFldRef.current.value, passwordFldRef.current.value).then(() => {
+ clearOnCreate();
+ });
+ } else if (resetPassword && resetPassword.length) {
loginApi(getAppContextValue('resetEmail'), getAppContextValue('resetPassword')).then(() => {
- // console.log('--------- useEffect secretCodeVerified in Login, clearing resetEmail and resetPassword', e, p);
- setAppContextValue('resetEmail', '');
- setAppContextValue('resetPassword', '');
- setAppContextValue('openVerifySecretCodeModalDialog', false);
- setAppContextValue('secretCodeVerified', false);
- setAppContextValue('secretCodeVerifiedForReset', false);
- // console.log('appContextData in Login L124: ', getAppContextData());
+ clearOnCreate();
});
}
+ // eslint-disable-next-line react-hooks/exhaustive-deps
}, [secretCodeVerified, resetPassword]);
- const logoutApi = async () => {
- const data = await weConnectQueryFn('logout', {}, METHOD.POST);
- console.log(`/logout response -- status: '${'status'}', data: ${JSON.stringify(data)}`);
+ const logoutApiInLogin = async () => {
+ const data = await weConnectQueryFn('logout', { credentials: 'same-origin' }, METHOD.POST);
+ // console.log(`/logout response -- status: '${'status'}', data: ${JSON.stringify(data)}`);
if (data.authenticated) {
- setWarningLine(data.errors.msg);
+ setWarningLine(data?.errors?.msg);
setSuccessLine('');
} else {
setWarningLine('');
@@ -139,13 +176,12 @@ const Login = ({ classes }) => {
};
const verifyYourEmail = async (personId) => {
- console.log('verifyYourEmail ----------------');
+ // console.log('verifyYourEmail ----------------');
if (!personId || personId < 1) {
console.error('Invalid personId found in verifyYourEmail');
}
- console.error('TESTING personId found in verifyYourEmail');
const data = await weConnectQueryFn('send-email-code', { personId }, METHOD.POST);
- console.log(`/send-email-code response: data: ${JSON.stringify(data)}`);
+ reactQueryLog(`/send-email-code response: data: ${JSON.stringify(data)}`);
};
const signupApi = async (firstName, lastName, location, emailPersonal, emailOfficial, password, confirmPassword) => {
@@ -153,31 +189,37 @@ const Login = ({ classes }) => {
const data = await weConnectQueryFn('signup', params, METHOD.POST);
try {
- console.log(`/signup response -- status: '${'status'}', data: ${JSON.stringify(data)}`);
+ reactQueryLog(`/signup response -- status: '${'status'}', data: ${JSON.stringify(data)}`);
let errStr = '';
for (let i = 0; i < data.errors.length; i++) {
- errStr += data.errors[i].msg;
+ errStr += data.errors[i]?.msg;
}
setWarningLine(errStr);
if (data.personCreated) {
setSuccessLine(`user # ${data.personId} created`);
- verifyYourEmail(data.personId).then(() => {
- setSuccessLine('A verification email has been sent to your address');
- console.log('verifyYourEmail in signupApi then clause , openVerifySecretCodeModalDialog true');
- setAppContextValue('openVerifySecretCodeModalDialog', true);
- });
+ setSuccessLine(`user # ${data.personId} created`);
+ authPerson.current = data.person;
+ if (isForSomeOneElse) {
+ setShowCreateStuff(false);
+ setSuccessLine(`A person record was created for ${firstName} ${lastName} (they will have to verify their email on their first login)`);
+ } else {
+ verifyYourEmail(data.personId).then(() => {
+ setSuccessLine('A verification email has been sent to your address');
+ setAppContextValue('openVerifySecretCodeModalDialog', true);
+ });
+ }
}
} catch (e) {
- console.log('signup error', e);
+ console.error('signup error', e);
}
};
const loginPressed = () => {
- const email = emailPersonalFldRef.current.value;
- const password = passwordFldRef.current.value;
+ const email = (emailPersonalFldRef.current.value)?.trim();
+ const password = (passwordFldRef.current.value)?.trim();
- if (email.length === 0 || password.length === 0) {
- console.log('too short');
+ if (email?.length === 0 || password?.length === 0) {
+ // console.log('too short');
setWarningLine('Enter a valid username and password');
} else {
setWarningLine('');
@@ -185,18 +227,32 @@ const Login = ({ classes }) => {
}
};
- const useSignOutPressed = () => {
- // clearSignedInGlobals is also called in logoutApi, so isn't needed here
- // TODO 2/23/25: unfortunately there are two logoutApi(), consolidating them is high priority
+ const removeSessionCookie = () => {
+ const urlObject = new URL(webAppConfig.STAFF_API_SERVER_ROOT_URL);
+ const updatedCookie = `WeConnectSession=; Max-Age=0; path=/; domain=${urlObject.hostname}`;
+ document.cookie = updatedCookie;
+ console.log('Login removeSessionCookie, cookie that was removed: ', updatedCookie);
+ };
+
+ const closeResetYourPassword = () => {
+ clearSignedInGlobals(setAppContextValue, getAppContextData);
+ // console.log('closeResetYourPassword in Login before logoutApiInLogin()');
+ logoutApiInLogin().then(() => removeSessionCookie());
+ };
+
+ const signOutButtonPressed = () => {
+ passwordFldRef.current.value = ''; // Blank the email field after signing out
clearSignedInGlobals(setAppContextValue, getAppContextData);
- logoutApi().then();
+ setOpenResetPasswordDialog(false);
+ // console.log('signOutButtonPressed in Login before logoutApiInLogin()');
+ logoutApiInLogin().then(() => removeSessionCookie());
};
const createPressed = () => {
if (!showCreateStuff) {
setShowCreateStuff(true);
setWarningLine('');
- setSuccessLine('');
+ setSuccessLine(isForSomeOneElse ? 'Creating an account for someone else' : '');
} else {
setWarningLine('');
let errStr = '';
@@ -224,8 +280,28 @@ const Login = ({ classes }) => {
}
};
+ const createForSomeoneElsePressed = () => {
+ setIsForSomeOneElse(true);
+ createPressed();
+ };
+
+ const resetYourPasswordClicked = () => {
+ console.log('resetYourPasswordClicked', openResetPasswordDialog);
+ setOpenResetPasswordDialog(true);
+ setAppContextValue('openVerifySecretCodeModalDialog', true);
+ };
// console.log(getAppContextData());
+ const isAdmin = viewerCanSeeOrDo('canAddTeamMemberAnyTeam', viewerAccessRights);
+ const isAuthSafe = getAppContextValue('isAuthenticated') || false;
+ const displayVerify =
+ !isForSomeOneElse &&
+ authPerson.current &&
+ Object.keys(authPerson.current).length > 0 &&
+ getAppContextValue('secretCodeVerified') !== true &&
+ (getAppContextValue('openVerifySecretCodeModalDialog') || false);
+
+ // console.log('login before return render, getAppContextData()', getAppContextData());
return (