import {computed, Computed, dom, DomElementArg, IDisposableOwner, Observable, styled} from "grainjs"; import {cssAnimatedModal, cssModalBody, cssModalButtons, cssModalTitle, IModalControl, modal} from 'app/client/ui2018/modals'; import {bigBasicButton, bigPrimaryButton} from 'app/client/ui2018/buttons'; import {mediaXSmall, testId, theme, vars} from 'app/client/ui2018/cssVars'; import {IOrgMemberSelectOption, UserManagerModel} from 'app/client/models/UserManagerModel'; import {icon} from 'app/client/ui2018/icons'; import {textarea} from "app/client/ui/inputs"; import {BasicRole, isBasicRole, NonGuestRole, VIEWER} from "app/common/roles"; import {menu, menuItem} from 'app/client/ui2018/menus'; function parseEmailList(emailListRaw: string): Array { return emailListRaw .split('\n') .map(email => email.trim().toLowerCase()) .filter(email => email !== ""); } function validateEmail(email: string): boolean { const mailformat = /\S+@\S+\.\S+/; return mailformat.test(email); } export function buildMultiUserManagerModal( owner: IDisposableOwner, model: UserManagerModel, onAdd: (email: string, role: NonGuestRole) => void, ) { const emailListObs = Observable.create(owner, ""); const rolesObs = Observable.create(owner, VIEWER); const isValidObs = Observable.create(owner, true); const enableAdd: Computed = computed( (use) => Boolean(use(emailListObs) && use(rolesObs) && use(isValidObs)) ); const save = (ctl: IModalControl) => { const emailList = parseEmailList(emailListObs.get()); const role = rolesObs.get(); if (emailList.some(email => !validateEmail(email))) { isValidObs.set(false); } else { emailList.forEach(email => onAdd(email, role)); ctl.close(); } }; return modal(ctl => [ { style: 'padding: 0;' }, dom.cls(cssAnimatedModal.className), cssTitle( 'Invite Users', testId('um-header'), ), cssModalBody( cssUserManagerBody( buildEmailsTextarea(emailListObs, isValidObs), dom.maybe(use => !use(isValidObs), () => cssErrorMessage('At least one email is invalid')), cssInheritRoles( dom('span', 'Access: '), buildRolesSelect(rolesObs, model) ) ), ), cssModalButtons( { style: 'margin: 32px 64px; display: flex;' }, bigPrimaryButton('Confirm', dom.boolAttr('disabled', (use) => !use(enableAdd)), dom.on('click', () => { save(ctl); }), testId('um-confirm') ), bigBasicButton( 'Cancel', dom.on('click', () => ctl.close()), testId('um-cancel') ), ) ]); } function buildRolesSelect( roleSelectedObs: Observable, model: UserManagerModel, ) { const allRoles = (model.isOrg ? model.orgUserSelectOptions : model.userSelectOptions) .filter((x): x is {value: BasicRole, label: string} => isBasicRole(x.value)); return cssOptionBtn( menu(() => [ dom.forEach(allRoles, (_role) => menuItem(() => roleSelectedObs.set(_role.value), _role.label, testId(`um-role-option`) ) ) ]), dom.text((use) => { // Get the label of the active role. const activeRole = allRoles.find((_role: IOrgMemberSelectOption) => use(roleSelectedObs) === _role.value); return activeRole ? activeRole.label : ""; }), cssCollapseIcon('Collapse'), testId('um-role-select') ); } function buildEmailsTextarea( emailListObs: Observable, isValidObs: Observable, ...args: DomElementArg[] ) { return cssTextarea(emailListObs, {onInput: true, isValid: isValidObs}, {placeholder: "Enter one email address per line"}, dom.on('change', (_ev) => isValidObs.set(true)), ...args, ); } const cssTitle = styled(cssModalTitle, ` margin: 40px 64px 0 64px; @media ${mediaXSmall} { & { margin: 16px; } } `); const cssInheritRoles = styled('span', ` margin: 13px 63px 42px; `); const cssErrorMessage = styled('span', ` margin: 0 63px; color: ${theme.errorText}; `); const cssOptionBtn = styled('span', ` display: inline-flex; font-size: ${vars.mediumFontSize}; color: ${theme.controlFg}; cursor: pointer; `); const cssCollapseIcon = styled(icon, ` margin-top: 1px; background-color: ${theme.controlFg}; `); const cssAccessDetailsBody = styled('div', ` display: flex; flex-direction: column; width: 600px; font-size: ${vars.mediumFontSize}; `); const cssUserManagerBody = styled(cssAccessDetailsBody, ` height: 374px; border-bottom: 1px solid ${theme.modalBorderDark}; `); const cssTextarea = styled(textarea, ` margin: 16px 63px; padding: 12px 10px; border-radius: 3px; resize: none; border: 1px solid ${theme.inputBorder}; color: ${theme.inputFg}; background-color: ${theme.inputBg}; flex: 1 1 0; font-size: ${vars.mediumFontSize}; font-family: ${vars.fontFamily}; outline: none; &::placeholder { color: ${theme.inputPlaceholderFg}; } `);