Merged in release/2024-09-06 (pull request #1691)

Release/2024 09 06
This commit is contained in:
Allan Carr
2024-09-07 01:08:25 +00:00
16 changed files with 493 additions and 465 deletions

View File

@@ -5,6 +5,7 @@ orbs:
aws-s3: circleci/aws-s3@4.0.0
aws-cli: circleci/aws-cli@4.0
eb: circleci/aws-elastic-beanstalk@2.0.1
jira: circleci/jira@2.1.0
jobs:
imex-api-deploy:
docker:
@@ -18,6 +19,12 @@ jobs:
eb status --verbose
eb deploy
eb status
- jira/notify:
environment: Production (ImEX) - API
environment_type: production
job_type: deployment
pipeline_id: << pipeline.id >>
pipeline_number: << pipeline.number >>
imex-hasura-migrate:
docker:
@@ -33,11 +40,16 @@ jobs:
- run:
name: Execute migration
command: |
npm install hasura-cli -g
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
hasura migrate apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.imex.online/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Production (ImEX) - Hasura
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
imex-app-build:
docker:
- image: cimg/node:18.18.2
@@ -62,6 +74,7 @@ jobs:
to: "s3://imex-online-production/"
arguments: "--exclude '*.map'"
imex-app-beta-build:
docker:
- image: cimg/node:18.18.2
@@ -86,6 +99,12 @@ jobs:
from: dist
to: "s3://imex-online-beta/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Production (ImEX) - Front End
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
rome-api-deploy:
docker:
@@ -99,7 +118,12 @@ jobs:
eb status --verbose
eb deploy
eb status
- jira/notify:
environment: Production (Rome) - API
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
rome-hasura-migrate:
docker:
- image: cimg/node:18.18.2
@@ -114,11 +138,16 @@ jobs:
- run:
name: Execute migration
command: |
npm install hasura-cli -g
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
hasura migrate apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.romeonline.io/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Production (Rome) - Hasura
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
rome-app-build:
docker:
- image: cimg/node:18.18.2
@@ -143,6 +172,12 @@ jobs:
from: dist
to: "s3://rome-online-production/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Production (Rome) - Front End
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
promanager-app-build:
docker:
@@ -168,6 +203,12 @@ jobs:
from: dist
to: "s3://promanager-production/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Production (ProManager) - Front End
environment_type: production
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-rome-hasura-migrate:
docker:
@@ -183,10 +224,16 @@ jobs:
- run:
name: Execute migration
command: |
npm install hasura-cli -g
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
hasura migrate apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.test.romeonline.io/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Test (Rome) - Hasura
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-rome-app-build:
docker:
@@ -212,6 +259,12 @@ jobs:
from: dist
to: "s3://rome-online-test/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Test (Rome) - Front End
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-promanager-app-build:
docker:
@@ -237,6 +290,12 @@ jobs:
from: dist
to: "s3://promanager-testing/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Test (ProManager) - Front End
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
test-hasura-migrate:
docker:
@@ -252,10 +311,16 @@ jobs:
- run:
name: Execute migration
command: |
npm install hasura-cli -g
curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash
hasura migrate apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
hasura metadata apply --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
hasura metadata reload --endpoint https://db.test.bodyshop.app/ --admin-secret << parameters.secret >>
- jira/notify:
environment: Test (ImEX) - Hasura
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
imex-test-app-build:
docker:
@@ -302,7 +367,12 @@ jobs:
from: dist
to: "s3://imex-online-test-beta/"
arguments: "--exclude '*.map'"
- jira/notify:
environment: Test (ImEX) - Front End
environment_type: testing
pipeline_id: << pipeline.id >>
job_type: deployment
pipeline_number: << pipeline.number >>
admin-app-build:
docker:

View File

@@ -1,5 +1,5 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
VITE_APP_GA_CODE=231099835
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test

View File

@@ -1,5 +1,5 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
VITE_APP_GA_CODE=231099835
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test

View File

@@ -1,7 +1,8 @@
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.bodyshop.app/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql
VITE_APP_GRAPHQL_ENDPOINT_WS=wss://db.dev.imex.online/v1/graphql
VITE_APP_GA_CODE=231099835
VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
# VITE_APP_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"}
VITE_APP_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"}
VITE_APP_CLOUDINARY_ENDPOINT_API=https://api.cloudinary.com/v1_1/io-test
VITE_APP_CLOUDINARY_ENDPOINT=https://res.cloudinary.com/io-test
VITE_APP_CLOUDINARY_API_KEY=957865933348715

View File

@@ -109,7 +109,8 @@
"vite-plugin-legacy": "^2.1.0",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-pwa": "^0.20.1",
"vite-plugin-style-import": "^2.0.0"
"vite-plugin-style-import": "^2.0.0",
"workbox-window": "^7.1.0"
},
"engines": {
"node": ">=18.18.2"
@@ -18429,6 +18430,7 @@
"resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.1.0.tgz",
"integrity": "sha512-ZHeROyqR+AS5UPzholQRDttLFqGMwP0Np8MKWAdyxsDETxq3qOAyXvqessc3GniohG6e0mAqSQyKOHmT8zPF7g==",
"dev": true,
"license": "MIT",
"dependencies": {
"@types/trusted-types": "^2.0.2",
"workbox-core": "7.1.0"

View File

@@ -153,6 +153,7 @@
"vite-plugin-legacy": "^2.1.0",
"vite-plugin-node-polyfills": "^0.22.0",
"vite-plugin-pwa": "^0.20.1",
"vite-plugin-style-import": "^2.0.0"
"vite-plugin-style-import": "^2.0.0",
"workbox-window": "^7.1.0"
}
}

View File

@@ -6,11 +6,11 @@ import {
PauseCircleOutlined
} from "@ant-design/icons";
import { Card, Col, Row, Space, Tooltip } from "antd";
import Dinero from "dinero.js";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { DateTimeFormatter } from "../../utils/DateFormatter";
import Dinero from "dinero.js";
import ProductionAlert from "../production-list-columns/production-list-columns.alert.component";
import ProductionListColumnProductionNote from "../production-list-columns/production-list-columns.productionnote.component";
@@ -18,8 +18,8 @@ import ProductionSubletsManageComponent from "../production-sublets-manage/produ
import dayjs from "../../utils/day";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
import JobPartsQueueCount from "../job-parts-queue-count/job-parts-queue-count.component";
import OwnerNameDisplay from "../owner-name-display/owner-name-display.component";
const cardColor = (ssbuckets, totalHrs) => {
const bucket = ssbuckets.find((bucket) => bucket.gte <= totalHrs && (!bucket.lt || bucket.lt > totalHrs));
@@ -213,21 +213,13 @@ const EstimatorToolTip = ({ metadata, cardSettings }) => {
};
const SubtotalTooltip = ({ metadata, cardSettings, t }) => {
const amount = metadata?.job_totals?.totals?.subtotal?.amount;
const dineroAmount = amount ? Dinero({ amount: parseInt(amount * 100) }).toFormat("0,0.00") : null;
const dineroAmount = Dinero(metadata?.job_totals?.totals?.subtotal ?? Dinero()).toFormat();
return (
cardSettings?.subtotal && (
<Col span={cardSettings.compact ? 24 : 12}>
<EllipsesToolTip
title={!!amount ? `${t("production.statistics.currency_symbol")}${dineroAmount}` : null}
kiosk={cardSettings.kiosk}
>
{!!amount ? (
<span>{`${t("production.statistics.currency_symbol")}${dineroAmount}`}</span>
) : (
<span>&nbsp;</span>
)}
<EllipsesToolTip title={`${dineroAmount}`} kiosk={cardSettings.kiosk}>
{dineroAmount}
</EllipsesToolTip>
</Col>
)

View File

@@ -32,17 +32,6 @@ const ProductionStatistics = ({ data, cardSettings, reducerData }) => {
return items.reduce((acc, item) => acc + (item[key]?.aggregate?.sum?.[subKey] || 0), 0);
};
const sumDineroAmounts = (items, key, getAmountFn) => {
return items.reduce(
(acc, item) => {
const amount = getAmountFn(item, key);
const dineroAmount = Dinero(amount ?? 0);
return acc.add(dineroAmount);
},
Dinero({ amount: 0 })
);
};
const calculateTotalAmount = (items, key) => {
return items.reduce((acc, item) => acc.add(Dinero(item[key]?.totals?.subtotal ?? Dinero())), Dinero({ amount: 0 }));
};

View File

@@ -30,219 +30,226 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
return (
<RbacWrapper action="shop:rbac">
<LayoutFormRow>
{...HasFeatureAccess({ featureName: "export", bodyshop }) ? [
<Form.Item
label={t("bodyshop.fields.rbac.accounting.exportlog")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:exportlog"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.accounting.payables")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:payables"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.accounting.payments")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:payments"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.accounting.receivables")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:receivables"]}
>
<InputNumber />
</Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "bills", bodyshop }) ? [
<Form.Item
label={t("bodyshop.fields.rbac.bills.delete")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:delete"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.enter")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:enter"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:list"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.reexport")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:reexport"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:view"]}
>
<InputNumber />
</Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "courtesycars", bodyshop }) ? [
<Form.Item
label={t("bodyshop.fields.rbac.contracts.create")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "contracts:create"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.contracts.detail")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "contracts:detail"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.contracts.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "contracts:list"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.courtesycar.create")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "courtesycar:create"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.courtesycar.detail")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "courtesycar:detail"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.courtesycar.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "courtesycar:list"]}
>
<InputNumber />
</Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "csi", bodyshop }) ? [
<Form.Item
label={t("bodyshop.fields.rbac.csi.export")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "csi:export"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.csi.page")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "csi:page"]}
>
<InputNumber />
</Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "export", bodyshop })
? [
<Form.Item
label={t("bodyshop.fields.rbac.accounting.exportlog")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:exportlog"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.accounting.payables")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:payables"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.accounting.payments")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:payments"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.accounting.receivables")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "accounting:receivables"]}
>
<InputNumber />
</Form.Item>
]
: []}
{...HasFeatureAccess({ featureName: "bills", bodyshop })
? [
<Form.Item
label={t("bodyshop.fields.rbac.bills.delete")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:delete"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.enter")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:enter"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:list"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.reexport")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:reexport"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.bills.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "bills:view"]}
>
<InputNumber />
</Form.Item>
]
: []}
{...HasFeatureAccess({ featureName: "courtesycars", bodyshop })
? [
<Form.Item
label={t("bodyshop.fields.rbac.contracts.create")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "contracts:create"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.contracts.detail")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "contracts:detail"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.contracts.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "contracts:list"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.courtesycar.create")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "courtesycar:create"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.courtesycar.detail")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "courtesycar:detail"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.courtesycar.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "courtesycar:list"]}
>
<InputNumber />
</Form.Item>
]
: []}
{...HasFeatureAccess({ featureName: "csi", bodyshop })
? [
<Form.Item
label={t("bodyshop.fields.rbac.csi.export")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "csi:export"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.csi.page")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "csi:page"]}
>
<InputNumber />
</Form.Item>
]
: []}
<Form.Item
label={t("bodyshop.fields.rbac.employees.page")}
rules={[
@@ -255,6 +262,18 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employee_teams.page")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "employee_teams:page"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.jobs.admin")}
rules={[
@@ -435,31 +454,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employees.page")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "employees:page"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.employee_teams.page")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "employee_teams:page"]}
>
<InputNumber />
</Form.Item>
<Form.Item
label={t("bodyshop.fields.rbac.payments.enter")}
rules={[
@@ -522,7 +516,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber />
</Form.Item>
)}
<Form.Item
label={t("bodyshop.fields.rbac.production.list")}
rules={[
@@ -561,128 +554,118 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber />
</Form.Item>
)}
{...HasFeatureAccess({ featureName: "timetickets", bodyshop }) ? [
<Form.Item
label={t("bodyshop.fields.rbac.shiftclock.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "shiftclock:view"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.shop.config")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "shop:config"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.edit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:edit"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:editcommitted"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "ttapprovals:view"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "ttapprovals:approve"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.enter")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:enter"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:list"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>
]:[]}
{...HasFeatureAccess({ featureName: "timetickets", bodyshop })
? [
<Form.Item
label={t("bodyshop.fields.rbac.shiftclock.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "shiftclock:view"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.shop.config")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "shop:config"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.edit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:edit"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.shiftedit")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:shiftedit"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.editcommitted")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:editcommitted"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.view")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "ttapprovals:view"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.ttapprovals.approve")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "ttapprovals:approve"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.enter")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:enter"]}
>
<InputNumber />
</Form.Item>,
<Form.Item
label={t("bodyshop.fields.rbac.timetickets.list")}
rules={[
{
required: true
//message: t("general.validation.required"),
}
]}
name={["md_rbac", "timetickets:list"]}
>
<InputNumber />
</Form.Item>
]
: []}
<Form.Item
label={t("bodyshop.fields.rbac.shop.vendors")}
rules={[
@@ -757,7 +740,6 @@ export function ShopInfoRbacComponent({ form, bodyshop }) {
<InputNumber />
</Form.Item>
)}
<Form.Item
label={t("bodyshop.fields.rbac.users.editaccess")}
rules={[

View File

@@ -12,7 +12,7 @@ import { DateFormatter, DateTimeFormatter } from "../../utils/DateFormatter";
import { onlyUnique } from "../../utils/arrayHelper";
import dayjs from "../../utils/day";
import { alphaSort, dateSort } from "../../utils/sorters";
import RbacWrapper, { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
import { HasRbacAccess } from "../rbac-wrapper/rbac-wrapper.component";
import TimeTicketEnterButton from "../time-ticket-enter-button/time-ticket-enter-button.component";
const mapStateToProps = createStructuredSelector({
@@ -52,6 +52,10 @@ export function TimeTicketList({
splitKey: bodyshop.imexshopid
});
const canEditCommittedTimeTickets = HasRbacAccess({ bodyshop, authLevel, action: "timetickets:editcommitted" });
const canEditTimeTickets = HasRbacAccess({ bodyshop, authLevel, action: "timetickets:edit" });
const canEditShiftTickets = HasRbacAccess({ bodyshop, authLevel, action: "timetickets:shiftedit" });
const totals = useMemo(() => {
if (timetickets)
return timetickets.reduce(
@@ -65,6 +69,18 @@ export function TimeTicketList({
return { productivehrs: 0, actualhrs: 0 };
}, [timetickets]);
const isDisabled = (record) => {
if (disabled === true || !record.id) return true;
const isShiftTicket = !record.ciecacode;
const isCommitted = record.committed_at;
if (isShiftTicket) {
return !(canEditShiftTickets && (!isCommitted || canEditCommittedTimeTickets));
}
return !(canEditTimeTickets && (!isCommitted || canEditCommittedTimeTickets));
};
const columns = [
...(Enhanced_Payroll.treatment === "on"
? [
@@ -241,59 +257,16 @@ export function TimeTicketList({
</TimeTicketEnterButton>
)}
{!techConsole && (
<RbacWrapper
action="timetickets:edit"
noauth={() => {
return <div />;
<TimeTicketEnterButton
actions={{ refetch }}
context={{
id: record.id,
timeticket: record
}}
disabled={isDisabled(record)}
>
<TimeTicketEnterButton
actions={{ refetch }}
context={{
id: record.id,
timeticket: record
}}
disabled={
record.ciecacode
? record.committed_at
? HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:editcommitted"
}) &&
HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:edit"
})
: HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:edit"
})
: record.committed_at
? HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:editcommitted"
}) &&
HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:shiftedit"
})
: HasRbacAccess({
bodyshop,
authLevel: authLevel,
action: "timetickets:shiftedit"
})
? disabled
: !record.jobid
}
>
<EditFilled />
</TimeTicketEnterButton>
</RbacWrapper>
<EditFilled />
</TimeTicketEnterButton>
)}
</Space>
)

View File

@@ -329,7 +329,9 @@ export function LaborAllocationContainer({ jobid, loading, lineTicketData, hideT
timetickets={lineTicketData.timetickets}
adjustments={lineTicketData.jobs_by_pk.lbr_adjustments}
/>
{!hideTimeTickets && <TimeTicketList loading={loading} timetickets={lineTicketData.timetickets} techConsole />}
{!hideTimeTickets && (
<TimeTicketList loading={loading} timetickets={jobid ? lineTicketData.timetickets : []} techConsole />
)}
</div>
);
}

View File

@@ -31,7 +31,7 @@ export function UpdateAlert({ updateAvailable }) {
() => {
r.update();
},
10 * 60 * 1000
30 * 60 * 1000
);
}
},

View File

@@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next";
import RbacWrapperComponent from "../../components/rbac-wrapper/rbac-wrapper.component";
import TechLookupJobsList from "../../components/tech-lookup-jobs-list/tech-lookup-jobs-list.component";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
export default function TechLookupContainer() {
const { t } = useTranslation();
@@ -20,6 +21,7 @@ export default function TechLookupContainer() {
return (
<div>
<RbacWrapperComponent action="jobs:list-active">
<TechLookupJobsDrawer />
<TechLookupJobsList />
</RbacWrapperComponent>
</div>

View File

@@ -9,7 +9,6 @@ import ErrorBoundary from "../../components/error-boundary/error-boundary.compon
import FeatureWrapper from "../../components/feature-wrapper/feature-wrapper.component";
import LoadingSpinner from "../../components/loading-spinner/loading-spinner.component";
import TechHeader from "../../components/tech-header/tech-header.component";
import TechLookupJobsDrawer from "../../components/tech-lookup-jobs-drawer/tech-lookup-jobs-drawer.component";
import TechSider from "../../components/tech-sider/tech-sider.component";
import UpdateAlert from "../../components/update-alert/update-alert.component";
import { selectTechnician } from "../../redux/tech/tech.selectors";
@@ -68,7 +67,7 @@ export function TechPage({ technician }) {
<Layout>
<UpdateAlert />
<TechHeader />
<TechLookupJobsDrawer />
<TaskUpsertModalContainer />
<Content className="tech-content-container">
<ErrorBoundary>

View File

@@ -10,7 +10,7 @@ import {
signInWithEmailAndPassword,
signOut
} from "firebase/auth";
import { doc, getDoc, setDoc } from "firebase/firestore";
import { arrayUnion, doc, getDoc, setDoc, updateDoc } from "firebase/firestore";
import { getToken } from "firebase/messaging";
import i18next from "i18next";
import LogRocket from "logrocket";
@@ -48,6 +48,7 @@ import {
validatePasswordResetSuccess
} from "./user.actions";
import UserActionTypes from "./user.types";
import cleanAxios from "../../utils/CleanAxios";
const fpPromise = FingerprintJS.load();
@@ -177,10 +178,24 @@ export function* setInstanceIdSaga({ payload: uid }) {
// Get the visitor identifier when you need it.
const fp = yield fpPromise;
const result = yield fp.get();
yield setDoc(userInstanceRef, {
timestamp: new Date(),
fingerprint: result.visitorId
});
const res = yield cleanAxios.get("https://api.ipify.org/?format=json");
const udoc = yield getDoc(userInstanceRef);
if (!udoc.data()) {
yield setDoc(userInstanceRef, {
timestamp: new Date(),
fingerprint: result.visitorId,
//totalFingerprint: result,
ip: [res.data.ip]
});
} else {
yield updateDoc(userInstanceRef, {
timestamp: new Date(),
fingerprint: result.visitorId,
//totalFingerprint: result,
ip: arrayUnion(res.data.ip)
});
}
yield put(setLocalFingerprint(result.visitorId));
yield delay(5 * 60 * 1000);

View File

@@ -1,5 +1,5 @@
version: 2
endpoint: https://db.dev.bodyshop.app
endpoint: https://db.dev.imex.online
admin_secret: Dev-BodyShopApp!
metadata_directory: metadata
actions: