Merged in feature/IO-3020-IO-3036-imex-lite-rome-lite (pull request #2012)

feature/IO-3020-IO-3036-imex-lite-rome-lite

Approved-by: Patrick Fic
This commit is contained in:
Patrick Fic
2024-12-11 17:45:25 +00:00
8 changed files with 258 additions and 142 deletions

79
.gitattributes vendored
View File

@@ -1 +1,80 @@
# Ensure all text files use LF for line endings
* text eol=lf
# Binary files should not be modified by Git
*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.webp binary
*.svg binary
# Fonts
*.woff binary
*.woff2 binary
*.ttf binary
*.otf binary
*.eot binary
# Videos
*.mp4 binary
*.mov binary
*.avi binary
*.mkv binary
*.webm binary
# Audio
*.mp3 binary
*.wav binary
*.ogg binary
*.flac binary
# Archives and compressed files
*.zip binary
*.gz binary
*.tar binary
*.7z binary
*.rar binary
# PDF and documents
*.pdf binary
*.doc binary
*.docx binary
*.xls binary
*.xlsx binary
*.ppt binary
*.pptx binary
# Exclude JSON and other data files from text processing, if necessary
*.json text
*.xml text
*.csv text
# Scripts and code files should maintain LF endings
*.js text eol=lf
*.jsx text eol=lf
*.ts text eol=lf
*.tsx text eol=lf
*.css text eol=lf
*.scss text eol=lf
*.html text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.md text eol=lf
*.sh text eol=lf
*.py text eol=lf
*.rb text eol=lf
*.java text eol=lf
*.php text eol=lf
# Git configuration files
.gitattributes text eol=lf
.gitignore text eol=lf
*.gitattributes text eol=lf
# Exclude some other potential binary files
*.db binary
*.sqlite binary
*.exe binary
*.dll binary

View File

@@ -47,7 +47,7 @@ export function JobDetailCards({ bodyshop, setPrintCenterContext }) {
md: "100%",
lg: "75%",
xl: "75%",
xxl: "60%"
xxl: "75%"
};
const drawerPercentage = selectedBreakpoint ? bpoints[selectedBreakpoint[0]] : "100%";

View File

@@ -3,6 +3,7 @@ import React from "react";
import { useTranslation } from "react-i18next";
import { GenerateThumbUrl } from "../jobs-documents-gallery/job-documents.utility";
import CardTemplate from "./job-detail-cards.template.component";
import UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
export default function JobDetailCardsDocumentsComponent({ loading, data }) {
const { t } = useTranslation();
@@ -20,15 +21,17 @@ export default function JobDetailCardsDocumentsComponent({ loading, data }) {
title={t("jobs.labels.cards.documents")}
extraLink={`/manage/jobs/${data.id}?tab=documents`}
>
{data.documents.length > 0 ? (
<Carousel autoplay>
{data.documents.map((item) => (
<img key={item.id} src={GenerateThumbUrl(item)} alt={item.name} />
))}
</Carousel>
) : (
<div>{t("documents.errors.nodocuments")}</div>
)}
<UpsellComponent disableMask upsell={upsellEnum().media.general}>
{data.documents.length > 0 ? (
<Carousel autoplay>
{data.documents.map((item) => (
<img key={item.id} src={GenerateThumbUrl(item)} alt={item.name} />
))}
</Carousel>
) : (
<div>{t("documents.errors.nodocuments")}</div>
)}
</UpsellComponent>
</CardTemplate>
);
}

View File

@@ -892,16 +892,16 @@ export function JobsDetailHeaderActions({
key: "postbills",
id: "job-actions-postbills",
disabled: !job.converted,
label: <LockerWrapperComponent featureName="bill">{t("jobs.actions.postbills")}</LockerWrapperComponent>,
label: <LockerWrapperComponent featureName="bills">{t("jobs.actions.postbills")}</LockerWrapperComponent>,
onClick: () => {
logImEXEvent("job_header_enter_bills");
setBillEnterContext({
actions: { refetch: refetch },
context: {
job: job
}
});
HasFeatureAccess({ featureName: "bills", bodyshop }) &&
setBillEnterContext({
actions: { refetch: refetch },
context: {
job: job
}
});
}
},

View File

@@ -133,7 +133,7 @@ function JobsDocumentsComponent({
{!hasMediaAccess && (
<Col span={24}>
<Card>
<UpsellComponent upsell={upsellEnum().media.general} />
<UpsellComponent disableMask upsell={upsellEnum().media.general} />
</Card>
</Col>
)}

View File

@@ -1,5 +1,5 @@
import { FileExcelFilled, SyncOutlined } from "@ant-design/icons";
import { Alert, Button, Card, Space } from "antd";
import { Alert, Button, Card, Col, Row, Space } from "antd";
import React, { useEffect, useState } from "react";
import { Gallery } from "react-grid-gallery";
import { useTranslation } from "react-i18next";
@@ -18,6 +18,7 @@ import JobsDocumentsLocalGallerySelectAllComponent from "./jobs-documents-local-
import Lightbox from "react-image-lightbox";
import "react-image-lightbox/style.css";
import { HasFeatureAccess } from "../feature-wrapper/feature-wrapper.component";
import UpsellComponent, { upsellEnum } from "../upsell/upsell.component";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
@@ -88,95 +89,117 @@ export function JobsDocumentsLocalGallery({
const hasMediaAccess = HasFeatureAccess({ bodyshop, featureName: "media" });
return (
<div>
<Space wrap>
<Button
onClick={() => {
if (job) {
if (invoice_number) {
getBillMedia({ jobid: job.id, invoice_number });
} else {
getJobMedia(job.id);
}
}
}}
>
<SyncOutlined />
</Button>
<a href={CreateExplorerLinkForJob({ jobid: job.id })}>
<Button>{t("documents.labels.openinexplorer")}</Button>
</a>
<JobsDocumentsLocalGalleryReassign jobid={job.id} />
<JobsDocumentsLocalGallerySelectAllComponent jobid={job.id} />
<JobsLocalGalleryDownloadButton job={job} />
<JobsDocumentsLocalDeleteButton jobid={job.id} />
</Space>
<Card>
<DocumentsLocalUploadComponent job={job} invoice_number={invoice_number} vendorid={vendorid} allowAllTypes />
</Card>
<Card title={t("jobs.labels.documents-images")}>
<Gallery
images={jobMedia.images}
onSelect={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
{...(optimized && {
customControls: [
<Alert style={{ margin: "4px" }} message={t("documents.labels.optimizedimage")} type="success" />
]
})}
onClick={(index) => {
setModalState({ open: true, index: index });
// const media = allMedia[job.id].find(
// (m) => m.optimized === item.src
// );
<Row gutter={[16, 16]}>
<Col span={24}>
<Space wrap>
<Button
onClick={() => {
if (job) {
if (invoice_number) {
getBillMedia({ jobid: job.id, invoice_number });
} else {
getJobMedia(job.id);
}
}
}}
>
<SyncOutlined />
</Button>
<a href={CreateExplorerLinkForJob({ jobid: job.id })}>
<Button>{t("documents.labels.openinexplorer")}</Button>
</a>
<JobsDocumentsLocalGalleryReassign jobid={job.id} />
<JobsDocumentsLocalGallerySelectAllComponent jobid={job.id} />
<JobsLocalGalleryDownloadButton job={job} />
<JobsDocumentsLocalDeleteButton jobid={job.id} />
</Space>
</Col>
{!hasMediaAccess && (
<Col span={24}>
<Card>
<UpsellComponent disableMask upsell={upsellEnum().media.general} />
</Card>
</Col>
)}
<Col span={24}>
<Card>
<DocumentsLocalUploadComponent
job={job}
invoice_number={invoice_number}
vendorid={vendorid}
allowAllTypes
/>
</Card>
</Col>
<Col span={24}>
<Card title={t("jobs.labels.documents-images")}>
<Gallery
images={jobMedia.images}
onSelect={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
{...(optimized && {
customControls: [
<Alert style={{ margin: "4px" }} message={t("documents.labels.optimizedimage")} type="success" />
]
})}
onClick={(index) => {
setModalState({ open: true, index: index });
// const media = allMedia[job.id].find(
// (m) => m.optimized === item.src
// );
// window.open(
// media ? media.fullsize : item.fullsize,
// "_blank",
// "toolbar=0,location=0,menubar=0"
// );
}}
/>
</Card>
<Card title={t("jobs.labels.documents-other")}>
<Gallery
images={jobMedia.other}
thumbnailStyle={() => {
return {
backgroundImage: <FileExcelFilled />,
height: "100%",
width: "100%",
cursor: "pointer"
};
}}
onClick={(index) => {
window.open(jobMedia.other[index].fullsize, "_blank", "toolbar=0,location=0,menubar=0");
}}
onSelect={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
/>
</Card>
{modalState.open && (
<Lightbox
mainSrc={jobMedia.images[modalState.index].fullsize}
nextSrc={jobMedia.images[(modalState.index + 1) % jobMedia.images.length].fullsize}
prevSrc={jobMedia.images[(modalState.index + jobMedia.images.length - 1) % jobMedia.images.length].fullsize}
onCloseRequest={() => setModalState({ open: false, index: 0 })}
onMovePrevRequest={() =>
setModalState({
...modalState,
index: (modalState.index + jobMedia.images.length - 1) % jobMedia.images.length
})
}
onMoveNextRequest={() =>
setModalState({
...modalState,
index: (modalState.index + 1) % jobMedia.images.length
})
}
/>
)}
// window.open(
// media ? media.fullsize : item.fullsize,
// "_blank",
// "toolbar=0,location=0,menubar=0"
// );
}}
/>
</Card>
</Col>
<Col span={24}>
<Card title={t("jobs.labels.documents-other")}>
<Gallery
images={jobMedia.other}
thumbnailStyle={() => {
return {
backgroundImage: <FileExcelFilled />,
height: "100%",
width: "100%",
cursor: "pointer"
};
}}
onClick={(index) => {
window.open(jobMedia.other[index].fullsize, "_blank", "toolbar=0,location=0,menubar=0");
}}
onSelect={(index, image) => {
toggleMediaSelected({ jobid: job.id, filename: image.filename });
}}
/>
</Card>
</Col>
{modalState.open && (
<Lightbox
mainSrc={jobMedia.images[modalState.index].fullsize}
nextSrc={jobMedia.images[(modalState.index + 1) % jobMedia.images.length].fullsize}
prevSrc={jobMedia.images[(modalState.index + jobMedia.images.length - 1) % jobMedia.images.length].fullsize}
onCloseRequest={() => setModalState({ open: false, index: 0 })}
onMovePrevRequest={() =>
setModalState({
...modalState,
index: (modalState.index + jobMedia.images.length - 1) % jobMedia.images.length
})
}
onMoveNextRequest={() =>
setModalState({
...modalState,
index: (modalState.index + 1) % jobMedia.images.length
})
}
/>
)}
</Row>
</div>
);
}

View File

@@ -1,4 +1,4 @@
import { Alert, Form, InputNumber, Switch } from "antd";
import { Alert, Form, Switch } from "antd";
import React from "react";
import { useTranslation } from "react-i18next";
import LayoutFormRow from "../layout-form-row/layout-form-row.component";

View File

@@ -1,5 +1,5 @@
import { useQuery } from "@apollo/client";
import { Col, Row, Typography } from "antd";
import { Card, Col, Row, Typography } from "antd";
import dayjs from "../../utils/day";
import React, { useEffect } from "react";
import { useTranslation } from "react-i18next";
@@ -14,6 +14,8 @@ import { QUERY_JOB_CHECKLISTS } from "../../graphql/jobs.queries";
import { setBreadcrumbs, setSelectedHeader } from "../../redux/application/application.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import InstanceRenderManager from "../../utils/instanceRenderMgr";
import FeatureWrapperComponent from "../../components/feature-wrapper/feature-wrapper.component";
import UpsellComponent, { upsellEnum } from "../../components/upsell/upsell.component";
const mapStateToProps = createStructuredSelector({
//currentUser: selectCurrentUser
@@ -73,38 +75,47 @@ export function JobsChecklistViewContainer({ bodyshop, setBreadcrumbs, setSelect
);
return (
<RbacWrapper action="jobs:checklist-view">
<Row gutter={[16, 16]}>
<Col span={12}>
<Typography.Title level={4}>{t("jobs.labels.intakechecklist")}</Typography.Title>
{data.jobs_by_pk.intakechecklist && data.jobs_by_pk.intakechecklist.form && (
<>
<JobChecklistForm
formItems={data.jobs_by_pk.intakechecklist && data.jobs_by_pk.intakechecklist.form}
type="intake"
job={data.jobs_by_pk}
readOnly
/>
<CompletedBy checklist={data.jobs_by_pk.intakechecklist} />
</>
)}
</Col>
<Col span={12}>
<Typography.Title level={4}>{t("jobs.labels.deliverchecklist")}</Typography.Title>
{data.jobs_by_pk.deliverchecklist && data.jobs_by_pk.deliverchecklist.form && (
<>
<JobChecklistForm
formItems={data.jobs_by_pk.deliverchecklist && data.jobs_by_pk.deliverchecklist.form}
type="deliver"
job={data.jobs_by_pk}
readOnly
/>
<CompletedBy checklist={data.jobs_by_pk.deliverchecklist} />
</>
)}
</Col>
</Row>
</RbacWrapper>
<FeatureWrapperComponent
featureName="checklist"
noauth={
<Card>
<UpsellComponent upsell={upsellEnum().checklist.general} />
</Card>
}
>
<RbacWrapper action="jobs:checklist-view">
<Row gutter={[16, 16]}>
<Col span={12}>
<Typography.Title level={4}>{t("jobs.labels.intakechecklist")}</Typography.Title>
{data.jobs_by_pk.intakechecklist && data.jobs_by_pk.intakechecklist.form && (
<>
<JobChecklistForm
formItems={data.jobs_by_pk.intakechecklist && data.jobs_by_pk.intakechecklist.form}
type="intake"
job={data.jobs_by_pk}
readOnly
/>
<CompletedBy checklist={data.jobs_by_pk.intakechecklist} />
</>
)}
</Col>
<Col span={12}>
<Typography.Title level={4}>{t("jobs.labels.deliverchecklist")}</Typography.Title>
{data.jobs_by_pk.deliverchecklist && data.jobs_by_pk.deliverchecklist.form && (
<>
<JobChecklistForm
formItems={data.jobs_by_pk.deliverchecklist && data.jobs_by_pk.deliverchecklist.form}
type="deliver"
job={data.jobs_by_pk}
readOnly
/>
<CompletedBy checklist={data.jobs_by_pk.deliverchecklist} />
</>
)}
</Col>
</Row>
</RbacWrapper>
</FeatureWrapperComponent>
);
}