From c9eadcc6a44d5887f4cb345d2cfbc46c0d5441df Mon Sep 17 00:00:00 2001 From: Vasiliy Kislov <64303474+VKislov@users.noreply.github.com> Date: Sat, 12 Aug 2023 22:58:01 +0500 Subject: [PATCH] feat(condo): DOMA-6736 schema MobileFeatureConfig with tests (#3662) * feat(condo): DOMA-6736 schema MobileFeatureConfig with tests * fix(condo): DOMA-6736 change field name from ticketSubmittingIsEnabled to ticketSubmittingIsDisabled. Rm emergencyPhone field * fix(condo): DOMA-6736 fix imports * feat(condo): DOMA-6753 frontend of MobileFeatureConfig * fix(condo): DOMA-6736 schemaDoc of some field of MobileFeatureConfig * fix(condo): DOMA-6736 review fixes * feat(condo): DOMA-6736 added permission canManageMobileFeatureConfigs for manage access * fix(condo): DOMA-6736 rename onlyProgressionMeterReadingsIsEnabled to onlyGreaterThanPreviousMeterReadingIsEnabled * fix(condo): DOMA-6736 replace catchErrorFrom to expectToThrowGQLError * fix(condo): DOMA-6736 use valuePropName * fix(condo): DOMA-6736 add deletedAt filter * fix(condo): DOMA-6736 review fixes some optimization on front, rm redundant react state. add tests for support. move FF constants to common/constants/featureFlags. fix schemaDoc for meta * fix(condo): DOMA-6736 some code improvements * chore(condo): DOMA-6736 fix imports Co-authored-by: Alllex202 <56914444+Alllex202@users.noreply.github.com> -------- --- .../domains/common/components/PhoneInput.tsx | 1 - .../settings/MobileFeatureConfigContent.tsx | 55 ++ .../domains/common/constants/featureflags.js | 6 + .../domains/common/constants/settingsTabs.js | 2 + .../domains/organization/constants/common.js | 1 + apps/condo/domains/organization/gql.js | 2 +- .../schema/OrganizationEmployeeRole.js | 1 + .../schema/OrganizationEmployeeRole.test.js | 1 + .../settings/access/MobileFeatureConfig.js | 77 +++ .../OnlyProgressionMeterReadingsForm.tsx | 113 ++++ ...nlyProgressionMeterReadingsSettingCard.tsx | 38 ++ .../TicketSubmittingSettingCard.tsx | 44 ++ .../TicketSubmittingSettingsForm.tsx | 167 +++++ apps/condo/domains/settings/gql.js | 22 + .../settings/schema/MobileFeatureConfig.js | 99 +++ .../schema/MobileFeatureConfig.test.js | 269 ++++++++ apps/condo/domains/settings/schema/index.js | 12 + .../MobileFeatureConfigSchemaValidations.js | 19 + .../utils/clientSchema/MobileFeatureConfig.ts | 30 + .../settings/utils/clientSchema/index.ts | 7 + .../settings/utils/serverSchema/index.js | 18 + .../settings/utils/testSchema/index.js | 56 ++ apps/condo/index.js | 1 + apps/condo/lang/en/en.json | 16 + apps/condo/lang/ru/ru.json | 16 + ...bilefeatureconfighistoryrecord_and_more.js | 81 +++ apps/condo/pages/settings/index.tsx | 19 +- .../onlyProgressionMeterReadings/index.tsx | 61 ++ .../ticketSubmitting/index.tsx | 61 ++ apps/condo/schema.graphql | 520 +++++++++++++++ apps/condo/schema.ts | 592 ++++++++++++++++++ 31 files changed, 2402 insertions(+), 5 deletions(-) create mode 100644 apps/condo/domains/common/components/settings/MobileFeatureConfigContent.tsx create mode 100644 apps/condo/domains/settings/access/MobileFeatureConfig.js create mode 100644 apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsForm.tsx create mode 100644 apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsSettingCard.tsx create mode 100644 apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingCard.tsx create mode 100644 apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingsForm.tsx create mode 100644 apps/condo/domains/settings/gql.js create mode 100644 apps/condo/domains/settings/schema/MobileFeatureConfig.js create mode 100644 apps/condo/domains/settings/schema/MobileFeatureConfig.test.js create mode 100644 apps/condo/domains/settings/schema/index.js create mode 100644 apps/condo/domains/settings/utils/MobileFeatureConfigSchemaValidations.js create mode 100644 apps/condo/domains/settings/utils/clientSchema/MobileFeatureConfig.ts create mode 100644 apps/condo/domains/settings/utils/clientSchema/index.ts create mode 100644 apps/condo/domains/settings/utils/serverSchema/index.js create mode 100644 apps/condo/domains/settings/utils/testSchema/index.js create mode 100644 apps/condo/migrations/20230811140557-0307_mobilefeatureconfighistoryrecord_and_more.js create mode 100644 apps/condo/pages/settings/mobileFeatureConfig/onlyProgressionMeterReadings/index.tsx create mode 100644 apps/condo/pages/settings/mobileFeatureConfig/ticketSubmitting/index.tsx diff --git a/apps/condo/domains/common/components/PhoneInput.tsx b/apps/condo/domains/common/components/PhoneInput.tsx index aacb181712b..2e85e32e522 100644 --- a/apps/condo/domains/common/components/PhoneInput.tsx +++ b/apps/condo/domains/common/components/PhoneInput.tsx @@ -96,7 +96,6 @@ export const PhoneInput: React.FC = forwardRef((props, ref) => value: formattedValue, }, } - // @ts-ignore props.onChange(event) } else { props.onChange(formattedValue) diff --git a/apps/condo/domains/common/components/settings/MobileFeatureConfigContent.tsx b/apps/condo/domains/common/components/settings/MobileFeatureConfigContent.tsx new file mode 100644 index 00000000000..094d5f7f165 --- /dev/null +++ b/apps/condo/domains/common/components/settings/MobileFeatureConfigContent.tsx @@ -0,0 +1,55 @@ +import { Col, Row } from 'antd' +import { Gutter } from 'antd/es/grid/row' +import get from 'lodash/get' +import React from 'react' + +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' +import { useOrganization } from '@open-condo/next/organization' + +import { CardsContainer } from '@condo/domains/common/components/Card/CardsContainer' +import { SettingCardSkeleton } from '@condo/domains/common/components/settings/SettingCard' +import { MOBILE_FEATURE_CONFIGURATION, TICKET_SUBMITTING_FORM_RESIDENT_MOBILE_APP, SUBMIT_ONLY_PROGRESSION_METER_READINGS } from '@condo/domains/common/constants/featureflags' +import { + OnlyProgressionMeterReadingsSettingCard, +} from '@condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsSettingCard' +import { TicketSubmittingSettingCard } from '@condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingCard' +import { MobileFeatureConfig as MobileFeatureConfigAPI } from '@condo/domains/settings/utils/clientSchema' + +const CONTENT_GUTTER: Gutter | [Gutter, Gutter] = [0, 40] + +export const MobileFeatureConfigContent: React.FC = () => { + const userOrganization = useOrganization() + const userOrganizationId = get(userOrganization, ['organization', 'id'], null) + const { obj: mobileConfig, loading } = MobileFeatureConfigAPI.useObject({ + where: { + organization: { id: userOrganizationId }, + }, + }) + const { useFlag } = useFeatureFlags() + const hasMobileFeatureConfigurationFeature = useFlag(MOBILE_FEATURE_CONFIGURATION) + const hasTicketSubmittingSettingFeature = useFlag(TICKET_SUBMITTING_FORM_RESIDENT_MOBILE_APP) + const hasOnlyProgressionMeterReadingsSettingFeature = useFlag(SUBMIT_ONLY_PROGRESSION_METER_READINGS) + + if (!hasMobileFeatureConfigurationFeature) { + return null + } + + return ( + + + + { + !hasTicketSubmittingSettingFeature ? null : (loading + ? + : ) + } + { + !hasOnlyProgressionMeterReadingsSettingFeature ? null : (loading + ? + : ) + } + + + + ) +} diff --git a/apps/condo/domains/common/constants/featureflags.js b/apps/condo/domains/common/constants/featureflags.js index 86f876a3848..43516e95888 100644 --- a/apps/condo/domains/common/constants/featureflags.js +++ b/apps/condo/domains/common/constants/featureflags.js @@ -12,6 +12,9 @@ const PROPERTY_REPORT_DELETE_ENTITIES = 'property-report-delete-entities' const SERVICE_PROVIDER_PROFILE = 'service-provider-profile' const SHOW_ORGANIZATION_TYPES = 'show-organization-types' const ENABLE_DISCOVER_SERVICE_CONSUMERS = 'enable-discover-service-consumers' +const MOBILE_FEATURE_CONFIGURATION = 'mobile-feature-configuration' +const TICKET_SUBMITTING_FORM_RESIDENT_MOBILE_APP = 'ticket-submitting-from-resident-mobile-app' +const SUBMIT_ONLY_PROGRESSION_METER_READINGS = 'submit-only-progression-meter-readings' module.exports = { SMS_AFTER_TICKET_CREATION, @@ -28,4 +31,7 @@ module.exports = { SERVICE_PROVIDER_PROFILE, SHOW_ORGANIZATION_TYPES, ENABLE_DISCOVER_SERVICE_CONSUMERS, + MOBILE_FEATURE_CONFIGURATION, + TICKET_SUBMITTING_FORM_RESIDENT_MOBILE_APP, + SUBMIT_ONLY_PROGRESSION_METER_READINGS, } diff --git a/apps/condo/domains/common/constants/settingsTabs.js b/apps/condo/domains/common/constants/settingsTabs.js index 0010e4a4fd8..462f66cebe3 100644 --- a/apps/condo/domains/common/constants/settingsTabs.js +++ b/apps/condo/domains/common/constants/settingsTabs.js @@ -6,6 +6,7 @@ const SETTINGS_TAB_PAYMENT_DETAILS = 'paymentDetails' const SETTINGS_TAB_CONTACT_ROLES = 'contactRoles' const SETTINGS_TAB_TICKET_ORGANIZATION = 'ticketOrganization' const SETTINGS_TAB_CONTROL_ROOM = 'controlRoom' +const SETTINGS_TAB_MOBILE_FEATURE_CONFIG = 'mobileFeatureConfig' module.exports = { SETTINGS_TAB_SUBSCRIPTION, @@ -16,4 +17,5 @@ module.exports = { SETTINGS_TAB_TICKET_ORGANIZATION, SETTINGS_TAB_CONTROL_ROOM, SETTINGS_TAB_EMPLOYEE_ROLES, + SETTINGS_TAB_MOBILE_FEATURE_CONFIG, } diff --git a/apps/condo/domains/organization/constants/common.js b/apps/condo/domains/organization/constants/common.js index 2cf030353d4..7b0d4360c97 100644 --- a/apps/condo/domains/organization/constants/common.js +++ b/apps/condo/domains/organization/constants/common.js @@ -63,6 +63,7 @@ const DEFAULT_ROLES = { 'ticketVisibilityType': ORGANIZATION_TICKET_VISIBILITY, 'canManageNewsItems': true, 'canManageNewsItemTemplates': true, + 'canManageMobileFeatureConfigs': true, }, 'Dispatcher': { 'name': 'employee.role.Dispatcher.name', diff --git a/apps/condo/domains/organization/gql.js b/apps/condo/domains/organization/gql.js index f5b5117f525..63330ef9115 100644 --- a/apps/condo/domains/organization/gql.js +++ b/apps/condo/domains/organization/gql.js @@ -13,7 +13,7 @@ const COMMON_FIELDS = 'id dv sender { dv fingerprint } v deletedAt newId created const ORGANIZATION_FIELDS = `{ country name type description avatar { publicUrl } meta tin features statusTransitions defaultEmployeeRoleStatusTransitions importId importRemoteSystem phone phoneNumberPrefix ${COMMON_FIELDS} }` const Organization = generateGqlQueries('Organization', ORGANIZATION_FIELDS) -const ORGANIZATION_EMPLOYEE_ROLE_FIELDS = '{ organization { id } name nameNonLocalized description descriptionNonLocalized statusTransitions canManageOrganization canManageCallRecords canDownloadCallRecords canManageEmployees canInviteNewOrganizationEmployees canManageRoles canManageTicketPropertyHints canManageIntegrations canReadBillingReceipts canReadPayments canManageProperties canManageTickets canManageContacts canManageContactRoles canManageTicketComments canManagePropertyScopes canShareTickets canBeAssignedAsResponsible canBeAssignedAsExecutor canManageMeters canManageMeterReadings ticketVisibilityType canManageBankAccounts canManageBankContractorAccounts canManageBankIntegrationAccountContexts canManageBankIntegrationOrganizationContexts canManageBankTransactions canManageBankAccountReports canManageBankAccountReportTasks canManageBankAccountReports canManageIncidents canManageNewsItems canManageNewsItemTemplates id dv sender { dv fingerprint } v createdBy { id name } updatedBy { id name } createdAt updatedAt }' +const ORGANIZATION_EMPLOYEE_ROLE_FIELDS = '{ organization { id } name nameNonLocalized description descriptionNonLocalized statusTransitions canManageOrganization canManageCallRecords canDownloadCallRecords canManageEmployees canInviteNewOrganizationEmployees canManageRoles canManageTicketPropertyHints canManageIntegrations canReadBillingReceipts canReadPayments canManageProperties canManageTickets canManageContacts canManageContactRoles canManageTicketComments canManagePropertyScopes canShareTickets canBeAssignedAsResponsible canBeAssignedAsExecutor canManageMeters canManageMeterReadings ticketVisibilityType canManageBankAccounts canManageBankContractorAccounts canManageBankIntegrationAccountContexts canManageBankIntegrationOrganizationContexts canManageBankTransactions canManageBankAccountReports canManageBankAccountReportTasks canManageBankAccountReports canManageIncidents canManageNewsItems canManageNewsItemTemplates canManageMobileFeatureConfigs id dv sender { dv fingerprint } v createdBy { id name } updatedBy { id name } createdAt updatedAt }' const OrganizationEmployeeRole = generateGqlQueries('OrganizationEmployeeRole', ORGANIZATION_EMPLOYEE_ROLE_FIELDS) const ORGANIZATION_EMPLOYEE_FIELDS = `{ organization ${ORGANIZATION_FIELDS} user { id name } name email phone role ${ORGANIZATION_EMPLOYEE_ROLE_FIELDS} hasAllSpecializations isRejected isAccepted isBlocked id dv sender { dv fingerprint } v createdBy { id name } updatedBy { id name } position createdAt deletedAt updatedAt }` diff --git a/apps/condo/domains/organization/schema/OrganizationEmployeeRole.js b/apps/condo/domains/organization/schema/OrganizationEmployeeRole.js index dd6ffacba12..f22fa440497 100644 --- a/apps/condo/domains/organization/schema/OrganizationEmployeeRole.js +++ b/apps/condo/domains/organization/schema/OrganizationEmployeeRole.js @@ -106,6 +106,7 @@ const OrganizationEmployeeRole = new GQLListSchema('OrganizationEmployeeRole', { canManageNewsItemTemplates: { type: Checkbox, defaultValue: false }, canManageCallRecords: { type: Checkbox, defaultValue: false }, canDownloadCallRecords: { type: Checkbox, defaultValue: false }, + canManageMobileFeatureConfigs: { type: Checkbox, defaultValue: false }, }, plugins: [uuided(), versioned(), tracked(), dvAndSender(), historical()], access: { diff --git a/apps/condo/domains/organization/schema/OrganizationEmployeeRole.test.js b/apps/condo/domains/organization/schema/OrganizationEmployeeRole.test.js index 90f302a686d..ebe0db5e3d2 100644 --- a/apps/condo/domains/organization/schema/OrganizationEmployeeRole.test.js +++ b/apps/condo/domains/organization/schema/OrganizationEmployeeRole.test.js @@ -50,6 +50,7 @@ describe('OrganizationEmployeeRole', () => { expect(obj.canManageIncidents).toBeFalsy() expect(obj.canManageNewsItems).toBeFalsy() expect(obj.canManageNewsItemTemplates).toBeFalsy() + expect(obj.canManageMobileFeatureConfigs).toBeFalsy() expect(obj.nameNonLocalized).toEqual(obj.name) expect(obj.descriptionNonLocalized).toEqual(obj.description) expect(obj.ticketVisibilityType).toEqual(ORGANIZATION_TICKET_VISIBILITY) diff --git a/apps/condo/domains/settings/access/MobileFeatureConfig.js b/apps/condo/domains/settings/access/MobileFeatureConfig.js new file mode 100644 index 00000000000..ce6776fc293 --- /dev/null +++ b/apps/condo/domains/settings/access/MobileFeatureConfig.js @@ -0,0 +1,77 @@ +/** + * Generated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + */ + +const { uniq, map, get } = require('lodash') + +const { throwAuthenticationError } = require('@open-condo/keystone/apolloErrorFormatter') +const { find, getById } = require('@open-condo/keystone/schema') + +const { checkPermissionInUserOrganizationOrRelatedOrganization } = require('@condo/domains/organization/utils/accessSchema') +const { queryOrganizationEmployeeFor, queryOrganizationEmployeeFromRelatedOrganizationFor } = require('@condo/domains/organization/utils/accessSchema') +const { RESIDENT } = require('@condo/domains/user/constants/common') + +async function canReadMobileFeatureConfigs ({ authentication: { item: user } }) { + if (!user) return throwAuthenticationError() + if (user.deletedAt) return false + + if (user.isAdmin || user.isSupport) return {} + + if (user.type === RESIDENT) { + const residents = await find('Resident', { user: { id: user.id }, deletedAt: null }) + const organizations = uniq(map(residents, 'organization')) + + if (residents.length > 0) { + return { + organization: { + id_in: organizations, + deletedAt: null, + }, + deletedAt: null, + } + } + return false + } + + return { + organization: { + OR: [ + queryOrganizationEmployeeFor(user.id), + queryOrganizationEmployeeFromRelatedOrganizationFor(user.id), + ], + }, + } +} + +async function canManageMobileFeatureConfigs (attrs) { + const { authentication: { item: user }, originalInput, operation, itemId } = attrs + if (!user) return throwAuthenticationError() + if (user.deletedAt) return false + if (user.type === RESIDENT) return false + if (user.isAdmin || user.isSupport) return true + + let organizationId + if (operation === 'create') { + organizationId = get(originalInput, 'organization.connect.id') + } + if ( operation === 'update') { + if (!itemId) return false + + const foundConfig = await getById('MobileFeatureConfig', itemId) + if (!foundConfig) return false + + organizationId = get(foundConfig, 'organization') + } + + return await checkPermissionInUserOrganizationOrRelatedOrganization(user.id, organizationId, 'canManageMobileFeatureConfigs') +} + +/* + 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 = { + canReadMobileFeatureConfigs, + canManageMobileFeatureConfigs, +} + diff --git a/apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsForm.tsx b/apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsForm.tsx new file mode 100644 index 00000000000..37aa14cdfd5 --- /dev/null +++ b/apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsForm.tsx @@ -0,0 +1,113 @@ +import { MobileFeatureConfig as MobileFeatureConfigType } from '@app/condo/schema' +import { Col, Form, Row } from 'antd' +import { Gutter } from 'antd/es/grid/row' +import get from 'lodash/get' +import { useRouter } from 'next/router' +import React, { useMemo, useState } from 'react' + +import { useIntl } from '@open-condo/next/intl' +import { ActionBar, Button, Checkbox, Typography } from '@open-condo/ui' + +import { FormWithAction } from '@condo/domains/common/components/containers/FormList' +import { MobileFeatureConfig } from '@condo/domains/settings/utils/clientSchema' + +const INPUT_LAYOUT_PROPS = { + labelCol: { + span: 24, + md: 5, + }, + wrapperCol: { + span: 24, + md: 6, + }, + styled: { + paddingBottom: '12px', + }, + colon: false, +} +const BIG_ROW_GUTTERS: [Gutter, Gutter] = [0, 60] +const MIDDLE_ROW_GUTTERS: [Gutter, Gutter] = [0, 40] +const SMALL_ROW_GUTTERS: [Gutter, Gutter] = [0, 20] + +interface ITicketSubmittingSettingsForm { + mobileConfig?: MobileFeatureConfigType, + userOrganizationId: string, +} + +export const OnlyProgressionMeterReadingsForm: React.FC = ({ mobileConfig, userOrganizationId }) => { + const intl = useIntl() + const SaveMessage = intl.formatMessage({ id: 'Save' }) + const MessageAboutFeat = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.OnlyProgressionMeterReadings.messageAboutFeat' }) + const EnableMessage = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.OnlyProgressionMeterReadings.isEnabled' }) + + const router = useRouter() + + const initialValues = { + onlyGreaterThanPreviousMeterReadingIsEnabled: get(mobileConfig, 'onlyGreaterThanPreviousMeterReadingIsEnabled'), + } + + const updateHook = MobileFeatureConfig.useUpdate({}, () => router.push('/settings?tab=mobileFeatureConfig')) + const updateAction = async (data) => { + await updateHook(data, mobileConfig) + } + const createAction = MobileFeatureConfig.useCreate({}, () => router.push('/settings?tab=mobileFeatureConfig')) + const action = mobileConfig ? updateAction : createAction + + return useMemo(() => ( + { + if (!mobileConfig) { + values.organization = { connect: { id: userOrganizationId } } + } + return values + }} + > + {({ handleSave, isLoading }) => ( + + + + + + + {MessageAboutFeat} + + + + + + + + + + + + + + + {SaveMessage} + , + ]} + /> + + + )} + + ), [action, mobileConfig]) +} diff --git a/apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsSettingCard.tsx b/apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsSettingCard.tsx new file mode 100644 index 00000000000..6a1a2ebc37b --- /dev/null +++ b/apps/condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsSettingCard.tsx @@ -0,0 +1,38 @@ +import { MobileFeatureConfig as MobileFeatureConfigType } from '@app/condo/schema' +import get from 'lodash/get' +import { useRouter } from 'next/router' +import React, { useCallback } from 'react' + +import { useIntl } from '@open-condo/next/intl' +import { Typography } from '@open-condo/ui' + +import { SettingCard } from '@condo/domains/common/components/settings/SettingCard' + +interface OnlyProgressionMeterReadingsSettingCardProps { + mobileConfig?: MobileFeatureConfigType +} + +const TICKET_DISABLING_SETTINGS_URL = '/settings/mobileFeatureConfig/onlyProgressionMeterReadings' + +export const OnlyProgressionMeterReadingsSettingCard: React.FC = ({ mobileConfig }) => { + const intl = useIntl() + const OnlyProgressionMeterReadingsTitle = intl.formatMessage({ id: 'pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.title' }) + const OnlyProgressionMeterReadingsIsDisabledLabel = intl.formatMessage({ id: 'pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.isDisabled' }) + const onlyGreaterThanPreviousMeterReadingIsEnabledLabel = intl.formatMessage({ id: 'pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.isEnabled' }) + + const router = useRouter() + + const handleClickCard = useCallback(() => { + router.push(TICKET_DISABLING_SETTINGS_URL) + }, [router]) + + const isEnabled = get(mobileConfig, 'onlyGreaterThanPreviousMeterReadingIsEnabled') + + return ( + + + {isEnabled ? onlyGreaterThanPreviousMeterReadingIsEnabledLabel : OnlyProgressionMeterReadingsIsDisabledLabel} + + + ) +} \ No newline at end of file diff --git a/apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingCard.tsx b/apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingCard.tsx new file mode 100644 index 00000000000..3d835b66333 --- /dev/null +++ b/apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingCard.tsx @@ -0,0 +1,44 @@ +import { MobileFeatureConfig as MobileFeatureConfigType } from '@app/condo/schema' +import get from 'lodash/get' +import { useRouter } from 'next/router' +import React, { useCallback } from 'react' + +import { useIntl } from '@open-condo/next/intl' +import { Typography } from '@open-condo/ui' + +import { SettingCard } from '@condo/domains/common/components/settings/SettingCard' + +interface TicketSubmittingSettingCardProps { + mobileConfig?: MobileFeatureConfigType +} + +const TICKET_DISABLING_SETTINGS_URL = '/settings/mobileFeatureConfig/ticketSubmitting' +const TEXT_ELLIPSIS_CONFIG = { rows: 2 } + +export const TicketSubmittingSettingCard: React.FC = ({ mobileConfig }) => { + const intl = useIntl() + const TicketSubmittingTitle = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.submittingPeriod.pageTitle' }) + const TicketSubmittingCommonPhoneLabel = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.submittingPeriod.commonPhone' }) + const TicketSubmittingIsDisabledLabel = intl.formatMessage({ id: 'pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.isDisabled' }) + const TicketSubmittingIsEnabledLabel = intl.formatMessage({ id: 'pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.isEnabled' }) + + const router = useRouter() + + const handleClickCard = useCallback(() => { + router.push(TICKET_DISABLING_SETTINGS_URL) + }, [router]) + + const commonPhone = get(mobileConfig, 'commonPhone') + const isDisabled = get(mobileConfig, 'ticketSubmittingIsDisabled') + + return ( + + + {isDisabled ? TicketSubmittingIsDisabledLabel : TicketSubmittingIsEnabledLabel} + + + {isDisabled ? `${TicketSubmittingCommonPhoneLabel}: ${commonPhone ? commonPhone : '-'}` : ''} + + + ) +} \ No newline at end of file diff --git a/apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingsForm.tsx b/apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingsForm.tsx new file mode 100644 index 00000000000..798ceccf6bc --- /dev/null +++ b/apps/condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingsForm.tsx @@ -0,0 +1,167 @@ +import { MobileFeatureConfig as MobileFeatureConfigType } from '@app/condo/schema' +import { Col, Form, Row } from 'antd' +import { Gutter } from 'antd/es/grid/row' +import get from 'lodash/get' +import isEmpty from 'lodash/isEmpty' +import { useRouter } from 'next/router' +import React, { useMemo, useState } from 'react' + +import { useIntl } from '@open-condo/next/intl' +import { ActionBar, Checkbox, Typography } from '@open-condo/ui' + +import { ButtonWithDisabledTooltip } from '@condo/domains/common/components/ButtonWithDisabledTooltip' +import { FormWithAction } from '@condo/domains/common/components/containers/FormList' +import { PhoneInput } from '@condo/domains/common/components/PhoneInput' +import { normalizePhone } from '@condo/domains/common/utils/phone' +import { MobileFeatureConfig } from '@condo/domains/settings/utils/clientSchema' + +const INPUT_LAYOUT_PROPS = { + labelCol: { + span: 24, + md: 5, + }, + wrapperCol: { + span: 24, + md: 6, + }, + styled: { + paddingBottom: '12px', + }, + colon: false, +} +const BIG_ROW_GUTTERS: [Gutter, Gutter] = [0, 60] +const MIDDLE_ROW_GUTTERS: [Gutter, Gutter] = [0, 40] +const SMALL_ROW_GUTTERS: [Gutter, Gutter] = [0, 20] + +interface ITicketSubmittingSettingsForm { + mobileConfig?: MobileFeatureConfigType, + userOrganizationId: string, +} + +export const TicketSubmittingSettingsForm: React.FC = ({ mobileConfig, userOrganizationId }) => { + const intl = useIntl() + const SaveMessage = intl.formatMessage({ id: 'Save' }) + const ExamplePhoneMessage = intl.formatMessage({ id: 'example.Phone' }) + const RequiredCommonPhoneMessage = intl.formatMessage({ id: 'pages.auth.register.field.Phone' }) + const InvalidPhoneMessage = intl.formatMessage({ id: 'global.input.error.wrongMobilePhone' }) + const ErrorsContainerTitle = intl.formatMessage({ id: 'errorsContainer.requiredErrors' }) + const CommonPhoneMessage = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.submittingPeriod.commonPhoneField' }) + const IsEnabledMessage = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.submittingPeriod.isEnabled' }) + const MessageAboutFeat = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.submittingPeriod.messageAboutFeat' }) + + const router = useRouter() + + const initialValues = { + commonPhone: get(mobileConfig, 'commonPhone'), + ticketSubmittingIsDisabled: get(mobileConfig, 'ticketSubmittingIsDisabled'), + } + + const updateHook = MobileFeatureConfig.useUpdate({}, () => router.push('/settings?tab=mobileFeatureConfig')) + const updateAction = async (data) => { + await updateHook(data, mobileConfig) + } + const createAction = MobileFeatureConfig.useCreate({}, () => router.push('/settings?tab=mobileFeatureConfig')) + const action = mobileConfig ? updateAction : createAction + + return useMemo(() => ( + { + if (!mobileConfig) { + values.organization = { connect: { id: userOrganizationId } } + } + return values + }} + > + {({ handleSave, isLoading }) => ( + + + + + + + {MessageAboutFeat} + + + + + + + + + + + + + + + + + + + + + { + ({ getFieldsValue, getFieldError }) => { + const { + ticketSubmittingIsDisabled, + commonPhone, + } = getFieldsValue(['ticketSubmittingIsDisabled', 'commonPhone']) + + const messageLabels = [] + if (ticketSubmittingIsDisabled && !commonPhone) messageLabels.push(RequiredCommonPhoneMessage) + + const requiredErrorMessage = !isEmpty(messageLabels) && ErrorsContainerTitle.concat(' ', messageLabels.join(', ')) + const hasInvalidPhoneError = commonPhone && (normalizePhone(commonPhone) !== commonPhone) ? InvalidPhoneMessage : undefined + const errors = [requiredErrorMessage, hasInvalidPhoneError] + .filter(Boolean) + .join(', ') + + const isDisabled = hasInvalidPhoneError || requiredErrorMessage + + return ( + + {SaveMessage} + , + ]} + /> + ) + } + } + + + + + )} + + ), [action, mobileConfig]) +} diff --git a/apps/condo/domains/settings/gql.js b/apps/condo/domains/settings/gql.js new file mode 100644 index 00000000000..88723319bc1 --- /dev/null +++ b/apps/condo/domains/settings/gql.js @@ -0,0 +1,22 @@ +/** + * Generated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + * In most cases you should not change it by hands + * Please, don't remove `AUTOGENERATE MARKER`s + */ + +const gql = require('graphql-tag') + +const { generateGqlQueries } = require('@open-condo/codegen/generate.gql') + + +const COMMON_FIELDS = 'id dv sender { dv fingerprint } v deletedAt newId createdBy { id name } updatedBy { id name } createdAt updatedAt' + +const MOBILE_FEATURE_CONFIG_FIELDS = `{ organization { id } commonPhone onlyGreaterThanPreviousMeterReadingIsEnabled ticketSubmittingIsDisabled meta ${COMMON_FIELDS} }` +const MobileFeatureConfig = generateGqlQueries('MobileFeatureConfig', MOBILE_FEATURE_CONFIG_FIELDS) + +/* AUTOGENERATE MARKER */ + +module.exports = { + MobileFeatureConfig, +/* AUTOGENERATE MARKER */ +} diff --git a/apps/condo/domains/settings/schema/MobileFeatureConfig.js b/apps/condo/domains/settings/schema/MobileFeatureConfig.js new file mode 100644 index 00000000000..cc46ff18395 --- /dev/null +++ b/apps/condo/domains/settings/schema/MobileFeatureConfig.js @@ -0,0 +1,99 @@ +/** + * Generated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsDisabled:Checkbox'` + */ + +const { Text, Relationship, Checkbox } = require('@keystonejs/fields') + +const { GQLError, GQLErrorCode: { BAD_USER_INPUT } } = require('@open-condo/keystone/errors') +const { Json } = require('@open-condo/keystone/fields') +const { historical, versioned, uuided, tracked, softDeleted, dvAndSender } = require('@open-condo/keystone/plugins') +const { GQLListSchema } = require('@open-condo/keystone/schema') + +const { PHONE_FIELD } = require('@condo/domains/common/schema/fields') +const { ORGANIZATION_OWNED_FIELD } = require('@condo/domains/organization/schema/fields') +const access = require('@condo/domains/settings/access/MobileFeatureConfig') +const { ticketSubmittingValidations } = require('@condo/domains/settings/utils/MobileFeatureConfigSchemaValidations') + + + +const ERRORS = { + TICKET_SUBMITTING_PHONES_NOT_CONFIGURED: { + code: BAD_USER_INPUT, + variable: ['data', 'commonPhone'], + type: 'TICKET_SUBMITTING_PHONES_NOT_CONFIGURED', + message: 'commonPhone field not specified', + messageForUser: 'api.organization.MobileFeatureConfig.TICKET_SUBMITTING_PHONES_NOT_CONFIGURED', + }, + COMMON_PHONE_INVALID: { + code: BAD_USER_INPUT, + variable: ['data', 'commonPhone'], + type: 'COMMON_PHONE_NOT_CONFIGURED', + message: 'commonPhone is invalid', + messageForUser: 'api.organization.MobileFeatureConfig.COMMON_PHONE_INVALID', + }, + ORGANIZATION_IS_REQUIRED: { + code: BAD_USER_INPUT, + variable: ['data', 'organization'], + type: 'ORGANIZATION_IS_REQUIRED', + message: 'The organization is required to create', + messageForUser: 'api.organization.MobileFeatureConfig.ORGANIZATION_IS_REQUIRED', + }, +} + +const MobileFeatureConfig = new GQLListSchema('MobileFeatureConfig', { + schemaDoc: 'Manages availability of some features in mobile application,' + + ' stores data required in disabled state of a feature.', + fields: { + organization: ORGANIZATION_OWNED_FIELD, + + commonPhone: { + ...PHONE_FIELD, + schemaDoc: 'Phone number where the organization wants to receive common calls', + }, + + ticketSubmittingIsDisabled: { + schemaDoc: 'Disabling the ability to send tickets by the user of the resident\'s mobile application. ' + + 'Instead, he will be shown a screen with phones where he can contact to send a request', + type: Checkbox, + }, + + onlyGreaterThanPreviousMeterReadingIsEnabled: { + schemaDoc: 'Restricts residents to submit only meter readings, that are greater than previous. ' + + 'Restrictions apply to mobile phones only.', + type: Checkbox, + }, + + meta: { + schemaDoc: 'Can be used to store some meta information for mobile applications.', + type: Json, + }, + + }, + plugins: [uuided(), versioned(), tracked(), softDeleted(), dvAndSender(), historical()], + access: { + read: access.canReadMobileFeatureConfigs, + create: access.canManageMobileFeatureConfigs, + update: access.canManageMobileFeatureConfigs, + delete: false, + auth: true, + }, + kmigratorOptions: { + constraints: [ + { + type: 'models.UniqueConstraint', + fields: ['organization'], + condition: 'Q(deletedAt__isnull=True)', + name: 'mobilefeatureconfig_unique_organization', + }, + ], + }, + hooks: { + validateInput: async ({ resolvedData, context, existingItem }) => { + await ticketSubmittingValidations(resolvedData, context, existingItem, ERRORS) + }, + }, +}) + +module.exports = { + MobileFeatureConfig, +} diff --git a/apps/condo/domains/settings/schema/MobileFeatureConfig.test.js b/apps/condo/domains/settings/schema/MobileFeatureConfig.test.js new file mode 100644 index 00000000000..81a70d82351 --- /dev/null +++ b/apps/condo/domains/settings/schema/MobileFeatureConfig.test.js @@ -0,0 +1,269 @@ +/** + * Generated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + */ + +const { makeLoggedInAdminClient, makeClient, UUID_RE, expectValuesOfCommonFields, catchErrorFrom, expectToThrowGQLError } = require('@open-condo/keystone/test.utils') +const { + expectToThrowAuthenticationErrorToObj, expectToThrowAuthenticationErrorToObjects, + expectToThrowAccessDeniedErrorToObj, +} = require('@open-condo/keystone/test.utils') + +const { createTestOrganization, createTestOrganizationEmployee, createTestOrganizationEmployeeRole } = require('@condo/domains/organization/utils/testSchema') +const { MobileFeatureConfig, createTestMobileFeatureConfig, updateTestMobileFeatureConfig } = require('@condo/domains/settings/utils/testSchema') +const { makeClientWithNewRegisteredAndLoggedInUser, makeClientWithSupportUser } = require('@condo/domains/user/utils/testSchema') + + +describe('MobileFeatureConfig', () => { + let admin, support + beforeAll(async () => { + admin = await makeLoggedInAdminClient() + support = await makeClientWithSupportUser() + }) + describe('CRUD tests', () => { + describe('accesses', () => { + describe('admin', () => { + test('can create', async () => { + const [organization] = await createTestOrganization(admin) + + const [obj, attrs] = await createTestMobileFeatureConfig(admin, organization) + + expectValuesOfCommonFields(obj, attrs, admin) + }) + + test('can update', async () => { + const [organization] = await createTestOrganization(admin) + + const [objCreated] = await createTestMobileFeatureConfig(admin, organization) + + const [obj, attrs] = await updateTestMobileFeatureConfig(admin, objCreated.id) + + expect(obj.dv).toEqual(1) + expect(obj.sender).toEqual(attrs.sender) + expect(obj.v).toEqual(2) + expect(obj.updatedBy).toEqual(expect.objectContaining({ id: admin.user.id })) + }) + + test('can\'t hard delete', async () => { + const [organization] = await createTestOrganization(admin) + + const [objCreated] = await createTestMobileFeatureConfig(admin, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await MobileFeatureConfig.delete(admin, objCreated.id) + }) + }) + + test('can read', async () => { + const [organization] = await createTestOrganization(admin) + + const [obj, attrs] = await createTestMobileFeatureConfig(admin, organization) + + const objs = await MobileFeatureConfig.getAll(admin, {}, { sortBy: ['updatedAt_DESC'] }) + + expect(objs.length).toBeGreaterThanOrEqual(1) + expect(objs).toEqual(expect.arrayContaining([ + expect.objectContaining({ + id: obj.id, + }), + ])) + }) + }) + + describe('support', () => { + test('can create', async () => { + const [organization] = await createTestOrganization(admin) + + const [obj, attrs] = await createTestMobileFeatureConfig(support, organization) + + expectValuesOfCommonFields(obj, attrs, support) + }) + + test('can update', async () => { + const [organization] = await createTestOrganization(admin) + + const [objCreated] = await createTestMobileFeatureConfig(support, organization) + + const [obj, attrs] = await updateTestMobileFeatureConfig(support, objCreated.id) + + expect(obj.dv).toEqual(1) + expect(obj.sender).toEqual(attrs.sender) + expect(obj.v).toEqual(2) + expect(obj.updatedBy).toEqual(expect.objectContaining({ id: support.user.id })) + }) + + test('can\'t hard delete', async () => { + const [organization] = await createTestOrganization(admin) + + const [objCreated] = await createTestMobileFeatureConfig(support, organization) + + await expectToThrowAccessDeniedErrorToObj(async () => { + await MobileFeatureConfig.delete(support, objCreated.id) + }) + }) + + test('can read', async () => { + const [organization] = await createTestOrganization(admin) + + const [obj, attrs] = await createTestMobileFeatureConfig(support, organization) + + const objs = await MobileFeatureConfig.getAll(support, {}, { sortBy: ['updatedAt_DESC'] }) + + expect(objs.length).toBeGreaterThanOrEqual(1) + expect(objs).toEqual(expect.arrayContaining([ + expect.objectContaining({ + id: obj.id, + }), + ])) + }) + }) + + describe('anonymous', () => { + test('can\'t create', async () => { + const client = await makeClient() + await expectToThrowAuthenticationErrorToObj(async () => { + await createTestMobileFeatureConfig(client, { id: 'id' }) + }) + }) + + test('can\'t update', async () => { + const client = await makeClient() + await expectToThrowAuthenticationErrorToObj(async () => { + await updateTestMobileFeatureConfig(client, 'id') + }) + }) + + test('can\'t hard delete', async () => { + const client = await makeClient() + await expectToThrowAccessDeniedErrorToObj(async () => { + await MobileFeatureConfig.delete(client, 'id') + }) + }) + + test('can\'t read', async () => { + const client = await makeClient() + await expectToThrowAuthenticationErrorToObjects(async () => { + await MobileFeatureConfig.getAll(client, {}, { sortBy: ['updatedAt_DESC'] }) + }) + }) + }) + + + describe('employee', () => { + test('can create', async () => { + const client = await makeClientWithNewRegisteredAndLoggedInUser() + const [organization] = await createTestOrganization(admin) + + const [role] = await createTestOrganizationEmployeeRole(admin, organization, { + canManageMobileFeatureConfigs: true, + }) + await createTestOrganizationEmployee(admin, organization, client.user, role) + + const [obj, attrs] = await createTestMobileFeatureConfig(client, organization) + + expect(obj.id).toMatch(UUID_RE) + expect(obj.dv).toEqual(1) + expect(obj.sender).toEqual(attrs.sender) + expect(obj.createdBy).toEqual(expect.objectContaining({ id: client.user.id })) + }) + + test('can update', async () => { + const client = await makeClientWithNewRegisteredAndLoggedInUser() + const [organization] = await createTestOrganization(admin) + + const [role] = await createTestOrganizationEmployeeRole(admin, organization, { + canManageMobileFeatureConfigs: true, + }) + await createTestOrganizationEmployee(admin, organization, client.user, role) + + const [objCreated] = await createTestMobileFeatureConfig(admin, organization) + + const [obj, attrs] = await updateTestMobileFeatureConfig(client, objCreated.id) + + expect(obj.id).toMatch(UUID_RE) + expect(obj.dv).toEqual(1) + expect(obj.sender).toEqual(attrs.sender) + expect(obj.v).toEqual(2) + expect(obj.updatedBy).toEqual(expect.objectContaining({ id: client.user.id })) + }) + + test('can\'t delete', async () => { + const client = await makeClientWithNewRegisteredAndLoggedInUser() + + await expectToThrowAccessDeniedErrorToObj(async () => { + await MobileFeatureConfig.delete(client, 'id') + }) + }) + + test('can read', async () => { + const client = await makeClientWithNewRegisteredAndLoggedInUser() + const [organization] = await createTestOrganization(admin) + + const [role] = await createTestOrganizationEmployeeRole(admin, organization, {}) + await createTestOrganizationEmployee(admin, organization, client.user, role) + + const [objCreated] = await createTestMobileFeatureConfig(admin, organization) + + const objs = await MobileFeatureConfig.getAll(client, {}, { sortBy: ['updatedAt_DESC'] }) + + expect(objs).toHaveLength(1) + expect(objs[0]).toMatchObject({ + id: objCreated.id, + }) + }) + }) + }) + + describe('Validation tests', () => { + + test('organization uniq constraint', async () => { + const [organization] = await createTestOrganization(admin) + + await createTestMobileFeatureConfig(admin, organization, { + commonPhone: undefined, + ticketSubmittingIsDisabled: false, + }) + + await catchErrorFrom(async () => { + await createTestMobileFeatureConfig(admin, organization, { + ticketSubmittingIsDisabled: false, + }) + }, ({ errors, data }) => { + expect(errors[0].message).toMatch('duplicate key value violates unique constraint "mobilefeatureconfig_unique_organization"') + expect(data).toEqual({ 'obj': null }) + }) + + + }) + + test('TICKET_SUBMITTING_PHONES_NOT_CONFIGURED', async () => { + const [organization] = await createTestOrganization(admin) + + await expectToThrowGQLError( + async () => await createTestMobileFeatureConfig(admin, organization, { + commonPhone: undefined, + ticketSubmittingIsDisabled: true, + }), + { + code: 'BAD_USER_INPUT', + type: 'TICKET_SUBMITTING_PHONES_NOT_CONFIGURED', + message: 'commonPhone field not specified', + }) + }) + + test('COMMON_PHONE_INVALID', async () => { + const [organization] = await createTestOrganization(admin) + + await expectToThrowGQLError( + async () => await createTestMobileFeatureConfig(admin, organization, { + commonPhone: 'undefined', + ticketSubmittingIsDisabled: true, + }), + { + code: 'BAD_USER_INPUT', + type: 'WRONG_PHONE_FORMAT', + message: 'Wrong phone number format', + }) + }) + }) + }) +}) diff --git a/apps/condo/domains/settings/schema/index.js b/apps/condo/domains/settings/schema/index.js new file mode 100644 index 00000000000..a7ae828c564 --- /dev/null +++ b/apps/condo/domains/settings/schema/index.js @@ -0,0 +1,12 @@ +/** + * This file is autogenerated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + * In most cases you should not change it by hands. And please don't remove `AUTOGENERATE MARKER`s + */ + +const { MobileFeatureConfig } = require('./MobileFeatureConfig') +/* AUTOGENERATE MARKER */ + +module.exports = { + MobileFeatureConfig, +/* AUTOGENERATE MARKER */ +} diff --git a/apps/condo/domains/settings/utils/MobileFeatureConfigSchemaValidations.js b/apps/condo/domains/settings/utils/MobileFeatureConfigSchemaValidations.js new file mode 100644 index 00000000000..106558df398 --- /dev/null +++ b/apps/condo/domains/settings/utils/MobileFeatureConfigSchemaValidations.js @@ -0,0 +1,19 @@ +const { GQLError } = require('@open-condo/keystone/errors') + +const { normalizePhone } = require('@condo/domains/common/utils/phone') + +const ticketSubmittingValidations = async (resolvedData, context, existingItem, ERRORS) => { + const newItem = { ...existingItem, ...resolvedData } + if (newItem.ticketSubmittingIsDisabled) { + if (!newItem['commonPhone']) { + throw new GQLError(ERRORS.TICKET_SUBMITTING_PHONES_NOT_CONFIGURED, context) + } + if (newItem['commonPhone'] && normalizePhone(newItem['commonPhone']) !== newItem['commonPhone']) { + throw new GQLError(ERRORS.COMMON_PHONE_INVALID, context) + } + } +} + +module.exports = { + ticketSubmittingValidations, +} \ No newline at end of file diff --git a/apps/condo/domains/settings/utils/clientSchema/MobileFeatureConfig.ts b/apps/condo/domains/settings/utils/clientSchema/MobileFeatureConfig.ts new file mode 100644 index 00000000000..39b25ce3ede --- /dev/null +++ b/apps/condo/domains/settings/utils/clientSchema/MobileFeatureConfig.ts @@ -0,0 +1,30 @@ +/** + * Generated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + */ + +import { + MobileFeatureConfig, + MobileFeatureConfigCreateInput, + MobileFeatureConfigUpdateInput, + QueryAllMobileFeatureConfigsArgs, +} from '@app/condo/schema' + +import { generateReactHooks } from '@open-condo/codegen/generate.hooks' + +import { MobileFeatureConfig as MobileFeatureConfigGQL } from '@condo/domains/settings/gql' + +const { + useObject, + useObjects, + useCreate, + useUpdate, + useSoftDelete, +} = generateReactHooks(MobileFeatureConfigGQL) + +export { + useObject, + useObjects, + useCreate, + useUpdate, + useSoftDelete, +} diff --git a/apps/condo/domains/settings/utils/clientSchema/index.ts b/apps/condo/domains/settings/utils/clientSchema/index.ts new file mode 100644 index 00000000000..abc4bbd41d3 --- /dev/null +++ b/apps/condo/domains/settings/utils/clientSchema/index.ts @@ -0,0 +1,7 @@ +/** + * This file is autogenerated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + * In most cases you should not change it by hands. And please don't remove `AUTOGENERATE MARKER`s + */ + +export * as MobileFeatureConfig from './MobileFeatureConfig' +/* AUTOGENERATE MARKER */ diff --git a/apps/condo/domains/settings/utils/serverSchema/index.js b/apps/condo/domains/settings/utils/serverSchema/index.js new file mode 100644 index 00000000000..7960e4cfb03 --- /dev/null +++ b/apps/condo/domains/settings/utils/serverSchema/index.js @@ -0,0 +1,18 @@ +/** + * Generated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + * In most cases you should not change it by hands + * Please, don't remove `AUTOGENERATE MARKER`s + */ + +const { generateServerUtils, execGqlWithoutAccess } = require('@open-condo/codegen/generate.server.utils') + +const { MobileFeatureConfig: MobileFeatureConfigGQL } = require('@condo/domains/settings/gql') +/* AUTOGENERATE MARKER */ + +const MobileFeatureConfig = generateServerUtils(MobileFeatureConfigGQL) +/* AUTOGENERATE MARKER */ + +module.exports = { + MobileFeatureConfig, +/* AUTOGENERATE MARKER */ +} diff --git a/apps/condo/domains/settings/utils/testSchema/index.js b/apps/condo/domains/settings/utils/testSchema/index.js new file mode 100644 index 00000000000..c9750544939 --- /dev/null +++ b/apps/condo/domains/settings/utils/testSchema/index.js @@ -0,0 +1,56 @@ +/** + * Generated by `createschema settings.MobileFeatureConfig 'organization:Relationship:Organization:CASCADE; emergencyPhone:Text; commonPhone:Text; onlyGreaterThanPreviousMeterReadingIsEnabled:Checkbox; meta:Json; ticketSubmittingIsEnabled:Checkbox'` + * In most cases you should not change it by hands + * Please, don't remove `AUTOGENERATE MARKER`s + */ +const { faker } = require('@faker-js/faker') + +const { generateServerUtils, execGqlWithoutAccess } = require('@open-condo/codegen/generate.server.utils') + +const { generateGQLTestUtils, throwIfError } = require('@open-condo/codegen/generate.test.utils') + +const { MobileFeatureConfig: MobileFeatureConfigGQL } = require('@condo/domains/settings/gql') +/* AUTOGENERATE MARKER */ + +const MobileFeatureConfig = generateGQLTestUtils(MobileFeatureConfigGQL) +/* AUTOGENERATE MARKER */ + +async function createTestMobileFeatureConfig (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, + commonPhone: faker.phone.number('+7922#######'), + ticketSubmittingIsDisabled: false, + onlyGreaterThanPreviousMeterReadingIsEnabled: false, + organization: { connect: { id: organization.id } }, + ...extraAttrs, + } + const obj = await MobileFeatureConfig.create(client, attrs) + return [obj, attrs] +} + +async function updateTestMobileFeatureConfig (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 MobileFeatureConfig.update(client, id, attrs) + return [obj, attrs] +} + +/* AUTOGENERATE MARKER */ + +module.exports = { + MobileFeatureConfig, createTestMobileFeatureConfig, updateTestMobileFeatureConfig, +/* AUTOGENERATE MARKER */ +} diff --git a/apps/condo/index.js b/apps/condo/index.js index d27feedc9dd..4bee86caa95 100644 --- a/apps/condo/index.js +++ b/apps/condo/index.js @@ -108,6 +108,7 @@ const schemas = () => [ // NOTE: miniapps must be placed after all other schemas. // "B2BAppAccess" preprocessor needs to know about all registered accesses to other schemas require('@condo/domains/miniapp/schema'), + require('@condo/domains/settings/schema'), getWebhookModels('@app/condo/schema.graphql'), ] diff --git a/apps/condo/lang/en/en.json b/apps/condo/lang/en/en.json index e516e1739d8..3077f7b2755 100644 --- a/apps/condo/lang/en/en.json +++ b/apps/condo/lang/en/en.json @@ -52,10 +52,12 @@ "Description": "Description", "Document": "Document", "Done": "Done", + "Disable": "Disable", "Ready": "Ready", "Edit": "Edit", "EditProhibited": "Editing is prohibited", "Edited": "Edited", + "Enable": "Enable", "Email": "Email", "Table": "Table", "Ticket": "Ticket", @@ -952,6 +954,20 @@ "pages.condo.settings.propertyScope.form.alert.descriptionLink": "Go to the list of employees", "pages.condo.settings.propertyScope.alert.delete.Title": "Delete property scope?", "pages.condo.settings.propertyScope.alert.delete.Message": "It will be impossible to restore. But it will be possible to create again.", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.pageTitle": "Disable creating tickets from the mobile app", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.commonPhone": "The number for the tickets that the resident will see in the application", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.commonPhoneField": "Mobile for tickets", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.isEnabled": "Disable submitting tickets", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.messageAboutFeat": "If tickets are disabled, residents will not be able to send requests to the control room via the mobile app. Instead, they will be asked to call the number below", + "pages.condo.settings.barItem.MobileFeatureConfig": "Resident mobile app", + "pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.title": "Tickets from resident mobile app", + "pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.isDisabled": "Tickets are off. Residents cannot submit a ticket through the mobile app.", + "pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.isEnabled": "Tickets is on. Residents can submit a ticket via the mobile app.", + "pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.title": "Readings of metering devices from the mobile application", + "pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.isDisabled": "Take any meter readings", + "pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.isEnabled": "Accept meter readings only more than the previous ones", + "pages.condo.settings.mobileFeatureConfig.OnlyProgressionMeterReadings.messageAboutFeat": "Enabling this restriction prevents residents from transmitting meter readings if they are less than the previous ones. If a resident submitted last month, for example, electricity meter readings of 100 kW, then this month he will not be able to transmit readings less than 100.", + "pages.condo.settings.mobileFeatureConfig.OnlyProgressionMeterReadings.isEnabled": "Residents cannot report less than previous readings", "pages.condo.profile.interfaceLanguage": "Language", "pages.condo.profile.chooseInterfaceLanguage": "Choose language", "pages.condo.profile.globalHints": "Hints in sections", diff --git a/apps/condo/lang/ru/ru.json b/apps/condo/lang/ru/ru.json index cc751897662..b24418233f5 100644 --- a/apps/condo/lang/ru/ru.json +++ b/apps/condo/lang/ru/ru.json @@ -52,10 +52,12 @@ "Description": "Описание", "Document": "Документ", "Done": "Выполнено", + "Disable": "Выключить", "Ready": "Готово", "Edit": "Редактировать", "EditProhibited": "Редактирование запрещено", "Edited": "Отредактировано", + "Enable": "Включить", "Email": "Email", "Table": "Таблица", "Ticket": "Заявка", @@ -952,6 +954,20 @@ "pages.condo.settings.propertyScope.form.alert.descriptionLink": "Перейти к списку сотрудников", "pages.condo.settings.propertyScope.alert.delete.Title": "Удалить область видимости?", "pages.condo.settings.propertyScope.alert.delete.Message": "Восстановить будет невозможно. Но можно будет создать снова.", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.pageTitle": "Отключение заявок", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.commonPhone": "Номер для заявок, который увидит житель в приложении", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.commonPhoneField": "Телефон для заявок", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.isEnabled": "Отключить отправку заявок", + "pages.condo.settings.mobileFeatureConfig.submittingPeriod.messageAboutFeat": "При отключении заявок жители не смогут отправлять заявки в диспетчерскую через мобильное приложение. Вместо этого им будет предложено позвонить по телефону, указанному ниже", + "pages.condo.settings.barItem.MobileFeatureConfig": "Мобильное приложение жителя", + "pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.title": "Заявки из мобильного приложения", + "pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.isDisabled": "Заявки выключены. Жители не могут отправить заявку через мобильное приложение.", + "pages.condo.settings.barItem.MobileFeatureConfig.ticketSubmitting.isEnabled": "Заявки включены. Жители могут отправить заявку через мобильное приложение.", + "pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.title": "Показания ИПУ из мобильного приложения жителя", + "pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.isDisabled": "Принимать любые показания", + "pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.isEnabled": "Принимать показания только больше предыдущих", + "pages.condo.settings.mobileFeatureConfig.OnlyProgressionMeterReadings.messageAboutFeat": "Включение этого ограничения не дает жителям передать показания индивидуального прибора учета, если они меньше предыдущих. Если житель в прошлом месяце передал, например, показания счетчика электроэнергии 100 кВт, то в этом месяце он не сможет передать показания меньше 100.", + "pages.condo.settings.mobileFeatureConfig.OnlyProgressionMeterReadings.isEnabled": "Жители не могут передавать показания меньше предыдущих", "pages.condo.profile.interfaceLanguage": "Язык интерфейса", "pages.condo.profile.chooseInterfaceLanguage": "Выберите язык интерфейса", "pages.condo.profile.globalHints": "Подсказки в разделах", diff --git a/apps/condo/migrations/20230811140557-0307_mobilefeatureconfighistoryrecord_and_more.js b/apps/condo/migrations/20230811140557-0307_mobilefeatureconfighistoryrecord_and_more.js new file mode 100644 index 00000000000..094471dd031 --- /dev/null +++ b/apps/condo/migrations/20230811140557-0307_mobilefeatureconfighistoryrecord_and_more.js @@ -0,0 +1,81 @@ +// auto generated by kmigrator +// KMIGRATOR:0307_mobilefeatureconfighistoryrecord_and_more:IyBHZW5lcmF0ZWQgYnkgRGphbmdvIDQuMi4zIG9uIDIwMjMtMDgtMTEgMDk6MzIKCmZyb20gZGphbmdvLmRiIGltcG9ydCBtaWdyYXRpb25zLCBtb2RlbHMKaW1wb3J0IGRqYW5nby5kYi5tb2RlbHMuZGVsZXRpb24KCgpjbGFzcyBNaWdyYXRpb24obWlncmF0aW9ucy5NaWdyYXRpb24pOgoKICAgIGRlcGVuZGVuY2llcyA9IFsKICAgICAgICAoJ19kamFuZ29fc2NoZW1hJywgJzAzMDZfYWx0ZXJfbm90aWZpY2F0aW9udXNlcnNldHRpbmdfbWVzc2FnZXR5cGUnKSwKICAgIF0KCiAgICBvcGVyYXRpb25zID0gWwogICAgICAgIG1pZ3JhdGlvbnMuQ3JlYXRlTW9kZWwoCiAgICAgICAgICAgIG5hbWU9J21vYmlsZWZlYXR1cmVjb25maWdoaXN0b3J5cmVjb3JkJywKICAgICAgICAgICAgZmllbGRzPVsKICAgICAgICAgICAgICAgICgnb3JnYW5pemF0aW9uJywgbW9kZWxzLlVVSURGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnY29tbW9uUGhvbmUnLCBtb2RlbHMuVGV4dEZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCd0aWNrZXRTdWJtaXR0aW5nSXNEaXNhYmxlZCcsIG1vZGVscy5Cb29sZWFuRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ29ubHlHcmVhdGVyVGhhblByZXZpb3VzTWV0ZXJSZWFkaW5nSXNFbmFibGVkJywgbW9kZWxzLkJvb2xlYW5GaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnbWV0YScsIG1vZGVscy5KU09ORmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2lkJywgbW9kZWxzLlVVSURGaWVsZChwcmltYXJ5X2tleT1UcnVlLCBzZXJpYWxpemU9RmFsc2UpKSwKICAgICAgICAgICAgICAgICgndicsIG1vZGVscy5JbnRlZ2VyRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2NyZWF0ZWRBdCcsIG1vZGVscy5EYXRlVGltZUZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCd1cGRhdGVkQXQnLCBtb2RlbHMuRGF0ZVRpbWVGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnY3JlYXRlZEJ5JywgbW9kZWxzLlVVSURGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgndXBkYXRlZEJ5JywgbW9kZWxzLlVVSURGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnZGVsZXRlZEF0JywgbW9kZWxzLkRhdGVUaW1lRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ25ld0lkJywgbW9kZWxzLkpTT05GaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgnZHYnLCBtb2RlbHMuSW50ZWdlckZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdzZW5kZXInLCBtb2RlbHMuSlNPTkZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdoaXN0b3J5X2RhdGUnLCBtb2RlbHMuRGF0ZVRpbWVGaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnaGlzdG9yeV9hY3Rpb24nLCBtb2RlbHMuQ2hhckZpZWxkKGNob2ljZXM9WygnYycsICdjJyksICgndScsICd1JyksICgnZCcsICdkJyldLCBtYXhfbGVuZ3RoPTUwKSksCiAgICAgICAgICAgICAgICAoJ2hpc3RvcnlfaWQnLCBtb2RlbHMuVVVJREZpZWxkKGRiX2luZGV4PVRydWUpKSwKICAgICAgICAgICAgXSwKICAgICAgICAgICAgb3B0aW9ucz17CiAgICAgICAgICAgICAgICAnZGJfdGFibGUnOiAnTW9iaWxlRmVhdHVyZUNvbmZpZ0hpc3RvcnlSZWNvcmQnLAogICAgICAgICAgICB9LAogICAgICAgICksCiAgICAgICAgbWlncmF0aW9ucy5BZGRGaWVsZCgKICAgICAgICAgICAgbW9kZWxfbmFtZT0nb3JnYW5pemF0aW9uZW1wbG95ZWVyb2xlJywKICAgICAgICAgICAgbmFtZT0nY2FuTWFuYWdlTW9iaWxlRmVhdHVyZUNvbmZpZ3MnLAogICAgICAgICAgICBmaWVsZD1tb2RlbHMuQm9vbGVhbkZpZWxkKGRlZmF1bHQ9RmFsc2UpLAogICAgICAgICAgICBwcmVzZXJ2ZV9kZWZhdWx0PUZhbHNlLAogICAgICAgICksCiAgICAgICAgbWlncmF0aW9ucy5BZGRGaWVsZCgKICAgICAgICAgICAgbW9kZWxfbmFtZT0nb3JnYW5pemF0aW9uZW1wbG95ZWVyb2xlaGlzdG9yeXJlY29yZCcsCiAgICAgICAgICAgIG5hbWU9J2Nhbk1hbmFnZU1vYmlsZUZlYXR1cmVDb25maWdzJywKICAgICAgICAgICAgZmllbGQ9bW9kZWxzLkJvb2xlYW5GaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpLAogICAgICAgICksCiAgICAgICAgbWlncmF0aW9ucy5DcmVhdGVNb2RlbCgKICAgICAgICAgICAgbmFtZT0nbW9iaWxlZmVhdHVyZWNvbmZpZycsCiAgICAgICAgICAgIGZpZWxkcz1bCiAgICAgICAgICAgICAgICAoJ2NvbW1vblBob25lJywgbW9kZWxzLlRleHRGaWVsZChibGFuaz1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgndGlja2V0U3VibWl0dGluZ0lzRGlzYWJsZWQnLCBtb2RlbHMuQm9vbGVhbkZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdvbmx5R3JlYXRlclRoYW5QcmV2aW91c01ldGVyUmVhZGluZ0lzRW5hYmxlZCcsIG1vZGVscy5Cb29sZWFuRmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ21ldGEnLCBtb2RlbHMuSlNPTkZpZWxkKGJsYW5rPVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCdpZCcsIG1vZGVscy5VVUlERmllbGQocHJpbWFyeV9rZXk9VHJ1ZSwgc2VyaWFsaXplPUZhbHNlKSksCiAgICAgICAgICAgICAgICAoJ3YnLCBtb2RlbHMuSW50ZWdlckZpZWxkKGRlZmF1bHQ9MSkpLAogICAgICAgICAgICAgICAgKCdjcmVhdGVkQXQnLCBtb2RlbHMuRGF0ZVRpbWVGaWVsZChibGFuaz1UcnVlLCBkYl9pbmRleD1UcnVlLCBudWxsPVRydWUpKSwKICAgICAgICAgICAgICAgICgndXBkYXRlZEF0JywgbW9kZWxzLkRhdGVUaW1lRmllbGQoYmxhbms9VHJ1ZSwgZGJfaW5kZXg9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2RlbGV0ZWRBdCcsIG1vZGVscy5EYXRlVGltZUZpZWxkKGJsYW5rPVRydWUsIGRiX2luZGV4PVRydWUsIG51bGw9VHJ1ZSkpLAogICAgICAgICAgICAgICAgKCduZXdJZCcsIG1vZGVscy5VVUlERmllbGQoYmxhbms9VHJ1ZSwgbnVsbD1UcnVlKSksCiAgICAgICAgICAgICAgICAoJ2R2JywgbW9kZWxzLkludGVnZXJGaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnc2VuZGVyJywgbW9kZWxzLkpTT05GaWVsZCgpKSwKICAgICAgICAgICAgICAgICgnY3JlYXRlZEJ5JywgbW9kZWxzLkZvcmVpZ25LZXkoYmxhbms9VHJ1ZSwgZGJfY29sdW1uPSdjcmVhdGVkQnknLCBudWxsPVRydWUsIG9uX2RlbGV0ZT1kamFuZ28uZGIubW9kZWxzLmRlbGV0aW9uLlNFVF9OVUxMLCByZWxhdGVkX25hbWU9JysnLCB0bz0nX2RqYW5nb19zY2hlbWEudXNlcicpKSwKICAgICAgICAgICAgICAgICgnb3JnYW5pemF0aW9uJywgbW9kZWxzLkZvcmVpZ25LZXkoZGJfY29sdW1uPSdvcmdhbml6YXRpb24nLCBvbl9kZWxldGU9ZGphbmdvLmRiLm1vZGVscy5kZWxldGlvbi5DQVNDQURFLCByZWxhdGVkX25hbWU9JysnLCB0bz0nX2RqYW5nb19zY2hlbWEub3JnYW5pemF0aW9uJykpLAogICAgICAgICAgICAgICAgKCd1cGRhdGVkQnknLCBtb2RlbHMuRm9yZWlnbktleShibGFuaz1UcnVlLCBkYl9jb2x1bW49J3VwZGF0ZWRCeScsIG51bGw9VHJ1ZSwgb25fZGVsZXRlPWRqYW5nby5kYi5tb2RlbHMuZGVsZXRpb24uU0VUX05VTEwsIHJlbGF0ZWRfbmFtZT0nKycsIHRvPSdfZGphbmdvX3NjaGVtYS51c2VyJykpLAogICAgICAgICAgICBdLAogICAgICAgICAgICBvcHRpb25zPXsKICAgICAgICAgICAgICAgICdkYl90YWJsZSc6ICdNb2JpbGVGZWF0dXJlQ29uZmlnJywKICAgICAgICAgICAgfSwKICAgICAgICApLAogICAgICAgIG1pZ3JhdGlvbnMuQWRkQ29uc3RyYWludCgKICAgICAgICAgICAgbW9kZWxfbmFtZT0nbW9iaWxlZmVhdHVyZWNvbmZpZycsCiAgICAgICAgICAgIGNvbnN0cmFpbnQ9bW9kZWxzLlVuaXF1ZUNvbnN0cmFpbnQoY29uZGl0aW9uPW1vZGVscy5RKCgnZGVsZXRlZEF0X19pc251bGwnLCBUcnVlKSksIGZpZWxkcz0oJ29yZ2FuaXphdGlvbicsKSwgbmFtZT0nbW9iaWxlZmVhdHVyZWNvbmZpZ191bmlxdWVfb3JnYW5pemF0aW9uJyksCiAgICAgICAgKSwKICAgIF0K + +exports.up = async (knex) => { + await knex.raw(` + BEGIN; +-- +-- Create model mobilefeatureconfighistoryrecord +-- +CREATE TABLE "MobileFeatureConfigHistoryRecord" ("organization" uuid NULL, "commonPhone" text NULL, "ticketSubmittingIsDisabled" boolean NULL, "onlyGreaterThanPreviousMeterReadingIsEnabled" 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); +-- +-- Add field canManageMobileFeatureConfigs to organizationemployeerole +-- +ALTER TABLE "OrganizationEmployeeRole" ADD COLUMN "canManageMobileFeatureConfigs" boolean DEFAULT false NOT NULL; +ALTER TABLE "OrganizationEmployeeRole" ALTER COLUMN "canManageMobileFeatureConfigs" DROP DEFAULT; +-- +-- Add field canManageMobileFeatureConfigs to organizationemployeerolehistoryrecord +-- +ALTER TABLE "OrganizationEmployeeRoleHistoryRecord" ADD COLUMN "canManageMobileFeatureConfigs" boolean NULL; +-- +-- Create model mobilefeatureconfig +-- +CREATE TABLE "MobileFeatureConfig" ("commonPhone" text NULL, "ticketSubmittingIsDisabled" boolean NULL, "onlyGreaterThanPreviousMeterReadingIsEnabled" boolean 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 constraint mobilefeatureconfig_unique_organization on model mobilefeatureconfig +-- +CREATE UNIQUE INDEX "mobilefeatureconfig_unique_organization" ON "MobileFeatureConfig" ("organization") WHERE "deletedAt" IS NULL; +CREATE INDEX "MobileFeatureConfigHistoryRecord_history_id_9bb4c2ac" ON "MobileFeatureConfigHistoryRecord" ("history_id"); +ALTER TABLE "MobileFeatureConfig" ADD CONSTRAINT "MobileFeatureConfig_createdBy_cdc8306c_fk_User_id" FOREIGN KEY ("createdBy") REFERENCES "User" ("id") DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE "MobileFeatureConfig" ADD CONSTRAINT "MobileFeatureConfig_organization_68b5d9ad_fk_Organization_id" FOREIGN KEY ("organization") REFERENCES "Organization" ("id") DEFERRABLE INITIALLY DEFERRED; +ALTER TABLE "MobileFeatureConfig" ADD CONSTRAINT "MobileFeatureConfig_updatedBy_f1a54d9d_fk_User_id" FOREIGN KEY ("updatedBy") REFERENCES "User" ("id") DEFERRABLE INITIALLY DEFERRED; +CREATE INDEX "MobileFeatureConfig_createdAt_43196f84" ON "MobileFeatureConfig" ("createdAt"); +CREATE INDEX "MobileFeatureConfig_updatedAt_c8c7ade9" ON "MobileFeatureConfig" ("updatedAt"); +CREATE INDEX "MobileFeatureConfig_deletedAt_0c7426d3" ON "MobileFeatureConfig" ("deletedAt"); +CREATE INDEX "MobileFeatureConfig_createdBy_cdc8306c" ON "MobileFeatureConfig" ("createdBy"); +CREATE INDEX "MobileFeatureConfig_organization_68b5d9ad" ON "MobileFeatureConfig" ("organization"); +CREATE INDEX "MobileFeatureConfig_updatedBy_f1a54d9d" ON "MobileFeatureConfig" ("updatedBy"); +-- +-- [CUSTOM] Set Statement Timeout to some large amount - 25 min (25 * 60 => 1500 sec) +-- +SET statement_timeout = '1500s'; +UPDATE "OrganizationEmployeeRole" +SET "canManageMobileFeatureConfigs" = true +WHERE "name" = 'employee.role.Administrator.name'; +-- +-- [CUSTOM] Revert Statement Timeout to default amount - 10 secs +-- +SET statement_timeout = '10s'; + +COMMIT; + + `) +} + +exports.down = async (knex) => { + await knex.raw(` + BEGIN; +-- +-- Create constraint mobilefeatureconfig_unique_organization on model mobilefeatureconfig +-- +DROP INDEX IF EXISTS "mobilefeatureconfig_unique_organization"; +-- +-- Create model mobilefeatureconfig +-- +DROP TABLE "MobileFeatureConfig" CASCADE; +-- +-- Add field canManageMobileFeatureConfigs to organizationemployeerolehistoryrecord +-- +ALTER TABLE "OrganizationEmployeeRoleHistoryRecord" DROP COLUMN "canManageMobileFeatureConfigs" CASCADE; +-- +-- Add field canManageMobileFeatureConfigs to organizationemployeerole +-- +ALTER TABLE "OrganizationEmployeeRole" DROP COLUMN "canManageMobileFeatureConfigs" CASCADE; +-- +-- Create model mobilefeatureconfighistoryrecord +-- +DROP TABLE "MobileFeatureConfigHistoryRecord" CASCADE; +COMMIT; + + `) +} diff --git a/apps/condo/pages/settings/index.tsx b/apps/condo/pages/settings/index.tsx index 1e34bcd6626..993029df073 100644 --- a/apps/condo/pages/settings/index.tsx +++ b/apps/condo/pages/settings/index.tsx @@ -3,6 +3,7 @@ import get from 'lodash/get' import Head from 'next/head' import React, { CSSProperties, useMemo } from 'react' +import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext' import { useIntl } from '@open-condo/next/intl' import { useOrganization } from '@open-condo/next/organization' @@ -10,8 +11,10 @@ import { PageHeader, PageWrapper } from '@condo/domains/common/components/contai import { TablePageContent } from '@condo/domains/common/components/containers/BaseLayout/BaseLayout' import { hasFeature } from '@condo/domains/common/components/containers/FeatureFlag' import { ControlRoomSettingsContent } from '@condo/domains/common/components/settings/ControlRoomSettingsContent' +import { MobileFeatureConfigContent } from '@condo/domains/common/components/settings/MobileFeatureConfigContent' import { SettingsPageContent } from '@condo/domains/common/components/settings/SettingsPageContent' import { SettingsTabPaneDescriptor } from '@condo/domains/common/components/settings/Tabs' +import { MOBILE_FEATURE_CONFIGURATION } from '@condo/domains/common/constants/featureflags' import { SETTINGS_TAB_CONTACT_ROLES, SETTINGS_TAB_PAYMENT_DETAILS, @@ -20,6 +23,7 @@ import { SETTINGS_TAB_CONTROL_ROOM, SETTINGS_TAB_PROPERTY_SCOPE, SETTINGS_TAB_EMPLOYEE_ROLES, + SETTINGS_TAB_MOBILE_FEATURE_CONFIG, } from '@condo/domains/common/constants/settingsTabs' import { ContactRolesSettingsContent } from '@condo/domains/contact/components/contactRoles/ContactRolesSettingsContent' import { EmployeeRolesSettingsContent } from '@condo/domains/organization/components/EmployeeRolesSettingsContent' @@ -32,7 +36,6 @@ import { } from '@condo/domains/ticket/components/TicketPropertyHint/SettingsContent' - const TITLE_STYLES: CSSProperties = { margin: 0 } const ALWAYS_AVAILABLE_TABS = [SETTINGS_TAB_PROPERTY_HINT, SETTINGS_TAB_PROPERTY_SCOPE, SETTINGS_TAB_EMPLOYEE_ROLES, SETTINGS_TAB_PAYMENT_DETAILS, SETTINGS_TAB_CONTROL_ROOM] @@ -47,16 +50,21 @@ const SettingsPage: React.FC = () => { const ControlRoomTitle = intl.formatMessage({ id: 'ControlRoom' }) const PropertyScopeTitle = intl.formatMessage({ id: 'pages.condo.settings.propertyScope.title' }) const EmployeeRolesTitle = intl.formatMessage({ id: 'EmployeeRoles' }) + const MobileFeatureConfigTitle = intl.formatMessage({ id: 'pages.condo.settings.barItem.MobileFeatureConfig' }) const hasSubscriptionFeature = hasFeature('subscription') - + const { useFlag } = useFeatureFlags() + const hasMobileFeatureConfigurationFeature = useFlag(MOBILE_FEATURE_CONFIGURATION) + const userOrganization = useOrganization() const canManageContactRoles = useMemo(() => get(userOrganization, ['link', 'role', 'canManageContactRoles']), [userOrganization]) + const canManageMobileFeatureConfigsRoles = useMemo(() => get(userOrganization, ['link', 'role', 'canManageContactRoles']), [userOrganization]) const tabKeysToDisplay = useMemo(() => { const result = ALWAYS_AVAILABLE_TABS if (hasSubscriptionFeature) result.push(SETTINGS_TAB_SUBSCRIPTION) if (canManageContactRoles) result.push(SETTINGS_TAB_CONTACT_ROLES) + if (canManageMobileFeatureConfigsRoles) result.push(SETTINGS_TAB_MOBILE_FEATURE_CONFIG) return result }, [hasSubscriptionFeature, canManageContactRoles]) @@ -98,8 +106,13 @@ const SettingsPage: React.FC = () => { title: ControlRoomTitle, content: , }, + canManageMobileFeatureConfigsRoles && hasMobileFeatureConfigurationFeature && { + key: SETTINGS_TAB_MOBILE_FEATURE_CONFIG, + title: MobileFeatureConfigTitle, + content: , + }, ].filter(Boolean), - [hasSubscriptionFeature, SubscriptionTitle, HintTitle, PropertyScopeTitle, EmployeeRolesTitle, DetailsTitle, canManageContactRoles, RolesTitle, ControlRoomTitle], + [hasSubscriptionFeature, hasMobileFeatureConfigurationFeature, SubscriptionTitle, HintTitle, PropertyScopeTitle, EmployeeRolesTitle, DetailsTitle, canManageContactRoles, RolesTitle, ControlRoomTitle, MobileFeatureConfigTitle], ) const titleContent = useMemo(() => ( diff --git a/apps/condo/pages/settings/mobileFeatureConfig/onlyProgressionMeterReadings/index.tsx b/apps/condo/pages/settings/mobileFeatureConfig/onlyProgressionMeterReadings/index.tsx new file mode 100644 index 00000000000..a14aa30428b --- /dev/null +++ b/apps/condo/pages/settings/mobileFeatureConfig/onlyProgressionMeterReadings/index.tsx @@ -0,0 +1,61 @@ +import { Col, Row, Typography } from 'antd' +import { Gutter } from 'antd/es/grid/row' +import get from 'lodash/get' +import Head from 'next/head' +import React from 'react' + +import { useIntl } from '@open-condo/next/intl' +import { useOrganization } from '@open-condo/next/organization' + +import { PageContent, PageHeader, PageWrapper } from '@condo/domains/common/components/containers/BaseLayout' +import { Loader } from '@condo/domains/common/components/Loader' +import { + OnlyProgressionMeterReadingsForm, +} from '@condo/domains/settings/components/ticketSubmitting/OnlyProgressionMeterReadingsForm' +import { MobileFeatureConfig } from '@condo/domains/settings/utils/clientSchema' + +const ROW_GUTTER: [Gutter, Gutter] = [0, 60] + +const OnlyProgressionMeterReadingsContent: React.FC = () => { + const intl = useIntl() + const PageTitle = intl.formatMessage({ id: 'pages.condo.settings.barItem.MobileFeatureConfig.OnlyProgressionMeterReadings.title' }) + + const userOrganization = useOrganization() + const userOrganizationId = get(userOrganization, ['organization', 'id'], null) + + const { obj: mobileConfig, loading } = MobileFeatureConfig.useObject({ + where: { + organization: { id: userOrganizationId }, + }, + }) + return loading ? : ( + <> + {PageTitle}} /> + + + + + + + ) +} + +const TicketSubmittingPage: React.FC = () => { + const intl = useIntl() + const PageTitle = intl.formatMessage({ id: 'pages.condo.settings.ticketDeadlines.pageTitle' }) + + return ( + <> + + {PageTitle} + + + + + + + + ) +} + +export default TicketSubmittingPage diff --git a/apps/condo/pages/settings/mobileFeatureConfig/ticketSubmitting/index.tsx b/apps/condo/pages/settings/mobileFeatureConfig/ticketSubmitting/index.tsx new file mode 100644 index 00000000000..10891dd7cae --- /dev/null +++ b/apps/condo/pages/settings/mobileFeatureConfig/ticketSubmitting/index.tsx @@ -0,0 +1,61 @@ +import { Col, Row, Typography } from 'antd' +import { Gutter } from 'antd/es/grid/row' +import get from 'lodash/get' +import Head from 'next/head' +import React from 'react' + +import { useIntl } from '@open-condo/next/intl' +import { useOrganization } from '@open-condo/next/organization' + +import { PageContent, PageHeader, PageWrapper } from '@condo/domains/common/components/containers/BaseLayout' +import { Loader } from '@condo/domains/common/components/Loader' +import { + TicketSubmittingSettingsForm, +} from '@condo/domains/settings/components/ticketSubmitting/TicketSubmittingSettingsForm' +import { MobileFeatureConfig } from '@condo/domains/settings/utils/clientSchema' + +const ROW_GUTTER: [Gutter, Gutter] = [0, 60] + +const TicketSubmittingContent: React.FC = () => { + const intl = useIntl() + const PageTitle = intl.formatMessage({ id: 'pages.condo.settings.mobileFeatureConfig.submittingPeriod.pageTitle' }) + + const userOrganization = useOrganization() + const userOrganizationId = get(userOrganization, ['organization', 'id'], null) + + const { obj: mobileConfig, loading } = MobileFeatureConfig.useObject({ + where: { + organization: { id: userOrganizationId }, + }, + }) + return loading ? : ( + <> + {PageTitle}} /> + + + + + + + ) +} + +const TicketSubmittingPage: React.FC = () => { + const intl = useIntl() + const PageTitle = intl.formatMessage({ id: 'pages.condo.settings.ticketDeadlines.pageTitle' }) + + return ( + <> + + {PageTitle} + + + + + + + + ) +} + +export default TicketSubmittingPage diff --git a/apps/condo/schema.graphql b/apps/condo/schema.graphql index 6e6bd3c6914..cc4880ddfc2 100644 --- a/apps/condo/schema.graphql +++ b/apps/condo/schema.graphql @@ -4632,6 +4632,7 @@ type OrganizationEmployeeRoleHistoryRecord { canManageNewsItemTemplates: Boolean canManageCallRecords: Boolean canDownloadCallRecords: Boolean + canManageMobileFeatureConfigs: Boolean id: ID! v: Int createdAt: String @@ -4772,6 +4773,8 @@ input OrganizationEmployeeRoleHistoryRecordWhereInput { canManageCallRecords_not: Boolean canDownloadCallRecords: Boolean canDownloadCallRecords_not: Boolean + canManageMobileFeatureConfigs: Boolean + canManageMobileFeatureConfigs_not: Boolean id: ID id_not: ID id_in: [ID] @@ -4911,6 +4914,8 @@ enum SortOrganizationEmployeeRoleHistoryRecordsBy { canManageCallRecords_DESC canDownloadCallRecords_ASC canDownloadCallRecords_DESC + canManageMobileFeatureConfigs_ASC + canManageMobileFeatureConfigs_DESC id_ASC id_DESC v_ASC @@ -4964,6 +4969,7 @@ input OrganizationEmployeeRoleHistoryRecordUpdateInput { canManageNewsItemTemplates: Boolean canManageCallRecords: Boolean canDownloadCallRecords: Boolean + canManageMobileFeatureConfigs: Boolean v: Int createdAt: String updatedAt: String @@ -5018,6 +5024,7 @@ input OrganizationEmployeeRoleHistoryRecordCreateInput { canManageNewsItemTemplates: Boolean canManageCallRecords: Boolean canDownloadCallRecords: Boolean + canManageMobileFeatureConfigs: Boolean v: Int createdAt: String updatedAt: String @@ -5101,6 +5108,7 @@ type OrganizationEmployeeRole { canManageNewsItemTemplates: Boolean canManageCallRecords: Boolean canDownloadCallRecords: Boolean + canManageMobileFeatureConfigs: Boolean id: ID! v: Int createdAt: String @@ -5229,6 +5237,8 @@ input OrganizationEmployeeRoleWhereInput { canManageCallRecords_not: Boolean canDownloadCallRecords: Boolean canDownloadCallRecords_not: Boolean + canManageMobileFeatureConfigs: Boolean + canManageMobileFeatureConfigs_not: Boolean id: ID id_not: ID id_in: [ID] @@ -5350,6 +5360,8 @@ enum SortOrganizationEmployeeRolesBy { canManageCallRecords_DESC canDownloadCallRecords_ASC canDownloadCallRecords_DESC + canManageMobileFeatureConfigs_ASC + canManageMobileFeatureConfigs_DESC id_ASC id_DESC v_ASC @@ -5402,6 +5414,7 @@ input OrganizationEmployeeRoleUpdateInput { canManageNewsItemTemplates: Boolean canManageCallRecords: Boolean canDownloadCallRecords: Boolean + canManageMobileFeatureConfigs: Boolean v: Int createdAt: String updatedAt: String @@ -5452,6 +5465,7 @@ input OrganizationEmployeeRoleCreateInput { canManageNewsItemTemplates: Boolean canManageCallRecords: Boolean canDownloadCallRecords: Boolean + canManageMobileFeatureConfigs: Boolean v: Int createdAt: String updatedAt: String @@ -62583,6 +62597,446 @@ input B2BAppAccessRightSetsCreateInput { data: B2BAppAccessRightSetCreateInput } +enum MobileFeatureConfigHistoryRecordHistoryActionType { + c + u + d +} + +""" A keystone list """ +type MobileFeatureConfigHistoryRecord { + """ + This virtual field will be resolved in one of the following ways (in this order): + 1. Execution of 'labelResolver' set on the MobileFeatureConfigHistoryRecord List config, or + 2. As an alias to the field set on 'labelField' in the MobileFeatureConfigHistoryRecord List config, or + 3. As an alias to a 'name' field on the MobileFeatureConfigHistoryRecord List (if one exists), or + 4. As an alias to the 'id' field on the MobileFeatureConfigHistoryRecord List. + """ + _label_: String + organization: String + commonPhone: String + ticketSubmittingIsDisabled: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled: 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: MobileFeatureConfigHistoryRecordHistoryActionType + history_id: String +} + +input MobileFeatureConfigHistoryRecordWhereInput { + AND: [MobileFeatureConfigHistoryRecordWhereInput] + OR: [MobileFeatureConfigHistoryRecordWhereInput] + organization: String + organization_not: String + organization_in: [String] + organization_not_in: [String] + commonPhone: String + commonPhone_not: String + commonPhone_contains: String + commonPhone_not_contains: String + commonPhone_starts_with: String + commonPhone_not_starts_with: String + commonPhone_ends_with: String + commonPhone_not_ends_with: String + commonPhone_i: String + commonPhone_not_i: String + commonPhone_contains_i: String + commonPhone_not_contains_i: String + commonPhone_starts_with_i: String + commonPhone_not_starts_with_i: String + commonPhone_ends_with_i: String + commonPhone_not_ends_with_i: String + commonPhone_in: [String] + commonPhone_not_in: [String] + ticketSubmittingIsDisabled: Boolean + ticketSubmittingIsDisabled_not: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled_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: MobileFeatureConfigHistoryRecordHistoryActionType + history_action_not: MobileFeatureConfigHistoryRecordHistoryActionType + history_action_in: [MobileFeatureConfigHistoryRecordHistoryActionType] + history_action_not_in: [MobileFeatureConfigHistoryRecordHistoryActionType] + history_id: String + history_id_not: String + history_id_in: [String] + history_id_not_in: [String] +} + +input MobileFeatureConfigHistoryRecordWhereUniqueInput { + id: ID! +} + +enum SortMobileFeatureConfigHistoryRecordsBy { + commonPhone_ASC + commonPhone_DESC + ticketSubmittingIsDisabled_ASC + ticketSubmittingIsDisabled_DESC + onlyGreaterThanPreviousMeterReadingIsEnabled_ASC + onlyGreaterThanPreviousMeterReadingIsEnabled_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 MobileFeatureConfigHistoryRecordUpdateInput { + organization: String + commonPhone: String + ticketSubmittingIsDisabled: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled: 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: MobileFeatureConfigHistoryRecordHistoryActionType + history_id: String +} + +input MobileFeatureConfigHistoryRecordsUpdateInput { + id: ID! + data: MobileFeatureConfigHistoryRecordUpdateInput +} + +input MobileFeatureConfigHistoryRecordCreateInput { + organization: String + commonPhone: String + ticketSubmittingIsDisabled: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled: 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: MobileFeatureConfigHistoryRecordHistoryActionType + history_id: String +} + +input MobileFeatureConfigHistoryRecordsCreateInput { + data: MobileFeatureConfigHistoryRecordCreateInput +} + +""" Manages availability of some features in mobile application, stores data required in disabled state of a feature. +""" +type MobileFeatureConfig { + """ + This virtual field will be resolved in one of the following ways (in this order): + 1. Execution of 'labelResolver' set on the MobileFeatureConfig List config, or + 2. As an alias to the field set on 'labelField' in the MobileFeatureConfig List config, or + 3. As an alias to a 'name' field on the MobileFeatureConfig List (if one exists), or + 4. As an alias to the 'id' field on the MobileFeatureConfig List. + """ + _label_: String + + """ Ref to the organization. The object will be deleted if the organization ceases to exist + """ + organization: Organization + + """ Phone number where the organization wants to receive common calls """ + commonPhone: String + + """ Disabling the ability to send tickets by the user of the resident's mobile application. Instead, he will be shown a screen with phones where he can contact to send a request + """ + ticketSubmittingIsDisabled: Boolean + + """ Restricts residents to submit only meter readings, that are greater than previous. Restrictions apply to mobile phones only. + """ + onlyGreaterThanPreviousMeterReadingIsEnabled: Boolean + + """ Can be used to store some meta information for mobile applications. """ + 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 MobileFeatureConfigWhereInput { + AND: [MobileFeatureConfigWhereInput] + OR: [MobileFeatureConfigWhereInput] + organization: OrganizationWhereInput + organization_is_null: Boolean + commonPhone: String + commonPhone_not: String + commonPhone_contains: String + commonPhone_not_contains: String + commonPhone_starts_with: String + commonPhone_not_starts_with: String + commonPhone_ends_with: String + commonPhone_not_ends_with: String + commonPhone_i: String + commonPhone_not_i: String + commonPhone_contains_i: String + commonPhone_not_contains_i: String + commonPhone_starts_with_i: String + commonPhone_not_starts_with_i: String + commonPhone_ends_with_i: String + commonPhone_not_ends_with_i: String + commonPhone_in: [String] + commonPhone_not_in: [String] + ticketSubmittingIsDisabled: Boolean + ticketSubmittingIsDisabled_not: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled_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 MobileFeatureConfigWhereUniqueInput { + id: ID! +} + +enum SortMobileFeatureConfigsBy { + organization_ASC + organization_DESC + commonPhone_ASC + commonPhone_DESC + ticketSubmittingIsDisabled_ASC + ticketSubmittingIsDisabled_DESC + onlyGreaterThanPreviousMeterReadingIsEnabled_ASC + onlyGreaterThanPreviousMeterReadingIsEnabled_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 MobileFeatureConfigUpdateInput { + organization: OrganizationRelateToOneInput + commonPhone: String + ticketSubmittingIsDisabled: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled: Boolean + meta: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: UserRelateToOneInput + updatedBy: UserRelateToOneInput + deletedAt: String + newId: String + dv: Int + sender: SenderFieldInput +} + +input MobileFeatureConfigsUpdateInput { + id: ID! + data: MobileFeatureConfigUpdateInput +} + +input MobileFeatureConfigCreateInput { + organization: OrganizationRelateToOneInput + commonPhone: String + ticketSubmittingIsDisabled: Boolean + onlyGreaterThanPreviousMeterReadingIsEnabled: Boolean + meta: JSON + v: Int + createdAt: String + updatedAt: String + createdBy: UserRelateToOneInput + updatedBy: UserRelateToOneInput + deletedAt: String + newId: String + dv: Int + sender: SenderFieldInput +} + +input MobileFeatureConfigsCreateInput { + data: MobileFeatureConfigCreateInput +} + enum WebhookHistoryRecordHistoryActionType { c u @@ -68157,6 +68611,36 @@ type Query { """ Retrieve the meta-data for the B2BAppAccessRightSet list. """ _B2BAppAccessRightSetsMeta: _ListMeta + """ Search for all MobileFeatureConfigHistoryRecord items which match the where clause. + """ + allMobileFeatureConfigHistoryRecords(where: MobileFeatureConfigHistoryRecordWhereInput, search: String, sortBy: [SortMobileFeatureConfigHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): [MobileFeatureConfigHistoryRecord] + + """ Search for the MobileFeatureConfigHistoryRecord item with the matching ID. + """ + MobileFeatureConfigHistoryRecord(where: MobileFeatureConfigHistoryRecordWhereUniqueInput!): MobileFeatureConfigHistoryRecord + + """ Perform a meta-query on all MobileFeatureConfigHistoryRecord items which match the where clause. + """ + _allMobileFeatureConfigHistoryRecordsMeta(where: MobileFeatureConfigHistoryRecordWhereInput, search: String, sortBy: [SortMobileFeatureConfigHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): _QueryMeta + + """ Retrieve the meta-data for the MobileFeatureConfigHistoryRecord list. + """ + _MobileFeatureConfigHistoryRecordsMeta: _ListMeta + + """ Search for all MobileFeatureConfig items which match the where clause. + """ + allMobileFeatureConfigs(where: MobileFeatureConfigWhereInput, search: String, sortBy: [SortMobileFeatureConfigsBy!], orderBy: String, first: Int, skip: Int): [MobileFeatureConfig] + + """ Search for the MobileFeatureConfig item with the matching ID. """ + MobileFeatureConfig(where: MobileFeatureConfigWhereUniqueInput!): MobileFeatureConfig + + """ Perform a meta-query on all MobileFeatureConfig items which match the where clause. + """ + _allMobileFeatureConfigsMeta(where: MobileFeatureConfigWhereInput, search: String, sortBy: [SortMobileFeatureConfigsBy!], orderBy: String, first: Int, skip: Int): _QueryMeta + + """ Retrieve the meta-data for the MobileFeatureConfig list. """ + _MobileFeatureConfigsMeta: _ListMeta + """ Search for all WebhookHistoryRecord items which match the where clause. """ allWebhookHistoryRecords(where: WebhookHistoryRecordWhereInput, search: String, sortBy: [SortWebhookHistoryRecordsBy!], orderBy: String, first: Int, skip: Int): [WebhookHistoryRecord] @@ -72397,6 +72881,42 @@ type Mutation { """ Delete multiple B2BAppAccessRightSet items by ID. """ deleteB2BAppAccessRightSets(ids: [ID!]): [B2BAppAccessRightSet] + """ Create a single MobileFeatureConfigHistoryRecord item. """ + createMobileFeatureConfigHistoryRecord(data: MobileFeatureConfigHistoryRecordCreateInput): MobileFeatureConfigHistoryRecord + + """ Create multiple MobileFeatureConfigHistoryRecord items. """ + createMobileFeatureConfigHistoryRecords(data: [MobileFeatureConfigHistoryRecordsCreateInput]): [MobileFeatureConfigHistoryRecord] + + """ Update a single MobileFeatureConfigHistoryRecord item by ID. """ + updateMobileFeatureConfigHistoryRecord(id: ID!, data: MobileFeatureConfigHistoryRecordUpdateInput): MobileFeatureConfigHistoryRecord + + """ Update multiple MobileFeatureConfigHistoryRecord items by ID. """ + updateMobileFeatureConfigHistoryRecords(data: [MobileFeatureConfigHistoryRecordsUpdateInput]): [MobileFeatureConfigHistoryRecord] + + """ Delete a single MobileFeatureConfigHistoryRecord item by ID. """ + deleteMobileFeatureConfigHistoryRecord(id: ID!): MobileFeatureConfigHistoryRecord + + """ Delete multiple MobileFeatureConfigHistoryRecord items by ID. """ + deleteMobileFeatureConfigHistoryRecords(ids: [ID!]): [MobileFeatureConfigHistoryRecord] + + """ Create a single MobileFeatureConfig item. """ + createMobileFeatureConfig(data: MobileFeatureConfigCreateInput): MobileFeatureConfig + + """ Create multiple MobileFeatureConfig items. """ + createMobileFeatureConfigs(data: [MobileFeatureConfigsCreateInput]): [MobileFeatureConfig] + + """ Update a single MobileFeatureConfig item by ID. """ + updateMobileFeatureConfig(id: ID!, data: MobileFeatureConfigUpdateInput): MobileFeatureConfig + + """ Update multiple MobileFeatureConfig items by ID. """ + updateMobileFeatureConfigs(data: [MobileFeatureConfigsUpdateInput]): [MobileFeatureConfig] + + """ Delete a single MobileFeatureConfig item by ID. """ + deleteMobileFeatureConfig(id: ID!): MobileFeatureConfig + + """ Delete multiple MobileFeatureConfig items by ID. """ + deleteMobileFeatureConfigs(ids: [ID!]): [MobileFeatureConfig] + """ Create a single WebhookHistoryRecord item. """ createWebhookHistoryRecord(data: WebhookHistoryRecordCreateInput): WebhookHistoryRecord diff --git a/apps/condo/schema.ts b/apps/condo/schema.ts index c9c86e11128..bbfb422c018 100644 --- a/apps/condo/schema.ts +++ b/apps/condo/schema.ts @@ -29656,6 +29656,380 @@ export type MiniAppOutput = { menuCategory?: Maybe; }; +/** Manages availability of some features in mobile application, stores data required in disabled state of a feature. */ +export type MobileFeatureConfig = { + __typename?: 'MobileFeatureConfig'; + /** + * This virtual field will be resolved in one of the following ways (in this order): + * 1. Execution of 'labelResolver' set on the MobileFeatureConfig List config, or + * 2. As an alias to the field set on 'labelField' in the MobileFeatureConfig List config, or + * 3. As an alias to a 'name' field on the MobileFeatureConfig List (if one exists), or + * 4. As an alias to the 'id' field on the MobileFeatureConfig List. + */ + _label_?: Maybe; + /** Ref to the organization. The object will be deleted if the organization ceases to exist */ + organization?: Maybe; + /** Phone number where the organization wants to receive common calls */ + commonPhone?: Maybe; + /** Disabling the ability to send tickets by the user of the resident's mobile application. Instead, he will be shown a screen with phones where he can contact to send a request */ + ticketSubmittingIsDisabled?: Maybe; + /** Restricts residents to submit only meter readings, that are greater than previous. Restrictions apply to mobile phones only. */ + onlyGreaterThanPreviousMeterReadingIsEnabled?: Maybe; + /** Can be used to store some meta information for mobile applications. */ + 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 MobileFeatureConfigCreateInput = { + organization?: Maybe; + commonPhone?: Maybe; + ticketSubmittingIsDisabled?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled?: Maybe; + meta?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; +}; + +/** A keystone list */ +export type MobileFeatureConfigHistoryRecord = { + __typename?: 'MobileFeatureConfigHistoryRecord'; + /** + * This virtual field will be resolved in one of the following ways (in this order): + * 1. Execution of 'labelResolver' set on the MobileFeatureConfigHistoryRecord List config, or + * 2. As an alias to the field set on 'labelField' in the MobileFeatureConfigHistoryRecord List config, or + * 3. As an alias to a 'name' field on the MobileFeatureConfigHistoryRecord List (if one exists), or + * 4. As an alias to the 'id' field on the MobileFeatureConfigHistoryRecord List. + */ + _label_?: Maybe; + organization?: Maybe; + commonPhone?: Maybe; + ticketSubmittingIsDisabled?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled?: 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 MobileFeatureConfigHistoryRecordCreateInput = { + organization?: Maybe; + commonPhone?: Maybe; + ticketSubmittingIsDisabled?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled?: 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 MobileFeatureConfigHistoryRecordHistoryActionType { + C = 'c', + U = 'u', + D = 'd' +} + +export type MobileFeatureConfigHistoryRecordUpdateInput = { + organization?: Maybe; + commonPhone?: Maybe; + ticketSubmittingIsDisabled?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled?: 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 MobileFeatureConfigHistoryRecordWhereInput = { + AND?: Maybe>>; + OR?: Maybe>>; + organization?: Maybe; + organization_not?: Maybe; + organization_in?: Maybe>>; + organization_not_in?: Maybe>>; + commonPhone?: Maybe; + commonPhone_not?: Maybe; + commonPhone_contains?: Maybe; + commonPhone_not_contains?: Maybe; + commonPhone_starts_with?: Maybe; + commonPhone_not_starts_with?: Maybe; + commonPhone_ends_with?: Maybe; + commonPhone_not_ends_with?: Maybe; + commonPhone_i?: Maybe; + commonPhone_not_i?: Maybe; + commonPhone_contains_i?: Maybe; + commonPhone_not_contains_i?: Maybe; + commonPhone_starts_with_i?: Maybe; + commonPhone_not_starts_with_i?: Maybe; + commonPhone_ends_with_i?: Maybe; + commonPhone_not_ends_with_i?: Maybe; + commonPhone_in?: Maybe>>; + commonPhone_not_in?: Maybe>>; + ticketSubmittingIsDisabled?: Maybe; + ticketSubmittingIsDisabled_not?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled_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 MobileFeatureConfigHistoryRecordWhereUniqueInput = { + id: Scalars['ID']; +}; + +export type MobileFeatureConfigHistoryRecordsCreateInput = { + data?: Maybe; +}; + +export type MobileFeatureConfigHistoryRecordsUpdateInput = { + id: Scalars['ID']; + data?: Maybe; +}; + +export type MobileFeatureConfigUpdateInput = { + organization?: Maybe; + commonPhone?: Maybe; + ticketSubmittingIsDisabled?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled?: Maybe; + meta?: Maybe; + v?: Maybe; + createdAt?: Maybe; + updatedAt?: Maybe; + createdBy?: Maybe; + updatedBy?: Maybe; + deletedAt?: Maybe; + newId?: Maybe; + dv?: Maybe; + sender?: Maybe; +}; + +export type MobileFeatureConfigWhereInput = { + AND?: Maybe>>; + OR?: Maybe>>; + organization?: Maybe; + organization_is_null?: Maybe; + commonPhone?: Maybe; + commonPhone_not?: Maybe; + commonPhone_contains?: Maybe; + commonPhone_not_contains?: Maybe; + commonPhone_starts_with?: Maybe; + commonPhone_not_starts_with?: Maybe; + commonPhone_ends_with?: Maybe; + commonPhone_not_ends_with?: Maybe; + commonPhone_i?: Maybe; + commonPhone_not_i?: Maybe; + commonPhone_contains_i?: Maybe; + commonPhone_not_contains_i?: Maybe; + commonPhone_starts_with_i?: Maybe; + commonPhone_not_starts_with_i?: Maybe; + commonPhone_ends_with_i?: Maybe; + commonPhone_not_ends_with_i?: Maybe; + commonPhone_in?: Maybe>>; + commonPhone_not_in?: Maybe>>; + ticketSubmittingIsDisabled?: Maybe; + ticketSubmittingIsDisabled_not?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled?: Maybe; + onlyGreaterThanPreviousMeterReadingIsEnabled_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 MobileFeatureConfigWhereUniqueInput = { + id: Scalars['ID']; +}; + +export type MobileFeatureConfigsCreateInput = { + data?: Maybe; +}; + +export type MobileFeatureConfigsUpdateInput = { + id: Scalars['ID']; + data?: Maybe; +}; + /** Information about resident's payment for single or multiple services/receipts */ export type MultiPayment = { __typename?: 'MultiPayment'; @@ -33158,6 +33532,30 @@ export type Mutation = { deleteB2BAppAccessRightSet?: Maybe; /** Delete multiple B2BAppAccessRightSet items by ID. */ deleteB2BAppAccessRightSets?: Maybe>>; + /** Create a single MobileFeatureConfigHistoryRecord item. */ + createMobileFeatureConfigHistoryRecord?: Maybe; + /** Create multiple MobileFeatureConfigHistoryRecord items. */ + createMobileFeatureConfigHistoryRecords?: Maybe>>; + /** Update a single MobileFeatureConfigHistoryRecord item by ID. */ + updateMobileFeatureConfigHistoryRecord?: Maybe; + /** Update multiple MobileFeatureConfigHistoryRecord items by ID. */ + updateMobileFeatureConfigHistoryRecords?: Maybe>>; + /** Delete a single MobileFeatureConfigHistoryRecord item by ID. */ + deleteMobileFeatureConfigHistoryRecord?: Maybe; + /** Delete multiple MobileFeatureConfigHistoryRecord items by ID. */ + deleteMobileFeatureConfigHistoryRecords?: Maybe>>; + /** Create a single MobileFeatureConfig item. */ + createMobileFeatureConfig?: Maybe; + /** Create multiple MobileFeatureConfig items. */ + createMobileFeatureConfigs?: Maybe>>; + /** Update a single MobileFeatureConfig item by ID. */ + updateMobileFeatureConfig?: Maybe; + /** Update multiple MobileFeatureConfig items by ID. */ + updateMobileFeatureConfigs?: Maybe>>; + /** Delete a single MobileFeatureConfig item by ID. */ + deleteMobileFeatureConfig?: Maybe; + /** Delete multiple MobileFeatureConfig items by ID. */ + deleteMobileFeatureConfigs?: Maybe>>; /** Create a single WebhookHistoryRecord item. */ createWebhookHistoryRecord?: Maybe; /** Create multiple WebhookHistoryRecord items. */ @@ -42431,6 +42829,68 @@ export type MutationDeleteB2BAppAccessRightSetsArgs = { }; +export type MutationCreateMobileFeatureConfigHistoryRecordArgs = { + data?: Maybe; +}; + + +export type MutationCreateMobileFeatureConfigHistoryRecordsArgs = { + data?: Maybe>>; +}; + + +export type MutationUpdateMobileFeatureConfigHistoryRecordArgs = { + id: Scalars['ID']; + data?: Maybe; +}; + + +export type MutationUpdateMobileFeatureConfigHistoryRecordsArgs = { + data?: Maybe>>; +}; + + +export type MutationDeleteMobileFeatureConfigHistoryRecordArgs = { + id: Scalars['ID']; +}; + + +export type MutationDeleteMobileFeatureConfigHistoryRecordsArgs = { + ids?: Maybe>; +}; + + +export type MutationCreateMobileFeatureConfigArgs = { + data?: Maybe; +}; + + +export type MutationCreateMobileFeatureConfigsArgs = { + data?: Maybe>>; +}; + + +export type MutationUpdateMobileFeatureConfigArgs = { + id: Scalars['ID']; + data?: Maybe; +}; + + +export type MutationUpdateMobileFeatureConfigsArgs = { + data?: Maybe>>; +}; + + +export type MutationDeleteMobileFeatureConfigArgs = { + id: Scalars['ID']; +}; + + +export type MutationDeleteMobileFeatureConfigsArgs = { + ids?: Maybe>; +}; + + export type MutationCreateWebhookHistoryRecordArgs = { data?: Maybe; }; @@ -47206,6 +47666,7 @@ export type OrganizationEmployeeRole = { canManageNewsItemTemplates?: Maybe; canManageCallRecords?: Maybe; canDownloadCallRecords?: Maybe; + canManageMobileFeatureConfigs?: Maybe; id: Scalars['ID']; v?: Maybe; createdAt?: Maybe; @@ -47256,6 +47717,7 @@ export type OrganizationEmployeeRoleCreateInput = { canManageNewsItemTemplates?: Maybe; canManageCallRecords?: Maybe; canDownloadCallRecords?: Maybe; + canManageMobileFeatureConfigs?: Maybe; v?: Maybe; createdAt?: Maybe; updatedAt?: Maybe; @@ -47312,6 +47774,7 @@ export type OrganizationEmployeeRoleHistoryRecord = { canManageNewsItemTemplates?: Maybe; canManageCallRecords?: Maybe; canDownloadCallRecords?: Maybe; + canManageMobileFeatureConfigs?: Maybe; id: Scalars['ID']; v?: Maybe; createdAt?: Maybe; @@ -47362,6 +47825,7 @@ export type OrganizationEmployeeRoleHistoryRecordCreateInput = { canManageNewsItemTemplates?: Maybe; canManageCallRecords?: Maybe; canDownloadCallRecords?: Maybe; + canManageMobileFeatureConfigs?: Maybe; v?: Maybe; createdAt?: Maybe; updatedAt?: Maybe; @@ -47417,6 +47881,7 @@ export type OrganizationEmployeeRoleHistoryRecordUpdateInput = { canManageNewsItemTemplates?: Maybe; canManageCallRecords?: Maybe; canDownloadCallRecords?: Maybe; + canManageMobileFeatureConfigs?: Maybe; v?: Maybe; createdAt?: Maybe; updatedAt?: Maybe; @@ -47556,6 +48021,8 @@ export type OrganizationEmployeeRoleHistoryRecordWhereInput = { canManageCallRecords_not?: Maybe; canDownloadCallRecords?: Maybe; canDownloadCallRecords_not?: Maybe; + canManageMobileFeatureConfigs?: Maybe; + canManageMobileFeatureConfigs_not?: Maybe; id?: Maybe; id_not?: Maybe; id_in?: Maybe>>; @@ -47678,6 +48145,7 @@ export type OrganizationEmployeeRoleUpdateInput = { canManageNewsItemTemplates?: Maybe; canManageCallRecords?: Maybe; canDownloadCallRecords?: Maybe; + canManageMobileFeatureConfigs?: Maybe; v?: Maybe; createdAt?: Maybe; updatedAt?: Maybe; @@ -47794,6 +48262,8 @@ export type OrganizationEmployeeRoleWhereInput = { canManageCallRecords_not?: Maybe; canDownloadCallRecords?: Maybe; canDownloadCallRecords_not?: Maybe; + canManageMobileFeatureConfigs?: Maybe; + canManageMobileFeatureConfigs_not?: Maybe; id?: Maybe; id_not?: Maybe; id_in?: Maybe>>; @@ -55010,6 +55480,22 @@ export type Query = { _allB2BAppAccessRightSetsMeta?: Maybe<_QueryMeta>; /** Retrieve the meta-data for the B2BAppAccessRightSet list. */ _B2BAppAccessRightSetsMeta?: Maybe<_ListMeta>; + /** Search for all MobileFeatureConfigHistoryRecord items which match the where clause. */ + allMobileFeatureConfigHistoryRecords?: Maybe>>; + /** Search for the MobileFeatureConfigHistoryRecord item with the matching ID. */ + MobileFeatureConfigHistoryRecord?: Maybe; + /** Perform a meta-query on all MobileFeatureConfigHistoryRecord items which match the where clause. */ + _allMobileFeatureConfigHistoryRecordsMeta?: Maybe<_QueryMeta>; + /** Retrieve the meta-data for the MobileFeatureConfigHistoryRecord list. */ + _MobileFeatureConfigHistoryRecordsMeta?: Maybe<_ListMeta>; + /** Search for all MobileFeatureConfig items which match the where clause. */ + allMobileFeatureConfigs?: Maybe>>; + /** Search for the MobileFeatureConfig item with the matching ID. */ + MobileFeatureConfig?: Maybe; + /** Perform a meta-query on all MobileFeatureConfig items which match the where clause. */ + _allMobileFeatureConfigsMeta?: Maybe<_QueryMeta>; + /** Retrieve the meta-data for the MobileFeatureConfig list. */ + _MobileFeatureConfigsMeta?: Maybe<_ListMeta>; /** Search for all WebhookHistoryRecord items which match the where clause. */ allWebhookHistoryRecords?: Maybe>>; /** Search for the WebhookHistoryRecord item with the matching ID. */ @@ -60737,6 +61223,56 @@ export type Query_AllB2BAppAccessRightSetsMetaArgs = { }; +export type QueryAllMobileFeatureConfigHistoryRecordsArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryMobileFeatureConfigHistoryRecordArgs = { + where: MobileFeatureConfigHistoryRecordWhereUniqueInput; +}; + + +export type Query_AllMobileFeatureConfigHistoryRecordsMetaArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryAllMobileFeatureConfigsArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + +export type QueryMobileFeatureConfigArgs = { + where: MobileFeatureConfigWhereUniqueInput; +}; + + +export type Query_AllMobileFeatureConfigsMetaArgs = { + where?: Maybe; + search?: Maybe; + sortBy?: Maybe>; + orderBy?: Maybe; + first?: Maybe; + skip?: Maybe; +}; + + export type QueryAllWebhookHistoryRecordsArgs = { where?: Maybe; search?: Maybe; @@ -68150,6 +68686,58 @@ export enum SortMetersBy { DvDesc = 'dv_DESC' } +export enum SortMobileFeatureConfigHistoryRecordsBy { + CommonPhoneAsc = 'commonPhone_ASC', + CommonPhoneDesc = 'commonPhone_DESC', + TicketSubmittingIsDisabledAsc = 'ticketSubmittingIsDisabled_ASC', + TicketSubmittingIsDisabledDesc = 'ticketSubmittingIsDisabled_DESC', + OnlyGreaterThanPreviousMeterReadingIsEnabledAsc = 'onlyGreaterThanPreviousMeterReadingIsEnabled_ASC', + OnlyGreaterThanPreviousMeterReadingIsEnabledDesc = 'onlyGreaterThanPreviousMeterReadingIsEnabled_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 SortMobileFeatureConfigsBy { + OrganizationAsc = 'organization_ASC', + OrganizationDesc = 'organization_DESC', + CommonPhoneAsc = 'commonPhone_ASC', + CommonPhoneDesc = 'commonPhone_DESC', + TicketSubmittingIsDisabledAsc = 'ticketSubmittingIsDisabled_ASC', + TicketSubmittingIsDisabledDesc = 'ticketSubmittingIsDisabled_DESC', + OnlyGreaterThanPreviousMeterReadingIsEnabledAsc = 'onlyGreaterThanPreviousMeterReadingIsEnabled_ASC', + OnlyGreaterThanPreviousMeterReadingIsEnabledDesc = 'onlyGreaterThanPreviousMeterReadingIsEnabled_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 SortMultiPaymentHistoryRecordsBy { ExplicitFeeAsc = 'explicitFee_ASC', ExplicitFeeDesc = 'explicitFee_DESC', @@ -68850,6 +69438,8 @@ export enum SortOrganizationEmployeeRoleHistoryRecordsBy { CanManageCallRecordsDesc = 'canManageCallRecords_DESC', CanDownloadCallRecordsAsc = 'canDownloadCallRecords_ASC', CanDownloadCallRecordsDesc = 'canDownloadCallRecords_DESC', + CanManageMobileFeatureConfigsAsc = 'canManageMobileFeatureConfigs_ASC', + CanManageMobileFeatureConfigsDesc = 'canManageMobileFeatureConfigs_DESC', IdAsc = 'id_ASC', IdDesc = 'id_DESC', VAsc = 'v_ASC', @@ -68937,6 +69527,8 @@ export enum SortOrganizationEmployeeRolesBy { CanManageCallRecordsDesc = 'canManageCallRecords_DESC', CanDownloadCallRecordsAsc = 'canDownloadCallRecords_ASC', CanDownloadCallRecordsDesc = 'canDownloadCallRecords_DESC', + CanManageMobileFeatureConfigsAsc = 'canManageMobileFeatureConfigs_ASC', + CanManageMobileFeatureConfigsDesc = 'canManageMobileFeatureConfigs_DESC', IdAsc = 'id_ASC', IdDesc = 'id_DESC', VAsc = 'v_ASC',