diff --git a/.github/workflows/deploy_website.yml b/.github/workflows/deploy_website.yml index 8de9de888..7c8abf9fe 100644 --- a/.github/workflows/deploy_website.yml +++ b/.github/workflows/deploy_website.yml @@ -14,8 +14,8 @@ jobs: REACT_APP_DOC_SEARCH_KEY: ${{ secrets.REACT_APP_DOC_SEARCH_KEY }} REACT_APP_DOC_SEARCH_APP_ID: ${{ secrets.REACT_APP_DOC_SEARCH_APP_ID }} steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v2 + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 with: node-version: '16' - run: yarn install --no-immutable diff --git a/lib/src/core/FireCMS.tsx b/lib/src/core/FireCMS.tsx index 68513650b..4d73ff82a 100644 --- a/lib/src/core/FireCMS.tsx +++ b/lib/src/core/FireCMS.tsx @@ -73,6 +73,7 @@ export function FireCMS(props: FireCMSProps) { const usedBasePath = basePath ?? "/"; const usedBasedCollectionPath = baseCollectionPath ?? DEFAULT_COLLECTION_PATH; + // @ts-ignore const dateUtilsLocale = locale ? locales[locale] : undefined; const navigation = useBuildNavigationContext({ diff --git a/lib/src/core/util/icons.tsx b/lib/src/core/util/icons.tsx index 9e223aa35..4a52ad25b 100644 --- a/lib/src/core/util/icons.tsx +++ b/lib/src/core/util/icons.tsx @@ -4,15 +4,17 @@ import { CMSView, EntityCollection } from "../../types"; import { SvgIconTypeMap } from "@mui/material"; import { hashString } from "./hash"; -export function getIcon(iconKey: string) { - return mui[iconKey]; +export function getIcon(iconKey?: keyof typeof mui) { + if (!iconKey) return undefined; + return iconKey in mui ? mui[iconKey] : undefined; } export function getIconForView(collectionOrView: EntityCollection | CMSView): React.ComponentType { - if (collectionOrView?.icon && getIcon(collectionOrView.icon)) - return getIcon(collectionOrView.icon); + const icon = getIcon(collectionOrView.icon); + if (collectionOrView?.icon && icon) + return icon; const iconsCount = collectionIconKeys.length; return mui[collectionIconKeys[hashString(collectionOrView.path) % iconsCount]]; } -export const collectionIconKeys = ["AcUnit", "Adjust", "AlignHorizontalCenter", "Album", "AllInclusive", "AllOut", "Animation", "Assistant", "Attractions", "Audiotrack", "AutoAwesome", "AutoAwesomeMosaic", "BeachAccess", "Bolt", "Brightness1", "BreakfastDining", "BrokenImage", "Brightness5", "Cable", "CalendarViewMonth", "CatchingPokemon", "Casino", "Category", "Cloud", "ColorLens", "CreditCard", "Coronavirus", "Earbuds", "EggAlt", "FiberSmartRecord", "Flag", "Healing", "HeatPump", "Hive", "Hub", "LocalFireDepartment", "LocalPizza", "Memory", "Outlet", "Pages", "PanoramaPhotosphere", "SignalCellular0Bar", "SportsBaseball", "Storm", "Stairs"]; +export const collectionIconKeys:(keyof typeof mui)[] = ["AcUnit", "Adjust", "AlignHorizontalCenter", "Album", "AllInclusive", "AllOut", "Animation", "Assistant", "Attractions", "Audiotrack", "AutoAwesome", "AutoAwesomeMosaic", "BeachAccess", "Bolt", "Brightness1", "BreakfastDining", "BrokenImage", "Brightness5", "Cable", "CalendarViewMonth", "CatchingPokemon", "Casino", "Category", "Cloud", "ColorLens", "CreditCard", "Coronavirus", "Earbuds", "EggAlt", "FiberSmartRecord", "Flag", "Healing", "HeatPump", "Hive", "Hub", "LocalFireDepartment", "LocalPizza", "Memory", "Outlet", "Pages", "PanoramaPhotosphere", "SignalCellular0Bar", "SportsBaseball", "Storm", "Stairs"]; diff --git a/lib/src/core/util/objects.ts b/lib/src/core/util/objects.ts index 634031c14..dc5922f0f 100644 --- a/lib/src/core/util/objects.ts +++ b/lib/src/core/util/objects.ts @@ -9,19 +9,19 @@ export const pick: (obj: T, ...args: any[]) => T = (obj: any, ...args: any[]) }); export function isObject(item: any) { - return (item && typeof item === "object" && !Array.isArray(item)); + return item && typeof item === "object" && !Array.isArray(item); } -export function mergeDeep(target: T, source: any): T { +export function mergeDeep(target: T, source: any): T { const targetIsObject = isObject(target); - const output: T = targetIsObject ? Object.assign({}, target) : target; + const output: T = targetIsObject ? { ...target } : target; if (targetIsObject && isObject(source)) { Object.keys(source).forEach(key => { if (isObject(source[key])) { if (!(key in target)) Object.assign(output, { [key]: source[key] }); else - (output)[key] = mergeDeep((target)[key], source[key]); + (output as any)[key] = mergeDeep((target as any)[key], source[key]); } else { Object.assign(output, { [key]: source[key] }); } @@ -34,11 +34,11 @@ export function getValueInPath(o: object | undefined, path: string): any { if (!o) return undefined; if (typeof o === "object") { if (path in o) { - return (o)[path]; + return (o as any)[path]; } if (path.includes(".")) { const pathSegments = path.split("."); - return getValueInPath((o)[pathSegments[0]], pathSegments.slice(1).join(".")) + return getValueInPath((o as any)[pathSegments[0]], pathSegments.slice(1).join(".")) } } return undefined; @@ -49,10 +49,10 @@ export function removeInPath(o: object, path: string): object | undefined { const parts = path.split("."); const last = parts.pop(); for (const part of parts) { - currentObject = currentObject[part] + currentObject = (currentObject as any)[part] } if (last) - delete currentObject[last]; + delete (currentObject as any)[last]; return currentObject; } @@ -98,7 +98,7 @@ export function removeUndefined(value: any): any { if (!isEmptyObject(value)) { const childRes = removeUndefined(value[key]); if (childRes !== undefined && !isEmptyObject(childRes)) - res[key] = childRes; + (res as any)[key] = childRes; } }); return res; diff --git a/lib/src/core/util/useStorageUploadController.tsx b/lib/src/core/util/useStorageUploadController.tsx index 3d54a7222..a30a45b0a 100644 --- a/lib/src/core/util/useStorageUploadController.tsx +++ b/lib/src/core/util/useStorageUploadController.tsx @@ -32,27 +32,27 @@ export interface StorageFieldItem { } export function useStorageUploadController({ - entityId, - entityValues, - path, - value, - property, - propertyKey, - storageSource, - disabled, - onChange - }: - { - entityId: string, - entityValues: EntityValues, - value: string | string[] | null; - path: string, - propertyKey: string, - property: ResolvedStringProperty | ResolvedArrayProperty, - storageSource: StorageSource, - disabled: boolean, - onChange: (value: string | string[] | null) => void - }) { + entityId, + entityValues, + path, + value, + property, + propertyKey, + storageSource, + disabled, + onChange + }: + { + entityId: string, + entityValues: EntityValues, + value: string | string[] | null; + path: string, + propertyKey: string, + property: ResolvedStringProperty | ResolvedArrayProperty, + storageSource: StorageSource, + disabled: boolean, + onChange: (value: string | string[] | null) => void + }) { const storage: StorageConfig | undefined = property.dataType === "string" ? property.storage @@ -208,7 +208,7 @@ function getRandomId() { return Math.floor(Math.random() * Math.floor(Number.MAX_SAFE_INTEGER)); } -const supportedTypes = { +const supportedTypes: Record = { "image/jpeg": "JPEG", "image/png": "PNG", "image/webp": "WEBP" @@ -221,11 +221,15 @@ const resizeAndCompressImage = (file: File, compression: ImageCompression) => ne const inputQuality = compression.quality === undefined ? defaultQuality : compression.quality; const quality = inputQuality >= 0 ? inputQuality <= 100 ? inputQuality : 100 : 100; + const format = compressionFormat(file); + if (!format) { + throw Error("resizeAndCompressImage: Unsupported image format"); + } Resizer.imageFileResizer( file, compression.maxWidth || Number.MAX_VALUE, compression.maxHeight || Number.MAX_VALUE, - compressionFormat(file), + format, quality, 0, (file: string | Blob | File | ProgressEvent) => resolve(file as File), diff --git a/lib/src/firebase_app/hooks/useFirestoreDataSource.ts b/lib/src/firebase_app/hooks/useFirestoreDataSource.ts index 49ba112df..5f7c1a690 100644 --- a/lib/src/firebase_app/hooks/useFirestoreDataSource.ts +++ b/lib/src/firebase_app/hooks/useFirestoreDataSource.ts @@ -489,12 +489,13 @@ export function useFirestoreDataSource({ * @param data * @category Firestore */ +export function firestoreToCMSModel(data: any): any export function firestoreToCMSModel(data: any): any { if (data === null || data === undefined) return null; if (serverTimestamp().isEqual(data)) { return null; } - if (data instanceof Timestamp || typeof data.toDate === "function") { + if (data instanceof Timestamp || (typeof data.toDate === "function" && data.toDate() instanceof Date)) { return data.toDate(); } if (data instanceof Date) { @@ -510,7 +511,7 @@ export function firestoreToCMSModel(data: any): any { return data.map(firestoreToCMSModel); } if (typeof data === "object") { - const result = {} + const result: Record = {}; for (const key of Object.keys(data)) { result[key] = firestoreToCMSModel(data[key]); } @@ -518,7 +519,6 @@ export function firestoreToCMSModel(data: any): any { } return data; } - export function cmsToFirestoreModel(data: any, firestore: Firestore): any { if (Array.isArray(data)) { return data.map(v => cmsToFirestoreModel(v, firestore)); diff --git a/lib/src/preview/components/DatePreview.tsx b/lib/src/preview/components/DatePreview.tsx index cc40d62a0..5679d11d2 100644 --- a/lib/src/preview/components/DatePreview.tsx +++ b/lib/src/preview/components/DatePreview.tsx @@ -14,6 +14,7 @@ export function DatePreview({ }: { date: Date }): React.ReactElement { const appConfig: FireCMSContext | undefined = useFireCMSContext(); + // @ts-ignore const dateUtilsLocale = appConfig?.locale ? locales[appConfig?.locale] : undefined; const dateFormat: string = appConfig?.dateTimeFormat ?? defaultDateFormat; const formattedDate = date ? format(date, dateFormat, { locale: dateUtilsLocale }) : ""; diff --git a/lib/src/preview/components/ReferencePreview.tsx b/lib/src/preview/components/ReferencePreview.tsx index 950469fcb..b25b93b65 100644 --- a/lib/src/preview/components/ReferencePreview.tsx +++ b/lib/src/preview/components/ReferencePreview.tsx @@ -94,10 +94,10 @@ function ReferencePreviewInternal>({ }); if (entity) { - referencesCache[reference.pathWithId] = entity; + referencesCache.set(reference.pathWithId, entity); } - const usedEntity = entity ?? referencesCache[reference.pathWithId]; + const usedEntity = entity ?? referencesCache.get(reference.pathWithId); const resolvedCollection = useMemo(() => resolveCollection({ collection, diff --git a/lib/src/types/collections.ts b/lib/src/types/collections.ts index 6b8a6d5a2..ba306bec8 100644 --- a/lib/src/types/collections.ts +++ b/lib/src/types/collections.ts @@ -5,6 +5,7 @@ import { FireCMSContext } from "./firecms_context"; import { EntityCallbacks } from "./entity_callbacks"; import { Permissions, PermissionsBuilder } from "./permissions"; import { EnumValues, PropertiesOrBuilders } from "./properties"; +import * as mui from "@mui/icons-material"; /** * This interface represents a view that includes a collection of entities. @@ -55,7 +56,7 @@ export interface EntityCollection = any, * https://mui.com/material-ui/material-icons/ * e.g. 'AccountTree' or 'Person' */ - icon?: string; + icon?: keyof typeof mui; /** * Optional field used to group top level navigation entries under a~ diff --git a/lib/src/types/navigation.ts b/lib/src/types/navigation.ts index 0983f3474..867aa5648 100644 --- a/lib/src/types/navigation.ts +++ b/lib/src/types/navigation.ts @@ -1,3 +1,4 @@ +import * as mui from "@mui/icons-material"; import { EntityCollection } from "./collections"; /** @@ -50,8 +51,8 @@ export type NavigationContext = { * among the */ getCollection: >(pathOrAlias: string, - entityId?: string, - includeUserOverride?: boolean) => EC | undefined; + entityId?: string, + includeUserOverride?: boolean) => EC | undefined; /** * Default path under the navigation routes of the CMS will be created @@ -78,7 +79,7 @@ export type NavigationContext = { */ buildCMSUrlPath: (path: string) => string; - buildUrlEditCollectionPath: (props: { path: string}) => string; + buildUrlEditCollectionPath: (props: { path: string }) => string; /** * Base url path for the home screen @@ -146,7 +147,7 @@ export interface CMSView { * https://mui.com/material-ui/material-icons/ * e.g. 'AccountTree' or 'Person' */ - icon?:string; + icon?: keyof typeof mui; /** * Should this view be hidden from the main navigation panel. diff --git a/lib/tsconfig.json b/lib/tsconfig.json index 4bab99d9c..d6c280b80 100644 --- a/lib/tsconfig.json +++ b/lib/tsconfig.json @@ -18,7 +18,6 @@ "noImplicitThis": true, "noImplicitAny": true, "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, "allowSyntheticDefaultImports": true, "allowJs": true, "skipLibCheck": true, diff --git a/tsconfig.json b/tsconfig.json index 2e0fc22bb..718ca4fa1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,7 +18,6 @@ "noImplicitThis": true, "noImplicitAny": true, "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, "allowSyntheticDefaultImports": true, "allowJs": true, "skipLibCheck": true, diff --git a/website/src/partials/data_enhancement/AutofillFeature.tsx b/website/src/partials/data_enhancement/AutofillFeature.tsx index b1c1a7cc5..eb8af53da 100644 --- a/website/src/partials/data_enhancement/AutofillFeature.tsx +++ b/website/src/partials/data_enhancement/AutofillFeature.tsx @@ -36,21 +36,22 @@ export function AutofillFeature() { } right={<> -

Fill in the details of the Nike Air Max - 270

-

+

+ Fill in the details of the Nike Air Max 90 +

+

Use a prompt to indicate how you would like the autofill feature to work. Imagine you have an ecommerce store and - you want to add a new product to your catalog. + you want to add a new product to your catalog.

-

- ...or you are writing an article +

+ ...or you are writing an article

-

- ...or creating a course on Sustainability +

+ ...or creating the content of a course

-

- ...or anything you can think of +

+ ...or anything you can think of

diff --git a/website/tsconfig.json b/website/tsconfig.json index 373e03ac6..3468bc967 100644 --- a/website/tsconfig.json +++ b/website/tsconfig.json @@ -27,7 +27,6 @@ "noImplicitReturns": true, "noImplicitThis": true, "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, "jsxImportSource": "@emotion/react" }, "include": [