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.
@@ -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((acc, nestedInput) => {
164
- acc[nestedInput.name] = nestedInput.defaultValue;
165
- return acc;
166
- }, {} as Record<string, unknown>);
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
- form.reset(data as T);
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
- }, [params, form.reset, formClass.getDetailsData, formClass, form]);
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
@@ -36,3 +36,6 @@ export { Layout } from './components/layout';
36
36
  export { Login } from './components/Login';
37
37
  export { login } from './utils/login';
38
38
  export { logout } from './utils/logout';
39
+
40
+ //SERVICES
41
+ export { updateDetailsData } from './services/DataService';
@@ -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
+ }
@@ -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
  ),
@@ -12,6 +12,10 @@
12
12
  max-width: 1200px;
13
13
  margin: 0 auto;
14
14
 
15
+ .details-header {
16
+ margin-bottom: 2rem;
17
+ }
18
+
15
19
  .details-item {
16
20
  background: var(--background-color);
17
21
  border-radius: 8px;