From e7eea142ae26cc02002aa6a1ae66b0611ddcb99e Mon Sep 17 00:00:00 2001 From: Konstantin Markov Date: Fri, 10 Jan 2025 00:03:14 +0200 Subject: [PATCH] Contacts field rework (#2173) --- client/api/contentProfiles.ts | 7 +- client/api/utils/constants.ts | 13 ++ .../Assignments/AssignmentEditor.tsx | 3 +- .../AssignmentPreview.tsx | 5 +- client/components/Contacts/ContactEditor.tsx | 119 +++++++++++++++ .../Contacts/ContactEditor/index.tsx | 127 ---------------- .../Contacts/ContactField.interface.ts | 41 ++++++ client/components/Contacts/ContactField.tsx | 118 +++++---------- .../Contacts/ContactInfoContainer/index.tsx | 54 ------- client/components/Contacts/ContactLabel.tsx | 77 +++++----- .../components/Contacts/ContactMetaData.tsx | 101 +++++++++++++ .../Contacts/ContactMetaData/index.tsx | 139 ------------------ .../Contacts/ContactsPreviewList.tsx | 36 ++--- .../SelectListPopup.tsx | 79 +++++----- .../SelectSearchContactsField/index.tsx | 48 +++--- client/components/Contacts/index.ts | 7 - .../Coverages/CoveragePreview/index.tsx | 4 +- .../ScheduledUpdate/ScheduledUpdateForm.tsx | 2 +- .../Coverages/ScheduledUpdate/index.tsx | 7 +- client/components/Modal/Footer.tsx | 7 +- client/components/UI/CollapseBox.tsx | 2 +- client/components/UI/FileReadOnlyList.tsx | 2 +- client/components/UI/Popup/Menu.tsx | 19 +-- client/components/UI/Popup/Popup.tsx | 71 ++++----- client/components/UI/Preview/Row.tsx | 51 +++---- client/components/UI/SearchField/index.tsx | 49 +++--- client/components/fields/editor/Contacts.tsx | 2 +- .../fields/editor/CoverageContact.tsx | 5 +- .../fields/editor/Location.interface.ts | 6 + client/components/fields/editor/Location.tsx | 8 +- .../editor/ProfileFieldDefaultValue.tsx | 3 - client/components/fields/preview/Contacts.tsx | 5 +- .../field-definitions/category-field.ts | 3 +- .../field-definitions/contacts.ts | 20 +++ .../field-definitions/index.ts | 2 + .../profile-fields.ts | 18 ++- client/extension_bridge.ts | 18 ++- .../authoring-react-fields/contact/editor.tsx | 33 +++++ .../authoring-react-fields/contact/index.tsx | 33 +++++ .../contact/interfaces.ts | 7 + .../contact/preview.tsx | 12 ++ client/planning-extension/src/extension.ts | 2 + .../src/extension_bridge.ts | 10 +- package-lock.json | 20 +-- 44 files changed, 690 insertions(+), 705 deletions(-) create mode 100644 client/api/utils/constants.ts create mode 100644 client/components/Contacts/ContactEditor.tsx delete mode 100644 client/components/Contacts/ContactEditor/index.tsx create mode 100644 client/components/Contacts/ContactField.interface.ts delete mode 100644 client/components/Contacts/ContactInfoContainer/index.tsx create mode 100644 client/components/Contacts/ContactMetaData.tsx delete mode 100644 client/components/Contacts/ContactMetaData/index.tsx delete mode 100644 client/components/Contacts/index.ts create mode 100644 client/components/fields/editor/Location.interface.ts create mode 100644 client/components/planning-editor-standalone/field-definitions/contacts.ts create mode 100644 client/planning-extension/src/authoring-react-fields/contact/editor.tsx create mode 100644 client/planning-extension/src/authoring-react-fields/contact/index.tsx create mode 100644 client/planning-extension/src/authoring-react-fields/contact/interfaces.ts create mode 100644 client/planning-extension/src/authoring-react-fields/contact/preview.tsx diff --git a/client/api/contentProfiles.ts b/client/api/contentProfiles.ts index 6136ce23c..81d2d5a48 100644 --- a/client/api/contentProfiles.ts +++ b/client/api/contentProfiles.ts @@ -18,6 +18,7 @@ import {sortProfileGroups} from '../utils/contentProfiles'; import {showModalConnectedToStore} from '../utils/ui'; import {ContentProfileModal} from '../components/ContentProfiles/ContentProfileModal'; import {getUsersDefaultLanguage} from '../utils/users'; +import {SYSTEM_REQUIRED_FIELDS} from './utils/constants'; const RESOURCE = 'planning_types'; @@ -148,11 +149,7 @@ function showManagePlanningProfileModal(): Promise { mainProfile: { label: gettext('Planning Fields'), profile: getProfile('planning'), - systemRequiredFields: [ - ['planning_date'], - ['slugline', 'headline', 'name'], - ['coverages'], - ], + systemRequiredFields: SYSTEM_REQUIRED_FIELDS, disableMinMaxFields: [ 'language', 'marked_for_not_publication', diff --git a/client/api/utils/constants.ts b/client/api/utils/constants.ts new file mode 100644 index 000000000..6d132f86d --- /dev/null +++ b/client/api/utils/constants.ts @@ -0,0 +1,13 @@ +import {flatMap} from 'lodash'; + +export const SYSTEM_REQUIRED_FIELDS = [ + ['planning_date'], + ['slugline', 'headline', 'name'], + ['coverages'], +]; + +export const isSystemRequiredField = (() => { + const SYSTEM_REQUIRED_FIELDS_SET = new Set(flatMap(SYSTEM_REQUIRED_FIELDS)); + + return (fieldId: string) => SYSTEM_REQUIRED_FIELDS_SET.has(fieldId); +})(); diff --git a/client/components/Assignments/AssignmentEditor.tsx b/client/components/Assignments/AssignmentEditor.tsx index 543f2bbf4..a9102c8e4 100644 --- a/client/components/Assignments/AssignmentEditor.tsx +++ b/client/components/Assignments/AssignmentEditor.tsx @@ -15,7 +15,8 @@ import { SelectInput, ColouredValueInput, } from '../UI/Form'; -import {ContactsPreviewList, SelectSearchContactsField} from '../Contacts'; +import {ContactsPreviewList} from '../Contacts/ContactsPreviewList'; +import {SelectSearchContactsField} from '../Contacts/SelectSearchContactsField'; import {superdeskApi} from '../../superdeskApi'; export class AssignmentEditorComponent extends React.Component { diff --git a/client/components/Assignments/AssignmentPreviewContainer/AssignmentPreview.tsx b/client/components/Assignments/AssignmentPreviewContainer/AssignmentPreview.tsx index 9ce0d4ef2..cfcd5fdf7 100644 --- a/client/components/Assignments/AssignmentPreviewContainer/AssignmentPreview.tsx +++ b/client/components/Assignments/AssignmentPreviewContainer/AssignmentPreview.tsx @@ -14,7 +14,7 @@ import { import {assignmentUtils, planningUtils} from '../../../utils'; -import {ContactsPreviewList} from '../../Contacts'; +import {ContactsPreviewList} from '../../Contacts/ContactsPreviewList'; import {Row} from '../../UI/Preview'; import {FileReadOnlyList} from '../../UI'; import {previewGroupToProfile, renderFieldsForPanel} from '../../fields'; @@ -52,9 +52,6 @@ export class AssignmentPreview extends React.PureComponent { )} diff --git a/client/components/Contacts/ContactEditor.tsx b/client/components/Contacts/ContactEditor.tsx new file mode 100644 index 000000000..5659e8a73 --- /dev/null +++ b/client/components/Contacts/ContactEditor.tsx @@ -0,0 +1,119 @@ +import React from 'react'; +import {gettext} from '../../utils'; + +import * as ContactFormComponents from 'superdesk-core/scripts/apps/contacts/components/Form'; +import ng from 'superdesk-core/scripts/core/services/ng'; +import {Button, Modal, Spacer} from 'superdesk-ui-framework/react'; +import {IContact} from 'superdesk-api'; + +interface IProps { + currentContact: IContact; + onSave: (contact: IContact) => void; + closeModal: () => void; +} + +interface IState { + dirty: boolean; + valid: boolean; +} + +export class ContactEditor extends React.Component { + contactForm: React.RefObject; + + constructor(props) { + super(props); + this.state = { + dirty: false, + valid: false, + }; + this.contactForm = React.createRef(); + + this.onDirty = this.onDirty.bind(this); + this.onValidation = this.onValidation.bind(this); + this.triggerSave = this.triggerSave.bind(this); + this.onSave = this.onSave.bind(this); + this.exitEditor = this.exitEditor.bind(this); + } + + onDirty() { + this.setState({ + dirty: true, + }); + } + + onValidation(validity) { + this.setState({ + valid: validity, + }); + } + + triggerSave() { + if (this.contactForm.current != null) { + this.contactForm.current.save(); + } + } + + exitEditor(result) { + // wait before exiting contact editor, allowing save changes to be completed on contact form. + setTimeout(() => this.props.onSave(result), 800); + } + + onSave(result) { + this.setState({ + dirty: false, + }, () => { + this.exitEditor(result); + this.props.closeModal(); + }); + } + + render() { + const {ContactFormContainer} = ContactFormComponents; + const {currentContact} = this.props; + + // Provides required services for Contact components + const services = { + contacts: ng.get('contacts'), + gettext: ng.get('gettext'), + notify: ng.get('notify'), + privileges: ng.get('privileges'), + metadata: ng.get('metadata'), + }; + + return ( + +