Added in-session recent items with cross tab support. BOD-178
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
|
||||
&__grow {
|
||||
flex: 1;
|
||||
|
||||
@@ -19,18 +19,24 @@ export function BreadCrumbs({ breadcrumbs, recentItems }) {
|
||||
const menu = (
|
||||
<Menu>
|
||||
{recentItems.map((i, idx) => (
|
||||
<Menu.Item key={idx}>{i.label}</Menu.Item>
|
||||
<Menu.Item key={idx}>
|
||||
<Link to={i.url}>{i.label}</Link>
|
||||
</Menu.Item>
|
||||
))}
|
||||
</Menu>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className='breadcrumb-container'>
|
||||
<Dropdown overlay={menu} trigger={["click"]}>
|
||||
<div className="breadcrumb-container imex-flex-row">
|
||||
<Dropdown
|
||||
className="imex-flex-row__margin-large"
|
||||
overlay={menu}
|
||||
trigger={["click"]}
|
||||
>
|
||||
<ClockCircleFilled onClick={(e) => e.preventDefault()} />
|
||||
</Dropdown>
|
||||
|
||||
<Breadcrumb separator='>'>
|
||||
<Breadcrumb separator=">">
|
||||
<Breadcrumb.Item>
|
||||
<Link to={`/manage`}>
|
||||
<HomeFilled />
|
||||
|
||||
@@ -33,11 +33,6 @@ export function TechClockInContainer({ technician, bodyshop }) {
|
||||
ciecacode: Object.keys(
|
||||
bodyshop.md_responsibility_centers.defaults.costs
|
||||
).find((key) => {
|
||||
console.log(
|
||||
"key",
|
||||
key,
|
||||
bodyshop.md_responsibility_centers.defaults.costs[key]
|
||||
);
|
||||
return (
|
||||
bodyshop.md_responsibility_centers.defaults.costs[key] ===
|
||||
values.cost_center
|
||||
|
||||
@@ -11,14 +11,19 @@ import {
|
||||
QUERY_CONTRACT_BY_PK,
|
||||
UPDATE_CONTRACT,
|
||||
} from "../../graphql/cccontracts.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
} from "../../redux/application/application.actions";
|
||||
import ContractDetailPageComponent from "./contract-detail.page.component";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
});
|
||||
|
||||
export function ContractDetailPageContainer({ setBreadcrumbs }) {
|
||||
export function ContractDetailPageContainer({ setBreadcrumbs, addRecentItem }) {
|
||||
const { t } = useTranslation();
|
||||
const [updateContract] = useMutation(UPDATE_CONTRACT);
|
||||
const [form] = Form.useForm();
|
||||
@@ -50,7 +55,17 @@ export function ContractDetailPageContainer({ setBreadcrumbs }) {
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}, [t, data, error, loading, setBreadcrumbs]);
|
||||
|
||||
if (data)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
contractId,
|
||||
"contract",
|
||||
data.cccontracts_by_pk.agreementnumber,
|
||||
`/manage/courtesycars/contracts/${contractId}`
|
||||
)
|
||||
);
|
||||
}, [t, data, error, loading, setBreadcrumbs, addRecentItem]);
|
||||
|
||||
const handleFinish = (values) => {
|
||||
updateContract({
|
||||
|
||||
@@ -7,13 +7,21 @@ import { connect } from "react-redux";
|
||||
import { useParams } from "react-router-dom";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_CC_BY_PK, UPDATE_CC } from "../../graphql/courtesy-car.queries";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import {
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import CourtesyCarDetailPageComponent from "./courtesy-car-detail.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
});
|
||||
export function CourtesyCarDetailPageContainer({ setBreadcrumbs }) {
|
||||
export function CourtesyCarDetailPageContainer({
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const [insertCourtesyCar] = useMutation(UPDATE_CC);
|
||||
const [form] = Form.useForm();
|
||||
@@ -42,7 +50,17 @@ export function CourtesyCarDetailPageContainer({ setBreadcrumbs }) {
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}, [t, data, error, loading, setBreadcrumbs]);
|
||||
|
||||
if (data)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
ccId,
|
||||
"courtesycar",
|
||||
data.courtesycars_by_pk.fleet_number || data.courtesycars_by_pk.vin,
|
||||
`/manage/courtesycars/${ccId}`
|
||||
)
|
||||
);
|
||||
}, [t, data, error, loading, setBreadcrumbs, addRecentItem]);
|
||||
|
||||
const handleFinish = (values) => {
|
||||
insertCourtesyCar({
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useMutation, useQuery } from "@apollo/react-hooks";
|
||||
import { notification } from "antd";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import SpinComponent from "../../components/loading-spinner/loading-spinner.component";
|
||||
import {
|
||||
@@ -10,12 +11,12 @@ import {
|
||||
UPDATE_JOB,
|
||||
UPDATE_JOB_STATUS,
|
||||
} from "../../graphql/jobs.queries";
|
||||
import JobsDetailPage from "./jobs-detail.page.component";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import JobsDetailPage from "./jobs-detail.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
@@ -71,13 +72,23 @@ function JobsDetailPageContainer({ match, setBreadcrumbs, addRecentItem }) {
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}, [loading, data, t, error, setBreadcrumbs, jobId]);
|
||||
|
||||
if (data)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
jobId,
|
||||
"job",
|
||||
`${data.jobs_by_pk.est_number || ""} | ${
|
||||
data.jobs_by_pk.ro_number || ""
|
||||
}`,
|
||||
`/manage/jobs/${jobId}`
|
||||
)
|
||||
);
|
||||
}, [loading, data, t, error, setBreadcrumbs, jobId, addRecentItem]);
|
||||
|
||||
if (loading) return <SpinComponent />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
if (data) addRecentItem({ id: 1234, label: " abcd" });
|
||||
|
||||
return data.jobs_by_pk ? (
|
||||
<JobsDetailPage
|
||||
job={data.jobs_by_pk}
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import React, { useEffect } from "react";
|
||||
import OwnersDetailComponent from "./owners-detail.page.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { QUERY_OWNER_BY_ID } from "../../graphql/owners.queries";
|
||||
import React, { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import { QUERY_OWNER_BY_ID } from "../../graphql/owners.queries";
|
||||
import {
|
||||
addRecentItem,
|
||||
setBreadcrumbs,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
import OwnersDetailComponent from "./owners-detail.page.component";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
});
|
||||
|
||||
export function OwnersDetailContainer({ match, setBreadcrumbs }) {
|
||||
export function OwnersDetailContainer({
|
||||
match,
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
}) {
|
||||
const { ownerId } = match.params;
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -42,7 +52,19 @@ export function OwnersDetailContainer({ match, setBreadcrumbs }) {
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}, [setBreadcrumbs, t, data, ownerId]);
|
||||
|
||||
if (data)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
ownerId,
|
||||
"owner",
|
||||
`${data.owners_by_pk.ownr_fn || ""} ${
|
||||
data.owners_by_pk.ownr_ln || ""
|
||||
} ${data.owners_by_pk.ownr_co_nm || ""}`,
|
||||
`/manage/owners/${ownerId}`
|
||||
)
|
||||
);
|
||||
}, [setBreadcrumbs, t, data, ownerId, addRecentItem]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
@@ -5,17 +5,26 @@ import { QUERY_VEHICLE_BY_ID } from "../../graphql/vehicles.queries";
|
||||
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
|
||||
import AlertComponent from "../../components/alert/alert.component";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { setBreadcrumbs } from "../../redux/application/application.actions";
|
||||
import {
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
} from "../../redux/application/application.actions";
|
||||
import { connect } from "react-redux";
|
||||
import { CreateRecentItem } from "../../utils/create-recent-item";
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
|
||||
addRecentItem: (item) => dispatch(addRecentItem(item)),
|
||||
});
|
||||
|
||||
export function VehicleDetailContainer({ match, setBreadcrumbs }) {
|
||||
export function VehicleDetailContainer({
|
||||
match,
|
||||
setBreadcrumbs,
|
||||
addRecentItem,
|
||||
}) {
|
||||
const { vehId } = match.params;
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
||||
const { loading, data, error, refetch } = useQuery(QUERY_VEHICLE_BY_ID, {
|
||||
variables: { id: vehId },
|
||||
fetchPolicy: "network-only",
|
||||
@@ -40,7 +49,17 @@ export function VehicleDetailContainer({ match, setBreadcrumbs }) {
|
||||
}),
|
||||
},
|
||||
]);
|
||||
}, [t, data, setBreadcrumbs, vehId]);
|
||||
|
||||
if (data)
|
||||
addRecentItem(
|
||||
CreateRecentItem(
|
||||
vehId,
|
||||
"vehicle",
|
||||
`${data.vehicles_by_pk.v_vin} | ${data.vehicles_by_pk.v_model_yr} ${data.vehicles_by_pk.v_make_desc} ${data.vehicles_by_pk.v_model_desc}`,
|
||||
`/manage/vehicles/${vehId}`
|
||||
)
|
||||
);
|
||||
}, [t, data, setBreadcrumbs, vehId, addRecentItem]);
|
||||
|
||||
if (loading) return <LoadingSpinner />;
|
||||
if (error) return <AlertComponent message={error.message} type="error" />;
|
||||
|
||||
@@ -16,7 +16,7 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
case ApplicationActionTypes.ADD_RECENT_ITEM:
|
||||
return {
|
||||
...state,
|
||||
recentItems: [action.payload, ...state.recentItems.slice(0, 9)],
|
||||
recentItems: updateRecentItemsArray(state, action.payload),
|
||||
};
|
||||
case ApplicationActionTypes.SET_BREAD_CRUMBS:
|
||||
return {
|
||||
@@ -63,3 +63,18 @@ const applicationReducer = (state = INITIAL_STATE, action) => {
|
||||
};
|
||||
|
||||
export default applicationReducer;
|
||||
|
||||
const updateRecentItemsArray = (state, newItem) => {
|
||||
//Check to see if the new item is in the list.
|
||||
const matchingIndex = state.recentItems.findIndex((i) => i.id === newItem.id);
|
||||
|
||||
if (matchingIndex >= 0) {
|
||||
return [
|
||||
newItem,
|
||||
...state.recentItems.slice(0, matchingIndex),
|
||||
...state.recentItems.slice(matchingIndex + 1, 9),
|
||||
];
|
||||
} else {
|
||||
return [newItem, ...state.recentItems.slice(0, 9)];
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { combineReducers } from "redux";
|
||||
import { persistReducer } from "redux-persist";
|
||||
import storage from "redux-persist/lib/storage";
|
||||
import { withReduxStateSync } from "redux-state-sync";
|
||||
|
||||
import userReducer from "./user/user.reducer";
|
||||
import messagingReducer from "./messaging/messaging.reducer";
|
||||
@@ -8,10 +9,11 @@ import emailReducer from "./email/email.reducer";
|
||||
import modalsReducer from "./modals/modals.reducer";
|
||||
import applicationReducer from "./application/application.reducer";
|
||||
import techReducer from "./tech/tech.reducer";
|
||||
|
||||
const persistConfig = {
|
||||
key: "root",
|
||||
storage,
|
||||
whitelist: ["messaging", "tech"],
|
||||
whitelist: ["messaging", "tech", "application"],
|
||||
blacklist: ["user", "email", "modals"],
|
||||
};
|
||||
|
||||
@@ -24,4 +26,4 @@ const rootReducer = combineReducers({
|
||||
tech: techReducer,
|
||||
});
|
||||
|
||||
export default persistReducer(persistConfig, rootReducer);
|
||||
export default withReduxStateSync(persistReducer(persistConfig, rootReducer));
|
||||
|
||||
@@ -2,11 +2,24 @@ import { createStore, applyMiddleware, compose } from "redux";
|
||||
import { persistStore } from "redux-persist";
|
||||
import { createLogger } from "redux-logger";
|
||||
import createSagaMiddleware from "redux-saga";
|
||||
import {
|
||||
createStateSyncMiddleware,
|
||||
initMessageListener,
|
||||
} from "redux-state-sync";
|
||||
|
||||
import rootReducer from "./root.reducer";
|
||||
import rootSaga from "./root.saga";
|
||||
|
||||
const sagaMiddleWare = createSagaMiddleware();
|
||||
const middlewares = [sagaMiddleWare];
|
||||
|
||||
const reduxSyncConfig = {
|
||||
whitelist: ["ADD_RECENT_ITEM"],
|
||||
};
|
||||
|
||||
const middlewares = [
|
||||
sagaMiddleWare,
|
||||
createStateSyncMiddleware(reduxSyncConfig),
|
||||
];
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
middlewares.push(createLogger({ collapsed: true, diff: true }));
|
||||
}
|
||||
@@ -25,6 +38,7 @@ const enhancer = composeEnhancers(
|
||||
|
||||
export const store = createStore(rootReducer, enhancer);
|
||||
sagaMiddleWare.run(rootSaga);
|
||||
initMessageListener(store);
|
||||
|
||||
export const persistor = persistStore(store);
|
||||
|
||||
|
||||
@@ -368,6 +368,13 @@
|
||||
"submit": "Submit",
|
||||
"submitticket": "Submit a Support Ticket"
|
||||
},
|
||||
"itemtypes": {
|
||||
"contract": "CC Contract",
|
||||
"courtesycar": "Courtesy Car",
|
||||
"job": "Job",
|
||||
"owner": "Owner",
|
||||
"vehicle": "Vehicle"
|
||||
},
|
||||
"labels": {
|
||||
"actions": "Actions",
|
||||
"areyousure": "Are you sure?",
|
||||
|
||||
@@ -368,6 +368,13 @@
|
||||
"submit": "",
|
||||
"submitticket": ""
|
||||
},
|
||||
"itemtypes": {
|
||||
"contract": "",
|
||||
"courtesycar": "",
|
||||
"job": "",
|
||||
"owner": "",
|
||||
"vehicle": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "Comportamiento",
|
||||
"areyousure": "",
|
||||
|
||||
@@ -368,6 +368,13 @@
|
||||
"submit": "",
|
||||
"submitticket": ""
|
||||
},
|
||||
"itemtypes": {
|
||||
"contract": "",
|
||||
"courtesycar": "",
|
||||
"job": "",
|
||||
"owner": "",
|
||||
"vehicle": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "actes",
|
||||
"areyousure": "",
|
||||
|
||||
9
client/src/utils/create-recent-item.js
Normal file
9
client/src/utils/create-recent-item.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import i18n from "i18next";
|
||||
|
||||
export function CreateRecentItem(id, type, labelid, url) {
|
||||
return {
|
||||
id,
|
||||
label: `${i18n.t(`general.itemtypes.${type}`)}: ${labelid}`,
|
||||
url,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user