diff --git a/src/cloud/components/ContentManager/ContentManagerToolbar.tsx b/src/cloud/components/ContentManager/ContentManagerToolbar.tsx
index 7aae85919a..47b893be0c 100644
--- a/src/cloud/components/ContentManager/ContentManagerToolbar.tsx
+++ b/src/cloud/components/ContentManager/ContentManagerToolbar.tsx
@@ -292,18 +292,10 @@ const ContentManagerToolbar = ({
const docProp = props[key] as FilledSerializedPropData
switch (docProp.type) {
- case 'json':
- if (docProp.data.dataType === 'timeperiod') {
- values[key] = {
- type: docProp.type,
- subType: 'timeperiod',
- value: docProp.data.data,
- }
- }
- break
case 'user':
values[key] = {
type: docProp.type,
+ subType: docProp.subType,
value:
docProp.data == null
? []
@@ -315,6 +307,7 @@ const ContentManagerToolbar = ({
case 'status':
values[key] = {
type: docProp.type,
+ subType: docProp.subType,
value:
docProp.data == null
? undefined
@@ -328,6 +321,7 @@ const ContentManagerToolbar = ({
case 'string':
values[key] = {
type: docProp.type,
+ subType: docProp.subType,
value: docProp.data,
}
break
@@ -366,14 +360,6 @@ const ContentManagerToolbar = ({
const docProp = props[key] as FilledSerializedPropData
switch (docProp.type) {
- case 'json':
- if (
- docProp.data.dataType !== 'timeperiod' ||
- docProp.data.data !== values[key]['value']
- ) {
- delete values[key]
- }
- break
case 'user':
{
let newUserArray = values[key].value.slice() as string[]
@@ -391,11 +377,9 @@ const ContentManagerToolbar = ({
case 'status':
{
const docPropStatus: SerializedStatus | undefined =
- props[key] == null
- ? undefined
- : Array.isArray(props[key].data)
- ? props[key].data[0]
- : props[key].data
+ docProp[key] == Array.isArray(docProp[key].data)
+ ? docProp[key].data[0]
+ : docProp[key].data
if (
docPropStatus == null ||
docPropStatus.id !== values[key].value.id
@@ -595,8 +579,9 @@ const ContentManagerToolbar = ({
updateProp([
propColumn.name,
{
- type: 'json',
- data: { dataType: 'timeperiod', data: val },
+ type: 'number',
+ subType: 'timeperiod',
+ data: val,
},
])
}}
diff --git a/src/cloud/components/DocPage/index.tsx b/src/cloud/components/DocPage/index.tsx
index 759a1e8658..deb76ad706 100644
--- a/src/cloud/components/DocPage/index.tsx
+++ b/src/cloud/components/DocPage/index.tsx
@@ -22,6 +22,7 @@ import ColoredBlock from '../../../design/components/atoms/ColoredBlock'
import Editor from '../Editor'
import ApplicationPage from '../ApplicationPage'
import { freePlanDocLimit, freePlanMembersLimit } from '../../lib/subscription'
+import { useCloudDocPreview } from '../../lib/hooks/useCloudDocPreview'
interface DocPageProps {
doc: SerializedDocWithSupplemental
@@ -74,6 +75,7 @@ const DocPage = ({
}, [currentDoc, team])
useTitle(pageTitle)
+ useCloudDocPreview(team)
useEffect(() => {
if (currentDoc == null) {
diff --git a/src/cloud/components/DocProperties.tsx b/src/cloud/components/DocProperties.tsx
index 24054f7aa8..35fce3bafe 100644
--- a/src/cloud/components/DocProperties.tsx
+++ b/src/cloud/components/DocProperties.tsx
@@ -84,12 +84,9 @@ const DocProperties = ({
{docProperties.map((prop, i) => {
const iconPath = getIconPathOfPropType(
- prop[1].data.type === 'json' &&
- prop[1].data.data != null &&
- prop[1].data.data.dataType != null
- ? prop[1].data.data.dataType
- : prop[1].data.type
+ prop[1].data.subType || prop[1].data.type
)
+
return (
diff --git a/src/cloud/components/Props/PropPicker.tsx b/src/cloud/components/Props/PropPicker.tsx
index 202838aa74..b2d8313301 100644
--- a/src/cloud/components/Props/PropPicker.tsx
+++ b/src/cloud/components/Props/PropPicker.tsx
@@ -1,6 +1,12 @@
import React, { useCallback } from 'react'
import { SerializedDocWithSupplemental } from '../../interfaces/db/doc'
-import { SerializedPropData, PropData, Props } from '../../interfaces/db/props'
+import {
+ SerializedPropData,
+ PropData,
+ Props,
+ NullablePropData,
+ SerializedCompoundProp,
+} from '../../interfaces/db/props'
import { useCloudApi } from '../../lib/hooks/useCloudApi'
import AssigneeSelect from './Pickers/AssigneeSelect'
import DatePropPicker from './Pickers/DatePropPicker'
@@ -14,6 +20,8 @@ import { MixpanelActionTrackTypes } from '../../interfaces/analytics/mixpanel'
import NumberSelect from './Pickers/NumberSelect'
import TextSelect from './Pickers/TextSelect'
import { getISODateFromLocalTime } from '../../lib/date'
+import UrlSelect from './Pickers/UrlSelect'
+import DocDependencySelect from './Pickers/DocDependencySelect'
interface PropPickerProps {
parent: { type: 'doc'; target: SerializedDocWithSupplemental }
@@ -133,7 +141,8 @@ const PropPicker = ({
return (
)
case 'number':
+ if (propData.subType === 'timeperiod') {
+ return (
+
{
+ updateProp({
+ type: 'number',
+ subType: 'timeperiod',
+ data: val,
+ })
+ }}
+ />
+ )
+ }
return (
)
case 'string':
+ if (propData.subType === 'url') {
+ return (
+
+ updateProp({
+ type: 'string',
+ subType: 'url',
+ data: val,
+ })
+ }
+ />
+ )
+ }
+
return (
)
- case 'json':
- if (
- propData.data != null &&
- propData.data.dataType === 'timeperiod' &&
- (propData.data.data == null || typeof propData.data.data === 'number')
- ) {
+ case 'compound':
+ if (propData.subType === 'dependency') {
return (
- {
- updateProp({
- type: 'json',
- data: { dataType: 'timeperiod', data: val },
- })
- }}
+ isLoading={sendingMap.get(parent.target.id) === propName}
+ readOnly={readOnly}
+ emptyLabel={emptyLabel}
+ defaultValue={
+ propData.data != null
+ ? Array.isArray(propData.data)
+ ? (propData.data.filter((item) => item != null) as any)
+ : [propData.data]
+ : []
+ }
+ showIcon={showIcon}
+ update={(val) =>
+ updateProp(
+ val.length === 0 || val == null
+ ? { type: 'compound', subType: 'dependency', data: null }
+ : {
+ type: 'compound',
+ subType: 'dependency',
+ data: val as NullablePropData,
+ }
+ )
+ }
/>
)
}
+
return null
default:
return null
diff --git a/src/cloud/components/Views/Calendar/CalendarView.tsx b/src/cloud/components/Views/Calendar/CalendarView.tsx
index 4d2465a11f..d90af5b807 100644
--- a/src/cloud/components/Views/Calendar/CalendarView.tsx
+++ b/src/cloud/components/Views/Calendar/CalendarView.tsx
@@ -69,6 +69,7 @@ const CalendarView = ({
}
if (
dateProps != null &&
+ dateProps.type === 'date' &&
dateProps.type === watchedProp.type &&
dateProps.data != null
) {
@@ -76,12 +77,12 @@ const CalendarView = ({
props.start = dateProps.data
} else {
const orderedDates = dateProps.data.sort((a, b) => {
- if (a < b) {
+ if (a! < b!) {
return -1
} else {
return 1
}
- })
+ }) as Date[]
if (dateProps.data.length === 2) {
props.start = orderedDates[0]
const endDate = new Date(orderedDates[1])
diff --git a/src/cloud/components/Views/List/ListDocProperties.tsx b/src/cloud/components/Views/List/ListDocProperties.tsx
index f9aff2fb7c..b5197a9021 100644
--- a/src/cloud/components/Views/List/ListDocProperties.tsx
+++ b/src/cloud/components/Views/List/ListDocProperties.tsx
@@ -65,8 +65,7 @@ const ListDocProperties = ({
getInitialPropDataOfPropType(propType)
const isPropDataAccurate =
- propData.type === propType ||
- (propData.type === 'json' && propData.data.dataType === propType)
+ propData.type === prop.type && propData.subType === prop.subType
return (
{
+export interface Prop {
type: T
data: D | D[]
+ subType?: S
createdAt: string
}
-export type PropData = Omit
-export type NullablePropData = T | undefined
-
export type FilledSerializedPropData =
- | Prop<'string', string>
- | Prop<'date', Date>
- | Prop<'json', any>
- | Prop<'number', number>
- | Prop<'user', SerializedUserTeamPermissions>
- | Prop<'status', SerializedStatus>
+ | Prop<'string', PropStringSubtype, string>
+ | Prop<'date', undefined, Date>
+ | Prop<'number', PropNumberSubtype, number>
+ | Prop<'user', undefined, SerializedUserTeamPermissions>
+ | Prop<'status', undefined, SerializedStatus>
+export type NullablePropData = T | undefined | null
export type SerializedPropData =
- | Prop<'string', NullablePropData>
- | Prop<'date', NullablePropData>
- | Prop<'json', any>
- | Prop<'number', NullablePropData>
- | Prop<'user', NullablePropData>
- | Prop<'status', NullablePropData>
+ | Prop<'string', PropStringSubtype, NullablePropData>
+ | Prop<'date', undefined, NullablePropData>
+ | Prop<'number', PropNumberSubtype, NullablePropData>
+ | Prop<'user', undefined, NullablePropData>
+ | Prop<'status', undefined, NullablePropData>
+ | Prop<
+ 'compound',
+ PropCompoundSubType,
+ NullablePropData
+ >
+
+export type PropData = Omit
+export type PropNumberSubtype = 'timeperiod'
+export type PropStringSubtype = 'url'
+export type PropCompoundSubType = 'dependency'
export type PropType = SerializedPropData['type']
+export type PropSubType = SerializedPropData['subType']
+
export type StaticPropType = 'creation_date' | 'update_date' | 'label'
-export type PropSubType = 'timeperiod'
export type Props = Record
+
+export type SerializedCompoundProp = {
+ string?: string
+ date?: Date
+ number?: number
+ member?: SerializedUserTeamPermissions
+ status?: SerializedStatus
+ targetDoc?: SerializedDocWithSupplemental
+}
diff --git a/src/cloud/interfaces/db/smartView.ts b/src/cloud/interfaces/db/smartView.ts
index 936b9ced77..d7a7f5c7c4 100644
--- a/src/cloud/interfaces/db/smartView.ts
+++ b/src/cloud/interfaces/db/smartView.ts
@@ -34,6 +34,7 @@ export interface ConditionType {
export interface PropConditionType {
name: string
type: T
+ subType?: string
value: U
}
diff --git a/src/cloud/lib/hooks/props/index.ts b/src/cloud/lib/hooks/props/index.ts
index e19cc9fbbc..e02be4a60c 100644
--- a/src/cloud/lib/hooks/props/index.ts
+++ b/src/cloud/lib/hooks/props/index.ts
@@ -65,8 +65,8 @@ export function useProps(
updateDocPropsApi(parent.target, [
propName,
isObject(data.data)
- ? { type: data.type, data: data.data }
- : { type: data.type, data: null },
+ ? { type: data.type, subType: data.subType, data: data.data }
+ : { type: data.type, subType: data.subType, data: null },
])
}
}
@@ -138,6 +138,7 @@ export function useProps(
trackEvent(MixpanelActionTrackTypes.DocPropDelete, {
propName,
propType: newProps[propName].type,
+ propSubType: newProps[propName].subType,
})
}
delete newProps[propName]
diff --git a/src/cloud/lib/hooks/useCloudDocPreview.tsx b/src/cloud/lib/hooks/useCloudDocPreview.tsx
index 9f0a518b44..4ea78554f0 100644
--- a/src/cloud/lib/hooks/useCloudDocPreview.tsx
+++ b/src/cloud/lib/hooks/useCloudDocPreview.tsx
@@ -7,11 +7,11 @@ import { useCloudResourceModals } from './useCloudResourceModals'
export const docPreviewCloseEvent = 'doc-preview-close'
-export function useCloudDocPreview(team: SerializedTeam) {
+export function useCloudDocPreview(team?: SerializedTeam) {
const { query } = useRouter()
const { openDocPreview } = useCloudResourceModals()
const prevPreviewRef = useRef('')
- const { docsMap } = useNav()
+ const { docsMap, mapsInitializedByProps } = useNav()
const openDocInPreview = useCallback(
(docId: string) => {
@@ -19,6 +19,9 @@ export function useCloudDocPreview(team: SerializedTeam) {
if (doc == null) {
return modalEventEmitter.dispatch({ type: docPreviewCloseEvent })
}
+ if (team == null) {
+ return
+ }
return openDocPreview(doc, team)
},
[openDocPreview, docsMap, team]
@@ -51,11 +54,12 @@ export function useCloudDocPreview(team: SerializedTeam) {
useEffect(() => {
if (
typeof query.preview !== 'string' ||
- query.preview === prevPreviewRef.current
+ query.preview === prevPreviewRef.current ||
+ !mapsInitializedByProps
) {
return
}
prevPreviewRef.current = query.preview
openDocInPreviewRef.current(query.preview)
- }, [query.preview])
+ }, [query.preview, mapsInitializedByProps])
}
diff --git a/src/cloud/lib/hooks/useCloudResourceModals.tsx b/src/cloud/lib/hooks/useCloudResourceModals.tsx
index 4a8f8ca8c9..2b1f6c6473 100644
--- a/src/cloud/lib/hooks/useCloudResourceModals.tsx
+++ b/src/cloud/lib/hooks/useCloudResourceModals.tsx
@@ -22,7 +22,7 @@ import { lngKeys } from '../i18n/types'
import { useRouter } from '../router'
import { useNav } from '../stores/nav'
import { usePage } from '../stores/pageStore'
-import { resourceDeleteEventEmitter } from '../utils/events'
+import { modalEventEmitter, resourceDeleteEventEmitter } from '../utils/events'
import {
getDocId,
getFolderId,
@@ -33,6 +33,7 @@ import {
import { useCloudApi } from './useCloudApi'
import { useI18n } from './useI18n'
import { stringify } from 'querystring'
+import { docPreviewCloseEvent } from './useCloudDocPreview'
export function useCloudResourceModals() {
const { openModal, closeLastModal } = useModal()
@@ -440,6 +441,7 @@ export function useCloudResourceModals() {
const goToDocPreview = useCallback(
(doc: SerializedDocWithSupplemental) => {
+ modalEventEmitter.dispatch({ type: docPreviewCloseEvent })
return push(`${pathname}?preview=${doc.id}`)
},
[pathname, push]
diff --git a/src/cloud/lib/props.ts b/src/cloud/lib/props.ts
index 37d5c13536..0a0360736f 100644
--- a/src/cloud/lib/props.ts
+++ b/src/cloud/lib/props.ts
@@ -6,7 +6,9 @@ import {
mdiContentSaveOutline,
mdiFormatText,
mdiLabelOutline,
+ mdiLinkVariant,
mdiMusicAccidentalSharp,
+ mdiSwapHorizontal,
mdiTimerOutline,
} from '@mdi/js'
import { capitalize, isNumber, isObject } from 'lodash'
@@ -32,11 +34,13 @@ export const supportedPropTypes: {
subType?: PropSubType
}[] = [
{ type: 'date' },
- { type: 'json', subType: 'timeperiod' },
+ { type: 'number', subType: 'timeperiod' },
{ type: 'status' },
{ type: 'user' },
{ type: 'number' },
{ type: 'string' },
+ { type: 'string', subType: 'url' },
+ { type: 'compound', subType: 'dependency' },
]
export function getLabelOfPropType(
@@ -53,6 +57,8 @@ export function getLabelOfPropType(
return 'Update Date'
case 'string':
return 'Text'
+ case 'dependency':
+ return 'Dependencies'
default:
return capitalize(propType)
}
@@ -100,6 +106,8 @@ export function getIconPathOfPropType(
type: PropType | StaticPropType | PropSubType
): string | undefined {
switch (type) {
+ case 'url':
+ return mdiLinkVariant
case 'creation_date':
return mdiClockOutline
case 'update_date':
@@ -118,6 +126,8 @@ export function getIconPathOfPropType(
return mdiMusicAccidentalSharp
case 'string':
return mdiFormatText
+ case 'dependency':
+ return mdiSwapHorizontal
default:
return
}
@@ -127,12 +137,20 @@ export function getInitialPropDataOfPropType(
type: PropType | PropSubType
): SerializedPropData {
switch (type) {
+ case 'dependency':
+ return {
+ type: 'compound',
+ subType: 'dependency',
+ createdAt: new Date().toString(),
+ data: undefined,
+ }
case 'date':
return { type: 'date', data: undefined, createdAt: new Date().toString() }
case 'timeperiod':
return {
- type: 'json',
- data: { dataType: 'timeperiod', data: null },
+ type: 'number',
+ subType: 'timeperiod',
+ data: null,
createdAt: new Date().toString(),
}
case 'user':
@@ -149,6 +167,13 @@ export function getInitialPropDataOfPropType(
data: undefined,
createdAt: new Date().toString(),
}
+ case 'url':
+ return {
+ type: 'string',
+ subType: 'url',
+ data: '',
+ createdAt: new Date().toString(),
+ }
case 'string':
default:
return {
@@ -165,6 +190,43 @@ export function getDomainOrInitialDataPropToPropData(
let propData = data.data
if (data.data != null) {
switch (data.type) {
+ case 'compound':
+ const copyData = Object.assign({}, propData)
+ Object.entries(copyData).forEach(([key, value]) => {
+ if (key == null) {
+ return
+ }
+ switch (key) {
+ case 'targetDoc': {
+ const val = Array.isArray(value) ? value : [value]
+ if (!isUUIDArray(value)) {
+ copyData[key] = val
+ .filter((doc: any) => doc != null)
+ .map((doc: any) => doc.id)
+ }
+ return
+ }
+ case 'member': {
+ const val = Array.isArray(value) ? value : [value]
+ if (!isUUIDArray(value)) {
+ copyData[key] = val
+ .filter((user: any) => user != null)
+ .map((user: any) => user.userId)
+ }
+ return
+ }
+ case 'status': {
+ const val = Array.isArray(value) ? value : [value]
+ if (!isUUIDArray(value)) {
+ copyData[key] = val
+ .filter((status: any) => status != null)
+ .map((status: any) => status.id)
+ }
+ return
+ }
+ }
+ })
+ break
case 'user':
const users = Array.isArray(data.data) ? data.data : [data.data]
if (!isUUIDArray(users)) {
@@ -187,10 +249,10 @@ export function getDomainOrInitialDataPropToPropData(
}
if (isObject(propData)) {
- return { type: data.type, data: propData }
+ return { type: data.type, subType: data.subType, data: propData }
}
- return { type: data.type, data: null }
+ return { type: data.type, subType: data.subType, data: null }
}
export function getDefaultStaticSuggestionsPerType(): {
@@ -212,12 +274,14 @@ export function getDefaultColumnSuggestionsPerType(): {
return [
{ type: 'user', name: 'Assignees' },
{ type: 'user', name: 'Reviewers' },
- { type: 'json', subType: 'timeperiod', name: 'Time Estimate' },
- { type: 'json', subType: 'timeperiod', name: 'Time Tracked' },
+ { type: 'number', subType: 'timeperiod', name: 'Time Estimate' },
+ { type: 'number', subType: 'timeperiod', name: 'Time Tracked' },
{ type: 'status', name: 'Status' },
{ type: 'date', name: 'Due Date' },
{ type: 'date', name: 'Start Date' },
{ type: 'number', name: 'Story Point' },
{ type: 'string', name: 'Text' },
+ { type: 'string', subType: 'url', name: 'Url' },
+ { type: 'compound', subType: 'dependency', name: 'Dependencies' },
]
}
diff --git a/src/cloud/lib/smartViews.ts b/src/cloud/lib/smartViews.ts
index c8dff6a054..b6c80c8a67 100644
--- a/src/cloud/lib/smartViews.ts
+++ b/src/cloud/lib/smartViews.ts
@@ -18,6 +18,7 @@ import {
} from '@mdi/js'
import { getInitialPropDataOfPropType } from './props'
import { floorISOTime, getISODateFromLocalTime } from './date'
+import { SerializedStatus } from '../interfaces/db/status'
export const supportedCustomPropertyTypes: Record<
string,
@@ -25,7 +26,11 @@ export const supportedCustomPropertyTypes: Record<
> = {
date: { label: 'Date', value: 'date', icon: mdiCalendarMonthOutline },
person: { label: 'Person', value: 'user', icon: mdiAccountOutline },
- timeperiod: { label: 'Time', value: 'json', icon: mdiTimerOutline },
+ timeperiod: {
+ label: 'Time',
+ value: 'number/timeperiod',
+ icon: mdiTimerOutline,
+ },
status: {
label: 'Status',
value: 'status',
@@ -107,22 +112,23 @@ const validators: Validators = {
return false
}
+ if (condition.value.type !== prop.type) {
+ return false
+ }
+
const nonNullableVal = Array.isArray(prop.data)
- ? prop.data.filter((d) => d != null)
+ ? (prop.data as Array).filter((d) => d != null)
: prop.data
if (Array.isArray(nonNullableVal) && nonNullableVal.length === 0) {
return false
}
-
switch (condition.value.type) {
case 'date':
return validateDateValue(
Array.isArray(nonNullableVal) ? nonNullableVal[0] : nonNullableVal,
condition.value.value
)
- case 'json':
- return false
case 'user':
return Array.isArray(condition.value.value)
? condition.value.value.length === 0
@@ -157,7 +163,7 @@ const validators: Validators = {
if (st == null) {
return id === -1
}
- return st.id === id
+ return (st as SerializedStatus).id === id
},
prop.data,
condition.value.value
diff --git a/src/cloud/lib/stores/nav/store.tsx b/src/cloud/lib/stores/nav/store.tsx
index ecfeee7f15..9d8e3be44e 100644
--- a/src/cloud/lib/stores/nav/store.tsx
+++ b/src/cloud/lib/stores/nav/store.tsx
@@ -66,6 +66,11 @@ import {
PagePropsUpdateEventDetails,
PagePropsUpdateEventEmitter,
} from '../../utils/events'
+import { isObject } from 'lodash'
+import {
+ NullablePropData,
+ SerializedCompoundProp,
+} from '../../../interfaces/db/props'
export * from './types'
function useNavStore(): NavContext {
@@ -84,6 +89,7 @@ function useNavStore(): NavContext {
const router = useRouter()
const { pushMessage } = useToast()
const previousPathRef = useRef('')
+ const [mapsInitializedByProps, setMapsInitializedByProps] = useState(false)
const [initialLoadDone, setInitialLoadDone] = useState(false)
const [sideNavCreateButtonState, setSideNavCreateButtonState] = useState<
string | undefined
@@ -281,6 +287,7 @@ function useNavStore(): NavContext {
setViewsMap((prev) => new Map([...prev, ...maps.viewsData]))
setSmartViewsMap((prev) => new Map([...prev, ...maps.smartViewsData]))
setWorkspacesMap((prev) => new Map([...prev, ...maps.workspacesData]))
+ setMapsInitializedByProps(true)
}
}, [pageProps, router.pathname, navigatingBetweenPage])
@@ -1027,6 +1034,7 @@ function useNavStore(): NavContext {
dashboardsMap,
updateDashboardsMap,
removeFromDashboardsMap,
+ mapsInitializedByProps,
}
}
@@ -1107,9 +1115,52 @@ function getTagsFoldersDocsMapsFromProps(
const foldersData = getMapFromEntityArray(
folders as SerializedFolderWithBookmark[]
)
+
const docsData = getMapFromEntityArray(
docs as SerializedDocWithSupplemental[]
)
+
+ const docsGivenByProps = (docs as SerializedDocWithSupplemental[]).reduce(
+ (acc, doc) => {
+ Object.values(doc.props).forEach((propData) => {
+ if (
+ !(
+ propData.type === 'compound' && propData.subType === 'dependency'
+ ) ||
+ propData.data == null
+ ) {
+ return
+ }
+
+ let data: NullablePropData[]
+ if (!Array.isArray(propData.data)) {
+ data = [propData.data]
+ } else {
+ data = propData.data
+ }
+
+ for (const dependency of data) {
+ if (
+ dependency == null ||
+ dependency.targetDoc == null ||
+ !isObject(dependency.targetDoc)
+ ) {
+ continue
+ }
+ acc.push(dependency.targetDoc)
+ }
+ })
+ return acc
+ },
+ [] as SerializedDocWithSupplemental[]
+ )
+
+ docsGivenByProps.forEach((doc) => {
+ if (!docsData.has(doc.id)) {
+ docsData.set(doc.id, doc)
+ }
+ })
+
const tagsData = getMapFromEntityArray(tags as SerializedTag[])
const workspacesData = getMapFromEntityArray(
workspaces as SerializedWorkspace[]
diff --git a/src/cloud/lib/stores/nav/types.ts b/src/cloud/lib/stores/nav/types.ts
index d585fd854e..41a1424cc5 100644
--- a/src/cloud/lib/stores/nav/types.ts
+++ b/src/cloud/lib/stores/nav/types.ts
@@ -26,6 +26,7 @@ import { SerializedDashboard } from '../../../interfaces/db/dashboard'
export interface NavContext {
initialLoadDone: boolean
+ mapsInitializedByProps: boolean
sideNavCreateButtonState: string | undefined
setSideNavCreateButtonState: (value?: string) => void
currentPath: string
diff --git a/src/cloud/lib/views/table.ts b/src/cloud/lib/views/table.ts
index 0213ac6321..677c7e2a54 100644
--- a/src/cloud/lib/views/table.ts
+++ b/src/cloud/lib/views/table.ts
@@ -345,13 +345,13 @@ export function mapComparableItem(
}
case 'column':
const docProp = doc.props[sort.columnName]
- if (docProp == null) {
+ if (docProp == null || docProp.type !== sort.columnType) {
return {
doc,
compareValue: null,
}
}
- switch (sort.columnType) {
+ switch (docProp.type) {
case 'string': {
const compareValue = isArray(docProp.data)
? docProp.data[0]
@@ -380,12 +380,6 @@ export function mapComparableItem(
compareValue: isValidDate(compareValue) ? compareValue : null,
}
}
- case 'json':
- return {
- doc,
- compareValue:
- docProp.data != null ? JSON.stringify(docProp.data) : null,
- }
case 'number': {
const compareValue = isArray(docProp.data)
? docProp.data[0]
@@ -426,7 +420,9 @@ export function mapComparableItem(
)
} else {
const targetPermission = permissions.find(
- (permission) => permission.userId === docProp.data?.userId
+ (permission) =>
+ permission.userId ===
+ (docProp.data as SerializedUserTeamPermissions)?.userId
)
compareValue =
diff --git a/src/design/components/molecules/Form/atoms/FormSelect.tsx b/src/design/components/molecules/Form/atoms/FormSelect.tsx
index 6129d6c86a..ae3610855f 100644
--- a/src/design/components/molecules/Form/atoms/FormSelect.tsx
+++ b/src/design/components/molecules/Form/atoms/FormSelect.tsx
@@ -4,6 +4,7 @@ import cc from 'classcat'
import styled from '../../../../lib/styled'
import { formInputHeight } from '../../../../lib/styled/styleFunctions'
import { capitalize } from 'lodash'
+import { getOptionLabel, getOptionValue } from 'react-select/src/builtins'
export interface FormSelectOption {
label: string | React.ReactNode
@@ -28,6 +29,8 @@ interface FormSelectCommonProps {
onMenuOpen?: () => void
minWidth?: string | number
placeholder?: React.ReactNode
+ getOptionLabel?: getOptionLabel
+ getOptionValue?: getOptionValue
}
interface StandardFormSelectOptions {
@@ -57,6 +60,8 @@ const FormSelect = ({
isLoading = false,
isMulti = false,
isSearchable = false,
+ getOptionLabel,
+ getOptionValue,
placeholder = 'Select...',
name,
filterOption,
@@ -81,6 +86,8 @@ const FormSelect = ({
filterOption={filterOption}
onChange={onChange}
isDisabled={isDisabled}
+ getOptionLabel={getOptionLabel}
+ getOptionValue={getOptionValue}
isLoading={isLoading}
isSearchable={isSearchable}
isMulti={isMulti}
diff --git a/src/lib/string.ts b/src/lib/string.ts
index df91b9930f..946fa5fb89 100644
--- a/src/lib/string.ts
+++ b/src/lib/string.ts
@@ -31,3 +31,9 @@ export function parseNumberStringOrReturnZero(str: string): number {
return 0
}
}
+
+export function isValidUrl(str: string): boolean {
+ return /^https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)$/.test(
+ str
+ )
+}
diff --git a/src/mobile/components/molecules/ContentManagerDocRow.tsx b/src/mobile/components/molecules/ContentManagerDocRow.tsx
index 6e1aebc092..a8916a67a6 100644
--- a/src/mobile/components/molecules/ContentManagerDocRow.tsx
+++ b/src/mobile/components/molecules/ContentManagerDocRow.tsx
@@ -237,7 +237,6 @@ const ContentManagerDocRow = ({
itemLink={
{
const filteredDocs = documents
.filter((doc) => {
- if (doc.props.status == null) {
+ if (doc.props.status == null || doc.props.status.data == null) {
if (doc.archivedAt == null) {
return true
}
return statusFilterSet.has('archived')
}
- return statusFilterSet.has(doc.props.status.data)
+ return statusFilterSet.has(doc.props.status.data as any)
})
.map((doc) => {
return {