mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'master' into theme-simplificatio
This commit is contained in:
30
.github/workflows/translations.yaml
vendored
30
.github/workflows/translations.yaml
vendored
@ -13,10 +13,11 @@ permissions:
|
|||||||
contents: read
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
synchronize-with-crowdin:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -39,16 +40,19 @@ jobs:
|
|||||||
apt-dependency: gettext
|
apt-dependency: gettext
|
||||||
- name: Make Translations
|
- name: Make Translations
|
||||||
run: invoke translate
|
run: invoke translate
|
||||||
- name: Commit files
|
- name: crowdin action
|
||||||
run: |
|
uses: crowdin/github-action@6ed209d411599a981ccb978df3be9dc9b8a81699 # pin@v2
|
||||||
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
|
||||||
git config --local user.name "github-actions[bot]"
|
|
||||||
git checkout -b l10_local
|
|
||||||
git add "*.po"
|
|
||||||
git commit -m "updated translation base"
|
|
||||||
- name: Push changes
|
|
||||||
uses: ad-m/github-push-action@d91a481090679876dfc4178fef17f286781251df # pin@v0.8.0
|
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
upload_sources: true
|
||||||
branch: l10
|
upload_translations: false
|
||||||
force: true
|
download_translations: true
|
||||||
|
localization_branch_name: l10_crowdin
|
||||||
|
create_pull_request: true
|
||||||
|
pull_request_title: 'New Crowdin updates'
|
||||||
|
pull_request_body: 'New Crowdin translations by [Crowdin GH Action](https://github.com/crowdin/github-action)'
|
||||||
|
pull_request_base_branch_name: 'l10'
|
||||||
|
pull_request_labels: 'translations'
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
CROWDIN_PROJECT_ID: ${{ secrets.CROWDIN_PROJECT_ID }}
|
||||||
|
CROWDIN_PERSONAL_TOKEN: ${{ secrets.CROWDIN_PERSONAL_TOKEN }}
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 246
|
INVENTREE_API_VERSION = 248
|
||||||
|
|
||||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
INVENTREE_API_TEXT = """
|
||||||
|
v248 - 2024-08-23 : https://github.com/inventree/InvenTree/pull/7965
|
||||||
|
- Small adjustments to labels for new custom status fields
|
||||||
|
|
||||||
|
v247 - 2024-08-22 : https://github.com/inventree/InvenTree/pull/7956
|
||||||
|
- Adjust "attachment" field on StockItemTestResult serializer
|
||||||
|
- Allow null values for attachment
|
||||||
|
|
||||||
v246 - 2024-08-21 : https://github.com/inventree/InvenTree/pull/7862
|
v246 - 2024-08-21 : https://github.com/inventree/InvenTree/pull/7862
|
||||||
- Adds custom status fields to various serializers
|
- Adds custom status fields to various serializers
|
||||||
|
@ -46,7 +46,7 @@ class Migration(migrations.Migration):
|
|||||||
models.CharField(
|
models.CharField(
|
||||||
help_text="Label that will be displayed in the frontend",
|
help_text="Label that will be displayed in the frontend",
|
||||||
max_length=250,
|
max_length=250,
|
||||||
verbose_name="label",
|
verbose_name="Label",
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
|
@ -3355,7 +3355,7 @@ class InvenTreeCustomUserStateModel(models.Model):
|
|||||||
)
|
)
|
||||||
label = models.CharField(
|
label = models.CharField(
|
||||||
max_length=250,
|
max_length=250,
|
||||||
verbose_name=_('label'),
|
verbose_name=_('Label'),
|
||||||
help_text=_('Label that will be displayed in the frontend'),
|
help_text=_('Label that will be displayed in the frontend'),
|
||||||
)
|
)
|
||||||
color = models.CharField(
|
color = models.CharField(
|
||||||
|
@ -239,7 +239,10 @@ class StockItemTestResultSerializer(
|
|||||||
)
|
)
|
||||||
|
|
||||||
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(
|
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(
|
||||||
required=False
|
required=False,
|
||||||
|
allow_null=True,
|
||||||
|
label=_('Attachment'),
|
||||||
|
help_text=_('Test result attachment'),
|
||||||
)
|
)
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
|
@ -380,7 +380,7 @@ function stockItemFields(options={}) {
|
|||||||
batch: {
|
batch: {
|
||||||
icon: 'fa-layer-group',
|
icon: 'fa-layer-group',
|
||||||
},
|
},
|
||||||
status_custom_key: {},
|
status: {},
|
||||||
expiry_date: {
|
expiry_date: {
|
||||||
icon: 'fa-calendar-alt',
|
icon: 'fa-calendar-alt',
|
||||||
},
|
},
|
||||||
|
@ -20,7 +20,7 @@ import { ReactNode, useMemo } from 'react';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { identifierString } from '../../functions/conversion';
|
import { identifierString } from '../../functions/conversion';
|
||||||
import { InvenTreeIcon } from '../../functions/icons';
|
import { InvenTreeIcon } from '../../functions/icons';
|
||||||
import { InvenTreeQRCode } from './QRCode';
|
import { InvenTreeQRCode, QRCodeLink, QRCodeUnlink } from './QRCode';
|
||||||
|
|
||||||
export type ActionDropdownItem = {
|
export type ActionDropdownItem = {
|
||||||
icon?: ReactNode;
|
icon?: ReactNode;
|
||||||
@ -112,69 +112,91 @@ export function ActionDropdown({
|
|||||||
|
|
||||||
// Dropdown menu for barcode actions
|
// Dropdown menu for barcode actions
|
||||||
export function BarcodeActionDropdown({
|
export function BarcodeActionDropdown({
|
||||||
actions
|
model,
|
||||||
}: {
|
pk,
|
||||||
actions: ActionDropdownItem[];
|
hash = null,
|
||||||
}) {
|
actions = [],
|
||||||
|
perm: permission = true
|
||||||
|
}: Readonly<{
|
||||||
|
model: ModelType;
|
||||||
|
pk: number;
|
||||||
|
hash?: boolean | null;
|
||||||
|
actions?: ActionDropdownItem[];
|
||||||
|
perm?: boolean;
|
||||||
|
}>) {
|
||||||
|
const hidden = hash === null;
|
||||||
|
const prop = { model, pk, hash };
|
||||||
return (
|
return (
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
tooltip={t`Barcode Actions`}
|
tooltip={t`Barcode Actions`}
|
||||||
icon={<IconQrcode />}
|
icon={<IconQrcode />}
|
||||||
actions={actions}
|
actions={[
|
||||||
|
GeneralBarcodeAction({
|
||||||
|
mdl_prop: prop,
|
||||||
|
title: t`View`,
|
||||||
|
icon: <IconQrcode />,
|
||||||
|
tooltip: t`View barcode`,
|
||||||
|
ChildItem: InvenTreeQRCode
|
||||||
|
}),
|
||||||
|
GeneralBarcodeAction({
|
||||||
|
hidden: hidden || hash || !permission,
|
||||||
|
mdl_prop: prop,
|
||||||
|
title: t`Link Barcode`,
|
||||||
|
icon: <IconLink />,
|
||||||
|
tooltip: t`Link a custom barcode to this item`,
|
||||||
|
ChildItem: QRCodeLink
|
||||||
|
}),
|
||||||
|
GeneralBarcodeAction({
|
||||||
|
hidden: hidden || !hash || !permission,
|
||||||
|
mdl_prop: prop,
|
||||||
|
title: t`Unlink Barcode`,
|
||||||
|
icon: <IconUnlink />,
|
||||||
|
tooltip: t`Unlink custom barcode`,
|
||||||
|
ChildItem: QRCodeUnlink
|
||||||
|
}),
|
||||||
|
...actions
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common action button for viewing a barcode
|
export type QrCodeType = {
|
||||||
export function ViewBarcodeAction({
|
|
||||||
hidden = false,
|
|
||||||
model,
|
|
||||||
pk
|
|
||||||
}: {
|
|
||||||
hidden?: boolean;
|
|
||||||
model: ModelType;
|
model: ModelType;
|
||||||
pk: number;
|
pk: number;
|
||||||
|
hash?: boolean | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
function GeneralBarcodeAction({
|
||||||
|
hidden = false,
|
||||||
|
mdl_prop,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
tooltip,
|
||||||
|
ChildItem
|
||||||
|
}: {
|
||||||
|
hidden?: boolean;
|
||||||
|
mdl_prop: QrCodeType;
|
||||||
|
title: string;
|
||||||
|
icon: ReactNode;
|
||||||
|
tooltip: string;
|
||||||
|
ChildItem: any;
|
||||||
}): ActionDropdownItem {
|
}): ActionDropdownItem {
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
modals.open({
|
modals.open({
|
||||||
title: t`View Barcode`,
|
title: title,
|
||||||
children: <InvenTreeQRCode model={model} pk={pk} />
|
children: <ChildItem mdl_prop={mdl_prop} />
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
icon: <IconQrcode />,
|
icon: icon,
|
||||||
name: t`View`,
|
name: title,
|
||||||
tooltip: t`View barcode`,
|
tooltip: tooltip,
|
||||||
onClick: onClick,
|
onClick: onClick,
|
||||||
hidden: hidden
|
hidden: hidden
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common action button for linking a custom barcode
|
|
||||||
export function LinkBarcodeAction(
|
|
||||||
props: ActionDropdownItem
|
|
||||||
): ActionDropdownItem {
|
|
||||||
return {
|
|
||||||
...props,
|
|
||||||
icon: <IconLink />,
|
|
||||||
name: t`Link Barcode`,
|
|
||||||
tooltip: t`Link custom barcode`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common action button for un-linking a custom barcode
|
|
||||||
export function UnlinkBarcodeAction(
|
|
||||||
props: ActionDropdownItem
|
|
||||||
): ActionDropdownItem {
|
|
||||||
return {
|
|
||||||
...props,
|
|
||||||
icon: <IconUnlink />,
|
|
||||||
name: t`Unlink Barcode`,
|
|
||||||
tooltip: t`Unlink custom barcode`
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Common action button for editing an item
|
// Common action button for editing an item
|
||||||
export function EditItemAction(props: ActionDropdownItem): ActionDropdownItem {
|
export function EditItemAction(props: ActionDropdownItem): ActionDropdownItem {
|
||||||
return {
|
return {
|
||||||
|
@ -1,24 +1,28 @@
|
|||||||
import { Trans, t } from '@lingui/macro';
|
import { Trans, t } from '@lingui/macro';
|
||||||
import {
|
import {
|
||||||
|
Alert,
|
||||||
Box,
|
Box,
|
||||||
|
Button,
|
||||||
Code,
|
Code,
|
||||||
Group,
|
Group,
|
||||||
Image,
|
Image,
|
||||||
Select,
|
Select,
|
||||||
Skeleton,
|
Skeleton,
|
||||||
Stack,
|
Stack,
|
||||||
Text
|
Text,
|
||||||
|
TextInput
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
|
import { modals } from '@mantine/modals';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import QR from 'qrcode';
|
import QR from 'qrcode';
|
||||||
import { useEffect, useMemo, useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { api } from '../../App';
|
import { api } from '../../App';
|
||||||
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
||||||
import { ModelType } from '../../enums/ModelType';
|
|
||||||
import { apiUrl } from '../../states/ApiState';
|
import { apiUrl } from '../../states/ApiState';
|
||||||
import { useGlobalSettingsState } from '../../states/SettingsState';
|
import { useGlobalSettingsState } from '../../states/SettingsState';
|
||||||
import { CopyButton } from '../buttons/CopyButton';
|
import { CopyButton } from '../buttons/CopyButton';
|
||||||
|
import { QrCodeType } from './ActionDropdown';
|
||||||
|
|
||||||
type QRCodeProps = {
|
type QRCodeProps = {
|
||||||
ecl?: 'L' | 'M' | 'Q' | 'H';
|
ecl?: 'L' | 'M' | 'Q' | 'H';
|
||||||
@ -51,15 +55,13 @@ export const QRCode = ({ data, ecl = 'Q', margin = 1 }: QRCodeProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
type InvenTreeQRCodeProps = {
|
type InvenTreeQRCodeProps = {
|
||||||
model: ModelType;
|
mdl_prop: QrCodeType;
|
||||||
pk: number;
|
|
||||||
showEclSelector?: boolean;
|
showEclSelector?: boolean;
|
||||||
} & Omit<QRCodeProps, 'data'>;
|
} & Omit<QRCodeProps, 'data'>;
|
||||||
|
|
||||||
export const InvenTreeQRCode = ({
|
export const InvenTreeQRCode = ({
|
||||||
|
mdl_prop,
|
||||||
showEclSelector = true,
|
showEclSelector = true,
|
||||||
model,
|
|
||||||
pk,
|
|
||||||
ecl: eclProp = 'Q',
|
ecl: eclProp = 'Q',
|
||||||
...props
|
...props
|
||||||
}: InvenTreeQRCodeProps) => {
|
}: InvenTreeQRCodeProps) => {
|
||||||
@ -71,11 +73,11 @@ export const InvenTreeQRCode = ({
|
|||||||
}, [eclProp]);
|
}, [eclProp]);
|
||||||
|
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ['qr-code', model, pk],
|
queryKey: ['qr-code', mdl_prop.model, mdl_prop.pk],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res = await api.post(apiUrl(ApiEndpoints.generate_barcode), {
|
const res = await api.post(apiUrl(ApiEndpoints.generate_barcode), {
|
||||||
model,
|
model: mdl_prop.model,
|
||||||
pk
|
pk: mdl_prop.pk
|
||||||
});
|
});
|
||||||
|
|
||||||
return res.data?.barcode as string;
|
return res.data?.barcode as string;
|
||||||
@ -94,6 +96,15 @@ export const InvenTreeQRCode = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
|
{mdl_prop.hash ? (
|
||||||
|
<Alert variant="outline" color="red" title={t`Custom bascode`}>
|
||||||
|
<Trans>
|
||||||
|
A custom barcode is registered for this item. The shown code is not
|
||||||
|
that custom barcode.
|
||||||
|
</Trans>
|
||||||
|
</Alert>
|
||||||
|
) : null}
|
||||||
|
|
||||||
<QRCode data={data} ecl={ecl} {...props} />
|
<QRCode data={data} ecl={ecl} {...props} />
|
||||||
|
|
||||||
{data && settings.getSetting('BARCODE_SHOW_TEXT', 'false') && (
|
{data && settings.getSetting('BARCODE_SHOW_TEXT', 'false') && (
|
||||||
@ -128,3 +139,55 @@ export const InvenTreeQRCode = ({
|
|||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const QRCodeLink = ({ mdl_prop }: { mdl_prop: QrCodeType }) => {
|
||||||
|
const [barcode, setBarcode] = useState('');
|
||||||
|
|
||||||
|
function linkBarcode() {
|
||||||
|
api
|
||||||
|
.post(apiUrl(ApiEndpoints.barcode_link), {
|
||||||
|
[mdl_prop.model]: mdl_prop.pk,
|
||||||
|
barcode: barcode
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
modals.closeAll();
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<TextInput
|
||||||
|
label={t`Barcode`}
|
||||||
|
value={barcode}
|
||||||
|
onChange={(event) => setBarcode(event.currentTarget.value)}
|
||||||
|
placeholder={t`Scan barcode data here using barcode scanner`}
|
||||||
|
/>
|
||||||
|
<Button color="green" onClick={linkBarcode} mt="lg" fullWidth>
|
||||||
|
<Trans>Link</Trans>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const QRCodeUnlink = ({ mdl_prop }: { mdl_prop: QrCodeType }) => {
|
||||||
|
function unlinkBarcode() {
|
||||||
|
api
|
||||||
|
.post(apiUrl(ApiEndpoints.barcode_unlink), {
|
||||||
|
[mdl_prop.model]: mdl_prop.pk
|
||||||
|
})
|
||||||
|
.then((response) => {
|
||||||
|
modals.closeAll();
|
||||||
|
location.reload();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Text>
|
||||||
|
<Trans>This will remove the link to the associated barcode</Trans>
|
||||||
|
</Text>
|
||||||
|
<Button color="red" onClick={unlinkBarcode}>
|
||||||
|
<Trans>Unlink Barcode</Trans>
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
@ -39,6 +39,8 @@ export enum ApiEndpoints {
|
|||||||
settings_global_list = 'settings/global/',
|
settings_global_list = 'settings/global/',
|
||||||
settings_user_list = 'settings/user/',
|
settings_user_list = 'settings/user/',
|
||||||
barcode = 'barcode/',
|
barcode = 'barcode/',
|
||||||
|
barcode_link = 'barcode/link/',
|
||||||
|
barcode_unlink = 'barcode/unlink/',
|
||||||
generate_barcode = 'barcode/generate/',
|
generate_barcode = 'barcode/generate/',
|
||||||
news = 'news/',
|
news = 'news/',
|
||||||
global_status = 'generic/status/',
|
global_status = 'generic/status/',
|
||||||
|
@ -138,7 +138,7 @@ export function useStockFields({
|
|||||||
value: batchCode,
|
value: batchCode,
|
||||||
onValueChange: (value) => setBatchCode(value)
|
onValueChange: (value) => setBatchCode(value)
|
||||||
},
|
},
|
||||||
status_custom_key: {},
|
status: {},
|
||||||
expiry_date: {
|
expiry_date: {
|
||||||
// TODO: icon
|
// TODO: icon
|
||||||
},
|
},
|
||||||
@ -922,10 +922,14 @@ export function stockLocationFields(): ApiFormFieldSet {
|
|||||||
// Construct a set of fields for
|
// Construct a set of fields for
|
||||||
export function useTestResultFields({
|
export function useTestResultFields({
|
||||||
partId,
|
partId,
|
||||||
itemId
|
itemId,
|
||||||
|
templateId,
|
||||||
|
editTemplate = false
|
||||||
}: {
|
}: {
|
||||||
partId: number;
|
partId: number;
|
||||||
itemId: number;
|
itemId: number;
|
||||||
|
templateId: number | undefined;
|
||||||
|
editTemplate?: boolean;
|
||||||
}): ApiFormFieldSet {
|
}): ApiFormFieldSet {
|
||||||
// Valid field choices
|
// Valid field choices
|
||||||
const [choices, setChoices] = useState<any[]>([]);
|
const [choices, setChoices] = useState<any[]>([]);
|
||||||
@ -947,6 +951,7 @@ export function useTestResultFields({
|
|||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
template: {
|
template: {
|
||||||
|
disabled: !editTemplate && !!templateId,
|
||||||
filters: {
|
filters: {
|
||||||
include_inherited: true,
|
include_inherited: true,
|
||||||
part: partId
|
part: partId
|
||||||
@ -990,5 +995,13 @@ export function useTestResultFields({
|
|||||||
hidden: !includeTestStation
|
hidden: !includeTestStation
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [choices, fieldType, partId, itemId, includeTestStation]);
|
}, [
|
||||||
|
choices,
|
||||||
|
editTemplate,
|
||||||
|
fieldType,
|
||||||
|
partId,
|
||||||
|
itemId,
|
||||||
|
templateId,
|
||||||
|
includeTestStation
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
@ -36,9 +36,13 @@ export type TableState = {
|
|||||||
setRecordCount: (count: number) => void;
|
setRecordCount: (count: number) => void;
|
||||||
page: number;
|
page: number;
|
||||||
setPage: (page: number) => void;
|
setPage: (page: number) => void;
|
||||||
|
pageSize: number;
|
||||||
|
setPageSize: (pageSize: number) => void;
|
||||||
records: any[];
|
records: any[];
|
||||||
setRecords: (records: any[]) => void;
|
setRecords: (records: any[]) => void;
|
||||||
updateRecord: (record: any) => void;
|
updateRecord: (record: any) => void;
|
||||||
|
editable: boolean;
|
||||||
|
setEditable: (value: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,6 +101,7 @@ export function useTable(tableName: string): TableState {
|
|||||||
|
|
||||||
// Pagination data
|
// Pagination data
|
||||||
const [page, setPage] = useState<number>(1);
|
const [page, setPage] = useState<number>(1);
|
||||||
|
const [pageSize, setPageSize] = useState<number>(25);
|
||||||
|
|
||||||
// A list of hidden columns, saved to local storage
|
// A list of hidden columns, saved to local storage
|
||||||
const [hiddenColumns, setHiddenColumns] = useLocalStorage<string[]>({
|
const [hiddenColumns, setHiddenColumns] = useLocalStorage<string[]>({
|
||||||
@ -131,6 +136,8 @@ export function useTable(tableName: string): TableState {
|
|||||||
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
const [isLoading, setIsLoading] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const [editable, setEditable] = useState<boolean>(false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tableKey,
|
tableKey,
|
||||||
refreshTable,
|
refreshTable,
|
||||||
@ -154,8 +161,12 @@ export function useTable(tableName: string): TableState {
|
|||||||
setRecordCount,
|
setRecordCount,
|
||||||
page,
|
page,
|
||||||
setPage,
|
setPage,
|
||||||
|
pageSize,
|
||||||
|
setPageSize,
|
||||||
records,
|
records,
|
||||||
setRecords,
|
setRecords,
|
||||||
updateRecord
|
updateRecord,
|
||||||
|
editable,
|
||||||
|
setEditable
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -30,10 +30,7 @@ import {
|
|||||||
CancelItemAction,
|
CancelItemAction,
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction,
|
||||||
HoldItemAction,
|
HoldItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
@ -43,7 +40,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useBuildOrderFields } from '../../forms/BuildForms';
|
import { useBuildOrderFields } from '../../forms/BuildForms';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
useEditApiFormModal
|
useEditApiFormModal
|
||||||
@ -472,20 +468,9 @@ export default function BuildDetail() {
|
|||||||
/>,
|
/>,
|
||||||
<AdminButton model={ModelType.build} pk={build.pk} />,
|
<AdminButton model={ModelType.build} pk={build.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
model={ModelType.build}
|
||||||
ViewBarcodeAction({
|
pk={build.pk}
|
||||||
model: ModelType.build,
|
hash={build?.barcode_hash}
|
||||||
pk: build.pk
|
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
hidden: build?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
hidden: !build?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
/>,
|
/>,
|
||||||
<PrintingActions
|
<PrintingActions
|
||||||
modelType={ModelType.build}
|
modelType={ModelType.build}
|
||||||
|
@ -22,10 +22,7 @@ import {
|
|||||||
BarcodeActionDropdown,
|
BarcodeActionDropdown,
|
||||||
DeleteItemAction,
|
DeleteItemAction,
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
import { PageDetail } from '../../components/nav/PageDetail';
|
import { PageDetail } from '../../components/nav/PageDetail';
|
||||||
@ -34,7 +31,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useSupplierPartFields } from '../../forms/CompanyForms';
|
import { useSupplierPartFields } from '../../forms/CompanyForms';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
|
||||||
import { getDetailUrl } from '../../functions/urls';
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
@ -271,24 +267,10 @@ export default function SupplierPartDetail() {
|
|||||||
return [
|
return [
|
||||||
<AdminButton model={ModelType.supplierpart} pk={supplierPart.pk} />,
|
<AdminButton model={ModelType.supplierpart} pk={supplierPart.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
model={ModelType.supplierpart}
|
||||||
ViewBarcodeAction({
|
pk={supplierPart.pk}
|
||||||
model: ModelType.supplierpart,
|
hash={supplierPart.barcode_hash}
|
||||||
pk: supplierPart.pk
|
perm={user.hasChangeRole(UserRoles.purchase_order)}
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
hidden:
|
|
||||||
supplierPart.barcode_hash ||
|
|
||||||
!user.hasChangeRole(UserRoles.purchase_order),
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
hidden:
|
|
||||||
!supplierPart.barcode_hash ||
|
|
||||||
!user.hasChangeRole(UserRoles.purchase_order),
|
|
||||||
onClick: notYetImplemented
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
/>,
|
/>,
|
||||||
<ActionDropdown
|
<ActionDropdown
|
||||||
tooltip={t`Supplier Part Actions`}
|
tooltip={t`Supplier Part Actions`}
|
||||||
|
@ -51,10 +51,7 @@ import {
|
|||||||
BarcodeActionDropdown,
|
BarcodeActionDropdown,
|
||||||
DeleteItemAction,
|
DeleteItemAction,
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
import { PlaceholderPanel } from '../../components/items/Placeholder';
|
||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
@ -74,7 +71,6 @@ import {
|
|||||||
useTransferStockItem
|
useTransferStockItem
|
||||||
} from '../../forms/StockForms';
|
} from '../../forms/StockForms';
|
||||||
import { InvenTreeIcon } from '../../functions/icons';
|
import { InvenTreeIcon } from '../../functions/icons';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
|
||||||
import { getDetailUrl } from '../../functions/urls';
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
@ -993,20 +989,10 @@ export default function PartDetail() {
|
|||||||
return [
|
return [
|
||||||
<AdminButton model={ModelType.part} pk={part.pk} />,
|
<AdminButton model={ModelType.part} pk={part.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
model={ModelType.part}
|
||||||
ViewBarcodeAction({
|
pk={part.pk}
|
||||||
model: ModelType.part,
|
hash={part?.barcode_hash}
|
||||||
pk: part.pk
|
perm={user.hasChangeRole(UserRoles.part)}
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
hidden: part?.barcode_hash || !user.hasChangeRole(UserRoles.part),
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
hidden: !part?.barcode_hash || !user.hasChangeRole(UserRoles.part),
|
|
||||||
onClick: notYetImplemented
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
key="action_dropdown"
|
key="action_dropdown"
|
||||||
/>,
|
/>,
|
||||||
<PrintingActions
|
<PrintingActions
|
||||||
|
@ -24,10 +24,7 @@ import {
|
|||||||
CancelItemAction,
|
CancelItemAction,
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction,
|
||||||
HoldItemAction,
|
HoldItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
@ -39,7 +36,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms';
|
import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
useEditApiFormModal
|
useEditApiFormModal
|
||||||
@ -403,20 +399,9 @@ export default function PurchaseOrderDetail() {
|
|||||||
/>,
|
/>,
|
||||||
<AdminButton model={ModelType.purchaseorder} pk={order.pk} />,
|
<AdminButton model={ModelType.purchaseorder} pk={order.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
model={ModelType.purchaseorder}
|
||||||
ViewBarcodeAction({
|
pk={order.pk}
|
||||||
model: ModelType.purchaseorder,
|
hash={order?.barcode_hash}
|
||||||
pk: order.pk
|
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
hidden: order?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
hidden: !order?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
/>,
|
/>,
|
||||||
<PrintingActions
|
<PrintingActions
|
||||||
modelType={ModelType.purchaseorder}
|
modelType={ModelType.purchaseorder}
|
||||||
|
@ -23,10 +23,7 @@ import {
|
|||||||
CancelItemAction,
|
CancelItemAction,
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction,
|
||||||
HoldItemAction,
|
HoldItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
@ -38,7 +35,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useReturnOrderFields } from '../../forms/SalesOrderForms';
|
import { useReturnOrderFields } from '../../forms/SalesOrderForms';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
useEditApiFormModal
|
useEditApiFormModal
|
||||||
@ -404,20 +400,9 @@ export default function ReturnOrderDetail() {
|
|||||||
/>,
|
/>,
|
||||||
<AdminButton model={ModelType.returnorder} pk={order.pk} />,
|
<AdminButton model={ModelType.returnorder} pk={order.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
model={ModelType.returnorder}
|
||||||
ViewBarcodeAction({
|
pk={order.pk}
|
||||||
model: ModelType.returnorder,
|
hash={order?.barcode_hash}
|
||||||
pk: order.pk
|
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
hidden: order?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
hidden: !order?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
/>,
|
/>,
|
||||||
<PrintingActions
|
<PrintingActions
|
||||||
modelType={ModelType.returnorder}
|
modelType={ModelType.returnorder}
|
||||||
|
@ -26,10 +26,7 @@ import {
|
|||||||
CancelItemAction,
|
CancelItemAction,
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction,
|
||||||
HoldItemAction,
|
HoldItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
@ -41,7 +38,6 @@ import { ApiEndpoints } from '../../enums/ApiEndpoints';
|
|||||||
import { ModelType } from '../../enums/ModelType';
|
import { ModelType } from '../../enums/ModelType';
|
||||||
import { UserRoles } from '../../enums/Roles';
|
import { UserRoles } from '../../enums/Roles';
|
||||||
import { useSalesOrderFields } from '../../forms/SalesOrderForms';
|
import { useSalesOrderFields } from '../../forms/SalesOrderForms';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
useEditApiFormModal
|
useEditApiFormModal
|
||||||
@ -444,20 +440,9 @@ export default function SalesOrderDetail() {
|
|||||||
/>,
|
/>,
|
||||||
<AdminButton model={ModelType.salesorder} pk={order.pk} />,
|
<AdminButton model={ModelType.salesorder} pk={order.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
model={ModelType.salesorder}
|
||||||
ViewBarcodeAction({
|
pk={order.pk}
|
||||||
model: ModelType.salesorder,
|
hash={order?.barcode_hash}
|
||||||
pk: order.pk
|
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
hidden: order?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
hidden: !order?.barcode_hash,
|
|
||||||
onClick: notYetImplemented
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
/>,
|
/>,
|
||||||
<PrintingActions
|
<PrintingActions
|
||||||
modelType={ModelType.salesorder}
|
modelType={ModelType.salesorder}
|
||||||
|
@ -18,10 +18,7 @@ import {
|
|||||||
ActionDropdown,
|
ActionDropdown,
|
||||||
BarcodeActionDropdown,
|
BarcodeActionDropdown,
|
||||||
DeleteItemAction,
|
DeleteItemAction,
|
||||||
EditItemAction,
|
EditItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import { ApiIcon } from '../../components/items/ApiIcon';
|
import { ApiIcon } from '../../components/items/ApiIcon';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
@ -287,17 +284,9 @@ export default function Stock() {
|
|||||||
/>,
|
/>,
|
||||||
location.pk ? (
|
location.pk ? (
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
|
model={ModelType.stocklocation}
|
||||||
|
pk={location.pk}
|
||||||
actions={[
|
actions={[
|
||||||
ViewBarcodeAction({
|
|
||||||
model: ModelType.stocklocation,
|
|
||||||
pk: location.pk
|
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
name: 'Scan in stock items',
|
name: 'Scan in stock items',
|
||||||
icon: <InvenTreeIcon icon="stock" />,
|
icon: <InvenTreeIcon icon="stock" />,
|
||||||
|
@ -27,10 +27,7 @@ import {
|
|||||||
BarcodeActionDropdown,
|
BarcodeActionDropdown,
|
||||||
DeleteItemAction,
|
DeleteItemAction,
|
||||||
DuplicateItemAction,
|
DuplicateItemAction,
|
||||||
EditItemAction,
|
EditItemAction
|
||||||
LinkBarcodeAction,
|
|
||||||
UnlinkBarcodeAction,
|
|
||||||
ViewBarcodeAction
|
|
||||||
} from '../../components/items/ActionDropdown';
|
} from '../../components/items/ActionDropdown';
|
||||||
import { StylishText } from '../../components/items/StylishText';
|
import { StylishText } from '../../components/items/StylishText';
|
||||||
import InstanceDetail from '../../components/nav/InstanceDetail';
|
import InstanceDetail from '../../components/nav/InstanceDetail';
|
||||||
@ -50,7 +47,6 @@ import {
|
|||||||
useTransferStockItem
|
useTransferStockItem
|
||||||
} from '../../forms/StockForms';
|
} from '../../forms/StockForms';
|
||||||
import { InvenTreeIcon } from '../../functions/icons';
|
import { InvenTreeIcon } from '../../functions/icons';
|
||||||
import { notYetImplemented } from '../../functions/notifications';
|
|
||||||
import { getDetailUrl } from '../../functions/urls';
|
import { getDetailUrl } from '../../functions/urls';
|
||||||
import {
|
import {
|
||||||
useCreateApiFormModal,
|
useCreateApiFormModal,
|
||||||
@ -477,22 +473,10 @@ export default function StockDetail() {
|
|||||||
() => [
|
() => [
|
||||||
<AdminButton model={ModelType.stockitem} pk={stockitem.pk} />,
|
<AdminButton model={ModelType.stockitem} pk={stockitem.pk} />,
|
||||||
<BarcodeActionDropdown
|
<BarcodeActionDropdown
|
||||||
actions={[
|
model={ModelType.stockitem}
|
||||||
ViewBarcodeAction({
|
pk={stockitem.pk}
|
||||||
model: ModelType.stockitem,
|
hash={stockitem?.barcode_hash}
|
||||||
pk: stockitem.pk
|
perm={user.hasChangeRole(UserRoles.stock)}
|
||||||
}),
|
|
||||||
LinkBarcodeAction({
|
|
||||||
hidden:
|
|
||||||
stockitem?.barcode_hash || !user.hasChangeRole(UserRoles.stock),
|
|
||||||
onClick: notYetImplemented
|
|
||||||
}),
|
|
||||||
UnlinkBarcodeAction({
|
|
||||||
hidden:
|
|
||||||
!stockitem?.barcode_hash || !user.hasChangeRole(UserRoles.stock),
|
|
||||||
onClick: notYetImplemented
|
|
||||||
})
|
|
||||||
]}
|
|
||||||
/>,
|
/>,
|
||||||
<PrintingActions
|
<PrintingActions
|
||||||
modelType={ModelType.stockitem}
|
modelType={ModelType.stockitem}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { ApiFormFieldType } from '../components/forms/fields/ApiFormField';
|
||||||
|
|
||||||
export type TableColumnProps<T = any> = {
|
export type TableColumnProps<T = any> = {
|
||||||
accessor?: string; // The key in the record to access
|
accessor?: string; // The key in the record to access
|
||||||
title?: string; // The title of the column - Note: this may be supplied by the API, and is not required, but it can be overridden if required
|
title?: string; // The title of the column - Note: this may be supplied by the API, and is not required, but it can be overridden if required
|
||||||
@ -5,6 +7,8 @@ export type TableColumnProps<T = any> = {
|
|||||||
sortable?: boolean; // Whether the column is sortable
|
sortable?: boolean; // Whether the column is sortable
|
||||||
switchable?: boolean; // Whether the column is switchable
|
switchable?: boolean; // Whether the column is switchable
|
||||||
hidden?: boolean; // Whether the column is hidden
|
hidden?: boolean; // Whether the column is hidden
|
||||||
|
editable?: boolean; // Whether the value of this column can be edited
|
||||||
|
definition?: ApiFormFieldType; // Optional field definition for the column
|
||||||
render?: (record: T, index?: number) => any; // A custom render function
|
render?: (record: T, index?: number) => any; // A custom render function
|
||||||
filter?: any; // A custom filter function
|
filter?: any; // A custom filter function
|
||||||
filtering?: boolean; // Whether the column is filterable
|
filtering?: boolean; // Whether the column is filterable
|
||||||
|
@ -55,6 +55,7 @@ import { RowAction, RowActions } from './RowActions';
|
|||||||
import { TableSearchInput } from './Search';
|
import { TableSearchInput } from './Search';
|
||||||
|
|
||||||
const defaultPageSize: number = 25;
|
const defaultPageSize: number = 25;
|
||||||
|
const PAGE_SIZES = [10, 15, 20, 25, 50, 100, 500];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set of optional properties which can be passed to an InvenTreeTable component
|
* Set of optional properties which can be passed to an InvenTreeTable component
|
||||||
@ -74,7 +75,6 @@ const defaultPageSize: number = 25;
|
|||||||
* @param enableRefresh : boolean - Enable refresh actions
|
* @param enableRefresh : boolean - Enable refresh actions
|
||||||
* @param enableColumnSwitching : boolean - Enable column switching
|
* @param enableColumnSwitching : boolean - Enable column switching
|
||||||
* @param enableColumnCaching : boolean - Enable caching of column names via API
|
* @param enableColumnCaching : boolean - Enable caching of column names via API
|
||||||
* @param pageSize : number - Number of records per page
|
|
||||||
* @param barcodeActions : any[] - List of barcode actions
|
* @param barcodeActions : any[] - List of barcode actions
|
||||||
* @param tableFilters : TableFilter[] - List of custom filters
|
* @param tableFilters : TableFilter[] - List of custom filters
|
||||||
* @param tableActions : any[] - List of custom action groups
|
* @param tableActions : any[] - List of custom action groups
|
||||||
@ -100,7 +100,6 @@ export type InvenTreeTableProps<T = any> = {
|
|||||||
enableLabels?: boolean;
|
enableLabels?: boolean;
|
||||||
enableReports?: boolean;
|
enableReports?: boolean;
|
||||||
afterBulkDelete?: () => void;
|
afterBulkDelete?: () => void;
|
||||||
pageSize?: number;
|
|
||||||
barcodeActions?: React.ReactNode[];
|
barcodeActions?: React.ReactNode[];
|
||||||
tableFilters?: TableFilter[];
|
tableFilters?: TableFilter[];
|
||||||
tableActions?: React.ReactNode[];
|
tableActions?: React.ReactNode[];
|
||||||
@ -129,7 +128,6 @@ const defaultInvenTreeTableProps: InvenTreeTableProps = {
|
|||||||
enableRefresh: true,
|
enableRefresh: true,
|
||||||
enableSearch: true,
|
enableSearch: true,
|
||||||
enableSelection: false,
|
enableSelection: false,
|
||||||
pageSize: defaultPageSize,
|
|
||||||
defaultSortColumn: '',
|
defaultSortColumn: '',
|
||||||
barcodeActions: [],
|
barcodeActions: [],
|
||||||
tableFilters: [],
|
tableFilters: [],
|
||||||
@ -362,7 +360,8 @@ export function InvenTreeTable<T = any>({
|
|||||||
|
|
||||||
// Pagination
|
// Pagination
|
||||||
if (tableProps.enablePagination && paginate) {
|
if (tableProps.enablePagination && paginate) {
|
||||||
let pageSize = tableProps.pageSize ?? defaultPageSize;
|
let pageSize = tableState.pageSize ?? defaultPageSize;
|
||||||
|
if (pageSize != tableState.pageSize) tableState.setPageSize(pageSize);
|
||||||
queryParams.limit = pageSize;
|
queryParams.limit = pageSize;
|
||||||
queryParams.offset = (tableState.page - 1) * pageSize;
|
queryParams.offset = (tableState.page - 1) * pageSize;
|
||||||
}
|
}
|
||||||
@ -590,6 +589,12 @@ export function InvenTreeTable<T = any>({
|
|||||||
[props.onRowClick, props.onCellClick]
|
[props.onRowClick, props.onCellClick]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// pagination refresth table if pageSize changes
|
||||||
|
function updatePageSize(newData: number) {
|
||||||
|
tableState.setPageSize(newData);
|
||||||
|
tableState.refreshTable();
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{deleteRecords.modal}
|
{deleteRecords.modal}
|
||||||
@ -699,6 +704,7 @@ export function InvenTreeTable<T = any>({
|
|||||||
|
|
||||||
<DataTable
|
<DataTable
|
||||||
withTableBorder
|
withTableBorder
|
||||||
|
withColumnBorders
|
||||||
striped
|
striped
|
||||||
highlightOnHover
|
highlightOnHover
|
||||||
loaderType={loader}
|
loaderType={loader}
|
||||||
@ -706,7 +712,7 @@ export function InvenTreeTable<T = any>({
|
|||||||
idAccessor={tableProps.idAccessor}
|
idAccessor={tableProps.idAccessor}
|
||||||
minHeight={300}
|
minHeight={300}
|
||||||
totalRecords={tableState.recordCount}
|
totalRecords={tableState.recordCount}
|
||||||
recordsPerPage={tableProps.pageSize ?? defaultPageSize}
|
recordsPerPage={tableState.pageSize}
|
||||||
page={tableState.page}
|
page={tableState.page}
|
||||||
onPageChange={tableState.setPage}
|
onPageChange={tableState.setPage}
|
||||||
sortStatus={sortStatus}
|
sortStatus={sortStatus}
|
||||||
@ -734,6 +740,8 @@ export function InvenTreeTable<T = any>({
|
|||||||
overflow: 'hidden'
|
overflow: 'hidden'
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
|
recordsPerPageOptions={PAGE_SIZES}
|
||||||
|
onRecordsPerPageChange={updatePageSize}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Stack>
|
</Stack>
|
||||||
|
@ -67,7 +67,8 @@ export default function BuildOrderTestTable({
|
|||||||
|
|
||||||
const testResultFields: ApiFormFieldSet = useTestResultFields({
|
const testResultFields: ApiFormFieldSet = useTestResultFields({
|
||||||
partId: partId,
|
partId: partId,
|
||||||
itemId: selectedOutput
|
itemId: selectedOutput,
|
||||||
|
templateId: selectedTemplate
|
||||||
});
|
});
|
||||||
|
|
||||||
const createTestResult = useCreateApiFormModal({
|
const createTestResult = useCreateApiFormModal({
|
||||||
|
@ -238,15 +238,16 @@ export default function StockItemTestResultTable({
|
|||||||
];
|
];
|
||||||
}, [itemId]);
|
}, [itemId]);
|
||||||
|
|
||||||
const resultFields: ApiFormFieldSet = useTestResultFields({
|
|
||||||
partId: partId,
|
|
||||||
itemId: itemId
|
|
||||||
});
|
|
||||||
|
|
||||||
const [selectedTemplate, setSelectedTemplate] = useState<number | undefined>(
|
const [selectedTemplate, setSelectedTemplate] = useState<number | undefined>(
|
||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const resultFields: ApiFormFieldSet = useTestResultFields({
|
||||||
|
partId: partId,
|
||||||
|
itemId: itemId,
|
||||||
|
templateId: selectedTemplate
|
||||||
|
});
|
||||||
|
|
||||||
const newTestModal = useCreateApiFormModal({
|
const newTestModal = useCreateApiFormModal({
|
||||||
url: ApiEndpoints.stock_test_result_list,
|
url: ApiEndpoints.stock_test_result_list,
|
||||||
fields: useMemo(() => ({ ...resultFields }), [resultFields]),
|
fields: useMemo(() => ({ ...resultFields }), [resultFields]),
|
||||||
|
@ -59,9 +59,36 @@ test('PUI - Purchase Orders', async ({ page }) => {
|
|||||||
await page.getByRole('cell', { name: 'PO0013' }).click();
|
await page.getByRole('cell', { name: 'PO0013' }).click();
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('PUI - Purchase Orders - Barcodes', async ({ page }) => {
|
||||||
|
await doQuickLogin(page);
|
||||||
|
|
||||||
|
await page.goto(`${baseUrl}/purchasing/purchase-order/13/detail`);
|
||||||
|
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||||
|
|
||||||
// Display QR code
|
// Display QR code
|
||||||
await page.getByLabel('action-menu-barcode-actions').click();
|
await page.getByLabel('action-menu-barcode-actions').click();
|
||||||
await page.getByLabel('action-menu-barcode-actions-view').click();
|
await page.getByLabel('action-menu-barcode-actions-view').click();
|
||||||
await page.getByRole('img', { name: 'QR Code' }).waitFor();
|
await page.getByRole('img', { name: 'QR Code' }).waitFor();
|
||||||
|
await page.getByRole('banner').getByRole('button').click();
|
||||||
|
|
||||||
|
// Link to barcode
|
||||||
|
await page.getByLabel('action-menu-barcode-actions').click();
|
||||||
|
await page.getByLabel('action-menu-barcode-actions-link-barcode').click();
|
||||||
|
await page.getByRole('heading', { name: 'Link Barcode' }).waitFor();
|
||||||
|
await page
|
||||||
|
.getByPlaceholder('Scan barcode data here using')
|
||||||
|
.fill('1234567890');
|
||||||
|
await page.getByRole('button', { name: 'Link' }).click();
|
||||||
|
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||||
|
|
||||||
|
// Unlink barcode
|
||||||
|
await page.getByLabel('action-menu-barcode-actions').click();
|
||||||
|
await page.getByLabel('action-menu-barcode-actions-unlink-barcode').click();
|
||||||
|
await page.getByRole('heading', { name: 'Unlink Barcode' }).waitFor();
|
||||||
|
await page.getByText('This will remove the link to').waitFor();
|
||||||
|
await page.getByRole('button', { name: 'Unlink Barcode' }).click();
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.getByRole('button', { name: 'Issue Order' }).waitFor();
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user