Label printing page updates
No longer a modal Use flex divs instead of a table
This commit is contained in:
@@ -192,6 +192,7 @@ function App() {
|
||||
/>
|
||||
<Route path="edit/:id" element={<LoadableResourcePage resource="spools" page="edit" />} />
|
||||
<Route path="show/:id" element={<LoadableResourcePage resource="spools" page="show" />} />
|
||||
<Route path="print" element={<LoadablePage name="printing" />} />
|
||||
</Route>
|
||||
<Route path="/filament">
|
||||
<Route index element={<LoadableResourcePage resource="filaments" page="list" />} />
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
// React FC that combines the functionality of first selecting what spools to print QR code for, and then opens up the QR code printing dialog.
|
||||
|
||||
import React from "react";
|
||||
import SpoolSelectModal from "./spoolSelectModal";
|
||||
import { Button } from "antd";
|
||||
import { ISpool } from "../pages/spools/model";
|
||||
import { PrinterOutlined } from "@ant-design/icons";
|
||||
import { useTranslate } from "@refinedev/core";
|
||||
import SpoolQRCodePrintingDialog from "./printing/spoolQrCodePrintingDialog";
|
||||
|
||||
const SelectAndPrint: React.FC = () => {
|
||||
const t = useTranslate();
|
||||
|
||||
const [step, setStep] = React.useState(0);
|
||||
const [selectedSpools, setSelectedSpools] = React.useState<ISpool[]>([]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PrinterOutlined />}
|
||||
onClick={() => {
|
||||
setStep(1);
|
||||
}}
|
||||
>
|
||||
{t("printing.qrcode.button")}
|
||||
</Button>
|
||||
<SpoolSelectModal
|
||||
visible={step === 1}
|
||||
description={t("printing.spoolSelect.description")}
|
||||
onCancel={() => {
|
||||
setStep(0);
|
||||
}}
|
||||
onContinue={(spools) => {
|
||||
setSelectedSpools(spools);
|
||||
setStep(2);
|
||||
}}
|
||||
/>
|
||||
<SpoolQRCodePrintingDialog
|
||||
visible={step === 2}
|
||||
onCancel={() => {
|
||||
setStep(0);
|
||||
}}
|
||||
items={selectedSpools}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectAndPrint;
|
||||
50
client/src/pages/printing/index.tsx
Normal file
50
client/src/pages/printing/index.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import React from "react";
|
||||
import { IResourceComponentsProps, useTranslate } from "@refinedev/core";
|
||||
import dayjs from "dayjs";
|
||||
import utc from "dayjs/plugin/utc";
|
||||
import { Content } from "antd/es/layout/layout";
|
||||
import { theme } from "antd";
|
||||
import SpoolQRCodePrintingDialog from "./spoolQrCodePrintingDialog";
|
||||
import SpoolSelectModal from "./spoolSelectModal";
|
||||
import { ISpool } from "../spools/model";
|
||||
|
||||
dayjs.extend(utc);
|
||||
|
||||
const { useToken } = theme;
|
||||
|
||||
export const Printing: React.FC<IResourceComponentsProps> = () => {
|
||||
const { token } = useToken();
|
||||
const t = useTranslate();
|
||||
|
||||
const [step, setStep] = React.useState<0 | 1>(0);
|
||||
const [selectedSpools, setSelectedSpools] = React.useState<ISpool[]>([]);
|
||||
|
||||
return (
|
||||
<Content
|
||||
style={{
|
||||
padding: 20,
|
||||
minHeight: 280,
|
||||
margin: "0 auto",
|
||||
backgroundColor: token.colorBgContainer,
|
||||
borderRadius: token.borderRadiusLG,
|
||||
color: token.colorText,
|
||||
fontFamily: token.fontFamily,
|
||||
fontSize: token.fontSizeLG,
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
{step === 0 && (
|
||||
<SpoolSelectModal
|
||||
description={t("printing.spoolSelect.description")}
|
||||
onContinue={(spools) => {
|
||||
setSelectedSpools(spools);
|
||||
setStep(1);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{step === 1 && <SpoolQRCodePrintingDialog items={selectedSpools} />}
|
||||
</Content>
|
||||
);
|
||||
};
|
||||
|
||||
export default Printing;
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { ReactNode } from "react";
|
||||
import { ISpool } from "../../pages/spools/model";
|
||||
import { ISpool } from "../spools/model";
|
||||
import { useGetSetting, useSetSetting } from "../../utils/querySettings";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
@@ -25,9 +25,6 @@ interface PrintingDialogProps {
|
||||
style?: string;
|
||||
extraSettings?: JSX.Element;
|
||||
extraSettingsStart?: JSX.Element;
|
||||
visible: boolean;
|
||||
onCancel: () => void;
|
||||
title?: string;
|
||||
}
|
||||
|
||||
interface PaperDimensions {
|
||||
@@ -69,9 +66,6 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
style,
|
||||
extraSettings,
|
||||
extraSettingsStart,
|
||||
visible,
|
||||
onCancel,
|
||||
title,
|
||||
}) => {
|
||||
const t = useTranslate();
|
||||
|
||||
@@ -98,7 +92,7 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
const itemHeight = (paperHeight - margin.top - margin.bottom - spacing.vertical) / paperRows - spacing.vertical;
|
||||
|
||||
const itemsPerRow = paperColumns;
|
||||
const rowsPerPage = paperRows;
|
||||
const itemsPerPage = itemsPerRow * paperRows;
|
||||
|
||||
const itemsIncludingSkipped = [...Array(skipItems).fill(<></>)];
|
||||
for (const item of items) {
|
||||
@@ -107,42 +101,33 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
const rowsOfItems = [];
|
||||
for (let row_idx = 0; row_idx < itemsIncludingSkipped.length / itemsPerRow; row_idx += 1) {
|
||||
rowsOfItems.push(itemsIncludingSkipped.slice(row_idx * itemsPerRow, (row_idx + 1) * itemsPerRow));
|
||||
}
|
||||
|
||||
const pageBlocks = [];
|
||||
for (let page_idx = 0; page_idx < rowsOfItems.length / rowsPerPage; page_idx += 1) {
|
||||
pageBlocks.push(rowsOfItems.slice(page_idx * rowsPerPage, (page_idx + 1) * rowsPerPage));
|
||||
for (let page_idx = 0; page_idx < itemsIncludingSkipped.length / itemsPerPage; page_idx += 1) {
|
||||
pageBlocks.push(itemsIncludingSkipped.slice(page_idx * itemsPerPage, (page_idx + 1) * itemsPerPage));
|
||||
}
|
||||
|
||||
const pages = pageBlocks.map(function (rows, pageIdx) {
|
||||
const itemRows = rows.map((row, rowIdx) => {
|
||||
const pages = pageBlocks.map(function (items, pageIdx) {
|
||||
const itemDivs = items.map((item, itemIdx) => {
|
||||
const isFirstColumn = itemIdx % itemsPerRow === 0;
|
||||
const isLastColumn = (itemIdx + 1) % itemsPerRow === 0;
|
||||
const isFirstRow = itemIdx < itemsPerRow;
|
||||
const isLastRow = itemsPerPage - itemIdx <= itemsPerRow;
|
||||
|
||||
return (
|
||||
<tr key={rowIdx}>
|
||||
{row.map(function (item, colIdx) {
|
||||
return (
|
||||
<td key={colIdx}>
|
||||
<div
|
||||
key={itemIdx}
|
||||
style={{
|
||||
width: `${itemWidth}mm`,
|
||||
height: `${itemHeight}mm`,
|
||||
border: borderShowMode === "grid" ? "1px solid #000" : "none",
|
||||
paddingLeft: colIdx === 0 ? `${Math.max(printerMargin.left - margin.left, 0)}mm` : 0,
|
||||
paddingRight:
|
||||
colIdx === paperColumns - 1 ? `${Math.max(printerMargin.right - margin.right, 0)}mm` : 0,
|
||||
paddingTop: rowIdx === 0 ? `${Math.max(printerMargin.top - margin.top, 0)}mm` : 0,
|
||||
paddingBottom:
|
||||
rowIdx === paperRows - 1 ? `${Math.max(printerMargin.bottom - margin.bottom, 0)}mm` : 0,
|
||||
paddingLeft: isFirstColumn ? `${Math.max(printerMargin.left - margin.left, 0)}mm` : 0,
|
||||
paddingRight: isLastColumn ? `${Math.max(printerMargin.right - margin.right, 0)}mm` : 0,
|
||||
paddingTop: isFirstRow ? `${Math.max(printerMargin.top - margin.top, 0)}mm` : 0,
|
||||
paddingBottom: isLastRow ? `${Math.max(printerMargin.bottom - margin.bottom, 0)}mm` : 0,
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</div>
|
||||
</td>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -160,7 +145,8 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
<div
|
||||
className="print-page-area"
|
||||
style={{
|
||||
border: borderShowMode !== "none" ? "1px solid #000" : "none",
|
||||
outline: borderShowMode !== "none" ? "1px solid #000" : "none",
|
||||
outlineOffset: "-1px",
|
||||
height: `${paperHeight - margin.top - margin.bottom}mm`,
|
||||
width: `${paperWidth - margin.left - margin.right}mm`,
|
||||
marginTop: `calc(${margin.top}mm - ${borderShowMode !== "none" ? "1px" : "0px"})`,
|
||||
@@ -169,35 +155,27 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
marginBottom: `calc(${margin.bottom}mm - ${borderShowMode !== "none" ? "1px" : "0px"})`,
|
||||
}}
|
||||
>
|
||||
<table
|
||||
<div
|
||||
style={{
|
||||
alignContent: "flex-start",
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
rowGap: `${spacing.vertical}mm`,
|
||||
columnGap: `${spacing.horizontal}mm`,
|
||||
paddingTop: `${spacing.vertical}mm`,
|
||||
paddingLeft: `${spacing.horizontal}mm`,
|
||||
}}
|
||||
>
|
||||
{itemRows}
|
||||
</table>
|
||||
{itemDivs}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={visible}
|
||||
title={title ?? t("printing.generic.title")}
|
||||
onCancel={onCancel}
|
||||
footer={[
|
||||
<ReactToPrint
|
||||
key="print-button"
|
||||
trigger={() => <Button type="primary">{t("printing.generic.print")}</Button>}
|
||||
content={() => printRef.current}
|
||||
/>,
|
||||
]}
|
||||
width={1200} // Set the modal width to accommodate the preview
|
||||
>
|
||||
<>
|
||||
<Row gutter={16}>
|
||||
<Col span={14}>
|
||||
{t("printing.generic.description")}
|
||||
<div
|
||||
style={{
|
||||
transform: "translateZ(0)",
|
||||
@@ -211,12 +189,17 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
style={{
|
||||
transform: `scale(${previewScale})`,
|
||||
transformOrigin: "top left",
|
||||
display: "block",
|
||||
}}
|
||||
>
|
||||
<style>
|
||||
{`
|
||||
@media print {
|
||||
html, body {
|
||||
height: initial !important;
|
||||
overflow: initial !important;
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: auto;
|
||||
margin: 0;
|
||||
@@ -235,14 +218,15 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
}
|
||||
}
|
||||
|
||||
.print-page table {
|
||||
border-spacing: ${spacing.horizontal}mm ${spacing.vertical}mm;
|
||||
border-collapse: separate;
|
||||
.print-page {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.print-page td {
|
||||
padding: 0;
|
||||
.print-page * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
${style ?? ""}
|
||||
`}
|
||||
</style>
|
||||
@@ -367,6 +351,8 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
{extraSettings}
|
||||
</Collapse.Panel>
|
||||
<Collapse.Panel header={t("printing.generic.layoutSettings")} key="2">
|
||||
{t("printing.generic.description")}
|
||||
<Divider />
|
||||
<Form.Item label={t("printing.generic.paperSize")}>
|
||||
<Select
|
||||
value={paperSize}
|
||||
@@ -786,7 +772,16 @@ const PrintingDialog: React.FC<PrintingDialogProps> = ({
|
||||
</Form>
|
||||
</Col>
|
||||
</Row>
|
||||
</Modal>
|
||||
<Row justify={"end"}>
|
||||
<Col>
|
||||
<ReactToPrint
|
||||
key="print-button"
|
||||
trigger={() => <Button type="primary">{t("printing.generic.print")}</Button>}
|
||||
content={() => printRef.current}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -11,28 +11,24 @@ interface QRCodeData {
|
||||
}
|
||||
|
||||
interface QRCodePrintingDialogProps {
|
||||
visible: boolean;
|
||||
items: QRCodeData[];
|
||||
printSettings: QRCodePrintSettings;
|
||||
setPrintSettings: (setPrintSettings: QRCodePrintSettings) => void;
|
||||
onCancel: () => void;
|
||||
extraSettings?: JSX.Element;
|
||||
extraSettingsStart?: JSX.Element;
|
||||
}
|
||||
|
||||
const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
|
||||
visible,
|
||||
items,
|
||||
printSettings,
|
||||
setPrintSettings,
|
||||
onCancel,
|
||||
extraSettings,
|
||||
extraSettingsStart,
|
||||
}) => {
|
||||
const t = useTranslate();
|
||||
|
||||
const showContent = printSettings?.showContent === undefined ? true : printSettings?.showContent;
|
||||
const textSize = printSettings?.textSize || 5;
|
||||
const textSize = printSettings?.textSize || 3;
|
||||
const showSpoolmanIcon = printSettings?.showSpoolmanIcon === undefined ? true : printSettings?.showSpoolmanIcon;
|
||||
|
||||
const elements = items.map((item) => {
|
||||
@@ -55,8 +51,6 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
|
||||
|
||||
return (
|
||||
<PrintingDialog
|
||||
visible={visible}
|
||||
title={t("printing.qrcode.title")}
|
||||
items={elements}
|
||||
printSettings={printSettings.printSettings}
|
||||
setPrintSettings={(newSettings) => {
|
||||
@@ -123,7 +117,7 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
|
||||
.print-page .print-qrcode-item {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
@@ -135,6 +129,7 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
|
||||
.print-page .print-qrcode {
|
||||
width: auto !important;
|
||||
height: auto !important;
|
||||
padding: 2mm;
|
||||
}
|
||||
|
||||
.print-page .print-qrcode-title {
|
||||
@@ -153,7 +148,6 @@ const QRCodePrintingDialog: React.FC<QRCodePrintingDialogProps> = ({
|
||||
max-width: 100%;
|
||||
}
|
||||
`}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Button, Flex, Form, Input, Modal, Popconfirm, Select, Space, Switch, Table, Typography } from "antd";
|
||||
import { IFilament } from "../../pages/filaments/model";
|
||||
import { ISpool } from "../../pages/spools/model";
|
||||
import { IFilament } from "../filaments/model";
|
||||
import { ISpool } from "../spools/model";
|
||||
import QRCodePrintingDialog from "./qrCodePrintingDialog";
|
||||
import { useSavedState } from "../../utils/saveload";
|
||||
import { useTranslate } from "@refinedev/core";
|
||||
@@ -21,12 +21,10 @@ import { EntityType, useGetFields } from "../../utils/queryFields";
|
||||
const { Text } = Typography;
|
||||
|
||||
interface SpoolQRCodePrintingDialog {
|
||||
visible: boolean;
|
||||
items: ISpool[];
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
const SpoolQRCodePrintingDialog: React.FC<SpoolQRCodePrintingDialog> = ({ visible, items, onCancel }) => {
|
||||
const SpoolQRCodePrintingDialog: React.FC<SpoolQRCodePrintingDialog> = ({ items }) => {
|
||||
const t = useTranslate();
|
||||
|
||||
// Selected setting state
|
||||
@@ -210,8 +208,6 @@ Lot Nr: {lot_nr}
|
||||
|
||||
return (
|
||||
<QRCodePrintingDialog
|
||||
visible={visible}
|
||||
onCancel={onCancel}
|
||||
printSettings={selectedPrintSetting.labelSettings}
|
||||
setPrintSettings={(newSettings) => {
|
||||
selectedPrintSetting.labelSettings = newSettings;
|
||||
@@ -276,6 +272,7 @@ Lot Nr: {lot_nr}
|
||||
<p
|
||||
style={{
|
||||
padding: "1mm 1mm 1mm 0",
|
||||
margin: 0,
|
||||
whiteSpace: "pre-wrap",
|
||||
}}
|
||||
>
|
||||
@@ -1,18 +1,16 @@
|
||||
import React, { useState } from "react";
|
||||
import { Modal, Table, Checkbox, Space, Row, Col, message } from "antd";
|
||||
import { ISpool } from "../pages/spools/model";
|
||||
import { FilteredQueryColumn, SortedColumn, SpoolIconColumn } from "./column";
|
||||
import { TableState } from "../utils/saveload";
|
||||
import { Modal, Table, Checkbox, Space, Row, Col, message, Button } from "antd";
|
||||
import { ISpool } from "../spools/model";
|
||||
import { FilteredQueryColumn, SortedColumn, SpoolIconColumn } from "../../components/column";
|
||||
import { TableState } from "../../utils/saveload";
|
||||
import { useTable } from "@refinedev/antd";
|
||||
import { t } from "i18next";
|
||||
import { useSpoolmanFilamentFilter, useSpoolmanMaterials } from "./otherModels";
|
||||
import { removeUndefined } from "../utils/filtering";
|
||||
import { useSpoolmanFilamentFilter, useSpoolmanMaterials } from "../../components/otherModels";
|
||||
import { removeUndefined } from "../../utils/filtering";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
interface Props {
|
||||
visible: boolean;
|
||||
description?: string;
|
||||
onCancel: () => void;
|
||||
onContinue: (selectedSpools: ISpool[]) => void;
|
||||
}
|
||||
|
||||
@@ -37,13 +35,14 @@ function collapseSpool(element: ISpool): ISpoolCollapsed {
|
||||
};
|
||||
}
|
||||
|
||||
const SpoolSelectModal: React.FC<Props> = ({ visible, description, onCancel, onContinue }) => {
|
||||
const SpoolSelectModal: React.FC<Props> = ({ description, onContinue }) => {
|
||||
const [selectedItems, setSelectedItems] = useState<number[]>([]);
|
||||
const [showArchived, setShowArchived] = useState(false);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { tableProps, sorters, filters, current, pageSize } = useTable<ISpoolCollapsed>({
|
||||
resource: "spool",
|
||||
meta: {
|
||||
queryParams: {
|
||||
["allow_archived"]: showArchived,
|
||||
@@ -116,22 +115,7 @@ const SpoolSelectModal: React.FC<Props> = ({ visible, description, onCancel, onC
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("printing.spoolSelect.title")}
|
||||
open={visible}
|
||||
onCancel={onCancel}
|
||||
onOk={() => {
|
||||
if (selectedItems.length === 0) {
|
||||
messageApi.open({
|
||||
type: "error",
|
||||
content: t("printing.spoolSelect.noSpoolsSelected"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
onContinue(dataSource.filter((spool) => selectedItems.includes(spool.id)));
|
||||
}}
|
||||
width={600}
|
||||
>
|
||||
<>
|
||||
{contextHolder}
|
||||
<Space direction="vertical" style={{ width: "100%" }}>
|
||||
{description && <div>{description}</div>}
|
||||
@@ -210,7 +194,19 @@ const SpoolSelectModal: React.FC<Props> = ({ visible, description, onCancel, onC
|
||||
</Col>
|
||||
</Row>
|
||||
</Space>
|
||||
</Modal>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (selectedItems.length === 0) {
|
||||
messageApi.open({
|
||||
type: "error",
|
||||
content: t("printing.spoolSelect.noSpoolsSelected"),
|
||||
});
|
||||
return;
|
||||
}
|
||||
onContinue(dataSource.filter((spool) => selectedItems.includes(spool.id)));
|
||||
}}
|
||||
></Button>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
CustomFieldColumn,
|
||||
} from "../../components/column";
|
||||
import { setSpoolArchived } from "./functions";
|
||||
import SelectAndPrint from "../../components/selectAndPrintDialog";
|
||||
import {
|
||||
useSpoolmanFilamentFilter,
|
||||
useSpoolmanLocations,
|
||||
@@ -254,7 +253,6 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
||||
<List
|
||||
headerButtons={({ defaultButtons }) => (
|
||||
<>
|
||||
<SelectAndPrint />
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<InboxOutlined />}
|
||||
|
||||
Reference in New Issue
Block a user