diff --git a/client/package.json b/client/package.json
index cd6b2f079..6a12b97a5 100644
--- a/client/package.json
+++ b/client/package.json
@@ -12,6 +12,7 @@
"apollo-link-ws": "^1.0.19",
"axios": "^0.19.1",
"chart.js": "^2.9.3",
+ "compress": "^0.99.0",
"dotenv": "^8.2.0",
"firebase": "^7.5.0",
"graphql": "^14.5.8",
@@ -23,6 +24,7 @@
"react-dom": "^16.12.0",
"react-i18next": "^11.2.7",
"react-icons": "^3.8.0",
+ "react-image-file-resizer": "^0.2.1",
"react-moment": "^0.9.7",
"react-number-format": "^4.3.1",
"react-router-dom": "^5.1.2",
diff --git a/client/src/components/jobs-documents/jobs-documents.container.jsx b/client/src/components/jobs-documents/jobs-documents.container.jsx
new file mode 100644
index 000000000..268000809
--- /dev/null
+++ b/client/src/components/jobs-documents/jobs-documents.container.jsx
@@ -0,0 +1,35 @@
+import React from "react";
+import { useQuery } from "react-apollo";
+import { QUERY_SHOP_ID } from "../../graphql/bodyshop.queries";
+import { GET_DOCUMENTS_BY_JOB } from "../../graphql/documents.queries";
+import AlertComponent from "../alert/alert.component";
+import LoadingSpinner from "../loading-spinner/loading-spinner.component";
+import JobDocuments from "./jobs-documents.page";
+
+export default function JobsDocumentsContainer({ jobId }) {
+ const { loading, error, data } = useQuery(GET_DOCUMENTS_BY_JOB, {
+ variables: { jobId: jobId },
+ fetchPolicy: "network-only"
+ });
+
+ const shopData = useQuery(QUERY_SHOP_ID, {
+ fetchPolicy: "network-only"
+ });
+
+ if (loading || shopData.loading) return ;
+ if (error) return ;
+ if (shopData.error)
+ return ;
+
+ return (
+
+ );
+}
diff --git a/client/src/components/jobs-documents/jobs-documents.page.jsx b/client/src/components/jobs-documents/jobs-documents.page.jsx
new file mode 100644
index 000000000..61a68269a
--- /dev/null
+++ b/client/src/components/jobs-documents/jobs-documents.page.jsx
@@ -0,0 +1,145 @@
+import React, { useState } from "react";
+import { useTranslation } from "react-i18next";
+import { Upload, Modal, Icon, Button } from "antd";
+import axios from "axios";
+import "./jobs-documents.styles.scss";
+import Resizer from "react-image-file-resizer";
+//import { Resizer } from "../../utils/resizer";
+
+function getBase64(file) {
+ return new Promise((resolve, reject) => {
+ const reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onload = () => resolve(reader.result);
+ reader.onerror = error => reject(error);
+ });
+}
+
+function JobsDetailPage({ shopId, jobId, loading, data }) {
+ //const { t } = useTranslation();
+
+ const [state, setState] = useState({
+ previewVisible: false,
+ previewImage: "",
+ fileList: [
+ {
+ uid: "-1",
+ name: "image.png",
+ status: "done",
+ url:
+ "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
+ },
+ {
+ uid: "-2",
+ name: "image.png",
+ status: "error"
+ }
+ ]
+ });
+
+ const handleUpload = ev => {
+ console.log("handleUpload Props:", ev);
+ Resizer.imageFileResizer(
+ ev.file,
+ 3000,
+ 3000,
+ "JPEG",
+ 85,
+ 0,
+ uri => {
+ let file = new File([uri], ev.file.name, {
+ //type: ev.file.type,
+ uid: ev.file.uid
+ });
+
+ // Split the filename to get the name and type
+ let fileName = file.name;
+ let fileType = file.type;
+ console.log("Preparing the upload ");
+ axios
+ .post("/sign_s3", {
+ fileName: `${shopId}/${jobId}/${fileName}`,
+ fileType
+ })
+ .then(response => {
+ var returnData = response.data.data.returnData;
+ var signedRequest = returnData.signedRequest;
+ var url = returnData.url;
+ setState({ ...state, url: url });
+ console.log("Recieved a signed request " + signedRequest);
+
+ // Put the fileType in the headers for the upload
+ var options = {
+ headers: {
+ "Content-Type": fileType
+ }
+ };
+
+ axios
+ .put(signedRequest, file, options)
+ .then(result => {
+ console.log("Response from s3", result);
+ setState({ ...state, success: true });
+ })
+ .catch(error => {
+ console.log("Inside Error here.", error);
+ alert("ERROR " + JSON.stringify(error));
+ });
+ })
+ .catch(error => {
+ console.log("Outside Error here.", error);
+ alert(JSON.stringify(error));
+ });
+ },
+ "blob"
+ );
+ };
+
+ const handleCancel = () => setState({ ...state, previewVisible: false });
+
+ const handlePreview = async file => {
+ if (!file.url && !file.preview) {
+ file.preview = await getBase64(file.originFileObj);
+ }
+
+ setState({
+ ...state,
+ previewImage: file.url || file.preview,
+ previewVisible: true
+ });
+ };
+
+ const handleChange = ({ fileList }) => setState({ ...state, fileList });
+
+ const { previewVisible, previewImage, fileList } = state;
+ const uploadButton = (
+
+ );
+
+ return (
+
+
({
+ photoCotent: file, // file is currently uploading pictures
+ photoWidth: file.height, // Get the width and height of the picture by handleBeforeUpload
+ photoHeight: file.width
+ })}
+ accept='.pdf,.jpg,.jpeg'
+ listType='picture-card'
+ fileList={fileList}
+ multiple={true}
+ onPreview={handlePreview}
+ onChange={handleChange}>
+ {uploadButton}
+
+
+
+
+
+ );
+}
+export default JobsDetailPage;
diff --git a/client/src/pages/jobs-documents/jobs-documents.styles.scss b/client/src/components/jobs-documents/jobs-documents.styles.scss
similarity index 100%
rename from client/src/pages/jobs-documents/jobs-documents.styles.scss
rename to client/src/components/jobs-documents/jobs-documents.styles.scss
diff --git a/client/src/graphql/apollo-error-handling.js b/client/src/graphql/apollo-error-handling.js
index 5ffb06e1c..7770dfc7c 100644
--- a/client/src/graphql/apollo-error-handling.js
+++ b/client/src/graphql/apollo-error-handling.js
@@ -9,15 +9,12 @@ const errorLink = onError(
console.log("networkError", networkError);
console.log("operation", operation);
console.log("forward", forward);
- if (graphQLErrors) {
+ if (graphQLErrors[0]?.message.includes("JWTExpired") || networkError?.message.includes("JWTExpired")) {
//User access token has expired
console.log("graphQLErrors", graphQLErrors);
- }
-
- if (networkError) {
console.log(`[Network error]: ${networkError}`);
//props.history.push("/network-error");
- if (networkError.message.includes("JWTExpired")) {
+ if (networkError?.message?.includes("JWTExpired")) {
console.log("Got to the error check.");
if (access_token && access_token !== "undefined") {
// Let's refresh token through async request
diff --git a/client/src/graphql/bodyshop.queries.js b/client/src/graphql/bodyshop.queries.js
index 82e660930..febac0b2f 100644
--- a/client/src/graphql/bodyshop.queries.js
+++ b/client/src/graphql/bodyshop.queries.js
@@ -22,3 +22,11 @@ export const QUERY_BODYSHOP = gql`
}
}
`;
+
+export const QUERY_SHOP_ID = gql`
+ query QUERY_SHOP_ID {
+ bodyshops(where: { associations: { active: { _eq: true } } }) {
+ id
+ }
+ }
+`;
diff --git a/client/src/graphql/documents.queries.js b/client/src/graphql/documents.queries.js
new file mode 100644
index 000000000..1217234d6
--- /dev/null
+++ b/client/src/graphql/documents.queries.js
@@ -0,0 +1,22 @@
+import { gql } from "apollo-boost";
+
+export const GET_DOCUMENTS_BY_JOB = gql`
+ query GET_DOCUMENTS_BY_JOB($jobId: uuid!) {
+ documents(where: { jobid: { _eq: $jobId } }) {
+ id
+ url
+ thumb_url
+ name
+ }
+ }
+`;
+
+export const INSERT_NEW_DOCUMENT = gql`
+ mutation INSERT_NEW_DOCUMENT($docInput: [documents_insert_input!]!) {
+ insert_ducments(objects: $docInput) {
+ returning {
+ id
+ }
+ }
+ }
+`;
diff --git a/client/src/pages/jobs-detail/jobs-detail.page.jsx b/client/src/pages/jobs-detail/jobs-detail.page.jsx
index 7890ee45f..497d4d679 100644
--- a/client/src/pages/jobs-detail/jobs-detail.page.jsx
+++ b/client/src/pages/jobs-detail/jobs-detail.page.jsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from "react";
+import React, { useEffect, useState } from "react";
import { useQuery } from "@apollo/react-hooks";
import SpinComponent from "../../components/loading-spinner/loading-spinner.component";
import AlertComponent from "../../components/alert/alert.component";
@@ -7,9 +7,11 @@ import { GET_JOB_BY_PK } from "../../graphql/jobs.queries";
import { Tabs, Icon, Row } from "antd";
import JobLinesContainer from "../../components/job-lines/job-lines.container.component";
import { useTranslation } from "react-i18next";
+import JobsDocumentsContainer from "../../components/jobs-documents/jobs-documents.container";
-function JobsDetailPage({ match }) {
+function JobsDetailPage({ match, location }) {
const { jobId } = match.params;
+ const { hash } = location;
const { t } = useTranslation();
const { loading, error, data } = useQuery(GET_JOB_BY_PK, {
variables: { id: jobId },
@@ -22,18 +24,19 @@ function JobsDetailPage({ match }) {
: t("titles.jobsdetail", {
ro_number: data.jobs_by_pk.ro_number
});
- }, [loading,data,t]);
+ }, [loading, data, t]);
+ const [selectedTab, setSelectedTab] = useState(hash ? hash : "#lines");
if (loading) return ;
if (error) return ;
-
+ console.log("selectedTab", selectedTab);
return (
-
+
@@ -41,7 +44,7 @@ function JobsDetailPage({ match }) {
Lines
}
- key='lines'>
+ key='#lines'>
}
- key='rates'>
+ key='#rates'>
Estimate Rates
}
- key='parts'>
+ key='#parts'>
Estimate Parts
+
+
+ Documents
+
+ }
+ key='#documents'>
+
+
diff --git a/client/src/pages/jobs-documents/jobs-documents.container.jsx b/client/src/pages/jobs-documents/jobs-documents.container.jsx
deleted file mode 100644
index 85329e289..000000000
--- a/client/src/pages/jobs-documents/jobs-documents.container.jsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import React from "react";
-import JobDocuments from "./jobs-documents.page";
-
-export default function JobsDocumentsContainer() {
- return ;
-}
diff --git a/client/src/pages/jobs-documents/jobs-documents.page.jsx b/client/src/pages/jobs-documents/jobs-documents.page.jsx
deleted file mode 100644
index da1c68a83..000000000
--- a/client/src/pages/jobs-documents/jobs-documents.page.jsx
+++ /dev/null
@@ -1,189 +0,0 @@
-import React, { useEffect, useState } from "react";
-import { useTranslation } from "react-i18next";
-import { Upload, Modal, Icon, Button } from "antd";
-import axios from "axios";
-import "./jobs-documents.styles.scss";
-
-function getBase64(file) {
- return new Promise((resolve, reject) => {
- const reader = new FileReader();
- reader.readAsDataURL(file);
- reader.onload = () => resolve(reader.result);
- reader.onerror = error => reject(error);
- });
-}
-
-function JobsDetailPage({ match, loading, data }) {
- //const { jobId } = match.params;
-
- console.log("match", match);
- const { t } = useTranslation();
-
- useEffect(() => {
- document.title = loading
- ? "..."
- : t("titles.jobsdocuments", {
- ro_number: data.jobs_by_pk.ro_number
- });
- }, [t, loading, data]);
-
- const [state, setState] = useState({
- previewVisible: false,
- previewImage: "",
- fileList: [
- {
- uid: "-1",
- name: "image.png",
- status: "done",
- url:
- "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
- },
- {
- uid: "-2",
- name: "image.png",
- status: "done",
- url:
- "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
- },
- {
- uid: "-3",
- name: "image.png",
- status: "done",
- url:
- "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
- },
- {
- uid: "-4",
- name: "image.png",
- status: "done",
- url:
- "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
- },
- {
- uid: "-5",
- name: "image.png",
- status: "error"
- }
- ]
- });
-
- const test = props => {
- axios({
- url: "/test",
- method: "get"
- })
- .then(response => {
- console.log("response", response);
- })
- .catch(error => {
- console.log("Payment Error: ", error);
- alert(
- "There was an issue with your payment! Please make sure you use the provided credit card."
- );
- });
- };
-
- const handleUpload = ev => {
- console.log("Handle Upload.", ev);
- let file = ev.file;
- // Split the filename to get the name and type
- let fileName = ev.file.name;
- let fileType = ev.file.type;
- console.log("Preparing the upload ");
- axios
- .post("/sign_s3", {
- fileName:
- "testjob/" + "adcd4c7b-029b-48c7-a1c2-8cf1761ce1dc" + fileName,
- fileType
- })
- .then(response => {
- var returnData = response.data.data.returnData;
- var signedRequest = returnData.signedRequest;
- var url = returnData.url;
- setState({ ...state, url: url });
- console.log("Recieved a signed request " + signedRequest);
-
- // Put the fileType in the headers for the upload
- var options = {
- headers: {
- "Content-Type": fileType
- }
- };
- axios
- .put(signedRequest, file, options)
- .then(result => {
- console.log("Response from s3", result);
- setState({ ...state, success: true });
- })
- .catch(error => {
- console.log("Inside Error here.", error);
- alert("ERROR " + JSON.stringify(error));
- });
- })
- .catch(error => {
- console.log("Outside Error here.", error);
- alert(JSON.stringify(error));
- });
- };
-
- const handleCancel = () => setState({ ...state, previewVisible: false });
-
- const handlePreview = async file => {
- if (!file.url && !file.preview) {
- file.preview = await getBase64(file.originFileObj);
- }
-
- setState({
- ...state,
- previewImage: file.url || file.preview,
- previewVisible: true
- });
- };
-
- const handleChange = ({ fileList }) => setState({ ...state, fileList });
-
- const { previewVisible, previewImage, fileList } = state;
- const uploadButton = (
-
- );
-
- const transformFile = file => {
- console.log("Transforming file.", file);
- // Make the image smaller here.
-
- const reader = new FileReader();
-
- reader.onload = e => {
- console.log(e.target.result);
- };
- reader.readAsDataURL(file);
-
- console.log("reader", reader);
-
- return file;
- };
-
- return (
-
-
-
- {fileList.length >= 8 ? null : uploadButton}
-
-
-
-
-
- );
-}
-export default JobsDetailPage;
diff --git a/client/src/pages/manage/manage.page.jsx b/client/src/pages/manage/manage.page.jsx
index 2ad279364..9d415fe6f 100644
--- a/client/src/pages/manage/manage.page.jsx
+++ b/client/src/pages/manage/manage.page.jsx
@@ -13,7 +13,7 @@ const JobsPage = lazy(() => import("../jobs/jobs.page"));
const JobsDetailPage = lazy(() => import("../jobs-detail/jobs-detail.page"));
const ProfilePage = lazy(() => import("../profile/profile.container.page"));
const JobsDocumentsPage = lazy(() =>
- import("../jobs-documents/jobs-documents.container")
+ import("../../components/jobs-documents/jobs-documents.container")
);
const { Header, Content, Footer } = Layout;
diff --git a/client/yarn.lock b/client/yarn.lock
index b8fbffdb2..144995976 100644
--- a/client/yarn.lock
+++ b/client/yarn.lock
@@ -3544,6 +3544,11 @@ compose-function@3.0.3:
dependencies:
arity-n "^1.0.4"
+compress@^0.99.0:
+ version "0.99.0"
+ resolved "https://registry.yarnpkg.com/compress/-/compress-0.99.0.tgz#97e301c25c4d01f097d85103f65eccb2e7796502"
+ integrity sha1-l+MBwlxNAfCX2FED9l7Msud5ZQI=
+
compressible@~2.0.16:
version "2.0.17"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.17.tgz#6e8c108a16ad58384a977f3a482ca20bff2f38c1"
@@ -10355,6 +10360,11 @@ react-icons@^3.8.0:
dependencies:
camelcase "^5.0.0"
+react-image-file-resizer@^0.2.1:
+ version "0.2.1"
+ resolved "https://registry.yarnpkg.com/react-image-file-resizer/-/react-image-file-resizer-0.2.1.tgz#ee081bd41798ff960eea1a56b1a86ba317fecf11"
+ integrity sha512-uvhNj2NKMUraVKIrsmPNZgWn34b7fjEcuWAyMXUrVb06gedNtOalOBxVwXYocd4KnZRFv2/ilmAE4KEzIkj4aA==
+
react-is@^16.12.0, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.8.6, react-is@^16.9.0:
version "16.12.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
diff --git a/hasura/migrations/1579106882840_create_table_public_documents/down.yaml b/hasura/migrations/1579106882840_create_table_public_documents/down.yaml
new file mode 100644
index 000000000..db79a3b27
--- /dev/null
+++ b/hasura/migrations/1579106882840_create_table_public_documents/down.yaml
@@ -0,0 +1,3 @@
+- args:
+ sql: DROP TABLE "public"."documents"
+ type: run_sql
diff --git a/hasura/migrations/1579106882840_create_table_public_documents/up.yaml b/hasura/migrations/1579106882840_create_table_public_documents/up.yaml
new file mode 100644
index 000000000..e0647ebe1
--- /dev/null
+++ b/hasura/migrations/1579106882840_create_table_public_documents/up.yaml
@@ -0,0 +1,20 @@
+- args:
+ sql: CREATE EXTENSION IF NOT EXISTS pgcrypto;
+ type: run_sql
+- args:
+ sql: "CREATE TABLE \"public\".\"documents\"(\"id\" uuid NOT NULL DEFAULT gen_random_uuid(),
+ \"created_at\" timestamptz NOT NULL DEFAULT now(), \"updated_at\" timestamptz
+ NOT NULL DEFAULT now(), \"url\" text NOT NULL, \"thumb_url\" text NOT NULL,
+ \"uploaded_by\" text NOT NULL, \"jobid\" uuid NOT NULL, \"name\" text, PRIMARY
+ KEY (\"id\") , FOREIGN KEY (\"jobid\") REFERENCES \"public\".\"jobs\"(\"id\")
+ ON UPDATE cascade ON DELETE cascade);\nCREATE OR REPLACE FUNCTION \"public\".\"set_current_timestamp_updated_at\"()\nRETURNS
+ TRIGGER AS $$\nDECLARE\n _new record;\nBEGIN\n _new := NEW;\n _new.\"updated_at\"
+ = NOW();\n RETURN _new;\nEND;\n$$ LANGUAGE plpgsql;\nCREATE TRIGGER \"set_public_documents_updated_at\"\nBEFORE
+ UPDATE ON \"public\".\"documents\"\nFOR EACH ROW\nEXECUTE PROCEDURE \"public\".\"set_current_timestamp_updated_at\"();\nCOMMENT
+ ON TRIGGER \"set_public_documents_updated_at\" ON \"public\".\"documents\" \nIS
+ 'trigger to set value of column \"updated_at\" to current timestamp on row update';\n"
+ type: run_sql
+- args:
+ name: documents
+ schema: public
+ type: add_existing_table_or_view
diff --git a/hasura/migrations/1579106900223_track_all_relationships/down.yaml b/hasura/migrations/1579106900223_track_all_relationships/down.yaml
new file mode 100644
index 000000000..41c2dbf13
--- /dev/null
+++ b/hasura/migrations/1579106900223_track_all_relationships/down.yaml
@@ -0,0 +1,12 @@
+- args:
+ relationship: job
+ table:
+ name: documents
+ schema: public
+ type: drop_relationship
+- args:
+ relationship: documents
+ table:
+ name: jobs
+ schema: public
+ type: drop_relationship
diff --git a/hasura/migrations/1579106900223_track_all_relationships/up.yaml b/hasura/migrations/1579106900223_track_all_relationships/up.yaml
new file mode 100644
index 000000000..3b52d494f
--- /dev/null
+++ b/hasura/migrations/1579106900223_track_all_relationships/up.yaml
@@ -0,0 +1,20 @@
+- args:
+ name: job
+ table:
+ name: documents
+ schema: public
+ using:
+ foreign_key_constraint_on: jobid
+ type: create_object_relationship
+- args:
+ name: documents
+ table:
+ name: jobs
+ schema: public
+ using:
+ foreign_key_constraint_on:
+ column: jobid
+ table:
+ name: documents
+ schema: public
+ type: create_array_relationship
diff --git a/hasura/migrations/1579106943432_update_permission_user_public_table_documents/down.yaml b/hasura/migrations/1579106943432_update_permission_user_public_table_documents/down.yaml
new file mode 100644
index 000000000..dd7923c4f
--- /dev/null
+++ b/hasura/migrations/1579106943432_update_permission_user_public_table_documents/down.yaml
@@ -0,0 +1,6 @@
+- args:
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: drop_insert_permission
diff --git a/hasura/migrations/1579106943432_update_permission_user_public_table_documents/up.yaml b/hasura/migrations/1579106943432_update_permission_user_public_table_documents/up.yaml
new file mode 100644
index 000000000..21534a4d6
--- /dev/null
+++ b/hasura/migrations/1579106943432_update_permission_user_public_table_documents/up.yaml
@@ -0,0 +1,31 @@
+- args:
+ permission:
+ allow_upsert: true
+ check:
+ job:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ columns:
+ - name
+ - thumb_url
+ - uploaded_by
+ - url
+ - created_at
+ - updated_at
+ - id
+ - jobid
+ localPresets:
+ - key: ""
+ value: ""
+ set: {}
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: create_insert_permission
diff --git a/hasura/migrations/1579106952393_update_permission_user_public_table_documents/down.yaml b/hasura/migrations/1579106952393_update_permission_user_public_table_documents/down.yaml
new file mode 100644
index 000000000..51466ca34
--- /dev/null
+++ b/hasura/migrations/1579106952393_update_permission_user_public_table_documents/down.yaml
@@ -0,0 +1,6 @@
+- args:
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: drop_select_permission
diff --git a/hasura/migrations/1579106952393_update_permission_user_public_table_documents/up.yaml b/hasura/migrations/1579106952393_update_permission_user_public_table_documents/up.yaml
new file mode 100644
index 000000000..2f2117f0a
--- /dev/null
+++ b/hasura/migrations/1579106952393_update_permission_user_public_table_documents/up.yaml
@@ -0,0 +1,28 @@
+- args:
+ permission:
+ allow_aggregations: false
+ columns:
+ - name
+ - thumb_url
+ - uploaded_by
+ - url
+ - created_at
+ - updated_at
+ - id
+ - jobid
+ filter:
+ job:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ limit: null
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: create_select_permission
diff --git a/hasura/migrations/1579106959311_update_permission_user_public_table_documents/down.yaml b/hasura/migrations/1579106959311_update_permission_user_public_table_documents/down.yaml
new file mode 100644
index 000000000..d9430d461
--- /dev/null
+++ b/hasura/migrations/1579106959311_update_permission_user_public_table_documents/down.yaml
@@ -0,0 +1,6 @@
+- args:
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: drop_update_permission
diff --git a/hasura/migrations/1579106959311_update_permission_user_public_table_documents/up.yaml b/hasura/migrations/1579106959311_update_permission_user_public_table_documents/up.yaml
new file mode 100644
index 000000000..1aa5bbecc
--- /dev/null
+++ b/hasura/migrations/1579106959311_update_permission_user_public_table_documents/up.yaml
@@ -0,0 +1,30 @@
+- args:
+ permission:
+ columns:
+ - name
+ - thumb_url
+ - uploaded_by
+ - url
+ - created_at
+ - updated_at
+ - id
+ - jobid
+ filter:
+ job:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ localPresets:
+ - key: ""
+ value: ""
+ set: {}
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: create_update_permission
diff --git a/hasura/migrations/1579106966146_update_permission_user_public_table_documents/down.yaml b/hasura/migrations/1579106966146_update_permission_user_public_table_documents/down.yaml
new file mode 100644
index 000000000..35842c257
--- /dev/null
+++ b/hasura/migrations/1579106966146_update_permission_user_public_table_documents/down.yaml
@@ -0,0 +1,6 @@
+- args:
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: drop_delete_permission
diff --git a/hasura/migrations/1579106966146_update_permission_user_public_table_documents/up.yaml b/hasura/migrations/1579106966146_update_permission_user_public_table_documents/up.yaml
new file mode 100644
index 000000000..0cf25dca3
--- /dev/null
+++ b/hasura/migrations/1579106966146_update_permission_user_public_table_documents/up.yaml
@@ -0,0 +1,17 @@
+- args:
+ permission:
+ filter:
+ job:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ role: user
+ table:
+ name: documents
+ schema: public
+ type: create_delete_permission