Frontend updates
This commit is contained in:
parent
e31a242238
commit
9a386428ba
|
|
@ -57,7 +57,7 @@ import {
|
|||
} from '@tabler/icons-react';
|
||||
import { useShallow } from 'zustand/react/shallow';
|
||||
import { formatDate } from '../../defaults/formatters';
|
||||
import { useNoteFields } from '../../forms/CommonForms';
|
||||
import { useNoteFields, useNoteTemplateFields } from '../../forms/CommonForms';
|
||||
import {
|
||||
useCreateApiFormModal,
|
||||
useDeleteApiFormModal,
|
||||
|
|
@ -111,10 +111,12 @@ function NoteInfoHover({ note }: { note: any }) {
|
|||
export default function NotesEditor({
|
||||
modelType,
|
||||
modelId,
|
||||
templateMode = false,
|
||||
setDirtyCallback
|
||||
}: Readonly<{
|
||||
modelType: ModelType;
|
||||
modelId: number;
|
||||
modelType?: ModelType;
|
||||
modelId?: number;
|
||||
templateMode?: boolean;
|
||||
setDirtyCallback?: (dirty: boolean) => void;
|
||||
}>) {
|
||||
const api = useApi();
|
||||
|
|
@ -173,23 +175,22 @@ export default function NotesEditor({
|
|||
onUpdate: () => setIsDirty(true)
|
||||
});
|
||||
|
||||
// Fetch the available notes for the given model type and ID
|
||||
// Fetch the available notes for the given model type and ID (or all templates)
|
||||
const notesQuery = useQuery({
|
||||
queryKey: ['notes', modelType, modelId],
|
||||
queryKey: ['notes', modelType, modelId, templateMode],
|
||||
queryFn: async () => {
|
||||
const params: Record<string, any> = templateMode
|
||||
? { template: true }
|
||||
: { model_id: modelId, model_type: modelType };
|
||||
|
||||
return api
|
||||
.get(apiUrl(ApiEndpoints.note_list), {
|
||||
params: {
|
||||
model_id: modelId,
|
||||
model_type: modelType
|
||||
}
|
||||
})
|
||||
.get(apiUrl(ApiEndpoints.note_list), { params })
|
||||
.then((response) => response.data ?? []);
|
||||
},
|
||||
staleTime: 0,
|
||||
refetchOnWindowFocus: false,
|
||||
refetchOnMount: true,
|
||||
enabled: !!modelId && !!modelType
|
||||
enabled: templateMode ? true : !!modelId && !!modelType
|
||||
});
|
||||
|
||||
const [selectedNote, setSelectedNote] = useState<any>(undefined);
|
||||
|
|
@ -244,11 +245,15 @@ export default function NotesEditor({
|
|||
|
||||
const canEdit: boolean = useMemo(
|
||||
() =>
|
||||
user.hasChangePermission(modelType) &&
|
||||
(templateMode
|
||||
? user.isStaff()
|
||||
: modelType
|
||||
? user.hasChangePermission(modelType)
|
||||
: false) &&
|
||||
notesQuery.isFetched &&
|
||||
notesQuery.isSuccess &&
|
||||
!!notesQuery.data,
|
||||
[user, modelType, notesQuery]
|
||||
[user, modelType, templateMode, notesQuery]
|
||||
);
|
||||
|
||||
const isInTable = useEditorState({
|
||||
|
|
@ -271,11 +276,16 @@ export default function NotesEditor({
|
|||
return notesQuery.data && notesQuery.data.length > 0;
|
||||
}, [notesQuery.data]);
|
||||
|
||||
const noteFields = useNoteFields({ modelType: modelType, modelId: modelId });
|
||||
const noteFields = useNoteFields({
|
||||
modelType: modelType!,
|
||||
modelId: modelId!
|
||||
});
|
||||
const noteTemplateFields = useNoteTemplateFields();
|
||||
const activeFields = templateMode ? noteTemplateFields : noteFields;
|
||||
|
||||
const createNote = useCreateApiFormModal({
|
||||
title: t`Add Note`,
|
||||
fields: noteFields,
|
||||
title: templateMode ? t`Add Note Template` : t`Add Note`,
|
||||
fields: activeFields,
|
||||
url: apiUrl(ApiEndpoints.note_list),
|
||||
method: 'POST',
|
||||
successMessage: null,
|
||||
|
|
@ -287,7 +297,7 @@ export default function NotesEditor({
|
|||
});
|
||||
|
||||
const deleteNote = useDeleteApiFormModal({
|
||||
title: t`Delete Note`,
|
||||
title: templateMode ? t`Delete Note Template` : t`Delete Note`,
|
||||
url: apiUrl(ApiEndpoints.note_list),
|
||||
pk: selectedNoteId,
|
||||
onFormSuccess: () => {
|
||||
|
|
@ -297,8 +307,8 @@ export default function NotesEditor({
|
|||
});
|
||||
|
||||
const editNote = useEditApiFormModal({
|
||||
title: t`Edit Note`,
|
||||
fields: noteFields,
|
||||
title: templateMode ? t`Edit Note Template` : t`Edit Note`,
|
||||
fields: activeFields,
|
||||
url: apiUrl(ApiEndpoints.note_list),
|
||||
pk: selectedNoteId,
|
||||
onFormSuccess: (response: any) => {
|
||||
|
|
@ -442,9 +452,7 @@ export default function NotesEditor({
|
|||
tooltip={t`Note Actions`}
|
||||
actions={[
|
||||
EditItemAction({
|
||||
hidden:
|
||||
!selectedNote ||
|
||||
!user.hasChangePermission(modelType),
|
||||
hidden: !selectedNote || !canEdit,
|
||||
onClick: () => {
|
||||
editNote.open();
|
||||
}
|
||||
|
|
@ -452,7 +460,11 @@ export default function NotesEditor({
|
|||
DeleteItemAction({
|
||||
hidden:
|
||||
!selectedNote ||
|
||||
!user.hasDeletePermission(modelType),
|
||||
!(templateMode
|
||||
? user.isStaff()
|
||||
: modelType
|
||||
? user.hasDeletePermission(modelType)
|
||||
: false),
|
||||
onClick: () => {
|
||||
deleteNote.open();
|
||||
}
|
||||
|
|
@ -626,7 +638,7 @@ export default function NotesEditor({
|
|||
</RichTextEditor>
|
||||
) : (
|
||||
<Alert title={t`Notes`} icon={<IconInfoCircle />}>
|
||||
{t`There are no notes yet for this item.`}
|
||||
{t`There are no notes here yet.`}
|
||||
</Alert>
|
||||
)}
|
||||
</Paper>
|
||||
|
|
|
|||
|
|
@ -285,6 +285,24 @@ export function useParameterFields({
|
|||
]);
|
||||
}
|
||||
|
||||
export function useNoteTemplateFields(): ApiFormFieldSet {
|
||||
return useMemo(() => {
|
||||
return {
|
||||
template: {
|
||||
hidden: true,
|
||||
value: true
|
||||
},
|
||||
model_type: {
|
||||
label: t`Model Type`,
|
||||
description: t`Limit this template to a specific model type, or leave blank for all models`,
|
||||
required: false
|
||||
},
|
||||
title: {},
|
||||
description: {}
|
||||
};
|
||||
}, []);
|
||||
}
|
||||
|
||||
export function useNoteFields({
|
||||
modelType,
|
||||
modelId
|
||||
|
|
@ -292,6 +310,27 @@ export function useNoteFields({
|
|||
modelType: ModelType;
|
||||
modelId: number;
|
||||
}): ApiFormFieldSet {
|
||||
const api = useApi();
|
||||
|
||||
const [title, setTitle] = useState<string>('');
|
||||
const [description, setDescription] = useState<string>('');
|
||||
const [content, setContent] = useState<string>('');
|
||||
|
||||
const fetchTemplate = useCallback(
|
||||
(pk: number | null) => {
|
||||
if (!pk) return;
|
||||
api
|
||||
.get(apiUrl(ApiEndpoints.note_list, pk))
|
||||
.then((response) => {
|
||||
setTitle(response.data.title ?? '');
|
||||
setDescription(response.data.description ?? '');
|
||||
setContent(response.data.content ?? '');
|
||||
})
|
||||
.catch(() => {});
|
||||
},
|
||||
[api]
|
||||
);
|
||||
|
||||
return useMemo(() => {
|
||||
return {
|
||||
model_type: {
|
||||
|
|
@ -302,9 +341,34 @@ export function useNoteFields({
|
|||
hidden: true,
|
||||
value: modelId
|
||||
},
|
||||
title: {},
|
||||
description: {},
|
||||
primary: {}
|
||||
template_source: {
|
||||
field_type: 'related field',
|
||||
label: t`From Template`,
|
||||
description: t`Optionally pre-fill this note from an existing template`,
|
||||
api_url: apiUrl(ApiEndpoints.note_list),
|
||||
filters: {
|
||||
template: true,
|
||||
model_type: modelType
|
||||
},
|
||||
pk_field: 'pk',
|
||||
render_description_field: 'description',
|
||||
required: false,
|
||||
onValueChange: (value: any) => fetchTemplate(value),
|
||||
value: null
|
||||
},
|
||||
title: {
|
||||
value: title,
|
||||
onValueChange: (value: any) => setTitle(value)
|
||||
},
|
||||
description: {
|
||||
value: description,
|
||||
onValueChange: (value: any) => setDescription(value)
|
||||
},
|
||||
primary: {},
|
||||
content: {
|
||||
hidden: true,
|
||||
value: content
|
||||
}
|
||||
};
|
||||
}, [modelType, modelId]);
|
||||
}, [modelType, modelId, title, description, content, fetchTemplate]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import {
|
|||
IconList,
|
||||
IconListDetails,
|
||||
IconMail,
|
||||
IconNotes,
|
||||
IconPackages,
|
||||
IconPlugConnected,
|
||||
IconQrcode,
|
||||
|
|
@ -71,6 +72,8 @@ const MachineManagementPanel = Loadable(
|
|||
|
||||
const ParameterPanel = Loadable(lazy(() => import('./ParameterPanel')));
|
||||
|
||||
const NoteTemplatePanel = Loadable(lazy(() => import('./NoteTemplatePanel')));
|
||||
|
||||
const ErrorReportTable = Loadable(
|
||||
lazy(() => import('../../../../tables/settings/ErrorTable'))
|
||||
);
|
||||
|
|
@ -195,6 +198,13 @@ export default function AdminCenter() {
|
|||
content: <ParameterPanel />,
|
||||
hidden: !user.hasViewRole(UserRoles.part)
|
||||
},
|
||||
{
|
||||
name: 'notes',
|
||||
label: t`Note Templates`,
|
||||
icon: <IconNotes />,
|
||||
content: <NoteTemplatePanel />,
|
||||
hidden: !user.isStaff()
|
||||
},
|
||||
{
|
||||
name: 'category-parameters',
|
||||
label: t`Category Parameters`,
|
||||
|
|
@ -274,6 +284,7 @@ export default function AdminCenter() {
|
|||
panelIDs: [
|
||||
'parameters',
|
||||
'category-parameters',
|
||||
'notes',
|
||||
'location-types',
|
||||
'stocktake'
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { t } from '@lingui/core/macro';
|
||||
import { Alert, Stack } from '@mantine/core';
|
||||
import { IconInfoCircle } from '@tabler/icons-react';
|
||||
import NotesEditor from '../../../../components/editors/NotesEditor';
|
||||
|
||||
export default function NoteTemplatePanel() {
|
||||
return (
|
||||
<Stack gap='xs'>
|
||||
<Alert color='blue' icon={<IconInfoCircle />} title={t`Note Templates`}>
|
||||
{t`Note templates can be used to create pre-defined notes which can be easily added to any model instance.`}
|
||||
</Alert>
|
||||
<NotesEditor templateMode />
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue