mirror of
https://github.com/inventree/InvenTree.git
synced 2026-09-01 09:41:22 +00:00
Enhance editing logic
This commit is contained in:
@@ -37,12 +37,14 @@ import {
|
|||||||
Tooltip
|
Tooltip
|
||||||
} from '@mantine/core';
|
} from '@mantine/core';
|
||||||
import {
|
import {
|
||||||
|
IconCheck,
|
||||||
IconCirclePlus,
|
IconCirclePlus,
|
||||||
IconColumnInsertLeft,
|
IconColumnInsertLeft,
|
||||||
IconColumnInsertRight,
|
IconColumnInsertRight,
|
||||||
IconColumnRemove,
|
IconColumnRemove,
|
||||||
IconDeviceFloppy,
|
IconDeviceFloppy,
|
||||||
IconInfoCircle,
|
IconInfoCircle,
|
||||||
|
IconPencil,
|
||||||
IconPhoto,
|
IconPhoto,
|
||||||
IconReload,
|
IconReload,
|
||||||
IconRowInsertBottom,
|
IconRowInsertBottom,
|
||||||
@@ -108,20 +110,18 @@ function NoteInfoHover({ note }: { note: any }) {
|
|||||||
|
|
||||||
export default function NotesEditor({
|
export default function NotesEditor({
|
||||||
modelType,
|
modelType,
|
||||||
modelId,
|
modelId
|
||||||
editable: _editable,
|
|
||||||
setDirtyCallback: _setDirtyCallback
|
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
modelType: ModelType;
|
modelType: ModelType;
|
||||||
modelId: number;
|
modelId: number;
|
||||||
editable?: boolean;
|
|
||||||
setDirtyCallback?: (dirty: boolean) => void;
|
|
||||||
}>) {
|
}>) {
|
||||||
const api = useApi();
|
const api = useApi();
|
||||||
const user = useUserState();
|
const user = useUserState();
|
||||||
const [_language] = useLocalState(useShallow((s) => [s.language]));
|
const [_language] = useLocalState(useShallow((s) => [s.language]));
|
||||||
const [searchParams, setSearchParams] = useSearchParams();
|
const [searchParams, setSearchParams] = useSearchParams();
|
||||||
|
|
||||||
|
const [isEditing, setIsEditing] = useState<boolean>(false);
|
||||||
|
|
||||||
const [isDirty, setIsDirty] = useState(false);
|
const [isDirty, setIsDirty] = useState(false);
|
||||||
|
|
||||||
const [selectedNoteId, setSelectedNoteId] = useState<number | undefined>(
|
const [selectedNoteId, setSelectedNoteId] = useState<number | undefined>(
|
||||||
@@ -151,6 +151,7 @@ export default function NotesEditor({
|
|||||||
}, [uploadFile]);
|
}, [uploadFile]);
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
|
editable: false,
|
||||||
extensions: [
|
extensions: [
|
||||||
StarterKit.configure({
|
StarterKit.configure({
|
||||||
link: { openOnClick: false }
|
link: { openOnClick: false }
|
||||||
@@ -210,6 +211,7 @@ export default function NotesEditor({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
// setIsEditing(false);
|
||||||
loadNote(selectedNoteId ?? -1);
|
loadNote(selectedNoteId ?? -1);
|
||||||
}, [editor, selectedNoteId, notesQuery.data]);
|
}, [editor, selectedNoteId, notesQuery.data]);
|
||||||
|
|
||||||
@@ -238,7 +240,7 @@ export default function NotesEditor({
|
|||||||
setSelectedNoteId((primary ?? notesQuery.data[0])?.pk ?? undefined);
|
setSelectedNoteId((primary ?? notesQuery.data[0])?.pk ?? undefined);
|
||||||
}, [notesQuery.data]);
|
}, [notesQuery.data]);
|
||||||
|
|
||||||
const canEdit = useMemo(
|
const canEdit: boolean = useMemo(
|
||||||
() =>
|
() =>
|
||||||
user.hasChangePermission(modelType) &&
|
user.hasChangePermission(modelType) &&
|
||||||
notesQuery.isFetched &&
|
notesQuery.isFetched &&
|
||||||
@@ -254,8 +256,8 @@ export default function NotesEditor({
|
|||||||
|
|
||||||
// Sync editor editable state when permissions change
|
// Sync editor editable state when permissions change
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
editor?.setEditable(canEdit);
|
editor?.setEditable(canEdit && isEditing);
|
||||||
}, [editor, canEdit]);
|
}, [editor, canEdit, isEditing]);
|
||||||
|
|
||||||
const hasNotes = useMemo(() => {
|
const hasNotes = useMemo(() => {
|
||||||
return notesQuery.data && notesQuery.data.length > 0;
|
return notesQuery.data && notesQuery.data.length > 0;
|
||||||
@@ -381,11 +383,21 @@ export default function NotesEditor({
|
|||||||
</Group>
|
</Group>
|
||||||
{canEdit && (
|
{canEdit && (
|
||||||
<Group justify='right' gap='xs'>
|
<Group justify='right' gap='xs'>
|
||||||
{isDirty && (
|
{!isEditing && (
|
||||||
|
<Tooltip label={t`Edit note`}>
|
||||||
|
<ActionIcon
|
||||||
|
variant='transparent'
|
||||||
|
onClick={() => setIsEditing(true)}
|
||||||
|
>
|
||||||
|
<IconPencil />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
{isEditing && isDirty && (
|
||||||
<Badge color='yellow'>{t`Unsaved Changes`}</Badge>
|
<Badge color='yellow'>{t`Unsaved Changes`}</Badge>
|
||||||
)}
|
)}
|
||||||
{isDirty && (
|
{isEditing && isDirty && (
|
||||||
<Tooltip label={t`Save Notes (Ctrl+S)`}>
|
<Tooltip label={t`Save note`}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant='transparent'
|
variant='transparent'
|
||||||
color={'green'}
|
color={'green'}
|
||||||
@@ -396,8 +408,8 @@ export default function NotesEditor({
|
|||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
{isDirty && (
|
{isEditing && isDirty && (
|
||||||
<Tooltip label={t`Reset Notes`}>
|
<Tooltip label={t`Reset note content`}>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
variant='transparent'
|
variant='transparent'
|
||||||
onClick={reloadNote}
|
onClick={reloadNote}
|
||||||
@@ -407,6 +419,17 @@ export default function NotesEditor({
|
|||||||
</ActionIcon>
|
</ActionIcon>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
)}
|
)}
|
||||||
|
{isEditing && !isDirty && (
|
||||||
|
<Tooltip label={t`Finish editing`}>
|
||||||
|
<ActionIcon
|
||||||
|
variant='transparent'
|
||||||
|
onClick={() => setIsEditing(false)}
|
||||||
|
color='green'
|
||||||
|
>
|
||||||
|
<IconCheck />
|
||||||
|
</ActionIcon>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
<OptionsActionDropdown
|
<OptionsActionDropdown
|
||||||
tooltip={t`Note Actions`}
|
tooltip={t`Note Actions`}
|
||||||
actions={[
|
actions={[
|
||||||
@@ -440,7 +463,7 @@ export default function NotesEditor({
|
|||||||
editor={editor}
|
editor={editor}
|
||||||
style={{ minHeight: '400px' }}
|
style={{ minHeight: '400px' }}
|
||||||
>
|
>
|
||||||
{canEdit && (
|
{canEdit && isEditing && (
|
||||||
<RichTextEditor.Toolbar sticky>
|
<RichTextEditor.Toolbar sticky>
|
||||||
<RichTextEditor.ControlsGroup>
|
<RichTextEditor.ControlsGroup>
|
||||||
<RichTextEditor.Bold />
|
<RichTextEditor.Bold />
|
||||||
@@ -602,15 +625,16 @@ export default function NotesEditor({
|
|||||||
</Box>
|
</Box>
|
||||||
<Paper p='xs' shadow='sm' withBorder style={{ width: '200px' }}>
|
<Paper p='xs' shadow='sm' withBorder style={{ width: '200px' }}>
|
||||||
<Stack gap='xs'>
|
<Stack gap='xs'>
|
||||||
<Button
|
{canEdit && (
|
||||||
color='green'
|
<Button
|
||||||
leftSection={<IconCirclePlus />}
|
color='green'
|
||||||
onClick={createNote.open}
|
leftSection={<IconCirclePlus />}
|
||||||
// disabled={!canEdit || isDirty}
|
onClick={createNote.open}
|
||||||
>
|
disabled={isEditing}
|
||||||
{t`Add Note`}
|
>
|
||||||
</Button>
|
{t`Add Note`}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
<Tabs
|
<Tabs
|
||||||
orientation='vertical'
|
orientation='vertical'
|
||||||
placement='right'
|
placement='right'
|
||||||
@@ -620,7 +644,7 @@ export default function NotesEditor({
|
|||||||
{notesQuery.data?.map((note: any) => (
|
{notesQuery.data?.map((note: any) => (
|
||||||
<Tabs.Tab
|
<Tabs.Tab
|
||||||
key={note.pk}
|
key={note.pk}
|
||||||
disabled={isDirty}
|
disabled={isEditing}
|
||||||
value={note.pk?.toString()}
|
value={note.pk?.toString()}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedNoteId(note.pk);
|
setSelectedNoteId(note.pk);
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { IconNotes } from '@tabler/icons-react';
|
|||||||
|
|
||||||
import type { ModelType } from '@lib/enums/ModelType';
|
import type { ModelType } from '@lib/enums/ModelType';
|
||||||
import type { PanelType } from '@lib/types/Panel';
|
import type { PanelType } from '@lib/types/Panel';
|
||||||
import { useUserState } from '../../states/UserState';
|
|
||||||
import NotesEditor from '../editors/NotesEditor';
|
import NotesEditor from '../editors/NotesEditor';
|
||||||
|
|
||||||
// const NotesEditor = lazy(() => import('../editors/NotesEditor'));
|
// const NotesEditor = lazy(() => import('../editors/NotesEditor'));
|
||||||
@@ -20,8 +19,6 @@ export default function NotesPanel({
|
|||||||
editable?: boolean;
|
editable?: boolean;
|
||||||
has_note?: boolean;
|
has_note?: boolean;
|
||||||
}): PanelType {
|
}): PanelType {
|
||||||
const user = useUserState.getState();
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: 'notes',
|
name: 'notes',
|
||||||
label: t`Notes`,
|
label: t`Notes`,
|
||||||
@@ -29,11 +26,7 @@ export default function NotesPanel({
|
|||||||
notification_dot: has_note ? 'info' : null,
|
notification_dot: has_note ? 'info' : null,
|
||||||
content:
|
content:
|
||||||
model_type && model_id ? (
|
model_type && model_id ? (
|
||||||
<NotesEditor
|
<NotesEditor modelType={model_type} modelId={model_id} />
|
||||||
modelType={model_type}
|
|
||||||
modelId={model_id}
|
|
||||||
editable={editable ?? user.hasChangePermission(model_type)}
|
|
||||||
/>
|
|
||||||
) : (
|
) : (
|
||||||
<Skeleton />
|
<Skeleton />
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user