proje-react-panel 1.1.4 → 1.1.6

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 (64) 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/Login.d.ts +2 -2
  6. package/dist/components/Panel.d.ts +2 -2
  7. package/dist/components/form/FormField.d.ts +1 -5
  8. package/dist/components/form/FormHeader.d.ts +10 -0
  9. package/dist/components/form/FormPage.d.ts +12 -1
  10. package/dist/components/form/Select.d.ts +1 -1
  11. package/dist/components/form/SelectStyles.d.ts +3 -3
  12. package/dist/components/list/CellField.d.ts +2 -3
  13. package/dist/components/list/EmptyList.d.ts +1 -1
  14. package/dist/decorators/details/Details.d.ts +1 -2
  15. package/dist/decorators/form/Form.d.ts +3 -5
  16. package/dist/decorators/form/Input.d.ts +10 -2
  17. package/dist/decorators/form/inputs/SelectInput.d.ts +8 -7
  18. package/dist/decorators/list/Cell.d.ts +3 -3
  19. package/dist/decorators/list/List.d.ts +3 -4
  20. package/dist/decorators/list/getListPageMeta.d.ts +2 -2
  21. package/dist/index.cjs.js +1 -1
  22. package/dist/index.esm.js +1 -1
  23. package/dist/store/store.d.ts +1 -0
  24. package/dist/utils/decerators.d.ts +3 -3
  25. package/eslint.config.mjs +60 -0
  26. package/package.json +6 -3
  27. package/src/api/CrudApi.ts +59 -53
  28. package/src/components/DetailsPage.tsx +8 -6
  29. package/src/components/Login.tsx +11 -4
  30. package/src/components/Panel.tsx +3 -3
  31. package/src/components/form/Checkbox.tsx +1 -1
  32. package/src/components/form/FormField.tsx +13 -9
  33. package/src/components/form/FormHeader.tsx +18 -0
  34. package/src/components/form/FormPage.tsx +146 -5
  35. package/src/components/form/InnerForm.tsx +13 -8
  36. package/src/components/form/Select.tsx +14 -15
  37. package/src/components/form/SelectStyles.ts +4 -4
  38. package/src/components/layout/Layout.tsx +1 -7
  39. package/src/components/layout/SideBar.tsx +1 -2
  40. package/src/components/list/CellField.tsx +2 -5
  41. package/src/components/list/Datagrid.tsx +0 -1
  42. package/src/components/list/EmptyList.tsx +2 -2
  43. package/src/components/list/FilterPopup.tsx +4 -2
  44. package/src/components/list/ListPage.tsx +7 -5
  45. package/src/components/list/cells/DefaultCell.tsx +2 -0
  46. package/src/decorators/details/Details.ts +2 -2
  47. package/src/decorators/details/DetailsItem.ts +2 -0
  48. package/src/decorators/form/Form.ts +10 -11
  49. package/src/decorators/form/Input.ts +19 -10
  50. package/src/decorators/form/inputs/SelectInput.ts +8 -7
  51. package/src/decorators/list/Cell.ts +6 -4
  52. package/src/decorators/list/ExtendedCell.ts +1 -9
  53. package/src/decorators/list/List.ts +8 -4
  54. package/src/decorators/list/cells/ImageCell.ts +1 -1
  55. package/src/decorators/list/getListPageMeta.ts +4 -3
  56. package/src/store/store.ts +3 -1
  57. package/src/styles/components/form-header.scss +75 -0
  58. package/src/styles/index.scss +1 -0
  59. package/src/types/AnyClass.ts +3 -0
  60. package/src/utils/decerators.ts +3 -2
  61. package/.eslintrc.js +0 -23
  62. package/.eslintrc.json +0 -26
  63. package/src/initPanel.ts +0 -3
  64. package/src/types/initPanelOptions.ts +0 -1
@@ -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,24 +10,24 @@ 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 || []);
22
- const [key, setKey] = useState(0);
21
+ const [options, setOptions] = useState<OptionType<TValue>[]>(inputSelect.defaultOptions || []);
22
+ //NOTE: is added to component to fix type error. Need to find a better solution.
23
+ const styles = useMemo(() => darkSelectStyles<TValue>(), []);
23
24
  useEffect(() => {
24
25
  if (inputSelect.onSelectPreloader) {
25
26
  inputSelect.onSelectPreloader().then(option => {
26
27
  setOptions(option);
27
- setKey(key + 1);
28
28
  });
29
29
  }
30
- }, [input]);
30
+ }, [inputSelect, inputSelect.onSelectPreloader]);
31
31
 
32
32
  return (
33
33
  <Controller
@@ -36,12 +36,11 @@ export function Select({ input, fieldName }: SelectProps) {
36
36
  render={({ field }) => {
37
37
  return (
38
38
  <ReactSelect
39
- key={key}
40
39
  options={options}
41
- styles={darkSelectStyles}
40
+ styles={styles}
42
41
  value={options.find(option => option.value === field.value) || null}
43
- onChange={(selectedOption: OptionType | null) => {
44
- field.onChange(selectedOption?.value);
42
+ onChange={(selectedOption: OptionType<TValue> | null) => {
43
+ field.onChange(selectedOption?.value as TValue);
45
44
  }}
46
45
  />
47
46
  );
@@ -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,
@@ -20,7 +20,7 @@ export function ListPage<T extends AnyClass>({
20
20
  const listPageMeta = useMemo(() => getListPageMeta(model), [model]);
21
21
 
22
22
  const [loading, setLoading] = useState(true);
23
- const [data, setData] = useState<any>(null);
23
+ const [data, setData] = useState<T[]>([]);
24
24
  const [error, setError] = useState<unknown>(null);
25
25
 
26
26
  const [pagination, setPagination] = useState({ total: 0, page: 0, limit: 0 });
@@ -37,7 +37,9 @@ export function ListPage<T extends AnyClass>({
37
37
  page,
38
38
  filters: filters ?? activeFilters ?? {},
39
39
  });
40
- 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);
41
43
  setPagination({
42
44
  total: result.total,
43
45
  page: result.page,
@@ -50,7 +52,7 @@ export function ListPage<T extends AnyClass>({
50
52
  setLoading(false);
51
53
  }
52
54
  },
53
- [activeFilters, listPageMeta.class.getData]
55
+ [activeFilters, listPageMeta.class]
54
56
  );
55
57
 
56
58
  useEffect(() => {
@@ -60,7 +62,7 @@ export function ListPage<T extends AnyClass>({
60
62
  filtersFromUrl[key] = value;
61
63
  });
62
64
  setActiveFilters(filtersFromUrl);
63
- }, [location.search]);
65
+ }, []);
64
66
 
65
67
  useEffect(() => {
66
68
  if (activeFilters) {
@@ -68,7 +70,7 @@ export function ListPage<T extends AnyClass>({
68
70
  }
69
71
  }, [fetchData, params.page, activeFilters, listPageMeta.class.getData]);
70
72
 
71
- const handleFilterApply = (filters: Record<string, any>) => {
73
+ const handleFilterApply = (filters: Record<string, string>) => {
72
74
  setActiveFilters(filters);
73
75
 
74
76
  // Convert filters to URLSearchParams
@@ -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,7 +1,6 @@
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
6
  export type OnSubmitFN<T> = (data: T | FormData) => Promise<T>;
@@ -9,21 +8,19 @@ export type OnSubmitFN<T> = (data: T | FormData) => Promise<T>;
9
8
  interface FormOptions<T extends AnyClass> {
10
9
  onSubmit: OnSubmitFN<T>;
11
10
  getDetailsData?: GetDetailsDataFN<T>;
12
- /** @deprecated */
13
- redirectBackOnSuccess?: boolean;
14
11
  type?: 'json' | 'formData';
15
12
  redirectSuccessUrl?: string;
16
13
  }
17
14
 
18
- export interface FormConfiguration<T extends AnyClass> extends FormOptions<T> {
19
- /** @deprecated */
20
- redirectBackOnSuccess: boolean;
15
+ export interface FormConfiguration<T extends AnyClass> {
16
+ onSubmit: OnSubmitFN<T>;
17
+ getDetailsData?: GetDetailsDataFN<T>;
21
18
  type: 'json' | 'formData';
22
19
  redirectSuccessUrl?: string;
23
20
  }
24
21
 
25
22
  export function Form<T extends AnyClass>(options?: FormOptions<T>): ClassDecorator {
26
- return (target: Function) => {
23
+ return (target: object) => {
27
24
  if (options) {
28
25
  Reflect.defineMetadata(DETAILS_METADATA_KEY, options, target);
29
26
  }
@@ -35,14 +32,16 @@ export function getFormConfiguration<T extends AnyClass, K extends AnyClassConst
35
32
  ): FormConfiguration<T> {
36
33
  const formOptions: FormOptions<T> = Reflect.getMetadata(
37
34
  DETAILS_METADATA_KEY,
38
- entityClass as Object
35
+ entityClass as object
39
36
  );
40
37
  if (!formOptions) {
41
38
  throw new Error('Form decerator should be used on class');
42
39
  }
43
- return {
44
- ...formOptions,
40
+ const formConfiguration: FormConfiguration<T> = {
41
+ onSubmit: formOptions.onSubmit,
42
+ getDetailsData: formOptions.getDetailsData,
45
43
  type: formOptions.type ?? 'json',
46
- redirectBackOnSuccess: formOptions.redirectBackOnSuccess ?? true,
44
+ redirectSuccessUrl: formOptions.redirectSuccessUrl,
47
45
  };
46
+ return formConfiguration;
48
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 {
@@ -44,9 +52,8 @@ export function ExtendedInput(options?: ExtendedInputOptions): PropertyDecorator
44
52
  return (target, propertyKey) => {
45
53
  const existingInputs: string[] = Reflect.getMetadata(INPUT_KEY, target) || [];
46
54
  Reflect.defineMetadata(INPUT_KEY, [...existingInputs, propertyKey.toString()], target);
47
-
48
55
  if (options) {
49
- const keyString = `${INPUT_KEY.toString()}:${propertyKey.toString()}:options`;
56
+ const keyString = `${INPUT_KEY.toString()}:${propertyKey.toString()}:options`;
50
57
  Reflect.defineMetadata(keyString, options, target);
51
58
  }
52
59
  };
@@ -55,21 +62,23 @@ export function ExtendedInput(options?: ExtendedInputOptions): PropertyDecorator
55
62
  export function getInputFields<T extends AnyClass>(
56
63
  entityClass: AnyClassConstructor<T>
57
64
  ): InputConfiguration[] {
58
- const prototype = (entityClass as any).prototype;
65
+ const prototype = entityClass.prototype;
59
66
  const inputFields: string[] = Reflect.getMetadata(INPUT_KEY, prototype) || [];
60
67
  return inputFields.map(field => {
61
- const fields = Reflect.getMetadata(`${INPUT_KEY.toString()}:${field}:options`, prototype) || {};
68
+ const fields: InputOptions =
69
+ Reflect.getMetadata(`${INPUT_KEY.toString()}:${field}:options`, prototype) || {};
62
70
  const inputType = fields?.inputType ?? (isFieldSensitive(field) ? 'password' : 'text');
63
- return {
71
+ const inputConfiguration: InputConfiguration = {
64
72
  ...fields,
65
- editable: fields.editable ?? true,
66
- sensitive: fields.sensitive,
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',
@@ -0,0 +1,75 @@
1
+ .form-header {
2
+ display: flex;
3
+ justify-content: space-between;
4
+ align-items: center;
5
+ padding: 1rem;
6
+ margin-bottom: 1.5rem;
7
+ background-color: #333333;
8
+ border-radius: 8px 0 0 8px;
9
+
10
+ .form-title {
11
+ margin: 0;
12
+ font-size: 1.5rem;
13
+ font-weight: 600;
14
+ color: #cccccc;
15
+ }
16
+
17
+ .form-actions {
18
+ display: flex;
19
+ gap: 0.5rem;
20
+ align-items: center;
21
+
22
+ //keep this even its not used in project (it can be used in other projects)
23
+ .export-button,
24
+ .import-button {
25
+ padding: 0.5rem 1rem;
26
+ border: 1px solid #444444;
27
+ border-radius: 4px;
28
+ background-color: #222222;
29
+ color: #cccccc;
30
+ font-size: 0.875rem;
31
+ cursor: pointer;
32
+ transition: all 0.2s ease;
33
+
34
+ &:hover {
35
+ background-color: #444444;
36
+ border-color: #555555;
37
+ }
38
+ }
39
+ }
40
+
41
+ &.dark-theme {
42
+ background-color: black;
43
+ box-shadow: none;
44
+
45
+ .form-title {
46
+ color: white;
47
+ }
48
+
49
+ .form-actions {
50
+ .export-button {
51
+ background-color: black;
52
+ border-color: black;
53
+ color: white;
54
+
55
+ &:hover {
56
+ background-color: black;
57
+ border-color: black;
58
+ }
59
+ }
60
+
61
+ .import-button {
62
+ .import-label {
63
+ background-color: black;
64
+ border-color: black;
65
+ color: white;
66
+
67
+ &:hover {
68
+ background-color: black;
69
+ border-color: black;
70
+ }
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
@@ -10,6 +10,7 @@
10
10
  @import './filter-popup';
11
11
  @import './components/uploader.scss';
12
12
  @import './components/checkbox';
13
+ @import './components/form-header';
13
14
  @import './details';
14
15
 
15
16
  //TODO: import deprecated
@@ -1,2 +1,5 @@
1
+ //TODO: any?
2
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1
3
  export type AnyClass = Record<string, any>;
4
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2
5
  export type AnyClassConstructor<T extends AnyClass> = new (...args: any[]) => T;
@@ -1,9 +1,10 @@
1
1
  export type DecoratorMap<T, K> = (
2
- { target, propertyKey }: { target: Object; propertyKey: string | symbol },
2
+ prop: { target: object; propertyKey: string | symbol },
3
3
  options: T
4
4
  ) => K;
5
+
5
6
  export function createDecorator<T extends { name?: string }, K>(
6
- key: Symbol,
7
+ key: symbol,
7
8
  options?: T,
8
9
  map?: DecoratorMap<T, K>
9
10
  ): PropertyDecorator {