From ddbb1df9d9e627907a6972c10dd99b9b366036a3 Mon Sep 17 00:00:00 2001
From: Patrick Fic <>
Date: Fri, 23 Apr 2021 09:11:48 -0700
Subject: [PATCH] IO-924 Rest of phonebook implementation
---
bodyshop_translations.babel | 450 ++++++++++++++++++
.../components/header/header.component.jsx | 3 +
.../phonebook-form.component.jsx | 122 +++++
.../phonebook-form.container.jsx | 151 ++++++
.../components/rbac-wrapper/rbac-defaults.js | 3 +
.../shop-info/shop-info.rbac.component.jsx | 24 +
client/src/graphql/accounting.queries.js | 38 ++
client/src/graphql/phonebook.queries.js | 92 ++++
.../pages/manage/manage.page.component.jsx | 3 +
.../phonebook/phonebook.page.component.jsx | 190 ++++++++
.../phonebook/phonebook.page.container.jsx | 68 +++
client/src/redux/messaging/messaging.sagas.js | 5 +-
client/src/translations/en_us/common.json | 32 ++
client/src/translations/es/common.json | 32 ++
client/src/translations/fr/common.json | 32 ++
.../down.yaml | 43 ++
.../up.yaml | 43 ++
hasura/migrations/metadata.yaml | 1 +
18 files changed, 1331 insertions(+), 1 deletion(-)
create mode 100644 client/src/components/phonebook-form/phonebook-form.component.jsx
create mode 100644 client/src/components/phonebook-form/phonebook-form.container.jsx
create mode 100644 client/src/graphql/phonebook.queries.js
create mode 100644 client/src/pages/phonebook/phonebook.page.component.jsx
create mode 100644 client/src/pages/phonebook/phonebook.page.container.jsx
create mode 100644 hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/down.yaml
create mode 100644 hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/up.yaml
diff --git a/bodyshop_translations.babel b/bodyshop_translations.babel
index 289e7d526..55055d963 100644
--- a/bodyshop_translations.babel
+++ b/bodyshop_translations.babel
@@ -4531,6 +4531,53 @@
+
+ phonebook
+
+
+ edit
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ view
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
production
@@ -23265,6 +23312,27 @@
+
+ phonebook
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
productionboard
false
@@ -26862,6 +26930,320 @@
+
+ phonebook
+
+
+ actions
+
+
+ new
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
+
+ fields
+
+
+ city
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ company
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ country
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ email
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ fax
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ firstname
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ lastname
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ phone1
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ phone2
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ state
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ street1
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+ street2
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
+
+ successes
+
+
+ saved
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
+
+
printcenter
@@ -28375,6 +28757,32 @@
+
+ payments
+
+
+ ca_bc_etf_table
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
+
+
vendors
@@ -31961,6 +32369,27 @@
+
+ phonebook
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
productionboard
false
@@ -32740,6 +33169,27 @@
+
+ phonebook
+ false
+
+
+
+
+
+ en-US
+ false
+
+
+ es-MX
+ false
+
+
+ fr-CA
+ false
+
+
+
productionboard
false
diff --git a/client/src/components/header/header.component.jsx b/client/src/components/header/header.component.jsx
index 625fff46d..b3148f09b 100644
--- a/client/src/components/header/header.component.jsx
+++ b/client/src/components/header/header.component.jsx
@@ -276,6 +276,9 @@ function Header({
{t("menus.header.temporarydocs")}
+
+ {t("menus.header.phonebook")}
+
{
//
//
diff --git a/client/src/components/phonebook-form/phonebook-form.component.jsx b/client/src/components/phonebook-form/phonebook-form.component.jsx
new file mode 100644
index 000000000..ac992a285
--- /dev/null
+++ b/client/src/components/phonebook-form/phonebook-form.component.jsx
@@ -0,0 +1,122 @@
+import { Button, Form, Input, PageHeader, Space } from "antd";
+import React from "react";
+import { useTranslation } from "react-i18next";
+import FormFieldsChanged from "../form-fields-changed-alert/form-fields-changed-alert.component";
+import FormItemEmail from "../form-items-formatted/email-form-item.component";
+import PhoneFormItem, {
+ PhoneItemFormatterValidation,
+} from "../form-items-formatted/phone-form-item.component";
+import LayoutFormRow from "../layout-form-row/layout-form-row.component";
+
+export default function PhonebookFormComponent({
+ form,
+ formLoading,
+ handleDelete,
+}) {
+ const { t } = useTranslation();
+
+ const { getFieldValue } = form;
+
+ return (
+
+ );
+}
diff --git a/client/src/components/phonebook-form/phonebook-form.container.jsx b/client/src/components/phonebook-form/phonebook-form.container.jsx
new file mode 100644
index 000000000..2b293f349
--- /dev/null
+++ b/client/src/components/phonebook-form/phonebook-form.container.jsx
@@ -0,0 +1,151 @@
+import { useMutation, useQuery } from "@apollo/client";
+import { Form, notification } from "antd";
+import queryString from "query-string";
+import React, { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
+import { connect } from "react-redux";
+import { useHistory, useLocation } from "react-router-dom";
+import { createStructuredSelector } from "reselect";
+import {
+ DELETE_PHONEBOOK,
+ INSERT_NEW_PHONEBOOK,
+ QUERY_PHONEBOOK_BY_ID,
+ UPDATE_PHONEBOOK,
+} from "../../graphql/phonebook.queries";
+import { selectBodyshop } from "../../redux/user/user.selectors";
+import AlertComponent from "../alert/alert.component";
+import LoadingSpinner from "../loading-spinner/loading-spinner.component";
+import PhonebookFormComponent from "./phonebook-form.component";
+
+const mapStateToProps = createStructuredSelector({
+ bodyshop: selectBodyshop,
+});
+
+function PhonebookFormContainer({ refetch, bodyshop }) {
+ const history = useHistory();
+ const search = queryString.parse(useLocation().search);
+ const { phonebookentry } = search;
+ const [formLoading, setFormLoading] = useState(false);
+ const [form] = Form.useForm();
+ const { t } = useTranslation();
+ const { loading, error, data } = useQuery(QUERY_PHONEBOOK_BY_ID, {
+ variables: { id: phonebookentry },
+ fetchPolicy: "network-only",
+ skip: !!!phonebookentry || phonebookentry === "new",
+ });
+
+ const [updatePhonebook] = useMutation(UPDATE_PHONEBOOK);
+ const [insertPhonebook] = useMutation(INSERT_NEW_PHONEBOOK);
+ const [deletePhonebook] = useMutation(DELETE_PHONEBOOK);
+
+ const handleDelete = async () => {
+ setFormLoading(true);
+ const result = await deletePhonebook({
+ variables: { id: phonebookentry },
+ refetchQueries: ["QUERY_PHONEBOOK_PAGINATED"],
+ });
+
+ if (!result.errors) {
+ notification["success"]({
+ message: t("phonebook.successes.deleted"),
+ });
+ delete search.phonebookentry;
+ history.push({ search: queryString.stringify(search) });
+ if (refetch)
+ refetch().then((r) => {
+ form.resetFields();
+ });
+ } else {
+ notification["error"]({
+ message: t("phonebook.errors.deleting", {
+ message: JSON.stringify(result.errors),
+ }),
+ });
+ }
+
+ setFormLoading(false);
+ };
+
+ const handleFinish = async (values) => {
+ setFormLoading(true);
+ if (phonebookentry && phonebookentry !== "new") {
+ //It's a phonebook to update.
+ const result = await updatePhonebook({
+ variables: { id: phonebookentry, phonebook: values },
+ refetchQueries: ["QUERY_PHONEBOOK_PAGINATED"],
+ });
+
+ if (!result.errors) {
+ notification["success"]({
+ message: t("Phonebook.successes.saved"),
+ });
+
+ if (refetch) await refetch();
+ form.setFieldsValue(data.phonebook_by_pk);
+ form.resetFields();
+
+ setFormLoading(false);
+ } else {
+ notification["error"]({
+ message: t("Phonebook.errors.saving"),
+ });
+
+ setFormLoading(false);
+ }
+ } else {
+ //It's a new phonebook to insert.
+ const result = await insertPhonebook({
+ variables: {
+ phonebook_entry: [{ ...values, bodyshopid: bodyshop.id }],
+ },
+ refetchQueries: ["QUERY_PHONEBOOK_PAGINATED"],
+ });
+
+ if (!result.errors) {
+ notification["success"]({
+ message: t("phonebook.successes.saved"),
+ });
+
+ if (refetch) await refetch();
+ form.resetFields();
+ form.resetFields();
+ delete search.phonebookentry;
+ history.push({ search: queryString.stringify(search) });
+ setFormLoading(false);
+ } else {
+ notification["error"]({
+ message: t("phonebook.errors.saving"),
+ });
+ setFormLoading(false);
+ }
+ }
+ };
+
+ useEffect(() => {
+ if (data || phonebookentry === "new") form.resetFields();
+ }, [data, form, phonebookentry]);
+
+ if (loading) return ;
+ if (error) return ;
+
+ return (
+
+ );
+}
+export default connect(mapStateToProps, null)(PhonebookFormContainer);
diff --git a/client/src/components/rbac-wrapper/rbac-defaults.js b/client/src/components/rbac-wrapper/rbac-defaults.js
index 8f2fa1f1e..e7180126a 100644
--- a/client/src/components/rbac-wrapper/rbac-defaults.js
+++ b/client/src/components/rbac-wrapper/rbac-defaults.js
@@ -39,6 +39,9 @@ const ret = {
"payments:enter": 3,
"payments:list": 3,
+ "phonebook:view": 1,
+ "phonebook:edit": 2,
+
"production:board": 1,
"production:list": 1,
diff --git a/client/src/components/shop-info/shop-info.rbac.component.jsx b/client/src/components/shop-info/shop-info.rbac.component.jsx
index 6615754bc..3aadcf16b 100644
--- a/client/src/components/shop-info/shop-info.rbac.component.jsx
+++ b/client/src/components/shop-info/shop-info.rbac.component.jsx
@@ -561,6 +561,30 @@ export default function ShopInfoRbacComponent({ form }) {
>
+
+
+
+
+
+
);
diff --git a/client/src/graphql/accounting.queries.js b/client/src/graphql/accounting.queries.js
index 85380e14a..d9fa75c83 100644
--- a/client/src/graphql/accounting.queries.js
+++ b/client/src/graphql/accounting.queries.js
@@ -84,6 +84,44 @@ export const INSERT_EXPORT_LOG = gql`
}
`;
+export const QUERY_PHONEBOOK_PAGINATED = gql`
+ query QUERY_PHONEBOOK_PAGINATED(
+ $search: String
+ $offset: Int
+ $limit: Int
+ $order: [phonebook_order_by!]
+ ) {
+ search_phonebook(
+ offset: $offset
+ limit: $limit
+ order_by: $order
+ args: { search: $search }
+ ) {
+ id
+ created_at
+ firstname
+ lastname
+ phone1
+ phone2
+ state
+ zip
+ fax
+ email
+ country
+ company
+ city
+ category
+ address2
+ address1
+ }
+ search_phonebook_aggregate(args: { search: $search }) {
+ aggregate {
+ count(distinct: true)
+ }
+ }
+ }
+`;
+
export const QUERY_EXPORT_LOG_PAGINATED = gql`
query QUERY_ALL_EXPORTLOG_PAGINATED(
$search: String
diff --git a/client/src/graphql/phonebook.queries.js b/client/src/graphql/phonebook.queries.js
new file mode 100644
index 000000000..67f547441
--- /dev/null
+++ b/client/src/graphql/phonebook.queries.js
@@ -0,0 +1,92 @@
+import { gql } from "@apollo/client";
+
+export const QUERY_PHONEBOOK_PAGINATED = gql`
+ query QUERY_PHONEBOOK_PAGINATED(
+ $search: String
+ $offset: Int
+ $limit: Int
+ $order: [phonebook_order_by!]
+ ) {
+ search_phonebook(
+ offset: $offset
+ limit: $limit
+ order_by: $order
+ args: { search: $search }
+ ) {
+ id
+ created_at
+ firstname
+ lastname
+ phone1
+ phone2
+ state
+ zip
+ fax
+ email
+ country
+ company
+ city
+ category
+ address2
+ address1
+ }
+ search_phonebook_aggregate(args: { search: $search }) {
+ aggregate {
+ count(distinct: true)
+ }
+ }
+ }
+`;
+
+export const QUERY_PHONEBOOK_BY_ID = gql`
+ query QUERY_PHONEBOOK_BY_ID($id: uuid!) {
+ phonebook_by_pk(id: $id) {
+ id
+ created_at
+ firstname
+ lastname
+ phone1
+ phone2
+ state
+ zip
+ fax
+ email
+ country
+ company
+ city
+ category
+ address2
+ address1
+ }
+ }
+`;
+
+export const UPDATE_PHONEBOOK = gql`
+ mutation UPDATE_VENDOR($id: uuid!, $phonebook: phonebook_set_input!) {
+ update_phonebook(where: { id: { _eq: $id } }, _set: $phonebook) {
+ returning {
+ id
+ }
+ }
+ }
+`;
+
+export const INSERT_NEW_PHONEBOOK = gql`
+ mutation INSERT_NEW_PHONEBOOK($phonebook_entry: [phonebook_insert_input!]!) {
+ insert_phonebook(objects: $phonebook_entry) {
+ returning {
+ id
+ }
+ }
+ }
+`;
+
+export const DELETE_PHONEBOOK = gql`
+ mutation DELETE_PHONEBOOK($id: uuid!) {
+ delete_phonebook(where: { id: { _eq: $id } }) {
+ returning {
+ id
+ }
+ }
+ }
+`;
diff --git a/client/src/pages/manage/manage.page.component.jsx b/client/src/pages/manage/manage.page.component.jsx
index 130b0efc9..95202c9ff 100644
--- a/client/src/pages/manage/manage.page.component.jsx
+++ b/client/src/pages/manage/manage.page.component.jsx
@@ -153,6 +153,8 @@ const PartsQueue = lazy(() =>
const ExportLogs = lazy(() =>
import("../export-logs/export-logs.page.container")
);
+const Phonebook = lazy(() => import("../phonebook/phonebook.page.container"));
+
const EmailTest = lazy(() =>
import("../../components/email-test/email-test-component")
);
@@ -348,6 +350,7 @@ export function Manage({ match, conflict, bodyshop }) {
component={ExportLogs}
/>
+
diff --git a/client/src/pages/phonebook/phonebook.page.component.jsx b/client/src/pages/phonebook/phonebook.page.component.jsx
new file mode 100644
index 000000000..cb2c07b7d
--- /dev/null
+++ b/client/src/pages/phonebook/phonebook.page.component.jsx
@@ -0,0 +1,190 @@
+import { SyncOutlined } from "@ant-design/icons";
+import { useQuery } from "@apollo/client";
+import { Button, Card, Input, Space, Table, Typography } from "antd";
+import _ from "lodash";
+import queryString from "query-string";
+import React from "react";
+import { useTranslation } from "react-i18next";
+import { connect } from "react-redux";
+import { useHistory, useLocation } from "react-router-dom";
+import { createStructuredSelector } from "reselect";
+import AlertComponent from "../../components/alert/alert.component";
+import { QUERY_PHONEBOOK_PAGINATED } from "../../graphql/phonebook.queries";
+import { selectBodyshop } from "../../redux/user/user.selectors";
+import ChatOpenButton from "../../components/chat-open-button/chat-open-button.component";
+
+const mapStateToProps = createStructuredSelector({
+ bodyshop: selectBodyshop,
+});
+
+export function PhonebookPageComponent({ bodyshop }) {
+ const searchParams = queryString.parse(useLocation().search);
+ const { page, sortcolumn, sortorder, search, phonebookentry } = searchParams;
+ const history = useHistory();
+
+ const { loading, error, data, refetch } = useQuery(
+ QUERY_PHONEBOOK_PAGINATED,
+ {
+ variables: {
+ search: search || "",
+ offset: page ? (page - 1) * 25 : 0,
+ limit: 25,
+ order: [
+ {
+ [sortcolumn || "lastname"]: sortorder
+ ? sortorder === "descend"
+ ? "desc"
+ : "asc"
+ : "desc",
+ },
+ ],
+ },
+ }
+ );
+
+ const { t } = useTranslation();
+
+ if (error) return ;
+
+ const handleTableChange = (pagination, filters, sorter) => {
+ searchParams.page = pagination.current;
+ searchParams.sortcolumn = sorter.columnKey;
+ searchParams.sortorder = sorter.order;
+ if (filters.status) {
+ searchParams.statusFilters = JSON.stringify(
+ _.flattenDeep(filters.status)
+ );
+ } else {
+ delete searchParams.statusFilters;
+ }
+ history.push({ search: queryString.stringify(searchParams) });
+ };
+
+ const columns = [
+ {
+ title: t("phonebook.fields.firstname"),
+ dataIndex: "firstname",
+ key: "firstname",
+ },
+ {
+ title: t("phonebook.fields.lastname"),
+ dataIndex: "lastname",
+ key: "lastname",
+ },
+ {
+ title: t("phonebook.fields.company"),
+ dataIndex: "company",
+ key: "company",
+ },
+ {
+ title: t("phonebook.fields.email"),
+ dataIndex: "email",
+ key: "email",
+ },
+ {
+ title: t("phonebook.fields.phone1"),
+ dataIndex: "phone1",
+ key: "phone1",
+ render: (text, record) => ,
+ },
+ {
+ title: t("phonebook.fields.phone2"),
+ dataIndex: "phone2",
+ key: "phone2",
+ render: (text, record) => ,
+ },
+ {
+ title: t("phonebook.fields.street1"),
+ dataIndex: "street1",
+ key: "street1",
+ },
+ {
+ title: t("phonebook.fields.city"),
+ dataIndex: "city",
+ key: "city",
+ },
+ ];
+
+ const handleNewPhonebook = () => {
+ searchParams.phonebookentry = "new";
+ history.push({ search: queryString.stringify(searchParams) });
+ };
+
+ const handleOnRowClick = (record) => {
+ if (record) {
+ searchParams.phonebookentry = record.id;
+ history.push({ search: queryString.stringify(searchParams) });
+ } else {
+ delete searchParams.phonebookentry;
+ history.push({ search: queryString.stringify(searchParams) });
+ }
+ };
+
+ return (
+
+ {searchParams.search && (
+ <>
+
+ {t("general.labels.searchresults", {
+ search: searchParams.search,
+ })}
+
+
+ >
+ )}
+
+
+ {
+ searchParams.search = value;
+ history.push({ search: queryString.stringify(searchParams) });
+ }}
+ />
+
+ }
+ >
+ {
+ return {
+ onClick: (event) => {
+ handleOnRowClick(record);
+ },
+ };
+ }}
+ />
+
+ );
+}
+
+export default connect(mapStateToProps, null)(PhonebookPageComponent);
diff --git a/client/src/pages/phonebook/phonebook.page.container.jsx b/client/src/pages/phonebook/phonebook.page.container.jsx
new file mode 100644
index 000000000..a1cd33107
--- /dev/null
+++ b/client/src/pages/phonebook/phonebook.page.container.jsx
@@ -0,0 +1,68 @@
+import React, { useEffect } from "react";
+import { useTranslation } from "react-i18next";
+import { connect } from "react-redux";
+import RbacWrapper from "../../components/rbac-wrapper/rbac-wrapper.component";
+import {
+ setBreadcrumbs,
+ setSelectedHeader,
+} from "../../redux/application/application.actions";
+import PhonebookPage from "./phonebook.page.component";
+import { Drawer, Grid } from "antd";
+import queryString from "query-string";
+import { useHistory, useLocation } from "react-router-dom";
+import PhonebookFormContainer from "../../components/phonebook-form/phonebook-form.container";
+const mapDispatchToProps = (dispatch) => ({
+ setBreadcrumbs: (breadcrumbs) => dispatch(setBreadcrumbs(breadcrumbs)),
+ setSelectedHeader: (key) => dispatch(setSelectedHeader(key)),
+});
+
+export function PhonebookContainer({ setBreadcrumbs, setSelectedHeader }) {
+ const { t } = useTranslation();
+
+ useEffect(() => {
+ document.title = t("titles.phonebook");
+ setSelectedHeader("phonebook");
+ setBreadcrumbs([
+ {
+ link: "/manage/phonebook",
+ label: t("titles.bc.phonebook"),
+ },
+ ]);
+ }, [setBreadcrumbs, t, setSelectedHeader]);
+ const search = queryString.parse(useLocation().search);
+ const { phonebookentry } = search;
+ const history = useHistory();
+
+ const selectedBreakpoint = Object.entries(Grid.useBreakpoint())
+ .filter((screen) => !!screen[1])
+ .slice(-1)[0];
+
+ const bpoints = {
+ xs: "100%",
+ sm: "100%",
+ md: "100%",
+ lg: "50%",
+ xl: "50%",
+ xxl: "45%",
+ };
+ const drawerPercentage = selectedBreakpoint
+ ? bpoints[selectedBreakpoint[0]]
+ : "100%";
+
+ return (
+
+
+ {
+ delete search.phonebookentry;
+ history.push({ search: queryString.stringify(search) });
+ }}
+ visible={phonebookentry}
+ >
+
+
+
+ );
+}
+export default connect(null, mapDispatchToProps)(PhonebookContainer);
diff --git a/client/src/redux/messaging/messaging.sagas.js b/client/src/redux/messaging/messaging.sagas.js
index 296dca198..8a6a9f10e 100644
--- a/client/src/redux/messaging/messaging.sagas.js
+++ b/client/src/redux/messaging/messaging.sagas.js
@@ -66,7 +66,10 @@ export function* openChatByPhone({ payload }) {
yield put(setSelectedConversation(conversations[0].id));
//Check to see if this job ID is already a child of it. If not add the tag.
- if (!conversations[0].job_conversations.find((jc) => jc.jobid === jobid))
+ if (
+ jobid &&
+ !conversations[0].job_conversations.find((jc) => jc.jobid === jobid)
+ )
yield client.mutate({
mutation: INSERT_CONVERSATION_TAG,
variables: {
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index 80c49ef36..e51094304 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -295,6 +295,10 @@
"enter": "Payments -> Enter",
"list": "Payments -> List"
},
+ "phonebook": {
+ "edit": "Phonebook -> Edit",
+ "view": "Phonebook -> View"
+ },
"production": {
"board": "Production -> Board",
"list": "Production -> List"
@@ -1373,6 +1377,7 @@
"jobs": "Jobs",
"owners": "Owners",
"parts-queue": "Parts Queue",
+ "phonebook": "Phonebook",
"productionboard": "Production Board - Visual",
"productionlist": "Production Board - List",
"recent": "Recent Items",
@@ -1606,6 +1611,28 @@
"stripe": "Credit card transaction charged successfully."
}
},
+ "phonebook": {
+ "actions": {
+ "new": "New Phonebook Entry"
+ },
+ "fields": {
+ "city": "City",
+ "company": "Company",
+ "country": "Country",
+ "email": "Email",
+ "fax": "Fax",
+ "firstname": "First Name",
+ "lastname": "Last Name",
+ "phone1": "Phone 1",
+ "phone2": "Phone 2",
+ "state": "state",
+ "street1": "Street 1",
+ "street2": "Street 2"
+ },
+ "successes": {
+ "saved": ""
+ }
+ },
"printcenter": {
"appointments": {
"appointment_confirmation": "Appointment Confirmation"
@@ -1693,6 +1720,9 @@
"speedprint": "Speed Print",
"title": "Print Center"
},
+ "payments": {
+ "ca_bc_etf_table": "ICBC ETF Table"
+ },
"vendors": {
"purchases_by_vendor_detailed": "Purchases by Vendor - Detailed",
"purchases_by_vendor_summary": "Purchases by Vendor - Summary"
@@ -1921,6 +1951,7 @@
"owners": "Owners",
"parts-queue": "Parts Queue",
"payments-all": "All Payments",
+ "phonebook": "Phonebook",
"productionboard": "Production Board - Visual",
"productionlist": "Production Board - List",
"profile": "My Profile",
@@ -1959,6 +1990,7 @@
"owners-detail": "{{name}} | $t(titles.app)",
"parts-queue": "Parts Queue | $t(titles.app)",
"payments-all": "Payments | $t(titles.app)",
+ "phonebook": "Phonebook | $t(titles.app)",
"productionboard": "Production - Board",
"productionlist": "Production Board - List | $t(titles.app)",
"profile": "My Profile | $t(titles.app)",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index 2f47ca2cc..aa29f4c29 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -295,6 +295,10 @@
"enter": "",
"list": ""
},
+ "phonebook": {
+ "edit": "",
+ "view": ""
+ },
"production": {
"board": "",
"list": ""
@@ -1373,6 +1377,7 @@
"jobs": "Trabajos",
"owners": "propietarios",
"parts-queue": "",
+ "phonebook": "",
"productionboard": "",
"productionlist": "",
"recent": "",
@@ -1606,6 +1611,28 @@
"stripe": ""
}
},
+ "phonebook": {
+ "actions": {
+ "new": ""
+ },
+ "fields": {
+ "city": "",
+ "company": "",
+ "country": "",
+ "email": "",
+ "fax": "",
+ "firstname": "",
+ "lastname": "",
+ "phone1": "",
+ "phone2": "",
+ "state": "",
+ "street1": "",
+ "street2": ""
+ },
+ "successes": {
+ "saved": ""
+ }
+ },
"printcenter": {
"appointments": {
"appointment_confirmation": ""
@@ -1693,6 +1720,9 @@
"speedprint": "",
"title": ""
},
+ "payments": {
+ "ca_bc_etf_table": ""
+ },
"vendors": {
"purchases_by_vendor_detailed": "",
"purchases_by_vendor_summary": ""
@@ -1921,6 +1951,7 @@
"owners": "",
"parts-queue": "",
"payments-all": "",
+ "phonebook": "",
"productionboard": "",
"productionlist": "",
"profile": "",
@@ -1959,6 +1990,7 @@
"owners-detail": "",
"parts-queue": "",
"payments-all": "",
+ "phonebook": "",
"productionboard": "",
"productionlist": "",
"profile": "Mi perfil | $t(titles.app)",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index da19d7026..65855ea5f 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -295,6 +295,10 @@
"enter": "",
"list": ""
},
+ "phonebook": {
+ "edit": "",
+ "view": ""
+ },
"production": {
"board": "",
"list": ""
@@ -1373,6 +1377,7 @@
"jobs": "Emplois",
"owners": "Propriétaires",
"parts-queue": "",
+ "phonebook": "",
"productionboard": "",
"productionlist": "",
"recent": "",
@@ -1606,6 +1611,28 @@
"stripe": ""
}
},
+ "phonebook": {
+ "actions": {
+ "new": ""
+ },
+ "fields": {
+ "city": "",
+ "company": "",
+ "country": "",
+ "email": "",
+ "fax": "",
+ "firstname": "",
+ "lastname": "",
+ "phone1": "",
+ "phone2": "",
+ "state": "",
+ "street1": "",
+ "street2": ""
+ },
+ "successes": {
+ "saved": ""
+ }
+ },
"printcenter": {
"appointments": {
"appointment_confirmation": ""
@@ -1693,6 +1720,9 @@
"speedprint": "",
"title": ""
},
+ "payments": {
+ "ca_bc_etf_table": ""
+ },
"vendors": {
"purchases_by_vendor_detailed": "",
"purchases_by_vendor_summary": ""
@@ -1921,6 +1951,7 @@
"owners": "",
"parts-queue": "",
"payments-all": "",
+ "phonebook": "",
"productionboard": "",
"productionlist": "",
"profile": "",
@@ -1959,6 +1990,7 @@
"owners-detail": "",
"parts-queue": "",
"payments-all": "",
+ "phonebook": "",
"productionboard": "",
"productionlist": "",
"profile": "Mon profil | $t(titles.app)",
diff --git a/hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/down.yaml b/hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/down.yaml
new file mode 100644
index 000000000..eb63b045d
--- /dev/null
+++ b/hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/down.yaml
@@ -0,0 +1,43 @@
+- args:
+ role: user
+ table:
+ name: phonebook
+ schema: public
+ type: drop_select_permission
+- args:
+ permission:
+ allow_aggregations: false
+ columns:
+ - address1
+ - address2
+ - category
+ - city
+ - company
+ - country
+ - email
+ - fax
+ - firstname
+ - lastname
+ - phone1
+ - phone2
+ - state
+ - zip
+ - created_at
+ - updated_at
+ - bodyshopid
+ - id
+ computed_fields: []
+ filter:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ role: user
+ table:
+ name: phonebook
+ schema: public
+ type: create_select_permission
diff --git a/hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/up.yaml b/hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/up.yaml
new file mode 100644
index 000000000..f354974de
--- /dev/null
+++ b/hasura/migrations/1619191596826_update_permission_user_public_table_phonebook/up.yaml
@@ -0,0 +1,43 @@
+- args:
+ role: user
+ table:
+ name: phonebook
+ schema: public
+ type: drop_select_permission
+- args:
+ permission:
+ allow_aggregations: true
+ columns:
+ - address1
+ - address2
+ - category
+ - city
+ - company
+ - country
+ - email
+ - fax
+ - firstname
+ - lastname
+ - phone1
+ - phone2
+ - state
+ - zip
+ - created_at
+ - updated_at
+ - bodyshopid
+ - id
+ computed_fields: []
+ filter:
+ bodyshop:
+ associations:
+ _and:
+ - user:
+ authid:
+ _eq: X-Hasura-User-Id
+ - active:
+ _eq: true
+ role: user
+ table:
+ name: phonebook
+ schema: public
+ type: create_select_permission
diff --git a/hasura/migrations/metadata.yaml b/hasura/migrations/metadata.yaml
index 3268efc56..874cb182b 100644
--- a/hasura/migrations/metadata.yaml
+++ b/hasura/migrations/metadata.yaml
@@ -3854,6 +3854,7 @@ tables:
_eq: X-Hasura-User-Id
- active:
_eq: true
+ allow_aggregations: true
update_permissions:
- role: user
permission: