diff --git a/client/src/components/chat-affix/registerMessagingSocketHandlers.js b/client/src/components/chat-affix/registerMessagingSocketHandlers.js index a6c7cc33a..41d65427d 100644 --- a/client/src/components/chat-affix/registerMessagingSocketHandlers.js +++ b/client/src/components/chat-affix/registerMessagingSocketHandlers.js @@ -334,12 +334,47 @@ export const registerMessagingHandlers = ({ socket, client }) => { break; case "tag-added": + // Ensure `job_conversations` is properly formatted + const formattedJobConversations = job_conversations.map((jc) => ({ + __typename: "job_conversations", + jobid: jc.jobid || jc.job?.id, + conversationid: conversationId, + job: jc.job || { + __typename: "jobs", + id: data.selectedJob.id, + ro_number: data.selectedJob.ro_number, + ownr_co_nm: data.selectedJob.ownr_co_nm, + ownr_fn: data.selectedJob.ownr_fn, + ownr_ln: data.selectedJob.ownr_ln + } + })); + client.cache.modify({ id: client.cache.identify({ __typename: "conversations", id: conversationId }), fields: { - job_conversations: (existing = []) => [...existing, ...job_conversations] + job_conversations: (existing = []) => { + // Ensure no duplicates based on `jobid` + const existingIds = new Set( + existing.map( + (jc) => + client.cache.readFragment({ + id: client.cache.identify(jc), + fragment: gql` + fragment JobConversationId on job_conversations { + jobid + } + ` + })?.jobid + ) + ); + + const newItems = formattedJobConversations.filter((jc) => !existingIds.has(jc.jobid)); + + return [...existing, ...newItems]; + } } }); + break; case "tag-removed": diff --git a/client/src/components/chat-messages-list/chat-message-list.component.jsx b/client/src/components/chat-messages-list/chat-message-list.component.jsx index f045cae8c..3f2bba2f0 100644 --- a/client/src/components/chat-messages-list/chat-message-list.component.jsx +++ b/client/src/components/chat-messages-list/chat-message-list.component.jsx @@ -1,4 +1,4 @@ -import React, { useEffect, useRef, useState } from "react"; +import React, { useCallback, useEffect, useRef, useState } from "react"; import { Virtuoso } from "react-virtuoso"; import { renderMessage } from "./renderMessage"; import "./chat-message-list.styles.scss"; @@ -16,7 +16,7 @@ export default function ChatMessageListComponent({ messages }) { loadedImagesRef.current = 0; }; - const preloadImages = (imagePaths, onComplete) => { + const preloadImages = useCallback((imagePaths, onComplete) => { resetImageLoadState(); if (imagePaths.length === 0) { @@ -34,7 +34,7 @@ export default function ChatMessageListComponent({ messages }) { } }; }); - }; + }, []); // Ensure all images are loaded on initial render useEffect(() => { @@ -51,7 +51,7 @@ export default function ChatMessageListComponent({ messages }) { }); } }); - }, [messages]); + }, [messages, preloadImages]); // Handle scrolling when new messages are added useEffect(() => { @@ -69,7 +69,7 @@ export default function ChatMessageListComponent({ messages }) { }); } }); - }, [messages, atBottom]); + }, [messages, atBottom, preloadImages]); return (
diff --git a/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx b/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx index 1bb791b00..63839b263 100644 --- a/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx +++ b/client/src/components/chat-tag-ro/chat-tag-ro.container.jsx @@ -52,20 +52,26 @@ export function ChatTagRoContainer({ conversation, bodyshop }) { // Find the job details from the search data const selectedJob = data?.search_jobs.find((job) => job.id === option.key); if (!selectedJob) return; - const newJobConversation = { - __typename: "job_conversations", - jobid: selectedJob.id, - conversationid: conversation.id, - job: { - __typename: "jobs", - ...selectedJob - } - }; socket.emit("conversation-modified", { conversationId: conversation.id, bodyshopId: bodyshop.id, type: "tag-added", - job_conversations: [newJobConversation] + selectedJob, + job_conversations: [ + { + __typename: "job_conversations", + jobid: selectedJob.id, + conversationid: conversation.id, + job: { + __typename: "jobs", + id: selectedJob.id, + ro_number: selectedJob.ro_number, + ownr_co_nm: selectedJob.ownr_co_nm, + ownr_fn: selectedJob.ownr_fn, + ownr_ln: selectedJob.ownr_ln + } + } + ] }); } diff --git a/client/src/components/owner-detail-form/owner-detail-form.component.jsx b/client/src/components/owner-detail-form/owner-detail-form.component.jsx index 419de25fa..5de4231f7 100644 --- a/client/src/components/owner-detail-form/owner-detail-form.component.jsx +++ b/client/src/components/owner-detail-form/owner-detail-form.component.jsx @@ -25,6 +25,9 @@ export default function OwnerDetailFormComponent({ form, loading }) { + + + diff --git a/client/src/graphql/owners.queries.js b/client/src/graphql/owners.queries.js index e28fc8aa2..d169263f4 100644 --- a/client/src/graphql/owners.queries.js +++ b/client/src/graphql/owners.queries.js @@ -48,6 +48,7 @@ export const QUERY_OWNER_BY_ID = gql` query QUERY_OWNER_BY_ID($id: uuid!) { owners_by_pk(id: $id) { id + accountingid allow_text_message ownr_addr1 ownr_addr2 diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index e86e9936e..95254cb3d 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -2394,6 +2394,7 @@ "selectexistingornew": "Select an existing owner record or create a new one. " }, "fields": { + "accountingid": "Accounting ID", "address": "Address", "allow_text_message": "Permission to Text?", "name": "Name", @@ -3057,6 +3058,7 @@ "production_not_production_status": "Production not in Production Status", "production_over_time": "Production Level over Time", "psr_by_make": "Percent of Sales by Vehicle Make", + "purchase_return_ratio_excel": "Purchase & Return Ratio - Excel", "purchase_return_ratio_grouped_by_vendor_detail": "Purchase & Return Ratio by Vendor (Detail)", "purchase_return_ratio_grouped_by_vendor_summary": "Purchase & Return Ratio by Vendor (Summary)", "purchases_by_cost_center_detail": "Purchases by Cost Center (Detail)", diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 8b9026489..9b4c00b4b 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2394,6 +2394,7 @@ "selectexistingornew": "" }, "fields": { + "accountingid": "", "address": "Dirección", "allow_text_message": "Permiso de texto?", "name": "Nombre", @@ -3057,6 +3058,7 @@ "production_not_production_status": "", "production_over_time": "", "psr_by_make": "", + "purchase_return_ratio_excel": "", "purchase_return_ratio_grouped_by_vendor_detail": "", "purchase_return_ratio_grouped_by_vendor_summary": "", "purchases_by_cost_center_detail": "", diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 8f9330ea0..1362054a4 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2394,6 +2394,7 @@ "selectexistingornew": "" }, "fields": { + "accountingid": "", "address": "Adresse", "allow_text_message": "Autorisation de texte?", "name": "Prénom", @@ -3057,6 +3058,7 @@ "production_not_production_status": "", "production_over_time": "", "psr_by_make": "", + "purchase_return_ratio_excel": "", "purchase_return_ratio_grouped_by_vendor_detail": "", "purchase_return_ratio_grouped_by_vendor_summary": "", "purchases_by_cost_center_detail": "", diff --git a/client/src/utils/GraphQLClient.js b/client/src/utils/GraphQLClient.js index c60545f02..689083139 100644 --- a/client/src/utils/GraphQLClient.js +++ b/client/src/utils/GraphQLClient.js @@ -3,7 +3,7 @@ import { setContext } from "@apollo/client/link/context"; import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http"; import { RetryLink } from "@apollo/client/link/retry"; import { WebSocketLink } from "@apollo/client/link/ws"; -import { getMainDefinition, offsetLimitPagination } from "@apollo/client/utilities"; +import { getMainDefinition } from "@apollo/client/utilities"; //import { split } from "apollo-link"; import apolloLogger from "apollo-link-logger"; //import axios from "axios"; @@ -143,36 +143,7 @@ middlewares.push( new SentryLink().concat(roundTripLink.concat(retryLink.concat(errorLink.concat(authLink.concat(link))))) ); -const cache = new InMemoryCache({ - typePolicies: { - conversations: { - fields: { - job_conversations: { - merge(existing = [], incoming = [], { readField }) { - const merged = new Map(); - - // Add existing data to the map - existing.forEach((jobConversation) => { - // Use `readField` to get the unique `jobid`, fallback to `__ref` - const jobId = readField("jobid", jobConversation) || jobConversation.__ref; - if (jobId) merged.set(jobId, jobConversation); - }); - - // Add or replace with incoming data - incoming.forEach((jobConversation) => { - // Use `readField` to get the unique `jobid`, fallback to `__ref` - const jobId = readField("jobid", jobConversation) || jobConversation.__ref; - if (jobId) merged.set(jobId, jobConversation); - }); - - // Return the merged data as an array - return Array.from(merged.values()); - } - } - } - } - } -}); +const cache = new InMemoryCache({}); const client = new ApolloClient({ link: ApolloLink.from(middlewares), cache, diff --git a/client/src/utils/TemplateConstants.js b/client/src/utils/TemplateConstants.js index 9f80986d2..c193dbb49 100644 --- a/client/src/utils/TemplateConstants.js +++ b/client/src/utils/TemplateConstants.js @@ -2184,6 +2184,19 @@ export const TemplateList = (type, context) => { }, group: "payroll", adp_payroll: true + }, + purchase_return_ratio_excel: { + title: i18n.t("reportcenter.templates.purchase_return_ratio_excel"), + subject: i18n.t("reportcenter.templates.purchase_return_ratio_excel"), + key: "purchase_return_ratio_excel", + //idtype: "vendor", + reporttype: "excel", + disabled: false, + rangeFilter: { + object: i18n.t("reportcenter.labels.objects.bills"), + field: i18n.t("bills.fields.date") + }, + group: "purchases" } } : {}),