Skip to content

Commit

Permalink
Implemented empty state using new way of handling forms
Browse files Browse the repository at this point in the history
  • Loading branch information
mkurczewski committed Oct 2, 2024
1 parent 958dd36 commit defff31
Show file tree
Hide file tree
Showing 18 changed files with 336 additions and 58 deletions.
7 changes: 7 additions & 0 deletions libs/device/models/src/lib/feature/data-provider-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,17 @@ const formFieldsSchema = z.object({
fields: fieldsSchema,
})

const formFieldsSchemaV2 = z.object({
source: z.literal("form-fields-v2"),
formName: z.string(),
fields: fieldsSchema,
})

export const dataProviderSchema = z.union([
entitiesArraySchema,
entitiesFieldSchema,
formFieldsSchema,
formFieldsSchemaV2,
])

export type DataProviderConfig = z.infer<typeof dataProviderSchema>
9 changes: 2 additions & 7 deletions libs/generic-view/feature/src/lib/generic-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,17 @@
*/

import React, { FunctionComponent } from "react"
import { useParams } from "react-router"
import { GenericThemeProvider } from "generic-view/theme"
import RecursiveLayout from "./recursive-layout"
import GenericModals from "./generic-modals"
import { useDevConsole } from "./use-dev-console"
import { useDevViews } from "./use-dev-views/use-dev-views"
import { GenericToasts } from "./generic-toasts"
import { FormsProvider } from "generic-view/utils"
import { FormsProvider, useCurrentViewKey } from "generic-view/utils"

export const GenericView: FunctionComponent = () => {
useDevConsole()
const { viewKey, subviewKey } = useParams<{
viewKey: string
subviewKey?: string
}>()
const currentViewKey = subviewKey || viewKey
const currentViewKey = useCurrentViewKey()
useDevViews(currentViewKey)

return (
Expand Down
22 changes: 22 additions & 0 deletions libs/generic-view/feature/src/lib/setup-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
selectEntitiesData,
selectEntitiesIdFieldKey,
selectEntityData,
useFormField,
} from "generic-view/store"
import {
dataProviderFilter,
Expand Down Expand Up @@ -73,6 +74,12 @@ export const setupComponent = <P extends object>(
if (dataProvider) return
return selectComponentData(state, { viewKey, componentKey })
})
const formDataV2 = useFormField({
formName:
dataProvider?.source === "form-fields-v2"
? dataProvider.formName
: undefined,
})
const entitiesData =
useSelector((state: ReduxRootState) => {
if (dataProvider?.source !== "entities-array") return
Expand Down Expand Up @@ -136,6 +143,19 @@ export const setupComponent = <P extends object>(
: formContext.watch(providerField)
const value = processFormFields(config, fieldValue)

if (isString(value) && componentField === "dataItemId") {
dataItemId = value
continue
}
set(editableProps || {}, componentField, value)
}
} else if (dataProvider?.source === "form-fields-v2") {
for (const fieldConfig of dataProvider.fields) {
const { componentField, providerField, ...config } = fieldConfig
const value = processFormFields(
config,
formDataV2.getValue(providerField)
)
if (isString(value) && componentField === "dataItemId") {
dataItemId = value
continue
Expand All @@ -147,6 +167,7 @@ export const setupComponent = <P extends object>(
const layoutDependency = JSON.stringify(layout)
const styleDependency = JSON.stringify(style)
const dataProviderDependency = JSON.stringify(dataProvider)
const formDataV2Dependency = JSON.stringify(formDataV2)

const styles = useMemo(() => {
return setupStyles(style, layout)
Expand Down Expand Up @@ -194,6 +215,7 @@ export const setupComponent = <P extends object>(
editablePropsDependency,
dataProviderDependency,
styles,
formDataV2Dependency,
])
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ const view: View = {
contactsForm: {
component: "form",
config: {
defaultValues: {
searchedContact: undefined,
activeContactId: undefined,
selectedContacts: [],
allContacts: [],
},
formOptions: {
defaultValues: {
searchedContact: undefined,
Expand All @@ -40,10 +46,88 @@ const view: View = {
config: {
entitiesTypes: ["contacts"],
},
childrenKeys: ["contactsPanel", "contactsFormWrapper"],
childrenKeys: ["contactsPanelWrapper", "contactsFormWrapper", "emptyListWrapper"],
},
emptyListWrapper: {
component: "conditional-renderer",
dataProvider: {
source: "form-fields-v2",
formName: "contactsForm",
fields: [
{
providerField: "allContacts",
componentField: "data.render",
modifier: "length",
condition: "eq",
value: 0,
},
],
},
childrenKeys: ["fullScreenWrapper"],
},
fullScreenWrapper: {
component: "block-plain",
layout: {
gridPlacement: {
row: 1,
column: 1,
width: 1,
height: 2,
},
flexLayout: {
rowGap: "24px",
direction: "column",
justifyContent: "center",
alignItems: "center",
},
},
// importContactsButton already comes from the device through API
childrenKeys: ["emptyStateIcon", "emptyStateText", "importContactsButton"],
},
emptyStateIcon: {
component: "modal.titleIcon",
config: {
type: IconType.ContactsBook,
},
},
emptyStateText: {
component: "block-plain",
layout: {
flexLayout: {
direction: "column",
alignItems: "center",
rowGap: "8px",
},
},
childrenKeys: ["title", "detailText"],
},
contactsPanelWrapper: {
component: "conditional-renderer",
dataProvider: {
source: "form-fields-v2",
formName: "contactsForm",
fields: [
{
providerField: "allContacts",
componentField: "data.render",
modifier: "length",
condition: "gt",
value: 0,
},
],
},
childrenKeys: ["contactsPanel"],
},
contactsPanel: {
component: "block-plain",
layout: {
gridPlacement: {
row: 1,
column: 1,
width: 1,
height: 1,
},
},
childrenKeys: ["contactsPanelDefaultMode", "contactsPanelSelectMode"],
},
contactsPanelDefaultMode: {
Expand Down Expand Up @@ -404,6 +488,14 @@ const view: View = {
selectedIdsFieldName: "selectedContacts",
allIdsFieldName: "allContacts",
},
form: {
formName: "contactsForm",
assignFields: {
activeIdFieldName: "activeContactId",
selectedIdsFieldName: "selectedContacts",
allIdsFieldName: "allContacts",
},
},
},
childrenKeys: [
"columnCheckbox",
Expand Down
29 changes: 14 additions & 15 deletions libs/generic-view/models/src/lib/device.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,21 @@ import { DeviceProperties } from "device-manager/models"
import { ApiConfig, MenuConfig, OverviewData } from "device/models"
import { DeviceId } from "Core/device/constants/device-id"

export interface GenericForm {
fields: Record<string, unknown>
}

interface Feature<Data = Record<string, unknown>> {
config?: View
data?: Data
forms?: Record<string, GenericForm>
}

export type Features = {
"mc-overview"?: {
config?: View
data?: OverviewData
}
"mc-about"?: {
config?: View
data?: OverviewData
}
"mc-overview"?: Feature<OverviewData>
"mc-about"?: Feature<OverviewData>
} & {
[key: string]: {
config?: View
data?: Record<string, unknown>
}
[key: string]: Feature
}

export interface DeviceConfiguration {
Expand All @@ -31,9 +32,7 @@ export interface DeviceConfiguration {
features?: Features
}

export interface Device
extends DeviceProperties,
Partial<DeviceConfiguration> {
export interface Device extends DeviceProperties, Partial<DeviceConfiguration> {
id: DeviceId
serialNumber: string | undefined
deviceType: DeviceType
Expand Down
2 changes: 2 additions & 0 deletions libs/generic-view/models/src/lib/form.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const dataValidator = z.undefined()

interface Config {
formOptions?: Pick<UseFormProps, "mode" | "reValidateMode" | "defaultValues">
defaultValues?: Record<string, unknown>
}

const configValidator: z.ZodType<Config | undefined> = z
Expand All @@ -21,6 +22,7 @@ const configValidator: z.ZodType<Config | undefined> = z
reValidateMode: z.enum(["onChange", "onBlur", "onSubmit"]).optional(),
defaultValues: z.record(z.string(), z.any()).optional(),
}),
defaultValues: z.record(z.string(), z.any()).optional(),
})
.optional()

Expand Down
8 changes: 8 additions & 0 deletions libs/generic-view/models/src/lib/table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ const configValidator = z.object({
selectedIdsFieldName: z.string().optional(),
allIdsFieldName: z.string().optional(),
}),
form: z.object({
formName: z.string(),
assignFields: z.object({
activeIdFieldName: z.string().optional(),
selectedIdsFieldName: z.string().optional(),
allIdsFieldName: z.string().optional(),
}),
}),
columnsNames: z.array(z.string()).optional(),
})

Expand Down
3 changes: 3 additions & 0 deletions libs/generic-view/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ export * from "./lib/toasts/actions"
export * from "./lib/toasts/open-toast.action"

export * from "./lib/action-names"

export * from "./lib/use-form-field"
export * from "./lib/use-form-register"
4 changes: 2 additions & 2 deletions libs/generic-view/store/src/lib/action-names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ export enum ActionName {
GetOutboxData = "api-actions/get-outbox-data",

SetMenu = "generic-views/set-menu",
SetViewLayout = "generic-views/set-view-layout",
SetViewData = "generic-views/set-view-data",
SetLastRefresh = "generic-views/set-last-refresh",
AddDevice = "generic-views/add-device",
RemoveDevice = "generic-views/remove-device",
SetDeviceState = "generic-views/set-device-state",
SetGenericConfig = "generic-views/set-generic-config",
RegisterForm = "generic-views/register-form",
SetFormField = "generic-views/set-form-field",

OpenModal = "generic-modals/open-modal",
CloseModal = "generic-modals/close-modal",
Expand Down
10 changes: 10 additions & 0 deletions libs/generic-view/store/src/lib/selectors/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,13 @@ export const selectViewData = createSelector(
selectView,
(config) => config?.data
)

const selectViewForms = createSelector(selectView, (config) => config?.forms)

export const selectViewForm = createSelector(
selectViewForms,
(state: ReduxRootState, { formName }: { formName: string }) => formName,
(forms, formName) => {
return forms?.[formName]
}
)
55 changes: 55 additions & 0 deletions libs/generic-view/store/src/lib/use-form-field.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright (c) Mudita sp. z o.o. All rights reserved.
* For licensing, see https://github.com/mudita/mudita-center/blob/master/LICENSE.md
*/

import { useDispatch, useSelector } from "react-redux"
import { Dispatch, ReduxRootState } from "Core/__deprecated__/renderer/store"
import { selectActiveApiDeviceId } from "./selectors/select-active-api-device-id"
import { selectViewForm } from "./selectors"
import { setFormField } from "./views/actions"
import { get } from "lodash"
import { useCallback } from "react"
import { useCurrentViewKey } from "generic-view/utils"

interface UseViewForm {
formName?: string
}

export const useFormField = ({ formName }: UseViewForm) => {
const viewKey = useCurrentViewKey()
const dispatch = useDispatch<Dispatch>()
const activeDeviceId = useSelector(selectActiveApiDeviceId)
const form = useSelector((state: ReduxRootState) => {
if (!formName) return undefined
return selectViewForm(state, { formName, viewKey })
})

const getValue = useCallback(
(field: string) => {
return field && form?.fields ? get(form.fields, field) : undefined
},
[form?.fields]
)

const setValue = useCallback(
(field: string, value: unknown) => {
if (!activeDeviceId || !field || !formName) return
dispatch(
setFormField({
field,
value,
deviceId: activeDeviceId,
feature: viewKey,
formName,
})
)
},
[activeDeviceId, dispatch, formName, viewKey]
)

return {
getValue,
setValue,
}
}
Loading

0 comments on commit defff31

Please sign in to comment.