From ff046be238f7a50c0b3586b54e50117e5016098e Mon Sep 17 00:00:00 2001 From: Pavel <52532264+nomerdvadcatpyat@users.noreply.github.com> Date: Fri, 5 Apr 2024 18:55:49 +0500 Subject: [PATCH] feat(condo): DOMA-8429 added user import help request (#4513) * feat(condo): DOMA-8429 added help modal * feat(condo): DOMA-8429 added UserHelpRequest schema * feat(condo): DOMA-8429 added UserHelpRequestFile schema * feat(condo): DOMA-8429 update help modal * feat(condo): DOMA-8429 added translations * fix(condo): DOMA-8429 rebase, fixes after review * feat(condo): DOMA-8429 add links to instructions * fix(condo): DOMA-8429 disable submit button when no files * fix(condo): DOMA-8429 add timeout to migration * chore(condo): DOMA-8429 recreate migrations --- .helm | 2 +- .../common/components/Import/Index.tsx | 58 +- .../common/components/MultipleFileUpload.tsx | 155 ++- .../common/components/containers/FormList.tsx | 3 +- .../components/containers/GlobalStyle.tsx | 47 - .../domains/common/constants/featureflags.js | 3 + .../common/hooks/useImportHelpModal.tsx | 384 ++++++ apps/condo/domains/common/schema/fields.js | 2 +- .../onboarding/access/UserHelpRequest.js | 62 + .../onboarding/access/UserHelpRequestFile.js | 69 ++ .../onboarding/constants/userHelpRequest.js | 16 + apps/condo/domains/onboarding/gql.js | 8 + .../onboarding/schema/UserHelpRequest.js | 91 ++ .../onboarding/schema/UserHelpRequest.test.js | 310 +++++ .../onboarding/schema/UserHelpRequestFile.js | 44 + .../schema/UserHelpRequestFile.test.js | 258 ++++ apps/condo/domains/onboarding/schema/index.js | 4 + .../utils/clientSchema/UserHelpRequest.ts | 31 + .../utils/clientSchema/UserHelpRequestFile.ts | 31 + .../onboarding/utils/clientSchema/index.ts | 2 + .../onboarding/utils/serverSchema/index.js | 6 + .../onboarding/utils/testSchema/index.js | 67 + .../components/TicketId/TicketFileList.tsx | 4 + apps/condo/lang/en/en.json | 19 + apps/condo/lang/ru/ru.json | 19 + ...toryrecord_userhelprequesthistoryrecord.js | 81 ++ apps/condo/next.config.js | 2 + apps/condo/schema.graphql | 947 ++++++++++++++ apps/condo/schema.ts | 1102 +++++++++++++++++ 29 files changed, 3720 insertions(+), 107 deletions(-) create mode 100644 apps/condo/domains/common/hooks/useImportHelpModal.tsx create mode 100644 apps/condo/domains/onboarding/access/UserHelpRequest.js create mode 100644 apps/condo/domains/onboarding/access/UserHelpRequestFile.js create mode 100644 apps/condo/domains/onboarding/constants/userHelpRequest.js create mode 100644 apps/condo/domains/onboarding/schema/UserHelpRequest.js create mode 100644 apps/condo/domains/onboarding/schema/UserHelpRequest.test.js create mode 100644 apps/condo/domains/onboarding/schema/UserHelpRequestFile.js create mode 100644 apps/condo/domains/onboarding/schema/UserHelpRequestFile.test.js create mode 100644 apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequest.ts create mode 100644 apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequestFile.ts create mode 100644 apps/condo/migrations/20240405181752-0379_userhelprequest_userhelprequestfile_userhelprequestfilehistoryrecord_userhelprequesthistoryrecord.js diff --git a/.helm b/.helm index 97f8ed0e478..a032495dfbb 160000 --- a/.helm +++ b/.helm @@ -1 +1 @@ -Subproject commit 97f8ed0e478d607e0de16412b75caadc52bbbbb8 +Subproject commit a032495dfbbdd150f5fea45a158828d22451a800 diff --git a/apps/condo/domains/common/components/Import/Index.tsx b/apps/condo/domains/common/components/Import/Index.tsx index acf67dc8ae7..e3975ea71e3 100644 --- a/apps/condo/domains/common/components/Import/Index.tsx +++ b/apps/condo/domains/common/components/Import/Index.tsx @@ -8,13 +8,19 @@ import isFunction from 'lodash/isFunction' import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import XLSX from 'xlsx' -import { Download, FileDown } from '@open-condo/icons' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' +import { Download, FileDown, QuestionCircle } from '@open-condo/icons' import { useIntl } from '@open-condo/next/intl' import { Alert, Button, Card, CardBodyProps, CardHeaderProps, Modal, Typography } from '@open-condo/ui' import { colors } from '@open-condo/ui/dist/colors' +import { DataImporter } from '@condo/domains/common/components/DataImporter' +import { FocusContainer } from '@condo/domains/common/components/FocusContainer' +import { LinkWithIcon } from '@condo/domains/common/components/LinkWithIcon' import { useTracking, TrackingEventType } from '@condo/domains/common/components/TrackingContext' +import { IMPORT_HELP_MODAL } from '@condo/domains/common/constants/featureflags' import { useImporter } from '@condo/domains/common/hooks/useImporter' +import { useImportHelpModal } from '@condo/domains/common/hooks/useImportHelpModal' import { Columns, RowNormalizer, @@ -24,9 +30,6 @@ import { MutationErrorsToMessagesType, } from '@condo/domains/common/utils/importer' -import { DataImporter } from '../DataImporter' -import { FocusContainer } from '../FocusContainer' - export interface IImportWrapperProps { accessCheck: boolean @@ -139,6 +142,7 @@ const ImportWrapper: React.FC = (props) => { const ErrorModalTitle = intl.formatMessage({ id: 'import.errorModal.title' }, { plural: ImportPluralMessage }) const SuccessModalButtonLabel = intl.formatMessage({ id: 'import.successModal.buttonLabel' }) const ErrorsMessage = intl.formatMessage({ id: 'import.Errors' }) + const NeedHelpMessage = intl.formatMessage({ id: 'import.uploadModal.needHelp' }) const exampleTemplateLink = useMemo(() => `/import/${domainName}/${intl.locale}/${domainName}-import-example.xlsx`, [domainName, intl.locale]) const exampleImageSrc = useMemo(() => `/import/${domainName}/${intl.locale}/${domainName}-import-example.webp`, [domainName, intl.locale]) @@ -153,6 +157,10 @@ const ImportWrapper: React.FC = (props) => { } }, [activeModal]) + const { Modal: ImportHelpModal, openImportHelpModal } = useImportHelpModal({ domainName }) + const { useFlag } = useFeatureFlags() + const isImportHelpModalEnabled = useFlag(IMPORT_HELP_MODAL) + const totalRowsRef = useRef(0) const setTotalRowsRef = (value: number) => { totalRowsRef.current = value @@ -268,11 +276,26 @@ const ImportWrapper: React.FC = (props) => { onCancel={closeModal} open={activeModal === 'example'} footer={ - - - + + { + isImportHelpModalEnabled && ( + { + setActiveModal(null) + openImportHelpModal() + }} + /> + ) + } + + + + } > @@ -288,15 +311,13 @@ const ImportWrapper: React.FC = (props) => { message={RequiredFieldsTitle} description={ImportRequiredFieldsMessage} /> - - - - {ExampleLinkMessage} - - + = (props) => { success-image + ) ) diff --git a/apps/condo/domains/common/components/MultipleFileUpload.tsx b/apps/condo/domains/common/components/MultipleFileUpload.tsx index 44ca375e202..dfa99201219 100644 --- a/apps/condo/domains/common/components/MultipleFileUpload.tsx +++ b/apps/condo/domains/common/components/MultipleFileUpload.tsx @@ -1,15 +1,18 @@ -import { DeleteFilled, EditFilled } from '@ant-design/icons' import { File } from '@app/condo/schema' +import styled from '@emotion/styled' import { Upload, UploadProps } from 'antd' import { UploadFile, UploadFileStatus } from 'antd/lib/upload/interface' import get from 'lodash/get' import isEmpty from 'lodash/isEmpty' +import isFunction from 'lodash/isFunction' import { UploadRequestOption } from 'rc-upload/lib/interface' import React, { useCallback, useEffect, useMemo, useReducer, useRef, useState } from 'react' +import { Paperclip, Trash } from '@open-condo/icons' import { useIntl } from '@open-condo/next/intl' +import { Button } from '@open-condo/ui' +import { colors } from '@open-condo/ui/dist/colors' -import { Button } from '@condo/domains/common/components/Button' import { MAX_UPLOAD_FILE_SIZE } from '@condo/domains/common/constants/uploads' import { useTracking, TrackingEventType } from './TrackingContext' @@ -87,7 +90,9 @@ interface IUploadComponentProps { initialFileList: DBFile[] UploadButton?: React.ReactElement uploadProps?: UploadProps + onFileListChange?: (fileList) => void } + interface IMultipleFileUploadHookArgs { Model: Module relationField: string @@ -95,6 +100,7 @@ interface IMultipleFileUploadHookArgs { initialCreateValues?: Record, dependenciesForRerenderUploadComponent?: Array } + interface IMultipleFileUploadHookResult { UploadComponent: React.FC, syncModifiedFiles: (id: string) => Promise @@ -139,7 +145,10 @@ export const useMultipleFileUploadHook = ({ dispatch({ type: 'reset' }) }, []) - const initialValues = useMemo(() => ({ ...initialCreateValues, [relationField]: null }), [initialCreateValues, relationField]) + const initialValues = useMemo(() => ({ + ...initialCreateValues, + [relationField]: null, + }), [initialCreateValues, relationField]) const UploadComponent: React.FC = useMemo(() => { const UploadWrapper = (props) => ( @@ -148,7 +157,7 @@ export const useMultipleFileUploadHook = ({ fileList={initialFileList} initialCreateValues={initialValues} Model={Model} - onFilesChange={dispatch} + updateFileList={dispatch} {...props} /> ) @@ -163,14 +172,76 @@ export const useMultipleFileUploadHook = ({ } } +const StyledUpload = styled(Upload)` + display: flex; + flex-flow: column-reverse; + + .ant-upload-list-item:hover .ant-upload-list-item-info { + background-color: inherit; + } + + .ant-upload-list-item-info { + & .ant-upload-text-icon { + transform: rotate(180deg); + + span { + color: black; + font-size: 16px; + } + } + } + + .ant-upload-list-text-container { + & .ant-upload-list-item-name { + font-size: 16px; + width: auto; + flex-grow: 0; + } + + &:last-child { + margin-bottom: 24px; + width: auto; + } + } + + .ant-upload-list-item-card-actions { + display: flex; + align-items: center; + } + + .ant-upload-list-item:not(.ant-upload-list-item-error) { + & .ant-upload-list-item-name { + text-decoration: underline; + color: ${colors.black}; + + &:hover { + color: ${colors.green[5]}; + text-decoration-color: ${colors.green[5]}; + } + } + } + + .ant-upload-list-item-error { + & .ant-upload-list-item-name { + text-decoration: none; + color: ${colors.red[5]}; + } + + & .ant-upload-text-icon span { + color: ${colors.red[5]}; + } + } +` + interface IMultipleFileUploadProps { setFilesCount: React.Dispatch> fileList: DBFile[] initialCreateValues: Record Model: Module - onFilesChange: React.Dispatch<{ type: string, payload: DBFile }> + updateFileList: React.Dispatch<{ type: string, payload: DBFile }> UploadButton?: React.FC uploadProps?: UploadProps + onFileListChange?: (fileList) => void } const MultipleFileUpload: React.FC = (props) => { @@ -184,9 +255,10 @@ const MultipleFileUpload: React.FC = (props) => { fileList, initialCreateValues, Model, - onFilesChange, + updateFileList, UploadButton, uploadProps = {}, + onFileListChange, } = props const [listFiles, setListFiles] = useState([]) @@ -217,27 +289,35 @@ const MultipleFileUpload: React.FC = (props) => { return file }) setListFiles(fileList) + + if (isFunction(onFileListChange)) { + onFileListChange(fileList) + } }, showUploadList: { showRemoveIcon: true, removeIcon: (file) => { const removeIcon = ( - { - const { id, uid } = file - const fileError = get(file, 'error') - if (!fileError) { - setFilesCount(filesCount => filesCount - 1) - } - - if (!id) { - // remove file that failed to upload from list - setListFiles([...listFiles].filter(file => file.uid !== uid)) - onFilesChange({ type: 'delete', payload: file }) - return - } - setListFiles([...listFiles].filter(file => file.id !== id)) - onFilesChange({ type: 'delete', payload: file }) - }} /> + { + const { id, uid } = file + const fileError = get(file, 'error') + if (!fileError) { + setFilesCount(filesCount => filesCount - 1) + } + + if (!id) { + // remove file that failed to upload from list + setListFiles([...listFiles].filter(file => file.uid !== uid)) + updateFileList({ type: 'delete', payload: file }) + return + } + setListFiles([...listFiles].filter(file => file.id !== id)) + updateFileList({ type: 'delete', payload: file }) + }} + /> ) return removeIcon }, @@ -250,10 +330,10 @@ const MultipleFileUpload: React.FC = (props) => { onError(error) return } - return createAction({ ...initialCreateValues, file }).then( dbFile => { + return createAction({ ...initialCreateValues, file }).then(dbFile => { const [uploadFile] = convertFilesToUploadFormat([dbFile]) onSuccess(uploadFile, null) - onFilesChange({ type: 'add', payload: dbFile }) + updateFileList({ type: 'add', payload: dbFile }) setFilesCount(filesCount => filesCount + 1) logEvent({ @@ -270,20 +350,17 @@ const MultipleFileUpload: React.FC = (props) => { } return ( -
- - { - UploadButton || ( - - ) - } - -
+ + { + UploadButton || ( + + ) + } + ) } diff --git a/apps/condo/domains/common/components/containers/FormList.tsx b/apps/condo/domains/common/components/containers/FormList.tsx index 253fb103fde..64fbbde1368 100644 --- a/apps/condo/domains/common/components/containers/FormList.tsx +++ b/apps/condo/domains/common/components/containers/FormList.tsx @@ -17,6 +17,7 @@ import { import { FormProps } from 'antd/lib/form/Form' import { ArgsProps } from 'antd/lib/notification' import { isUndefined, throttle } from 'lodash' +import isFunction from 'lodash/isFunction' import omitBy from 'lodash/omitBy' import React, { useCallback, useState, useRef, CSSProperties, ComponentProps } from 'react' import { Options } from 'scroll-into-view-if-needed' @@ -408,7 +409,7 @@ const FormWithAction: React.FC = (props) => { {...formProps} > - {children({ handleSave, isLoading, handleSubmit: _handleSubmit, form })} + {isFunction(children) ? children({ handleSave, isLoading, handleSubmit: _handleSubmit, form }) : children} ) } diff --git a/apps/condo/domains/common/components/containers/GlobalStyle.tsx b/apps/condo/domains/common/components/containers/GlobalStyle.tsx index 7ea4097c91e..157567e5f6b 100644 --- a/apps/condo/domains/common/components/containers/GlobalStyle.tsx +++ b/apps/condo/domains/common/components/containers/GlobalStyle.tsx @@ -457,7 +457,6 @@ export default function GlobalStyle () { margin-left: 8px; } - ${uploadControlCss} ${radioGroupCss} ${inputControlCss} ${page} @@ -551,52 +550,6 @@ const radioGroupCss = css` } ` - -const uploadControlCss = css` - .upload-control-wrapper .ant-upload-list-text-container { - transition: none; - } - .upload-control-wrapper .ant-upload-list-item-info .ant-upload-text-icon .anticon, - .upload-control-wrapper a.ant-upload-list-item-name, - .upload-control-wrapper a.ant-upload-list-item-name:active, - .upload-control-wrapper a.ant-upload-list-item-name:focus { - color: ${colors.green[7]}; - } - .upload-control-wrapper .ant-upload-list-item:hover a, - .upload-control-wrapper .ant-upload-list-item:hover .ant-upload-text-icon > .anticon.anticon-paper-clip, - .upload-control-wrapper .ant-upload-list-item:hover .ant-upload-list-item-info.ant-upload-text-icon.anticon { - color: ${colors.green[5]}; - } - .upload-control-wrapper .ant-upload-list-item.ant-upload-list-item-error .ant-upload-list-item-info .ant-upload-text-icon .anticon, - .upload-control-wrapper .ant-upload-list-item.ant-upload-list-item-error:hover .ant-upload-list-item-info .ant-upload-text-icon .anticon { - color: ${colors.sberDanger[5]}; - } - .upload-control-wrapper .ant-upload-list-item:hover .ant-upload-list-item-info { - background-color: transparent; - } - .upload-control-wrapper > span { - display: flex; - flex-direction: column; - } - .upload-control-wrapper .ant-upload.ant-upload-select { - order:1; - margin-top:5px; - } - .ant-upload-select-block { - width: 100%; - - .ant-upload { - width: 100%; - } - } - .upload-control-wrapper .ant-upload-list-item-card-actions .anticon.anticon-delete { - font-size:18px; - } - .upload-control-wrapper .ant-upload-list-item-card-actions { - margin-left: 50px; - } -` - const inputControlCss = css` .ant-input.white { background: ${colors.white}; diff --git a/apps/condo/domains/common/constants/featureflags.js b/apps/condo/domains/common/constants/featureflags.js index 2e36a5139a8..3d15eb72422 100644 --- a/apps/condo/domains/common/constants/featureflags.js +++ b/apps/condo/domains/common/constants/featureflags.js @@ -23,6 +23,8 @@ const MAX_EMPLOYEE_SIZE_IN_ORGANIZATION_TO_TELEGRAM_NOTIFICATIONS = 'max-employe const SEND_TELEGRAM_NOTIFICATIONS = 'send-telegram-notifications' const ORGANIZATION_TOUR = 'organization-tour' const CAPTCHA_CHECK_ENABLED = 'captcha-check-enabled' +// TODO(DOMA-8667): Remove flag after links to instructions will be ready +const IMPORT_HELP_MODAL = 'import-help-modal' module.exports = { SMS_AFTER_TICKET_CREATION, @@ -50,4 +52,5 @@ module.exports = { SEND_TELEGRAM_NOTIFICATIONS, ORGANIZATION_TOUR, CAPTCHA_CHECK_ENABLED, + IMPORT_HELP_MODAL, } diff --git a/apps/condo/domains/common/hooks/useImportHelpModal.tsx b/apps/condo/domains/common/hooks/useImportHelpModal.tsx new file mode 100644 index 00000000000..9453cc05a46 --- /dev/null +++ b/apps/condo/domains/common/hooks/useImportHelpModal.tsx @@ -0,0 +1,384 @@ +import styled from '@emotion/styled' +import { Col, Form, Row, Space } from 'antd' +import get from 'lodash/get' +import isEmpty from 'lodash/isEmpty' +import getConfig from 'next/config' +import React, { Dispatch, SetStateAction, useCallback, useMemo, useState } from 'react' + +import { ArrowLeft, Phone } from '@open-condo/icons' +import { useAuth } from '@open-condo/next/auth' +import { useIntl } from '@open-condo/next/intl' +import { useOrganization } from '@open-condo/next/organization' +import { Alert, Button, Card, Modal, Typography } from '@open-condo/ui' + +import { FormWithAction } from '@condo/domains/common/components/containers/FormList' +import { LinkWithIcon } from '@condo/domains/common/components/LinkWithIcon' +import { useMultipleFileUploadHook } from '@condo/domains/common/components/MultipleFileUpload' +import { PhoneInput } from '@condo/domains/common/components/PhoneInput' +import { UserHelpRequest, UserHelpRequestFile } from '@condo/domains/onboarding/utils/clientSchema' + +import { useValidations } from './useValidations' + + +const { publicRuntimeConfig: { importInstructionUrl } } = getConfig() + +const CardsWrapper = styled.div<{ hasInstructionCard: boolean }>` + display: flex; + flex-direction: row; + gap: 8px; + + & .condo-card { + width: ${props => props.hasInstructionCard ? '240px' : 'auto'}; + display: flex; + flex-direction: column; + + .condo-card-body { + flex-grow: 1; + } + } +` +const UserHelpModal = styled(Modal)` + &.condo-modal .condo-modal-header { + padding: 40px 40px 8px 40px; + } + + &.condo-modal .condo-modal-body { + padding-top: 0; + } +` + +const CallBackModal = ({ domainName, activeModal, setActiveModal }) => { + const intl = useIntl() + const ModalTitle = intl.formatMessage({ id: 'import.callbackModal.title' }) + const BackMessage = intl.formatMessage({ id: 'Back' }) + const SubmitButtonLabel = intl.formatMessage({ id: 'import.callbackModal.submitButtonLabel' }) + const ModalBodyText = intl.formatMessage({ id: 'import.callbackModal.body' }) + const PhoneLabel = intl.formatMessage({ id: 'import.callbackModal.phoneLabel' }) + + const { user } = useAuth() + const { organization } = useOrganization() + const phone = useMemo(() => get(user, 'phone'), [user]) + const initialValues = useMemo(() => ({ phone }), [phone]) + const createHelpRequestAction = UserHelpRequest.useCreate({ + organization: { connect: { id: get(organization, 'id', null) } }, + meta: { importType: domainName }, + }) + + const [callForm] = Form.useForm() + const [loading, setLoading] = useState(false) + + const callFormAction = useCallback(async values => { + setLoading(true) + await createHelpRequestAction({ + type: 'callback', + phone: get(values, 'phone'), + }) + + setActiveModal(null) + setLoading(false) + }, [createHelpRequestAction, setActiveModal]) + + const { requiredValidator, phoneValidator } = useValidations() + + return ( + + setActiveModal(null)} + title={ModalTitle} + footer={( + + setActiveModal('choose')} + PrefixIcon={ArrowLeft} + /> + + { + ({ getFieldError }) => { + const errors = getFieldError('phone') + + return ( + + ) + } + } + + + )} + > + + + + {ModalBodyText} + + + + + + + + + + + ) +} + +const FileImportModal = ({ domainName, activeModal, setActiveModal }) => { + const intl = useIntl() + const ModalTitle = intl.formatMessage({ id: 'import.fileImportHelpModal.title' }) + const BackMessage = intl.formatMessage({ id: 'Back' }) + const SubmitButtonLabel = intl.formatMessage({ id: 'import.fileImportHelpModal.submitButtonLabel' }) + const ModalBodyText = intl.formatMessage({ id: 'import.fileImportHelpModal.body' }) + const AlertMessage = intl.formatMessage({ id: 'import.fileImportHelpModal.alert.message' }) + const AlertDescription = intl.formatMessage({ id: 'import.fileImportHelpModal.alert.description' }) + const PhoneCheckMessage = intl.formatMessage({ id: 'import.fileImportHelpModal.phoneCheck' }) + const PhoneLabel = intl.formatMessage({ id: 'import.fileImportHelpModal.phoneLabel' }) + + const { user } = useAuth() + const { organization } = useOrganization() + const phone = useMemo(() => get(user, 'phone'), [user]) + const initialValues = useMemo(() => ({ phone }), [phone]) + const createHelpRequestAction = UserHelpRequest.useCreate({ + organization: { connect: { id: get(organization, 'id', null) } }, + meta: { importType: domainName }, + }) + const updateHelpRequestAction = UserHelpRequest.useUpdate({}) + const { requiredValidator, phoneValidator } = useValidations() + + const [loading, setLoading] = useState(false) + const [filesUploading, setFilesUploading] = useState(false) + const [files, setFiles] = useState([]) + const [uploadForm] = Form.useForm() + + const { UploadComponent, syncModifiedFiles } = useMultipleFileUploadHook({ + Model: UserHelpRequestFile, + relationField: 'userHelpRequest', + initialFileList: [], + }) + + const uploadFormAction = useCallback(async values => { + setLoading(true) + const helpRequest = await createHelpRequestAction({ + type: 'importFile', + phone: get(values, 'phone'), + isReadyToSend: false, + }) + + await syncModifiedFiles(helpRequest.id) + + await updateHelpRequestAction({ + isReadyToSend: true, + }, helpRequest) + + setLoading(false) + setActiveModal(null) + }, [createHelpRequestAction, setActiveModal, syncModifiedFiles, updateHelpRequestAction]) + + return ( + + setActiveModal(null)} + title={ModalTitle} + footer={( + + setActiveModal('choose')} + PrefixIcon={ArrowLeft} + /> + + { + ({ getFieldsError }) => { + const errors = getFieldsError(['phone', 'file']) + .flatMap(obj => obj.errors) + + return ( + + ) + } + } + + + )} + > + + + {ModalBodyText} + + + + { + setFiles(files) + const isFilesUploading = files.some(file => get(file, 'status') === 'uploading') + setFilesUploading(isFilesUploading) + }} + /> + + + + + {PhoneCheckMessage} + + + + + + + + + + + + ) +} + +type ActiveModalType = 'choose' | 'call' | 'upload' +type ImportHelpModalProps = { + domainName: string + activeModal: ActiveModalType + setActiveModal: Dispatch> +} + +export const ImportHelpModal: React.FC = ({ domainName, activeModal, setActiveModal }) => { + const intl = useIntl() + const ModalTitle = intl.formatMessage({ id: 'import.helpModal.title' }) + const ChooseVariantMessage = intl.formatMessage({ id: 'import.helpModal.chooseVariant' }) + const ReadInstructionsCardTitle = intl.formatMessage({ id: 'import.helpModal.readInstruction.title' }) + const ReadInstructionsCardBody = intl.formatMessage({ id: 'import.helpModal.readInstruction.body' }) + const ImportFileCardTitle = intl.formatMessage({ id: 'import.helpModal.importFile.card.title' }) + const ImportFileCardBody = intl.formatMessage({ id: 'import.helpModal.importFile.card.body' }) + const RequestCallMessage = intl.formatMessage({ id: 'import.helpModal.callback' }) + + const locale = useMemo(() => get(intl, 'locale'), [intl]) + const instructionUrl = useMemo(() => get(importInstructionUrl, [locale, domainName]), [domainName, locale]) + + if (!activeModal) return null + + return ( + <> + setActiveModal(null)} + title={ModalTitle} + > + + + {ChooseVariantMessage} + + + + { + instructionUrl && ( + window.open(instructionUrl, '_blank')} + header={{ + emoji: [{ symbol: '📄' }], + headingTitle: ReadInstructionsCardTitle, + }} + body={{ + description: ReadInstructionsCardBody, + }} + /> + ) + } + setActiveModal('upload')} + header={{ + emoji: [{ symbol: '🙀' }], + headingTitle: ImportFileCardTitle, + }} + body={{ + description: ImportFileCardBody, + }} + /> + + setActiveModal('call')} + /> + + + + + + + ) +} + +export const useImportHelpModal = ({ domainName }) => { + const [activeModal, setActiveModal] = useState() + const openImportHelpModal = useCallback(() => setActiveModal('choose'), []) + + const Modal = useCallback(() => ( + + ), [activeModal, domainName]) + + return useMemo(() => ({ Modal, openImportHelpModal }), [Modal, openImportHelpModal]) +} diff --git a/apps/condo/domains/common/schema/fields.js b/apps/condo/domains/common/schema/fields.js index cbf7ddf35b9..1d3e34be555 100644 --- a/apps/condo/domains/common/schema/fields.js +++ b/apps/condo/domains/common/schema/fields.js @@ -190,7 +190,7 @@ const PERCENT_FIELD = { } const getPhoneFieldHooks = ({ allowLandline }) => ({ - resolveInput: async ({ resolvedData, fieldPath }) => { + resolveInput: ({ resolvedData, fieldPath }) => { const newValue = normalizePhone(resolvedData[fieldPath], allowLandline) return newValue || resolvedData[fieldPath] }, diff --git a/apps/condo/domains/onboarding/access/UserHelpRequest.js b/apps/condo/domains/onboarding/access/UserHelpRequest.js new file mode 100644 index 00000000000..3f2a1dd852a --- /dev/null +++ b/apps/condo/domains/onboarding/access/UserHelpRequest.js @@ -0,0 +1,62 @@ +/** + * Generated by `createschema onboarding.UserHelpRequest 'type:Select:callback;importFile;user:Relationship:User:CASCADE;organization:Relationship:Organization:CASCADE;phone:Text;file?:File;meta?:Json'` + */ + +const get = require('lodash/get') +const isEmpty = require('lodash/isEmpty') +const omit = require('lodash/omit') + +const { throwAuthenticationError } = require('@open-condo/keystone/apolloErrorFormatter') +const { getById } = require('@open-condo/keystone/schema') + +const { checkOrganizationPermission } = require('@condo/domains/organization/utils/accessSchema') +const { STAFF } = require('@condo/domains/user/constants/common') + + +const AVAILABLE_TO_UPDATE_USER_HELP_REQUEST_FIELDS = ['dv', 'sender', 'isReadyToSend'] + +async function canReadUserHelpRequests ({ authentication: { item: user } }) { + if (!user) return throwAuthenticationError() + if (user.deletedAt) return false + + if (user.isAdmin || user.isSupport) return {} + + return { createdBy: { id: user.id } } +} + +async function canManageUserHelpRequests ({ authentication: { item: user }, originalInput, operation, itemId }) { + if (!user) return throwAuthenticationError() + if (user.deletedAt) return false + if (user.isAdmin) return true + + if (user.type !== STAFF) return false + + if (operation === 'create') { + const organizationIdInRequest = get(originalInput, 'organization.connect.id', null) + + return await checkOrganizationPermission(user.id, organizationIdInRequest) + } else if (operation === 'update') { + const helpRequest = await getById('UserHelpRequest', itemId) + const inaccessibleUpdatedFields = omit(originalInput, AVAILABLE_TO_UPDATE_USER_HELP_REQUEST_FIELDS) + + // can update only isReadyToSend field from false to true + if ( + helpRequest.createdBy !== user.id || + !isEmpty(inaccessibleUpdatedFields) || + (helpRequest.isReadyToSend && originalInput.isReadyToSend === false) + ) return false + + return true + } + + return false +} + +/* + Rules are logical functions that used for list access, and may return a boolean (meaning + all or no items are available) or a set of filters that limit the available items. +*/ +module.exports = { + canReadUserHelpRequests, + canManageUserHelpRequests, +} diff --git a/apps/condo/domains/onboarding/access/UserHelpRequestFile.js b/apps/condo/domains/onboarding/access/UserHelpRequestFile.js new file mode 100644 index 00000000000..c551b6ed197 --- /dev/null +++ b/apps/condo/domains/onboarding/access/UserHelpRequestFile.js @@ -0,0 +1,69 @@ +/** + * Generated by `createschema onboarding.UserHelpRequestFile 'userHelpRequest?:Relationship:UserHelpRequest:CASCADE;file:File'` + */ + +const { get } = require('lodash') +const isEmpty = require('lodash/isEmpty') +const omit = require('lodash/omit') + +const { throwAuthenticationError } = require('@open-condo/keystone/apolloErrorFormatter') +const { getById } = require('@open-condo/keystone/schema') + +const { checkOrganizationPermission } = require('@condo/domains/organization/utils/accessSchema') +const { STAFF } = require('@condo/domains/user/constants/common') + + +const AVAILABLE_TO_UPDATE_USER_HELP_REQUEST_FILE_FIELDS = ['dv', 'sender', 'userHelpRequest', 'deletedAt'] + +async function canReadUserHelpRequestFiles ({ authentication: { item: user } }) { + if (!user) return throwAuthenticationError() + if (user.deletedAt) return false + + if (user.isAdmin || user.isSupport) return {} + + return { createdBy: { id: user.id } } +} + +async function canManageUserHelpRequestFiles ({ authentication: { item: user }, originalInput, operation, itemId }) { + if (!user) return throwAuthenticationError() + if (user.deletedAt) return false + if (user.isAdmin) return true + + if (user.type !== STAFF) return false + + if (operation === 'create') { + const userHelpRequestId = get(originalInput, ['userHelpRequest', 'connect', 'id'], null) + // all staff users can create temp file without help request connection + if (!userHelpRequestId) return true + + const userHelpRequest = await getById('UserHelpRequest', userHelpRequestId) + if (!userHelpRequest) return false + + return await checkOrganizationPermission(user.id, userHelpRequest.organization) + } else if (operation === 'update') { + const inaccessibleUpdatedFields = omit(originalInput, AVAILABLE_TO_UPDATE_USER_HELP_REQUEST_FILE_FIELDS) + if (!isEmpty(inaccessibleUpdatedFields)) return false + + const userHelpRequestFile = await getById('UserHelpRequestFile', itemId) + if (userHelpRequestFile.userHelpRequest || userHelpRequestFile.createdBy !== user.id) return false + + const userHelpRequestId = get(originalInput, ['userHelpRequest', 'connect', 'id'], null) + if (!userHelpRequestId) return true + + const userHelpRequest = await getById('UserHelpRequest', userHelpRequestId) + if (!userHelpRequest || userHelpRequest.createdBy !== user.id) return false + + return true + } + + return false +} + +/* + Rules are logical functions that used for list access, and may return a boolean (meaning + all or no items are available) or a set of filters that limit the available items. +*/ +module.exports = { + canReadUserHelpRequestFiles, + canManageUserHelpRequestFiles, +} diff --git a/apps/condo/domains/onboarding/constants/userHelpRequest.js b/apps/condo/domains/onboarding/constants/userHelpRequest.js new file mode 100644 index 00000000000..041d88a4382 --- /dev/null +++ b/apps/condo/domains/onboarding/constants/userHelpRequest.js @@ -0,0 +1,16 @@ +const CALLBACK_TYPE = 'callback' +const IMPORT_FILE_TYPE = 'importFile' + +const USER_HELP_REQUEST_TYPES = [ + CALLBACK_TYPE, + IMPORT_FILE_TYPE, +] + +const USER_HELP_REQUEST_FOLDER_NAME = 'UserHelpRequest' + +module.exports = { + CALLBACK_TYPE, + IMPORT_FILE_TYPE, + USER_HELP_REQUEST_TYPES, + USER_HELP_REQUEST_FOLDER_NAME, +} \ No newline at end of file diff --git a/apps/condo/domains/onboarding/gql.js b/apps/condo/domains/onboarding/gql.js index f2548a43aec..17ef9b49bb6 100644 --- a/apps/condo/domains/onboarding/gql.js +++ b/apps/condo/domains/onboarding/gql.js @@ -34,6 +34,12 @@ const SYNC_TOUR_STEPS_MUTATION = gql` } ` +const USER_HELP_REQUEST_FIELDS = `{ type organization { id } phone meta isReadyToSend ${COMMON_FIELDS} }` +const UserHelpRequest = generateGqlQueries('UserHelpRequest', USER_HELP_REQUEST_FIELDS) + +const USER_HELP_REQUEST_FILE_FIELDS = `{ userHelpRequest { id } file { id publicUrl } ${COMMON_FIELDS} }` +const UserHelpRequestFile = generateGqlQueries('UserHelpRequestFile', USER_HELP_REQUEST_FILE_FIELDS) + /* AUTOGENERATE MARKER */ module.exports = { @@ -42,5 +48,7 @@ module.exports = { CREATE_ONBOARDING_MUTATION, TourStep, SYNC_TOUR_STEPS_MUTATION, + UserHelpRequest, + UserHelpRequestFile, /* AUTOGENERATE MARKER */ } diff --git a/apps/condo/domains/onboarding/schema/UserHelpRequest.js b/apps/condo/domains/onboarding/schema/UserHelpRequest.js new file mode 100644 index 00000000000..0a19a490c34 --- /dev/null +++ b/apps/condo/domains/onboarding/schema/UserHelpRequest.js @@ -0,0 +1,91 @@ +/** + * Generated by `createschema onboarding.UserHelpRequest 'type:Select:callback;importFile;organization:Relationship:Organization:CASCADE;phone:Text;file?:File;meta?:Json'` + */ +const get = require('lodash/get') +const isEmpty = require('lodash/isEmpty') + +const { GQLError } = require('@open-condo/keystone/errors') +const { historical, versioned, uuided, tracked, softDeleted, dvAndSender } = require('@open-condo/keystone/plugins') +const { GQLListSchema } = require('@open-condo/keystone/schema') +const { webHooked } = require('@open-condo/webhooks/plugins') + +const { COMMON_ERRORS } = require('@condo/domains/common/constants/errors') +const { normalizePhone } = require('@condo/domains/common/utils/phone') +const access = require('@condo/domains/onboarding/access/UserHelpRequest') +const { USER_HELP_REQUEST_TYPES } = require('@condo/domains/onboarding/constants/userHelpRequest') +const { UserHelpRequestFile } = require('@condo/domains/onboarding/utils/serverSchema') +const { ORGANIZATION_OWNED_FIELD } = require('@condo/domains/organization/schema/fields') + + +const UserHelpRequest = new GQLListSchema('UserHelpRequest', { + schemaDoc: 'Request from the user to help him with some functionality', + fields: { + type: { + schemaDoc: 'Type of request. It\'s can be, for example, request for callback or request to help with import', + type: 'Select', + dataType: 'string', + options: USER_HELP_REQUEST_TYPES, + isRequired: true, + }, + organization: ORGANIZATION_OWNED_FIELD, + phone: { + schemaDoc: 'Specified phone in request for callback', + type: 'Text', + isRequired: true, + hooks: { + resolveInput: ({ resolvedData, fieldPath }) => { + const newValue = resolvedData[fieldPath] + + if (newValue) { + return normalizePhone(newValue) || newValue + } + }, + validateInput: async ({ resolvedData, fieldPath, context }) => { + const newValue = resolvedData[fieldPath] + + if (newValue && newValue !== normalizePhone(newValue)) { + throw new GQLError(COMMON_ERRORS.WRONG_PHONE_FORMAT, context) + } + }, + }, + }, + isReadyToSend: { + schemaDoc: 'Shows if the request is ready to send. False value can be, for example, if files are not synced with help request yet', + type: 'Checkbox', + defaultValue: true, + }, + meta: { + schemaDoc: 'Additional info about request. May contain information about file urls, page where user made request or import type', + type: 'Json', + hooks: { + resolveInput: async ({ resolvedData, fieldPath, existingItem, context }) => { + if (!existingItem) return resolvedData[fieldPath] + + const userHelpRequestId = existingItem.id + const files = await UserHelpRequestFile.getAll(context, { userHelpRequest: { id: userHelpRequestId } }) + + if (!isEmpty(files)) { + const currentMeta = { ...get(existingItem, fieldPath, {}), ...get(resolvedData, fieldPath, {}) } + const urls = files.map(file => get(file, 'file.publicUrl')).filter(Boolean) + + return { ...currentMeta, files: urls } + } + + return resolvedData[fieldPath] + }, + }, + }, + }, + plugins: [uuided(), versioned(), tracked(), softDeleted(), dvAndSender(), historical(), webHooked()], + access: { + read: access.canReadUserHelpRequests, + create: access.canManageUserHelpRequests, + update: access.canManageUserHelpRequests, + delete: false, + auth: true, + }, +}) + +module.exports = { + UserHelpRequest, +} diff --git a/apps/condo/domains/onboarding/schema/UserHelpRequest.test.js b/apps/condo/domains/onboarding/schema/UserHelpRequest.test.js new file mode 100644 index 00000000000..a33fb8a5eeb --- /dev/null +++ b/apps/condo/domains/onboarding/schema/UserHelpRequest.test.js @@ -0,0 +1,310 @@ +/** + * Generated by `createschema onboarding.UserHelpRequest 'type:Select:callback;importFile;user:Relationship:User:CASCADE;organization:Relationship:Organization:CASCADE;phone:Text;file?:File;meta?:Json'` + */ + +const { faker } = require('@faker-js/faker') + +const { makeLoggedInAdminClient, makeClient, expectToThrowGQLError } = require('@open-condo/keystone/test.utils') +const { + expectToThrowAuthenticationErrorToObj, expectToThrowAuthenticationErrorToObjects, + expectToThrowAccessDeniedErrorToObj, +} = require('@open-condo/keystone/test.utils') + +const { COMMON_ERRORS } = require('@condo/domains/common/constants/errors') +const { UserHelpRequest, createTestUserHelpRequest, updateTestUserHelpRequest } = require('@condo/domains/onboarding/utils/testSchema') +const { createTestOrganization, createTestOrganizationEmployeeRole, createTestOrganizationEmployee } = require('@condo/domains/organization/utils/testSchema') +const { makeClientWithNewRegisteredAndLoggedInUser, makeClientWithSupportUser, makeClientWithResidentUser } = require('@condo/domains/user/utils/testSchema') +const { createTestPhone } = require('@condo/domains/user/utils/testSchema') + + +const UPDATE_PAYLOAD = { isReadyToSend: true } + +describe('UserHelpRequest', () => { + let admin, support, anonymous, organization, employeeUser, notEmployeeUser, employeeUserRequest, residentClient + + beforeAll(async () => { + admin = await makeLoggedInAdminClient() + support = await makeClientWithSupportUser() + employeeUser = await makeClientWithNewRegisteredAndLoggedInUser() + notEmployeeUser = await makeClientWithNewRegisteredAndLoggedInUser() + anonymous = await makeClient() + + const [testOrganization] = await createTestOrganization(admin) + organization = testOrganization + + const [role] = await createTestOrganizationEmployeeRole(admin, organization, { + canManageCallRecords: true, + canManageTickets: true, + }) + await createTestOrganizationEmployee(admin, organization, employeeUser.user, role) + + const [helpRequest] = await createTestUserHelpRequest(employeeUser, organization) + employeeUserRequest = helpRequest + + residentClient = await makeClientWithResidentUser() + }) + + describe('Access', () => { + describe('Create', () => { + it('admin can', async () => { + const phone = createTestPhone() + + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + phone, + }) + + expect(helpRequest).toBeDefined() + expect(helpRequest.phone).toEqual(phone) + }) + + it('anonymous can not', async () => { + await expectToThrowAuthenticationErrorToObj(async () => { + await createTestUserHelpRequest(anonymous, organization) + }) + }) + + it('support can not', async () => { + await expectToThrowAccessDeniedErrorToObj(async () => { + await createTestUserHelpRequest(support, organization) + }) + }) + + it('user: can if he is employee', async () => { + const [helpRequest] = await createTestUserHelpRequest(employeeUser, organization) + + expect(helpRequest).toBeDefined() + expect(helpRequest.phone).toEqual(employeeUser.userAttrs.phone) + }) + + it('user: can not if he is not employee', async () => { + await expectToThrowAccessDeniedErrorToObj(async () => { + await createTestUserHelpRequest(notEmployeeUser, organization) + }) + }) + + it('user with resident type: can not create', async () => { + await expectToThrowAccessDeniedErrorToObj(async () => { + await createTestUserHelpRequest(residentClient, organization) + }) + }) + }) + + describe('Read', () => { + it('admin can', async () => { + const readHelpRequest = await UserHelpRequest.getOne(admin, { id: employeeUserRequest.id }) + expect(readHelpRequest).toBeDefined() + }) + + it('anonymous can not', async () => { + await expectToThrowAuthenticationErrorToObjects(async () => { + await UserHelpRequest.getOne(anonymous, { id: employeeUserRequest.id }) + }) + }) + + it('support can', async () => { + const readHelpRequest = await UserHelpRequest.getOne(support, { id: employeeUserRequest.id }) + expect(readHelpRequest).toBeDefined() + }) + + it('user: can read own help requests', async () => { + const readHelpRequest = await UserHelpRequest.getOne(employeeUser, { id: employeeUserRequest.id }) + expect(readHelpRequest).toBeDefined() + }) + + it('user: can not read not his help requests', async () => { + const readHelpRequest = await UserHelpRequest.getOne(notEmployeeUser, { id: employeeUserRequest.id }) + expect(readHelpRequest).toBeUndefined() + }) + }) + + describe('Update', () => { + it('admin can', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + isReadyToSend: false, + }) + const [updatedHelpRequest] = await updateTestUserHelpRequest(admin, helpRequest.id, UPDATE_PAYLOAD) + + expect(updatedHelpRequest.isReadyToSend).toBeTruthy() + }) + + it('anonymous can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + isReadyToSend: false, + }) + + await expectToThrowAuthenticationErrorToObj(async () => { + await updateTestUserHelpRequest(anonymous, helpRequest.id, UPDATE_PAYLOAD) + }) + }) + + it('support can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + isReadyToSend: false, + }) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequest(support, helpRequest.id, UPDATE_PAYLOAD) + }) + }) + + it('user: can update his help request', async () => { + const [helpRequest] = await createTestUserHelpRequest(employeeUser, organization, { + isReadyToSend: false, + }) + + const [updatedHelpRequest] = await updateTestUserHelpRequest(employeeUser, helpRequest.id, UPDATE_PAYLOAD) + + expect(updatedHelpRequest.isReadyToSend).toBeTruthy() + }) + + it('user: can not update not his help request', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + isReadyToSend: false, + }) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequest(employeeUser, helpRequest.id, UPDATE_PAYLOAD) + }) + }) + + it('user: can update only isReadyToSend field from false to true', async () => { + const [helpRequest] = await createTestUserHelpRequest(employeeUser, organization, { + isReadyToSend: false, + }) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequest(employeeUser, helpRequest.id, { + meta: { test: '123' }, + }) + }) + + const [updatedHelpRequest] = await updateTestUserHelpRequest(employeeUser, helpRequest.id, { + isReadyToSend: true, + }) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequest(employeeUser, updatedHelpRequest.id, { + isReadyToSend: false, + }) + }) + }) + + it('user with resident type: can not update', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + isReadyToSend: false, + }) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequest(residentClient, helpRequest.id, UPDATE_PAYLOAD) + }) + }) + }) + + describe('Soft Delete', () => { + it('admin can', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + const [deletedRequest] = await updateTestUserHelpRequest(admin, helpRequest.id, { deletedAt: 'true' }) + + expect(deletedRequest.deletedAt).toBeDefined() + }) + + it('anonymous can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + + await expectToThrowAuthenticationErrorToObj(async () => { + await updateTestUserHelpRequest(anonymous, helpRequest.id, { deletedAt: 'true' }) + }) + }) + + it('support can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequest(support, helpRequest.id, { deletedAt: 'true' }) + }) + }) + + it('user: can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequest(employeeUser, helpRequest.id, { deletedAt: 'true' }) + }) + }) + }) + + describe('Delete', () => { + it('admin can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequest.delete(admin, helpRequest.id) + }) + }) + + it('anonymous can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequest.delete(anonymous, helpRequest.id) + }) + }) + + it('support can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequest.delete(support, helpRequest.id) + }) + }) + + it('user: can not', async () => { + const [helpRequest] = await createTestUserHelpRequest(admin, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequest.delete(employeeUser, helpRequest.id) + }) + }) + }) + }) + + describe('Validations', () => { + it('can create help request with right format phone', async () => { + const phone = createTestPhone() + + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + phone, + }) + + expect(helpRequest).toBeDefined() + expect(helpRequest.phone).toEqual(phone) + }) + + it('can not create help request with wrong format phone', async () => { + const wrongPhone = faker.random.alphaNumeric(8) + + await expectToThrowGQLError( + async () => await createTestUserHelpRequest(admin, organization, { phone: wrongPhone }), + { ...COMMON_ERRORS.WRONG_PHONE_FORMAT } + ) + }) + + it('can not update phone if no phone passed in update input', async () => { + const phone = createTestPhone() + + const [helpRequest] = await createTestUserHelpRequest(admin, organization, { + phone, + }) + + expect(helpRequest).toBeDefined() + expect(helpRequest.phone).toEqual(phone) + + const updatePayload = { meta: { test: '123' } } + const [updatedHelpRequest] = await updateTestUserHelpRequest(admin, helpRequest.id, updatePayload) + + expect(updatedHelpRequest.phone).toEqual(helpRequest.phone) + expect(updatedHelpRequest.meta).toEqual(updatePayload.meta) + }) + + }) +}) diff --git a/apps/condo/domains/onboarding/schema/UserHelpRequestFile.js b/apps/condo/domains/onboarding/schema/UserHelpRequestFile.js new file mode 100644 index 00000000000..3cadb5baa18 --- /dev/null +++ b/apps/condo/domains/onboarding/schema/UserHelpRequestFile.js @@ -0,0 +1,44 @@ +/** + * Generated by `createschema onboarding.UserHelpRequestFile 'userHelpRequest?:Relationship:UserHelpRequest:CASCADE;file:File'` + */ +const { historical, versioned, uuided, tracked, softDeleted, dvAndSender } = require('@open-condo/keystone/plugins') +const { GQLListSchema } = require('@open-condo/keystone/schema') + +const FileAdapter = require('@condo/domains/common/utils/fileAdapter') +const { getFileMetaAfterChange } = require('@condo/domains/common/utils/fileAdapter') +const access = require('@condo/domains/onboarding/access/UserHelpRequestFile') +const { USER_HELP_REQUEST_FOLDER_NAME } = require('@condo/domains/onboarding/constants/userHelpRequest') + + +const Adapter = new FileAdapter(USER_HELP_REQUEST_FOLDER_NAME) +const fileMetaAfterChange = getFileMetaAfterChange(Adapter) + +const UserHelpRequestFile = new GQLListSchema('UserHelpRequestFile', { + schemaDoc: 'File related to user help request', + fields: { + userHelpRequest: { + type: 'Relationship', + ref: 'UserHelpRequest', + kmigratorOptions: { null: true, on_delete: 'models.CASCADE' }, + }, + file: { + adapter: Adapter, + type: 'File', + }, + }, + hooks: { + afterChange: fileMetaAfterChange, + }, + plugins: [uuided(), versioned(), tracked(), softDeleted(), dvAndSender(), historical()], + access: { + read: access.canReadUserHelpRequestFiles, + create: access.canManageUserHelpRequestFiles, + update: access.canManageUserHelpRequestFiles, + delete: false, + auth: true, + }, +}) + +module.exports = { + UserHelpRequestFile, +} diff --git a/apps/condo/domains/onboarding/schema/UserHelpRequestFile.test.js b/apps/condo/domains/onboarding/schema/UserHelpRequestFile.test.js new file mode 100644 index 00000000000..8126b090a93 --- /dev/null +++ b/apps/condo/domains/onboarding/schema/UserHelpRequestFile.test.js @@ -0,0 +1,258 @@ +/** + * Generated by `createschema onboarding.UserHelpRequestFileFile 'userHelpRequestFile?:Relationship:UserHelpRequestFile:CASCADE;file:File'` + */ + +const { makeLoggedInAdminClient, makeClient } = require('@open-condo/keystone/test.utils') +const { + expectToThrowAuthenticationErrorToObj, expectToThrowAuthenticationErrorToObjects, + expectToThrowAccessDeniedErrorToObj, +} = require('@open-condo/keystone/test.utils') + +const { createTestUserHelpRequest } = require('@condo/domains/onboarding/utils/testSchema') +const { createTestUserHelpRequestFile, UserHelpRequestFile, updateTestUserHelpRequestFile } = require('@condo/domains/onboarding/utils/testSchema') +const { createTestOrganization, createTestOrganizationEmployeeRole, createTestOrganizationEmployee } = require('@condo/domains/organization/utils/testSchema') +const { makeClientWithNewRegisteredAndLoggedInUser, makeClientWithSupportUser } = require('@condo/domains/user/utils/testSchema') +const { makeClientWithResidentUser } = require('@condo/domains/user/utils/testSchema') + + +describe('UserHelpRequestFileFile', () => { + let admin, support, anonymous, organization, employeeUser, notEmployeeUser, residentClient, + employeeUserRequest, connectHelpRequestPayload, employeeHelpRequestFile + + beforeAll(async () => { + admin = await makeLoggedInAdminClient() + support = await makeClientWithSupportUser() + employeeUser = await makeClientWithNewRegisteredAndLoggedInUser() + notEmployeeUser = await makeClientWithNewRegisteredAndLoggedInUser() + anonymous = await makeClient() + + const [testOrganization] = await createTestOrganization(admin) + organization = testOrganization + + const [role] = await createTestOrganizationEmployeeRole(admin, organization, { + canManageCallRecords: true, + canManageTickets: true, + }) + await createTestOrganizationEmployee(admin, organization, employeeUser.user, role) + + const [helpRequest] = await createTestUserHelpRequest(employeeUser, organization) + employeeUserRequest = helpRequest + + connectHelpRequestPayload = { userHelpRequest: { connect: { id: employeeUserRequest.id } } } + + const [helpRequestFile] = await createTestUserHelpRequestFile(employeeUser, connectHelpRequestPayload) + employeeHelpRequestFile = helpRequestFile + + residentClient = await makeClientWithResidentUser() + }) + + describe('Access', () => { + describe('Create', () => { + it('admin can', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + expect(helpRequestFile).toBeDefined() + }) + + it('anonymous can not', async () => { + await expectToThrowAuthenticationErrorToObj(async () => { + await createTestUserHelpRequestFile(anonymous) + }) + }) + + it('support can not', async () => { + await expectToThrowAccessDeniedErrorToObj(async () => { + await createTestUserHelpRequestFile(support, connectHelpRequestPayload) + }) + }) + + it('user: can create without help request connection', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(notEmployeeUser) + + expect(helpRequestFile).toBeDefined() + }) + + it('user: can create with help request connection if he is employee', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(employeeUser, connectHelpRequestPayload) + + expect(helpRequestFile).toBeDefined() + expect(helpRequestFile.userHelpRequest.id).toEqual(employeeUserRequest.id) + }) + + it('user: can not create with help request connection if he is not employee', async () => { + await expectToThrowAccessDeniedErrorToObj(async () => { + await createTestUserHelpRequestFile(notEmployeeUser, connectHelpRequestPayload) + }) + }) + + it('user with resident type: can not', async () => { + await expectToThrowAccessDeniedErrorToObj(async () => { + await createTestUserHelpRequestFile(residentClient) + }) + }) + }) + + describe('Read', () => { + it('admin can', async () => { + const readHelpRequestFile = await UserHelpRequestFile.getOne(admin, { id: employeeHelpRequestFile.id }) + expect(readHelpRequestFile).toBeDefined() + }) + + it('anonymous can not', async () => { + await expectToThrowAuthenticationErrorToObjects(async () => { + await UserHelpRequestFile.getOne(anonymous, { id: employeeHelpRequestFile.id }) + }) + }) + + it('support can', async () => { + const readHelpRequestFile = await UserHelpRequestFile.getOne(support, { id: employeeHelpRequestFile.id }) + expect(readHelpRequestFile).toBeDefined() + }) + + it('user: can read own help request files', async () => { + const readHelpRequestFile = await UserHelpRequestFile.getOne(employeeUser, { id: employeeHelpRequestFile.id }) + expect(readHelpRequestFile).toBeDefined() + }) + + it('user: can not read not his help request files', async () => { + const readHelpRequestFile = await UserHelpRequestFile.getOne(notEmployeeUser, { id: employeeHelpRequestFile.id }) + expect(readHelpRequestFile).toBeUndefined() + }) + }) + + describe('Update', () => { + it('admin can', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + const [updatedHelpRequestFile] = await updateTestUserHelpRequestFile(admin, helpRequestFile.id, connectHelpRequestPayload) + + expect(updatedHelpRequestFile.userHelpRequest.id).toEqual(employeeUserRequest.id) + }) + + it('anonymous can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAuthenticationErrorToObj(async () => { + await updateTestUserHelpRequestFile(anonymous, helpRequestFile.id, connectHelpRequestPayload) + }) + }) + + it('support can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequestFile(support, helpRequestFile.id, connectHelpRequestPayload) + }) + }) + + it('user: can update his help request file', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(employeeUser) + const [updatedHelpRequestFile] = await updateTestUserHelpRequestFile(employeeUser, helpRequestFile.id, connectHelpRequestPayload) + + expect(updatedHelpRequestFile.userHelpRequest.id).toEqual(employeeUserRequest.id) + }) + + it('user: can not update not his help request file', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequestFile(employeeUser, helpRequestFile.id, connectHelpRequestPayload) + }) + }) + + it('user: can connect help request only one time', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(employeeUser) + const [updatedHelpRequestFile] = await updateTestUserHelpRequestFile(employeeUser, helpRequestFile.id, connectHelpRequestPayload) + + expect(updatedHelpRequestFile.userHelpRequest.id).toEqual(employeeUserRequest.id) + + const [otherHelpRequest] = await createTestUserHelpRequest(employeeUser, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequestFile(support, helpRequestFile.id, { + userHelpRequest: { connect: { id: otherHelpRequest.id } }, + }) + }) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequestFile(support, helpRequestFile.id, { + userHelpRequest: { disconnectAll: true }, + }) + }) + }) + + it('user with resident type: can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequestFile(residentClient, helpRequestFile.id, connectHelpRequestPayload) + }) + }) + }) + + describe('Soft Delete', () => { + it('admin can', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + const [deletedRequestFile] = await updateTestUserHelpRequestFile(admin, helpRequestFile.id, { deletedAt: 'true' }) + + expect(deletedRequestFile.deletedAt).toBeDefined() + }) + + it('anonymous can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAuthenticationErrorToObj(async () => { + await updateTestUserHelpRequestFile(anonymous, helpRequestFile.id, { deletedAt: 'true' }) + }) + }) + + it('support can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await updateTestUserHelpRequestFile(support, helpRequestFile.id, { deletedAt: 'true' }) + }) + }) + + it('user: can soft delete his help request file', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(employeeUser) + const [deletedRequestFile] = await updateTestUserHelpRequestFile(employeeUser, helpRequestFile.id, { deletedAt: 'true' }) + + expect(deletedRequestFile.deletedAt).toBeDefined() + }) + }) + + describe('Delete', () => { + it('admin can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequestFile.delete(admin, helpRequestFile.id) + }) + }) + + it('anonymous can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequestFile.delete(anonymous, helpRequestFile.id) + }) + }) + + it('support can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequestFile.delete(support, helpRequestFile.id) + }) + }) + + it('user: can not', async () => { + const [helpRequestFile] = await createTestUserHelpRequestFile(admin) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await UserHelpRequestFile.delete(employeeUser, helpRequestFile.id) + }) + }) + }) + }) +}) diff --git a/apps/condo/domains/onboarding/schema/index.js b/apps/condo/domains/onboarding/schema/index.js index ce7a2644820..36ae6728d30 100644 --- a/apps/condo/domains/onboarding/schema/index.js +++ b/apps/condo/domains/onboarding/schema/index.js @@ -8,6 +8,8 @@ const { OnBoarding } = require('./OnBoarding') const { OnBoardingStep } = require('./OnBoardingStep') const { SyncTourStepsService } = require('./SyncTourStepsService') const { TourStep } = require('./TourStep') +const { UserHelpRequest } = require('./UserHelpRequest') +const { UserHelpRequestFile } = require('./UserHelpRequestFile') /* AUTOGENERATE MARKER */ module.exports = { @@ -16,5 +18,7 @@ module.exports = { CreateOnBoardingService, TourStep, SyncTourStepsService, + UserHelpRequest, + UserHelpRequestFile, /* AUTOGENERATE MARKER */ } diff --git a/apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequest.ts b/apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequest.ts new file mode 100644 index 00000000000..53cd84bca70 --- /dev/null +++ b/apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequest.ts @@ -0,0 +1,31 @@ +/** + * Generated by `createschema onboarding.UserHelpRequest 'type:Select:callback;importFile;user:Relationship:User:CASCADE;organization:Relationship:Organization:CASCADE;phone:Text;file?:File;meta?:Json'` + */ + +import { + UserHelpRequest, + UserHelpRequestCreateInput, + UserHelpRequestUpdateInput, + QueryAllUserHelpRequestsArgs, +} from '@app/condo/schema' + +import { generateReactHooks } from '@open-condo/codegen/generate.hooks' + +import { UserHelpRequest as UserHelpRequestGQL } from '@condo/domains/onboarding/gql' + + +const { + useObject, + useObjects, + useCreate, + useUpdate, + useSoftDelete, +} = generateReactHooks(UserHelpRequestGQL) + +export { + useObject, + useObjects, + useCreate, + useUpdate, + useSoftDelete, +} diff --git a/apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequestFile.ts b/apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequestFile.ts new file mode 100644 index 00000000000..d182b8f73c5 --- /dev/null +++ b/apps/condo/domains/onboarding/utils/clientSchema/UserHelpRequestFile.ts @@ -0,0 +1,31 @@ +/** + * Generated by `createschema onboarding.UserHelpRequestFile 'userHelpRequest?:Relationship:UserHelpRequest:CASCADE;file:File'` + */ + +import { + UserHelpRequestFile, + UserHelpRequestFileCreateInput, + UserHelpRequestFileUpdateInput, + QueryAllUserHelpRequestFilesArgs, +} from '@app/condo/schema' + +import { generateReactHooks } from '@open-condo/codegen/generate.hooks' + +import { UserHelpRequestFile as UserHelpRequestFileGQL } from '@condo/domains/onboarding/gql' + + +const { + useObject, + useObjects, + useCreate, + useUpdate, + useSoftDelete, +} = generateReactHooks(UserHelpRequestFileGQL) + +export { + useObject, + useObjects, + useCreate, + useUpdate, + useSoftDelete, +} diff --git a/apps/condo/domains/onboarding/utils/clientSchema/index.ts b/apps/condo/domains/onboarding/utils/clientSchema/index.ts index dc9c7323424..f52db989747 100644 --- a/apps/condo/domains/onboarding/utils/clientSchema/index.ts +++ b/apps/condo/domains/onboarding/utils/clientSchema/index.ts @@ -6,4 +6,6 @@ export * as OnBoarding from './OnBoarding' export * as OnBoardingStep from './OnBoardingStep' export * as TourStep from './TourStep' +export * as UserHelpRequest from './UserHelpRequest' +export * as UserHelpRequestFile from './UserHelpRequestFile' /* AUTOGENERATE MARKER */ diff --git a/apps/condo/domains/onboarding/utils/serverSchema/index.js b/apps/condo/domains/onboarding/utils/serverSchema/index.js index f2197dab40e..52d29fd6495 100644 --- a/apps/condo/domains/onboarding/utils/serverSchema/index.js +++ b/apps/condo/domains/onboarding/utils/serverSchema/index.js @@ -8,6 +8,8 @@ const { OnBoardingStep: OnBoardingStepGQL } = require('@condo/domains/onboarding const { CREATE_ON_BOARDING_MUTATION } = require('@condo/domains/onboarding/gql') const { TourStep: TourStepGQL } = require('@condo/domains/onboarding/gql') const { SYNC_TOUR_STEPS_MUTATION } = require('@condo/domains/onboarding/gql') +const { UserHelpRequest: UserHelpRequestGQL } = require('@condo/domains/onboarding/gql') +const { UserHelpRequestFile: UserHelpRequestFileGQL } = require('@condo/domains/onboarding/gql') /* AUTOGENERATE MARKER */ const OnBoarding = generateServerUtils(OnBoardingGQL) @@ -41,6 +43,8 @@ async function syncTourSteps (context, data) { }) } +const UserHelpRequest = generateServerUtils(UserHelpRequestGQL) +const UserHelpRequestFile = generateServerUtils(UserHelpRequestFileGQL) /* AUTOGENERATE MARKER */ const createTourStepsForOrganization = async (context, organization, dvSenderData) => { @@ -64,5 +68,7 @@ module.exports = { createTourStepsForOrganization, TourStep, syncTourSteps, + UserHelpRequest, + UserHelpRequestFile, /* AUTOGENERATE MARKER */ } diff --git a/apps/condo/domains/onboarding/utils/testSchema/index.js b/apps/condo/domains/onboarding/utils/testSchema/index.js index c20e5b78a1b..25d92dd6533 100644 --- a/apps/condo/domains/onboarding/utils/testSchema/index.js +++ b/apps/condo/domains/onboarding/utils/testSchema/index.js @@ -12,12 +12,18 @@ const { OnBoardingStep: OnBoardingStepGQL } = require('@condo/domains/onboarding const { CREATE_ONBOARDING_MUTATION } = require('@condo/domains/onboarding/gql') const { TourStep: TourStepGQL } = require('@condo/domains/onboarding/gql') const { SYNC_TOUR_STEPS_MUTATION } = require('@condo/domains/onboarding/gql') +const { UserHelpRequest: UserHelpRequestGQL } = require('@condo/domains/onboarding/gql') +const { UserHelpRequestFile: UserHelpRequestFileGQL } = require('@condo/domains/onboarding/gql') +const { createTestPhone } = require('@condo/domains/user/utils/testSchema') +const get = require('lodash/get') /* AUTOGENERATE MARKER */ const OnBoarding = generateGQLTestUtils(OnBoardingGQL) const OnBoardingStep = generateGQLTestUtils(OnBoardingStepGQL) const TourStep = generateGQLTestUtils(TourStepGQL) +const UserHelpRequest = generateGQLTestUtils(UserHelpRequestGQL) +const UserHelpRequestFile = generateGQLTestUtils(UserHelpRequestFileGQL) /* AUTOGENERATE MARKER */ async function createTestOnBoarding (client, extraAttrs = {}) { @@ -143,6 +149,65 @@ async function syncTourStepsByTestClient(client, organization) { throwIfError(data, errors) return [data.result, attrs] } + +async function createTestUserHelpRequest (client, organization, extraAttrs = {}) { + if (!client) throw new Error('no client') + if (!organization || !organization.id) throw new Error('no organization.id') + const sender = { dv: 1, fingerprint: faker.random.alphaNumeric(8) } + + const attrs = { + dv: 1, + sender, + organization: { connect: { id: organization.id } }, + type: 'callback', + phone: get(client, 'userAttrs.phone', createTestPhone()), + ...extraAttrs, + } + const obj = await UserHelpRequest.create(client, attrs) + return [obj, attrs] +} + +async function updateTestUserHelpRequest (client, id, extraAttrs = {}) { + if (!client) throw new Error('no client') + if (!id) throw new Error('no id') + const sender = { dv: 1, fingerprint: faker.random.alphaNumeric(8) } + + const attrs = { + dv: 1, + sender, + ...extraAttrs, + } + const obj = await UserHelpRequest.update(client, id, attrs) + return [obj, attrs] +} + +async function createTestUserHelpRequestFile (client, extraAttrs = {}) { + if (!client) throw new Error('no client') + const sender = { dv: 1, fingerprint: faker.random.alphaNumeric(8) } + + const attrs = { + dv: 1, + sender, + ...extraAttrs, + } + const obj = await UserHelpRequestFile.create(client, attrs) + return [obj, attrs] +} + +async function updateTestUserHelpRequestFile (client, id, extraAttrs = {}) { + if (!client) throw new Error('no client') + if (!id) throw new Error('no id') + const sender = { dv: 1, fingerprint: faker.random.alphaNumeric(8) } + + const attrs = { + dv: 1, + sender, + ...extraAttrs, + } + const obj = await UserHelpRequestFile.update(client, id, attrs) + return [obj, attrs] +} + /* AUTOGENERATE MARKER */ module.exports = { @@ -151,5 +216,7 @@ module.exports = { createOnBoardingByTestClient, TourStep, createTestTourStep, updateTestTourStep, syncTourStepsByTestClient, + UserHelpRequest, createTestUserHelpRequest, updateTestUserHelpRequest, + UserHelpRequestFile, createTestUserHelpRequestFile, updateTestUserHelpRequestFile, /* AUTOGENERATE MARKER */ } diff --git a/apps/condo/domains/ticket/components/TicketId/TicketFileList.tsx b/apps/condo/domains/ticket/components/TicketId/TicketFileList.tsx index bd75ee149db..454d407b039 100644 --- a/apps/condo/domains/ticket/components/TicketId/TicketFileList.tsx +++ b/apps/condo/domains/ticket/components/TicketId/TicketFileList.tsx @@ -18,6 +18,10 @@ const UploadListWrapperStyles = css` .ant-upload-list-text-container:first-child .ant-upload-list-item { margin-top: 0; } + + .ant-upload-list-item:hover .ant-upload-list-item-info { + background-color: inherit; + } & .ant-upload-span a.ant-upload-list-item-name { color: ${colors.black}; diff --git a/apps/condo/lang/en/en.json b/apps/condo/lang/en/en.json index f42df9c8539..fb4747143fe 100644 --- a/apps/condo/lang/en/en.json +++ b/apps/condo/lang/en/en.json @@ -35,6 +35,7 @@ "import.uploadModal.message": "Download the file with {prepositional} information or use the template", "import.uploadModal.requiredFields.title": "Fill in the required fields", "import.uploadModal.exampleLinkMessage": "{genitive} list template.xslx", + "import.uploadModal.needHelp": "Need help", "import.progressModal.title": "Data loading", "import.progressModal.message": "Wait please", "import.progressModal.description": "The download will stop if you close the browser tab or turn off the computer", @@ -61,6 +62,24 @@ "import.ticket.genitive": "Tickets", "import.ticket.prepositional": "Tickets", "import.ticket.requiredFields": "Enter the address and describe the problem", + "import.helpModal.title": "We will help", + "import.helpModal.chooseVariant": "Choose the most convenient option:", + "import.helpModal.readInstruction.title": "Read the instructions", + "import.helpModal.readInstruction.body": "We have prepared a detailed document with examples", + "import.helpModal.importFile.card.title": "Download data in other format", + "import.helpModal.importFile.card.body": "Bulk data in non-standard format can be sent to the manager - he will add them to the platform", + "import.helpModal.callback": "Request a call", + "import.callbackModal.title": "Check phone number", + "import.callbackModal.submitButtonLabel": "The number is correct – call", + "import.callbackModal.body": "The manager will call in 40 minutes and help to figure out", + "import.callbackModal.phoneLabel": "Phone number", + "import.fileImportHelpModal.title": "Upload in a different format", + "import.fileImportHelpModal.submitButtonLabel": "Upload", + "import.fileImportHelpModal.body": "Information can be uploaded in other format: Excel-tables with other fields, photo documents, downloads from CRM, etc.", + "import.fileImportHelpModal.alert.message": "Upload takes 1 to 3 working days", + "import.fileImportHelpModal.alert.description": "The manager will add the information manually. We will call and let you know when everything is ready.", + "import.fileImportHelpModal.phoneCheck": "Check your phone number so we can quickly contact you if we have any questions about the file.", + "import.fileImportHelpModal.phoneLabel": "Phone number", "Create": "Create", "Created": "Created", "CreatedDate": "Creation date", diff --git a/apps/condo/lang/ru/ru.json b/apps/condo/lang/ru/ru.json index ebe6eb0780e..bdc795f523c 100644 --- a/apps/condo/lang/ru/ru.json +++ b/apps/condo/lang/ru/ru.json @@ -35,6 +35,7 @@ "import.uploadModal.message": "Загрузите свой файл с информацией о {prepositional} или воспользуйтесь шаблоном", "import.uploadModal.requiredFields.title": "Заполните обязательные поля", "import.uploadModal.exampleLinkMessage": "Шаблон списка {genitive}.xslx", + "import.uploadModal.needHelp": "Нужна помощь", "import.progressModal.title": "Загрузка данных", "import.progressModal.message": "Пожалуйста, подождите", "import.progressModal.description": "Загрузка прервется, если вы закроете вкладку браузера или выключите компьютер", @@ -61,6 +62,24 @@ "import.ticket.genitive": "Заявок", "import.ticket.prepositional": "Заявках", "import.ticket.requiredFields": "Укажите адрес и опишите проблему", + "import.helpModal.title": "Мы обязательно поможем", + "import.helpModal.chooseVariant": "Выберите наиболее удобный вариант:", + "import.helpModal.readInstruction.title": "Изучить инструкцию", + "import.helpModal.readInstruction.body": "Мы подготовили подробный документ с примерами", + "import.helpModal.importFile.card.title": "Загрузить данные в другом формате", + "import.helpModal.importFile.card.body": "Объемные данные в нестандартном формате можно отправить менеджеру – он добавит их на платформу.", + "import.helpModal.callback": "Заказать звонок", + "import.callbackModal.title": "Проверьте номер телефона", + "import.callbackModal.submitButtonLabel": "Номер верный — звоните", + "import.callbackModal.body": "Менеджер позвонит в течение 40 минут и поможет разобраться", + "import.callbackModal.phoneLabel": "Номер телефона", + "import.fileImportHelpModal.title": "Загрузить в другом формате", + "import.fileImportHelpModal.submitButtonLabel": "Загрузить", + "import.fileImportHelpModal.body": "Информацию можно загрузить в другом формате: Excel-таблицы с другими полями, фото документов, выгрузки из CRM и т. д.", + "import.fileImportHelpModal.alert.message": "Загрузка займет от 1 до 3 рабочих дней", + "import.fileImportHelpModal.alert.description": "Менеджер будет добавлять информацию вручную. Мы позвоним и сообщим, когда все будет готово.", + "import.fileImportHelpModal.phoneCheck": "Проверьте ваш номер телефона, чтобы мы могли оперативно с вами связаться, если у нас возникнут вопросы по файлу.", + "import.fileImportHelpModal.phoneLabel": "Номер телефона", "Create": "Создать", "Created": "Создано", "CreatedDate": "Дата создания", diff --git a/apps/condo/migrations/20240405181752-0379_userhelprequest_userhelprequestfile_userhelprequestfilehistoryrecord_userhelprequesthistoryrecord.js b/apps/condo/migrations/20240405181752-0379_userhelprequest_userhelprequestfile_userhelprequestfilehistoryrecord_userhelprequesthistoryrecord.js new file mode 100644 index 00000000000..b1f8a18a452 --- /dev/null +++ b/apps/condo/migrations/20240405181752-0379_userhelprequest_userhelprequestfile_userhelprequestfilehistoryrecord_userhelprequesthistoryrecord.js @@ -0,0 +1,81 @@ +// auto generated by kmigrator +// KMIGRATOR:0379_userhelprequest_userhelprequestfile_userhelprequestfilehistoryrecord_userhelprequesthistoryrecord:IyBHZW5lcmF0ZWQgYnkgRGphbmdvIDMuMi41IG9uIDIwMjQtMDQtMDUgMTM6MTcKCmZyb20gZGphbmdvLmRiIGltcG9ydCBtaWdyYXRpb25zLCBtb2RlbHMKaW1wb3J0IGRqYW5nby5kYi5tb2RlbHMuZGVsZXRpb24KCgpjbGFzcyBNaWdyYXRpb24obWlncmF0aW9ucy5NaWdyYXRpb24pOgoKICAgIGRlcGVuZGVuY2llcyA9IFsKICAgICAgICAoJ19kamFuZ29fc2NoZW1hJywgJzAzNzhfcmVtb3ZlX2Jhbmtjb250cmFjdG9yYWNjb3VudGhpc3RvcnlyZWNvcmRfcmVsYXRlZHRyYW5zYWN0aW9uc19hbmRfbW9yZScpLAogICAgXQoKICAgIG9wZXJhdGlvbnMgPSBbCiAgICAgICAgbWlncmF0aW9ucy5DcmVhdGVNb2RlbCgKICAgICAgICAgICAgbmFtZT0ndXNlcmhlbHByZXF1ZXN0JywKICAgICAgICAgICAgZmllbGRzPVsKICAgICAgICAgICAgICAgICgndHlwZScsIG1vZGVscy5UZXh0RmllbGQoKSksCiAgICAgICAgICAgICAgICAoJ3Bob25lJywgbW9kZWxzLlRleHRGaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnaXNSZWFkeVRvU2VuZCcsIG1vZGVscy5Cb29sZWFuRmllbGQoKSksCiAgICAgICAgICAgICAgICAoJ21ldGEnLCBtb2RlbHMuSlNPTkZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdpZCcsIG1vZGVscy5VVUlERmllbGQocHJpbWFyeV9rZXk9VHJ1ZSwgc2VyaWFsaXplPUZhbHNlKSksCiAgICAgICAgICAgICAgICAoJ3YnLCBtb2RlbHMuSW50ZWdlckZpZWxkKGRlZmF1bHQ9MSkpLAogICAgICAgICAgICAgICAgKCdjcmVhdGVkQXQnLCBtb2RlbHMuRGF0ZVRpbWVGaWVsZChibGFuaz1UcnVlLCBkYl9pbmRleD1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgndXBkYXRlZEF0JywgbW9kZWxzLkRhdGVUaW1lRmllbGQoYmxhbms9VHJ1ZSwgZGJfaW5kZXg9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2RlbGV0ZWRBdCcsIG1vZGVscy5EYXRlVGltZUZpZWxkKGJsYW5rPVRydWUsIGRiX2luZGV4PVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCduZXdJZCcsIG1vZGVscy5VVUlERmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2R2JywgbW9kZWxzLkludGVnZXJGaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnc2VuZGVyJywgbW9kZWxzLkpTT05GaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnY3JlYXRlZEJ5JywgbW9kZWxzLkZvcmVpZ25LZXkoYmxhbms9VHJ1ZSwgZGJfY29sdW1uPSdjcmVhdGVkQnknLCBudWxsPVRydWUsIG9uX2RlbGV0ZT1kamFuZ28uZGIubW9kZWxzLmRlbGV0aW9uLlNFVF9OVUxMLCByZWxhdGVkX25hbWU9JysnLCB0bz0nX2RqYW5nb19zY2hlbWEudXNlcicpKSwKICAgICAgICAgICAgICAgICgnb3JnYW5pemF0aW9uJywgbW9kZWxzLkZvcmVpZ25LZXkoZGJfY29sdW1uPSdvcmdhbml6YXRpb24nLCBvbl9kZWxldGU9ZGphbmdvLmRiLm1vZGVscy5kZWxldGlvbi5DQVNDQURFLCByZWxhdGVkX25hbWU9JysnLCB0bz0nX2RqYW5nb19zY2hlbWEub3JnYW5pemF0aW9uJykpLAogICAgICAgICAgICAgICAgKCd1cGRhdGVkQnknLCBtb2RlbHMuRm9yZWlnbktleShibGFuaz1UcnVlLCBkYl9jb2x1bW49J3VwZGF0ZWRCeScsIG51bGw9VHJ1ZSwgb25fZGVsZXRlPWRqYW5nby5kYi5tb2RlbHMuZGVsZXRpb24uU0VUX05VTEwsIHJlbGF0ZWRfbmFtZT0nKycsIHRvPSdfZGphbmdvX3NjaGVtYS51c2VyJykpLAogICAgICAgICAgICBdLAogICAgICAgICAgICBvcHRpb25zPXsKICAgICAgICAgICAgICAgICdkYl90YWJsZSc6ICdVc2VySGVscFJlcXVlc3QnLAogICAgICAgICAgICB9LAogICAgICAgICksCiAgICAgICAgbWlncmF0aW9ucy5DcmVhdGVNb2RlbCgKICAgICAgICAgICAgbmFtZT0ndXNlcmhlbHByZXF1ZXN0ZmlsZWhpc3RvcnlyZWNvcmQnLAogICAgICAgICAgICBmaWVsZHM9WwogICAgICAgICAgICAgICAgKCd1c2VySGVscFJlcXVlc3QnLCBtb2RlbHMuVVVJREZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdmaWxlJywgbW9kZWxzLkpTT05GaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnaWQnLCBtb2RlbHMuVVVJREZpZWxkKHByaW1hcnlfa2V5PVRydWUsIHNlcmlhbGl6ZT1GYWxzZSkpLAogICAgICAgICAgICAgICAgKCd2JywgbW9kZWxzLkludGVnZXJGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnY3JlYXRlZEF0JywgbW9kZWxzLkRhdGVUaW1lRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ3VwZGF0ZWRBdCcsIG1vZGVscy5EYXRlVGltZUZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdjcmVhdGVkQnknLCBtb2RlbHMuVVVJREZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCd1cGRhdGVkQnknLCBtb2RlbHMuVVVJREZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdkZWxldGVkQXQnLCBtb2RlbHMuRGF0ZVRpbWVGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnbmV3SWQnLCBtb2RlbHMuSlNPTkZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdkdicsIG1vZGVscy5JbnRlZ2VyRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ3NlbmRlcicsIG1vZGVscy5KU09ORmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2hpc3RvcnlfZGF0ZScsIG1vZGVscy5EYXRlVGltZUZpZWxkKCkpLAogICAgICAgICAgICAgICAgKCdoaXN0b3J5X2FjdGlvbicsIG1vZGVscy5DaGFyRmllbGQoY2hvaWNlcz1bKCdjJywgJ2MnKSwgKCd1JywgJ3UnKSwgKCdkJywgJ2QnKV0sIG1heF9sZW5ndGg9NTApKSwKICAgICAgICAgICAgICAgICgnaGlzdG9yeV9pZCcsIG1vZGVscy5VVUlERmllbGQoZGJfaW5kZXg9VHJ1ZSkpLAogICAgICAgICAgICBdLAogICAgICAgICAgICBvcHRpb25zPXsKICAgICAgICAgICAgICAgICdkYl90YWJsZSc6ICdVc2VySGVscFJlcXVlc3RGaWxlSGlzdG9yeVJlY29yZCcsCiAgICAgICAgICAgIH0sCiAgICAgICAgKSwKICAgICAgICBtaWdyYXRpb25zLkNyZWF0ZU1vZGVsKAogICAgICAgICAgICBuYW1lPSd1c2VyaGVscHJlcXVlc3RoaXN0b3J5cmVjb3JkJywKICAgICAgICAgICAgZmllbGRzPVsKICAgICAgICAgICAgICAgICgndHlwZScsIG1vZGVscy5UZXh0RmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ29yZ2FuaXphdGlvbicsIG1vZGVscy5VVUlERmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ3Bob25lJywgbW9kZWxzLlRleHRGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnaXNSZWFkeVRvU2VuZCcsIG1vZGVscy5Cb29sZWFuRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ21ldGEnLCBtb2RlbHMuSlNPTkZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdpZCcsIG1vZGVscy5VVUlERmllbGQocHJpbWFyeV9rZXk9VHJ1ZSwgc2VyaWFsaXplPUZhbHNlKSksCiAgICAgICAgICAgICAgICAoJ3YnLCBtb2RlbHMuSW50ZWdlckZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdjcmVhdGVkQXQnLCBtb2RlbHMuRGF0ZVRpbWVGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgndXBkYXRlZEF0JywgbW9kZWxzLkRhdGVUaW1lRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2NyZWF0ZWRCeScsIG1vZGVscy5VVUlERmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ3VwZGF0ZWRCeScsIG1vZGVscy5VVUlERmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2RlbGV0ZWRBdCcsIG1vZGVscy5EYXRlVGltZUZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCduZXdJZCcsIG1vZGVscy5KU09ORmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2R2JywgbW9kZWxzLkludGVnZXJGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnc2VuZGVyJywgbW9kZWxzLkpTT05GaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnaGlzdG9yeV9kYXRlJywgbW9kZWxzLkRhdGVUaW1lRmllbGQoKSksCiAgICAgICAgICAgICAgICAoJ2hpc3RvcnlfYWN0aW9uJywgbW9kZWxzLkNoYXJGaWVsZChjaG9pY2VzPVsoJ2MnLCAnYycpLCAoJ3UnLCAndScpLCAoJ2QnLCAnZCcpXSwgbWF4X2xlbmd0aD01MCkpLAogICAgICAgICAgICAgICAgKCdoaXN0b3J5X2lkJywgbW9kZWxzLlVVSURGaWVsZChkYl9pbmRleD1UcnVlKSksCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgIG9wdGlvbnM9ewogICAgICAgICAgICAgICAgJ2RiX3RhYmxlJzogJ1VzZXJIZWxwUmVxdWVzdEhpc3RvcnlSZWNvcmQnLAogICAgICAgICAgICB9LAogICAgICAgICksCiAgICAgICAgbWlncmF0aW9ucy5DcmVhdGVNb2RlbCgKICAgICAgICAgICAgbmFtZT0ndXNlcmhlbHByZXF1ZXN0ZmlsZScsCiAgICAgICAgICAgIGZpZWxkcz1bCiAgICAgICAgICAgICAgICAoJ2ZpbGUnLCBtb2RlbHMuSlNPTkZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdpZCcsIG1vZGVscy5VVUlERmllbGQocHJpbWFyeV9rZXk9VHJ1ZSwgc2VyaWFsaXplPUZhbHNlKSksCiAgICAgICAgICAgICAgICAoJ3YnLCBtb2RlbHMuSW50ZWdlckZpZWxkKGRlZmF1bHQ9MSkpLAogICAgICAgICAgICAgICAgKCdjcmVhdGVkQXQnLCBtb2RlbHMuRGF0ZVRpbWVGaWVsZChibGFuaz1UcnVlLCBkYl9pbmRleD1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgndXBkYXRlZEF0JywgbW9kZWxzLkRhdGVUaW1lRmllbGQoYmxhbms9VHJ1ZSwgZGJfaW5kZXg9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2RlbGV0ZWRBdCcsIG1vZGVscy5EYXRlVGltZUZpZWxkKGJsYW5rPVRydWUsIGRiX2luZGV4PVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCduZXdJZCcsIG1vZGVscy5VVUlERmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2R2JywgbW9kZWxzLkludGVnZXJGaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnc2VuZGVyJywgbW9kZWxzLkpTT05GaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnY3JlYXRlZEJ5JywgbW9kZWxzLkZvcmVpZ25LZXkoYmxhbms9VHJ1ZSwgZGJfY29sdW1uPSdjcmVhdGVkQnknLCBudWxsPVRydWUsIG9uX2RlbGV0ZT1kamFuZ28uZGIubW9kZWxzLmRlbGV0aW9uLlNFVF9OVUxMLCByZWxhdGVkX25hbWU9JysnLCB0bz0nX2RqYW5nb19zY2hlbWEudXNlcicpKSwKICAgICAgICAgICAgICAgICgndXBkYXRlZEJ5JywgbW9kZWxzLkZvcmVpZ25LZXkoYmxhbms9VHJ1ZSwgZGJfY29sdW1uPSd1cGRhdGVkQnknLCBudWxsPVRydWUsIG9uX2RlbGV0ZT1kamFuZ28uZGIubW9kZWxzLmRlbGV0aW9uLlNFVF9OVUxMLCByZWxhdGVkX25hbWU9JysnLCB0bz0nX2RqYW5nb19zY2hlbWEudXNlcicpKSwKICAgICAgICAgICAgICAgICgndXNlckhlbHBSZXF1ZXN0JywgbW9kZWxzLkZvcmVpZ25LZXkoYmxhbms9VHJ1ZSwgZGJfY29sdW1uPSd1c2VySGVscFJlcXVlc3QnLCBudWxsPVRydWUsIG9uX2RlbGV0ZT1kamFuZ28uZGIubW9kZWxzLmRlbGV0aW9uLkNBU0NBREUsIHJlbGF0ZWRfbmFtZT0nKycsIHRvPSdfZGphbmdvX3NjaGVtYS51c2VyaGVscHJlcXVlc3QnKSksCiAgICAgICAgICAgIF0sCiAgICAgICAgICAgIG9wdGlvbnM9ewogICAgICAgICAgICAgICAgJ2RiX3RhYmxlJzogJ1VzZXJIZWxwUmVxdWVzdEZpbGUnLAogICAgICAgICAgICB9LAogICAgICAgICksCiAgICBdCg== + +exports.up = async (knex) => { + await knex.raw(` + BEGIN; +-- +-- [CUSTOM] Set Statement Timeout to some large amount - 25 min (25 * 60 => 1500 sec) +-- +SET statement_timeout = '1500s'; + +-- +-- Create model userhelprequest +-- +CREATE TABLE "UserHelpRequest" ("type" text NOT NULL, "phone" text NOT NULL, "isReadyToSend" boolean NOT NULL, "meta" jsonb NULL, "id" uuid NOT NULL PRIMARY KEY, "v" integer NOT NULL, "createdAt" timestamp with time zone NULL, "updatedAt" timestamp with time zone NULL, "deletedAt" timestamp with time zone NULL, "newId" uuid NULL, "dv" integer NOT NULL, "sender" jsonb NOT NULL, "createdBy" uuid NULL, "organization" uuid NOT NULL, "updatedBy" uuid NULL); +-- +-- Create model userhelprequestfilehistoryrecord +-- +CREATE TABLE "UserHelpRequestFileHistoryRecord" ("userHelpRequest" uuid NULL, "file" jsonb NULL, "id" uuid NOT NULL PRIMARY KEY, "v" integer NULL, "createdAt" timestamp with time zone NULL, "updatedAt" timestamp with time zone NULL, "createdBy" uuid NULL, "updatedBy" uuid NULL, "deletedAt" timestamp with time zone NULL, "newId" jsonb NULL, "dv" integer NULL, "sender" jsonb NULL, "history_date" timestamp with time zone NOT NULL, "history_action" varchar(50) NOT NULL, "history_id" uuid NOT NULL); +-- +-- Create model userhelprequesthistoryrecord +-- +CREATE TABLE "UserHelpRequestHistoryRecord" ("type" text NULL, "organization" uuid NULL, "phone" text NULL, "isReadyToSend" boolean NULL, "meta" jsonb NULL, "id" uuid NOT NULL PRIMARY KEY, "v" integer NULL, "createdAt" timestamp with time zone NULL, "updatedAt" timestamp with time zone NULL, "createdBy" uuid NULL, "updatedBy" uuid NULL, "deletedAt" timestamp with time zone NULL, "newId" jsonb NULL, "dv" integer NULL, "sender" jsonb NULL, "history_date" timestamp with time zone NOT NULL, "history_action" varchar(50) NOT NULL, "history_id" uuid NOT NULL); +-- +-- Create model userhelprequestfile +-- +CREATE TABLE "UserHelpRequestFile" ("file" jsonb NULL, "id" uuid NOT NULL PRIMARY KEY, "v" integer NOT NULL, "createdAt" timestamp with time zone NULL, "updatedAt" timestamp with time zone NULL, "deletedAt" timestamp with time zone NULL, "newId" uuid NULL, "dv" integer NOT NULL, "sender" jsonb NOT NULL, "createdBy" uuid NULL, "updatedBy" uuid NULL, "userHelpRequest" uuid NULL); +ALTER TABLE "UserHelpRequest" ADD CONSTRAINT "UserHelpRequest_createdBy_9f8c84f4_fk_User_id" FOREIGN KEY ("createdBy") REFERENCES "User" ("id") DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE "UserHelpRequest" ADD CONSTRAINT "UserHelpRequest_organization_b896a882_fk_Organization_id" FOREIGN KEY ("organization") REFERENCES "Organization" ("id") DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE "UserHelpRequest" ADD CONSTRAINT "UserHelpRequest_updatedBy_65008d5e_fk_User_id" FOREIGN KEY ("updatedBy") REFERENCES "User" ("id") DEFERRABLE INITIALLY DEFERRED; +CREATE INDEX "UserHelpRequest_createdAt_1ac87b57" ON "UserHelpRequest" ("createdAt"); +CREATE INDEX "UserHelpRequest_updatedAt_21bff763" ON "UserHelpRequest" ("updatedAt"); +CREATE INDEX "UserHelpRequest_deletedAt_7b48c7c4" ON "UserHelpRequest" ("deletedAt"); +CREATE INDEX "UserHelpRequest_createdBy_9f8c84f4" ON "UserHelpRequest" ("createdBy"); +CREATE INDEX "UserHelpRequest_organization_b896a882" ON "UserHelpRequest" ("organization"); +CREATE INDEX "UserHelpRequest_updatedBy_65008d5e" ON "UserHelpRequest" ("updatedBy"); +CREATE INDEX "UserHelpRequestFileHistoryRecord_history_id_35d43f14" ON "UserHelpRequestFileHistoryRecord" ("history_id"); +CREATE INDEX "UserHelpRequestHistoryRecord_history_id_6d4dc92a" ON "UserHelpRequestHistoryRecord" ("history_id"); +ALTER TABLE "UserHelpRequestFile" ADD CONSTRAINT "UserHelpRequestFile_createdBy_a8b1d090_fk_User_id" FOREIGN KEY ("createdBy") REFERENCES "User" ("id") DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE "UserHelpRequestFile" ADD CONSTRAINT "UserHelpRequestFile_updatedBy_618548f6_fk_User_id" FOREIGN KEY ("updatedBy") REFERENCES "User" ("id") DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE "UserHelpRequestFile" ADD CONSTRAINT "UserHelpRequestFile_userHelpRequest_f4ab33aa_fk_UserHelpR" FOREIGN KEY ("userHelpRequest") REFERENCES "UserHelpRequest" ("id") DEFERRABLE INITIALLY DEFERRED; +CREATE INDEX "UserHelpRequestFile_createdAt_57efb223" ON "UserHelpRequestFile" ("createdAt"); +CREATE INDEX "UserHelpRequestFile_updatedAt_61b645ae" ON "UserHelpRequestFile" ("updatedAt"); +CREATE INDEX "UserHelpRequestFile_deletedAt_ea09d53e" ON "UserHelpRequestFile" ("deletedAt"); +CREATE INDEX "UserHelpRequestFile_createdBy_a8b1d090" ON "UserHelpRequestFile" ("createdBy"); +CREATE INDEX "UserHelpRequestFile_updatedBy_618548f6" ON "UserHelpRequestFile" ("updatedBy"); +CREATE INDEX "UserHelpRequestFile_userHelpRequest_f4ab33aa" ON "UserHelpRequestFile" ("userHelpRequest"); + +-- +-- [CUSTOM] Revert Statement Timeout to default amount - 10 secs +-- +SET statement_timeout = '10s'; + +COMMIT; + + `) +} + +exports.down = async (knex) => { + await knex.raw(` + BEGIN; +-- +-- Create model userhelprequestfile +-- +DROP TABLE "UserHelpRequestFile" CASCADE; +-- +-- Create model userhelprequesthistoryrecord +-- +DROP TABLE "UserHelpRequestHistoryRecord" CASCADE; +-- +-- Create model userhelprequestfilehistoryrecord +-- +DROP TABLE "UserHelpRequestFileHistoryRecord" CASCADE; +-- +-- Create model userhelprequest +-- +DROP TABLE "UserHelpRequest" CASCADE; +COMMIT; + + `) +} diff --git a/apps/condo/next.config.js b/apps/condo/next.config.js index d9eb3697139..1165bf8744e 100644 --- a/apps/condo/next.config.js +++ b/apps/condo/next.config.js @@ -57,6 +57,7 @@ const residentAppLandingUrl = JSON.parse(conf['RESIDENT_APP_LANDING_URL'] || '{} const createMapVideoUrl = JSON.parse(conf['CREATE_MAP_VIDEO_URL'] || '{}') const guideModalCardLink = JSON.parse(conf['GUIDE_MODAL_CARD_LINK'] || '{}') const guideIntroduceAppMaterials = JSON.parse(conf['GUIDE_INTRODUCE_APP_MATERIALS'] || '{}') +const importInstructionUrl = JSON.parse(conf['IMPORT_INSTRUCTION_URL'] || '{}') let nextConfig = withTM(withLess(withCSS({ publicRuntimeConfig: { @@ -94,6 +95,7 @@ let nextConfig = withTM(withLess(withCSS({ createMapVideoUrl, guideModalCardLink, guideIntroduceAppMaterials, + importInstructionUrl, }, lessLoaderOptions: { javascriptEnabled: true, diff --git a/apps/condo/schema.graphql b/apps/condo/schema.graphql index 65db60f048a..6c7771e26d8 100644 --- a/apps/condo/schema.graphql +++ b/apps/condo/schema.graphql @@ -45887,6 +45887,823 @@ input TourStepsCreateInput { data: TourStepCreateInput } +enum UserHelpRequestHistoryRecordHistoryActionType { + c + u + d +} + +""" A keystone list """ +type UserHelpRequestHistoryRecord { + """ + This virtual field will be resolved in one of the following ways (in this order): + 1. Execution of 'labelResolver' set on the UserHelpRequestHistoryRecord List config, or + 2. As an alias to the field set on 'labelField' in the UserHelpRequestHistoryRecord List config, or + 3. As an alias to a 'name' field on the UserHelpRequestHistoryRecord List (if one exists), or + 4. As an alias to the 'id' field on the UserHelpRequestHistoryRecord List. + """ + _label_: String + type: String + organization: String + phone: String + isReadyToSend: Boolean + meta: JSON + id: ID! + v: Int + createdAt: String + updatedAt: String + createdBy: String + updatedBy: String + deletedAt: String + newId: JSON + dv: Int + sender: JSON + history_date: String + history_action: UserHelpRequestHistoryRecordHistoryActionType + history_id: String +} + +input UserHelpRequestHistoryRecordWhereInput { + AND: [UserHelpRequestHistoryRecordWhereInput] + OR: [UserHelpRequestHistoryRecordWhereInput] + type: String + type_not: String + type_contains: String + type_not_contains: String + type_starts_with: String + type_not_starts_with: String + type_ends_with: String + type_not_ends_with: String + type_i: String + type_not_i: String + type_contains_i: String + type_not_contains_i: String + type_starts_with_i: String + type_not_starts_with_i: String + type_ends_with_i: String + type_not_ends_with_i: String + type_in: [String] + type_not_in: [String] + organization: String + organization_not: String + organization_in: [String] + organization_not_in: [String] + phone: String + phone_not: String + phone_contains: String + phone_not_contains: String + phone_starts_with: String + phone_not_starts_with: String + phone_ends_with: String + phone_not_ends_with: String + phone_i: String + phone_not_i: String + phone_contains_i: String + phone_not_contains_i: String + phone_starts_with_i: String + phone_not_starts_with_i: String + phone_ends_with_i: String + phone_not_ends_with_i: String + phone_in: [String] + phone_not_in: [String] + isReadyToSend: Boolean + isReadyToSend_not: Boolean + meta: JSON + meta_not: JSON + meta_in: [JSON] + meta_not_in: [JSON] + id: ID + id_not: ID + id_in: [ID] + id_not_in: [ID] + v: Int + v_not: Int + v_lt: Int + v_lte: Int + v_gt: Int + v_gte: Int + v_in: [Int] + v_not_in: [Int] + createdAt: String + createdAt_not: String + createdAt_lt: String + createdAt_lte: String + createdAt_gt: String + createdAt_gte: String + createdAt_in: [String] + createdAt_not_in: [String] + updatedAt: String + updatedAt_not: String + updatedAt_lt: String + updatedAt_lte: String + updatedAt_gt: String + updatedAt_gte: String + updatedAt_in: [String] + updatedAt_not_in: [String] + createdBy: String + createdBy_not: String + createdBy_in: [String] + createdBy_not_in: [String] + updatedBy: String + updatedBy_not: String + updatedBy_in: [String] + updatedBy_not_in: [String] + deletedAt: String + deletedAt_not: String + deletedAt_lt: String + deletedAt_lte: String + deletedAt_gt: String + deletedAt_gte: String + deletedAt_in: [String] + deletedAt_not_in: [String] + newId: JSON + newId_not: JSON + newId_in: [JSON] + newId_not_in: [JSON] + dv: Int + dv_not: Int + dv_lt: Int + dv_lte: Int + dv_gt: Int + dv_gte: Int + dv_in: [Int] + dv_not_in: [Int] + sender: JSON + sender_not: JSON + sender_in: [JSON] + sender_not_in: [JSON] + history_date: String + history_date_not: String + history_date_lt: String + history_date_lte: String + history_date_gt: String + history_date_gte: String + history_date_in: [String] + history_date_not_in: [String] + history_action: UserHelpRequestHistoryRecordHistoryActionType + history_action_not: UserHelpRequestHistoryRecordHistoryActionType + history_action_in: [UserHelpRequestHistoryRecordHistoryActionType] + history_action_not_in: [UserHelpRequestHistoryRecordHistoryActionType] + history_id: String + history_id_not: String + history_id_in: [String] + history_id_not_in: [String] +} + +input UserHelpRequestHistoryRecordWhereUniqueInput { + id: ID! +} + +enum SortUserHelpRequestHistoryRecordsBy { + type_ASC + type_DESC + phone_ASC + phone_DESC + isReadyToSend_ASC + isReadyToSend_DESC + id_ASC + id_DESC + v_ASC + v_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC + deletedAt_ASC + deletedAt_DESC + dv_ASC + dv_DESC + history_date_ASC + history_date_DESC + history_action_ASC + history_action_DESC +} + +input UserHelpRequestHistoryRecordUpdateInput { + type: String + organization: String + phone: String + isReadyToSend: Boolean + meta: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: String + updatedBy: String + deletedAt: String + newId: JSON + dv: Int + sender: JSON + history_date: String + history_action: UserHelpRequestHistoryRecordHistoryActionType + history_id: String +} + +input UserHelpRequestHistoryRecordsUpdateInput { + id: ID! + data: UserHelpRequestHistoryRecordUpdateInput +} + +input UserHelpRequestHistoryRecordCreateInput { + type: String + organization: String + phone: String + isReadyToSend: Boolean + meta: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: String + updatedBy: String + deletedAt: String + newId: JSON + dv: Int + sender: JSON + history_date: String + history_action: UserHelpRequestHistoryRecordHistoryActionType + history_id: String +} + +input UserHelpRequestHistoryRecordsCreateInput { + data: UserHelpRequestHistoryRecordCreateInput +} + +""" Request from the user to help him with some functionality """ +type UserHelpRequest { + """ + This virtual field will be resolved in one of the following ways (in this order): + 1. Execution of 'labelResolver' set on the UserHelpRequest List config, or + 2. As an alias to the field set on 'labelField' in the UserHelpRequest List config, or + 3. As an alias to a 'name' field on the UserHelpRequest List (if one exists), or + 4. As an alias to the 'id' field on the UserHelpRequest List. + """ + _label_: String + + """ Type of request. It's can be, for example, request for callback or request to help with import + """ + type: String + + """ Ref to the organization. The object will be deleted if the organization ceases to exist + """ + organization: Organization + + """ Specified phone in request for callback """ + phone: String + + """ Shows if the request is ready to send. False value can be, for example, if files are not synced with help request yet + """ + isReadyToSend: Boolean + + """ Additional info about request. May contain information about file urls, page where user made request or import type + """ + meta: JSON + id: ID! + v: Int + createdAt: String + updatedAt: String + + """ Identifies a user, which has created this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. + """ + createdBy: User + + """ Identifies a user, which has updated this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. + """ + updatedBy: User + deletedAt: String + newId: String + + """ Data structure Version """ + dv: Int + + """ Client-side device identification used for the anti-fraud detection. Example `{ "dv":1, "fingerprint":"VaxSw2aXZa"}`. Where the `fingerprint` should be the same for the same devices and it's not linked to the user ID. It's the device ID like browser / mobile application / remote system + """ + sender: SenderField +} + +input UserHelpRequestWhereInput { + AND: [UserHelpRequestWhereInput] + OR: [UserHelpRequestWhereInput] + type: String + type_not: String + type_in: [String] + type_not_in: [String] + organization: OrganizationWhereInput + organization_is_null: Boolean + phone: String + phone_not: String + phone_contains: String + phone_not_contains: String + phone_starts_with: String + phone_not_starts_with: String + phone_ends_with: String + phone_not_ends_with: String + phone_i: String + phone_not_i: String + phone_contains_i: String + phone_not_contains_i: String + phone_starts_with_i: String + phone_not_starts_with_i: String + phone_ends_with_i: String + phone_not_ends_with_i: String + phone_in: [String] + phone_not_in: [String] + isReadyToSend: Boolean + isReadyToSend_not: Boolean + meta: JSON + meta_not: JSON + meta_in: [JSON] + meta_not_in: [JSON] + id: ID + id_not: ID + id_in: [ID] + id_not_in: [ID] + v: Int + v_not: Int + v_lt: Int + v_lte: Int + v_gt: Int + v_gte: Int + v_in: [Int] + v_not_in: [Int] + createdAt: String + createdAt_not: String + createdAt_lt: String + createdAt_lte: String + createdAt_gt: String + createdAt_gte: String + createdAt_in: [String] + createdAt_not_in: [String] + updatedAt: String + updatedAt_not: String + updatedAt_lt: String + updatedAt_lte: String + updatedAt_gt: String + updatedAt_gte: String + updatedAt_in: [String] + updatedAt_not_in: [String] + createdBy: UserWhereInput + createdBy_is_null: Boolean + updatedBy: UserWhereInput + updatedBy_is_null: Boolean + deletedAt: String + deletedAt_not: String + deletedAt_lt: String + deletedAt_lte: String + deletedAt_gt: String + deletedAt_gte: String + deletedAt_in: [String] + deletedAt_not_in: [String] + newId: String + newId_not: String + newId_in: [String] + newId_not_in: [String] + dv: Int + dv_not: Int + dv_lt: Int + dv_lte: Int + dv_gt: Int + dv_gte: Int + dv_in: [Int] + dv_not_in: [Int] + sender: SenderFieldInput + sender_not: SenderFieldInput + sender_in: [SenderFieldInput] + sender_not_in: [SenderFieldInput] +} + +input UserHelpRequestWhereUniqueInput { + id: ID! +} + +enum SortUserHelpRequestsBy { + type_ASC + type_DESC + organization_ASC + organization_DESC + phone_ASC + phone_DESC + isReadyToSend_ASC + isReadyToSend_DESC + id_ASC + id_DESC + v_ASC + v_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC + createdBy_ASC + createdBy_DESC + updatedBy_ASC + updatedBy_DESC + deletedAt_ASC + deletedAt_DESC + dv_ASC + dv_DESC +} + +input UserHelpRequestUpdateInput { + type: String + organization: OrganizationRelateToOneInput + phone: String + isReadyToSend: Boolean + meta: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: UserRelateToOneInput + updatedBy: UserRelateToOneInput + deletedAt: String + newId: String + dv: Int + sender: SenderFieldInput +} + +input UserHelpRequestsUpdateInput { + id: ID! + data: UserHelpRequestUpdateInput +} + +input UserHelpRequestCreateInput { + type: String + organization: OrganizationRelateToOneInput + phone: String + isReadyToSend: Boolean + meta: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: UserRelateToOneInput + updatedBy: UserRelateToOneInput + deletedAt: String + newId: String + dv: Int + sender: SenderFieldInput +} + +input UserHelpRequestsCreateInput { + data: UserHelpRequestCreateInput +} + +enum UserHelpRequestFileHistoryRecordHistoryActionType { + c + u + d +} + +""" A keystone list """ +type UserHelpRequestFileHistoryRecord { + """ + This virtual field will be resolved in one of the following ways (in this order): + 1. Execution of 'labelResolver' set on the UserHelpRequestFileHistoryRecord List config, or + 2. As an alias to the field set on 'labelField' in the UserHelpRequestFileHistoryRecord List config, or + 3. As an alias to a 'name' field on the UserHelpRequestFileHistoryRecord List (if one exists), or + 4. As an alias to the 'id' field on the UserHelpRequestFileHistoryRecord List. + """ + _label_: String + userHelpRequest: String + file: JSON + id: ID! + v: Int + createdAt: String + updatedAt: String + createdBy: String + updatedBy: String + deletedAt: String + newId: JSON + dv: Int + sender: JSON + history_date: String + history_action: UserHelpRequestFileHistoryRecordHistoryActionType + history_id: String +} + +input UserHelpRequestFileHistoryRecordWhereInput { + AND: [UserHelpRequestFileHistoryRecordWhereInput] + OR: [UserHelpRequestFileHistoryRecordWhereInput] + userHelpRequest: String + userHelpRequest_not: String + userHelpRequest_in: [String] + userHelpRequest_not_in: [String] + file: JSON + file_not: JSON + file_in: [JSON] + file_not_in: [JSON] + id: ID + id_not: ID + id_in: [ID] + id_not_in: [ID] + v: Int + v_not: Int + v_lt: Int + v_lte: Int + v_gt: Int + v_gte: Int + v_in: [Int] + v_not_in: [Int] + createdAt: String + createdAt_not: String + createdAt_lt: String + createdAt_lte: String + createdAt_gt: String + createdAt_gte: String + createdAt_in: [String] + createdAt_not_in: [String] + updatedAt: String + updatedAt_not: String + updatedAt_lt: String + updatedAt_lte: String + updatedAt_gt: String + updatedAt_gte: String + updatedAt_in: [String] + updatedAt_not_in: [String] + createdBy: String + createdBy_not: String + createdBy_in: [String] + createdBy_not_in: [String] + updatedBy: String + updatedBy_not: String + updatedBy_in: [String] + updatedBy_not_in: [String] + deletedAt: String + deletedAt_not: String + deletedAt_lt: String + deletedAt_lte: String + deletedAt_gt: String + deletedAt_gte: String + deletedAt_in: [String] + deletedAt_not_in: [String] + newId: JSON + newId_not: JSON + newId_in: [JSON] + newId_not_in: [JSON] + dv: Int + dv_not: Int + dv_lt: Int + dv_lte: Int + dv_gt: Int + dv_gte: Int + dv_in: [Int] + dv_not_in: [Int] + sender: JSON + sender_not: JSON + sender_in: [JSON] + sender_not_in: [JSON] + history_date: String + history_date_not: String + history_date_lt: String + history_date_lte: String + history_date_gt: String + history_date_gte: String + history_date_in: [String] + history_date_not_in: [String] + history_action: UserHelpRequestFileHistoryRecordHistoryActionType + history_action_not: UserHelpRequestFileHistoryRecordHistoryActionType + history_action_in: [UserHelpRequestFileHistoryRecordHistoryActionType] + history_action_not_in: [UserHelpRequestFileHistoryRecordHistoryActionType] + history_id: String + history_id_not: String + history_id_in: [String] + history_id_not_in: [String] +} + +input UserHelpRequestFileHistoryRecordWhereUniqueInput { + id: ID! +} + +enum SortUserHelpRequestFileHistoryRecordsBy { + id_ASC + id_DESC + v_ASC + v_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC + deletedAt_ASC + deletedAt_DESC + dv_ASC + dv_DESC + history_date_ASC + history_date_DESC + history_action_ASC + history_action_DESC +} + +input UserHelpRequestFileHistoryRecordUpdateInput { + userHelpRequest: String + file: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: String + updatedBy: String + deletedAt: String + newId: JSON + dv: Int + sender: JSON + history_date: String + history_action: UserHelpRequestFileHistoryRecordHistoryActionType + history_id: String +} + +input UserHelpRequestFileHistoryRecordsUpdateInput { + id: ID! + data: UserHelpRequestFileHistoryRecordUpdateInput +} + +input UserHelpRequestFileHistoryRecordCreateInput { + userHelpRequest: String + file: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: String + updatedBy: String + deletedAt: String + newId: JSON + dv: Int + sender: JSON + history_date: String + history_action: UserHelpRequestFileHistoryRecordHistoryActionType + history_id: String +} + +input UserHelpRequestFileHistoryRecordsCreateInput { + data: UserHelpRequestFileHistoryRecordCreateInput +} + +input UserHelpRequestRelateToOneInput { + create: UserHelpRequestCreateInput + connect: UserHelpRequestWhereUniqueInput + disconnect: UserHelpRequestWhereUniqueInput + disconnectAll: Boolean +} + +""" File related to user help request """ +type UserHelpRequestFile { + """ + This virtual field will be resolved in one of the following ways (in this order): + 1. Execution of 'labelResolver' set on the UserHelpRequestFile List config, or + 2. As an alias to the field set on 'labelField' in the UserHelpRequestFile List config, or + 3. As an alias to a 'name' field on the UserHelpRequestFile List (if one exists), or + 4. As an alias to the 'id' field on the UserHelpRequestFile List. + """ + _label_: String + userHelpRequest: UserHelpRequest + file: File + id: ID! + v: Int + createdAt: String + updatedAt: String + + """ Identifies a user, which has created this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. + """ + createdBy: User + + """ Identifies a user, which has updated this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. + """ + updatedBy: User + deletedAt: String + newId: String + + """ Data structure Version """ + dv: Int + + """ Client-side device identification used for the anti-fraud detection. Example `{ "dv":1, "fingerprint":"VaxSw2aXZa"}`. Where the `fingerprint` should be the same for the same devices and it's not linked to the user ID. It's the device ID like browser / mobile application / remote system + """ + sender: SenderField +} + +input UserHelpRequestFileWhereInput { + AND: [UserHelpRequestFileWhereInput] + OR: [UserHelpRequestFileWhereInput] + userHelpRequest: UserHelpRequestWhereInput + userHelpRequest_is_null: Boolean + file: String + file_not: String + file_in: [String] + file_not_in: [String] + id: ID + id_not: ID + id_in: [ID] + id_not_in: [ID] + v: Int + v_not: Int + v_lt: Int + v_lte: Int + v_gt: Int + v_gte: Int + v_in: [Int] + v_not_in: [Int] + createdAt: String + createdAt_not: String + createdAt_lt: String + createdAt_lte: String + createdAt_gt: String + createdAt_gte: String + createdAt_in: [String] + createdAt_not_in: [String] + updatedAt: String + updatedAt_not: String + updatedAt_lt: String + updatedAt_lte: String + updatedAt_gt: String + updatedAt_gte: String + updatedAt_in: [String] + updatedAt_not_in: [String] + createdBy: UserWhereInput + createdBy_is_null: Boolean + updatedBy: UserWhereInput + updatedBy_is_null: Boolean + deletedAt: String + deletedAt_not: String + deletedAt_lt: String + deletedAt_lte: String + deletedAt_gt: String + deletedAt_gte: String + deletedAt_in: [String] + deletedAt_not_in: [String] + newId: String + newId_not: String + newId_in: [String] + newId_not_in: [String] + dv: Int + dv_not: Int + dv_lt: Int + dv_lte: Int + dv_gt: Int + dv_gte: Int + dv_in: [Int] + dv_not_in: [Int] + sender: SenderFieldInput + sender_not: SenderFieldInput + sender_in: [SenderFieldInput] + sender_not_in: [SenderFieldInput] +} + +input UserHelpRequestFileWhereUniqueInput { + id: ID! +} + +enum SortUserHelpRequestFilesBy { + userHelpRequest_ASC + userHelpRequest_DESC + id_ASC + id_DESC + v_ASC + v_DESC + createdAt_ASC + createdAt_DESC + updatedAt_ASC + updatedAt_DESC + createdBy_ASC + createdBy_DESC + updatedBy_ASC + updatedBy_DESC + deletedAt_ASC + deletedAt_DESC + dv_ASC + dv_DESC +} + +input UserHelpRequestFileUpdateInput { + userHelpRequest: UserHelpRequestRelateToOneInput + file: Upload + v: Int + createdAt: String + updatedAt: String + createdBy: UserRelateToOneInput + updatedBy: UserRelateToOneInput + deletedAt: String + newId: String + dv: Int + sender: SenderFieldInput +} + +input UserHelpRequestFilesUpdateInput { + id: ID! + data: UserHelpRequestFileUpdateInput +} + +input UserHelpRequestFileCreateInput { + userHelpRequest: UserHelpRequestRelateToOneInput + file: Upload + v: Int + createdAt: String + updatedAt: String + createdBy: UserRelateToOneInput + updatedBy: UserRelateToOneInput + deletedAt: String + newId: String + dv: Int + sender: SenderFieldInput +} + +input UserHelpRequestFilesCreateInput { + data: UserHelpRequestFileCreateInput +} + enum MeterResourceHistoryRecordHistoryActionType { c u @@ -76323,6 +77140,64 @@ type Query { """ Retrieve the meta-data for the TourStep list. """ _TourStepsMeta: _ListMeta + """ Search for all UserHelpRequestHistoryRecord items which match the where clause. + """ + allUserHelpRequestHistoryRecords(where: UserHelpRequestHistoryRecordWhereInput, search: String, sortBy: [SortUserHelpRequestHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): [UserHelpRequestHistoryRecord] + + """ Search for the UserHelpRequestHistoryRecord item with the matching ID. + """ + UserHelpRequestHistoryRecord(where: UserHelpRequestHistoryRecordWhereUniqueInput!): UserHelpRequestHistoryRecord + + """ Perform a meta-query on all UserHelpRequestHistoryRecord items which match the where clause. + """ + _allUserHelpRequestHistoryRecordsMeta(where: UserHelpRequestHistoryRecordWhereInput, search: String, sortBy: [SortUserHelpRequestHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): _QueryMeta + + """ Retrieve the meta-data for the UserHelpRequestHistoryRecord list. """ + _UserHelpRequestHistoryRecordsMeta: _ListMeta + + """ Search for all UserHelpRequest items which match the where clause. """ + allUserHelpRequests(where: UserHelpRequestWhereInput, search: String, sortBy: [SortUserHelpRequestsBy!], orderBy: String, first: Int, skip: Int): [UserHelpRequest] + + """ Search for the UserHelpRequest item with the matching ID. """ + UserHelpRequest(where: UserHelpRequestWhereUniqueInput!): UserHelpRequest + + """ Perform a meta-query on all UserHelpRequest items which match the where clause. + """ + _allUserHelpRequestsMeta(where: UserHelpRequestWhereInput, search: String, sortBy: [SortUserHelpRequestsBy!], orderBy: String, first: Int, skip: Int): _QueryMeta + + """ Retrieve the meta-data for the UserHelpRequest list. """ + _UserHelpRequestsMeta: _ListMeta + + """ Search for all UserHelpRequestFileHistoryRecord items which match the where clause. + """ + allUserHelpRequestFileHistoryRecords(where: UserHelpRequestFileHistoryRecordWhereInput, search: String, sortBy: [SortUserHelpRequestFileHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): [UserHelpRequestFileHistoryRecord] + + """ Search for the UserHelpRequestFileHistoryRecord item with the matching ID. + """ + UserHelpRequestFileHistoryRecord(where: UserHelpRequestFileHistoryRecordWhereUniqueInput!): UserHelpRequestFileHistoryRecord + + """ Perform a meta-query on all UserHelpRequestFileHistoryRecord items which match the where clause. + """ + _allUserHelpRequestFileHistoryRecordsMeta(where: UserHelpRequestFileHistoryRecordWhereInput, search: String, sortBy: [SortUserHelpRequestFileHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): _QueryMeta + + """ Retrieve the meta-data for the UserHelpRequestFileHistoryRecord list. + """ + _UserHelpRequestFileHistoryRecordsMeta: _ListMeta + + """ Search for all UserHelpRequestFile items which match the where clause. + """ + allUserHelpRequestFiles(where: UserHelpRequestFileWhereInput, search: String, sortBy: [SortUserHelpRequestFilesBy!], orderBy: String, first: Int, skip: Int): [UserHelpRequestFile] + + """ Search for the UserHelpRequestFile item with the matching ID. """ + UserHelpRequestFile(where: UserHelpRequestFileWhereUniqueInput!): UserHelpRequestFile + + """ Perform a meta-query on all UserHelpRequestFile items which match the where clause. + """ + _allUserHelpRequestFilesMeta(where: UserHelpRequestFileWhereInput, search: String, sortBy: [SortUserHelpRequestFilesBy!], orderBy: String, first: Int, skip: Int): _QueryMeta + + """ Retrieve the meta-data for the UserHelpRequestFile list. """ + _UserHelpRequestFilesMeta: _ListMeta + """ Search for all MeterResourceHistoryRecord items which match the where clause. """ allMeterResourceHistoryRecords(where: MeterResourceHistoryRecordWhereInput, search: String, sortBy: [SortMeterResourceHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): [MeterResourceHistoryRecord] @@ -80740,6 +81615,78 @@ type Mutation { """ Delete multiple TourStep items by ID. """ deleteTourSteps(ids: [ID!]): [TourStep] + """ Create a single UserHelpRequestHistoryRecord item. """ + createUserHelpRequestHistoryRecord(data: UserHelpRequestHistoryRecordCreateInput): UserHelpRequestHistoryRecord + + """ Create multiple UserHelpRequestHistoryRecord items. """ + createUserHelpRequestHistoryRecords(data: [UserHelpRequestHistoryRecordsCreateInput]): [UserHelpRequestHistoryRecord] + + """ Update a single UserHelpRequestHistoryRecord item by ID. """ + updateUserHelpRequestHistoryRecord(id: ID!, data: UserHelpRequestHistoryRecordUpdateInput): UserHelpRequestHistoryRecord + + """ Update multiple UserHelpRequestHistoryRecord items by ID. """ + updateUserHelpRequestHistoryRecords(data: [UserHelpRequestHistoryRecordsUpdateInput]): [UserHelpRequestHistoryRecord] + + """ Delete a single UserHelpRequestHistoryRecord item by ID. """ + deleteUserHelpRequestHistoryRecord(id: ID!): UserHelpRequestHistoryRecord + + """ Delete multiple UserHelpRequestHistoryRecord items by ID. """ + deleteUserHelpRequestHistoryRecords(ids: [ID!]): [UserHelpRequestHistoryRecord] + + """ Create a single UserHelpRequest item. """ + createUserHelpRequest(data: UserHelpRequestCreateInput): UserHelpRequest + + """ Create multiple UserHelpRequest items. """ + createUserHelpRequests(data: [UserHelpRequestsCreateInput]): [UserHelpRequest] + + """ Update a single UserHelpRequest item by ID. """ + updateUserHelpRequest(id: ID!, data: UserHelpRequestUpdateInput): UserHelpRequest + + """ Update multiple UserHelpRequest items by ID. """ + updateUserHelpRequests(data: [UserHelpRequestsUpdateInput]): [UserHelpRequest] + + """ Delete a single UserHelpRequest item by ID. """ + deleteUserHelpRequest(id: ID!): UserHelpRequest + + """ Delete multiple UserHelpRequest items by ID. """ + deleteUserHelpRequests(ids: [ID!]): [UserHelpRequest] + + """ Create a single UserHelpRequestFileHistoryRecord item. """ + createUserHelpRequestFileHistoryRecord(data: UserHelpRequestFileHistoryRecordCreateInput): UserHelpRequestFileHistoryRecord + + """ Create multiple UserHelpRequestFileHistoryRecord items. """ + createUserHelpRequestFileHistoryRecords(data: [UserHelpRequestFileHistoryRecordsCreateInput]): [UserHelpRequestFileHistoryRecord] + + """ Update a single UserHelpRequestFileHistoryRecord item by ID. """ + updateUserHelpRequestFileHistoryRecord(id: ID!, data: UserHelpRequestFileHistoryRecordUpdateInput): UserHelpRequestFileHistoryRecord + + """ Update multiple UserHelpRequestFileHistoryRecord items by ID. """ + updateUserHelpRequestFileHistoryRecords(data: [UserHelpRequestFileHistoryRecordsUpdateInput]): [UserHelpRequestFileHistoryRecord] + + """ Delete a single UserHelpRequestFileHistoryRecord item by ID. """ + deleteUserHelpRequestFileHistoryRecord(id: ID!): UserHelpRequestFileHistoryRecord + + """ Delete multiple UserHelpRequestFileHistoryRecord items by ID. """ + deleteUserHelpRequestFileHistoryRecords(ids: [ID!]): [UserHelpRequestFileHistoryRecord] + + """ Create a single UserHelpRequestFile item. """ + createUserHelpRequestFile(data: UserHelpRequestFileCreateInput): UserHelpRequestFile + + """ Create multiple UserHelpRequestFile items. """ + createUserHelpRequestFiles(data: [UserHelpRequestFilesCreateInput]): [UserHelpRequestFile] + + """ Update a single UserHelpRequestFile item by ID. """ + updateUserHelpRequestFile(id: ID!, data: UserHelpRequestFileUpdateInput): UserHelpRequestFile + + """ Update multiple UserHelpRequestFile items by ID. """ + updateUserHelpRequestFiles(data: [UserHelpRequestFilesUpdateInput]): [UserHelpRequestFile] + + """ Delete a single UserHelpRequestFile item by ID. """ + deleteUserHelpRequestFile(id: ID!): UserHelpRequestFile + + """ Delete multiple UserHelpRequestFile items by ID. """ + deleteUserHelpRequestFiles(ids: [ID!]): [UserHelpRequestFile] + """ Create a single MeterResourceHistoryRecord item. """ createMeterResourceHistoryRecord(data: MeterResourceHistoryRecordCreateInput): MeterResourceHistoryRecord diff --git a/apps/condo/schema.ts b/apps/condo/schema.ts index 98c124ec6ac..fe7ab63d678 100644 --- a/apps/condo/schema.ts +++ b/apps/condo/schema.ts @@ -36843,6 +36843,54 @@ export type Mutation = { deleteTourStep?: Maybe; /** Delete multiple TourStep items by ID. */ deleteTourSteps?: Maybe>>; + /** Create a single UserHelpRequestHistoryRecord item. */ + createUserHelpRequestHistoryRecord?: Maybe; + /** Create multiple UserHelpRequestHistoryRecord items. */ + createUserHelpRequestHistoryRecords?: Maybe>>; + /** Update a single UserHelpRequestHistoryRecord item by ID. */ + updateUserHelpRequestHistoryRecord?: Maybe; + /** Update multiple UserHelpRequestHistoryRecord items by ID. */ + updateUserHelpRequestHistoryRecords?: Maybe>>; + /** Delete a single UserHelpRequestHistoryRecord item by ID. */ + deleteUserHelpRequestHistoryRecord?: Maybe; + /** Delete multiple UserHelpRequestHistoryRecord items by ID. */ + deleteUserHelpRequestHistoryRecords?: Maybe>>; + /** Create a single UserHelpRequest item. */ + createUserHelpRequest?: Maybe; + /** Create multiple UserHelpRequest items. */ + createUserHelpRequests?: Maybe>>; + /** Update a single UserHelpRequest item by ID. */ + updateUserHelpRequest?: Maybe; + /** Update multiple UserHelpRequest items by ID. */ + updateUserHelpRequests?: Maybe>>; + /** Delete a single UserHelpRequest item by ID. */ + deleteUserHelpRequest?: Maybe; + /** Delete multiple UserHelpRequest items by ID. */ + deleteUserHelpRequests?: Maybe>>; + /** Create a single UserHelpRequestFileHistoryRecord item. */ + createUserHelpRequestFileHistoryRecord?: Maybe; + /** Create multiple UserHelpRequestFileHistoryRecord items. */ + createUserHelpRequestFileHistoryRecords?: Maybe>>; + /** Update a single UserHelpRequestFileHistoryRecord item by ID. */ + updateUserHelpRequestFileHistoryRecord?: Maybe; + /** Update multiple UserHelpRequestFileHistoryRecord items by ID. */ + updateUserHelpRequestFileHistoryRecords?: Maybe>>; + /** Delete a single UserHelpRequestFileHistoryRecord item by ID. */ + deleteUserHelpRequestFileHistoryRecord?: Maybe; + /** Delete multiple UserHelpRequestFileHistoryRecord items by ID. */ + deleteUserHelpRequestFileHistoryRecords?: Maybe>>; + /** Create a single UserHelpRequestFile item. */ + createUserHelpRequestFile?: Maybe; + /** Create multiple UserHelpRequestFile items. */ + createUserHelpRequestFiles?: Maybe>>; + /** Update a single UserHelpRequestFile item by ID. */ + updateUserHelpRequestFile?: Maybe; + /** Update multiple UserHelpRequestFile items by ID. */ + updateUserHelpRequestFiles?: Maybe>>; + /** Delete a single UserHelpRequestFile item by ID. */ + deleteUserHelpRequestFile?: Maybe; + /** Delete multiple UserHelpRequestFile items by ID. */ + deleteUserHelpRequestFiles?: Maybe>>; /** Create a single MeterResourceHistoryRecord item. */ createMeterResourceHistoryRecord?: Maybe; /** Create multiple MeterResourceHistoryRecord items. */ @@ -45703,6 +45751,130 @@ export type MutationDeleteTourStepsArgs = { }; +export type MutationCreateUserHelpRequestHistoryRecordArgs = { + data?: Maybe; +}; + + +export type MutationCreateUserHelpRequestHistoryRecordsArgs = { + data?: Maybe>>; +}; + + +export type MutationUpdateUserHelpRequestHistoryRecordArgs = { + id: Scalars['ID']; + data?: Maybe; +}; + + +export type MutationUpdateUserHelpRequestHistoryRecordsArgs = { + data?: Maybe>>; +}; + + +export type MutationDeleteUserHelpRequestHistoryRecordArgs = { + id: Scalars['ID']; +}; + + +export type MutationDeleteUserHelpRequestHistoryRecordsArgs = { + ids?: Maybe>; +}; + + +export type MutationCreateUserHelpRequestArgs = { + data?: Maybe; +}; + + +export type MutationCreateUserHelpRequestsArgs = { + data?: Maybe>>; +}; + + +export type MutationUpdateUserHelpRequestArgs = { + id: Scalars['ID']; + data?: Maybe; +}; + + +export type MutationUpdateUserHelpRequestsArgs = { + data?: Maybe>>; +}; + + +export type MutationDeleteUserHelpRequestArgs = { + id: Scalars['ID']; +}; + + +export type MutationDeleteUserHelpRequestsArgs = { + ids?: Maybe>; +}; + + +export type MutationCreateUserHelpRequestFileHistoryRecordArgs = { + data?: Maybe; +}; + + +export type MutationCreateUserHelpRequestFileHistoryRecordsArgs = { + data?: Maybe>>; +}; + + +export type MutationUpdateUserHelpRequestFileHistoryRecordArgs = { + id: Scalars['ID']; + data?: Maybe; +}; + + +export type MutationUpdateUserHelpRequestFileHistoryRecordsArgs = { + data?: Maybe>>; +}; + + +export type MutationDeleteUserHelpRequestFileHistoryRecordArgs = { + id: Scalars['ID']; +}; + + +export type MutationDeleteUserHelpRequestFileHistoryRecordsArgs = { + ids?: Maybe>; +}; + + +export type MutationCreateUserHelpRequestFileArgs = { + data?: Maybe; +}; + + +export type MutationCreateUserHelpRequestFilesArgs = { + data?: Maybe>>; +}; + + +export type MutationUpdateUserHelpRequestFileArgs = { + id: Scalars['ID']; + data?: Maybe; +}; + + +export type MutationUpdateUserHelpRequestFilesArgs = { + data?: Maybe>>; +}; + + +export type MutationDeleteUserHelpRequestFileArgs = { + id: Scalars['ID']; +}; + + +export type MutationDeleteUserHelpRequestFilesArgs = { + ids?: Maybe>; +}; + + export type MutationCreateMeterResourceHistoryRecordArgs = { data?: Maybe; }; @@ -62057,6 +62229,38 @@ export type Query = { _allTourStepsMeta?: Maybe<_QueryMeta>; /** Retrieve the meta-data for the TourStep list. */ _TourStepsMeta?: Maybe<_ListMeta>; + /** Search for all UserHelpRequestHistoryRecord items which match the where clause. */ + allUserHelpRequestHistoryRecords?: Maybe>>; + /** Search for the UserHelpRequestHistoryRecord item with the matching ID. */ + UserHelpRequestHistoryRecord?: Maybe; + /** Perform a meta-query on all UserHelpRequestHistoryRecord items which match the where clause. */ + _allUserHelpRequestHistoryRecordsMeta?: Maybe<_QueryMeta>; + /** Retrieve the meta-data for the UserHelpRequestHistoryRecord list. */ + _UserHelpRequestHistoryRecordsMeta?: Maybe<_ListMeta>; + /** Search for all UserHelpRequest items which match the where clause. */ + allUserHelpRequests?: Maybe>>; + /** Search for the UserHelpRequest item with the matching ID. */ + UserHelpRequest?: Maybe; + /** Perform a meta-query on all UserHelpRequest items which match the where clause. */ + _allUserHelpRequestsMeta?: Maybe<_QueryMeta>; + /** Retrieve the meta-data for the UserHelpRequest list. */ + _UserHelpRequestsMeta?: Maybe<_ListMeta>; + /** Search for all UserHelpRequestFileHistoryRecord items which match the where clause. */ + allUserHelpRequestFileHistoryRecords?: Maybe>>; + /** Search for the UserHelpRequestFileHistoryRecord item with the matching ID. */ + UserHelpRequestFileHistoryRecord?: Maybe; + /** Perform a meta-query on all UserHelpRequestFileHistoryRecord items which match the where clause. */ + _allUserHelpRequestFileHistoryRecordsMeta?: Maybe<_QueryMeta>; + /** Retrieve the meta-data for the UserHelpRequestFileHistoryRecord list. */ + _UserHelpRequestFileHistoryRecordsMeta?: Maybe<_ListMeta>; + /** Search for all UserHelpRequestFile items which match the where clause. */ + allUserHelpRequestFiles?: Maybe>>; + /** Search for the UserHelpRequestFile item with the matching ID. */ + UserHelpRequestFile?: Maybe; + /** Perform a meta-query on all UserHelpRequestFile items which match the where clause. */ + _allUserHelpRequestFilesMeta?: Maybe<_QueryMeta>; + /** Retrieve the meta-data for the UserHelpRequestFile list. */ + _UserHelpRequestFilesMeta?: Maybe<_ListMeta>; /** Search for all MeterResourceHistoryRecord items which match the where clause. */ allMeterResourceHistoryRecords?: Maybe>>; /** Search for the MeterResourceHistoryRecord item with the matching ID. */ @@ -66896,6 +67100,106 @@ export type Query_AllTourStepsMetaArgs = { }; +export type QueryAllUserHelpRequestHistoryRecordsArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryUserHelpRequestHistoryRecordArgs = { + where: UserHelpRequestHistoryRecordWhereUniqueInput; +}; + + +export type Query_AllUserHelpRequestHistoryRecordsMetaArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryAllUserHelpRequestsArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryUserHelpRequestArgs = { + where: UserHelpRequestWhereUniqueInput; +}; + + +export type Query_AllUserHelpRequestsMetaArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryAllUserHelpRequestFileHistoryRecordsArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryUserHelpRequestFileHistoryRecordArgs = { + where: UserHelpRequestFileHistoryRecordWhereUniqueInput; +}; + + +export type Query_AllUserHelpRequestFileHistoryRecordsMetaArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryAllUserHelpRequestFilesArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryUserHelpRequestFileArgs = { + where: UserHelpRequestFileWhereUniqueInput; +}; + + +export type Query_AllUserHelpRequestFilesMetaArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + export type QueryAllMeterResourceHistoryRecordsArgs = { where?: Maybe; search?: Maybe; @@ -80808,6 +81112,98 @@ export enum SortUserFavoriteTicketsBy { DvDesc = 'dv_DESC' } +export enum SortUserHelpRequestFileHistoryRecordsBy { + IdAsc = 'id_ASC', + IdDesc = 'id_DESC', + VAsc = 'v_ASC', + VDesc = 'v_DESC', + CreatedAtAsc = 'createdAt_ASC', + CreatedAtDesc = 'createdAt_DESC', + UpdatedAtAsc = 'updatedAt_ASC', + UpdatedAtDesc = 'updatedAt_DESC', + DeletedAtAsc = 'deletedAt_ASC', + DeletedAtDesc = 'deletedAt_DESC', + DvAsc = 'dv_ASC', + DvDesc = 'dv_DESC', + HistoryDateAsc = 'history_date_ASC', + HistoryDateDesc = 'history_date_DESC', + HistoryActionAsc = 'history_action_ASC', + HistoryActionDesc = 'history_action_DESC' +} + +export enum SortUserHelpRequestFilesBy { + UserHelpRequestAsc = 'userHelpRequest_ASC', + UserHelpRequestDesc = 'userHelpRequest_DESC', + IdAsc = 'id_ASC', + IdDesc = 'id_DESC', + VAsc = 'v_ASC', + VDesc = 'v_DESC', + CreatedAtAsc = 'createdAt_ASC', + CreatedAtDesc = 'createdAt_DESC', + UpdatedAtAsc = 'updatedAt_ASC', + UpdatedAtDesc = 'updatedAt_DESC', + CreatedByAsc = 'createdBy_ASC', + CreatedByDesc = 'createdBy_DESC', + UpdatedByAsc = 'updatedBy_ASC', + UpdatedByDesc = 'updatedBy_DESC', + DeletedAtAsc = 'deletedAt_ASC', + DeletedAtDesc = 'deletedAt_DESC', + DvAsc = 'dv_ASC', + DvDesc = 'dv_DESC' +} + +export enum SortUserHelpRequestHistoryRecordsBy { + TypeAsc = 'type_ASC', + TypeDesc = 'type_DESC', + PhoneAsc = 'phone_ASC', + PhoneDesc = 'phone_DESC', + IsReadyToSendAsc = 'isReadyToSend_ASC', + IsReadyToSendDesc = 'isReadyToSend_DESC', + IdAsc = 'id_ASC', + IdDesc = 'id_DESC', + VAsc = 'v_ASC', + VDesc = 'v_DESC', + CreatedAtAsc = 'createdAt_ASC', + CreatedAtDesc = 'createdAt_DESC', + UpdatedAtAsc = 'updatedAt_ASC', + UpdatedAtDesc = 'updatedAt_DESC', + DeletedAtAsc = 'deletedAt_ASC', + DeletedAtDesc = 'deletedAt_DESC', + DvAsc = 'dv_ASC', + DvDesc = 'dv_DESC', + HistoryDateAsc = 'history_date_ASC', + HistoryDateDesc = 'history_date_DESC', + HistoryActionAsc = 'history_action_ASC', + HistoryActionDesc = 'history_action_DESC' +} + +export enum SortUserHelpRequestsBy { + TypeAsc = 'type_ASC', + TypeDesc = 'type_DESC', + OrganizationAsc = 'organization_ASC', + OrganizationDesc = 'organization_DESC', + PhoneAsc = 'phone_ASC', + PhoneDesc = 'phone_DESC', + IsReadyToSendAsc = 'isReadyToSend_ASC', + IsReadyToSendDesc = 'isReadyToSend_DESC', + IdAsc = 'id_ASC', + IdDesc = 'id_DESC', + VAsc = 'v_ASC', + VDesc = 'v_DESC', + CreatedAtAsc = 'createdAt_ASC', + CreatedAtDesc = 'createdAt_DESC', + UpdatedAtAsc = 'updatedAt_ASC', + UpdatedAtDesc = 'updatedAt_DESC', + CreatedByAsc = 'createdBy_ASC', + CreatedByDesc = 'createdBy_DESC', + UpdatedByAsc = 'updatedBy_ASC', + UpdatedByDesc = 'updatedBy_DESC', + DeletedAtAsc = 'deletedAt_ASC', + DeletedAtDesc = 'deletedAt_DESC', + DvAsc = 'dv_ASC', + DvDesc = 'dv_DESC' +} + export enum SortUserHistoryRecordsBy { NameAsc = 'name_ASC', NameDesc = 'name_DESC', @@ -92058,6 +92454,712 @@ export type UserFavoriteTicketsUpdateInput = { data?: Maybe; }; +/** Request from the user to help him with some functionality */ +export type UserHelpRequest = { + __typename?: 'UserHelpRequest'; + /** + * This virtual field will be resolved in one of the following ways (in this order): + * 1. Execution of 'labelResolver' set on the UserHelpRequest List config, or + * 2. As an alias to the field set on 'labelField' in the UserHelpRequest List config, or + * 3. As an alias to a 'name' field on the UserHelpRequest List (if one exists), or + * 4. As an alias to the 'id' field on the UserHelpRequest List. + */ + _label_?: Maybe; + /** Type of request. It's can be, for example, request for callback or request to help with import */ + type?: Maybe; + /** Ref to the organization. The object will be deleted if the organization ceases to exist */ + organization?: Maybe; + /** Specified phone in request for callback */ + phone?: Maybe; + /** Shows if the request is ready to send. False value can be, for example, if files are not synced with help request yet */ + isReadyToSend?: Maybe; + /** Additional info about request. May contain information about file urls, page where user made request or import type */ + meta?: Maybe; + id: Scalars['ID']; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + /** Identifies a user, which has created this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. */ + createdBy?: Maybe; + /** Identifies a user, which has updated this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. */ + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + /** Data structure Version */ + dv?: Maybe; + /** Client-side device identification used for the anti-fraud detection. Example `{ "dv":1, "fingerprint":"VaxSw2aXZa"}`. Where the `fingerprint` should be the same for the same devices and it's not linked to the user ID. It's the device ID like browser / mobile application / remote system */ + sender?: Maybe; +}; + +export type UserHelpRequestCreateInput = { + type?: Maybe; + organization?: Maybe; + phone?: Maybe; + isReadyToSend?: Maybe; + meta?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; +}; + +/** File related to user help request */ +export type UserHelpRequestFile = { + __typename?: 'UserHelpRequestFile'; + /** + * This virtual field will be resolved in one of the following ways (in this order): + * 1. Execution of 'labelResolver' set on the UserHelpRequestFile List config, or + * 2. As an alias to the field set on 'labelField' in the UserHelpRequestFile List config, or + * 3. As an alias to a 'name' field on the UserHelpRequestFile List (if one exists), or + * 4. As an alias to the 'id' field on the UserHelpRequestFile List. + */ + _label_?: Maybe; + userHelpRequest?: Maybe; + file?: Maybe; + id: Scalars['ID']; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + /** Identifies a user, which has created this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. */ + createdBy?: Maybe; + /** Identifies a user, which has updated this record. It is a technical connection, that can represent real users, as well as automated systems (bots, scripts). This field should not participate in business logic. */ + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + /** Data structure Version */ + dv?: Maybe; + /** Client-side device identification used for the anti-fraud detection. Example `{ "dv":1, "fingerprint":"VaxSw2aXZa"}`. Where the `fingerprint` should be the same for the same devices and it's not linked to the user ID. It's the device ID like browser / mobile application / remote system */ + sender?: Maybe; +}; + +export type UserHelpRequestFileCreateInput = { + userHelpRequest?: Maybe; + file?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; +}; + +/** A keystone list */ +export type UserHelpRequestFileHistoryRecord = { + __typename?: 'UserHelpRequestFileHistoryRecord'; + /** + * This virtual field will be resolved in one of the following ways (in this order): + * 1. Execution of 'labelResolver' set on the UserHelpRequestFileHistoryRecord List config, or + * 2. As an alias to the field set on 'labelField' in the UserHelpRequestFileHistoryRecord List config, or + * 3. As an alias to a 'name' field on the UserHelpRequestFileHistoryRecord List (if one exists), or + * 4. As an alias to the 'id' field on the UserHelpRequestFileHistoryRecord List. + */ + _label_?: Maybe; + userHelpRequest?: Maybe; + file?: Maybe; + id: Scalars['ID']; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; + history_date?: Maybe; + history_action?: Maybe; + history_id?: Maybe; +}; + +export type UserHelpRequestFileHistoryRecordCreateInput = { + userHelpRequest?: Maybe; + file?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; + history_date?: Maybe; + history_action?: Maybe; + history_id?: Maybe; +}; + +export enum UserHelpRequestFileHistoryRecordHistoryActionType { + C = 'c', + U = 'u', + D = 'd' +} + +export type UserHelpRequestFileHistoryRecordUpdateInput = { + userHelpRequest?: Maybe; + file?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; + history_date?: Maybe; + history_action?: Maybe; + history_id?: Maybe; +}; + +export type UserHelpRequestFileHistoryRecordWhereInput = { + AND?: Maybe>>; + OR?: Maybe>>; + userHelpRequest?: Maybe; + userHelpRequest_not?: Maybe; + userHelpRequest_in?: Maybe>>; + userHelpRequest_not_in?: Maybe>>; + file?: Maybe; + file_not?: Maybe; + file_in?: Maybe>>; + file_not_in?: Maybe>>; + id?: Maybe; + id_not?: Maybe; + id_in?: Maybe>>; + id_not_in?: Maybe>>; + v?: Maybe; + v_not?: Maybe; + v_lt?: Maybe; + v_lte?: Maybe; + v_gt?: Maybe; + v_gte?: Maybe; + v_in?: Maybe>>; + v_not_in?: Maybe>>; + createdAt?: Maybe; + createdAt_not?: Maybe; + createdAt_lt?: Maybe; + createdAt_lte?: Maybe; + createdAt_gt?: Maybe; + createdAt_gte?: Maybe; + createdAt_in?: Maybe>>; + createdAt_not_in?: Maybe>>; + updatedAt?: Maybe; + updatedAt_not?: Maybe; + updatedAt_lt?: Maybe; + updatedAt_lte?: Maybe; + updatedAt_gt?: Maybe; + updatedAt_gte?: Maybe; + updatedAt_in?: Maybe>>; + updatedAt_not_in?: Maybe>>; + createdBy?: Maybe; + createdBy_not?: Maybe; + createdBy_in?: Maybe>>; + createdBy_not_in?: Maybe>>; + updatedBy?: Maybe; + updatedBy_not?: Maybe; + updatedBy_in?: Maybe>>; + updatedBy_not_in?: Maybe>>; + deletedAt?: Maybe; + deletedAt_not?: Maybe; + deletedAt_lt?: Maybe; + deletedAt_lte?: Maybe; + deletedAt_gt?: Maybe; + deletedAt_gte?: Maybe; + deletedAt_in?: Maybe>>; + deletedAt_not_in?: Maybe>>; + newId?: Maybe; + newId_not?: Maybe; + newId_in?: Maybe>>; + newId_not_in?: Maybe>>; + dv?: Maybe; + dv_not?: Maybe; + dv_lt?: Maybe; + dv_lte?: Maybe; + dv_gt?: Maybe; + dv_gte?: Maybe; + dv_in?: Maybe>>; + dv_not_in?: Maybe>>; + sender?: Maybe; + sender_not?: Maybe; + sender_in?: Maybe>>; + sender_not_in?: Maybe>>; + history_date?: Maybe; + history_date_not?: Maybe; + history_date_lt?: Maybe; + history_date_lte?: Maybe; + history_date_gt?: Maybe; + history_date_gte?: Maybe; + history_date_in?: Maybe>>; + history_date_not_in?: Maybe>>; + history_action?: Maybe; + history_action_not?: Maybe; + history_action_in?: Maybe>>; + history_action_not_in?: Maybe>>; + history_id?: Maybe; + history_id_not?: Maybe; + history_id_in?: Maybe>>; + history_id_not_in?: Maybe>>; +}; + +export type UserHelpRequestFileHistoryRecordWhereUniqueInput = { + id: Scalars['ID']; +}; + +export type UserHelpRequestFileHistoryRecordsCreateInput = { + data?: Maybe; +}; + +export type UserHelpRequestFileHistoryRecordsUpdateInput = { + id: Scalars['ID']; + data?: Maybe; +}; + +export type UserHelpRequestFileUpdateInput = { + userHelpRequest?: Maybe; + file?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; +}; + +export type UserHelpRequestFileWhereInput = { + AND?: Maybe>>; + OR?: Maybe>>; + userHelpRequest?: Maybe; + userHelpRequest_is_null?: Maybe; + file?: Maybe; + file_not?: Maybe; + file_in?: Maybe>>; + file_not_in?: Maybe>>; + id?: Maybe; + id_not?: Maybe; + id_in?: Maybe>>; + id_not_in?: Maybe>>; + v?: Maybe; + v_not?: Maybe; + v_lt?: Maybe; + v_lte?: Maybe; + v_gt?: Maybe; + v_gte?: Maybe; + v_in?: Maybe>>; + v_not_in?: Maybe>>; + createdAt?: Maybe; + createdAt_not?: Maybe; + createdAt_lt?: Maybe; + createdAt_lte?: Maybe; + createdAt_gt?: Maybe; + createdAt_gte?: Maybe; + createdAt_in?: Maybe>>; + createdAt_not_in?: Maybe>>; + updatedAt?: Maybe; + updatedAt_not?: Maybe; + updatedAt_lt?: Maybe; + updatedAt_lte?: Maybe; + updatedAt_gt?: Maybe; + updatedAt_gte?: Maybe; + updatedAt_in?: Maybe>>; + updatedAt_not_in?: Maybe>>; + createdBy?: Maybe; + createdBy_is_null?: Maybe; + updatedBy?: Maybe; + updatedBy_is_null?: Maybe; + deletedAt?: Maybe; + deletedAt_not?: Maybe; + deletedAt_lt?: Maybe; + deletedAt_lte?: Maybe; + deletedAt_gt?: Maybe; + deletedAt_gte?: Maybe; + deletedAt_in?: Maybe>>; + deletedAt_not_in?: Maybe>>; + newId?: Maybe; + newId_not?: Maybe; + newId_in?: Maybe>>; + newId_not_in?: Maybe>>; + dv?: Maybe; + dv_not?: Maybe; + dv_lt?: Maybe; + dv_lte?: Maybe; + dv_gt?: Maybe; + dv_gte?: Maybe; + dv_in?: Maybe>>; + dv_not_in?: Maybe>>; + sender?: Maybe; + sender_not?: Maybe; + sender_in?: Maybe>>; + sender_not_in?: Maybe>>; +}; + +export type UserHelpRequestFileWhereUniqueInput = { + id: Scalars['ID']; +}; + +export type UserHelpRequestFilesCreateInput = { + data?: Maybe; +}; + +export type UserHelpRequestFilesUpdateInput = { + id: Scalars['ID']; + data?: Maybe; +}; + +/** A keystone list */ +export type UserHelpRequestHistoryRecord = { + __typename?: 'UserHelpRequestHistoryRecord'; + /** + * This virtual field will be resolved in one of the following ways (in this order): + * 1. Execution of 'labelResolver' set on the UserHelpRequestHistoryRecord List config, or + * 2. As an alias to the field set on 'labelField' in the UserHelpRequestHistoryRecord List config, or + * 3. As an alias to a 'name' field on the UserHelpRequestHistoryRecord List (if one exists), or + * 4. As an alias to the 'id' field on the UserHelpRequestHistoryRecord List. + */ + _label_?: Maybe; + type?: Maybe; + organization?: Maybe; + phone?: Maybe; + isReadyToSend?: Maybe; + meta?: Maybe; + id: Scalars['ID']; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; + history_date?: Maybe; + history_action?: Maybe; + history_id?: Maybe; +}; + +export type UserHelpRequestHistoryRecordCreateInput = { + type?: Maybe; + organization?: Maybe; + phone?: Maybe; + isReadyToSend?: Maybe; + meta?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; + history_date?: Maybe; + history_action?: Maybe; + history_id?: Maybe; +}; + +export enum UserHelpRequestHistoryRecordHistoryActionType { + C = 'c', + U = 'u', + D = 'd' +} + +export type UserHelpRequestHistoryRecordUpdateInput = { + type?: Maybe; + organization?: Maybe; + phone?: Maybe; + isReadyToSend?: Maybe; + meta?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; + history_date?: Maybe; + history_action?: Maybe; + history_id?: Maybe; +}; + +export type UserHelpRequestHistoryRecordWhereInput = { + AND?: Maybe>>; + OR?: Maybe>>; + type?: Maybe; + type_not?: Maybe; + type_contains?: Maybe; + type_not_contains?: Maybe; + type_starts_with?: Maybe; + type_not_starts_with?: Maybe; + type_ends_with?: Maybe; + type_not_ends_with?: Maybe; + type_i?: Maybe; + type_not_i?: Maybe; + type_contains_i?: Maybe; + type_not_contains_i?: Maybe; + type_starts_with_i?: Maybe; + type_not_starts_with_i?: Maybe; + type_ends_with_i?: Maybe; + type_not_ends_with_i?: Maybe; + type_in?: Maybe>>; + type_not_in?: Maybe>>; + organization?: Maybe; + organization_not?: Maybe; + organization_in?: Maybe>>; + organization_not_in?: Maybe>>; + phone?: Maybe; + phone_not?: Maybe; + phone_contains?: Maybe; + phone_not_contains?: Maybe; + phone_starts_with?: Maybe; + phone_not_starts_with?: Maybe; + phone_ends_with?: Maybe; + phone_not_ends_with?: Maybe; + phone_i?: Maybe; + phone_not_i?: Maybe; + phone_contains_i?: Maybe; + phone_not_contains_i?: Maybe; + phone_starts_with_i?: Maybe; + phone_not_starts_with_i?: Maybe; + phone_ends_with_i?: Maybe; + phone_not_ends_with_i?: Maybe; + phone_in?: Maybe>>; + phone_not_in?: Maybe>>; + isReadyToSend?: Maybe; + isReadyToSend_not?: Maybe; + meta?: Maybe; + meta_not?: Maybe; + meta_in?: Maybe>>; + meta_not_in?: Maybe>>; + id?: Maybe; + id_not?: Maybe; + id_in?: Maybe>>; + id_not_in?: Maybe>>; + v?: Maybe; + v_not?: Maybe; + v_lt?: Maybe; + v_lte?: Maybe; + v_gt?: Maybe; + v_gte?: Maybe; + v_in?: Maybe>>; + v_not_in?: Maybe>>; + createdAt?: Maybe; + createdAt_not?: Maybe; + createdAt_lt?: Maybe; + createdAt_lte?: Maybe; + createdAt_gt?: Maybe; + createdAt_gte?: Maybe; + createdAt_in?: Maybe>>; + createdAt_not_in?: Maybe>>; + updatedAt?: Maybe; + updatedAt_not?: Maybe; + updatedAt_lt?: Maybe; + updatedAt_lte?: Maybe; + updatedAt_gt?: Maybe; + updatedAt_gte?: Maybe; + updatedAt_in?: Maybe>>; + updatedAt_not_in?: Maybe>>; + createdBy?: Maybe; + createdBy_not?: Maybe; + createdBy_in?: Maybe>>; + createdBy_not_in?: Maybe>>; + updatedBy?: Maybe; + updatedBy_not?: Maybe; + updatedBy_in?: Maybe>>; + updatedBy_not_in?: Maybe>>; + deletedAt?: Maybe; + deletedAt_not?: Maybe; + deletedAt_lt?: Maybe; + deletedAt_lte?: Maybe; + deletedAt_gt?: Maybe; + deletedAt_gte?: Maybe; + deletedAt_in?: Maybe>>; + deletedAt_not_in?: Maybe>>; + newId?: Maybe; + newId_not?: Maybe; + newId_in?: Maybe>>; + newId_not_in?: Maybe>>; + dv?: Maybe; + dv_not?: Maybe; + dv_lt?: Maybe; + dv_lte?: Maybe; + dv_gt?: Maybe; + dv_gte?: Maybe; + dv_in?: Maybe>>; + dv_not_in?: Maybe>>; + sender?: Maybe; + sender_not?: Maybe; + sender_in?: Maybe>>; + sender_not_in?: Maybe>>; + history_date?: Maybe; + history_date_not?: Maybe; + history_date_lt?: Maybe; + history_date_lte?: Maybe; + history_date_gt?: Maybe; + history_date_gte?: Maybe; + history_date_in?: Maybe>>; + history_date_not_in?: Maybe>>; + history_action?: Maybe; + history_action_not?: Maybe; + history_action_in?: Maybe>>; + history_action_not_in?: Maybe>>; + history_id?: Maybe; + history_id_not?: Maybe; + history_id_in?: Maybe>>; + history_id_not_in?: Maybe>>; +}; + +export type UserHelpRequestHistoryRecordWhereUniqueInput = { + id: Scalars['ID']; +}; + +export type UserHelpRequestHistoryRecordsCreateInput = { + data?: Maybe; +}; + +export type UserHelpRequestHistoryRecordsUpdateInput = { + id: Scalars['ID']; + data?: Maybe; +}; + +export type UserHelpRequestRelateToOneInput = { + create?: Maybe; + connect?: Maybe; + disconnect?: Maybe; + disconnectAll?: Maybe; +}; + +export type UserHelpRequestUpdateInput = { + type?: Maybe; + organization?: Maybe; + phone?: Maybe; + isReadyToSend?: Maybe; + meta?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; +}; + +export type UserHelpRequestWhereInput = { + AND?: Maybe>>; + OR?: Maybe>>; + type?: Maybe; + type_not?: Maybe; + type_in?: Maybe>>; + type_not_in?: Maybe>>; + organization?: Maybe; + organization_is_null?: Maybe; + phone?: Maybe; + phone_not?: Maybe; + phone_contains?: Maybe; + phone_not_contains?: Maybe; + phone_starts_with?: Maybe; + phone_not_starts_with?: Maybe; + phone_ends_with?: Maybe; + phone_not_ends_with?: Maybe; + phone_i?: Maybe; + phone_not_i?: Maybe; + phone_contains_i?: Maybe; + phone_not_contains_i?: Maybe; + phone_starts_with_i?: Maybe; + phone_not_starts_with_i?: Maybe; + phone_ends_with_i?: Maybe; + phone_not_ends_with_i?: Maybe; + phone_in?: Maybe>>; + phone_not_in?: Maybe>>; + isReadyToSend?: Maybe; + isReadyToSend_not?: Maybe; + meta?: Maybe; + meta_not?: Maybe; + meta_in?: Maybe>>; + meta_not_in?: Maybe>>; + id?: Maybe; + id_not?: Maybe; + id_in?: Maybe>>; + id_not_in?: Maybe>>; + v?: Maybe; + v_not?: Maybe; + v_lt?: Maybe; + v_lte?: Maybe; + v_gt?: Maybe; + v_gte?: Maybe; + v_in?: Maybe>>; + v_not_in?: Maybe>>; + createdAt?: Maybe; + createdAt_not?: Maybe; + createdAt_lt?: Maybe; + createdAt_lte?: Maybe; + createdAt_gt?: Maybe; + createdAt_gte?: Maybe; + createdAt_in?: Maybe>>; + createdAt_not_in?: Maybe>>; + updatedAt?: Maybe; + updatedAt_not?: Maybe; + updatedAt_lt?: Maybe; + updatedAt_lte?: Maybe; + updatedAt_gt?: Maybe; + updatedAt_gte?: Maybe; + updatedAt_in?: Maybe>>; + updatedAt_not_in?: Maybe>>; + createdBy?: Maybe; + createdBy_is_null?: Maybe; + updatedBy?: Maybe; + updatedBy_is_null?: Maybe; + deletedAt?: Maybe; + deletedAt_not?: Maybe; + deletedAt_lt?: Maybe; + deletedAt_lte?: Maybe; + deletedAt_gt?: Maybe; + deletedAt_gte?: Maybe; + deletedAt_in?: Maybe>>; + deletedAt_not_in?: Maybe>>; + newId?: Maybe; + newId_not?: Maybe; + newId_in?: Maybe>>; + newId_not_in?: Maybe>>; + dv?: Maybe; + dv_not?: Maybe; + dv_lt?: Maybe; + dv_lte?: Maybe; + dv_gt?: Maybe; + dv_gte?: Maybe; + dv_in?: Maybe>>; + dv_not_in?: Maybe>>; + sender?: Maybe; + sender_not?: Maybe; + sender_in?: Maybe>>; + sender_not_in?: Maybe>>; +}; + +export type UserHelpRequestWhereUniqueInput = { + id: Scalars['ID']; +}; + +export type UserHelpRequestsCreateInput = { + data?: Maybe; +}; + +export type UserHelpRequestsUpdateInput = { + id: Scalars['ID']; + data?: Maybe; +}; + /** A keystone list */ export type UserHistoryRecord = { __typename?: 'UserHistoryRecord';