Skip to content

Commit

Permalink
feat(condo): DOMA-8414 tour page (#4462)
Browse files Browse the repository at this point in the history
* feat(condo): DOMA-8414 added tour icon to side bar

* feat(condo): DOMA-8414 added basic tour page

* feat(condo): DOMA-8414 added basic ticket flow

* feat(condo): DOMA-8414 added tour flow for ticket and meter

* feat(condo): DOMA-8414 added billing step

* feat(condo): DOMA-8414 tour mobile screen

* feat(condo): DOMA-8414 added sync steps mutation

* feat(condo): DOMA-8414 sync organization steps

* feat(condo): DOMA-8414 disabled step tooltips

* fix(condo): DOMA-8414 open complete modal when createNews step completed

* feat(condo): DOMA-8414 added modals in technic app card

* feat(condo): DOMA-8414 added accesses and tests for SyncTourSteps mutation

* fix(condo): DOMA-8414 remove second step from active step from storage

* feat(condo): DOMA-8414 refactor code

* fix(condo): DOMA-8414 added useCompletedTourStepsData hook

* fix(condo): DOMA-8414 removed hook to useCompletedTourModals and returns modals from it

* fix(condo): DOMA-8414 added translations

* fix(condo): DOMA-8414 refactor useCompletedTourModals modals data getters

* fix(condo): DOMA-8414 added translations

* feat(condo): DOMA-8414 update SyncTourStepsService tests

* fix(condo): DOMA-8414 fix semgrep issues

* feat(condo): DOMA-8618 added analytics to tour events

* feat(condo): DOMA-8414 disable all iner todo steps instead first todo step

* feat(condo): DOMA-8414 added videos to tour page and moved links to env

* fix(condo): DOMA-8414 fixes after review

* feat(condo): DOMA-8626 new property form

* feat(condo): DOMA-8414 added video to map creation form

* feat(condo): DOMA-8312 added tour step to ticket and property form

* feat(condo): DOMA-8312 added hints on property map create

* feat(condo): DOMA-8414 update redirects after organization created

* feat(condo): DOMA-8414 redirect to tour after create organization

* feat(condo): DOMA-8414 added no address modal

* feat(condo): DOMA-8414 fixes after review

* chore(condo): DOMA-8414 recreate migration

* fix(condo): DOMA-8414 update tour video env

* feat(condo): DOMA-8414 added notFound content for property address search select and unit select

* chore(condo): DOMA-8414 recreate migration

* feat(condo): DOMA-8414 add README.md

* feat(condo): DOMA-8414 rename control room to tickets in side bar

* chore(condo): DOMA-8414 recreate migration

* feat(condo): DOMA-8644 added pics for technic card

* fix(condo): DOMA-8414 remove size value 32 from space props

* fix(condo): DOMA-8414 fix initial notFoundContent in AddressSuggestionsSearchInput

* fix(condo): DOMA-8414 fix link with icon spacing

* fix(condo): DOMA-8669 update base search input styles

* fix(condo): DOMA-8414 fixed technic app card images

* fix(condo): DOMA-8414 fix ticket empty list view

* fix(condo): DOMA-8414 fix translations

* fix(condo): DOMA-8414 fix redirect routes

* fix(condo): DOMA-8414 fix tooltip and focus
  • Loading branch information
nomerdvadcatpyat authored Mar 27, 2024
1 parent 1d0c94e commit 656c94e
Show file tree
Hide file tree
Showing 95 changed files with 3,641 additions and 416 deletions.
2 changes: 1 addition & 1 deletion .helm
Submodule .helm updated from d8e8d1 to cf5513
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { SortBillingReceiptsBy, BillingReceipt as BillingReceiptType } from '@app/condo/schema'
import { SortBillingReceiptsBy, BillingReceipt as BillingReceiptType, TourStepTypeType } from '@app/condo/schema'
import { Row, Col, Typography, Space } from 'antd'
import dayjs from 'dayjs'
import get from 'lodash/get'
import { useRouter } from 'next/router'
import React, { useCallback, useMemo, useState, CSSProperties } from 'react'

import { useDeepCompareEffect } from '@open-condo/codegen/utils/useDeepCompareEffect'
import { useFeatureFlags } from '@open-condo/featureflags/FeatureFlagsContext'
import { useIntl } from '@open-condo/next/intl'

import { ServicesModal } from '@condo/domains/billing/components/BillingPageContent/ServicesModal'
Expand All @@ -16,6 +17,7 @@ import Input from '@condo/domains/common/components/antd/Input'
import { BasicEmptyListView } from '@condo/domains/common/components/EmptyListView'
import DatePicker from '@condo/domains/common/components/Pickers/DatePicker'
import { Table, DEFAULT_PAGE_SIZE } from '@condo/domains/common/components/Table/Index'
import { ORGANIZATION_TOUR } from '@condo/domains/common/constants/featureflags'
import { useQueryMappers } from '@condo/domains/common/hooks/useQueryMappers'
import { useSearch } from '@condo/domains/common/hooks/useSearch'
import { getFiltersQueryData } from '@condo/domains/common/utils/filters.utils'
Expand All @@ -25,9 +27,11 @@ import {
getPageIndexFromOffset,
parseQuery,
} from '@condo/domains/common/utils/tables.utils'
import { useTourContext } from '@condo/domains/onboarding/contexts/TourContext'

import { useBillingAndAcquiringContexts } from './ContextProvider'


const SORTABLE_PROPERTIES = ['toPay']
const INPUT_STYLE: CSSProperties = { width: '18em' }

Expand Down Expand Up @@ -71,6 +75,15 @@ export const ReceiptsTable: React.FC = () => {
skip: (currentPageIndex - 1) * DEFAULT_PAGE_SIZE,
})

const { updateStepIfNotCompleted } = useTourContext()
const { useFlag } = useFeatureFlags()
const isTourEnabled = useFlag(ORGANIZATION_TOUR)
useDeepCompareEffect(() => {
if (isTourEnabled && receipts.length > 0) {
updateStepIfNotCompleted(TourStepTypeType.UploadReceipts)
}
}, [receipts, isTourEnabled])

const mainTableColumns = useReceiptTableColumns(filterMetas, hasToPayDetails, currencyCode)

const [modalIsVisible, setModalIsVisible] = useState(false)
Expand Down
36 changes: 32 additions & 4 deletions apps/condo/domains/common/components/BaseSearchInput/index.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import styled from '@emotion/styled'
import { isEmpty } from 'lodash'
import debounce from 'lodash/debounce'
import get from 'lodash/get'
import isFunction from 'lodash/isFunction'
import throttle from 'lodash/throttle'
import uniqBy from 'lodash/uniqBy'
import React, { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from 'react'

import { Close } from '@open-condo/icons'
import { useIntl } from '@open-condo/next/intl'
import { colors } from '@open-condo/ui/dist/colors'

import Select, { CustomSelectProps } from '@condo/domains/common/components/antd/Select'
import { Loader } from '@condo/domains/common/components/Loader'
Expand All @@ -15,6 +19,7 @@ import { isNeedToLoadNewElements } from '@condo/domains/common/utils/select.util
import { InitialValuesGetter, useInitialValueGetter } from './useInitialValueGetter'
import { useSelectCareeteControls } from './useSelectCareeteControls'


const { Option } = Select

const DEBOUNCE_TIMEOUT = 800
Expand All @@ -32,6 +37,18 @@ interface ISearchInput<S> extends Omit<CustomSelectProps<S>, 'onSelect'> {
setIsMatchSelectedProperty?: Dispatch<SetStateAction<boolean>>
}

const StyledSelect = styled(Select)`
.ant-select-selection-placeholder {
color: ${colors.gray[7]};
opacity: 0.6;
}
.ant-select-clear {
right: 16px;
color: ${colors.black};
}
`

const SELECT_LOADER_STYLE = { display: 'flex', justifyContent: 'center', padding: '10px 0' }

export const BaseSearchInput = <S extends string> (props: ISearchInput<S>) => {
Expand All @@ -49,7 +66,7 @@ export const BaseSearchInput = <S extends string> (props: ISearchInput<S>) => {
renderOption,
initialValueGetter,
loadOptionsOnFocus = true,
notFoundContent = NotFoundMessage,
notFoundContent: propsNotFoundContent = NotFoundMessage,
setIsMatchSelectedProperty,
style,
infinityScroll,
Expand All @@ -68,12 +85,14 @@ export const BaseSearchInput = <S extends string> (props: ISearchInput<S>) => {
const [initialOptionsLoaded, setInitialOptionsLoaded] = useState(false)
const [initialValue, isInitialValueFetching] = useInitialValueGetter(value, initialValueGetter)
const [scrollInputCaretToEnd, setSelectRef, selectInputNode] = useSelectCareeteControls(restSelectProps.id)
const [isEmptyDataFetched, setIsEmptyDataFetched] = useState<boolean>(false)

const searchSuggestions = useCallback(
async (value) => {
setIsAllDataLoaded(false)
setFetching(true)
const data = await search(value)
setIsEmptyDataFetched(value && get(data, 'length', 0) === 0)
setFetching(false)
setData(data)
},
Expand Down Expand Up @@ -200,11 +219,20 @@ export const BaseSearchInput = <S extends string> (props: ISearchInput<S>) => {
[data, fetching, loading, renderOption, value],
)

const notFoundContent = useMemo(() => {
if (fetching || loading) return <Loader size='small' delay={0} fill/>

if (isEmptyDataFetched) {
return propsNotFoundContent
}
}, [fetching, isEmptyDataFetched, loading, propsNotFoundContent])

return (
<Select
<StyledSelect
showSearch
autoFocus={autoFocus}
allowClear
clearIcon={<Close size='small' />}
id={props.id}
value={isInitialValueFetching ? LoadingMessage : searchValue}
disabled={props.disabled || Boolean(isInitialValueFetching)}
Expand All @@ -217,7 +245,7 @@ export const BaseSearchInput = <S extends string> (props: ISearchInput<S>) => {
onPopupScroll={infinityScroll && handleScroll}
ref={setSelectRef}
placeholder={placeholder}
notFoundContent={fetching ? <Loader size='small' delay={0} fill/> : notFoundContent}
notFoundContent={notFoundContent}
// TODO(Dimitreee): remove ts ignore after combobox mode will be introduced after ant update
// @ts-ignore
mode='SECRET_COMBOBOX_MODE_DO_NOT_USE'
Expand All @@ -231,6 +259,6 @@ export const BaseSearchInput = <S extends string> (props: ISearchInput<S>) => {
loading={loading}
>
{options}
</Select>
</StyledSelect>
)
}
47 changes: 47 additions & 0 deletions apps/condo/domains/common/components/CardVideo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React, { CSSProperties, useEffect, useState } from 'react'

import { Card, Space, Typography } from '@open-condo/ui'

import { Loader } from './Loader'


const CARD_VIDEO_WRAPPER_STYLES: CSSProperties = {
borderRadius: '12px',
overflow: 'hidden',
height: '260px',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}

export const CardVideo = ({ src, title, description }) => {
const [loading, setLoading] = useState<boolean>(true)

useEffect(() => {
setLoading(true)
}, [src])

return (
<Card hoverable>
<Space size={24} direction='vertical'>
<div style={CARD_VIDEO_WRAPPER_STYLES}>
<iframe
width='100%'
height='100%'
src={src}
frameBorder='0'
onLoad={() => setLoading(false)}
hidden={loading}
allowFullScreen
/>
{loading && <Loader size='large' />}
</div>
<Space size={8} direction='vertical'>
<Typography.Title level={2}>{title}</Typography.Title>
<Typography.Paragraph type='secondary'>{description}</Typography.Paragraph>
</Space>
</Space>
</Card>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CSSProperties, useMemo } from 'react'


export const FormItemTooltipWrapper = ({ children, padding = '10px 8px' }) => {
const wrapperStyles: CSSProperties = useMemo(() =>
({ display: 'flex', flexDirection: 'column', gap: '8px', padding }), [padding])

return (
<div style={wrapperStyles}>
{children}
</div>
)
}
19 changes: 17 additions & 2 deletions apps/condo/domains/common/components/Import/Index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import styled from '@emotion/styled'
import { Col, Progress, Row, Space } from 'antd'
import dayjs from 'dayjs'
import { EventEmitter } from 'eventemitter3'
import get from 'lodash/get'
import isDate from 'lodash/isDate'
import isFunction from 'lodash/isFunction'
import React, { useCallback, useMemo, useRef, useState } from 'react'
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import XLSX from 'xlsx'

import { Download, FileDown } from '@open-condo/icons'
Expand Down Expand Up @@ -92,6 +93,14 @@ function fitToColumn (arrayOfArray) {
))
}

const eventEmitter = new EventEmitter()
export const IMPORT_EVENT = 'ImportEvent'
export const ImportEmitter = {
addListener: (event, fn) => eventEmitter.addListener(event, fn),
removeListener: (event, fn) => eventEmitter.removeListener(event, fn),
emit: (event, payload) => eventEmitter.emit(event, payload),
}

type ActiveModalType = null | 'example' | 'progress' | 'partlyLoaded' | 'success' | 'error'

const ImportWrapper: React.FC<IImportWrapperProps> = (props) => {
Expand Down Expand Up @@ -136,7 +145,13 @@ const ImportWrapper: React.FC<IImportWrapperProps> = (props) => {

const { logEvent, getEventName } = useTracking()

const [activeModal, setActiveModal] = useState<ActiveModalType>(null)
const [activeModal, setActiveModal] = useState<ActiveModalType>()

useEffect(() => {
if (typeof activeModal !== 'undefined') {
ImportEmitter.emit(IMPORT_EVENT, { domain: domainName, status: activeModal })
}
}, [activeModal])

const totalRowsRef = useRef(0)
const setTotalRowsRef = (value: number) => {
Expand Down
49 changes: 49 additions & 0 deletions apps/condo/domains/common/components/LinkWithIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Link from 'next/link'
import React, { useMemo } from 'react'

import { IconProps } from '@open-condo/icons'
import { Typography, TypographyLinkProps } from '@open-condo/ui'


type LinkWithIconPropsType = {
href?: string
onClick?: () => void
title: string
size: TypographyLinkProps['size']
target?: '_self' | '_blank'
PrefixIcon?: React.ComponentType<IconProps>
PostfixIcon?: React.ComponentType<IconProps>
}

const CONTENT_WRAPPER_STYLE = { display: 'inline-flex', textDecoration: 'underline', flexFlow: 'row', gap: '8px', alignItems: 'center' }

export const LinkWithIcon: React.FC<LinkWithIconPropsType> = ({ href, title, size, onClick, PrefixIcon, PostfixIcon, target = '_self' }) => {
const linkContent = useMemo(() => (
<div style={CONTENT_WRAPPER_STYLE}>
{PrefixIcon && <PrefixIcon size='small' />}
{title}
{PostfixIcon && <PostfixIcon size='small' />}
</div>
), [PostfixIcon, PrefixIcon, title])

if (target === '_blank' || !href) {
return (
<Typography.Link
href={href}
onClick={onClick}
target='_blank'
size={size}
>
{linkContent}
</Typography.Link>
)
}

return (
<Link href={href}>
<Typography.Link size={size} onClick={onClick}>
{linkContent}
</Typography.Link>
</Link>
)
}
3 changes: 3 additions & 0 deletions apps/condo/domains/common/constants/emoji.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ const EMOJI = {
HOUSE: '🏠',
HOUSES: '🏘️',
MAN: '👱‍',
WOMAN: '👩',
FAMILY: '👪️',
PHONE: '📞',
MECHANIC: '🧑‍🔧',
WRENCH: '🔧',
}

const EMOJI_IMAGES = {
Expand Down
3 changes: 3 additions & 0 deletions apps/condo/domains/common/constants/menuCategories.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
const TOUR_CATEGORY = 'TOUR'
const DASHBOARD_CATEGORY = 'DASHBOARD'
const COMMUNICATION_CATEGORY = 'COMMUNICATION'
const PROPERTIES_CATEGORY = 'PROPERTIES'
Expand All @@ -10,6 +11,7 @@ const MINIAPPS_CATEGORY = 'MINIAPPS'
const SETTINGS_CATEGORY = 'SETTINGS'

const ALL_MENU_CATEGORIES = [
TOUR_CATEGORY,
DASHBOARD_CATEGORY,
COMMUNICATION_CATEGORY,
PROPERTIES_CATEGORY,
Expand All @@ -25,6 +27,7 @@ const ALL_MENU_CATEGORIES = [
const DEFAULT_MENU_CATEGORY = MINIAPPS_CATEGORY

module.exports = {
TOUR_CATEGORY,
DASHBOARD_CATEGORY,
COMMUNICATION_CATEGORY,
PROPERTIES_CATEGORY,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Col, Row } from 'antd'
import { Col, Form, Row } from 'antd'
import { Gutter } from 'antd/es/grid/row'
import dayjs, { Dayjs } from 'dayjs'
import get from 'lodash/get'
Expand Down Expand Up @@ -90,6 +90,7 @@ export const BaseMeterModalForm: React.FC<BaseMeterModalFormProps> = ({
organizationId,
disabled,
meterType,
submitButtonProps = {},
...otherProps
}) => {
const intl = useIntl()
Expand Down Expand Up @@ -186,6 +187,7 @@ export const BaseMeterModalForm: React.FC<BaseMeterModalFormProps> = ({
handleSubmit={handleSubmit}
submitButtonProps={{
disabled: disabledFields,
...submitButtonProps,
}}
ErrorToFormFieldMsgMapping={ErrorToFormFieldMsgMapping}
{...otherProps}
Expand Down
Loading

0 comments on commit 656c94e

Please sign in to comment.