diff --git a/client/public/locales/en/common.json b/client/public/locales/en/common.json index c705113..92ae88b 100644 --- a/client/public/locales/en/common.json +++ b/client/public/locales/en/common.json @@ -101,20 +101,10 @@ "qrcode": { "button": "Print QR Codes", "title": "QR Code Printing", - "spoolWeight": "Spool Weight: {{weight}}", - "lotNr": "Lot Nr: {{lot}}", - "bedTemp": "BT: {{temp}}", - "extruderTemp": "ET: {{temp}}", + "template": "Template", + "templateHelp": "Use {} to insert values of the spool object as text. For example {id} will be replaced with the spool id, or {filament.material} will be replaced with the material of the spool. Click the button to view a list of all available tags.", "textSize": "Content Text Size", - "showSpoolmanIcon": "Show Spoolman Icon", - "showVendor": "Manufacturer", - "showContent": "Print Label", - "showLotNr": "Lot Nr", - "showSpoolWeight": "Spool Weight", - "showTemperatures": "Temperatures", - "showSpoolComment": "Spool Comment", - "showFilamentComment": "Filament Comment", - "showVendorComment": "Manufacturer Comment" + "showSpoolmanIcon": "Show Spoolman Icon" }, "spoolSelect": { "title": "Select Spools", diff --git a/client/src/components/printing/printing.ts b/client/src/components/printing/printing.tsx similarity index 60% rename from client/src/components/printing/printing.ts rename to client/src/components/printing/printing.tsx index 9b1547b..3b834d4 100644 --- a/client/src/components/printing/printing.ts +++ b/client/src/components/printing/printing.tsx @@ -1,3 +1,5 @@ +import React, { ReactNode } from "react"; +import { ISpool } from "../../pages/spools/model"; import { useGetSetting, useSetSetting } from "../../utils/querySettings"; import { v4 as uuidv4 } from "uuid"; @@ -24,13 +26,7 @@ export interface QRCodePrintSettings { } export interface SpoolQRCodePrintSettings { - showVendor?: boolean; - showLotNr?: boolean; - showSpoolWeight?: boolean; - showTemperatures?: boolean; - showSpoolComment?: boolean; - showFilamentComment?: boolean; - showVendorComment?: boolean; + template?: string; labelSettings: QRCodePrintSettings; } @@ -55,3 +51,42 @@ export function useSetPrintSettings(): (spoolQRCodePrintSettings: SpoolQRCodePri mut.mutate(spoolQRCodePrintSettings); }; } + +interface GenericObject { + [key: string]: any; + extra: { [key: string]: string }; +} + +function getTagValue(tag: string, obj: GenericObject): any { + // Split tag by . + const tagParts = tag.split("."); + if (tagParts[0] === "extra") { + return JSON.parse(obj.extra[tagParts[1]]); + } + + const value = obj[tagParts[0]] ?? "?"; + // check if value is itself an object. If so, recursively call this and remove the first part of the tag + if (typeof value === "object") { + return getTagValue(tagParts.slice(1).join("."), value); + } + return value; +} + +export function renderLabelContents(template: string, spool: ISpool): ReactNode { + // Find all {tags} in the template string and loop over them + let result = template.replace(/\{(.*?)\}/g, function (_, tag) { + return getTagValue(tag, spool); + }); + + // Split string on \n into individual lines + return ( + <> + {result.split("\n").map((line, index) => ( + + {line} +
+
+ ))} + + ); +} diff --git a/client/src/components/printing/spoolQrCodePrintingDialog.tsx b/client/src/components/printing/spoolQrCodePrintingDialog.tsx index 5b4af15..069635d 100644 --- a/client/src/components/printing/spoolQrCodePrintingDialog.tsx +++ b/client/src/components/printing/spoolQrCodePrintingDialog.tsx @@ -1,14 +1,24 @@ -import { Button, Flex, Form, Input, Modal, Popconfirm, Select, Space, Switch } from "antd"; +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 QRCodePrintingDialog from "./qrCodePrintingDialog"; import { useSavedState } from "../../utils/saveload"; import { useTranslate } from "@refinedev/core"; -import { QRCodePrintSettings, SpoolQRCodePrintSettings, useGetPrintSettings, useSetPrintSettings } from "./printing"; +import { + QRCodePrintSettings, + SpoolQRCodePrintSettings, + renderLabelContents, + useGetPrintSettings, + useSetPrintSettings, +} from "./printing"; import { useMemo, useState } from "react"; import { DeleteOutlined, EditOutlined, PlusOutlined } from "@ant-design/icons"; import { v4 as uuidv4 } from "uuid"; import _ from "lodash"; +import TextArea from "antd/es/input/TextArea"; +import { EntityType, useGetFields } from "../../utils/queryFields"; + +const { Text } = Typography; interface SpoolQRCodePrintingDialog { visible: boolean; @@ -120,25 +130,73 @@ const SpoolQRCodePrintingDialog: React.FC = ({ visibl } } - const showVendor = selectedPrintSetting?.showVendor; - const showLotNr = selectedPrintSetting?.showLotNr; - const showSpoolWeight = selectedPrintSetting?.showSpoolWeight; - const showTemperatures = selectedPrintSetting?.showTemperatures; - const showSpoolComment = selectedPrintSetting?.showSpoolComment; - const showFilamentComment = selectedPrintSetting?.showFilamentComment; - const showVendorComment = selectedPrintSetting?.showVendorComment; + const [templateHelpOpen, setTemplateHelpOpen] = useState(false); + const template = selectedPrintSetting.template ?? ""; - const formatFilament = (filament: IFilament) => { - let vendorPrefix = ""; - if (showVendor && filament.vendor) { - vendorPrefix = `${filament.vendor.name} - `; - } - let name = filament.name; - if (!name) { - name = `Filament #${filament.id}`; - } - return `${vendorPrefix}${name}`; - }; + const spoolTags = [ + { tag: "id" }, + { tag: "registered" }, + { tag: "first_used" }, + { tag: "last_used" }, + { tag: "price" }, + { tag: "initial_weight" }, + { tag: "spool_weight" }, + { tag: "remaining_weight" }, + { tag: "used_weight" }, + { tag: "remaining_length" }, + { tag: "used_length" }, + { tag: "location" }, + { tag: "lot_nr" }, + { tag: "comment" }, + { tag: "archived" }, + ]; + const spoolFields = useGetFields(EntityType.spool); + if (spoolFields.data !== undefined) { + spoolFields.data.forEach((field) => { + spoolTags.push({ tag: `extra.${field.key}` }); + }); + } + const filamentTags = [ + { tag: "filament.id" }, + { tag: "filament.registered" }, + { tag: "filament.name" }, + { tag: "filament.material" }, + { tag: "filament.price" }, + { tag: "filament.density" }, + { tag: "filament.diameter" }, + { tag: "filament.weight" }, + { tag: "filament.spool_weight" }, + { tag: "filament.article_number" }, + { tag: "filament.comment" }, + { tag: "filament.settings_extruder_temp" }, + { tag: "filament.settings_bed_temp" }, + { tag: "filament.color_hex" }, + { tag: "filament.multi_color_hexes" }, + { tag: "filament.multi_color_direction" }, + { tag: "filament.external_id" }, + ]; + const filamentFields = useGetFields(EntityType.filament); + if (filamentFields.data !== undefined) { + filamentFields.data.forEach((field) => { + filamentTags.push({ tag: `filament.extra.${field.key}` }); + }); + } + const vendorTags = [ + { tag: "filament.vendor.id" }, + { tag: "filament.vendor.registered" }, + { tag: "filament.vendor.name" }, + { tag: "filament.vendor.comment" }, + { tag: "filament.vendor.empty_spool_weight" }, + { tag: "filament.vendor.external_id" }, + ]; + const vendorFields = useGetFields(EntityType.vendor); + if (vendorFields.data !== undefined) { + vendorFields.data.forEach((field) => { + vendorTags.push({ tag: `filament.vendor.extra.${field.key}` }); + }); + } + + const templateTags = [...spoolTags, ...filamentTags, ...vendorTags]; return ( = ({ visibl } items={items.map(function (spool) { - const temps = []; - if (spool.filament.settings_extruder_temp) { - temps.push(t("printing.qrcode.extruderTemp", { temp: `${spool.filament.settings_extruder_temp} °C` })); - } - if (spool.filament.settings_bed_temp) { - temps.push(t("printing.qrcode.bedTemp", { temp: `${spool.filament.settings_bed_temp} °C` })); - } - const tempLine = temps.join(" - "); - return { value: `web+spoolman:s-${spool.id}`, label: (

- {formatFilament(spool.filament)} -
- - #{spool.id} - {spool.filament.material && <> - {spool.filament.material}} - - {showSpoolWeight && ( - <> -
- {t("printing.qrcode.spoolWeight", { weight: `${spool.filament.spool_weight ?? "?"} g` })} - - )} - {showTemperatures && tempLine && ( - <> -
- {tempLine} - - )} - {showLotNr && ( - <> -
- {t("printing.qrcode.lotNr", { lot: spool.lot_nr ?? "?" })} - - )} - {showSpoolComment && spool.comment && ( - <> -
- {spool.comment} - - )} - {showFilamentComment && spool.filament.comment && ( - <> -
- {spool.filament.comment} - - )} - {showVendorComment && spool.filament.vendor?.comment && ( - <> -
- {spool.filament.vendor.comment} - - )} + {renderLabelContents(template, spool)}

), errorLevel: "H", @@ -268,69 +277,32 @@ const SpoolQRCodePrintingDialog: React.FC = ({ visibl })} extraSettings={ <> - - { - selectedPrintSetting.showVendor = checked; + +