proje-react-panel 1.1.3 → 1.1.5

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 (68) hide show
  1. package/.cursor/rules.md +21 -95
  2. package/.vscode/settings.json +6 -1
  3. package/PTD.md +124 -24
  4. package/dist/components/DetailsPage.d.ts +2 -2
  5. package/dist/components/ErrorComponent.d.ts +3 -2
  6. package/dist/components/LoadingScreen.d.ts +3 -1
  7. package/dist/components/Login.d.ts +2 -2
  8. package/dist/components/Panel.d.ts +2 -2
  9. package/dist/components/form/FormField.d.ts +1 -5
  10. package/dist/components/form/FormHeader.d.ts +10 -0
  11. package/dist/components/form/FormPage.d.ts +12 -1
  12. package/dist/components/form/Select.d.ts +1 -1
  13. package/dist/components/form/SelectStyles.d.ts +3 -3
  14. package/dist/components/list/CellField.d.ts +2 -3
  15. package/dist/components/list/EmptyList.d.ts +1 -1
  16. package/dist/decorators/details/Details.d.ts +1 -2
  17. package/dist/decorators/form/Form.d.ts +6 -4
  18. package/dist/decorators/form/Input.d.ts +10 -2
  19. package/dist/decorators/form/inputs/SelectInput.d.ts +8 -7
  20. package/dist/decorators/list/Cell.d.ts +3 -3
  21. package/dist/decorators/list/List.d.ts +3 -4
  22. package/dist/decorators/list/getListPageMeta.d.ts +2 -2
  23. package/dist/index.cjs.js +1 -1
  24. package/dist/index.esm.js +1 -1
  25. package/dist/store/store.d.ts +1 -0
  26. package/dist/utils/decerators.d.ts +3 -3
  27. package/eslint.config.mjs +60 -0
  28. package/package.json +7 -3
  29. package/src/api/CrudApi.ts +59 -53
  30. package/src/components/DetailsPage.tsx +12 -9
  31. package/src/components/ErrorComponent.tsx +36 -33
  32. package/src/components/LoadingScreen.tsx +4 -3
  33. package/src/components/Login.tsx +11 -4
  34. package/src/components/Panel.tsx +10 -6
  35. package/src/components/form/Checkbox.tsx +1 -1
  36. package/src/components/form/FormField.tsx +13 -9
  37. package/src/components/form/FormHeader.tsx +18 -0
  38. package/src/components/form/FormPage.tsx +146 -5
  39. package/src/components/form/InnerForm.tsx +24 -8
  40. package/src/components/form/Select.tsx +14 -12
  41. package/src/components/form/SelectStyles.ts +4 -4
  42. package/src/components/layout/Layout.tsx +1 -7
  43. package/src/components/layout/SideBar.tsx +1 -2
  44. package/src/components/list/CellField.tsx +2 -5
  45. package/src/components/list/Datagrid.tsx +0 -1
  46. package/src/components/list/EmptyList.tsx +2 -2
  47. package/src/components/list/FilterPopup.tsx +4 -2
  48. package/src/components/list/ListPage.tsx +11 -8
  49. package/src/components/list/cells/DefaultCell.tsx +2 -0
  50. package/src/decorators/details/Details.ts +2 -2
  51. package/src/decorators/details/DetailsItem.ts +2 -0
  52. package/src/decorators/form/Form.ts +13 -10
  53. package/src/decorators/form/Input.ts +18 -9
  54. package/src/decorators/form/inputs/SelectInput.ts +8 -7
  55. package/src/decorators/list/Cell.ts +6 -4
  56. package/src/decorators/list/ExtendedCell.ts +1 -9
  57. package/src/decorators/list/List.ts +8 -4
  58. package/src/decorators/list/cells/ImageCell.ts +1 -1
  59. package/src/decorators/list/getListPageMeta.ts +4 -3
  60. package/src/store/store.ts +3 -1
  61. package/src/styles/components/form-header.scss +75 -0
  62. package/src/styles/index.scss +1 -0
  63. package/src/types/AnyClass.ts +3 -0
  64. package/src/utils/decerators.ts +3 -2
  65. package/.eslintrc.js +0 -23
  66. package/.eslintrc.json +0 -26
  67. package/src/initPanel.ts +0 -3
  68. package/src/types/initPanelOptions.ts +0 -1
@@ -1,10 +1,11 @@
1
- import React, { useEffect, useRef, useState } from 'react';
2
- import { FormProvider, useForm, useFormContext, UseFormReturn } from 'react-hook-form';
1
+ import React, { useRef, useState } from 'react';
2
+ import { useFormContext } from 'react-hook-form';
3
3
  import { InputConfiguration } from '../../decorators/form/Input';
4
4
  import { FormField } from './FormField';
5
5
  import { useNavigate } from 'react-router';
6
6
  import { FormConfiguration } from '../../decorators/form/Form';
7
7
  import { AnyClass } from '../../types/AnyClass';
8
+ import { toast } from 'react-toastify';
8
9
 
9
10
  interface InnerFormProps<T extends AnyClass> {
10
11
  inputs: InputConfiguration[];
@@ -17,6 +18,7 @@ export function InnerForm<T extends AnyClass>({ inputs, formClass }: InnerFormPr
17
18
  const formRef = useRef<HTMLFormElement>(null);
18
19
  const navigate = useNavigate();
19
20
  const form = useFormContext<T>();
21
+ const loadingRef = useRef(false);
20
22
 
21
23
  return (
22
24
  <div className="form-wrapper">
@@ -24,6 +26,8 @@ export function InnerForm<T extends AnyClass>({ inputs, formClass }: InnerFormPr
24
26
  ref={formRef}
25
27
  onSubmit={form.handleSubmit(
26
28
  async (dataForm: T) => {
29
+ if (loadingRef.current) return;
30
+ loadingRef.current = true;
27
31
  try {
28
32
  const data =
29
33
  formClass.type === 'json'
@@ -37,17 +41,23 @@ export function InnerForm<T extends AnyClass>({ inputs, formClass }: InnerFormPr
37
41
  }
38
42
  return formData;
39
43
  })();
40
- await formClass.onSubmit(data);
44
+ const resut = await formClass.onSubmit(data);
45
+ form.reset(resut);
41
46
  setErrorMessage(null);
42
- if (formClass.redirectBackOnSuccess) {
43
- navigate(-1);
47
+ toast.success('Form submitted successfully');
48
+ if (formClass.redirectSuccessUrl) {
49
+ navigate(formClass.redirectSuccessUrl);
44
50
  }
45
- } catch (error: any) {
51
+ } catch (error: unknown) {
52
+ const errorResponse = error as { response?: { data?: { message?: string } } };
46
53
  const message =
47
- error?.response?.data?.message ||
54
+ errorResponse?.response?.data?.message ||
48
55
  (error instanceof Error ? error.message : 'An error occurred');
56
+ toast.error('Something went wrong');
49
57
  setErrorMessage(message);
50
58
  console.error(error);
59
+ } finally {
60
+ loadingRef.current = false;
51
61
  }
52
62
  },
53
63
  (errors, event) => {
@@ -69,7 +79,13 @@ export function InnerForm<T extends AnyClass>({ inputs, formClass }: InnerFormPr
69
79
  register={form.register}
70
80
  error={
71
81
  input.name
72
- ? { message: (form.formState.errors[input.name as keyof T] as any)?.message }
82
+ ? {
83
+ message: (
84
+ form.formState.errors[input.name as keyof T] as {
85
+ message: string;
86
+ }
87
+ )?.message,
88
+ }
73
89
  : undefined
74
90
  }
75
91
  />
@@ -1,8 +1,8 @@
1
- import React, { useEffect, useState } from 'react';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
2
  import { InputConfiguration } from '../../decorators/form/Input';
3
- import { useFormContext, Controller, useWatch } from 'react-hook-form';
3
+ import { useFormContext, Controller } from 'react-hook-form';
4
4
  import { SelectInputConfiguration } from '../../decorators/form/inputs/SelectInput';
5
- import ReactSelect, { SingleValue } from 'react-select';
5
+ import ReactSelect from 'react-select';
6
6
  import { darkSelectStyles } from './SelectStyles';
7
7
 
8
8
  interface SelectProps {
@@ -10,16 +10,18 @@ interface SelectProps {
10
10
  fieldName: string;
11
11
  }
12
12
 
13
- interface OptionType {
13
+ interface OptionType<TValue> {
14
14
  label: string;
15
- value: string;
15
+ value: TValue;
16
16
  }
17
17
 
18
- export function Select({ input, fieldName }: SelectProps) {
19
- const inputSelect = input as SelectInputConfiguration;
18
+ export function Select<TValue>({ input, fieldName }: SelectProps) {
19
+ const inputSelect = input as SelectInputConfiguration<TValue>;
20
20
  const { control } = useFormContext();
21
- const [options, setOptions] = useState<OptionType[]>(inputSelect.defaultOptions || []);
21
+ const [options, setOptions] = useState<OptionType<TValue>[]>(inputSelect.defaultOptions || []);
22
22
  const [key, setKey] = useState(0);
23
+ //NOTE: is added to component to fix type error. Need to find a better solution.
24
+ const styles = useMemo(() => darkSelectStyles<TValue>(), []);
23
25
  useEffect(() => {
24
26
  if (inputSelect.onSelectPreloader) {
25
27
  inputSelect.onSelectPreloader().then(option => {
@@ -27,7 +29,7 @@ export function Select({ input, fieldName }: SelectProps) {
27
29
  setKey(key + 1);
28
30
  });
29
31
  }
30
- }, [input]);
32
+ }, [inputSelect, inputSelect.onSelectPreloader, key]);
31
33
 
32
34
  return (
33
35
  <Controller
@@ -38,10 +40,10 @@ export function Select({ input, fieldName }: SelectProps) {
38
40
  <ReactSelect
39
41
  key={key}
40
42
  options={options}
41
- styles={darkSelectStyles}
43
+ styles={styles}
42
44
  value={options.find(option => option.value === field.value) || null}
43
- onChange={(selectedOption: OptionType | null) => {
44
- field.onChange(selectedOption?.value);
45
+ onChange={(selectedOption: OptionType<TValue> | null) => {
46
+ field.onChange(selectedOption?.value as TValue);
45
47
  }}
46
48
  />
47
49
  );
@@ -1,11 +1,11 @@
1
1
  import { StylesConfig } from 'react-select';
2
2
 
3
- interface OptionType {
3
+ interface OptionType<TValue> {
4
4
  label: string;
5
- value: string;
5
+ value: TValue;
6
6
  }
7
7
 
8
- export const darkSelectStyles: StylesConfig<OptionType, false> = {
8
+ export const darkSelectStyles = <TValue>(): StylesConfig<OptionType<TValue>, false> => ({
9
9
  control: (baseStyles, state) => ({
10
10
  ...baseStyles,
11
11
  backgroundColor: '#1f2937',
@@ -70,4 +70,4 @@ export const darkSelectStyles: StylesConfig<OptionType, false> = {
70
70
  color: '#6366f1',
71
71
  },
72
72
  }),
73
- };
73
+ });
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
2
  import { SideBar } from './SideBar';
3
3
  import { useAppStore } from '../../store/store';
4
- import { useNavigate } from 'react-router';
5
4
 
6
5
  export function Layout<IconType>({
7
6
  children,
@@ -17,18 +16,13 @@ export function Layout<IconType>({
17
16
  const { user } = useAppStore(s => ({
18
17
  user: s.user,
19
18
  }));
20
- const navigate = useNavigate();
21
19
  if (!user) {
22
20
  logout?.('redirect');
23
21
  }
24
22
 
25
23
  return (
26
24
  <div className="layout">
27
- <SideBar
28
- onLogout={logout}
29
- menu={menu}
30
- getIcons={getIcons}
31
- />
25
+ <SideBar onLogout={logout} menu={menu} getIcons={getIcons} />
32
26
  <main className="content">{children}</main>
33
27
  </div>
34
28
  );
@@ -1,5 +1,5 @@
1
1
  import React, { useState } from 'react';
2
- import { Link, useLocation, useNavigate } from 'react-router';
2
+ import { Link, useLocation } from 'react-router';
3
3
 
4
4
  type GetMenuFunction<IconType> = () => { name: string; path: string; iconType: IconType }[];
5
5
 
@@ -16,7 +16,6 @@ export function SideBar<IconType>({
16
16
  }) {
17
17
  const [isOpen, setIsOpen] = useState(true);
18
18
  const location = useLocation();
19
- const navigate = useNavigate();
20
19
 
21
20
  // Helper function to determine if a link is active
22
21
  const isActiveLink = (path: string) => {
@@ -1,7 +1,6 @@
1
1
  import React from 'react';
2
- import { ImageCellOptions } from '../../decorators/list/cells/ImageCell';
3
2
  import { AnyClass } from '../../types/AnyClass';
4
- import { CellConfiguration, ExtendedCellTypes } from '../../decorators/list/Cell';
3
+ import { CellConfiguration } from '../../decorators/list/Cell';
5
4
  import { BooleanCell } from './cells/BooleanCell';
6
5
  import { DateCell } from './cells/DateCell';
7
6
  import { ImageCell } from './cells/ImageCell';
@@ -11,13 +10,11 @@ import { DownloadCell } from './cells/DownloadCell';
11
10
 
12
11
  interface CellFieldProps<T extends AnyClass> {
13
12
  configuration: CellConfiguration;
14
- item: T;
15
- value: any;
13
+ value: T[keyof T];
16
14
  }
17
15
 
18
16
  export function CellField<T extends AnyClass>({
19
17
  configuration,
20
- item,
21
18
  value,
22
19
  }: CellFieldProps<T>): React.ReactElement {
23
20
  let render;
@@ -58,7 +58,6 @@ export function Datagrid<T extends AnyClass>({
58
58
  <CellField
59
59
  key={configuration.name}
60
60
  configuration={configuration}
61
- item={item}
62
61
  value={value}
63
62
  />
64
63
  );
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
 
3
- export const EmptyList: React.FC = () => {
3
+ export function EmptyList() {
4
4
  return (
5
5
  <div className="empty-list">
6
6
  <div className="empty-list-content">
@@ -23,4 +23,4 @@ export const EmptyList: React.FC = () => {
23
23
  </div>
24
24
  </div>
25
25
  );
26
- };
26
+ }
@@ -134,6 +134,8 @@ export function FilterPopup<T extends AnyClass>({
134
134
  listPageMeta,
135
135
  activeFilters,
136
136
  }: FilterPopupProps<T>): React.ReactElement | null {
137
+ //TODO: any is not a good solution, we need to find a better way to do this
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
137
139
  const [filters, setFilters] = React.useState<Record<string, any>>(activeFilters ?? {});
138
140
  const popupRef = useRef<HTMLDivElement>(null);
139
141
  const fields = useMemo(
@@ -158,8 +160,8 @@ export function FilterPopup<T extends AnyClass>({
158
160
  }, [isOpen, onClose]);
159
161
 
160
162
  if (!isOpen) return null;
161
-
162
- const handleFilterChange = (fieldName: string, value: any) => {
163
+
164
+ const handleFilterChange = (fieldName: string, value: string) => {
163
165
  setFilters(prev => ({
164
166
  ...prev,
165
167
  [fieldName]: value,
@@ -1,4 +1,4 @@
1
- import React, { useMemo, useCallback, useEffect, useState } from 'react';
1
+ import React, { useMemo, useCallback, useEffect, useState, useId } from 'react';
2
2
  import { useParams, useNavigate } from 'react-router';
3
3
  import { Datagrid } from './Datagrid';
4
4
  import { ErrorComponent } from '../ErrorComponent';
@@ -16,10 +16,11 @@ export function ListPage<T extends AnyClass>({
16
16
  model: AnyClassConstructor<T>;
17
17
  customHeader?: React.ReactNode;
18
18
  }) {
19
+ const id = useId();
19
20
  const listPageMeta = useMemo(() => getListPageMeta(model), [model]);
20
21
 
21
22
  const [loading, setLoading] = useState(true);
22
- const [data, setData] = useState<any>(null);
23
+ const [data, setData] = useState<T[]>([]);
23
24
  const [error, setError] = useState<unknown>(null);
24
25
 
25
26
  const [pagination, setPagination] = useState({ total: 0, page: 0, limit: 0 });
@@ -36,7 +37,9 @@ export function ListPage<T extends AnyClass>({
36
37
  page,
37
38
  filters: filters ?? activeFilters ?? {},
38
39
  });
39
- setData(result.data);
40
+ //TODO: any is not a good solution, we need to find a better way to do this
41
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
42
+ setData(result.data as any);
40
43
  setPagination({
41
44
  total: result.total,
42
45
  page: result.page,
@@ -49,7 +52,7 @@ export function ListPage<T extends AnyClass>({
49
52
  setLoading(false);
50
53
  }
51
54
  },
52
- [activeFilters, listPageMeta.class.getData]
55
+ [activeFilters, listPageMeta.class]
53
56
  );
54
57
 
55
58
  useEffect(() => {
@@ -59,7 +62,7 @@ export function ListPage<T extends AnyClass>({
59
62
  filtersFromUrl[key] = value;
60
63
  });
61
64
  setActiveFilters(filtersFromUrl);
62
- }, [location.search]);
65
+ }, []);
63
66
 
64
67
  useEffect(() => {
65
68
  if (activeFilters) {
@@ -67,7 +70,7 @@ export function ListPage<T extends AnyClass>({
67
70
  }
68
71
  }, [fetchData, params.page, activeFilters, listPageMeta.class.getData]);
69
72
 
70
- const handleFilterApply = (filters: Record<string, any>) => {
73
+ const handleFilterApply = (filters: Record<string, string>) => {
71
74
  setActiveFilters(filters);
72
75
 
73
76
  // Convert filters to URLSearchParams
@@ -83,8 +86,8 @@ export function ListPage<T extends AnyClass>({
83
86
  fetchData(1, filters); // Reset to first page when filters change
84
87
  };
85
88
 
86
- if (loading) return <LoadingScreen />;
87
- if (error) return <ErrorComponent error={error} />;
89
+ if (loading) return <LoadingScreen id={id} />;
90
+ if (error) return <ErrorComponent id={id} error={error} />;
88
91
 
89
92
  return (
90
93
  <div className="list">
@@ -2,6 +2,8 @@ import React from 'react';
2
2
  import { CellConfiguration } from '../../../decorators/list/Cell';
3
3
 
4
4
  interface DefaultCellProps {
5
+ //TODO: any is not a good solution, we need to find a better way to do this
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
5
7
  value: any;
6
8
  configuration: CellConfiguration;
7
9
  }
@@ -8,10 +8,10 @@ interface DetailsOptions<T extends AnyClass> {
8
8
  getDetailsData: GetDetailsDataFN<T>;
9
9
  }
10
10
 
11
- export interface DetailsConfiguration<T extends AnyClass> extends DetailsOptions<T> {}
11
+ export type DetailsConfiguration<T extends AnyClass> = DetailsOptions<T>;
12
12
 
13
13
  export function Details<T extends AnyClass>(options?: DetailsOptions<T>): ClassDecorator {
14
- return (target: Function) => {
14
+ return (target: object) => {
15
15
  if (options) {
16
16
  Reflect.defineMetadata(DETAILS_METADATA_KEY, options, target);
17
17
  }
@@ -26,6 +26,8 @@ export function DetailsItem(options?: DetailsItemOptions): PropertyDecorator {
26
26
  export function getDetailsItemFields<T extends AnyClass>(
27
27
  entityClass: T
28
28
  ): DetailsItemConfiguration[] {
29
+ //TODO: any is not a good solution, we need to find a better way to do this
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
31
  const prototype = (entityClass as any).prototype;
30
32
  const inputFields: string[] = Reflect.getMetadata(DETAILS_ITEM_KEY, prototype) || [];
31
33
 
@@ -1,25 +1,26 @@
1
1
  import 'reflect-metadata';
2
2
  import { AnyClass, AnyClassConstructor } from '../../types/AnyClass';
3
3
  import { GetDetailsDataFN } from '../details/Details';
4
- import { InputConfiguration } from './Input';
5
4
 
6
5
  const DETAILS_METADATA_KEY = 'DetailsMetaData';
7
- export type OnSubmitFN<T> = (data: T | FormData) => Promise<T | FormData>;
6
+ export type OnSubmitFN<T> = (data: T | FormData) => Promise<T>;
8
7
 
9
8
  interface FormOptions<T extends AnyClass> {
10
9
  onSubmit: OnSubmitFN<T>;
11
10
  getDetailsData?: GetDetailsDataFN<T>;
12
- redirectBackOnSuccess?: boolean;
13
11
  type?: 'json' | 'formData';
12
+ redirectSuccessUrl?: string;
14
13
  }
15
14
 
16
- export interface FormConfiguration<T extends AnyClass> extends FormOptions<T> {
17
- redirectBackOnSuccess: boolean;
15
+ export interface FormConfiguration<T extends AnyClass> {
16
+ onSubmit: OnSubmitFN<T>;
17
+ getDetailsData?: GetDetailsDataFN<T>;
18
18
  type: 'json' | 'formData';
19
+ redirectSuccessUrl?: string;
19
20
  }
20
21
 
21
22
  export function Form<T extends AnyClass>(options?: FormOptions<T>): ClassDecorator {
22
- return (target: Function) => {
23
+ return (target: object) => {
23
24
  if (options) {
24
25
  Reflect.defineMetadata(DETAILS_METADATA_KEY, options, target);
25
26
  }
@@ -31,14 +32,16 @@ export function getFormConfiguration<T extends AnyClass, K extends AnyClassConst
31
32
  ): FormConfiguration<T> {
32
33
  const formOptions: FormOptions<T> = Reflect.getMetadata(
33
34
  DETAILS_METADATA_KEY,
34
- entityClass as Object
35
+ entityClass as object
35
36
  );
36
37
  if (!formOptions) {
37
38
  throw new Error('Form decerator should be used on class');
38
39
  }
39
- return {
40
- ...formOptions,
40
+ const formConfiguration: FormConfiguration<T> = {
41
+ onSubmit: formOptions.onSubmit,
42
+ getDetailsData: formOptions.getDetailsData,
41
43
  type: formOptions.type ?? 'json',
42
- redirectBackOnSuccess: formOptions.redirectBackOnSuccess ?? true,
44
+ redirectSuccessUrl: formOptions.redirectSuccessUrl,
43
45
  };
46
+ return formConfiguration;
44
47
  }
@@ -11,21 +11,29 @@ export type InputTypes = 'input' | 'textarea' | 'file-upload' | 'checkbox' | 'hi
11
11
  export type ExtendedInputTypes = InputTypes | 'select';
12
12
 
13
13
  export interface InputOptions {
14
+ name?: string;
14
15
  type?: InputTypes;
15
16
  inputType?: 'text' | 'email' | 'tel' | 'password' | 'number' | 'date';
16
- name?: string;
17
17
  label?: string;
18
18
  placeholder?: string;
19
19
  nestedFields?: InputConfiguration[];
20
+ includeInCSV?: boolean;
21
+ includeInJSON?: boolean;
20
22
  }
21
23
 
22
24
  export interface ExtendedInputOptions extends Omit<InputOptions, 'type'> {
23
25
  type: ExtendedInputTypes;
24
26
  }
25
27
 
26
- export interface InputConfiguration extends Omit<InputOptions, 'type'> {
28
+ export interface InputConfiguration {
27
29
  name: string;
28
30
  type: ExtendedInputTypes;
31
+ inputType: 'text' | 'email' | 'tel' | 'password' | 'number' | 'date';
32
+ label?: string;
33
+ placeholder?: string;
34
+ nestedFields?: InputConfiguration[];
35
+ includeInCSV: boolean;
36
+ includeInJSON: boolean;
29
37
  }
30
38
 
31
39
  export function Input(options?: InputOptions): PropertyDecorator {
@@ -55,21 +63,22 @@ export function ExtendedInput(options?: ExtendedInputOptions): PropertyDecorator
55
63
  export function getInputFields<T extends AnyClass>(
56
64
  entityClass: AnyClassConstructor<T>
57
65
  ): InputConfiguration[] {
58
- const prototype = (entityClass as any).prototype;
66
+ const prototype = entityClass.prototype;
59
67
  const inputFields: string[] = Reflect.getMetadata(INPUT_KEY, prototype) || [];
60
68
  return inputFields.map(field => {
61
- const fields = Reflect.getMetadata(`${INPUT_KEY.toString()}:${field}:options`, prototype) || {};
69
+ const fields: InputOptions =
70
+ Reflect.getMetadata(`${INPUT_KEY.toString()}:${field}:options`, prototype) || {};
62
71
  const inputType = fields?.inputType ?? (isFieldSensitive(field) ? 'password' : 'text');
63
- return {
64
- ...fields,
65
- editable: fields.editable ?? true,
66
- sensitive: fields.sensitive,
72
+ const inputConfiguration: InputConfiguration = {
67
73
  name: fields?.name ?? field,
68
74
  label: fields?.label ?? field,
69
75
  placeholder: fields?.placeholder ?? field,
70
76
  inputType: inputType,
77
+ nestedFields: fields?.nestedFields ?? [],
71
78
  type: fields?.type ?? 'input',
72
- selectOptions: fields?.selectOptions ?? [],
79
+ includeInCSV: fields?.includeInCSV ?? false,
80
+ includeInJSON: fields?.includeInJSON ?? false,
73
81
  };
82
+ return inputConfiguration;
74
83
  });
75
84
  }
@@ -1,17 +1,18 @@
1
1
  import { ExtendedInput, ExtendedInputOptions, InputConfiguration, InputOptions } from '../Input';
2
2
 
3
- export interface SelectInputOptions extends InputOptions {
4
- onSelectPreloader?: () => Promise<{ label: string; value: any }[]>;
5
- defaultOptions?: { value: any; label: string }[];
3
+ export interface SelectInputOptions<T> extends InputOptions {
4
+ onSelectPreloader?: () => Promise<{ label: string; value: T }[]>;
5
+ defaultOptions?: { value: T; label: string }[];
6
+ csvExport?: never;
6
7
  }
7
8
 
8
- export interface SelectInputConfiguration extends InputConfiguration {
9
+ export interface SelectInputConfiguration<T> extends InputConfiguration {
9
10
  type: 'select';
10
- onSelectPreloader?: () => Promise<{ label: string; value: string }[]>;
11
- defaultOptions?: { value: any; label: string }[];
11
+ onSelectPreloader?: () => Promise<{ label: string; value: T }[]>;
12
+ defaultOptions?: { value: T; label: string }[];
12
13
  }
13
14
 
14
- export function SelectInput(options?: SelectInputOptions): PropertyDecorator {
15
+ export function SelectInput<K>(options?: SelectInputOptions<K>): PropertyDecorator {
15
16
  return ExtendedInput({
16
17
  ...options,
17
18
  type: 'select',
@@ -1,8 +1,8 @@
1
1
  import 'reflect-metadata';
2
- import { AnyClass } from '../../types/AnyClass';
2
+ import { AnyClass, AnyClassConstructor } from '../../types/AnyClass';
3
3
  import { createDecorator, DecoratorMap } from '../../utils/decerators';
4
4
 
5
- export const CELL_KEY: Symbol = Symbol('cell');
5
+ export const CELL_KEY = Symbol('cell');
6
6
 
7
7
  interface Filter {
8
8
  type: 'string' | 'number' | 'date' | 'static-select';
@@ -42,8 +42,10 @@ export function Cell(options?: CellOptions): PropertyDecorator {
42
42
  return createDecorator(CELL_KEY, options, cellMap);
43
43
  }
44
44
 
45
- export function getCellFields<T extends AnyClass>(entityClass: T): CellConfiguration[] {
46
- const prototype = (entityClass as any).prototype;
45
+ export function getCellFields<T extends AnyClass>(
46
+ entityClass: AnyClassConstructor<T>
47
+ ): CellConfiguration[] {
48
+ const prototype = entityClass.prototype;
47
49
  const inputFields: string[] = Reflect.getMetadata(CELL_KEY, prototype) || [];
48
50
 
49
51
  return inputFields.map(field => {
@@ -1,12 +1,4 @@
1
- import {
2
- CELL_KEY,
3
- CellConfiguration,
4
- CellOptions,
5
- CellTypes,
6
- Cell,
7
- ExtendedCellTypes,
8
- cellMap,
9
- } from './Cell';
1
+ import { CELL_KEY, CellConfiguration, CellOptions, ExtendedCellTypes, cellMap } from './Cell';
10
2
  import { createDecorator, DecoratorMap } from '../../utils/decerators';
11
3
 
12
4
  export interface ExtendedCellOptions extends Omit<CellOptions, 'type'> {
@@ -1,11 +1,13 @@
1
1
  import 'reflect-metadata';
2
- import { AnyClass } from '../../types/AnyClass';
2
+ import { AnyClass, AnyClassConstructor } from '../../types/AnyClass';
3
3
 
4
4
  const LIST_METADATA_KEY = 'ListMetaData';
5
5
 
6
6
  export interface GetDataParams {
7
7
  page?: number;
8
8
  limit?: number;
9
+ //TODO: fix this
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
9
11
  filters?: Record<string, any>;
10
12
  }
11
13
 
@@ -35,17 +37,19 @@ export interface ListOptions<T> {
35
37
  cells?: ((item: T) => ListCellOptions<T>) | ListCellOptions<T>;
36
38
  }
37
39
 
38
- export interface ListConfiguration<T> extends ListOptions<T> {}
40
+ export type ListConfiguration<T> = ListOptions<T>;
39
41
 
40
42
  export function List<T>(options?: ListOptions<T> | ((item: T) => ListOptions<T>)): ClassDecorator {
41
- return (target: Function) => {
43
+ return (target: object) => {
42
44
  if (options) {
43
45
  Reflect.defineMetadata(LIST_METADATA_KEY, options, target);
44
46
  }
45
47
  };
46
48
  }
47
49
 
48
- export function getListConfiguration<T extends AnyClass>(entityClass: T): ListConfiguration<T> {
50
+ export function getListConfiguration<T extends AnyClass>(
51
+ entityClass: AnyClassConstructor<T>
52
+ ): ListConfiguration<T> {
49
53
  const listConfiguration = Reflect.getMetadata(LIST_METADATA_KEY, entityClass);
50
54
  if (!listConfiguration) {
51
55
  throw new Error('List decerator should be used on class');
@@ -1,5 +1,5 @@
1
1
  import { CellConfiguration, CellOptions } from '../Cell';
2
- import { ExtendedCell, ExtendedCellOptions } from '../ExtendedCell';
2
+ import { ExtendedCell } from '../ExtendedCell';
3
3
 
4
4
  export interface ImageCellOptions extends Omit<CellOptions, 'type'> {
5
5
  baseUrl: string;
@@ -1,5 +1,4 @@
1
- import { AnyClass } from '../../types/AnyClass';
2
- import { getDetailsItemFields } from '../details/DetailsItem';
1
+ import { AnyClass, AnyClassConstructor } from '../../types/AnyClass';
3
2
  import { CellConfiguration, getCellFields } from './Cell';
4
3
  import { getListConfiguration, ListConfiguration } from './List';
5
4
 
@@ -8,7 +7,9 @@ export interface ListPageMeta<T extends AnyClass> {
8
7
  cells: CellConfiguration[];
9
8
  }
10
9
 
11
- export function getListPageMeta<T extends AnyClass>(entityClass: T): ListPageMeta<T> {
10
+ export function getListPageMeta<T extends AnyClass>(
11
+ entityClass: AnyClassConstructor<T>
12
+ ): ListPageMeta<T> {
12
13
  return {
13
14
  class: getListConfiguration(entityClass),
14
15
  cells: getCellFields<T>(entityClass),
@@ -8,12 +8,14 @@ interface User {
8
8
 
9
9
  interface AppState {
10
10
  user: User | null;
11
+ login: (user: User) => void;
11
12
  }
12
13
 
13
14
  export const useAppStore = createWithEqualityFn<AppState>()(
14
15
  persist(
15
- _ => ({
16
+ set => ({
16
17
  user: null,
18
+ login: (user: User) => set({ user }),
17
19
  }),
18
20
  {
19
21
  name: 'app-store-1',