fix: customFields was not persisted on import and force Custom Field keys to be lower case (#849)

* refactor: force Custom Field keys to be lower case

* fix: customFields was not persisted on import
This commit is contained in:
Alex Christoffer Rasmussen
2024-03-28 13:51:32 +01:00
committed by GitHub
parent df8b76305e
commit a86496ed27
6 changed files with 42 additions and 40 deletions
@@ -11,6 +11,7 @@ import CustomFieldForm from './CustomFieldForm';
import style from './ProjectSettingsPanel.module.scss'; import style from './ProjectSettingsPanel.module.scss';
interface CustomFieldEntryProps { interface CustomFieldEntryProps {
field: string;
colour: string; colour: string;
label: string; label: string;
onEdit: (label: CustomFieldLabel, patch: CustomField) => Promise<void>; onEdit: (label: CustomFieldLabel, patch: CustomField) => Promise<void>;
@@ -18,13 +19,11 @@ interface CustomFieldEntryProps {
} }
export default function CustomFieldEntry(props: CustomFieldEntryProps) { export default function CustomFieldEntry(props: CustomFieldEntryProps) {
const { colour, label, onEdit, onDelete } = props; const { colour, label, onEdit, onDelete, field } = props;
const [isEditing, setIsEditing] = useState(false); const [isEditing, setIsEditing] = useState(false);
const handleEdit = async (patch: CustomField) => { const handleEdit = async (patch: CustomField) => {
const oldLabel = label; await onEdit(field, patch);
await onEdit(oldLabel, patch);
setIsEditing(false); setIsEditing(false);
}; };
@@ -64,7 +63,7 @@ export default function CustomFieldEntry(props: CustomFieldEntryProps) {
color='#FA5656' // $red-500 color='#FA5656' // $red-500
icon={<IoTrash />} icon={<IoTrash />}
aria-label='Delete entry' aria-label='Delete entry'
onClick={() => onDelete(label)} onClick={() => onDelete(field)}
/> />
</td> </td>
</tr> </tr>
@@ -84,6 +84,7 @@ export default function ProjectSettingsPanel() {
return ( return (
<CustomFieldEntry <CustomFieldEntry
key={key} key={key}
field={key}
colour={colour} colour={colour}
label={label} label={label}
onEdit={handleEditField} onEdit={handleEditField}
@@ -841,35 +841,35 @@ describe('custom fields', () => {
it('creates a field from given parameters', async () => { it('creates a field from given parameters', async () => {
const expected = { const expected = {
lighting: { lighting: {
label: 'lighting', label: 'Lighting',
type: 'string', type: 'string',
colour: 'blue', colour: 'blue',
}, },
}; };
const customField = await createCustomField({ label: 'lighting', type: 'string', colour: 'blue' }); const customField = await createCustomField({ label: 'Lighting', type: 'string', colour: 'blue' });
expect(customField).toStrictEqual(expected); expect(customField).toStrictEqual(expected);
}); });
}); });
describe('editCustomField()', () => { describe('editCustomField()', () => {
it('edits a field with a given label', async () => { it('edits a field with a given label', async () => {
await createCustomField({ label: 'sound', type: 'string', colour: 'blue' }); await createCustomField({ label: 'Sound', type: 'string', colour: 'blue' });
const expected = { const expected = {
lighting: { lighting: {
label: 'lighting', label: 'Lighting',
type: 'string', type: 'string',
colour: 'blue', colour: 'blue',
}, },
sound: { sound: {
label: 'sound', label: 'Sound',
type: 'string', type: 'string',
colour: 'blue', colour: 'green',
}, },
}; };
const customField = await editCustomField('sound', { label: 'sound', type: 'string', colour: 'blue' }); const customField = await editCustomField('sound', { label: 'Sound', type: 'string', colour: 'green' });
expect(customField).toStrictEqual(expected); expect(customField).toStrictEqual(expected);
}); });
@@ -879,7 +879,7 @@ describe('custom fields', () => {
it('deletes a field with a given label', async () => { it('deletes a field with a given label', async () => {
const expected = { const expected = {
lighting: { lighting: {
label: 'lighting', label: 'Lighting',
type: 'string', type: 'string',
colour: 'blue', colour: 'blue',
}, },
@@ -60,6 +60,7 @@ export async function init(initialRundown: Readonly<OntimeRundown>, customFields
persistedCustomFields = structuredClone(customFields); persistedCustomFields = structuredClone(customFields);
generate(); generate();
await DataProvider.setRundown(persistedRundown); await DataProvider.setRundown(persistedRundown);
await DataProvider.setCustomFields(customFields);
} }
/** /**
@@ -428,16 +429,16 @@ function scheduleCustomFieldPersist(persistedCustomFields: CustomFields) {
*/ */
export const createCustomField = async (field: CustomField) => { export const createCustomField = async (field: CustomField) => {
const { label, type, colour } = field; const { label, type, colour } = field;
const key = label.toLowerCase();
// check if label already exists // check if label already exists
const alreadyExists = Object.hasOwn(persistedCustomFields, label); const alreadyExists = Object.hasOwn(persistedCustomFields, key);
if (alreadyExists) { if (alreadyExists) {
throw new Error('Label already exists'); throw new Error('Label already exists');
} }
// update object and persist // update object and persist
persistedCustomFields[label] = { label, type, colour }; persistedCustomFields[key] = { label, type, colour };
scheduleCustomFieldPersist(persistedCustomFields); scheduleCustomFieldPersist(persistedCustomFields);
@@ -446,29 +447,30 @@ export const createCustomField = async (field: CustomField) => {
/** /**
* Edits an existing custom field in the database * Edits an existing custom field in the database
* @param label * @param key
* @param newField * @param newField
* @returns * @returns
*/ */
export const editCustomField = async (label: string, newField: Partial<CustomField>) => { export const editCustomField = async (key: string, newField: Partial<CustomField>) => {
if (!(label in persistedCustomFields)) { if (!(key in persistedCustomFields)) {
throw new Error('Could not find label'); throw new Error('Could not find label');
} }
const existingField = persistedCustomFields[label]; const existingField = persistedCustomFields[key];
if (existingField.type !== newField.type) { if (existingField.type !== newField.type) {
throw new Error('Change of field type is not allowed'); throw new Error('Change of field type is not allowed');
} }
persistedCustomFields[newField.label] = { ...existingField, ...newField }; const newKey = newField.label.toLowerCase();
persistedCustomFields[newKey] = { ...existingField, ...newField };
if (existingField.label !== newField.label) { if (key !== newKey) {
delete persistedCustomFields[existingField.label]; delete persistedCustomFields[key];
customFieldChangelog[label] = newField.label; customFieldChangelog[key] = newKey;
} }
scheduleCustomFieldPersist(persistedCustomFields); scheduleCustomFieldPersist(persistedCustomFields);
invalidateIfUsed(label); invalidateIfUsed(key);
return persistedCustomFields; return persistedCustomFields;
}; };
@@ -796,7 +796,7 @@ describe('parseExcel()', () => {
'Skip', 'Skip',
'Notes', 'Notes',
't0', 't0',
'UpperCaseFromSheet', 'Test1',
'test2', 'test2',
'test3', 'test3',
'test4', 'test4',
@@ -859,8 +859,8 @@ describe('parseExcel()', () => {
const importMap = { const importMap = {
custom: { custom: {
user0: 't0', user0: 't0',
user1: 'UpperCaseFromSheet', User1: 'Test1',
UpperCaseFromOntime: 'test2', user2: 'test2',
user3: 'test3', user3: 'test3',
user4: 'test4', user4: 'test4',
user5: 'test5', user5: 'test5',
@@ -885,7 +885,7 @@ describe('parseExcel()', () => {
custom: { custom: {
user0: { value: 'a0' }, user0: { value: 'a0' },
user1: { value: 'a1' }, user1: { value: 'a1' },
UpperCaseFromOntime: { value: 'a2' }, user2: { value: 'a2' },
user3: { value: 'a3' }, user3: { value: 'a3' },
user4: { value: 'a4' }, user4: { value: 'a4' },
user5: { value: 'a5' }, user5: { value: 'a5' },
@@ -927,12 +927,12 @@ describe('parseExcel()', () => {
user1: { user1: {
type: 'string', type: 'string',
colour: '', colour: '',
label: 'user1', label: 'User1',
}, },
UpperCaseFromOntime: { user2: {
type: 'string', type: 'string',
colour: '', colour: '',
label: 'UpperCaseFromOntime', label: 'user2',
}, },
user3: { user3: {
type: 'string', type: 'string',
+8 -8
View File
@@ -49,15 +49,15 @@ export function getCustomFieldData(importMap: ImportMap): {
} { } {
const customFields = {}; const customFields = {};
const customFieldImportKeys = {}; const customFieldImportKeys = {};
for (const key in importMap.custom) { for (const ontimeLabel in importMap.custom) {
const ontimeName = key; const ontimeKey = ontimeLabel.toLowerCase();
const importName = importMap.custom[key]; const importLabel = importMap.custom[ontimeLabel].toLowerCase();
customFields[ontimeName] = { customFields[ontimeKey] = {
type: 'string', type: 'string',
colour: '', colour: '',
label: ontimeName, label: ontimeLabel,
}; };
customFieldImportKeys[importName] = ontimeName; customFieldImportKeys[importLabel] = ontimeKey;
} }
return { customFields, customFieldImportKeys }; return { customFields, customFieldImportKeys };
} }
@@ -230,8 +230,8 @@ export const parseExcel = (excelData: unknown[][], options?: Partial<ImportMap>)
} }
// check if it is a custom field // check if it is a custom field
if (column in customFieldImportKeys) { if (columnText in customFieldImportKeys) {
handlers.custom(rowIndex, j, column); handlers.custom(rowIndex, j, columnText);
} }
// else. we don't know how to handle this column // else. we don't know how to handle this column