proje-react-panel 1.4.1 → 1.6.0

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 (39) hide show
  1. package/.vscode/launch.json +9 -0
  2. package/dist/components/list/CellField.d.ts +2 -2
  3. package/dist/components/list/cells/BooleanCell.d.ts +5 -2
  4. package/dist/components/list/cells/DateCell.d.ts +5 -2
  5. package/dist/components/list/cells/DefaultCell.d.ts +3 -2
  6. package/dist/components/list/cells/DownloadCell.d.ts +3 -2
  7. package/dist/components/list/cells/ImageCell.d.ts +3 -2
  8. package/dist/components/list/cells/LinkCell.d.ts +8 -0
  9. package/dist/components/list/cells/UUIDCell.d.ts +5 -2
  10. package/dist/decorators/details/Details.d.ts +1 -1
  11. package/dist/decorators/list/Cell.d.ts +5 -1
  12. package/dist/decorators/list/List.d.ts +8 -4
  13. package/dist/decorators/list/cells/LinkCell.d.ts +13 -0
  14. package/dist/index.cjs.js +1 -1
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.esm.js +1 -1
  17. package/package.json +1 -1
  18. package/src/assets/icons/svg/down-arrow-backup-2.svg +3 -0
  19. package/src/components/DetailsPage.tsx +5 -1
  20. package/src/components/list/CellField.tsx +25 -10
  21. package/src/components/list/Datagrid.tsx +83 -53
  22. package/src/components/list/ListPage.tsx +3 -0
  23. package/src/components/list/cells/BooleanCell.tsx +7 -2
  24. package/src/components/list/cells/DateCell.tsx +6 -2
  25. package/src/components/list/cells/DefaultCell.tsx +4 -4
  26. package/src/components/list/cells/DownloadCell.tsx +4 -2
  27. package/src/components/list/cells/ImageCell.tsx +5 -2
  28. package/src/components/list/cells/LinkCell.tsx +31 -0
  29. package/src/components/list/cells/UUIDCell.tsx +6 -2
  30. package/src/decorators/details/Details.ts +1 -1
  31. package/src/decorators/list/Cell.ts +5 -1
  32. package/src/decorators/list/List.ts +4 -4
  33. package/src/decorators/list/cells/LinkCell.ts +22 -0
  34. package/src/index.ts +2 -1
  35. package/src/services/DataService.ts +14 -10
  36. package/src/store/store.ts +1 -1
  37. package/src/styles/components/button.scss +14 -0
  38. package/src/styles/index.scss +1 -1
  39. package/src/styles/list.scss +64 -1
@@ -7,38 +7,53 @@ import { ImageCell } from './cells/ImageCell';
7
7
  import { UUIDCell } from './cells/UUIDCell';
8
8
  import { DefaultCell } from './cells/DefaultCell';
9
9
  import { DownloadCell } from './cells/DownloadCell';
10
+ import { LinkCell } from './cells/LinkCell';
10
11
 
11
12
  interface CellFieldProps<T extends AnyClass> {
12
13
  configuration: CellConfiguration;
13
- value: T[keyof T];
14
+ item: T;
14
15
  }
15
16
 
16
17
  export function CellField<T extends AnyClass>({
17
18
  configuration,
18
- value,
19
+ item,
19
20
  }: CellFieldProps<T>): React.ReactElement {
20
21
  let render;
21
22
 
22
23
  switch (configuration.type) {
23
24
  case 'boolean':
24
- render = <BooleanCell value={value} />;
25
+ render = <BooleanCell item={item} configuration={configuration} />;
25
26
  break;
26
27
  case 'date':
27
- render = <DateCell value={value} />;
28
+ render = <DateCell item={item} configuration={configuration} />;
28
29
  break;
29
30
  case 'image':
30
- render = <ImageCell value={value} configuration={configuration} />;
31
+ render = <ImageCell item={item} configuration={configuration} />;
31
32
  break;
32
33
  case 'uuid':
33
- render = <UUIDCell value={value} />;
34
+ render = <UUIDCell item={item} configuration={configuration} />;
34
35
  break;
35
36
  case 'download':
36
- render = <DownloadCell value={value} configuration={configuration} />;
37
+ render = <DownloadCell item={item} configuration={configuration} />;
38
+ break;
39
+ case 'link':
40
+ render = <LinkCell item={item} configuration={configuration} />;
37
41
  break;
38
42
  default:
39
- render = <DefaultCell value={value} configuration={configuration} />;
43
+ render = <DefaultCell item={item} configuration={configuration} />;
40
44
  break;
41
45
  }
42
-
43
- return <td key={configuration.name}>{render}</td>;
46
+ const width = configuration.style?.width;
47
+ const minWidth = configuration.style?.minWidth;
48
+ return (
49
+ <td
50
+ key={configuration.name}
51
+ style={{
52
+ minWidth,
53
+ width,
54
+ }}
55
+ >
56
+ {render}
57
+ </td>
58
+ );
44
59
  }
@@ -3,6 +3,7 @@ import { Link } from 'react-router';
3
3
  import { EmptyList } from './EmptyList';
4
4
  import SearchIcon from '../../assets/icons/svg/search.svg';
5
5
  import PencilIcon from '../../assets/icons/svg/pencil.svg';
6
+ import DownArrowIcon from '../../assets/icons/svg/down-arrow-backup-2.svg';
6
7
  import TrashIcon from '../../assets/icons/svg/trash.svg';
7
8
  import { ListPageMeta } from '../../decorators/list/getListPageMeta';
8
9
  import { AnyClass } from '../../types/AnyClass';
@@ -23,10 +24,10 @@ export function Datagrid<T extends AnyClass>({
23
24
  }: DatagridProps<T>) {
24
25
  const cells = listPageMeta.cells;
25
26
  const listData = useAppStore(state => state.listData[listPageMeta.class.key]);
26
- const listGeneralCells = data?.[0]
27
- ? typeof listPageMeta.class.cells === 'function'
28
- ? listPageMeta.class.cells?.(data[0])
29
- : listPageMeta.class.cells
27
+ const listActions = data?.[0]
28
+ ? typeof listPageMeta.class.actions === 'function'
29
+ ? listPageMeta.class.actions?.(data[0])
30
+ : listPageMeta.class.actions
30
31
  : null;
31
32
 
32
33
  return (
@@ -38,72 +39,101 @@ export function Datagrid<T extends AnyClass>({
38
39
  <thead>
39
40
  <tr>
40
41
  {cells.map(cellOptions => (
41
- <th key={cellOptions.name}>{cellOptions.title ?? cellOptions.name}</th>
42
+ <th
43
+ key={cellOptions.name}
44
+ style={{ width: cellOptions.style?.width, minWidth: cellOptions.style?.minWidth }}
45
+ >
46
+ {cellOptions.title ?? cellOptions.name}
47
+ </th>
42
48
  ))}
43
- {listGeneralCells?.details && <th>Details</th>}
44
- {listGeneralCells?.edit && <th>Edit</th>}
45
- {listGeneralCells?.delete && <th>Delete</th>}
49
+ {(listActions?.details ||
50
+ listActions?.edit ||
51
+ listActions?.delete ||
52
+ listActions?.customActions?.length) && <th style={{ width: '30px' }}>Actions</th>}
46
53
  </tr>
47
54
  </thead>
48
55
  <tbody>
49
56
  {data.map((item, index) => {
50
57
  const listCells = item
51
- ? typeof listPageMeta.class.cells === 'function'
52
- ? listPageMeta.class.cells?.(item)
53
- : listPageMeta.class.cells
58
+ ? typeof listPageMeta.class.actions === 'function'
59
+ ? listPageMeta.class.actions?.(item)
60
+ : listPageMeta.class.actions
61
+ : null;
62
+ //TODO: memoize this
63
+ const listDataItem = listPageMeta.class.primaryId
64
+ ? (listData?.[item[listPageMeta.class.primaryId!] as string] as
65
+ | Record<string, unknown>
66
+ | undefined)
54
67
  : null;
55
- const listDataItem = listData?.[item[listPageMeta.class.primaryId]] as
56
- | Record<string, unknown>
57
- | undefined;
58
68
  return (
59
69
  <tr key={index}>
60
70
  {cells.map((configuration: CellConfiguration) => {
61
- const value = listDataItem?.[configuration.name] ?? item[configuration.name];
62
71
  return (
63
72
  <CellField
64
73
  key={configuration.name}
74
+ item={{
75
+ ...(listDataItem ?? {}),
76
+ ...item,
77
+ }}
65
78
  configuration={configuration}
66
- value={value}
67
79
  />
68
80
  );
69
81
  })}
70
- {listCells?.details && (
71
- <td>
72
- <Link to={listCells.details.path} className="util-cell-link">
73
- <SearchIcon className="icon icon-search" />
74
- <span className="util-cell-label">{listCells.details.label}</span>
75
- </Link>
76
- </td>
77
- )}
78
- {listCells?.edit && (
79
- <td>
80
- <Link to={listCells.edit.path} className="util-cell-link">
81
- <PencilIcon className="icon icon-pencil" />
82
- <span className="util-cell-label">{listCells.edit.label}</span>
83
- </Link>
84
- </td>
85
- )}
86
- {listCells?.delete && (
87
- <td>
88
- <a
89
- onClick={() => {
90
- listCells.delete
91
- ?.onRemoveItem?.(item)
92
- .then(() => {
93
- onRemoveItem?.(item);
94
- })
95
- .catch((e: unknown) => {
96
- console.error(e);
97
- const message =
98
- e instanceof Error ? e.message : 'Error deleting item';
99
- alert(message);
100
- });
101
- }}
102
- className="util-cell-link util-cell-link-remove"
103
- >
104
- <TrashIcon className="icon icon-trash" />
105
- <span className="util-cell-label">{listCells.delete.label}</span>
106
- </a>
82
+ {(listCells?.details || listCells?.edit || listCells?.delete) && (
83
+ <td style={{ width: '30px' }}>
84
+ <div className="util-cell-actions">
85
+ <p className="util-cell-actions-label">
86
+ Actions <DownArrowIcon className="icon icon-down" />
87
+ </p>
88
+ <ul className="util-cell-actions-list">
89
+ {listCells?.details && (
90
+ <li>
91
+ <Link to={listCells.details.path} className="util-cell-link">
92
+ <SearchIcon className="icon icon-search" />
93
+ <span className="util-cell-label">{listCells.details.label}</span>
94
+ </Link>
95
+ </li>
96
+ )}
97
+ {listCells?.edit && (
98
+ <li>
99
+ <Link to={listCells.edit.path} className="util-cell-link">
100
+ <PencilIcon className="icon icon-pencil" />
101
+ <span className="util-cell-label">{listCells.edit.label}</span>
102
+ </Link>
103
+ </li>
104
+ )}
105
+ {listCells?.delete && (
106
+ <li>
107
+ <a
108
+ onClick={() => {
109
+ listCells.delete
110
+ ?.onRemoveItem?.(item)
111
+ .then(() => {
112
+ onRemoveItem?.(item);
113
+ })
114
+ .catch((e: unknown) => {
115
+ console.error(e);
116
+ const message =
117
+ e instanceof Error ? e.message : 'Error deleting item';
118
+ alert(message);
119
+ });
120
+ }}
121
+ className="util-cell-link util-cell-link-remove"
122
+ >
123
+ <TrashIcon className="icon icon-trash" />
124
+ <span className="util-cell-label">{listCells.delete.label}</span>
125
+ </a>
126
+ </li>
127
+ )}
128
+ {listCells?.customActions?.map(action => (
129
+ <li key={action.label}>
130
+ <a onClick={() => action.onClick(item)} className="util-cell-link">
131
+ <span className="util-cell-label">{action.label}</span>
132
+ </a>
133
+ </li>
134
+ ))}
135
+ </ul>
136
+ </div>
107
137
  </td>
108
138
  )}
109
139
  </tr>
@@ -106,6 +106,9 @@ export function ListPage<T extends AnyClass>({
106
106
  />
107
107
  <div className="list-footer">
108
108
  <Pagination pagination={pagination} onPageChange={fetchData} />
109
+ <p className="list-footer-total">
110
+ TOTAL: {pagination.total} / SHOWING: {pagination.limit}
111
+ </p>
109
112
  </div>
110
113
  <FilterPopup
111
114
  isOpen={isFilterOpen}
@@ -1,12 +1,17 @@
1
1
  import React from 'react';
2
2
  import CheckIcon from '../../../assets/icons/svg/check.svg';
3
3
  import CrossIcon from '../../../assets/icons/svg/cross.svg';
4
+ import { AnyClass } from '../../../types/AnyClass';
5
+ import { CellConfiguration } from '../../../decorators/list/Cell';
4
6
 
5
7
  interface BooleanCellProps {
6
- value: boolean;
8
+ item: AnyClass;
9
+ configuration: CellConfiguration;
7
10
  }
8
11
 
9
- export function BooleanCell({ value }: BooleanCellProps) {
12
+ export function BooleanCell({ item, configuration }: BooleanCellProps) {
13
+ const value = item[configuration.name];
14
+
10
15
  return value ? (
11
16
  <CheckIcon className="icon icon-true" />
12
17
  ) : (
@@ -1,10 +1,14 @@
1
1
  import React from 'react';
2
+ import { AnyClass } from '../../../types/AnyClass';
3
+ import { CellConfiguration } from '../../../decorators/list/Cell';
2
4
 
3
5
  interface DateCellProps {
4
- value: string | number | Date;
6
+ item: AnyClass;
7
+ configuration: CellConfiguration;
5
8
  }
6
9
 
7
- export function DateCell({ value }: DateCellProps) {
10
+ export function DateCell({ item, configuration }: DateCellProps) {
11
+ const value = item[configuration.name];
8
12
  if (!value) return <>-</>;
9
13
 
10
14
  const date = new Date(value);
@@ -1,13 +1,13 @@
1
1
  import React from 'react';
2
2
  import { CellConfiguration } from '../../../decorators/list/Cell';
3
+ import { AnyClass } from '../../../types/AnyClass';
3
4
 
4
5
  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
7
- value: any;
6
+ item: AnyClass;
8
7
  configuration: CellConfiguration;
9
8
  }
10
9
 
11
- export function DefaultCell({ value, configuration }: DefaultCellProps): React.ReactElement {
10
+ export function DefaultCell({ item, configuration }: DefaultCellProps): React.ReactElement {
11
+ const value = item[configuration.name];
12
12
  return <>{value ? value.toString() : configuration.placeHolder}</>;
13
13
  }
@@ -1,13 +1,15 @@
1
1
  import React from 'react';
2
2
  import { CellConfiguration } from '../../../decorators/list/Cell';
3
3
  import { DownloadCellConfiguration } from '../../../decorators/list/cells/DownloadCell';
4
+ import { AnyClass } from '../../../types/AnyClass';
4
5
 
5
6
  interface DownloadCellProps {
6
- value: string;
7
+ item: AnyClass;
7
8
  configuration: CellConfiguration;
8
9
  }
9
10
 
10
- export function DownloadCell({ value, configuration }: DownloadCellProps): React.ReactElement {
11
+ export function DownloadCell({ item, configuration }: DownloadCellProps): React.ReactElement {
12
+ const value = item[configuration.name];
11
13
  if (!value) return <>-</>;
12
14
 
13
15
  const downloadConfiguration = configuration as DownloadCellConfiguration;
@@ -1,13 +1,16 @@
1
1
  import React from 'react';
2
2
  import { CellConfiguration } from '../../../decorators/list/Cell';
3
3
  import { ImageCellConfiguration } from '../../../decorators/list/cells/ImageCell';
4
+ import { AnyClass } from '../../../types/AnyClass';
5
+
4
6
  interface ImageCellProps {
5
- value: string;
7
+ item: AnyClass;
6
8
  configuration: CellConfiguration;
7
9
  }
8
10
 
9
- export function ImageCell({ value, configuration }: ImageCellProps) {
11
+ export function ImageCell({ item, configuration }: ImageCellProps) {
10
12
  const imageConfiguration = configuration as ImageCellConfiguration;
13
+ const value = item[configuration.name];
11
14
  if (!value) return <>-</>;
12
15
 
13
16
  return (
@@ -0,0 +1,31 @@
1
+ import React from 'react';
2
+ import { CellConfiguration } from '../../../decorators/list/Cell';
3
+ import { LinkCellConfiguration } from '../../../decorators/list/cells/LinkCell';
4
+ import { Link } from 'react-router';
5
+
6
+ interface LinkCellProps<T> {
7
+ item: T;
8
+ configuration: CellConfiguration;
9
+ }
10
+
11
+ export function LinkCell<T>({ item, configuration }: LinkCellProps<T>) {
12
+ const linkConfiguration = configuration as LinkCellConfiguration<T>;
13
+ const value = item[configuration.name as keyof T] ?? 'Link';
14
+
15
+ return (
16
+ <Link to={linkConfiguration.path ?? linkConfiguration.url ?? ''}>
17
+ {linkConfiguration.onClick ? (
18
+ <a
19
+ className="util-cell-link"
20
+ onClick={() => {
21
+ linkConfiguration.onClick?.(item as T);
22
+ }}
23
+ >
24
+ {value?.toString()}
25
+ </a>
26
+ ) : (
27
+ value?.toString() || linkConfiguration.placeHolder
28
+ )}
29
+ </Link>
30
+ );
31
+ }
@@ -1,10 +1,14 @@
1
1
  import React from 'react';
2
+ import { AnyClass } from '../../../types/AnyClass';
3
+ import { CellConfiguration } from '../../../decorators/list/Cell';
2
4
 
3
5
  interface UUIDCellProps {
4
- value: string;
6
+ item: AnyClass;
7
+ configuration: CellConfiguration;
5
8
  }
6
9
 
7
- export function UUIDCell({ value }: UUIDCellProps) {
10
+ export function UUIDCell({ item, configuration }: UUIDCellProps) {
11
+ const value = item[configuration.name];
8
12
  if (!value || typeof value !== 'string' || value.length < 6) return <>-</>;
9
13
 
10
14
  return <>{`${value.slice(0, 3)}...${value.slice(-3)}`}</>;
@@ -7,7 +7,7 @@ export type GetDetailsDataFN<T> = (param: Record<string, string>) => Promise<T>;
7
7
  interface DetailsOptions<T extends AnyClass> {
8
8
  getDetailsData: GetDetailsDataFN<T>;
9
9
  key?: string;
10
- primaryId: keyof T;
10
+ primaryId?: keyof T;
11
11
  }
12
12
 
13
13
  export type DetailsConfiguration<T extends AnyClass> = DetailsOptions<T> & {
@@ -14,7 +14,7 @@ export interface StaticSelectFilter extends Filter {
14
14
  }
15
15
 
16
16
  export type CellTypes = 'string' | 'date' | 'number' | 'boolean' | 'uuid';
17
- export type ExtendedCellTypes = CellTypes | 'image' | 'download';
17
+ export type ExtendedCellTypes = CellTypes | 'image' | 'download' | 'link';
18
18
 
19
19
  export interface CellOptions {
20
20
  name?: string;
@@ -22,6 +22,10 @@ export interface CellOptions {
22
22
  type?: CellTypes;
23
23
  placeHolder?: string;
24
24
  filter?: Filter | StaticSelectFilter;
25
+ style?: {
26
+ minWidth?: string;
27
+ width?: string;
28
+ };
25
29
  }
26
30
 
27
31
  export interface CellConfiguration extends Omit<CellOptions, 'type'> {
@@ -25,7 +25,8 @@ export interface ListHeaderOptions {
25
25
  create?: { path: string; label: string };
26
26
  }
27
27
 
28
- export interface ListCellOptions<T> {
28
+ export interface ListActionOptions<T> {
29
+ customActions?: { label: string; onClick: (item: T) => void; icon?: string }[];
29
30
  details?: { path: string; label: string };
30
31
  edit?: { path: string; label: string };
31
32
  delete?: { label: string; onRemoveItem?: (item: T) => Promise<void> };
@@ -34,13 +35,12 @@ export interface ListCellOptions<T> {
34
35
  export interface ListOptions<T> {
35
36
  getData: GetDataForList<T>;
36
37
  headers?: ListHeaderOptions;
37
- cells?: ((item: T) => ListCellOptions<T>) | ListCellOptions<T>;
38
- primaryId: string;
38
+ actions?: ((item: T) => ListActionOptions<T>) | ListActionOptions<T>;
39
+ primaryId?: string;
39
40
  key?: string;
40
41
  }
41
42
 
42
43
  export type ListConfiguration<T> = ListOptions<T> & {
43
- primaryId: string;
44
44
  key: string;
45
45
  };
46
46
 
@@ -0,0 +1,22 @@
1
+ import { CellConfiguration, CellOptions } from '../Cell';
2
+ import { ExtendedCell } from '../ExtendedCell';
3
+
4
+ export interface LinkCellOptions<T> extends Omit<CellOptions, 'type'> {
5
+ url?: string;
6
+ path?: string;
7
+ onClick?: (data: T) => void;
8
+ }
9
+
10
+ export interface LinkCellConfiguration<T> extends CellConfiguration {
11
+ type: 'link';
12
+ url?: string;
13
+ path?: string;
14
+ onClick?: (data: T) => void;
15
+ }
16
+
17
+ export function LinkCell<T>(options?: LinkCellOptions<T>): PropertyDecorator {
18
+ return ExtendedCell(options, (_, options) => ({
19
+ ...options,
20
+ type: 'link',
21
+ }));
22
+ }
package/src/index.ts CHANGED
@@ -13,13 +13,14 @@ export {
13
13
  } from './decorators/list/List';
14
14
  export { ImageCell } from './decorators/list/cells/ImageCell';
15
15
  export { Cell } from './decorators/list/Cell';
16
+ export { DownloadCell } from './decorators/list/cells/DownloadCell';
17
+ export { LinkCell } from './decorators/list/cells/LinkCell';
16
18
 
17
19
  //FORM
18
20
  export { FormPage } from './components/form/FormPage';
19
21
  export { Form, type OnSubmitFN } from './decorators/form/Form';
20
22
  export { Input } from './decorators/form/Input';
21
23
  export { SelectInput } from './decorators/form/inputs/SelectInput';
22
- export { DownloadCell } from './decorators/list/cells/DownloadCell';
23
24
  //for nested form fields
24
25
  export { getInputFields } from './decorators/form/Input';
25
26
 
@@ -9,13 +9,15 @@ export function updateDetailsData<T extends AnyClass>(
9
9
  ) {
10
10
  const { class: detailsClass } = getDetailsPageMeta(model);
11
11
  const key = detailsClass.key;
12
- const id = detailsClass.primaryId;
13
- console.log('updateDetailsData', model, data, detailsClass);
14
- if (!data[id]) {
15
- throw new Error(`Id ${id} not found in data`);
12
+ if (!detailsClass.primaryId) {
13
+ throw new Error('Primary id is required to use this utility function');
16
14
  }
17
15
 
18
- useAppStore.getState().updateDetailsData(key, data[id]?.toString(), data);
16
+ if (!data[detailsClass.primaryId]) {
17
+ throw new Error(`Id ${detailsClass.primaryId} not found in data`);
18
+ }
19
+
20
+ useAppStore.getState().updateDetailsData(key, data[detailsClass.primaryId]?.toString(), data);
19
21
  }
20
22
 
21
23
  export function updateListData<T extends AnyClass>(
@@ -24,11 +26,13 @@ export function updateListData<T extends AnyClass>(
24
26
  ) {
25
27
  const { class: listClass } = getListPageMeta(model);
26
28
  const key = listClass.key;
27
- const id = listClass.primaryId;
28
- console.log('updateListData', model, data, listClass);
29
- if (!data[id]) {
30
- throw new Error(`Id ${id} not found in data`);
29
+ if (!listClass.primaryId) {
30
+ throw new Error('Primary id is required to use this utility function');
31
+ }
32
+
33
+ if (!data[listClass.primaryId]) {
34
+ throw new Error(`Id ${listClass.primaryId} not found in data`);
31
35
  }
32
36
 
33
- useAppStore.getState().updateListData(key, data[id]?.toString(), data);
37
+ useAppStore.getState().updateListData(key, data[listClass.primaryId]?.toString(), data);
34
38
  }
@@ -34,7 +34,7 @@ export const useAppStore = createWithEqualityFn<AppState>()(
34
34
  }),
35
35
  }),
36
36
  {
37
- name: 'app-store-1',
37
+ name: 'proje-panel-store',
38
38
  storage: createJSONStorage(() => localStorage),
39
39
  partialize: state => ({
40
40
  user: state.user,
@@ -0,0 +1,14 @@
1
+ .panel-button {
2
+ padding: 0.5rem 1rem;
3
+ border: 1px solid #444444;
4
+ border-radius: 4px;
5
+ background-color: cornflowerblue;
6
+ color: #ffffff;
7
+ font-size: 0.875rem;
8
+ font-weight: 500;
9
+ cursor: pointer;
10
+ transition: all 0.2s ease;
11
+ &:hover {
12
+ transform: scale(1.01);
13
+ }
14
+ }
@@ -11,8 +11,8 @@
11
11
  @import './components/uploader.scss';
12
12
  @import './components/checkbox';
13
13
  @import './components/form-header';
14
+ @import './components/button';
14
15
  @import './details';
15
-
16
16
  //TODO: import deprecated
17
17
  .layout {
18
18
  display: flex;