# dha-login

## Getting Started

### Install

Install from npm:

- npm i dha-login

### Build

The package uses the Typescript compiler (TSC) & [Vite](https://vitejs.dev/) to build the source code. To build the project run the following command:

- npm run build

### Peer Dependencies

These are the peerDependencies for dha-login. Make sure your app has these installed as dependencies if you want to use dha-login.

```json
"peerDependencies": {
  "@reduxjs/toolkit": "^1.8.1",
  "react": "^17.0.2",
  "react-redux": "^7.2.8",
  "react-router-dom": "^6.3.0"
}
```

## Usage Overview

This module should be used if you want login functionality in your PWA. This functionality involves prompting a user to make an "account" by designating a 6-digit pin & two security questions. The pin will be used on every subsequent login to identify the user, and correctly answering the security questions can authorize the user to reset their pin for any reason.

There are a few pieces from this module that you will need to configure inside your app:

- [Redux State](#redux-state)
  - **accountReducer**
  - **AccountState (type)**
  - **initialAccountState**
- [Providers](#providers)
  - `<LoginDatabaseProvider />` - wrapper containing access to user login info
  - `<AuthProvider />` - wrapper inside `<LoginDatabaseProvider />` that handles authentication & provides access to **AuthContext**
- [Components](#components) (Routing)
  - `<RequireAuth />` - wraps any routes you want only accessible by authenticated (logged-in) users, providing the screens necessary for user authentication.
- [Context](#context)

  - `{AuthContext}` - holds authenticated (logged-in) state boolean, a setter for that state (setAuthenticated), the username set by the current user, and the setter for setting the username variable.

  If you plan on using this module in the pwa-starter, make sure to edit the starter's 'tailwind.config.cjs' file with the following code (to tell tailwind to scan dha-login for tailwind classes):

  ```javascript
  module.exports = {
    content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}', './node_modules/dha-login/**/*.js'],
  };
  ```

### Redux State

#### Relevant exports:

- accountReducer
- AccountState (type)
- InitialAccountState

#### Details

**accountReducer**, **AccountState (type)**, and **initialAccountState** will be utilized to ensure that a user's "account created?" state (either true or false) remains even after they close the app.

The **accountReducer** reducer controls a a boolean state-value "created" that indicates whether or not the user has made an "account."

**accountReducer** is created using [createSlice() from Redux Toolkit](https://redux-toolkit.js.org/api/createSlice). You will need to add **accountReducer** to your root reducer. Most likely this will be done by including it in your _combineReducers()_ function, which combines multiple reducers into one rootReducer.
for example:

#### Implementation

_example rootReducer.ts_:

```javascript
import { combineReducers } from '@reduxjs/toolkit';

import { accountReducer, AccountState, initialAccountState } from 'dha-login'; // Redux elements

export type State = {
  counterState: CounterState,
  eulaState: EulaState,
  accountState: AccountState, // AccountState type defines accountState prop of State type
};

export const initialState: State = {
  counterState: initialCounterState,
  eulaState: initialEulaState,
  accountState: initialAccountState, // initialAccountState
};

export const rootReducer = combineReducers({
  counterState: counterSlice,
  eulaState: eulaSlice,
  // key name for accountReducer MUST be "accountState"
  accountState: accountReducer, // accountReducer is added to rootReducers
});
```

**!! IMPORTANT NOTE !!**: In the root reducer, the key for the accountReducer **MUST** be "accountState." This is because `<RequireAuth />` accesses that bit of state by that exact key.

**!! You will also need to whitelist** `accountState` **in your redux-persist config** (likely in your store.ts file, see example below) **!!**:

_example store.ts_:

```javascript
const persistConfig = {
  key: 'root',
  storage,
  whitelist: ['eulaState', 'endorsementState', 'androidViewState', 'accountState'],
};

const store = configureStore({
  reducer: persistReducer(persistConfig, rootReducer),
  preloadedState: initialState,
  middleware: (getDefaultMiddleware) =>
    getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }),
});
```

The whitelist property controls which parts of your redux state actually get persisted across all sessions. See [redux-persist](https://www.npmjs.com/package/redux-persist) documentation if you want more specific details.

### Providers

#### Relevant Exports

- `<LoginDatabaseProvider />`
- `<AuthProvider />`

#### Details

`<LoginDatabaseProvider />` and `<AuthProvider />` should be wrapped somewhere around your `<Routes />`.

`<LoginDatabaseProvider />` **should wrap around** `<AuthProvider />` **in your app** (`AuthContext` depends on `<LoginDatabaseProvider />` for authentication functionality)**.**

- `<AuthProvider />` gives its children access to `{AuthContext}`, and stores base64-encoded `authenticated` state from `{AuthContext}` in [Session Storage](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage) in order to persist authentication-state through page refresh.
- `<LoginDatabaseProvider />` gives `<AuthProvider />` necessary database access (user "account" information).

#### Implementation

Your nesting should look something like this:
_example App.tsx_:

```javascript
import { AuthProvider, LoginDatabaseProvider } from 'dha-login';

const App = () => {
  return (
    //<BrowserRouter > or some other Router...
    <Provider store={store}>
      <PersistGate persistor={persistor}>
        <LoginDatabaseProvider>
          <DialogProvider>
            <AuthProvider>
              <Layout>
                <AppRoutes />
              </Layout>
            </AuthProvider>
          </DialogProvider>
        </LoginDatabaseProvider>
      </PersistGate>
    </Provider>
    //</BrowserRouter>
  );
};
```

### Components

#### Relevant Exports

- `<RequireAuth />`

#### Details

Use `<RequireAuth />` to wrap any `<Route />` that you want to only be accessible by a logged-in user.

You will use this component wherever you have set up routing for your app (using React Router).

Make a `<Route />` for `<RequireAuth />`, and use it to wrap any other `<Route />`'s you only want accessible by the authenticated user.

#### Implementation

_example AppRoutes.tsx_:

```javascript jsx
import { RequireAuth } from 'dha-login';

import Home from 'somewhere/Home.tsx';

<Routes>
  <Route element={<RequireAuth />}>
    <Route element={<Home />} path="/" />
    <Route element={<Protected2 />} path="/protectedRoute2" />
  </Route>
</Routes>;
```

### Context

#### Relevant Exports

- `{AuthContext}`
  - **contains**:
    - `username` - string given by user during initial account setup
    - `authenticated` - boolean state value where _true_ means the user is currently 'logged in'
    - `setAuthenticated(booleanValue)` - function you can use to log a user out by setting `authenticated` to _false_
    - `setUsername(string)` - function that allows username to be set, shouldn't be necessary but it's there if you need it

#### Details

`{AuthContext}` can be used via `React.useContext()` to access the `authenticated` state, and the setter for that state, `setAuthenticated()`

when `authenticated` state is true, `<RequireAuth />` allows its child (protected) routes to be accessed/rendered.

when `authenticated` state is false, `<RequireAuth />` prompts the user for their pin, or allows them to reset their PIN by answering two security questions set up during account creation.

you can use `setAuthenticated(false)` to log the user out. A user will also be logged out automatically an hour after logging-in, and will have to re-log.

#### Implementation

```javascript
import { useContext } from 'react';

import { AuthContext } from 'dha-login';

const exampleComponent = () => {
  const { authenticated, setAuthenticated, username } = useContext(AuthContext);
  // authenticated returns true or false
  // setAuthenticated(false) sets authenticated to false, effectively logging a user out
  // username is the string the user set as their username upon account creation
  // ...
};
```

### Theme

#### Relevant Exports

- `{themeProps}`
- `{useUpdateTheme}`

#### Details

You can update the theme of dha-login using `{useUpdateTheme}` by passing in a `{themeProps}` object.
You call this function in a useEffect wherever you have set up routing for your app (using React Router).
You must use tailwindcss in the `{themeProps}` object to modify the theme.

#### Implementation

_example AppRoutes.tsx_:

```javascript
import { useUpdateTheme } from 'dha-login';
import type { themeProps } from 'dha-login';

const updater = useUpdateTheme();

useEffect(() => {
  const theme: themeProps = {
    modalBackground: 'bg-gradient-to-b from-gradient-light to-gradient-dark',
    cardColor: 'bg-white',
    textColor: 'text-black',
    secondaryTextColor: 'text-secondary-color',
    buttonColor: 'bg-secondary-color',
    buttonTextColor: 'text-theme-dark-grey',
    buttonShadow: 'shadow-card',
    dropDownBorder: 'border border-solid border-[#6b7280]',
  };
  updater(theme);
}, []);
```

##### themeProps

Properties you can modify to update the theme.

```javascript
interface themeProps {
  modalBackground?: string;
  cardColor?: string;
  textColor?: string;
  secondaryTextColor?: string;
  buttonColor?: string;
  buttonTextColor?: string;
  buttonShadow?: string;
  dropDownBorder?: string;
}
```

## Troubleshooting

Npm Install Issues

- Clearing the package-lock.json and node_modules folder can help ensure that an app or package starts in a clean state.
  - rm -rf package-lock.json node_modules

Hook Errors

- Hook errors can be caused when a local package is installed into an application where the package devDependencies conflict with the application dependencies by having multiple instances of the same dependency.
- You must tell the local dha-login to use peerDependencies from the app you are including the dha-login into using npm link (the example below is for using the dha-login in the dha-login).
  - Refer to "Link peerDependencies" section above.
    - npm link ../dha-login/node_modules/@material-ui/core

## NPM

https://www.npmjs.com/package/dha-login

## License

_pending_
