@headless-adminapp/app 1.1.12 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/board/BoardColumnDataResolver.js +33 -1
  2. package/board/context.d.ts +1 -0
  3. package/board/hooks/useQuickFilter.d.ts +1 -0
  4. package/board/hooks/useQuickFilter.js +20 -0
  5. package/board/types.d.ts +2 -0
  6. package/builders/CommandBuilder/DefaultCommandBuilder.js +5 -0
  7. package/builders/CommandBuilder/ViewCommandBuilder.d.ts +5 -0
  8. package/builders/CommandBuilder/ViewCommandBuilder.js +84 -3
  9. package/builders/DefineViewExperience.d.ts +1 -1
  10. package/builders/SchemaExperienceBuilder.d.ts +1 -1
  11. package/builders/utils.js +2 -1
  12. package/calendar/CalendarProvider.d.ts +8 -0
  13. package/calendar/CalendarProvider.js +18 -0
  14. package/calendar/hooks/useDeleteEvent.d.ts +1 -0
  15. package/calendar/hooks/useDeleteEvent.js +54 -0
  16. package/calendar/hooks/useOpenDetailDialog.d.ts +2 -0
  17. package/calendar/hooks/useOpenDetailDialog.js +61 -0
  18. package/calendar/types.d.ts +25 -6
  19. package/components/ScrollView/index.js +9 -8
  20. package/dataform/utils/index.js +27 -0
  21. package/datagrid/DataGridProvider/DataResolver.js +22 -2
  22. package/datagrid/DataGridProvider/index.js +2 -0
  23. package/datagrid/DataGridProvider/utils.d.ts +2 -1
  24. package/datagrid/DataGridProvider/utils.js +38 -3
  25. package/datagrid/context.d.ts +1 -0
  26. package/datagrid/hooks/useQuickFilter.d.ts +1 -0
  27. package/datagrid/hooks/useQuickFilter.js +20 -0
  28. package/form/FormValidationStringContext.d.ts +1 -0
  29. package/form/FormValidationStringContext.js +1 -0
  30. package/package.json +2 -2
  31. package/recordset/RecordSetProvider.js +1 -1
  32. package/recordset/hooks/useRecordSetResult.js +1 -0
  33. package/transport/hooks/useRetriveRecords.js +9 -2
@@ -2,24 +2,56 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DataResolver = DataResolver;
4
4
  const react_1 = require("react");
5
+ const auth_1 = require("../auth");
5
6
  const hooks_1 = require("../hooks");
6
7
  const mutable_1 = require("../mutable");
7
8
  const useRetriveRecords_1 = require("../transport/hooks/useRetriveRecords");
8
9
  const context_1 = require("./context");
9
10
  const useBoardColumnConfig_1 = require("./hooks/useBoardColumnConfig");
10
11
  const useBoardConfig_1 = require("./hooks/useBoardConfig");
12
+ const useQuickFilter_1 = require("./hooks/useQuickFilter");
11
13
  const useSearchText_1 = require("./hooks/useSearchText");
12
14
  const MAX_RECORDS = 10000;
13
15
  function DataResolver() {
14
16
  const { schema, sorting, projection: { columns, expand }, } = (0, useBoardConfig_1.useBoardConfig)();
15
17
  const [searchText] = (0, useSearchText_1.useSearchText)();
18
+ const [quickFilter, quickFilterValues] = (0, useQuickFilter_1.useQuickFilter)();
19
+ const authSession = (0, auth_1.useAuthSession)();
16
20
  const { filter, maxRecords = MAX_RECORDS } = (0, useBoardColumnConfig_1.useBoardColumnConfig)();
17
21
  const setState = (0, mutable_1.useContextSetValue)(context_1.BoardColumnContext);
18
22
  const [search] = (0, hooks_1.useDebouncedValue)(searchText, 500);
23
+ const quickFilterResults = (0, react_1.useMemo)(() => {
24
+ if (!quickFilter) {
25
+ return null;
26
+ }
27
+ if (!quickFilterValues) {
28
+ return null;
29
+ }
30
+ return quickFilter.resolver(quickFilterValues, authSession);
31
+ }, [authSession, quickFilter, quickFilterValues]);
32
+ const mergedFilter = (0, react_1.useMemo)(() => {
33
+ const filters = [];
34
+ if (filter) {
35
+ filters.push(filter);
36
+ }
37
+ if (quickFilterResults) {
38
+ filters.push(quickFilterResults);
39
+ }
40
+ if (filters.length === 0) {
41
+ return null;
42
+ }
43
+ if (filters.length === 1) {
44
+ return filters[0];
45
+ }
46
+ return {
47
+ type: 'and',
48
+ filters,
49
+ };
50
+ }, [filter, quickFilterResults]);
19
51
  const { fetchNextPage, data, hasNextPage, isFetching, isFetchingNextPage, queryKey, } = (0, useRetriveRecords_1.useRetriveRecords)({
20
52
  columns,
21
53
  expand,
22
- filter,
54
+ filter: mergedFilter,
23
55
  maxRecords,
24
56
  schema,
25
57
  search,
@@ -4,6 +4,7 @@ import { BoardColumnConfig, BoardConfig } from './types';
4
4
  export interface BoardContextState<S extends SchemaAttributes = SchemaAttributes> {
5
5
  config: BoardConfig<S>;
6
6
  searchText: string;
7
+ quickFilterValues: Record<string, unknown>;
7
8
  }
8
9
  export interface BoardColumnContextState<S extends SchemaAttributes = SchemaAttributes> {
9
10
  config: BoardColumnConfig;
@@ -0,0 +1 @@
1
+ export declare function useQuickFilter(): readonly [import("@headless-adminapp/core/experience/view/QuickFilter").QuickFilter<import("@headless-adminapp/core/schema").SchemaAttributes> | undefined, Record<string, unknown>, (key: string, value: unknown) => void];
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useQuickFilter = useQuickFilter;
4
+ const react_1 = require("react");
5
+ const context_1 = require("../../mutable/context");
6
+ const context_2 = require("../context");
7
+ function useQuickFilter() {
8
+ const quickFilter = (0, context_1.useContextSelector)(context_2.BoardContext, (state) => state.config.quickFilter);
9
+ const values = (0, context_1.useContextSelector)(context_2.BoardContext, (state) => state.quickFilterValues);
10
+ const _setValue = (0, context_1.useContextSetValue)(context_2.BoardContext);
11
+ const setValue = (0, react_1.useCallback)((key, value) => {
12
+ _setValue((state) => ({
13
+ quickFilterValues: {
14
+ ...state.quickFilterValues,
15
+ [key]: value,
16
+ },
17
+ }));
18
+ }, [_setValue]);
19
+ return [quickFilter, values, setValue];
20
+ }
package/board/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { CommandContextBase } from '@headless-adminapp/core/experience/command';
2
2
  import { SortingState, View } from '@headless-adminapp/core/experience/view';
3
+ import { QuickFilter } from '@headless-adminapp/core/experience/view/QuickFilter';
3
4
  import { Schema, SchemaAttributes } from '@headless-adminapp/core/schema';
4
5
  import { Filter } from '@headless-adminapp/core/transport';
5
6
  import { FC } from 'react';
@@ -48,4 +49,5 @@ export interface BoardConfig<S extends SchemaAttributes = SchemaAttributes> {
48
49
  emptyMessage?: string;
49
50
  minColumnWidth?: number;
50
51
  maxColumnWidth?: number;
52
+ quickFilter?: QuickFilter;
51
53
  }
@@ -34,6 +34,11 @@ var DefaultCommandBuilder;
34
34
  text: strings.new,
35
35
  localizedTexts: extractLocalizedStrings(localizedSrings, (x) => x.new),
36
36
  }),
37
+ ViewCommandBuilder_1.ViewCommandBuilder.createNewRecordForVirtualCommand({
38
+ Icon: icons.New,
39
+ text: strings.new,
40
+ localizedTexts: extractLocalizedStrings(localizedSrings, (x) => x.new),
41
+ }),
37
42
  ViewCommandBuilder_1.ViewCommandBuilder.createEditRecordCommand({
38
43
  Icon: icons.Edit,
39
44
  text: strings.edit,
@@ -15,6 +15,11 @@ export declare namespace ViewCommandBuilder {
15
15
  text: string;
16
16
  localizedTexts?: Record<string, string>;
17
17
  }): EntityMainGridCommandItemExperience;
18
+ function createNewRecordForVirtualCommand({ Icon, text, localizedTexts, }: {
19
+ Icon: Icon;
20
+ text: string;
21
+ localizedTexts?: Record<string, string>;
22
+ }): EntityMainGridCommandItemExperience;
18
23
  function createEditRecordCommand({ Icon, text, localizedTexts, }: {
19
24
  Icon: Icon;
20
25
  text: string;
@@ -86,6 +86,54 @@ var ViewCommandBuilder;
86
86
  };
87
87
  }
88
88
  ViewCommandBuilder.createNewRecordCommand = createNewRecordCommand;
89
+ function createNewRecordForVirtualCommand({ Icon, text, localizedTexts, }) {
90
+ return {
91
+ type: 'button',
92
+ Icon,
93
+ text,
94
+ localizedText: localizedTexts,
95
+ onClick: async (context) => {
96
+ if (!context.primaryControl.schema.baseSchemaLogicalNames?.length) {
97
+ return;
98
+ }
99
+ const data = await context.utility.openPromptDialog({
100
+ title: 'Select type',
101
+ attributes: {
102
+ type: {
103
+ type: 'choice',
104
+ label: 'Select type',
105
+ string: true,
106
+ required: true,
107
+ options: context.primaryControl.schema.baseSchemaLogicalNames?.map((x) => {
108
+ const schema = context.stores.schemaStore.getSchema(x);
109
+ return {
110
+ value: x,
111
+ label: schema.label,
112
+ };
113
+ }),
114
+ },
115
+ },
116
+ defaultValues: {},
117
+ });
118
+ if (!data) {
119
+ return;
120
+ }
121
+ context.navigation.openForm({
122
+ logicalName: data.type,
123
+ });
124
+ },
125
+ hidden: (context) => {
126
+ if (EnabledRules.IsPhysicalSchema(context)) {
127
+ return true;
128
+ }
129
+ if (!context.primaryControl.schema.baseSchemaLogicalNames?.length) {
130
+ return true;
131
+ }
132
+ return false;
133
+ },
134
+ };
135
+ }
136
+ ViewCommandBuilder.createNewRecordForVirtualCommand = createNewRecordForVirtualCommand;
89
137
  function createEditRecordCommand({ Icon, text, localizedTexts, }) {
90
138
  return {
91
139
  type: 'button',
@@ -94,11 +142,35 @@ var ViewCommandBuilder;
94
142
  localizedText: localizedTexts,
95
143
  isContextMenu: true,
96
144
  onClick: (context) => {
97
- context.primaryControl.openRecord(context.primaryControl.selectedIds[0], context.primaryControl.schema.logicalName);
145
+ if (EnabledRules.IsPhysicalSchema(context)) {
146
+ context.primaryControl.openRecord(context.primaryControl.selectedIds[0], context.primaryControl.schema.logicalName);
147
+ }
148
+ else {
149
+ if (!context.primaryControl.schema.virtualLogicalNameAttribute) {
150
+ return;
151
+ }
152
+ const logicalName = context.primaryControl.selectedRecords[0][context.primaryControl.schema.virtualLogicalNameAttribute];
153
+ if (!logicalName) {
154
+ return;
155
+ }
156
+ context.primaryControl.openRecord(context.primaryControl.selectedIds[0], logicalName);
157
+ }
98
158
  },
99
159
  hidden: [
100
160
  (context) => !EnabledRules.HasSingleRecordSelected(context),
101
- (context) => !EnabledRules.IsPhysicalSchema(context),
161
+ (context) => {
162
+ if (EnabledRules.IsPhysicalSchema(context)) {
163
+ return false;
164
+ }
165
+ if (!context.primaryControl.schema.virtualLogicalNameAttribute) {
166
+ return true;
167
+ }
168
+ const logicalName = context.primaryControl.selectedRecords[0][context.primaryControl.schema.virtualLogicalNameAttribute];
169
+ if (!logicalName) {
170
+ return true;
171
+ }
172
+ return false;
173
+ },
102
174
  ],
103
175
  };
104
176
  }
@@ -115,7 +187,16 @@ var ViewCommandBuilder;
115
187
  },
116
188
  hidden: [
117
189
  (context) => !EnabledRules.HasSingleRecordSelected(context),
118
- (context) => EnabledRules.IsPhysicalSchema(context),
190
+ (context) => {
191
+ if (EnabledRules.IsPhysicalSchema(context)) {
192
+ return true;
193
+ }
194
+ if (context.primaryControl.schema.virtualLogicalNameAttribute ||
195
+ context.primaryControl.schema.baseSchemaLogicalName) {
196
+ return true;
197
+ }
198
+ return false;
199
+ },
119
200
  ],
120
201
  };
121
202
  }
@@ -1,7 +1,7 @@
1
1
  import type { ViewColumn as $ViewColumn, ViewExperience as $ViewExperience } from '@headless-adminapp/core/experience/view';
2
2
  import type { SchemaAttributes } from '@headless-adminapp/core/schema';
3
3
  export declare namespace DefineViewExperience {
4
- export type Experience<S extends SchemaAttributes = SchemaAttributes> = Pick<$ViewExperience<S>, 'filter' | 'defaultSorting' | 'card'> & {
4
+ export type Experience<S extends SchemaAttributes = SchemaAttributes> = Pick<$ViewExperience<S>, 'filter' | 'defaultSorting' | 'card' | 'quickFilter'> & {
5
5
  grid: GridView<S>;
6
6
  };
7
7
  type GridView<S extends SchemaAttributes = SchemaAttributes> = {
@@ -39,7 +39,7 @@ export declare class SchemaExperienceBuilder<S extends SchemaAttributes = Schema
39
39
  private readonly defaults?;
40
40
  private readonly options?;
41
41
  constructor(schema: Schema<S>, defaults?: SchemaExperienceBuilderDefaults | undefined, options?: SchemaExperienceBuilderOptions | undefined);
42
- defineViewExperience(viewExperience: Pick<DefineViewExperience.Experience<S>, 'filter' | 'defaultSorting'> & {
42
+ defineViewExperience(viewExperience: Pick<DefineViewExperience.Experience<S>, 'filter' | 'defaultSorting' | 'quickFilter'> & {
43
43
  grid?: DefineViewExperience.Experience<S>['grid'];
44
44
  card?: DefineViewExperience.Experience<S>['card'];
45
45
  }): ViewExperience<S>;
package/builders/utils.js CHANGED
@@ -162,7 +162,8 @@ async function retriveRecords({ gridColumns, dataService, schema, search, view,
162
162
  search,
163
163
  columns: columns,
164
164
  expand,
165
- filter: (0, utils_1.mergeConditions)(schema, view.experience.filter, extraFilter, columnFilters, schemaStore),
165
+ filter: (0, utils_1.mergeConditions)(schema, view.experience.filter, extraFilter, null, // quickFilterResults
166
+ columnFilters, schemaStore),
166
167
  skip,
167
168
  limit,
168
169
  sort: sorting,
@@ -0,0 +1,8 @@
1
+ import { SchemaAttributes } from '@headless-adminapp/core/schema';
2
+ import { PropsWithChildren } from 'react';
3
+ import { CalendarConfig } from './types';
4
+ interface CalendarProviderProps<SA1 extends SchemaAttributes = SchemaAttributes, SA2 extends SchemaAttributes = SchemaAttributes, SA3 extends SchemaAttributes = SchemaAttributes> {
5
+ config: CalendarConfig<SA1, SA2, SA3>;
6
+ }
7
+ export declare function CalendarProvider<SA1 extends SchemaAttributes = SchemaAttributes, SA2 extends SchemaAttributes = SchemaAttributes, SA3 extends SchemaAttributes = SchemaAttributes>(props: Readonly<PropsWithChildren<CalendarProviderProps<SA1, SA2, SA3>>>): import("react/jsx-runtime").JSX.Element;
8
+ export {};
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CalendarProvider = CalendarProvider;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const mutable_1 = require("../mutable");
7
+ const context_1 = require("./context");
8
+ function CalendarProvider(props) {
9
+ const contextValue = (0, mutable_1.useCreateContextStore)({
10
+ config: props.config,
11
+ });
12
+ (0, react_1.useEffect)(() => {
13
+ contextValue.setValue({
14
+ config: props.config,
15
+ });
16
+ }, [contextValue, props.config]);
17
+ return ((0, jsx_runtime_1.jsx)(context_1.CalendarContext.Provider, { value: contextValue, children: props.children }));
18
+ }
@@ -0,0 +1 @@
1
+ export declare function useDeleteEvent(): (id: string) => Promise<void>;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useDeleteEvent = useDeleteEvent;
4
+ const dialog_1 = require("@headless-adminapp/app/dialog");
5
+ const progress_indicator_1 = require("@headless-adminapp/app/progress-indicator");
6
+ const toast_notification_1 = require("@headless-adminapp/app/toast-notification");
7
+ const react_query_1 = require("@tanstack/react-query");
8
+ const react_1 = require("react");
9
+ const useConfig_1 = require("./useConfig");
10
+ function useDeleteEvent() {
11
+ const openConfirmDialog = (0, dialog_1.useOpenConfirmDialog)();
12
+ const openToastNotification = (0, toast_notification_1.useOpenToastNotification)();
13
+ const { hideProgressIndicator, showProgressIndicator } = (0, progress_indicator_1.useProgressIndicator)();
14
+ const config = (0, useConfig_1.useConfig)();
15
+ const queryClient = (0, react_query_1.useQueryClient)();
16
+ const deleteEvent = (0, react_1.useCallback)(async (id) => {
17
+ try {
18
+ if (!config.deleteEvent) {
19
+ throw new Error('Delete event function is not defined.');
20
+ }
21
+ const confirmResult = await openConfirmDialog({
22
+ title: 'Delete Event',
23
+ text: 'Are you sure you want to delete this event?',
24
+ });
25
+ if (!confirmResult?.confirmed) {
26
+ return;
27
+ }
28
+ showProgressIndicator('Deleting event...');
29
+ await config.deleteEvent(id);
30
+ await queryClient.invalidateQueries({
31
+ queryKey: ['calendar-events'],
32
+ });
33
+ close();
34
+ }
35
+ catch (error) {
36
+ openToastNotification({
37
+ title: 'Error',
38
+ text: error.message,
39
+ type: 'error',
40
+ });
41
+ }
42
+ finally {
43
+ hideProgressIndicator();
44
+ }
45
+ }, [
46
+ config,
47
+ hideProgressIndicator,
48
+ openConfirmDialog,
49
+ openToastNotification,
50
+ queryClient,
51
+ showProgressIndicator,
52
+ ]);
53
+ return deleteEvent;
54
+ }
@@ -0,0 +1,2 @@
1
+ import { SchemaAttributes } from '@headless-adminapp/core/schema';
2
+ export declare function useOpenDetailDialog<SA1 extends SchemaAttributes = SchemaAttributes, SA2 extends SchemaAttributes = SchemaAttributes>(DialogComponent: React.ComponentType<any>): (values: unknown) => void;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useOpenDetailDialog = useOpenDetailDialog;
4
+ const react_query_1 = require("@tanstack/react-query");
5
+ const useOpenDialog_1 = require("../../dialog/hooks/useOpenDialog");
6
+ const useOpenToastNotification_1 = require("../../toast-notification/hooks/useOpenToastNotification");
7
+ const useConfig_1 = require("./useConfig");
8
+ function useOpenDetailDialog(DialogComponent) {
9
+ const openDialog = (0, useOpenDialog_1.useOpenDialog)();
10
+ const openToastNotification = (0, useOpenToastNotification_1.useOpenToastNotification)();
11
+ const config = (0, useConfig_1.useConfig)();
12
+ const queryClient = (0, react_query_1.useQueryClient)();
13
+ function openEventDetailModel(values) {
14
+ const { close } = openDialog({
15
+ type: 'custom',
16
+ Component: DialogComponent,
17
+ props: {
18
+ onDismiss: () => {
19
+ close();
20
+ },
21
+ config,
22
+ values,
23
+ onSubmit: async (data) => {
24
+ if (!config.saveEvent) {
25
+ return;
26
+ }
27
+ try {
28
+ const { id, end, start, title, allDay, description, ...rest } = data.values;
29
+ await config.saveEvent({
30
+ event: {
31
+ id,
32
+ title,
33
+ start: start ? new Date(start) : null,
34
+ end: end ? new Date(end) : null,
35
+ allDay: allDay ?? false,
36
+ description,
37
+ ...rest,
38
+ },
39
+ modifiedValues: data.modifiedValues,
40
+ });
41
+ await queryClient.invalidateQueries({
42
+ queryKey: ['calendar-events'],
43
+ });
44
+ close();
45
+ }
46
+ catch (error) {
47
+ openToastNotification({
48
+ title: 'Error',
49
+ text: error.message,
50
+ type: 'error',
51
+ });
52
+ }
53
+ },
54
+ onCancel: () => {
55
+ close();
56
+ },
57
+ },
58
+ });
59
+ }
60
+ return openEventDetailModel;
61
+ }
@@ -1,5 +1,7 @@
1
1
  import { AuthSession } from '@headless-adminapp/core/experience/auth';
2
+ import { OpenFormOptions } from '@headless-adminapp/core/experience/command';
2
3
  import { InferredSchemaType, SchemaAttributes } from '@headless-adminapp/core/schema';
4
+ import { InternalRouteResolver, RouterInstance } from '../route/context';
3
5
  export interface CalendarEvent {
4
6
  id: string;
5
7
  title: string;
@@ -28,18 +30,35 @@ export type CalendarEventSaveFn<SA1 extends SchemaAttributes = SchemaAttributes,
28
30
  export interface CalendarConfig<SA1 extends SchemaAttributes = SchemaAttributes, SA2 extends SchemaAttributes = SchemaAttributes, SA3 extends SchemaAttributes = SchemaAttributes> {
29
31
  eventsResolver: CalendarEventsResolverFn<SA3>;
30
32
  eventResolver?: CalendarEventResolverFn;
31
- saveEvent: CalendarEventSaveFn<SA1, SA2>;
33
+ saveEvent?: CalendarEventSaveFn<SA1, SA2>;
32
34
  beforeDescriptionAttributes?: SA1;
33
35
  afterDescriptionAttributes?: SA2;
34
36
  filterAttributes?: SA3;
35
37
  defaultFilter?: InferredSchemaType<SA3>;
36
- openRecord?: (id: string) => void;
37
- deleteEvent: (id: string) => Promise<void>;
38
+ deleteEvent?: (id: string) => Promise<void>;
38
39
  title: string;
39
40
  description: string;
40
41
  eventLabel: string;
41
- disableCreate?: boolean;
42
- disableEdit?: boolean;
43
- disableDelete?: boolean;
44
42
  disableAllDay?: boolean;
43
+ createOptions?: {
44
+ mode: 'dialog';
45
+ } | {
46
+ mode: 'custom';
47
+ allowQuickCreate?: boolean;
48
+ onClick: (defaultValues: Record<string, unknown>, options: {
49
+ router: RouterInstance;
50
+ routeResolver: InternalRouteResolver;
51
+ openForm: (options: OpenFormOptions) => void;
52
+ }) => void;
53
+ };
54
+ editOptions?: {
55
+ mode: 'dialog';
56
+ } | {
57
+ mode: 'custom';
58
+ onClick: (event: CalendarEvent, options: {
59
+ router: RouterInstance;
60
+ routeResolver: InternalRouteResolver;
61
+ openForm: (options: OpenFormOptions) => void;
62
+ }) => void;
63
+ };
45
64
  }
@@ -15,20 +15,21 @@ const ScrollView = ({ children, className, rtl, autoHide, onScroll, shadowEffect
15
15
  const [isTop, setIsTop] = (0, react_1.useState)(true);
16
16
  const [isBottom, setIsBottom] = (0, react_1.useState)(false);
17
17
  const handleScroll = (event) => {
18
- const div = event.target;
19
- const remainingSpace = getReaminingSpace(div);
20
- const _isTop = div.scrollTop === 0;
21
- const _isBottom = remainingSpace <= 0;
18
+ if (onScroll) {
19
+ onScroll(event);
20
+ }
21
+ };
22
+ const handleOnUpdate = (values) => {
23
+ const remainingSpace = values.scrollHeight - values.scrollTop - values.clientHeight;
24
+ const _isTop = values.scrollTop === 0;
25
+ const _isBottom = remainingSpace <= 10;
22
26
  if (isTop !== _isTop) {
23
27
  setIsTop(_isTop);
24
28
  }
25
29
  if (isBottom !== _isBottom) {
26
30
  setIsBottom(_isBottom);
27
31
  }
28
- if (onScroll) {
29
- onScroll(event);
30
- }
31
32
  };
32
- return ((0, jsx_runtime_1.jsx)(react_custom_scrollbars_2_1.Scrollbars, { autoHide: autoHide, className: (0, clsx_1.default)(className, 'hdl-scrollbar', rtl && 'rtl', shadowEffect && !isTop && 'hdl-scrollbar-shadow-top', shadowEffect && !isBottom && 'hdl-scrollbar-shadow-bottom'), onScroll: handleScroll, children: children }));
33
+ return ((0, jsx_runtime_1.jsx)(react_custom_scrollbars_2_1.Scrollbars, { autoHide: autoHide, className: (0, clsx_1.default)(className, 'hdl-scrollbar', rtl && 'rtl', shadowEffect && !isTop && 'hdl-scrollbar-shadow-top', shadowEffect && !isBottom && 'hdl-scrollbar-shadow-bottom'), onScroll: handleScroll, onUpdate: handleOnUpdate, children: children }));
33
34
  };
34
35
  exports.ScrollView = ScrollView;
@@ -33,6 +33,8 @@ const utils_1 = require("../../dataform/DataFormProvider/utils");
33
33
  const utils_2 = require("../../locale/utils");
34
34
  var saveRecord_1 = require("./saveRecord");
35
35
  Object.defineProperty(exports, "saveRecord", { enumerable: true, get: function () { return saveRecord_1.saveRecord; } });
36
+ const guidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
37
+ const objectIdRegex = /^[0-9a-f]{24}$/i;
36
38
  function getInitialValues({ cloneRecord, form, record, recordId, schema, defaultParameters, }) {
37
39
  const formColumns = (0, utils_1.getColumns)(form, schema);
38
40
  const editableGridControls = (0, utils_1.getControls)(form).filter((control) => control.type === 'editablegrid' && control.alias !== false);
@@ -253,6 +255,14 @@ function createAttributeValidationSchema(attribute) {
253
255
  case 'lookups':
254
256
  validationSchema = yup.array().nullable();
255
257
  break;
258
+ case 'id':
259
+ if ('number' in attribute && attribute.number) {
260
+ validationSchema = yup.number().nullable();
261
+ }
262
+ else {
263
+ validationSchema = yup.string().nullable();
264
+ }
265
+ break;
256
266
  default:
257
267
  validationSchema = yup.mixed().nullable();
258
268
  break;
@@ -284,6 +294,14 @@ function extendAttributeValidationSchema({ attribute, validationSchema, label, s
284
294
  strings,
285
295
  });
286
296
  break;
297
+ case 'id':
298
+ validationSchema = extendAttributeIdValidationSchema({
299
+ attribute,
300
+ validationSchema: validationSchema,
301
+ label,
302
+ strings,
303
+ });
304
+ break;
287
305
  default:
288
306
  break;
289
307
  }
@@ -337,6 +355,15 @@ function extendAttributeAttachmentsValidationSchema({ attribute, validationSchem
337
355
  }
338
356
  return validationSchema;
339
357
  }
358
+ function extendAttributeIdValidationSchema({ attribute, validationSchema, label, strings, }) {
359
+ if ('guid' in attribute && attribute.guid) {
360
+ validationSchema = validationSchema.matches(guidRegex, `${label}: ${strings.invalidIdFormat}`);
361
+ }
362
+ else if ('objectId' in attribute && attribute.objectId) {
363
+ validationSchema = validationSchema.matches(objectIdRegex, `${label}: ${strings.invalidIdFormat}`);
364
+ }
365
+ return validationSchema;
366
+ }
340
367
  exports.generateAttributeValidationSchema = (0, lodash_1.memoize)(function generateAttributeValidationSchema(attribute, language, strings, region) {
341
368
  let validationSchema = createAttributeValidationSchema(attribute);
342
369
  const label = (0, utils_2.localizedLabel)(language, attribute);
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.DataResolver = DataResolver;
4
4
  const react_1 = require("react");
5
+ const auth_1 = require("../../auth");
5
6
  const hooks_1 = require("../../hooks");
6
7
  const useDebouncedValue_1 = require("../../hooks/useDebouncedValue");
7
8
  const useMetadata_1 = require("../../metadata/hooks/useMetadata");
@@ -10,6 +11,7 @@ const useRetriveRecords_1 = require("../../transport/hooks/useRetriveRecords");
10
11
  const context_2 = require("../context");
11
12
  const hooks_2 = require("../hooks");
12
13
  const useGridDisabled_1 = require("../hooks/useGridDisabled");
14
+ const useQuickFilter_1 = require("../hooks/useQuickFilter");
13
15
  const utils_1 = require("./utils");
14
16
  function DataResolver() {
15
17
  const schema = (0, hooks_2.useDataGridSchema)();
@@ -22,6 +24,8 @@ function DataResolver() {
22
24
  const maxRecords = (0, hooks_2.useMaxRecords)();
23
25
  const [selectedIds] = (0, hooks_2.useGridSelection)();
24
26
  const disabled = (0, useGridDisabled_1.useGridDisabled)();
27
+ const [quickFilter, quickFilterValues] = (0, useQuickFilter_1.useQuickFilter)();
28
+ const authSession = (0, auth_1.useAuthSession)();
25
29
  const { schemaStore } = (0, useMetadata_1.useMetadata)();
26
30
  const selectedIdsRef = (0, react_1.useRef)(selectedIds);
27
31
  selectedIdsRef.current = selectedIds;
@@ -37,9 +41,25 @@ function DataResolver() {
37
41
  const expand = (0, react_1.useMemo)(() => isMobile
38
42
  ? (0, utils_1.collectCardExpandedKeys)({ cardView: view.experience.card })
39
43
  : (0, utils_1.collectExpandedKeys)(gridColumns), [gridColumns, isMobile, view.experience.card]);
44
+ const quickFilterResults = (0, react_1.useMemo)(() => {
45
+ if (!quickFilter) {
46
+ return null;
47
+ }
48
+ if (!quickFilterValues) {
49
+ return null;
50
+ }
51
+ return quickFilter.resolver(quickFilterValues, authSession);
52
+ }, [authSession, quickFilter, quickFilterValues]);
40
53
  const filter = (0, react_1.useMemo)(() => {
41
- return (0, utils_1.mergeConditions)(schema, view.experience.filter, extraFilter, columnFilters, schemaStore);
42
- }, [columnFilters, extraFilter, schema, schemaStore, view.experience.filter]);
54
+ return (0, utils_1.mergeConditions)(schema, view.experience.filter, extraFilter, quickFilterResults, columnFilters, schemaStore);
55
+ }, [
56
+ columnFilters,
57
+ extraFilter,
58
+ schema,
59
+ schemaStore,
60
+ view.experience.filter,
61
+ quickFilterResults,
62
+ ]);
43
63
  const { fetchNextPage, data, hasNextPage, isFetching, isFetchingNextPage, queryKey, } = (0, useRetriveRecords_1.useRetriveRecords)({
44
64
  columns,
45
65
  expand,
@@ -28,6 +28,7 @@ function DataGridProvider(props) {
28
28
  searchText: '',
29
29
  selectedIds: [],
30
30
  sorting: props.view.experience.defaultSorting ?? [],
31
+ quickFilterValues: props.view.experience.quickFilter?.defaultValues ?? {},
31
32
  view: props.view,
32
33
  commands: props.commands,
33
34
  columns: (0, transformViewColumns_1.transformViewColumns)(props.view.logicalName, props.view.experience.grid.columns, schemaStore, language),
@@ -52,6 +53,7 @@ function DataGridProvider(props) {
52
53
  searchText: '',
53
54
  selectedIds: [],
54
55
  sorting: props.view.experience.defaultSorting ?? [],
56
+ quickFilterValues: props.view.experience.quickFilter?.defaultValues ?? {},
55
57
  columns: (0, transformViewColumns_1.transformViewColumns)(props.view.logicalName, props.view.experience.grid.columns, schemaStore, language),
56
58
  maxRecords: props.maxRecords,
57
59
  });
@@ -4,7 +4,8 @@ import { ISchemaStore } from '@headless-adminapp/core/store';
4
4
  import { Filter } from '@headless-adminapp/core/transport';
5
5
  import { TransformedViewColumn } from '../context';
6
6
  export declare function transformColumnFilter<S extends SchemaAttributes = SchemaAttributes>(filter: Partial<Record<string, ColumnCondition>>, schema: Schema<S>, schemaStore: ISchemaStore): Record<string, ColumnCondition> | null;
7
- export declare function mergeConditions<S extends SchemaAttributes = SchemaAttributes>(schema: Schema<S>, filter: Filter | null | undefined, extraFilter: Filter | null | undefined, columnFilters: Partial<Record<string, ColumnCondition>> | undefined, schemaStore: ISchemaStore): Filter | null;
7
+ export declare function mergeConditions<S extends SchemaAttributes = SchemaAttributes>(schema: Schema<S>, filter: Filter | null | undefined, extraFilter: Filter | null | undefined, quickFilterResults: Filter | null | undefined, columnFilters: Partial<Record<string, ColumnCondition>> | undefined, schemaStore: ISchemaStore): Filter | null;
8
+ export declare function simplyfyFilter(filter: Filter): Filter | null;
8
9
  export declare function collectExpandedKeys(columns: TransformedViewColumn<SchemaAttributes>[]): Record<string, string[]>;
9
10
  export declare function collectGridColumns<S extends SchemaAttributes = SchemaAttributes>({ gridColumns, schema, }: {
10
11
  gridColumns: TransformedViewColumn<S>[];
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.transformColumnFilter = transformColumnFilter;
7
7
  exports.mergeConditions = mergeConditions;
8
+ exports.simplyfyFilter = simplyfyFilter;
8
9
  exports.collectExpandedKeys = collectExpandedKeys;
9
10
  exports.collectGridColumns = collectGridColumns;
10
11
  exports.collectCardColumns = collectCardColumns;
@@ -77,7 +78,7 @@ function transformColumnFilter(filter, schema, schemaStore) {
77
78
  }
78
79
  return transformedResult;
79
80
  }
80
- function mergeConditions(schema, filter, extraFilter, columnFilters, schemaStore) {
81
+ function mergeConditions(schema, filter, extraFilter, quickFilterResults, columnFilters, schemaStore) {
81
82
  const filters = [];
82
83
  if (filter) {
83
84
  filters.push(filter);
@@ -85,6 +86,9 @@ function mergeConditions(schema, filter, extraFilter, columnFilters, schemaStore
85
86
  if (extraFilter) {
86
87
  filters.push(extraFilter);
87
88
  }
89
+ if (quickFilterResults) {
90
+ filters.push(quickFilterResults);
91
+ }
88
92
  if (columnFilters) {
89
93
  const transformedColumnFilters = transformColumnFilter(columnFilters, schema, schemaStore);
90
94
  if (transformedColumnFilters) {
@@ -105,11 +109,42 @@ function mergeConditions(schema, filter, extraFilter, columnFilters, schemaStore
105
109
  return null;
106
110
  }
107
111
  if (filters.length === 1) {
108
- return filters[0];
112
+ return simplyfyFilter(filters[0]);
109
113
  }
110
- return {
114
+ return simplyfyFilter({
111
115
  type: 'and',
112
116
  filters: filters,
117
+ });
118
+ }
119
+ function simplyfyFilter(filter) {
120
+ const conditions = filter.conditions ?? [];
121
+ const filters = [];
122
+ if (filter.filters) {
123
+ for (const f of filter.filters) {
124
+ const _f = simplyfyFilter(f);
125
+ if (!_f) {
126
+ continue;
127
+ }
128
+ if (_f.type !== filter.type) {
129
+ filters.push(_f);
130
+ }
131
+ else {
132
+ if (_f.conditions) {
133
+ conditions.push(..._f.conditions);
134
+ }
135
+ if (_f.filters) {
136
+ filters.push(..._f.filters);
137
+ }
138
+ }
139
+ }
140
+ }
141
+ if (conditions.length === 0 && filters.length === 0) {
142
+ return null;
143
+ }
144
+ return {
145
+ type: filter.type,
146
+ conditions: conditions,
147
+ filters: filters,
113
148
  };
114
149
  }
115
150
  function collectExpandedKeys(columns) {
@@ -23,6 +23,7 @@ export interface GridContextState<S extends SchemaAttributes = SchemaAttributes,
23
23
  searchText: string;
24
24
  columnFilters: Partial<Record<keyof S, ColumnCondition>>;
25
25
  sorting: SortingState<S>;
26
+ quickFilterValues: Record<string, unknown>;
26
27
  selectedIds: string[];
27
28
  data: RetriveRecordsResult<InferredSchemaType<S>> | null;
28
29
  dataState: {
@@ -0,0 +1 @@
1
+ export declare function useQuickFilter(): readonly [import("@headless-adminapp/core/experience/view/QuickFilter").QuickFilter<import("@headless-adminapp/core/schema").SchemaAttributes> | undefined, Record<string, unknown>, (key: string, value: unknown) => void];
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useQuickFilter = useQuickFilter;
4
+ const react_1 = require("react");
5
+ const context_1 = require("../../mutable/context");
6
+ const context_2 = require("../context");
7
+ function useQuickFilter() {
8
+ const quickFilter = (0, context_1.useContextSelector)(context_2.GridContext, (state) => state.view.experience.quickFilter);
9
+ const values = (0, context_1.useContextSelector)(context_2.GridContext, (state) => state.quickFilterValues);
10
+ const _setValue = (0, context_1.useContextSetValue)(context_2.GridContext);
11
+ const setValue = (0, react_1.useCallback)((key, value) => {
12
+ _setValue((state) => ({
13
+ quickFilterValues: {
14
+ ...state.quickFilterValues,
15
+ [key]: value,
16
+ },
17
+ }));
18
+ }, [_setValue]);
19
+ return [quickFilter, values, setValue];
20
+ }
@@ -7,6 +7,7 @@ export interface FormValidationStringSet {
7
7
  invalidPhoneNumber: string;
8
8
  atLeastOneRowRequired: string;
9
9
  fileSizeExceeded: string;
10
+ invalidIdFormat: string;
10
11
  }
11
12
  export declare const defaultFormValidationStrings: FormValidationStringSet;
12
13
  export declare const FormValidationStringContext: import("react").Context<FormValidationStringSet>;
@@ -12,6 +12,7 @@ exports.defaultFormValidationStrings = {
12
12
  invalidPhoneNumber: 'Invalid phone number.',
13
13
  atLeastOneRowRequired: 'At least one row required.',
14
14
  fileSizeExceeded: 'File size exceeded.',
15
+ invalidIdFormat: 'Invalid ID format.',
15
16
  };
16
17
  exports.FormValidationStringContext = (0, react_1.createContext)(exports.defaultFormValidationStrings);
17
18
  function useFormValidationStrings() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@headless-adminapp/app",
3
- "version": "1.1.12",
3
+ "version": "1.2.1",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -36,5 +36,5 @@
36
36
  "uuid": "11.0.3",
37
37
  "yup": "^1.4.0"
38
38
  },
39
- "gitHead": "ead918f1f609c2a260ceae498368ddc2c4980725"
39
+ "gitHead": "1cf9322dda84c1898cc90f1e92278b79ed9f2902"
40
40
  }
@@ -28,7 +28,7 @@ const CardViewSetter = () => {
28
28
  return;
29
29
  }
30
30
  experienceStore
31
- .getViewLookupV2(logicalName)
31
+ .getPublicView(logicalName)
32
32
  .then((view) => {
33
33
  setValue({
34
34
  cardView: view.experience.card,
@@ -29,6 +29,7 @@ function useRecordSetResult() {
29
29
  ...(cardView.secondaryColumns ?? [])
30
30
  .filter((x) => !x.expandedKey)
31
31
  .map((x) => x.name),
32
+ ...(cardView.rightColumn ?? []).map((x) => x.name),
32
33
  ])).filter(Boolean);
33
34
  }, [cardView]);
34
35
  const { isPending, data, error } = (0, react_query_1.useQuery)({
@@ -7,6 +7,7 @@ exports.useRetriveRecords = useRetriveRecords;
7
7
  const transport_1 = require("@headless-adminapp/app/transport");
8
8
  const react_query_1 = require("@tanstack/react-query");
9
9
  const react_1 = require("react");
10
+ const utils_1 = require("../../datagrid/DataGridProvider/utils");
10
11
  const ROWS_PER_PAGE = 100;
11
12
  const MAX_RECORDS = 10000;
12
13
  function useRetrieveRecordsKey({ schema, search, filter, sorting, columns, expand, maxRecords, }) {
@@ -125,10 +126,16 @@ function useRetriveRecordsInternal(queryKey, { columns, expand, filter, maxRecor
125
126
  };
126
127
  }
127
128
  function useRetriveRecords({ columns, expand, filter, maxRecords, schema, search, sorting, disabled, }) {
129
+ const _filter = (0, react_1.useMemo)(() => {
130
+ if (!filter) {
131
+ return null;
132
+ }
133
+ return (0, utils_1.simplyfyFilter)(filter);
134
+ }, [filter]);
128
135
  const queryKey = useRetrieveRecordsKey({
129
136
  columns,
130
137
  expand,
131
- filter,
138
+ filter: _filter,
132
139
  maxRecords,
133
140
  schema,
134
141
  search,
@@ -137,7 +144,7 @@ function useRetriveRecords({ columns, expand, filter, maxRecords, schema, search
137
144
  const query = useRetriveRecordsInternal(queryKey, {
138
145
  columns,
139
146
  expand,
140
- filter,
147
+ filter: _filter,
141
148
  maxRecords,
142
149
  schema,
143
150
  search,