proje-react-panel 1.0.8 → 1.0.10

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 (100) hide show
  1. package/.eslintrc.js +2 -0
  2. package/.idea/vcs.xml +0 -1
  3. package/.idea/watcherTasks.xml +4 -0
  4. package/dist/api/crudApi.d.ts +12 -4
  5. package/dist/components/ErrorBoundary.d.ts +16 -0
  6. package/dist/components/ErrorComponent.d.ts +4 -0
  7. package/dist/components/Form.d.ts +6 -0
  8. package/dist/components/FormField.d.ts +13 -0
  9. package/dist/components/Label.d.ts +8 -0
  10. package/dist/components/Panel.d.ts +3 -1
  11. package/dist/components/layout/Layout.d.ts +9 -2
  12. package/dist/components/layout/SideBar.d.ts +13 -2
  13. package/dist/components/list/List.d.ts +5 -5
  14. package/dist/components/screens/ControllerCreate.d.ts +5 -0
  15. package/dist/components/screens/ControllerDetails.d.ts +5 -0
  16. package/dist/components/screens/ControllerEdit.d.ts +5 -0
  17. package/dist/components/screens/ControllerList.d.ts +5 -0
  18. package/dist/components/screens/Login.d.ts +4 -0
  19. package/dist/decorators/Cell.d.ts +9 -0
  20. package/dist/decorators/Input.d.ts +13 -0
  21. package/dist/hooks/useScreens.d.ts +2 -0
  22. package/dist/index.cjs.js +1 -12
  23. package/dist/index.d.ts +6 -1
  24. package/dist/index.esm.js +1 -12
  25. package/dist/initPanel.d.ts +2 -0
  26. package/dist/initPanelOptions.d.ts +8 -0
  27. package/dist/screens/ControllerDetails.d.ts +2 -2
  28. package/dist/screens/ControllerEdit.d.ts +2 -2
  29. package/dist/screens/ControllerList.d.ts +2 -2
  30. package/dist/screens/Form.d.ts +2 -2
  31. package/dist/src/api/crudApi.d.ts +6 -0
  32. package/dist/src/components/Panel.d.ts +9 -0
  33. package/dist/src/components/layout/Layout.d.ts +11 -0
  34. package/dist/src/components/layout/SideBar.d.ts +10 -0
  35. package/dist/src/components/list/List.d.ts +10 -0
  36. package/dist/src/decorators/Crud.d.ts +6 -0
  37. package/dist/src/index.d.ts +8 -0
  38. package/dist/src/screens/ControllerCreate.d.ts +5 -0
  39. package/dist/src/screens/ControllerDetails.d.ts +5 -0
  40. package/dist/src/screens/ControllerEdit.d.ts +5 -0
  41. package/dist/src/screens/ControllerList.d.ts +5 -0
  42. package/dist/src/screens/Form.d.ts +6 -0
  43. package/dist/src/store/store.d.ts +19 -0
  44. package/dist/src/types/Screen.d.ts +4 -0
  45. package/dist/src/types/ScreenCreatorData.d.ts +8 -0
  46. package/dist/src/utils/createScreens.d.ts +1 -0
  47. package/dist/src/utils/getFields.d.ts +2 -0
  48. package/dist/src/utils/getScreens.d.ts +2 -0
  49. package/dist/store/store.d.ts +19 -0
  50. package/dist/types/ScreenCreatorData.d.ts +9 -6
  51. package/dist/types/initPanelOptions.d.ts +8 -0
  52. package/dist/utils/crudScreens.d.ts +2 -0
  53. package/dist/utils/getFields.d.ts +1 -1
  54. package/dist/utils/getScreens.d.ts +2 -0
  55. package/package.json +14 -8
  56. package/src/api/crudApi.ts +30 -28
  57. package/src/components/ErrorBoundary.tsx +64 -0
  58. package/src/components/ErrorComponent.tsx +15 -0
  59. package/src/components/Form.tsx +70 -0
  60. package/src/components/FormField.tsx +60 -0
  61. package/src/components/Label.tsx +15 -0
  62. package/src/components/Panel.tsx +12 -7
  63. package/src/components/layout/Layout.tsx +16 -12
  64. package/src/components/layout/SideBar.tsx +38 -12
  65. package/src/components/list/List.tsx +73 -67
  66. package/src/components/screens/ControllerCreate.tsx +7 -0
  67. package/src/components/screens/ControllerDetails.tsx +40 -0
  68. package/src/components/screens/ControllerEdit.tsx +35 -0
  69. package/src/components/screens/ControllerList.tsx +44 -0
  70. package/src/components/screens/Login.tsx +64 -0
  71. package/src/decorators/Cell.ts +34 -0
  72. package/src/decorators/Input.ts +50 -0
  73. package/src/hooks/useScreens.tsx +37 -0
  74. package/src/index.ts +7 -2
  75. package/src/initPanel.ts +14 -0
  76. package/src/store/store.ts +18 -0
  77. package/src/styles/error-boundary.scss +89 -0
  78. package/src/styles/form.scss +38 -6
  79. package/src/styles/index.scss +22 -4
  80. package/src/styles/layout.scss +49 -0
  81. package/src/styles/list.scss +8 -6
  82. package/src/styles/login.scss +90 -0
  83. package/src/styles/sidebar.scss +17 -22
  84. package/src/types/ScreenCreatorData.ts +10 -7
  85. package/src/types/initPanelOptions.ts +8 -0
  86. package/src/utils/createScreens.ts +2 -2
  87. package/src/utils/getFields.ts +8 -9
  88. package/dist/utils/getScreenForRoutes.d.ts +0 -2
  89. package/dist/utils/storeData.d.ts +0 -4
  90. package/src/declerations/Cell.ts +0 -37
  91. package/src/screens/ControllerCreate.tsx +0 -13
  92. package/src/screens/ControllerDetails.tsx +0 -34
  93. package/src/screens/ControllerEdit.tsx +0 -31
  94. package/src/screens/ControllerList.tsx +0 -40
  95. package/src/screens/Form.tsx +0 -67
  96. package/src/utils/getScreenForRoutes.tsx +0 -44
  97. package/src/utils/storeData.ts +0 -7
  98. /package/dist/{declerations → decorators}/Crud.d.ts +0 -0
  99. /package/dist/{declerations → src/decorators}/Cell.d.ts +0 -0
  100. /package/src/{declerations → decorators}/Crud.ts +0 -0
package/package.json CHANGED
@@ -1,15 +1,12 @@
1
1
  {
2
2
  "name": "proje-react-panel",
3
- "version": "1.0.8",
3
+ "version": "1.0.10",
4
4
  "description": "",
5
5
  "author": "SEFA DEMİR",
6
6
  "license": "ISC",
7
7
  "main": "dist/index.cjs.js",
8
8
  "module": "dist/index.esm.js",
9
- "source": [
10
- "src/index.ts",
11
- "src/dom/index.ts"
12
- ],
9
+ "source": "src/index.ts",
13
10
  "types": "dist/index.d.ts",
14
11
  "scripts": {
15
12
  "test": "echo \"Error: no test specified\" && exit 1",
@@ -31,22 +28,31 @@
31
28
  "@rollup/plugin-commonjs": "^28.0.3",
32
29
  "@rollup/plugin-node-resolve": "^16.0.1",
33
30
  "@types/react": "^19.0.10",
34
- "@types/react-dom": "^19.0.4",
35
31
  "@typescript-eslint/eslint-plugin": "^8.26.1",
36
32
  "@typescript-eslint/parser": "^8.26.1",
37
33
  "class-transformer": "^0.5.1",
38
34
  "class-validator": "^0.14.1",
39
35
  "eslint": "^8.57.1",
36
+ "eslint-plugin-react": "^7.37.4",
37
+ "prettier": "^3.5.3",
40
38
  "react": "^19.0.0",
41
39
  "react-hook-form": "^7.54.2",
42
40
  "react-router": "^7.3.0",
43
- "react-router-dom": "^7.3.0",
44
41
  "reflect-metadata": "^0.2.2",
45
42
  "rollup": "^4.35.0",
46
43
  "rollup-plugin-peer-deps-external": "^2.2.4",
47
44
  "rollup-plugin-serve": "^3.0.0",
48
45
  "rollup-plugin-terser": "^7.0.2",
49
46
  "rollup-plugin-typescript2": "^0.36.0",
50
- "typescript": "^5.8.2"
47
+ "typescript": "^5.8.2",
48
+ "use-sync-external-store": "^1.4.0",
49
+ "zustand": "^5.0.3"
50
+ },
51
+ "peerDependencies": {
52
+ "react": ">=19.0.0",
53
+ "react-hook-form": ">=7.54.2",
54
+ "react-router": "7.3.0",
55
+ "use-sync-external-store": ">=1.4.0",
56
+ "zustand": ">=5.0.3"
51
57
  }
52
58
  }
@@ -1,30 +1,32 @@
1
-
2
1
  export const CrudApi = {
3
- getList: (api: string, page: number) => {
4
- return fetch(api, {
5
- method: "GET",
6
- headers: { "Content-Type": "application/json" },
7
- body: JSON.stringify({ page }),
8
- }).then((res) => res.json());
9
- },
10
- create: (api: string, data: any) => {
11
- return fetch(api, {
12
- method: "POST",
13
- headers: { "Content-Type": "application/json" },
14
- body: JSON.stringify(data),
15
- }).then((res) => res.json());
16
- },
17
- details(api: string, id: any) {
18
- return fetch(`${api}/${id}`, {
19
- method: "GET",
20
- headers: { "Content-Type": "application/json" },
21
- }).then((res) => res.json());
22
- },
23
- edit(api: string, data: any) {
24
- return fetch(`${api}/${data.id}`, {
25
- method: "PUT",
26
- headers: { "Content-Type": "application/json" },
27
- body: JSON.stringify(data),
28
- }).then((res) => res.json());
29
- },
2
+ getList: (fetchSettings: { baseUrl: string }, api: string) => {
3
+ return fetch(`${fetchSettings.baseUrl}/${api}`, {
4
+ method: "GET",
5
+ headers: { "Content-Type": "application/json" },
6
+ }).then((res) => {
7
+ return res.json();
8
+ });
9
+ },
10
+ create: (fetchSettings: { baseUrl: string }, api: string, data: any) => {
11
+ return fetch(`${fetchSettings?.baseUrl ?? ""}/${api}`, {
12
+ method: "POST",
13
+ headers: { "Content-Type": "application/json" },
14
+ body: JSON.stringify(data),
15
+ }).then((res) => res.json());
16
+ },
17
+ details: (fetchSettings: { baseUrl: string }, api: string, id: any) => {
18
+ return fetch(`${fetchSettings?.baseUrl ?? ""}/${api}/${id}`, {
19
+ method: "GET",
20
+ headers: { "Content-Type": "application/json" },
21
+ }).then((res) => {
22
+ return res.json();
23
+ });
24
+ },
25
+ edit: (fetchSettings: { baseUrl: string }, api: string, data: any) => {
26
+ return fetch(`${fetchSettings?.baseUrl ?? ""}/${api}/${data.id}`, {
27
+ method: "PUT",
28
+ headers: { "Content-Type": "application/json" },
29
+ body: JSON.stringify(data),
30
+ }).then((res) => res.json());
31
+ },
30
32
  };
@@ -0,0 +1,64 @@
1
+ import React, { Component, ErrorInfo, ReactNode } from "react";
2
+
3
+ interface Props {
4
+ children: ReactNode;
5
+ }
6
+
7
+ interface State {
8
+ hasError: boolean;
9
+ error: Error | null;
10
+ errorInfo: ErrorInfo | null;
11
+ }
12
+
13
+ export class ErrorBoundary extends Component<Props, State> {
14
+ public state: State = {
15
+ hasError: false,
16
+ error: null,
17
+ errorInfo: null,
18
+ };
19
+
20
+ public static getDerivedStateFromError(error: Error): State {
21
+ return { hasError: true, error, errorInfo: null };
22
+ }
23
+
24
+ public componentDidCatch(error: Error, errorInfo: ErrorInfo) {
25
+ this.setState({
26
+ error: error,
27
+ errorInfo: errorInfo,
28
+ });
29
+ }
30
+
31
+ public render() {
32
+ if (this.state.hasError) {
33
+ return (
34
+ <div className="error-boundary">
35
+ <div className="error-boundary__content">
36
+ <div className="error-boundary__icon">
37
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor">
38
+ <circle cx="12" cy="12" r="10" />
39
+ <line x1="12" y1="8" x2="12" y2="12" />
40
+ <line x1="12" y1="16" x2="12" y2="16" />
41
+ </svg>
42
+ </div>
43
+ <h1>Oops! Something went wrong</h1>
44
+ <p className="error-boundary__message">
45
+ {this.state.error?.message || "An unexpected error occurred"}
46
+ </p>
47
+ <button className="error-boundary__button" onClick={() => window.location.reload()}>
48
+ Refresh Page
49
+ </button>
50
+ {process.env.NODE_ENV === "development" && (
51
+ <details className="error-boundary__details">
52
+ <summary>Error Details</summary>
53
+ <pre>{this.state.error?.toString()}</pre>
54
+ <pre>{this.state.errorInfo?.componentStack}</pre>
55
+ </details>
56
+ )}
57
+ </div>
58
+ </div>
59
+ );
60
+ }
61
+
62
+ return this.props.children;
63
+ }
64
+ }
@@ -0,0 +1,15 @@
1
+ import React from "react";
2
+
3
+ export function ErrorComponent({ error }: { error: unknown }) {
4
+ return (
5
+ <div className="error-container">
6
+ <div className="error-icon">
7
+ <i className="fa fa-exclamation-circle" />
8
+ </div>
9
+ <div className="error-content">
10
+ <h3>Error Occurred</h3>
11
+ <p>{(error as { message?: string })?.message || "Something went wrong. Please try again later."}</p>
12
+ </div>
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,70 @@
1
+ import React, { useEffect } from "react";
2
+ import { Screen } from "../types/Screen";
3
+ import { useForm } from "react-hook-form";
4
+ import { useNavigate } from "react-router";
5
+ import { CrudApi } from "../api/crudApi";
6
+ import { useAppStore } from "../store/store";
7
+ import { InputOptions } from "../decorators/Input";
8
+ import { FormField } from "./FormField";
9
+
10
+ export function Form({ data, screen }: { data?: any; screen: Screen }) {
11
+ const { screens, fetchSettings } = useAppStore((s) => ({
12
+ screens: s.screens ?? {},
13
+ fetchSettings: s.fetchSettings,
14
+ }));
15
+ const isEditForm = !!data;
16
+ const {
17
+ register,
18
+ handleSubmit,
19
+ reset,
20
+ formState: { errors },
21
+ } = useForm<any>({
22
+ resolver: screens[screen.controller].resolver,
23
+ //TODO: remove __formEdit from api
24
+ defaultValues: { ...data, __formEdit: isEditForm },
25
+ });
26
+ const navigate = useNavigate();
27
+ //const fields = screens[screen.controller].fields;
28
+ const inputs = screens[screen.controller].inputs;
29
+ useEffect(() => {
30
+ reset({ ...data, __formEdit: isEditForm });
31
+ }, [isEditForm, data, reset]);
32
+
33
+ return (
34
+ <div className="form-wrapper">
35
+ <form
36
+ onSubmit={handleSubmit((dataForm) => {
37
+ if (!fetchSettings) {
38
+ return;
39
+ }
40
+ delete dataForm.__formEdit;
41
+ if (isEditForm) {
42
+ CrudApi.edit(fetchSettings, screen.controller, dataForm).then(() => {
43
+ navigate("/" + screen.controller, {
44
+ replace: true,
45
+ });
46
+ });
47
+ } else {
48
+ CrudApi.create(fetchSettings, screen.controller, dataForm).then(() => {
49
+ navigate("/" + screen.controller, {
50
+ replace: true,
51
+ });
52
+ });
53
+ }
54
+ })}>
55
+ {inputs.map((input: InputOptions) => (
56
+ <FormField
57
+ key={input.name || ""}
58
+ input={input}
59
+ register={register}
60
+ isEditForm={isEditForm}
61
+ error={errors[input.name || ""]}
62
+ />
63
+ ))}
64
+ <button type="submit" className="submit-button">
65
+ Submit
66
+ </button>
67
+ </form>
68
+ </div>
69
+ );
70
+ }
@@ -0,0 +1,60 @@
1
+ import React from "react";
2
+ import { InputOptions } from "../decorators/Input";
3
+ import { Label } from "./Label";
4
+ import { UseFormRegister } from "react-hook-form";
5
+
6
+ interface FormFieldProps {
7
+ input: InputOptions;
8
+ register: UseFormRegister<any>;
9
+ isEditForm: boolean;
10
+ error?: { message?: string };
11
+ }
12
+
13
+ export function FormField({ input, register, isEditForm, error }: FormFieldProps) {
14
+ const fieldName = input.name || "";
15
+
16
+ const renderField = () => {
17
+ switch (input.type) {
18
+ case "textarea":
19
+ return (
20
+ <textarea
21
+ {...register(fieldName)}
22
+ placeholder={input.placeholder}
23
+ id={fieldName}
24
+ disabled={isEditForm && input.editable === false}
25
+ />
26
+ );
27
+ case "select":
28
+ return (
29
+ <select {...register(fieldName)} id={fieldName} disabled={isEditForm && input.editable === false}>
30
+ <option value="">Select {fieldName}</option>
31
+ {input.selectOptions?.map((option) => (
32
+ <option key={option} value={option}>
33
+ {option}
34
+ </option>
35
+ ))}
36
+ </select>
37
+ );
38
+ case "input":
39
+ default: {
40
+ return (
41
+ <input
42
+ type={input.inputType}
43
+ {...register(fieldName)}
44
+ placeholder={input.placeholder}
45
+ id={fieldName}
46
+ disabled={isEditForm && input.editable === false}
47
+ />
48
+ );
49
+ }
50
+ }
51
+ };
52
+
53
+ return (
54
+ <div className="form-field">
55
+ <Label htmlFor={fieldName} label={input.label} fieldName={fieldName} />
56
+ {renderField()}
57
+ {error && <span className="error-message">{error.message}</span>}
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+
3
+ interface LabelProps {
4
+ htmlFor: string;
5
+ label?: string;
6
+ fieldName: string;
7
+ }
8
+
9
+ export function Label({ htmlFor, label, fieldName }: LabelProps) {
10
+ return (
11
+ <label htmlFor={htmlFor}>
12
+ {label ?? fieldName.charAt(0).toUpperCase() + fieldName.slice(1)}
13
+ </label>
14
+ );
15
+ }
@@ -1,13 +1,18 @@
1
- import React from "react";
1
+ import React, { useEffect } from "react";
2
+ import { ErrorBoundary } from "./ErrorBoundary";
3
+ import { initPanel } from "../initPanel";
4
+ import { InitPanelOptions } from "../types/initPanelOptions";
2
5
 
3
6
  type AppProps = {
4
7
  children: React.ReactNode;
8
+ init: () => InitPanelOptions;
5
9
  };
6
10
 
7
- export function Panel({ children }: AppProps) {
8
- return (
9
- <>
10
- {children}
11
- </>
12
- );
11
+ export function Panel({ children, init }: AppProps) {
12
+ useEffect(() => {
13
+ const options = init();
14
+ initPanel(options);
15
+ }, [init]);
16
+
17
+ return <ErrorBoundary>{children}</ErrorBoundary>;
13
18
  }
@@ -1,16 +1,20 @@
1
- import React from 'react';
2
- import { Outlet } from 'react-router';
3
- import { SideBar } from './SideBar';
1
+ import React from "react";
2
+ import { SideBar } from "./SideBar";
3
+ import { ScreenCreatorData } from "../../types/ScreenCreatorData";
4
4
 
5
- export function Layout({
6
- children,
5
+ export function Layout<IconType>({
6
+ children,
7
+ menu,
8
+ getIcons,
7
9
  }: {
8
- children?: React.ReactNode;
10
+ children?: React.ReactNode;
11
+ menu?: (screens: Record<string, ScreenCreatorData>) => { name: string; path: string; iconType: IconType }[];
12
+ getIcons?: (iconType: IconType) => React.ReactNode;
9
13
  }) {
10
- return (
11
- <div className="layout">
12
- <SideBar />
13
- <main className="content">{children || <Outlet />}</main>
14
- </div>
15
- );
14
+ return (
15
+ <div className="layout">
16
+ <SideBar menu={menu} getIcons={getIcons} />
17
+ <main className="content">{children}</main>
18
+ </div>
19
+ );
16
20
  }
@@ -1,16 +1,42 @@
1
- import React, { useState } from 'react';
1
+ import React, { useState } from "react";
2
+ import { Link } from "react-router";
3
+ import { ScreenCreatorData } from "../../types/ScreenCreatorData";
4
+ import { useAppStore } from "../../store/store";
2
5
 
6
+ type GetMenuFunction<IconType> = (
7
+ screens: Record<string, ScreenCreatorData>
8
+ ) => { name: string; path: string; iconType: IconType }[];
3
9
 
4
- export function SideBar() {
5
- const [isOpen, setIsOpen] = useState(true);
10
+ type GetIconsFunction<IconType> = (iconType: IconType) => React.ReactNode;
6
11
 
7
- return (
8
- <div className={`sidebar ${isOpen ? 'open' : 'closed'}`}>
9
- <button className='toggle-button' onClick={() => setIsOpen(!isOpen)}>
10
- {isOpen ? '<' : '>'}
11
- </button>
12
- <nav className='nav-links'>
13
- </nav>
14
- </div>
15
- );
12
+ export function SideBar<IconType>({
13
+ menu,
14
+ getIcons,
15
+ }: {
16
+ menu?: GetMenuFunction<IconType>;
17
+ getIcons?: GetIconsFunction<IconType>;
18
+ }) {
19
+ const screens = useAppStore((s) => s.screens ?? {});
20
+ const [isOpen, setIsOpen] = useState(true);
21
+
22
+ return (
23
+ <div className={`sidebar ${isOpen ? "open" : "closed"}`}>
24
+ <button className="toggle-button" onClick={() => setIsOpen(!isOpen)}>
25
+ {isOpen ? "<" : ">"}
26
+ </button>
27
+ <nav className="nav-links">
28
+ {menu?.(screens).map((item, index) => (
29
+ <Link key={index} to={item.path} className="nav-link">
30
+ <span className={"nav-links-icon"}>{getIcons?.(item.iconType)}</span>
31
+ {isOpen ? <span>{item.name}</span> : null}
32
+ </Link>
33
+ ))}
34
+ {/*{screens.map(([key, screen], index) => (
35
+ <Link key={`screen-${index}`} to={`/${key}`} className="nav-link">
36
+ {isOpen ? <span>{key}</span> : null}
37
+ </Link>
38
+ ))}*/}
39
+ </nav>
40
+ </div>
41
+ );
16
42
  }
@@ -1,75 +1,81 @@
1
- import React from 'react';
2
- import { CellOptions } from '../../declerations/Cell';
3
- import { Link } from 'react-router';
4
- import { Screen } from '../../types/Screen';
1
+ import React from "react";
2
+ import { CellOptions } from "../../decorators/Cell";
3
+ import { Link } from "react-router";
4
+ import { Screen } from "../../types/Screen";
5
5
 
6
6
  interface ListProps<T> {
7
- data: T[];
8
- cells: CellOptions<T>[];
9
- screen: Screen;
7
+ data: T[];
8
+ cells: CellOptions[];
9
+ screen: Screen;
10
10
  }
11
11
 
12
- export function List<T>({ data, cells, screen }: ListProps<T>) {
13
- if (!data || data.length === 0) {
14
- return <div>No items available</div>;
15
- }
12
+ export function List<T>({ data, cells }: ListProps<T>) {
13
+ if (!data || data.length === 0) {
14
+ return <div>No items available</div>;
15
+ }
16
16
 
17
- return (
18
- <div className='list-wrapper'>
19
- <div className='header'>List</div>
20
- <table className='list-table'>
21
- <thead>
22
- <tr>
23
- {cells.map((cellOptions) => (
24
- <th key={cellOptions.name}>{cellOptions.title ?? cellOptions.name}</th>
25
- ))}
26
- <th />
27
- </tr>
28
- </thead>
29
- <tbody>
30
- {data.map((item, index) => (
31
- <tr key={index}>
32
- {cells.map((cellOptions) => {
33
- // @ts-ignore
34
- const value = item[cellOptions.name];
35
- let formattedValue = value ?? '-'; // Default value if the field is undefined or null
17
+ return (
18
+ <div className="list-wrapper">
19
+ <div className="header">List</div>
20
+ <table className="list-table">
21
+ <thead>
22
+ <tr>
23
+ {cells.map((cellOptions) => (
24
+ <th key={cellOptions.name}>{cellOptions.title ?? cellOptions.name}</th>
25
+ ))}
26
+ <th />
27
+ </tr>
28
+ </thead>
29
+ <tbody>
30
+ {data.map((item, index) => (
31
+ <tr key={index}>
32
+ {cells.map((cellOptions) => {
33
+ // @ts-ignore
34
+ const value = item[cellOptions.name];
35
+ let formattedValue = value ?? "-"; // Default value if the field is undefined or null
36
36
 
37
- switch (cellOptions.type) {
38
- case 'date':
39
- if (value) {
40
- const date = new Date(value);
41
- formattedValue = `${date.getDate().toString().padStart(2, '0')}/${(date.getMonth() + 1)
42
- .toString()
43
- .padStart(2, '0')}/${date.getFullYear()} ${date.getHours().toString().padStart(2, '0')}:${date
44
- .getMinutes()
45
- .toString()
46
- .padStart(2, '0')}`;
47
- }
48
- break;
37
+ switch (cellOptions.type) {
38
+ case "date":
39
+ if (value) {
40
+ const date = new Date(value);
41
+ formattedValue = `${date.getDate().toString().padStart(2, "0")}/${(
42
+ date.getMonth() + 1
43
+ )
44
+ .toString()
45
+ .padStart(
46
+ 2,
47
+ "0"
48
+ )}/${date.getFullYear()} ${date.getHours().toString().padStart(2, "0")}:${date
49
+ .getMinutes()
50
+ .toString()
51
+ .padStart(2, "0")}`;
52
+ }
53
+ break;
49
54
 
50
- case 'number':
51
- case 'string':
52
- default:
53
- formattedValue = value ? value.toString() : (cellOptions?.placeHolder ?? '-'); // Handles string type or default fallback
54
- break;
55
- }
56
- let render = formattedValue;
57
- if (cellOptions.linkTo) {
58
- render = <Link to={cellOptions.linkTo(item)}>{formattedValue}</Link>;
59
- }
60
- return <td key={cellOptions.name}>{render}</td>;
61
- })}
62
- <td>
63
- {/*@ts-ignore*/}
64
- <Link to={'edit/' + (item?.id ?? '-')}>Edit</Link>
65
- {/*@ts-ignore*/}
66
- <Link to={'details/' + (item?.id ?? '-')}>Details</Link>
67
- </td>
68
- </tr>
69
- ))}
70
-
71
- </tbody>
72
- </table>
73
- </div>
74
- );
55
+ case "number":
56
+ case "string":
57
+ default:
58
+ formattedValue = value ? value.toString() : (cellOptions?.placeHolder ?? "-"); // Handles string type or default fallback
59
+ break;
60
+ }
61
+ let render = formattedValue;
62
+ /*
63
+ if (cellOptions.linkTo) {
64
+ render = <Link to={cellOptions.linkTo(item)}>{formattedValue}</Link>;
65
+ }
66
+ */
67
+ return <td key={cellOptions.name}>{render}</td>;
68
+ })}
69
+ <td>
70
+ {/*@ts-ignore*/}
71
+ <Link to={"edit/" + (item?.id ?? "-")}>Edit</Link>
72
+ {/*@ts-ignore*/}
73
+ <Link to={"details/" + (item?.id ?? "-")}>Details</Link>
74
+ </td>
75
+ </tr>
76
+ ))}
77
+ </tbody>
78
+ </table>
79
+ </div>
80
+ );
75
81
  }
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import { Screen } from "../../types/Screen";
3
+ import { Form } from "../Form";
4
+
5
+ export function ControllerCreate({ screen }: { screen: Screen }) {
6
+ return <Form screen={screen} />;
7
+ }
@@ -0,0 +1,40 @@
1
+ import { useParams } from "react-router";
2
+ import React, { useEffect, useState } from "react";
3
+ import { CrudApi } from "../../api/crudApi";
4
+ import { Screen } from "../../types/Screen";
5
+ import { useAppStore } from "../../store/store";
6
+ import { ErrorComponent } from "../ErrorComponent";
7
+
8
+ export function ControllerDetails({ screen }: { screen: Screen }) {
9
+ const { fetchSettings } = useAppStore((s) => ({
10
+ fetchSettings: s.fetchSettings,
11
+ }));
12
+ const { id } = useParams();
13
+ const [data, setData] = useState<any>(null);
14
+ const [error, setError] = useState(null);
15
+
16
+ useEffect(() => {
17
+ if (fetchSettings && screen.controller && id) {
18
+ CrudApi.details(fetchSettings, screen.controller, id)
19
+ .then((res) => {
20
+ setData(res);
21
+ })
22
+ .catch((e: any) => {
23
+ setError(e);
24
+ console.error(e);
25
+ });
26
+ }
27
+ }, [fetchSettings, id, screen]);
28
+
29
+ if (error) {
30
+ return <ErrorComponent error={error} />;
31
+ }
32
+
33
+ return (
34
+ <p
35
+ dangerouslySetInnerHTML={{
36
+ __html: JSON.stringify(data, null, " " + "<br/>"),
37
+ }}
38
+ />
39
+ );
40
+ }