first commit.
This commit is contained in:
290
src/config/Settings.tsx
Normal file
290
src/config/Settings.tsx
Normal file
@@ -0,0 +1,290 @@
|
||||
import React from 'react';
|
||||
|
||||
import { produce } from 'immer';
|
||||
import invariant from 'tiny-invariant';
|
||||
import {
|
||||
loadPluginConfigLookup,
|
||||
PluginConfigLookup,
|
||||
PluginConfigLookupItem,
|
||||
savePluginConfigLookup,
|
||||
} from '../common/config';
|
||||
import { filterConfigByPluginContext, getPluginContext, PluginContext } from '../common/context';
|
||||
import KintonePluginAlert from '../common/ui/KintonePluginAlert';
|
||||
import KintonePluginButton from '../common/ui/KintonePluginButton';
|
||||
import KintonePluginDesc from '../common/ui/KintonePluginDesc';
|
||||
import KintonePluginLabel from '../common/ui/KintonePluginLabel';
|
||||
import KintonePluginRequire from '../common/ui/KintonePluginRequire';
|
||||
import KintonePluginRow from '../common/ui/KintonePluginRow';
|
||||
import KintonePluginSelect, { KintonePluginSelectOptionData } from '../common/ui/KintonePluginSelect';
|
||||
import KintonePluginTable from '../common/ui/KintonePluginTable';
|
||||
import KintonePluginTableTd from '../common/ui/KintonePluginTableTd';
|
||||
import KintonePluginTableTh from '../common/ui/KintonePluginTableTh';
|
||||
import KintonePluginTitle from '../common/ui/KintonePluginTitle';
|
||||
|
||||
import moize from 'moize';
|
||||
import styles from './Settings.module.css';
|
||||
|
||||
const hasDuplicate = <T,>(items: T[], func: (a: T, b: T) => boolean = (a, b) => a === b): boolean => {
|
||||
return items.find((item, idx) => items.some((item2, idx2) => func(item, item2) && idx !== idx2)) != null;
|
||||
};
|
||||
|
||||
const hasDuplicateDestAttachmentFields = (rows: PluginConfigLookup): boolean => {
|
||||
return hasDuplicate<PluginConfigLookupItem>(rows, (a, b) => a.destAttachmentFieldCode === b.destAttachmentFieldCode);
|
||||
};
|
||||
|
||||
interface FieldSelectTableRowProps {
|
||||
context: PluginContext;
|
||||
row: PluginConfigLookupItem;
|
||||
onUpdate: (item: PluginConfigLookupItem) => void;
|
||||
onClickAddRow: () => void;
|
||||
onClickRemoveRow: () => void;
|
||||
}
|
||||
|
||||
const FieldSelectTableRow: React.FC<FieldSelectTableRowProps> = (props) => {
|
||||
const { context, row, onUpdate, onClickAddRow, onClickRemoveRow } = props;
|
||||
const defaultOption: KintonePluginSelectOptionData = React.useMemo(() => {
|
||||
return { value: '', label: '--', disabled: true };
|
||||
}, []);
|
||||
const lookupOptions: KintonePluginSelectOptionData[] = React.useMemo(() => {
|
||||
return [
|
||||
defaultOption,
|
||||
...context.lookupFields.map((property) => ({
|
||||
value: property.code,
|
||||
label: `${property.label} (${property.code})`,
|
||||
})),
|
||||
];
|
||||
}, [context.lookupFields, defaultOption]);
|
||||
|
||||
const srcAppId = context.lookupFields.find((field) => field.code === row.lookupFieldCode)?.lookup.relatedApp.app;
|
||||
const srcFileOptions: KintonePluginSelectOptionData[] = React.useMemo(() => {
|
||||
return [
|
||||
defaultOption,
|
||||
...(srcAppId != null
|
||||
? context.attachmentFields[srcAppId].map((property) => ({
|
||||
value: property.code,
|
||||
label: `${property.label} (${property.code})`,
|
||||
}))
|
||||
: []),
|
||||
];
|
||||
}, [srcAppId, context.attachmentFields, defaultOption]);
|
||||
|
||||
const destFileOptions: KintonePluginSelectOptionData[] = React.useMemo(() => {
|
||||
return [
|
||||
defaultOption,
|
||||
...(srcAppId != null
|
||||
? context.attachmentFields[context.appId].map((property) => ({
|
||||
value: property.code,
|
||||
label: `${property.label} (${property.code})`,
|
||||
}))
|
||||
: []),
|
||||
];
|
||||
}, [context.appId, context.attachmentFields, defaultOption, srcAppId]);
|
||||
|
||||
const handleOnChangeLookup = (code: string) => {
|
||||
const draft = {
|
||||
lookupFieldCode: code,
|
||||
srcAttachmentFieldCode: '',
|
||||
destAttachmentFieldCode: '',
|
||||
};
|
||||
onUpdate(draft);
|
||||
};
|
||||
const handleOnChangeSrcFile = (code: string) => {
|
||||
const draft = {
|
||||
lookupFieldCode: row.lookupFieldCode,
|
||||
srcAttachmentFieldCode: code,
|
||||
destAttachmentFieldCode: '',
|
||||
};
|
||||
onUpdate(draft);
|
||||
};
|
||||
|
||||
const handleOnChangeDestFile = (code: string) => {
|
||||
const draft = {
|
||||
lookupFieldCode: row.lookupFieldCode,
|
||||
srcAttachmentFieldCode: row.srcAttachmentFieldCode,
|
||||
destAttachmentFieldCode: code,
|
||||
};
|
||||
onUpdate(draft);
|
||||
};
|
||||
|
||||
const handleOnClickAddRow: React.MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
e.preventDefault();
|
||||
onClickAddRow();
|
||||
};
|
||||
|
||||
const handleOnClickRemoveRow: React.MouseEventHandler<HTMLButtonElement> = (e) => {
|
||||
e.preventDefault();
|
||||
onClickRemoveRow();
|
||||
};
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<KintonePluginTableTd variant="control">
|
||||
<KintonePluginSelect value={row.lookupFieldCode} onChange={handleOnChangeLookup} options={lookupOptions} />
|
||||
</KintonePluginTableTd>
|
||||
<KintonePluginTableTd variant="control">
|
||||
<KintonePluginSelect
|
||||
value={row.srcAttachmentFieldCode}
|
||||
onChange={handleOnChangeSrcFile}
|
||||
options={srcFileOptions}
|
||||
/>
|
||||
</KintonePluginTableTd>
|
||||
<KintonePluginTableTd variant="control">
|
||||
<KintonePluginSelect
|
||||
value={row.destAttachmentFieldCode}
|
||||
onChange={handleOnChangeDestFile}
|
||||
options={destFileOptions}
|
||||
/>
|
||||
</KintonePluginTableTd>
|
||||
<KintonePluginTableTd variant="operation">
|
||||
<KintonePluginButton variant="add-row-image" onClick={handleOnClickAddRow} />
|
||||
<KintonePluginButton variant="remove-row-image" onClick={handleOnClickRemoveRow} />
|
||||
</KintonePluginTableTd>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const cachedPluginContext = moize.promise(getPluginContext);
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
const appId = kintone.app.getId();
|
||||
invariant(appId, 'The app ID is not available. Please ensure you are on a Kintone app page.');
|
||||
const context = React.use(cachedPluginContext(appId));
|
||||
|
||||
const [mappings, setMappings] = React.useState<PluginConfigLookup>(
|
||||
() =>
|
||||
filterConfigByPluginContext(loadPluginConfigLookup(), context) ?? [
|
||||
{ lookupFieldCode: '', srcAttachmentFieldCode: '', destAttachmentFieldCode: '' },
|
||||
],
|
||||
);
|
||||
|
||||
const [error, setError] = React.useState<string>('');
|
||||
|
||||
const handleOnUpdateFieldSelect = (idx: number, item: PluginConfigLookupItem) => {
|
||||
setError('');
|
||||
setMappings(
|
||||
produce((draft) => {
|
||||
draft[idx] = item;
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const handleOnAddFieldSelect = (idx: number) => {
|
||||
setError('');
|
||||
if (mappings.length < context.attachmentFields[context.appId].length) {
|
||||
setMappings(
|
||||
produce((draft) => {
|
||||
draft.splice(idx + 1, 0, {
|
||||
lookupFieldCode: '',
|
||||
srcAttachmentFieldCode: '',
|
||||
destAttachmentFieldCode: '',
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnRemoveFieldSelect = (idx: number) => {
|
||||
setError('');
|
||||
if (mappings.length === 1) {
|
||||
setMappings(
|
||||
produce((draft) => {
|
||||
draft[idx] = {
|
||||
lookupFieldCode: '',
|
||||
srcAttachmentFieldCode: '',
|
||||
destAttachmentFieldCode: '',
|
||||
};
|
||||
}),
|
||||
);
|
||||
} else if (mappings.length > 1) {
|
||||
setMappings(
|
||||
produce((draft) => {
|
||||
draft.splice(idx, 1);
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnSubmit = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (mappings.find((item) => item.destAttachmentFieldCode === '') != null) {
|
||||
setError('Incomplete field mapping found');
|
||||
} else if (hasDuplicateDestAttachmentFields(mappings)) {
|
||||
setError('Same destination attachment fields found');
|
||||
} else {
|
||||
setError('');
|
||||
savePluginConfigLookup(mappings, () => {
|
||||
alert('The plug-in settings have been saved. Please update the app!');
|
||||
window.location.href = `../../flow?app=${appId}`;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handleOnClickCancel = () => {
|
||||
setError('');
|
||||
window.location.href = `../../${appId}/plugin/`;
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="settings">
|
||||
<KintonePluginLabel>Settings for the Kintone Lookup File Sync plugin</KintonePluginLabel>
|
||||
{error !== '' && (
|
||||
<KintonePluginRow>
|
||||
<KintonePluginAlert>{error}</KintonePluginAlert>
|
||||
</KintonePluginRow>
|
||||
)}
|
||||
<form onSubmit={handleOnSubmit}>
|
||||
<KintonePluginRow>
|
||||
<KintonePluginTitle>
|
||||
Field Mappings<KintonePluginRequire>*</KintonePluginRequire>
|
||||
</KintonePluginTitle>
|
||||
<KintonePluginDesc>
|
||||
Select lookup and attachment fields that used for the file synchronization.
|
||||
</KintonePluginDesc>
|
||||
{context.lookupFields.length === 0 ? (
|
||||
<KintonePluginAlert>
|
||||
No lookup fields found in the app. Please add a lookup field to use this plugin.
|
||||
</KintonePluginAlert>
|
||||
) : (
|
||||
<KintonePluginTable>
|
||||
<thead>
|
||||
<tr>
|
||||
<KintonePluginTableTh variant="title">Lookup Field</KintonePluginTableTh>
|
||||
<KintonePluginTableTh variant="title">Source Attachment Field (in Related App)</KintonePluginTableTh>
|
||||
<KintonePluginTableTh variant="title">Destination Attachment Field</KintonePluginTableTh>
|
||||
<KintonePluginTableTh variant="blankspace" />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{mappings.map((row, idx) => (
|
||||
<FieldSelectTableRow
|
||||
key={idx}
|
||||
context={context}
|
||||
row={row}
|
||||
onUpdate={(draft) => {
|
||||
handleOnUpdateFieldSelect(idx, draft);
|
||||
}}
|
||||
onClickAddRow={() => {
|
||||
handleOnAddFieldSelect(idx);
|
||||
}}
|
||||
onClickRemoveRow={() => {
|
||||
handleOnRemoveFieldSelect(idx);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</tbody>
|
||||
</KintonePluginTable>
|
||||
)}
|
||||
</KintonePluginRow>
|
||||
<KintonePluginRow className={styles.buttons}>
|
||||
<KintonePluginButton variant="dialog-cancel" type="button" onClick={handleOnClickCancel}>
|
||||
Cancel
|
||||
</KintonePluginButton>
|
||||
<KintonePluginButton variant="dialog-ok" type="submit">
|
||||
Save
|
||||
</KintonePluginButton>
|
||||
</KintonePluginRow>
|
||||
</form>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
export default Settings;
|
Reference in New Issue
Block a user