Merge branch 'feature/IO-1828-Front-End-Package-Updates' into vite
# Conflicts: # client/package-lock.json
This commit is contained in:
@@ -62,6 +62,7 @@
|
|||||||
"react-icons": "^5.0.1",
|
"react-icons": "^5.0.1",
|
||||||
"react-image-lightbox": "^5.1.4",
|
"react-image-lightbox": "^5.1.4",
|
||||||
"react-intersection-observer": "^9.5.3",
|
"react-intersection-observer": "^9.5.3",
|
||||||
|
"react-markdown": "^9.0.1",
|
||||||
"react-number-format": "^5.1.4",
|
"react-number-format": "^5.1.4",
|
||||||
"react-redux": "^9.1.0",
|
"react-redux": "^9.1.0",
|
||||||
"react-resizable": "^3.0.5",
|
"react-resizable": "^3.0.5",
|
||||||
@@ -99,6 +100,7 @@
|
|||||||
"buildcra": "cross-env-shell VITE_APP_GIT_SHA=\\\"`git rev-parse --short HEAD`\\\" vite build",
|
"buildcra": "cross-env-shell VITE_APP_GIT_SHA=\\\"`git rev-parse --short HEAD`\\\" vite build",
|
||||||
"test": "cypress open",
|
"test": "cypress open",
|
||||||
"eject": "react-scripts eject",
|
"eject": "react-scripts eject",
|
||||||
|
"eulaize": "node src/utils/eulaize.js",
|
||||||
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular ."
|
"madge": "madge --image ./madge-graph.svg --extensions js,jsx,ts,tsx --circular ."
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {Route, Routes} from "react-router-dom";
|
|||||||
import {createStructuredSelector} from "reselect";
|
import {createStructuredSelector} from "reselect";
|
||||||
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
|
import DocumentEditorContainer from "../components/document-editor/document-editor.container";
|
||||||
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
|
import ErrorBoundary from "../components/error-boundary/error-boundary.component";
|
||||||
|
|
||||||
//Component Imports
|
//Component Imports
|
||||||
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
import LoadingSpinner from "../components/loading-spinner/loading-spinner.component";
|
||||||
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
|
import DisclaimerPage from "../pages/disclaimer/disclaimer.page";
|
||||||
@@ -20,6 +21,7 @@ import {selectBodyshop, selectCurrentUser,} from "../redux/user/user.selectors";
|
|||||||
import PrivateRoute from "../components/PrivateRoute";
|
import PrivateRoute from "../components/PrivateRoute";
|
||||||
import "./App.styles.scss";
|
import "./App.styles.scss";
|
||||||
import handleBeta from "../utils/betaHandler";
|
import handleBeta from "../utils/betaHandler";
|
||||||
|
import Eula from "../components/eula/eula.component";
|
||||||
|
|
||||||
const ResetPassword = lazy(() =>
|
const ResetPassword = lazy(() =>
|
||||||
import("../pages/reset-password/reset-password.component")
|
import("../pages/reset-password/reset-password.component")
|
||||||
@@ -42,7 +44,6 @@ const mapDispatchToProps = (dispatch) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function App({bodyshop, checkUserSession, currentUser, online, setOnline}) {
|
export function App({bodyshop, checkUserSession, currentUser, online, setOnline}) {
|
||||||
|
|
||||||
const client = useSplitClient().client;
|
const client = useSplitClient().client;
|
||||||
const [listenersAdded, setListenersAdded] = useState(false)
|
const [listenersAdded, setListenersAdded] = useState(false)
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
@@ -121,6 +122,10 @@ export function App({bodyshop, checkUserSession, currentUser, online, setOnline}
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (!currentUser.eulaIsAccepted) {
|
||||||
|
return <Eula/>
|
||||||
|
}
|
||||||
|
|
||||||
// Any route that is not assigned and matched will default to the Landing Page component
|
// Any route that is not assigned and matched will default to the Landing Page component
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={<LoadingSpinner message="ImEX Online"/>}>
|
<Suspense fallback={<LoadingSpinner message="ImEX Online"/>}>
|
||||||
@@ -131,10 +136,12 @@ export function App({bodyshop, checkUserSession, currentUser, online, setOnline}
|
|||||||
<Route path="/csi/:surveyId" element={<ErrorBoundary><CsiPage/></ErrorBoundary>}/>
|
<Route path="/csi/:surveyId" element={<ErrorBoundary><CsiPage/></ErrorBoundary>}/>
|
||||||
<Route path="/disclaimer" element={<ErrorBoundary><DisclaimerPage/></ErrorBoundary>}/>
|
<Route path="/disclaimer" element={<ErrorBoundary><DisclaimerPage/></ErrorBoundary>}/>
|
||||||
<Route path="/mp/:paymentIs" element={<ErrorBoundary><MobilePaymentContainer/></ErrorBoundary>}/>
|
<Route path="/mp/:paymentIs" element={<ErrorBoundary><MobilePaymentContainer/></ErrorBoundary>}/>
|
||||||
<Route path="/manage/*" element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
|
<Route path="/manage/*"
|
||||||
|
element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
|
||||||
<Route path="*" element={<ManagePage/>}/>
|
<Route path="*" element={<ManagePage/>}/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/tech/*" element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
|
<Route path="/tech/*"
|
||||||
|
element={<ErrorBoundary><PrivateRoute isAuthorized={currentUser.authorized}/></ErrorBoundary>}>
|
||||||
<Route path="*" element={<TechPageContainer/>}/>
|
<Route path="*" element={<TechPageContainer/>}/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route path="/edit/*" element={<PrivateRoute isAuthorized={currentUser.authorized}/>}>
|
<Route path="/edit/*" element={<PrivateRoute isAuthorized={currentUser.authorized}/>}>
|
||||||
|
|||||||
@@ -147,23 +147,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update row highlighting on production board.
|
|
||||||
.ant-table-tbody > tr.ant-table-row:hover > td {
|
|
||||||
background: #e7f3ff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table-tbody > tr.ant-table-row-selected > td {
|
|
||||||
background: #e6f7ff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.job-line-manual {
|
.job-line-manual {
|
||||||
color: tomato;
|
color: tomato;
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.ant-table-column-sort {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.ant-table-tbody > tr.ant-table-row:nth-child(2n) > td {
|
.ant-table-tbody > tr.ant-table-row:nth-child(2n) > td {
|
||||||
background-color: #f4f4f4;
|
background-color: #f4f4f4;
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import {defaultsDeep} from "lodash";
|
import {defaultsDeep} from "lodash";
|
||||||
|
import {theme} from "antd";
|
||||||
|
|
||||||
|
const {defaultAlgorithm, darkAlgorithm} = theme;
|
||||||
|
|
||||||
|
let isDarkMode = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default theme
|
* Default theme
|
||||||
@@ -6,6 +11,11 @@ import {defaultsDeep} from "lodash";
|
|||||||
*/
|
*/
|
||||||
const defaultTheme = {
|
const defaultTheme = {
|
||||||
components: {
|
components: {
|
||||||
|
Table: {
|
||||||
|
rowHoverBg: '#e7f3ff',
|
||||||
|
rowSelectedBg: '#e6f7ff',
|
||||||
|
headerSortHoverBg: 'transparent',
|
||||||
|
},
|
||||||
Menu: {
|
Menu: {
|
||||||
darkItemHoverBg: '#1677ff',
|
darkItemHoverBg: '#1677ff',
|
||||||
itemHoverBg: '#1677ff',
|
itemHoverBg: '#1677ff',
|
||||||
@@ -40,7 +50,11 @@ const devTheme = {
|
|||||||
*/
|
*/
|
||||||
const prodTheme = {};
|
const prodTheme = {};
|
||||||
|
|
||||||
const theme = process.env.NODE_ENV === "development" ? devTheme
|
const currentTheme = process.env.NODE_ENV === "development" ? devTheme
|
||||||
: prodTheme;
|
: prodTheme;
|
||||||
|
|
||||||
export default defaultsDeep(theme, defaultTheme);
|
const finaltheme = {
|
||||||
|
algorithm: isDarkMode ? darkAlgorithm : defaultAlgorithm,
|
||||||
|
...defaultsDeep(currentTheme, defaultTheme)
|
||||||
|
}
|
||||||
|
export default finaltheme;
|
||||||
226
client/src/components/eula/eula.component.jsx
Normal file
226
client/src/components/eula/eula.component.jsx
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
import React, { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
import {Button, Card, Checkbox, Col, Form, Input, Modal, notification, Row, Space} from "antd";
|
||||||
|
import Markdown from "react-markdown";
|
||||||
|
import { createStructuredSelector } from "reselect";
|
||||||
|
import { selectCurrentEula, selectCurrentUser } from "../../redux/user/user.selectors";
|
||||||
|
import { connect } from "react-redux";
|
||||||
|
import { FormDatePicker } from "../form-date-picker/form-date-picker.component";
|
||||||
|
import { INSERT_EULA_ACCEPTANCE } from "../../graphql/user.queries";
|
||||||
|
import { useMutation } from "@apollo/client";
|
||||||
|
import { acceptEula } from "../../redux/user/user.actions";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import day from '../../utils/day';
|
||||||
|
|
||||||
|
import './eula.styles.scss';
|
||||||
|
|
||||||
|
const Eula = ({ currentEula, currentUser, acceptEula }) => {
|
||||||
|
const [formReady, setFormReady] = useState(false);
|
||||||
|
const [hasEverScrolledToBottom, setHasEverScrolledToBottom] = useState(false);
|
||||||
|
const [insertEulaAcceptance] = useMutation(INSERT_EULA_ACCEPTANCE);
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const markdownCardRef = useRef(null);
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [api, contextHolder] = notification.useNotification();
|
||||||
|
|
||||||
|
const handleScroll = (e) => {
|
||||||
|
const bottom = e.target.scrollHeight - 100 <= e.target.scrollTop + e.target.clientHeight;
|
||||||
|
if (bottom && !hasEverScrolledToBottom) {
|
||||||
|
setHasEverScrolledToBottom(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChange = useCallback(() => {
|
||||||
|
form.validateFields({ validateOnly: true })
|
||||||
|
.then(() => setFormReady(hasEverScrolledToBottom))
|
||||||
|
.catch(() => setFormReady(false));
|
||||||
|
}, [form, hasEverScrolledToBottom]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
handleChange();
|
||||||
|
}, [handleChange, hasEverScrolledToBottom, form]);
|
||||||
|
|
||||||
|
const onFinish = async ({ acceptTerms, ...formValues }) => {
|
||||||
|
const eulaId = currentEula.id;
|
||||||
|
const useremail = currentUser.email;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { accepted_terms, ...otherFormValues } = formValues;
|
||||||
|
await insertEulaAcceptance({
|
||||||
|
variables: {
|
||||||
|
eulaAcceptance: {
|
||||||
|
eulaid: eulaId,
|
||||||
|
useremail,
|
||||||
|
...otherFormValues,
|
||||||
|
date_accepted: new Date(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
acceptEula();
|
||||||
|
} catch (err) {
|
||||||
|
api.error({
|
||||||
|
message: t('eula.errors.acceptance.message'),
|
||||||
|
description: t('eula.errors.acceptance.description'),
|
||||||
|
placement: 'bottomRight',
|
||||||
|
duration: 5000,
|
||||||
|
|
||||||
|
});
|
||||||
|
console.log(`${t('eula.errors.acceptance.message')}`);
|
||||||
|
console.dir({
|
||||||
|
message: err.message,
|
||||||
|
stack: err.stack,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{contextHolder}
|
||||||
|
<Modal
|
||||||
|
title={t('eula.titles.modal')}
|
||||||
|
className='eula-modal'
|
||||||
|
width={'100vh'}
|
||||||
|
open={currentEula}
|
||||||
|
footer={() => (
|
||||||
|
<Button
|
||||||
|
className='eula-accept-button'
|
||||||
|
form='tosForm'
|
||||||
|
type="primary"
|
||||||
|
size='large'
|
||||||
|
htmlType="submit"
|
||||||
|
disabled={!formReady}
|
||||||
|
children={t('eula.buttons.accept')}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
closable={false}
|
||||||
|
>
|
||||||
|
<Space direction='vertical'>
|
||||||
|
<Card type='inner' className='eula-markdown-card' onScroll={handleScroll} ref={markdownCardRef}>
|
||||||
|
<div id='markdowndiv' className='eula-markdown-div'>
|
||||||
|
<Markdown children={currentEula?.content?.replace(/\\n/g, '\n')} />
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
<EulaFormComponent form={form} handleChange={handleChange} onFinish={onFinish} t={t} />
|
||||||
|
{!hasEverScrolledToBottom && (
|
||||||
|
<Card className='eula-never-scrolled' type='inner'>
|
||||||
|
<h3>{t('eula.content.never_scrolled')}</h3>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const EulaFormComponent = ({ form, handleChange, onFinish, t }) => (
|
||||||
|
<Card type='inner' title={t('eula.titles.upper_card')}>
|
||||||
|
<Form id='tosForm' onChange={handleChange} onFinish={onFinish} form={form}>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
label={t('eula.labels.first_name')}
|
||||||
|
name="first_name"
|
||||||
|
rules={[{ required: true, message: t('eula.messages.first_name') }]}
|
||||||
|
>
|
||||||
|
<Input placeholder={t('eula.labels.first_name')}
|
||||||
|
aria-label={t('eula.labels.first_name')} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
label={t('eula.labels.last_name')}
|
||||||
|
name="last_name"
|
||||||
|
rules={[{ required: true, message: t('eula.messages.last_name') }]}
|
||||||
|
>
|
||||||
|
<Input placeholder={t('eula.labels.last_name')}
|
||||||
|
aria-label={t('eula.labels.last_name')} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
label={t('eula.labels.business_name')}
|
||||||
|
name="business_name"
|
||||||
|
rules={[{ required: true, message: t('eula.messages.business_name') }]}
|
||||||
|
>
|
||||||
|
<Input placeholder={t('eula.labels.business_name')}
|
||||||
|
aria-label={t('eula.labels.business_name')} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
label={t('eula.labels.phone_number')}
|
||||||
|
name="phone_number"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
pattern: /^(\+\d{1,2}\s?)?1?-?\.?\s?\(?\d{3}\)?[\s.-]?\d{3}[\s.-]?\d{4}$/,
|
||||||
|
message: t('eula.messages.phone_number'),
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input placeholder={t('eula.labels.phone_number')}
|
||||||
|
aria-label={t('eula.labels.phone_number')} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
label={t('eula.labels.address')}
|
||||||
|
name="address"
|
||||||
|
>
|
||||||
|
<Input placeholder={t('eula.labels.address')} aria-label={t('eula.labels.address')} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<Form.Item
|
||||||
|
label={t('eula.labels.date_accepted')}
|
||||||
|
name="date_accepted"
|
||||||
|
rules={[
|
||||||
|
{ required: true },
|
||||||
|
{
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (day(value).isSame(day(), 'day')) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(t('eula.messages.date_accepted')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<FormDatePicker onChange={handleChange} onlyToday
|
||||||
|
aria-label={t('eula.labels.date_accepted')} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={24}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Form.Item
|
||||||
|
name="accepted_terms"
|
||||||
|
valuePropName="checked"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
validator: (_, value) =>
|
||||||
|
value ? Promise.resolve() : Promise.reject(new Error(t('eula.messages.accepted_terms'))),
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Checkbox
|
||||||
|
aria-label={t('eula.labels.accepted_terms')}>{t('eula.labels.accepted_terms')}</Checkbox>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Form>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
|
||||||
|
const mapStateToProps = createStructuredSelector({
|
||||||
|
currentEula: selectCurrentEula,
|
||||||
|
currentUser: selectCurrentUser,
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
|
acceptEula: () => dispatch(acceptEula()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(Eula);
|
||||||
21
client/src/components/eula/eula.styles.scss
Normal file
21
client/src/components/eula/eula.styles.scss
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
.eula-modal {
|
||||||
|
top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eula-markdown-card {
|
||||||
|
max-height: 50vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
background-color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eula-markdown-div {
|
||||||
|
padding: 0 10px 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eula-never-scrolled {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eula-accept-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
@@ -16,7 +16,16 @@ export default connect(mapStateToProps, mapDispatchToProps)(FormDatePicker);
|
|||||||
|
|
||||||
const dateFormat = "MM/DD/YYYY";
|
const dateFormat = "MM/DD/YYYY";
|
||||||
|
|
||||||
export function FormDatePicker({bodyshop, value, onChange, onBlur, onlyFuture, isDateOnly = true, ...restProps }) {
|
export function FormDatePicker({
|
||||||
|
bodyshop,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
onBlur,
|
||||||
|
onlyFuture,
|
||||||
|
onlyToday,
|
||||||
|
isDateOnly = true,
|
||||||
|
...restProps
|
||||||
|
}) {
|
||||||
const ref = useRef();
|
const ref = useRef();
|
||||||
|
|
||||||
const handleChange = (newDate) => {
|
const handleChange = (newDate) => {
|
||||||
@@ -87,9 +96,13 @@ export function FormDatePicker({bodyshop, value, onChange, onBlur, onlyFuture, i
|
|||||||
onBlur={onBlur || handleBlur}
|
onBlur={onBlur || handleBlur}
|
||||||
showToday={false}
|
showToday={false}
|
||||||
disabledTime
|
disabledTime
|
||||||
{...(onlyFuture && {
|
disabledDate={(d) => {
|
||||||
disabledDate: (d) => dayjs().subtract(1, "day").isAfter(d),
|
if (onlyToday) {
|
||||||
})}
|
return !dayjs().isSame(d, 'day');
|
||||||
|
} else if (onlyFuture) {
|
||||||
|
return dayjs().subtract(1, "day").isAfter(d);
|
||||||
|
}
|
||||||
|
}}
|
||||||
{...restProps}
|
{...restProps}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,21 @@ export const INTROSPECTION = gql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const QUERY_EULA = gql`
|
||||||
|
query QUERY_EULA($now: timestamptz!) {
|
||||||
|
eulas(where: {effective_date: {_lte: $now}, _or: [{end_date: {_is_null: true}}, {end_date: {_gt: $now}}]}) {
|
||||||
|
id
|
||||||
|
content
|
||||||
|
|
||||||
|
eula_acceptances {
|
||||||
|
id
|
||||||
|
date_accepted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const QUERY_BODYSHOP = gql`
|
export const QUERY_BODYSHOP = gql`
|
||||||
query QUERY_BODYSHOP {
|
query QUERY_BODYSHOP {
|
||||||
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
bodyshops(where: { associations: { active: { _eq: true } } }) {
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ export const UPDATE_ASSOCIATION = gql`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const INSERT_EULA_ACCEPTANCE = gql`
|
||||||
|
mutation INSERT_EULA_ACCEPTANCE($eulaAcceptance:eula_acceptances_insert_input!) {
|
||||||
|
insert_eula_acceptances_one(object: $eulaAcceptance){
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
export const UPSERT_USER = gql`
|
export const UPSERT_USER = gql`
|
||||||
mutation UPSERT_USER($authEmail: String!, $authToken: String!) {
|
mutation UPSERT_USER($authEmail: String!, $authToken: String!) {
|
||||||
insert_users(
|
insert_users(
|
||||||
|
|||||||
@@ -109,3 +109,13 @@ export const setAuthlevel = (authlevel) => ({
|
|||||||
type: UserActionTypes.SET_AUTH_LEVEL,
|
type: UserActionTypes.SET_AUTH_LEVEL,
|
||||||
payload: authlevel,
|
payload: authlevel,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const setCurrentEula = (eula) => ({
|
||||||
|
type: UserActionTypes.SET_CURRENT_EULA,
|
||||||
|
payload: eula,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const acceptEula = () => ({
|
||||||
|
type: UserActionTypes.EULA_ACCEPTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import UserActionTypes from "./user.types";
|
|||||||
const INITIAL_STATE = {
|
const INITIAL_STATE = {
|
||||||
currentUser: {
|
currentUser: {
|
||||||
authorized: null,
|
authorized: null,
|
||||||
|
eulaIsAccepted: false,
|
||||||
//language: "en-US"
|
//language: "en-US"
|
||||||
},
|
},
|
||||||
bodyshop: null,
|
bodyshop: null,
|
||||||
@@ -17,6 +18,7 @@ const INITIAL_STATE = {
|
|||||||
loading: false,
|
loading: false,
|
||||||
},
|
},
|
||||||
authLevel: 0,
|
authLevel: 0,
|
||||||
|
currentEula: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
const userReducer = (state = INITIAL_STATE, action) => {
|
const userReducer = (state = INITIAL_STATE, action) => {
|
||||||
@@ -63,11 +65,19 @@ const userReducer = (state = INITIAL_STATE, action) => {
|
|||||||
loading: false,
|
loading: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
case UserActionTypes.EULA_ACCEPTED:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentUser:{...state.currentUser, eulaIsAccepted: true},
|
||||||
|
currentEula: null,
|
||||||
|
};
|
||||||
case UserActionTypes.SIGN_IN_SUCCESS:
|
case UserActionTypes.SIGN_IN_SUCCESS:
|
||||||
|
const{ currentEula,...currentUser} = action.payload
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
loginLoading: false,
|
loginLoading: false,
|
||||||
currentUser: action.payload,
|
currentUser: currentUser,
|
||||||
|
currentEula,
|
||||||
error: null,
|
error: null,
|
||||||
};
|
};
|
||||||
case UserActionTypes.SIGN_OUT_SUCCESS:
|
case UserActionTypes.SIGN_OUT_SUCCESS:
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ import {
|
|||||||
validatePasswordResetSuccess,
|
validatePasswordResetSuccess,
|
||||||
} from "./user.actions";
|
} from "./user.actions";
|
||||||
import UserActionTypes from "./user.types";
|
import UserActionTypes from "./user.types";
|
||||||
|
import client from "../../utils/GraphQLClient";
|
||||||
|
import {QUERY_EULA} from "../../graphql/bodyshop.queries";
|
||||||
|
import day from "../../utils/day";
|
||||||
|
|
||||||
const fpPromise = FingerprintJS.load();
|
const fpPromise = FingerprintJS.load();
|
||||||
|
|
||||||
@@ -73,6 +76,8 @@ export function* signInWithEmail({ payload: { email, password } }) {
|
|||||||
export function* onCheckUserSession() {
|
export function* onCheckUserSession() {
|
||||||
yield takeLatest(UserActionTypes.CHECK_USER_SESSION, isUserAuthenticated);
|
yield takeLatest(UserActionTypes.CHECK_USER_SESSION, isUserAuthenticated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function* isUserAuthenticated() {
|
export function* isUserAuthenticated() {
|
||||||
try {
|
try {
|
||||||
logImEXEvent("redux_auth_check");
|
logImEXEvent("redux_auth_check");
|
||||||
@@ -85,6 +90,15 @@ export function* isUserAuthenticated() {
|
|||||||
|
|
||||||
LogRocket.identify(user.email);
|
LogRocket.identify(user.email);
|
||||||
|
|
||||||
|
const eulaQuery = yield client.query({
|
||||||
|
query: QUERY_EULA,
|
||||||
|
variables: {
|
||||||
|
now: day()
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const eulaIsAccepted = eulaQuery.data.eulas.length > 0 && eulaQuery.data.eulas[0].eula_acceptances.length > 0;
|
||||||
|
|
||||||
yield put(
|
yield put(
|
||||||
signInSuccess({
|
signInSuccess({
|
||||||
uid: user.uid,
|
uid: user.uid,
|
||||||
@@ -92,6 +106,8 @@ export function* isUserAuthenticated() {
|
|||||||
displayName: user.displayName,
|
displayName: user.displayName,
|
||||||
photoURL: user.photoURL,
|
photoURL: user.photoURL,
|
||||||
authorized: true,
|
authorized: true,
|
||||||
|
eulaIsAccepted,
|
||||||
|
currentEula: eulaIsAccepted ? null : eulaQuery.data.eulas[0],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -36,3 +36,8 @@ export const selectLoginLoading = createSelector(
|
|||||||
[selectUser],
|
[selectUser],
|
||||||
(user) => user.loginLoading
|
(user) => user.loginLoading
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const selectCurrentEula = createSelector(
|
||||||
|
[selectUser],
|
||||||
|
(user) => user.currentEula
|
||||||
|
);
|
||||||
|
|||||||
@@ -32,5 +32,7 @@ const UserActionTypes = {
|
|||||||
CHECK_ACTION_CODE_START: "CHECK_ACTION_CODE_START",
|
CHECK_ACTION_CODE_START: "CHECK_ACTION_CODE_START",
|
||||||
CHECK_ACTION_CODE_SUCCESS: "CHECK_ACTION_CODE_SUCCESS",
|
CHECK_ACTION_CODE_SUCCESS: "CHECK_ACTION_CODE_SUCCESS",
|
||||||
CHECK_ACTION_CODE_FAILURE: "CHECK_ACTION_CODE_FAILURE",
|
CHECK_ACTION_CODE_FAILURE: "CHECK_ACTION_CODE_FAILURE",
|
||||||
|
SET_CURRENT_EULA: "SET_CURRENT_EULA",
|
||||||
|
EULA_ACCEPTED : "EULA_ACCEPTED",
|
||||||
};
|
};
|
||||||
export default UserActionTypes;
|
export default UserActionTypes;
|
||||||
|
|||||||
@@ -933,6 +933,41 @@
|
|||||||
"updated": "Document updated successfully. "
|
"updated": "Document updated successfully. "
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eula": {
|
||||||
|
"titles": {
|
||||||
|
"modal": "Terms and Conditions",
|
||||||
|
"upper_card": "Acknowledgement"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"first_name": "Please enter your first name.",
|
||||||
|
"last_name": "Please enter your last name.",
|
||||||
|
"business_name": "Please enter your legal business name.",
|
||||||
|
"phone_number": "Please enter your phone number.",
|
||||||
|
"date_accepted": "Please enter Today's Date.",
|
||||||
|
"accepted_terms": "Please accept the terms and conditions of this agreement."
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"accept": "Accept EULA"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"first_name": "First Name",
|
||||||
|
"last_name": "Last Name",
|
||||||
|
"business_name": "Legal Business Name",
|
||||||
|
"phone_number": "Phone Number",
|
||||||
|
"address": "Address",
|
||||||
|
"date_accepted": "Date Accepted",
|
||||||
|
"accepted_terms": "I accept the terms and conditions of this agreement."
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"never_scrolled": "You must scroll to the bottom of the Terms and Conditions before accepting."
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"acceptance": {
|
||||||
|
"message": "Eula Acceptance Error",
|
||||||
|
"description": "Something went wrong while accepting the EULA. Please try again."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"emails": {
|
"emails": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"notsent": "Email not sent. Error encountered while sending {{message}}"
|
"notsent": "Email not sent. Error encountered while sending {{message}}"
|
||||||
|
|||||||
@@ -933,6 +933,41 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eula": {
|
||||||
|
"titles": {
|
||||||
|
"modal": "Terms and Conditions",
|
||||||
|
"upper_card": "Acknowledgement"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"first_name": "Please enter your first name.",
|
||||||
|
"last_name": "Please enter your last name.",
|
||||||
|
"business_name": "Please enter your legal business name.",
|
||||||
|
"phone_number": "Please enter your phone number.",
|
||||||
|
"date_accepted": "Please enter Today's Date.",
|
||||||
|
"accepted_terms": "Please accept the terms and conditions of this agreement."
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"accept": "Accept EULA"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"first_name": "First Name",
|
||||||
|
"last_name": "Last Name",
|
||||||
|
"business_name": "Legal Business Name",
|
||||||
|
"phone_number": "Phone Number",
|
||||||
|
"address": "Address",
|
||||||
|
"date_accepted": "Date Accepted",
|
||||||
|
"accepted_terms": "I accept the terms and conditions of this agreement."
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"never_scrolled": "You must scroll to the bottom of the Terms and Conditions before accepting."
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"acceptance": {
|
||||||
|
"message": "Eula Acceptance Error",
|
||||||
|
"description": "Something went wrong while accepting the EULA. Please try again."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"emails": {
|
"emails": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"notsent": "Correo electrónico no enviado Se encontró un error al enviar {{message}}"
|
"notsent": "Correo electrónico no enviado Se encontró un error al enviar {{message}}"
|
||||||
|
|||||||
@@ -933,6 +933,41 @@
|
|||||||
"updated": ""
|
"updated": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"eula": {
|
||||||
|
"titles": {
|
||||||
|
"modal": "Terms and Conditions",
|
||||||
|
"upper_card": "Acknowledgement"
|
||||||
|
},
|
||||||
|
"messages": {
|
||||||
|
"first_name": "Please enter your first name.",
|
||||||
|
"last_name": "Please enter your last name.",
|
||||||
|
"business_name": "Please enter your legal business name.",
|
||||||
|
"phone_number": "Please enter your phone number.",
|
||||||
|
"date_accepted": "Please enter Today's Date.",
|
||||||
|
"accepted_terms": "Please accept the terms and conditions of this agreement."
|
||||||
|
},
|
||||||
|
"buttons": {
|
||||||
|
"accept": "Accept EULA"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"first_name": "First Name",
|
||||||
|
"last_name": "Last Name",
|
||||||
|
"business_name": "Legal Business Name",
|
||||||
|
"phone_number": "Phone Number",
|
||||||
|
"address": "Address",
|
||||||
|
"date_accepted": "Date Accepted",
|
||||||
|
"accepted_terms": "I accept the terms and conditions of this agreement."
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"never_scrolled": "You must scroll to the bottom of the Terms and Conditions before accepting."
|
||||||
|
},
|
||||||
|
"errors": {
|
||||||
|
"acceptance": {
|
||||||
|
"message": "Eula Acceptance Error",
|
||||||
|
"description": "Something went wrong while accepting the EULA. Please try again."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"emails": {
|
"emails": {
|
||||||
"errors": {
|
"errors": {
|
||||||
"notsent": "Courriel non envoyé. Erreur rencontrée lors de l'envoi de {{message}}"
|
"notsent": "Courriel non envoyé. Erreur rencontrée lors de l'envoi de {{message}}"
|
||||||
|
|||||||
@@ -32,11 +32,6 @@ import objectSupport from 'dayjs/plugin/objectSupport';
|
|||||||
import toArray from 'dayjs/plugin/toArray';
|
import toArray from 'dayjs/plugin/toArray';
|
||||||
import toObject from 'dayjs/plugin/toObject';
|
import toObject from 'dayjs/plugin/toObject';
|
||||||
|
|
||||||
// import badMutable from 'dayjs/plugin/badMutable';
|
|
||||||
// import preParsePostFormat from 'dayjs/plugin/preParsePostFormat';
|
|
||||||
|
|
||||||
|
|
||||||
// dayjs.extend(badMutable); // TODO: Client Update - This is not advised, scoreboard page
|
|
||||||
dayjs.extend(toObject);
|
dayjs.extend(toObject);
|
||||||
dayjs.extend(toArray);
|
dayjs.extend(toArray);
|
||||||
dayjs.extend(objectSupport);
|
dayjs.extend(objectSupport);
|
||||||
@@ -46,7 +41,6 @@ dayjs.extend(isToday);
|
|||||||
dayjs.extend(localeData);
|
dayjs.extend(localeData);
|
||||||
dayjs.extend(quarterOfYear);
|
dayjs.extend(quarterOfYear);
|
||||||
dayjs.extend(localizedFormat);
|
dayjs.extend(localizedFormat);
|
||||||
// dayjs.extend(preParsePostFormat); // TODO: This should not be needed
|
|
||||||
dayjs.extend(isLeapYear);
|
dayjs.extend(isLeapYear);
|
||||||
dayjs.extend(isoWeeksInYear);
|
dayjs.extend(isoWeeksInYear);
|
||||||
dayjs.extend(isoWeek);
|
dayjs.extend(isoWeek);
|
||||||
|
|||||||
16
client/src/utils/eulaize.js
Normal file
16
client/src/utils/eulaize.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
|
||||||
|
const filename = process.argv[2];
|
||||||
|
|
||||||
|
fs.readFile(filename, 'utf8', (err, data) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(`Error reading file ${filename}:`, err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const filteredData = JSON.stringify(data);
|
||||||
|
console.log('Select the content between the quotes below and paste it into the EULA Content field in the EULA Content table in the database.')
|
||||||
|
console.log('--------------------------------------------------')
|
||||||
|
console.log(filteredData);
|
||||||
|
console.log('--------------------------------------------------')
|
||||||
|
});
|
||||||
@@ -2442,7 +2442,7 @@
|
|||||||
_eq: X-Hasura-User-Id
|
_eq: X-Hasura-User-Id
|
||||||
columns:
|
columns:
|
||||||
- address
|
- address
|
||||||
- buisness_name
|
- business_name
|
||||||
- date_accepted
|
- date_accepted
|
||||||
- eulaid
|
- eulaid
|
||||||
- first_name
|
- first_name
|
||||||
@@ -2454,7 +2454,7 @@
|
|||||||
permission:
|
permission:
|
||||||
columns:
|
columns:
|
||||||
- address
|
- address
|
||||||
- buisness_name
|
- business_name
|
||||||
- first_name
|
- first_name
|
||||||
- last_name
|
- last_name
|
||||||
- phone_number
|
- phone_number
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."eula_acceptances" rename column "business_name" to "buisness_name";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."eula_acceptances" rename column "buisness_name" to "business_name";
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."eula_acceptances" alter column "phone_number" set not null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."eula_acceptances" alter column "phone_number" drop not null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."eula_acceptances" alter column "address" set not null;
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
alter table "public"."eula_acceptances" alter column "address" drop not null;
|
||||||
Reference in New Issue
Block a user