Skip to content

Commit

Permalink
Contacts field rework (#2173)
Browse files Browse the repository at this point in the history
  • Loading branch information
thecalcc authored Jan 9, 2025
1 parent a5dd21d commit e7eea14
Show file tree
Hide file tree
Showing 44 changed files with 690 additions and 705 deletions.
7 changes: 2 additions & 5 deletions client/api/contentProfiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -148,11 +149,7 @@ function showManagePlanningProfileModal(): Promise<void> {
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',
Expand Down
13 changes: 13 additions & 0 deletions client/api/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -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);
})();
3 changes: 2 additions & 1 deletion client/components/Assignments/AssignmentEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -52,9 +52,6 @@ export class AssignmentPreview extends React.PureComponent<IProps> {
<Row label={assignmentUtils.getContactLabel(assignment)}>
<ContactsPreviewList
contactIds={[contactId]}
scrollInView={true}
scrollIntoViewOptions={{block: 'center'}}
tabEnabled={true}
/>
</Row>
)}
Expand Down
119 changes: 119 additions & 0 deletions client/components/Contacts/ContactEditor.tsx
Original file line number Diff line number Diff line change
@@ -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<IProps, IState> {
contactForm: React.RefObject<ContactFormComponents.ContactFormContainer>;

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 (
<Modal
closeOnEscape
size="medium"
visible
headerTemplate={gettext('Add Contact')}
footerTemplate={(
<Spacer gap="4" alignItems="end" justifyContent="end" h noGrow>
<Button
onClick={this.props.closeModal}
text={gettext('Cancel')}
/>
<Button
style="filled"
type="primary"
onClick={this.triggerSave}
text={gettext('Save')}
disabled={!this.state.valid || !this.state.dirty}
/>
</Spacer>
)}
>
<ContactFormContainer
ref={this.contactForm}
contact={currentContact}
svc={services}
onCancel={this.props.closeModal}
onDirty={this.onDirty}
onValidation={this.onValidation}
triggerSave={false}
onSave={this.onSave}
hideActionBar={true}
/>
</Modal>
);
}
}
127 changes: 0 additions & 127 deletions client/components/Contacts/ContactEditor/index.tsx

This file was deleted.

41 changes: 41 additions & 0 deletions client/components/Contacts/ContactField.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import {IPrivileges} from '../../interfaces';
import {IContact} from 'superdesk-api';

interface IBaseProps extends IContactReduxStateProps, IContactReduxDispatchProps {
field: string;
label: string;
querySearch?: boolean;
readOnly?: boolean;
paddingTop?: boolean;
testId?: string;
onFocus?(): void;
refNode?(node: HTMLElement): void;
onPopupOpen?(): void;
onPopupClose?(): void;
}

interface ISingleContactProps extends IBaseProps {
singleValue: true;
value: IContact['_id'] | null;
onChange(field: string, value: IContact['_id'] | null): void;
}

interface IMultiContactProps extends IBaseProps {
singleValue: false;
value: Array<IContact['_id']> | null;
onChange(field: string, value: Array<IContact['_id']>): void;
}

export type IContactFieldProps = ISingleContactProps | IMultiContactProps;

export interface IContactReduxDispatchProps {
addContact(newContact: Partial<IContact>): void;
}

export interface IContactReduxStateProps {
contacts: Array<IContact>;
privileges: IPrivileges;
}

export type IContactPropsNoRedux =
Omit<IContactFieldProps, keyof IContactReduxStateProps | keyof IContactReduxDispatchProps>;
Loading

0 comments on commit e7eea14

Please sign in to comment.