@@ -11,6 +11,7 @@ import { DateField, TextField } from "@refinedev/antd";
|
|||||||
import Icon from "@ant-design/icons";
|
import Icon from "@ant-design/icons";
|
||||||
import { ReactComponent as SpoolIcon } from "../icon_spool.svg";
|
import { ReactComponent as SpoolIcon } from "../icon_spool.svg";
|
||||||
import { useTranslate } from "@refinedev/core";
|
import { useTranslate } from "@refinedev/core";
|
||||||
|
import { enrichText } from "../utils/parsing";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -62,10 +63,18 @@ function Column<Obj>(props: BaseColumnProps<Obj> & FilteredColumnProps & CustomC
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function SortedColumn<Obj>(props: BaseColumnProps<Obj>) {
|
export function SortedColumn<Obj>(props: BaseColumnProps<Obj>) {
|
||||||
// return <Table.Column dataIndex={["comment"]} title="Comment" />;
|
|
||||||
return Column(props);
|
return Column(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function RichColumn<Obj>(props: BaseColumnProps<Obj>) {
|
||||||
|
return Column({
|
||||||
|
...props,
|
||||||
|
render: (value: string | undefined) => {
|
||||||
|
return enrichText(value);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function FilteredColumn<Obj>(props: BaseColumnProps<Obj>) {
|
export function FilteredColumn<Obj>(props: BaseColumnProps<Obj>) {
|
||||||
const typedFilters = typeFilters<Obj>(props.tableState.filters);
|
const typedFilters = typeFilters<Obj>(props.tableState.filters);
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,14 @@ import { genericSorter, typeSorters } from "../../utils/sorting";
|
|||||||
import { genericFilterer, typeFilters } from "../../utils/filtering";
|
import { genericFilterer, typeFilters } from "../../utils/filtering";
|
||||||
import { EditOutlined, FilterOutlined } from "@ant-design/icons";
|
import { EditOutlined, FilterOutlined } from "@ant-design/icons";
|
||||||
import { TableState, useInitialTableState, useStoreInitialState } from "../../utils/saveload";
|
import { TableState, useInitialTableState, useStoreInitialState } from "../../utils/saveload";
|
||||||
import { DateColumn, FilteredColumn, NumberColumn, SortedColumn, SpoolIconColumn } from "../../components/column";
|
import {
|
||||||
|
DateColumn,
|
||||||
|
FilteredColumn,
|
||||||
|
NumberColumn,
|
||||||
|
RichColumn,
|
||||||
|
SortedColumn,
|
||||||
|
SpoolIconColumn,
|
||||||
|
} from "../../components/column";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -246,7 +253,7 @@ export const FilamentList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
dataSource,
|
dataSource,
|
||||||
tableState,
|
tableState,
|
||||||
})}
|
})}
|
||||||
{SortedColumn({
|
{RichColumn({
|
||||||
id: "comment",
|
id: "comment",
|
||||||
i18ncat: "filament",
|
i18ncat: "filament",
|
||||||
dataSource,
|
dataSource,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { NumberFieldUnit } from "../../components/numberField";
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { IFilament } from "./model";
|
import { IFilament } from "./model";
|
||||||
|
import { enrichText } from "../../utils/parsing";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ export const FilamentShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
<Title level={5}>{t("filament.fields.article_number")}</Title>
|
<Title level={5}>{t("filament.fields.article_number")}</Title>
|
||||||
<TextField value={record?.article_number} />
|
<TextField value={record?.article_number} />
|
||||||
<Title level={5}>{t("filament.fields.comment")}</Title>
|
<Title level={5}>{t("filament.fields.comment")}</Title>
|
||||||
<TextField value={record?.comment} />
|
<TextField value={enrichText(record?.comment)} />
|
||||||
</Show>
|
</Show>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,14 @@ import { genericFilterer, typeFilters } from "../../utils/filtering";
|
|||||||
import { ISpool } from "./model";
|
import { ISpool } from "./model";
|
||||||
import { TableState, useInitialTableState, useSavedState, useStoreInitialState } from "../../utils/saveload";
|
import { TableState, useInitialTableState, useSavedState, useStoreInitialState } from "../../utils/saveload";
|
||||||
import { EditOutlined, FilterOutlined, InboxOutlined, ToTopOutlined } from "@ant-design/icons";
|
import { EditOutlined, FilterOutlined, InboxOutlined, ToTopOutlined } from "@ant-design/icons";
|
||||||
import { DateColumn, FilteredColumn, NumberColumn, SortedColumn, SpoolIconColumn } from "../../components/column";
|
import {
|
||||||
|
DateColumn,
|
||||||
|
FilteredColumn,
|
||||||
|
NumberColumn,
|
||||||
|
RichColumn,
|
||||||
|
SortedColumn,
|
||||||
|
SpoolIconColumn,
|
||||||
|
} from "../../components/column";
|
||||||
import { setSpoolArchived } from "./functions";
|
import { setSpoolArchived } from "./functions";
|
||||||
import SelectAndPrint from "../../components/selectAndPrintDialog";
|
import SelectAndPrint from "../../components/selectAndPrintDialog";
|
||||||
|
|
||||||
@@ -305,7 +312,7 @@ export const SpoolList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
dataSource,
|
dataSource,
|
||||||
tableState,
|
tableState,
|
||||||
})}
|
})}
|
||||||
{SortedColumn({
|
{RichColumn({
|
||||||
id: "comment",
|
id: "comment",
|
||||||
i18ncat: "spool",
|
i18ncat: "spool",
|
||||||
dataSource,
|
dataSource,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { NumberFieldUnit } from "../../components/numberField";
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { ISpool } from "./model";
|
import { ISpool } from "./model";
|
||||||
|
import { enrichText } from "../../utils/parsing";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -90,7 +91,7 @@ export const SpoolShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
<Title level={5}>{t("spool.fields.lot_nr")}</Title>
|
<Title level={5}>{t("spool.fields.lot_nr")}</Title>
|
||||||
<TextField value={record?.lot_nr} />
|
<TextField value={record?.lot_nr} />
|
||||||
<Title level={5}>{t("spool.fields.comment")}</Title>
|
<Title level={5}>{t("spool.fields.comment")}</Title>
|
||||||
<TextField value={record?.comment} />
|
<TextField value={enrichText(record?.comment)} />
|
||||||
<Title level={5}>{t("spool.fields.archived")}</Title>
|
<Title level={5}>{t("spool.fields.archived")}</Title>
|
||||||
<TextField value={record?.archived ? t("yes") : t("no")} />
|
<TextField value={record?.archived ? t("yes") : t("no")} />
|
||||||
</Show>
|
</Show>
|
||||||
|
|||||||
4
client/src/pages/vendors/list.tsx
vendored
4
client/src/pages/vendors/list.tsx
vendored
@@ -9,7 +9,7 @@ import { genericFilterer, typeFilters } from "../../utils/filtering";
|
|||||||
import { IVendor } from "./model";
|
import { IVendor } from "./model";
|
||||||
import { TableState, useInitialTableState, useStoreInitialState } from "../../utils/saveload";
|
import { TableState, useInitialTableState, useStoreInitialState } from "../../utils/saveload";
|
||||||
import { EditOutlined, FilterOutlined } from "@ant-design/icons";
|
import { EditOutlined, FilterOutlined } from "@ant-design/icons";
|
||||||
import { DateColumn, SortedColumn } from "../../components/column";
|
import { DateColumn, RichColumn, SortedColumn } from "../../components/column";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ export const VendorList: React.FC<IResourceComponentsProps> = () => {
|
|||||||
dataSource,
|
dataSource,
|
||||||
tableState,
|
tableState,
|
||||||
})}
|
})}
|
||||||
{SortedColumn({
|
{RichColumn({
|
||||||
id: "comment",
|
id: "comment",
|
||||||
i18ncat: "vendor",
|
i18ncat: "vendor",
|
||||||
dataSource,
|
dataSource,
|
||||||
|
|||||||
3
client/src/pages/vendors/show.tsx
vendored
3
client/src/pages/vendors/show.tsx
vendored
@@ -5,6 +5,7 @@ import { Typography } from "antd";
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import utc from "dayjs/plugin/utc";
|
import utc from "dayjs/plugin/utc";
|
||||||
import { IVendor } from "./model";
|
import { IVendor } from "./model";
|
||||||
|
import { enrichText } from "../../utils/parsing";
|
||||||
|
|
||||||
dayjs.extend(utc);
|
dayjs.extend(utc);
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ export const VendorShow: React.FC<IResourceComponentsProps> = () => {
|
|||||||
<Title level={5}>{t("vendor.fields.name")}</Title>
|
<Title level={5}>{t("vendor.fields.name")}</Title>
|
||||||
<TextField value={record?.name} />
|
<TextField value={record?.name} />
|
||||||
<Title level={5}>{t("vendor.fields.comment")}</Title>
|
<Title level={5}>{t("vendor.fields.comment")}</Title>
|
||||||
<TextField value={record?.comment} />
|
<TextField value={enrichText(record?.comment)} />
|
||||||
</Show>
|
</Show>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
// Number formatter that nicely formats numbers with correct decimal separator
|
|
||||||
export function numberFormatter(value: number | undefined) {
|
|
||||||
return value ? Number(value).toLocaleString() : "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number parser that supports both comma and dot as decimal separator
|
|
||||||
export function numberParser(value: string | undefined) {
|
|
||||||
// Convert comma to dot
|
|
||||||
const decimalSeparator = (1.1).toLocaleString().charAt(1);
|
|
||||||
if (decimalSeparator === ",") {
|
|
||||||
value = value?.replace(",", ".");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove all non-digit characters
|
|
||||||
value = value?.replace(/[^\d.-]/g, "");
|
|
||||||
|
|
||||||
// Parse as float
|
|
||||||
return parseFloat(value || "0");
|
|
||||||
}
|
|
||||||
45
client/src/utils/parsing.tsx
Normal file
45
client/src/utils/parsing.tsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
// Number formatter that nicely formats numbers with correct decimal separator
|
||||||
|
export function numberFormatter(value: number | undefined) {
|
||||||
|
return value ? Number(value).toLocaleString() : "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Number parser that supports both comma and dot as decimal separator
|
||||||
|
export function numberParser(value: string | undefined) {
|
||||||
|
// Convert comma to dot
|
||||||
|
const decimalSeparator = (1.1).toLocaleString().charAt(1);
|
||||||
|
if (decimalSeparator === ",") {
|
||||||
|
value = value?.replace(",", ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all non-digit characters
|
||||||
|
value = value?.replace(/[^\d.-]/g, "");
|
||||||
|
|
||||||
|
// Parse as float
|
||||||
|
return parseFloat(value || "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enrichText(text: string | undefined) {
|
||||||
|
// Regular expression to match URLs
|
||||||
|
const urlRegex =
|
||||||
|
/(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi;
|
||||||
|
|
||||||
|
// Split the input text by URLs
|
||||||
|
const parts = (text ?? "").split(urlRegex);
|
||||||
|
|
||||||
|
// Convert URLs to <a> tags
|
||||||
|
const elements = parts.map((part, index) => {
|
||||||
|
if (part.match(urlRegex)) {
|
||||||
|
return (
|
||||||
|
<a href={part} key={index} target="_blank" rel="noopener noreferrer">
|
||||||
|
{part}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return <React.Fragment key={index}>{part}</React.Fragment>;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return <>{elements}</>;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user