BOD-5 BOD-36 #comment Added Audit Trail List to jobs and created Audit List view component + queries
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
<babeledit_project be_version="2.6.1" version="1.2">
|
||||
<babeledit_project version="1.2" be_version="2.6.1">
|
||||
<!--
|
||||
|
||||
BabelEdit project file
|
||||
@@ -639,6 +639,100 @@
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>audit</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>fields</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>created</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>operation</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>useremail</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>values</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>bodyshop</name>
|
||||
<children>
|
||||
@@ -2224,6 +2318,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>vendorname</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
@@ -5061,6 +5176,27 @@
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>audit</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
<description></description>
|
||||
<comment></comment>
|
||||
<default_text></default_text>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-MX</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-CA</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>available_new_jobs</name>
|
||||
<definition_loaded>false</definition_loaded>
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
import React, { useState } from "react";
|
||||
import { Table } from "antd";
|
||||
import { alphaSort } from "../../utils/sorters";
|
||||
import { DateTimeFormatter } from "../../utils/DateFormatter";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import AuditTrailValuesComponent from "../audit-trail-values/audit-trail-values.component";
|
||||
|
||||
export default function AuditTrailListComponent({ loading, data }) {
|
||||
const [state, setState] = useState({
|
||||
sortedInfo: {},
|
||||
filteredInfo: {}
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const columns = [
|
||||
{
|
||||
title: t("audit.fields.created"),
|
||||
dataIndex: " created",
|
||||
key: " created",
|
||||
|
||||
render: (text, record) => (
|
||||
<DateTimeFormatter>{record.created}</DateTimeFormatter>
|
||||
),
|
||||
sorter: (a, b) => a.created - b.created,
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "created" && state.sortedInfo.order
|
||||
},
|
||||
{
|
||||
title: t("audit.fields.operation"),
|
||||
dataIndex: "operation",
|
||||
key: "operation",
|
||||
sorter: (a, b) => alphaSort(a.operation, b.operation),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "operation" && state.sortedInfo.order
|
||||
},
|
||||
{
|
||||
title: t("audit.fields.values"),
|
||||
dataIndex: " old_val",
|
||||
key: " old_val",
|
||||
render: (text, record) => (
|
||||
<AuditTrailValuesComponent
|
||||
oldV={record.old_val}
|
||||
newV={record.new_val}
|
||||
/>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: t("audit.fields.useremail"),
|
||||
dataIndex: "useremail",
|
||||
key: "useremail",
|
||||
sorter: (a, b) => alphaSort(a.useremail, b.useremail),
|
||||
sortOrder:
|
||||
state.sortedInfo.columnKey === "useremail" && state.sortedInfo.order
|
||||
}
|
||||
];
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {
|
||||
xs: { span: 12 },
|
||||
sm: { span: 5 }
|
||||
},
|
||||
wrapperCol: {
|
||||
xs: { span: 24 },
|
||||
sm: { span: 12 }
|
||||
}
|
||||
};
|
||||
const handleTableChange = (pagination, filters, sorter) => {
|
||||
setState({ ...state, filteredInfo: filters, sortedInfo: sorter });
|
||||
};
|
||||
|
||||
return (
|
||||
<Table
|
||||
{...formItemLayout}
|
||||
loading={loading}
|
||||
size="small"
|
||||
pagination={{ position: "top", defaultPageSize: 25 }}
|
||||
columns={columns.map(item => ({ ...item }))}
|
||||
rowKey="id"
|
||||
dataSource={data}
|
||||
onChange={handleTableChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import React from "react";
|
||||
import AuditTrailListComponent from "./audit-trail-list.component";
|
||||
import { useQuery } from "react-apollo";
|
||||
import { QUERY_AUDIT_TRAIL } from "../../graphql/audit_trail.queries";
|
||||
import AlertComponent from "../alert/alert.component";
|
||||
|
||||
export default function AuditTrailListContainer({ recordId }) {
|
||||
const { loading, error, data } = useQuery(QUERY_AUDIT_TRAIL, {
|
||||
variables: { id: recordId },
|
||||
fetchPolicy: "network-only"
|
||||
});
|
||||
return (
|
||||
<div>
|
||||
{error ? (
|
||||
<AlertComponent type="error" message={error.message} />
|
||||
) : (
|
||||
<AuditTrailListComponent
|
||||
loading={loading}
|
||||
data={data ? data.audit_trail : null}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from "react";
|
||||
import { List } from "antd";
|
||||
import Icon from "@ant-design/icons";
|
||||
import { FaArrowRight } from "react-icons/fa";
|
||||
export default function AuditTrailValuesComponent({ oldV, newV }) {
|
||||
return (
|
||||
<List bordered size="small">
|
||||
{Object.keys(oldV).map((key, idx) => (
|
||||
<List.Item key={idx} value={key}>
|
||||
{key}: {oldV[key]} <Icon component={FaArrowRight} /> {newV[key]}
|
||||
</List.Item>
|
||||
))}
|
||||
</List>
|
||||
);
|
||||
}
|
||||
18
client/src/graphql/audit_trail.queries.js
Normal file
18
client/src/graphql/audit_trail.queries.js
Normal file
@@ -0,0 +1,18 @@
|
||||
import { gql } from "apollo-boost";
|
||||
|
||||
export const QUERY_AUDIT_TRAIL = gql`
|
||||
query QUERY_AUDIT_TRAIL($id: uuid!) {
|
||||
audit_trail(where: {recordid: {_eq: $id}}) {
|
||||
useremail
|
||||
tabname
|
||||
schemaname
|
||||
recordid
|
||||
operation
|
||||
old_val
|
||||
new_val
|
||||
id
|
||||
created
|
||||
bodyshopid
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
FaHardHat,
|
||||
FaInfo,
|
||||
FaRegStickyNote,
|
||||
FaShieldAlt
|
||||
FaShieldAlt,
|
||||
FaHistory
|
||||
} from "react-icons/fa";
|
||||
//import JobsLinesContainer from "../../components/job-detail-lines/job-lines.container";
|
||||
//import JobsDetailClaims from "../../components/jobs-detail-claims/jobs-detail-claims.component";
|
||||
@@ -70,6 +71,9 @@ const EnterInvoiceModalContainer = lazy(() =>
|
||||
const JobsDetailPliContainer = lazy(() =>
|
||||
import("../../components/jobs-detail-pli/jobs-detail-pli.container")
|
||||
);
|
||||
const JobsDetailAuditContainer = lazy(() =>
|
||||
import("../../components/audit-trail-list/audit-trail-list.container")
|
||||
);
|
||||
|
||||
export default function JobsDetailPage({
|
||||
job,
|
||||
@@ -120,7 +124,7 @@ export default function JobsDetailPage({
|
||||
|
||||
<Form
|
||||
form={form}
|
||||
onFieldsChange={(a, b) => console.log("a,b", a, b)}
|
||||
//onFieldsChange={(a, b) => console.log("a,b", a, b)}
|
||||
name="JobDetailForm"
|
||||
onFinish={handleFinish}
|
||||
{...formItemLayout}
|
||||
@@ -262,6 +266,21 @@ export default function JobsDetailPage({
|
||||
>
|
||||
<JobNotesContainer jobId={job.id} />
|
||||
</Tabs.TabPane>
|
||||
|
||||
|
||||
<Tabs.TabPane
|
||||
tab={
|
||||
<span>
|
||||
<Icon component={FaHistory} />
|
||||
{t("jobs.labels.audit")}
|
||||
</span>
|
||||
}
|
||||
key="audit"
|
||||
>
|
||||
<JobsDetailAuditContainer recordId={job.id }/>
|
||||
</Tabs.TabPane>
|
||||
|
||||
|
||||
</Tabs>
|
||||
</Form>
|
||||
</Suspense>
|
||||
|
||||
@@ -56,6 +56,14 @@
|
||||
"actions": "Actions"
|
||||
}
|
||||
},
|
||||
"audit": {
|
||||
"fields": {
|
||||
"created": "Time",
|
||||
"operation": "Operation",
|
||||
"useremail": "User",
|
||||
"values": ""
|
||||
}
|
||||
},
|
||||
"bodyshop": {
|
||||
"errors": {
|
||||
"loading": "Unable to load shop details. Please call technical support."
|
||||
@@ -178,7 +186,8 @@
|
||||
"is_credit_memo": "Credit Memo?",
|
||||
"ro_number": "RO Number",
|
||||
"total": "Invoice Total",
|
||||
"vendor": "Vendor"
|
||||
"vendor": "Vendor",
|
||||
"vendorname": "Vendor Name"
|
||||
},
|
||||
"labels": {
|
||||
"actions": "Actions",
|
||||
@@ -337,6 +346,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"appointmentconfirmation": "Send confirmation to customer?",
|
||||
"audit": "Audit Trail",
|
||||
"available_new_jobs": "",
|
||||
"cards": {
|
||||
"appraiser": "Appraiser",
|
||||
|
||||
@@ -56,6 +56,14 @@
|
||||
"actions": "Comportamiento"
|
||||
}
|
||||
},
|
||||
"audit": {
|
||||
"fields": {
|
||||
"created": "",
|
||||
"operation": "",
|
||||
"useremail": "",
|
||||
"values": ""
|
||||
}
|
||||
},
|
||||
"bodyshop": {
|
||||
"errors": {
|
||||
"loading": "No se pueden cargar los detalles de la tienda. Por favor llame al soporte técnico."
|
||||
@@ -178,7 +186,8 @@
|
||||
"is_credit_memo": "",
|
||||
"ro_number": "",
|
||||
"total": "",
|
||||
"vendor": ""
|
||||
"vendor": "",
|
||||
"vendorname": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "",
|
||||
@@ -337,6 +346,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"appointmentconfirmation": "¿Enviar confirmación al cliente?",
|
||||
"audit": "",
|
||||
"available_new_jobs": "",
|
||||
"cards": {
|
||||
"appraiser": "Tasador",
|
||||
|
||||
@@ -56,6 +56,14 @@
|
||||
"actions": "actes"
|
||||
}
|
||||
},
|
||||
"audit": {
|
||||
"fields": {
|
||||
"created": "",
|
||||
"operation": "",
|
||||
"useremail": "",
|
||||
"values": ""
|
||||
}
|
||||
},
|
||||
"bodyshop": {
|
||||
"errors": {
|
||||
"loading": "Impossible de charger les détails de la boutique. Veuillez appeler le support technique."
|
||||
@@ -178,7 +186,8 @@
|
||||
"is_credit_memo": "",
|
||||
"ro_number": "",
|
||||
"total": "",
|
||||
"vendor": ""
|
||||
"vendor": "",
|
||||
"vendorname": ""
|
||||
},
|
||||
"labels": {
|
||||
"actions": "",
|
||||
@@ -337,6 +346,7 @@
|
||||
},
|
||||
"labels": {
|
||||
"appointmentconfirmation": "Envoyer une confirmation au client?",
|
||||
"audit": "",
|
||||
"available_new_jobs": "",
|
||||
"cards": {
|
||||
"appraiser": "Expert",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
var JSZip = require("jszip");
|
||||
//var JSZip = require("jszip");
|
||||
const axios = require("axios"); // to get the images
|
||||
require("dotenv").config();
|
||||
|
||||
@@ -6,19 +6,19 @@ module.exports.downloadImages = async function(req, res) {
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
console.log("[IMAGES] Incoming Images to Download", req.body);
|
||||
}
|
||||
const zip = new JSZip();
|
||||
//res.json({ success: true });
|
||||
req.body.images.forEach(i => {
|
||||
axios.get(i).then(r => zip.file(r));
|
||||
// const buffer = await response.buffer();
|
||||
// zip.file(file, buffer);
|
||||
});
|
||||
// // Set the name of the zip file in the download
|
||||
res.setHeader("Content-Type", "application/zip");
|
||||
// const zip = new JSZip();
|
||||
// //res.json({ success: true });
|
||||
// req.body.images.forEach(i => {
|
||||
// axios.get(i).then(r => zip.file(r));
|
||||
// // const buffer = await response.buffer();
|
||||
// // zip.file(file, buffer);
|
||||
// });
|
||||
// // // Set the name of the zip file in the download
|
||||
// res.setHeader("Content-Type", "application/zip");
|
||||
|
||||
zip.generateAsync({ type: "nodebuffer" }).then(
|
||||
function(content) {
|
||||
res.send(content);
|
||||
}.bind(res)
|
||||
);
|
||||
// zip.generateAsync({ type: "nodebuffer" }).then(
|
||||
// function(content) {
|
||||
// res.send(content);
|
||||
// }.bind(res)
|
||||
// );
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user