proje-react-panel 1.3.3 → 1.4.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.
- package/dist/components/DetailsPage.d.ts +4 -1
- package/dist/decorators/details/Details.d.ts +5 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.esm.js +1 -1
- package/dist/services/DataService.d.ts +2 -0
- package/dist/store/store.d.ts +2 -0
- package/package.json +1 -1
- package/src/components/DetailsPage.tsx +22 -1
- package/src/components/form/FormPage.tsx +16 -6
- package/src/decorators/details/Details.ts +6 -1
- package/src/index.ts +3 -0
- package/src/services/DataService.ts +18 -0
- package/src/store/store.ts +12 -1
- package/src/styles/details.scss +4 -0
@@ -4,18 +4,21 @@ import { ErrorComponent } from './ErrorComponent';
|
|
4
4
|
import { AnyClass, AnyClassConstructor } from '../types/AnyClass';
|
5
5
|
import { getDetailsPageMeta } from '../decorators/details/getDetailsPageMeta';
|
6
6
|
import { LoadingScreen } from './LoadingScreen';
|
7
|
+
import { useAppStore } from '../store/store';
|
7
8
|
|
8
9
|
interface DetailsPageProps<T extends AnyClass> {
|
9
10
|
model: AnyClassConstructor<T>;
|
11
|
+
CustomHeader?: ({ data }: { data: T | null }) => React.ReactNode;
|
10
12
|
}
|
11
13
|
|
12
|
-
export function DetailsPage<T extends AnyClass>({ model }: DetailsPageProps<T>) {
|
14
|
+
export function DetailsPage<T extends AnyClass>({ model, CustomHeader }: DetailsPageProps<T>) {
|
13
15
|
const id = useId();
|
14
16
|
const { class: detailsClass, items } = useMemo(() => getDetailsPageMeta(model), [model]);
|
15
17
|
const params = useParams();
|
16
18
|
const [data, setData] = useState<T | null>(null);
|
17
19
|
const [error, setError] = useState(null);
|
18
20
|
const [loading, setLoading] = useState(true);
|
21
|
+
const allDetailsData = useAppStore(state => state.detailsData);
|
19
22
|
|
20
23
|
useEffect(() => {
|
21
24
|
detailsClass
|
@@ -29,6 +32,19 @@ export function DetailsPage<T extends AnyClass>({ model }: DetailsPageProps<T>)
|
|
29
32
|
.finally(() => setLoading(false));
|
30
33
|
}, [params, detailsClass.getDetailsData, detailsClass]);
|
31
34
|
|
35
|
+
useEffect(() => {
|
36
|
+
setData(data => {
|
37
|
+
if (data) {
|
38
|
+
const detailsData =
|
39
|
+
allDetailsData?.[detailsClass.key]?.[data[detailsClass.primaryId] as string] ??
|
40
|
+
({} as Partial<T>);
|
41
|
+
|
42
|
+
return { ...data, ...detailsData };
|
43
|
+
}
|
44
|
+
return null;
|
45
|
+
});
|
46
|
+
}, [detailsClass.key, detailsClass.primaryId, allDetailsData]);
|
47
|
+
|
32
48
|
if (error) {
|
33
49
|
return (
|
34
50
|
<div className="error-container">
|
@@ -47,6 +63,11 @@ export function DetailsPage<T extends AnyClass>({ model }: DetailsPageProps<T>)
|
|
47
63
|
|
48
64
|
return (
|
49
65
|
<div className="details-page">
|
66
|
+
{CustomHeader && (
|
67
|
+
<div className="details-header">
|
68
|
+
<CustomHeader data={data} />
|
69
|
+
</div>
|
70
|
+
)}
|
50
71
|
{items.map(item => (
|
51
72
|
<div key={item.name} className="details-item">
|
52
73
|
<div className="item-label">{item.name}</div>
|
@@ -160,10 +160,13 @@ export function FormPage<T extends AnyClass>({
|
|
160
160
|
defaultValues: inputs.reduce(
|
161
161
|
(acc, input) => {
|
162
162
|
if (input.type === 'nested') {
|
163
|
-
acc[input.name] = input.nestedFields?.reduce(
|
164
|
-
acc
|
165
|
-
|
166
|
-
|
163
|
+
acc[input.name] = input.nestedFields?.reduce(
|
164
|
+
(acc, nestedInput) => {
|
165
|
+
acc[nestedInput.name] = nestedInput.defaultValue;
|
166
|
+
return acc;
|
167
|
+
},
|
168
|
+
{} as Record<string, unknown>
|
169
|
+
);
|
167
170
|
} else {
|
168
171
|
acc[input.name] = input.defaultValue;
|
169
172
|
}
|
@@ -183,10 +186,17 @@ export function FormPage<T extends AnyClass>({
|
|
183
186
|
useEffect(() => {
|
184
187
|
if (formClass.getDetailsData) {
|
185
188
|
formClass.getDetailsData(params as Record<string, string>).then(data => {
|
186
|
-
|
189
|
+
inputs.forEach(input => {
|
190
|
+
if (input.type === 'nested') {
|
191
|
+
//TODO: examine this
|
192
|
+
form.setValue(input.name as Path<T>, data[input.name]);
|
193
|
+
} else {
|
194
|
+
form.setValue(input.name as Path<T>, data[input.name]);
|
195
|
+
}
|
196
|
+
});
|
187
197
|
});
|
188
198
|
}
|
189
|
-
}, [
|
199
|
+
}, [form, formClass, inputs, params]);
|
190
200
|
|
191
201
|
return (
|
192
202
|
<div className={`form-wrapper ${className ?? ''}`}>
|
@@ -6,9 +6,13 @@ export type GetDetailsDataFN<T> = (param: Record<string, string>) => Promise<T>;
|
|
6
6
|
|
7
7
|
interface DetailsOptions<T extends AnyClass> {
|
8
8
|
getDetailsData: GetDetailsDataFN<T>;
|
9
|
+
key?: string;
|
10
|
+
primaryId: keyof T;
|
9
11
|
}
|
10
12
|
|
11
|
-
export type DetailsConfiguration<T extends AnyClass> = DetailsOptions<T
|
13
|
+
export type DetailsConfiguration<T extends AnyClass> = DetailsOptions<T> & {
|
14
|
+
key: string;
|
15
|
+
};
|
12
16
|
|
13
17
|
export function Details<T extends AnyClass>(options?: DetailsOptions<T>): ClassDecorator {
|
14
18
|
return (target: object) => {
|
@@ -27,5 +31,6 @@ export function getDetailsConfiguration<T extends AnyClass>(
|
|
27
31
|
}
|
28
32
|
return {
|
29
33
|
...detailsConfiguration,
|
34
|
+
key: detailsConfiguration.key || entityClass.name,
|
30
35
|
};
|
31
36
|
}
|
package/src/index.ts
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
import { getDetailsPageMeta } from '../decorators/details/getDetailsPageMeta';
|
2
|
+
import { AnyClass, AnyClassConstructor } from '../types/AnyClass';
|
3
|
+
import { useAppStore } from '../store/store';
|
4
|
+
|
5
|
+
export function updateDetailsData<T extends AnyClass>(
|
6
|
+
model: AnyClassConstructor<T>,
|
7
|
+
data: Partial<T>
|
8
|
+
) {
|
9
|
+
const { class: detailsClass } = getDetailsPageMeta(model);
|
10
|
+
const key = detailsClass.key;
|
11
|
+
const id = detailsClass.primaryId;
|
12
|
+
console.log('updateDetailsData', model, data, detailsClass);
|
13
|
+
if (!data[id]) {
|
14
|
+
throw new Error(`Id ${id} not found in data`);
|
15
|
+
}
|
16
|
+
|
17
|
+
useAppStore.getState().updateDetailsData(key, data[id]?.toString(), data);
|
18
|
+
}
|
package/src/store/store.ts
CHANGED
@@ -4,6 +4,8 @@ import { shallow } from 'zustand/vanilla/shallow';
|
|
4
4
|
import { User } from '../types/User';
|
5
5
|
|
6
6
|
interface AppState {
|
7
|
+
detailsData: Record<string, Record<string, unknown>>;
|
8
|
+
updateDetailsData: (key: string, id: string, data: unknown) => void;
|
7
9
|
user: User | null;
|
8
10
|
login: (user: User) => void;
|
9
11
|
logout: () => void;
|
@@ -11,16 +13,25 @@ interface AppState {
|
|
11
13
|
|
12
14
|
export const useAppStore = createWithEqualityFn<AppState>()(
|
13
15
|
persist(
|
14
|
-
set => ({
|
16
|
+
(set, get) => ({
|
15
17
|
user: null,
|
16
18
|
login: (user: User) => set({ user }),
|
17
19
|
logout: () => set({ user: null }),
|
20
|
+
detailsData: {},
|
21
|
+
updateDetailsData: (key: string, id: string, data: unknown) =>
|
22
|
+
set({
|
23
|
+
detailsData: {
|
24
|
+
...get().detailsData,
|
25
|
+
[key]: { ...get().detailsData[key], [id]: data },
|
26
|
+
},
|
27
|
+
}),
|
18
28
|
}),
|
19
29
|
{
|
20
30
|
name: 'app-store-1',
|
21
31
|
storage: createJSONStorage(() => localStorage),
|
22
32
|
partialize: state => ({
|
23
33
|
user: state.user,
|
34
|
+
detailsData: {},
|
24
35
|
}),
|
25
36
|
}
|
26
37
|
),
|