diff --git a/_reference/dockerreadme.md b/_reference/dockerreadme.md index 9ae7c140e..d1201564d 100644 --- a/_reference/dockerreadme.md +++ b/_reference/dockerreadme.md @@ -4,139 +4,16 @@ This guide will walk you through the steps to configure your WSL2 (Windows Subsy ## Prerequisites -1. **Windows 10/11** with **WSL2** installed. -2. **Hyper-V** enabled on your system. If not, follow these steps to enable it: - - Open PowerShell as Administrator and run: - ```powershell - dism.exe /Online /Enable-Feature /All /FeatureName:Microsoft-Hyper-V - ``` - - Restart your computer. +1. **Windows 11** +2. **Docker Desktop For Windows (Latest Version) -3. A basic understanding of networking and WSL2 configuration. +# Docker Setup +Inside the root of the project exists the `docker-compose.yaml` file, you can simply run +`docker-compose up` to launch the backend. ---- - -## Step 1: Create an External Hyper-V Switch - -1. **Open Hyper-V Manager**: - - Press `Windows Key + X`, select `Hyper-V Manager`. - -2. **Create a Virtual Switch**: - - In the right-hand pane, click `Virtual Switch Manager`. - - Choose `External` and click `Create Virtual Switch`. - - Select your external network adapter (this is usually your Ethernet or Wi-Fi adapter). - - Give the switch a name (e.g., `WSL External Switch`), then click `Apply` and `OK`. - ---- - -## Step 2: Configure WSL2 to Use the External Hyper-V Switch - -Now that you've created the external virtual switch, follow these steps to configure your WSL2 instance to use this switch. - -1. **Set WSL2 to Use the External Switch**: - - By default, WSL2 uses NAT to connect to your local network. You need to configure WSL2 to use the external Hyper-V switch instead. - -2. **Check WSL2 Networking**: - - Inside WSL, run: - ```bash - ip a - ``` - - You should see an IP address in the range of your local network (e.g., `192.168.x.x`). - ---- - -## Step 3: Configure a Static IP Address for WSL2 - -Once WSL2 is connected to the external network, you can assign a static IP address to your WSL2 instance. - -1. **Open WSL2** and Edit the Network Configuration: - - Depending on your Linux distribution, the file paths may vary, but typically for Ubuntu-based systems: - ```bash - sudo nano /etc/netplan/01-netcfg.yaml - ``` - - If this file doesn’t exist, create a new file or use the correct configuration file path. - -2. **Configure Static IP**: - - Add or update the following configuration: - ```yaml - network: - version: 2 - renderer: networkd - ethernets: - eth0: - dhcp4: no - addresses: - - 192.168.1.100/24 # Choose an IP address in your network range - gateway4: 192.168.1.1 # Your router's IP address - nameservers: - addresses: - - 8.8.8.8 - - 8.8.4.4 - ``` - - Adjust the values according to your local network settings: - - `addresses`: This is the static IP you want to assign. - - `gateway4`: This should be the IP address of your router. - - `nameservers`: These are DNS servers, you can use Google's public DNS or any other DNS provider. - -3. **Apply the Changes**: - - Run the following command to apply the network configuration: - ```bash - sudo netplan apply - ``` - -4. **Verify the Static IP**: - - Check if the static IP is correctly set by running: - ```bash - ip a - ``` - - You should see the static IP you configured (e.g., `192.168.1.100`) on the appropriate network interface (usually `eth0`). - ---- - -## Step 4: Restart WSL2 to Apply Changes - -To ensure the changes are fully applied, restart WSL2: - -1. Open PowerShell or Command Prompt and run: - ```powershell - wsl --shutdown -2. Then, start your WSL2 instance again. - -## Step 5: Verify Connectivity - -1. Check Internet and Local Network Connectivity: - - Run a ping command from within WSL to verify that it can reach the internet: ```ping 8.8.8.8``` -2. Test Access from other Devices: - - If you're running services inside WSL (e.g., a web server), ensure they are accessible from other devices on your local network using the static IP address you configured (e.g., `http://192.168.1.100:4000`). - - - -# Configuring `vm.overcommit_memory` in sysctl for WSL2 - -To prevent memory overcommitment issues and optimize performance, you can configure the `vm.overcommit_memory` setting in WSL2. This is particularly useful when running Redis or other memory-intensive services inside WSL2, as it helps control how the Linux kernel handles memory allocation. - -### 1. **Open the sysctl Configuration File**: -To set the `vm.overcommit_memory` value, you'll need to edit the sysctl configuration file. Inside your WSL2 instance, run the following command to open the `sysctl.conf` file for editing: -```bash - sudo nano /etc/sysctl.conf -``` -### 2. Add the Overcommit Memory Setting: -Add the following line at the end of the file to allow memory overcommitment: -```bash -vm.overcommit_memory = 1 -``` - -This setting tells the Linux kernel to always allow memory allocation, regardless of how much memory is available, which can prevent out-of-memory errors when running certain applications. - -### 3. Apply the Changes: -After editing the file, save it and then apply the new sysctl configuration by running: - -```bash -sudo sysctl -p -``` - -# Install Docker and Docker Compose in WSL2 -- https://docs.docker.com/desktop/wsl/ +Things to note: +- When installing NPM packages, you will need to rebuild the `node-app` container +- Making changes to the server files will restart the `node-app` # Local Stack - LocalStack Front end (Optional) - https://apps.microsoft.com/detail/9ntrnft9zws2?hl=en-us&gl=US diff --git a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx index f501b3dbb..1e21ebe34 100644 --- a/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx +++ b/client/src/components/accounting-payables-table/accounting-payables-table.component.jsx @@ -9,7 +9,7 @@ import { logImEXEvent } from "../../firebase/firebase.utils"; import { selectBodyshop } from "../../redux/user/user.selectors"; import CurrencyFormatter from "../../utils/CurrencyFormatter"; import { DateFormatter } from "../../utils/DateFormatter"; -import { pageLimit } from "../../utils/config"; +import { exportPageLimit } from "../../utils/config"; import { alphaSort, dateSort } from "../../utils/sorters"; import ExportLogsCountDisplay from "../export-logs-count-display/export-logs-count-display.component"; import PayableExportAll from "../payable-export-all-button/payable-export-all-button.component"; @@ -175,7 +175,7 @@ export function AccountingPayablesTableComponent({ bodyshop, loading, bills, ref setData([])}> + { + if (e.key !== "Enter") return; + const firstUrlForSearch = data?.[0]?.options?.[0]?.label?.props?.to; + if (!firstUrlForSearch) return; + navigate(firstUrlForSearch); + }} + defaultActiveFirstOption + onClear={() => setData([])} + > { if (v && v.variables.search && v.variables.search !== "" && v.variables.search.length >= 3) callSearch(v); @@ -20,7 +21,6 @@ export default function GlobalSearch() { const debouncedExecuteSearch = _.debounce(executeSearch, 750); const handleSearch = (value) => { - console.log("Handle Search"); debouncedExecuteSearch({ variables: { search: value } }); }; @@ -156,7 +156,17 @@ export default function GlobalSearch() { if (error) return ; return ( - + { + if (e.key !== "Enter") return; + const firstUrlForSearch = options?.[0]?.options?.[0]?.label?.props?.to; + if (!firstUrlForSearch) return; + navigate(firstUrlForSearch); + }} + > = 0 ? 1 : -1) + .toFormat(0.0) }) - .percentage(Math.abs(values.prt_dsmk_p || 0)) - .multiply(values.prt_dsmk_p >= 0 ? 1 : -1) - .toFormat(0.0) } }, refetchQueries: ["GET_LINE_TICKET_BY_PK"] diff --git a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx index 31234c1d1..18eeef23f 100644 --- a/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx +++ b/client/src/components/parts-order-list-table/parts-order-list-table.component.jsx @@ -242,7 +242,8 @@ export function PartsOrderListTableComponent({ title: t("general.labels.actions"), dataIndex: "actions", key: "actions", - render: (text, record) => recordActions(record, true) + render: (text, record) => recordActions(record, true), + id: "parts-order-list-table-actions" } ]; diff --git a/client/src/components/production-board-kanban/production-board-kanban.component.jsx b/client/src/components/production-board-kanban/production-board-kanban.component.jsx index 0c515132b..ef10f99b9 100644 --- a/client/src/components/production-board-kanban/production-board-kanban.component.jsx +++ b/client/src/components/production-board-kanban/production-board-kanban.component.jsx @@ -185,7 +185,7 @@ function ProductionBoardKanbanComponent({ data, bodyshop, refetch, insertAuditTr const cardSettings = useMemo(() => { const kanbanSettings = associationSettings?.kanban_settings; return mergeWithDefaults(kanbanSettings); - }, [associationSettings]); + }, [associationSettings?.kanban_settings]); const handleSettingsChange = () => { setFilter(defaultFilters); diff --git a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js index 87b364ea2..c9a8c59c6 100644 --- a/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js +++ b/client/src/components/production-board-kanban/settings/defaultKanbanSettings.js @@ -1,15 +1,66 @@ +import InstanceRenderManager from "../../../utils/instanceRenderMgr.js"; + const statisticsItems = [ { id: 0, name: "totalHrs", label: "total_hours_in_production" }, { id: 1, name: "totalAmountInProduction", label: "total_amount_in_production" }, { id: 2, name: "totalLAB", label: "total_lab_in_production" }, { id: 3, name: "totalLAR", label: "total_lar_in_production" }, { id: 4, name: "jobsInProduction", label: "jobs_in_production" }, - { id: 5, name: "totalHrsOnBoard", label: "total_hours_on_board" }, - { id: 6, name: "totalAmountOnBoard", label: "total_amount_on_board" }, - { id: 7, name: "totalLABOnBoard", label: "total_lab_on_board" }, - { id: 8, name: "totalLAROnBoard", label: "total_lar_on_board" }, - { id: 9, name: "jobsOnBoard", label: "total_jobs_on_board" }, - { id: 10, name: "tasksOnBoard", label: "tasks_on_board" }, + + { + id: 5, + name: "totalHrsOnBoard", + label: InstanceRenderManager({ + imex: "total_hours_in_view", + rome: "total_hours_on_board", + promanager: "total_hours_on_board" + }) + }, + { + id: 6, + name: "totalAmountOnBoard", + label: InstanceRenderManager({ + imex: "total_amount_in_view", + rome: "total_amount_on_board", + promanager: "total_amount_on_board" + }) + }, + { + id: 7, + name: "totalLABOnBoard", + label: InstanceRenderManager({ + imex: "total_lab_in_view", + rome: "total_lab_on_board", + promanager: "total_lab_on_board" + }) + }, + { + id: 8, + name: "totalLAROnBoard", + label: InstanceRenderManager({ + imex: "total_lar_in_view", + rome: "total_lar_on_board", + promanager: "total_lar_on_board" + }) + }, + { + id: 9, + name: "jobsOnBoard", + label: InstanceRenderManager({ + imex: "total_jobs_in_view", + rome: "total_jobs_on_board", + promanager: "total_jobs_on_board" + }) + }, + { + id: 10, + name: "tasksOnBoard", + label: InstanceRenderManager({ + imex: "tasks_in_view", + rome: "tasks_on_board", + promanager: "tasks_on_board" + }) + }, { id: 11, name: "tasksInProduction", label: "tasks_in_production" } ]; diff --git a/client/src/components/production-list-columns/production-list-columns.status.component.jsx b/client/src/components/production-list-columns/production-list-columns.status.component.jsx index 23dc378ef..820f7f80a 100644 --- a/client/src/components/production-list-columns/production-list-columns.status.component.jsx +++ b/client/src/components/production-list-columns/production-list-columns.status.component.jsx @@ -21,25 +21,26 @@ export function ProductionListColumnStatus({ record, bodyshop, insertAuditTrail const [loading, setLoading] = useState(false); const handleSetStatus = async (e) => { - logImEXEvent("production_change_status"); - // e.stopPropagation(); - setLoading(true); - const { key } = e; - await updateJob({ - variables: { - jobId: record.id, - job: { - status: key + if (bodyshop.md_ro_statuses.production_statuses.includes(record.status) && !bodyshop.md_ro_statuses.post_production_statuses.includes(record.status)) { + logImEXEvent("production_change_status"); + // e.stopPropagation(); + setLoading(true); + const { key } = e; + await updateJob({ + variables: { + jobId: record.id, + job: { + status: key + } } - } - }); - insertAuditTrail({ - jobid: record.id, - operation: AuditTrailMapping.jobstatuschange(key), - type: "jobstatuschange" - }); - - setLoading(false); + }); + insertAuditTrail({ + jobid: record.id, + operation: AuditTrailMapping.jobstatuschange(key), + type: "jobstatuschange" + }); + setLoading(false); + } }; const menu = { diff --git a/client/src/components/production-list-table/production-list-config-manager.component.jsx b/client/src/components/production-list-table/production-list-config-manager.component.jsx index b34fb92d7..ef8b07af7 100644 --- a/client/src/components/production-list-table/production-list-config-manager.component.jsx +++ b/client/src/components/production-list-table/production-list-config-manager.component.jsx @@ -457,41 +457,42 @@ export function ProductionListConfigManager({ value={activeView} disabled={open || isAddingNewProfile} // Disable the Select box when the popover is open or adding a new profile > - {bodyshop.production_config - .slice() - .sort((a, b) => - a.name === t("production.constants.main_profile") - ? -1 - : b.name === t("production.constants.main_profile") - ? 1 - : 0 - ) // - .map((config) => ( - -
- - {config.name} - - {config.name !== t("production.constants.main_profile") && ( - handleTrash(config.name)} - onCancel={(e) => e.stopPropagation()} + {bodyshop?.production_config && + bodyshop.production_config + .slice() + .sort((a, b) => + a.name === t("production.constants.main_profile") + ? -1 + : b.name === t("production.constants.main_profile") + ? 1 + : 0 + ) // + .map((config) => ( + +
+ - e.stopPropagation()} /> - - )} -
-
- ))} + {config.name} + + {config.name !== t("production.constants.main_profile") && ( + handleTrash(config.name)} + onCancel={(e) => e.stopPropagation()} + > + e.stopPropagation()} /> + + )} +
+
+ ))}
diff --git a/client/src/components/production-list-table/production-list-table.component.jsx b/client/src/components/production-list-table/production-list-table.component.jsx index ea3d9c2a2..c8763ad13 100644 --- a/client/src/components/production-list-table/production-list-table.component.jsx +++ b/client/src/components/production-list-table/production-list-table.component.jsx @@ -51,8 +51,8 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici const initialColumnsRef = useRef( (initialStateRef.current && - bodyshop.production_config - .find((p) => p.name === defaultView) + bodyshop?.production_config + ?.find((p) => p.name === defaultView) ?.columns.columnKeys.map((k) => { return { ...ProductionListColumns({ @@ -76,8 +76,8 @@ export function ProductionListTable({ loading, data, refetch, bodyshop, technici const { t } = useTranslation(); const matchingColumnConfig = useMemo(() => { - return bodyshop.production_config.find((p) => p.name === defaultView); - }, [bodyshop.production_config, defaultView]); + return bodyshop?.production_config?.find((p) => p.name === defaultView); + }, [bodyshop.production_config]); useEffect(() => { const newColumns = diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json index bd4738eb8..bdc45df6e 100644 --- a/client/src/translations/en_us/common.json +++ b/client/src/translations/en_us/common.json @@ -119,7 +119,7 @@ "jobclosedwithbypass": "Job was invoiced using the master bypass password. ", "jobconverted": "Job converted and assigned number {{ro_number}}.", "jobdelivery": "Job intake completed. Status set to {{status}}. Actual completion is {{actual_completion}}.", - "jobexported": "", + "jobexported": "Job has been exported", "jobfieldchanged": "Job field $t(jobs.fields.{{field}}) changed to {{value}}.", "jobimported": "Job imported.", "jobinproductionchange": "Job production status set to {{inproduction}}", @@ -2849,15 +2849,21 @@ "jobs_in_production": "Jobs in Production", "tasks_in_production": "Tasks in Production", "tasks_on_board": "Tasks on Board", + "tasks_in_view": "Tasks in View", "total_amount_in_production": "Dollars in Production", "total_amount_on_board": "Dollars on Board", + "total_amount_in_view": "Dollars in View", "total_hours_in_production": "Hours in Production", "total_hours_on_board": "Hours on Board", + "total_hours_in_view": "Hours in View", "total_jobs_on_board": "Jobs on Board", + "total_jobs_in_view": "Jobs in View", "total_lab_in_production": "Body Hours in Production", "total_lab_on_board": "Body Hours on Board", + "total_lab_in_view": "Body Hours in View", "total_lar_in_production": "Refinish Hours in Production", - "total_lar_on_board": "Refinish Hours on Board" + "total_lar_on_board": "Refinish Hours on Board", + "total_lar_in_view": "Refinish Hours in View" }, "statistics_title": "Statistics" }, @@ -2869,15 +2875,21 @@ "tasks": "Tasks", "tasks_in_production": "Tasks in Production", "tasks_on_board": "Tasks on Board", + "tasks_in_view": "Tasks in View", "total_amount_in_production": "Dollars in Production", "total_amount_on_board": "Dollars on Board", + "total_amount_in_view": "Dollars in View", "total_hours_in_production": "Hours in Production", "total_hours_on_board": "Hours on Board", + "total_hours_in_view": "Hours in View", "total_jobs_on_board": "Jobs on Board", + "total_jobs_in_view": "Jobs in View", "total_lab_in_production": "Body Hours in Production", "total_lab_on_board": "Body Hours on Board", + "total_lab_in_view": "Body Hours in View", "total_lar_in_production": "Refinish Hours in Production", - "total_lar_on_board": "Refinish Hours on Board" + "total_lar_on_board": "Refinish Hours on Board", + "total_lar_in_view": "Refinish Hours in View" }, "successes": { "removed": "Job removed from production." diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json index 6e9074c44..66d512cef 100644 --- a/client/src/translations/es/common.json +++ b/client/src/translations/es/common.json @@ -2849,15 +2849,21 @@ "jobs_in_production": "", "tasks_in_production": "", "tasks_on_board": "", + "tasks_in_view": "", "total_amount_in_production": "", "total_amount_on_board": "", + "total_amount_in_view": "", "total_hours_in_production": "", "total_hours_on_board": "", + "total_hours_in_view": "", "total_jobs_on_board": "", + "total_jobs_in_view": "", "total_lab_in_production": "", "total_lab_on_board": "", + "total_lab_in_view": "", "total_lar_in_production": "", - "total_lar_on_board": "" + "total_lar_on_board": "", + "total_lar_in_view": "" }, "statistics_title": "" }, @@ -2869,15 +2875,21 @@ "tasks": "", "tasks_in_production": "", "tasks_on_board": "", + "tasks_in_view": "", "total_amount_in_production": "", "total_amount_on_board": "", + "total_amount_in_view": "", "total_hours_in_production": "", "total_hours_on_board": "", + "total_hours_in_view": "", "total_jobs_on_board": "", + "total_jobs_in_view": "", "total_lab_in_production": "", "total_lab_on_board": "", + "total_lab_in_view": "", "total_lar_in_production": "", - "total_lar_on_board": "" + "total_lar_on_board": "", + "total_lar_in_view": "" }, "successes": { "removed": "" diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json index 48d5a05af..c4cdbf53f 100644 --- a/client/src/translations/fr/common.json +++ b/client/src/translations/fr/common.json @@ -2845,40 +2845,52 @@ "filters_title": "", "information": "", "layout": "", - "statistics": { - "jobs_in_production": "", - "tasks_in_production": "", - "tasks_on_board": "", - "total_amount_in_production": "", - "total_amount_on_board": "", - "total_hours_in_production": "", - "total_hours_on_board": "", - "total_jobs_on_board": "", - "total_lab_in_production": "", - "total_lab_on_board": "", - "total_lar_in_production": "", - "total_lar_on_board": "" - }, + "statistics": { + "jobs_in_production": "", + "tasks_in_production": "", + "tasks_on_board": "", + "tasks_in_view": "", + "total_amount_in_production": "", + "total_amount_on_board": "", + "total_amount_in_view": "", + "total_hours_in_production": "", + "total_hours_on_board": "", + "total_hours_in_view": "", + "total_jobs_on_board": "", + "total_jobs_in_view": "", + "total_lab_in_production": "", + "total_lab_on_board": "", + "total_lab_in_view": "", + "total_lar_in_production": "", + "total_lar_on_board": "", + "total_lar_in_view": "" + }, "statistics_title": "" }, - "statistics": { - "currency_symbol": "", - "hours": "", - "jobs": "", - "jobs_in_production": "", - "tasks": "", - "tasks_in_production": "", - "tasks_on_board": "", - "total_amount_in_production": "", - "total_amount_on_board": "", - "total_hours_in_production": "", - "total_hours_on_board": "", - "total_jobs_on_board": "", - "total_lab_in_production": "", - "total_lab_on_board": "", - "total_lar_in_production": "", - "total_lar_on_board": "" - }, + "statistics": { + "currency_symbol": "", + "hours": "", + "jobs": "", + "jobs_in_production": "", + "tasks": "", + "tasks_in_production": "", + "tasks_on_board": "", + "tasks_in_view": "", + "total_amount_in_production": "", + "total_amount_on_board": "", + "total_amount_in_view": "", + "total_hours_in_production": "", + "total_hours_on_board": "", + "total_hours_in_view": "", + "total_jobs_on_board": "", + "total_jobs_in_view": "", + "total_lab_in_production": "", + "total_lab_on_board": "", + "total_lab_in_view": "", + "total_lar_in_production": "", + "total_lar_on_board": "", + "total_lar_in_view": "" + }, "successes": { "removed": "" } diff --git a/client/src/utils/config.js b/client/src/utils/config.js index 06c6d2362..844872b7c 100644 --- a/client/src/utils/config.js +++ b/client/src/utils/config.js @@ -1,3 +1,4 @@ // Sometimes referred to as PageSize, this variable controls the amount of records // to show on one page during pagination. export const pageLimit = 50; +export const exportPageLimit = 10; diff --git a/client/src/utils/localmedia.js b/client/src/utils/localmedia.js index 7734d4b2b..4c776bc25 100644 --- a/client/src/utils/localmedia.js +++ b/client/src/utils/localmedia.js @@ -2,5 +2,5 @@ import { store } from "../redux/store"; export function CreateExplorerLinkForJob({ jobid }) { const bodyshop = store.getState().user.bodyshop; - return `imexmedia://${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`; + return `imexmedia://`.concat(encodeURIComponent(`${bodyshop.localmediaservernetwork}\\Jobs\\${jobid}`)); }