proje-react-panel 1.11.0-beta.0 → 1.11.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "proje-react-panel",
3
- "version": "1.11.0-beta.0",
3
+ "version": "1.11.0",
4
4
  "type": "module",
5
5
  "description": "",
6
6
  "author": "SEFA DEMİR",
@@ -73,7 +73,7 @@
73
73
  "axios": ">=1.0.0",
74
74
  "react": ">=19.0.0",
75
75
  "react-hook-form": ">=7.54.2",
76
- "react-router": "7.3.0",
76
+ "react-router": "^7.3.0",
77
77
  "react-select": "^5.10.1",
78
78
  "use-sync-external-store": ">=1.4.0",
79
79
  "zustand": ">=5.0.3"
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @jest-environment jsdom
3
+ */
4
+ import { TextDecoder, TextEncoder } from 'util';
5
+ // react-router reads TextEncoder at import time and jsdom has none — set it up before the
6
+ // imports below pull the router in.
7
+ Object.assign(globalThis, { TextEncoder, TextDecoder });
8
+
9
+ import 'reflect-metadata';
10
+ import React from 'react';
11
+ import { describe, expect, it } from '@jest/globals';
12
+ import { render } from '@testing-library/react';
13
+ import { MemoryRouter } from 'react-router';
14
+ import { LinkCell } from '../../../components/list/cells/LinkCell';
15
+ import { CellConfiguration } from '../../../decorators/list/Cell';
16
+
17
+ // `@LinkCell({ path: '/users/:id' })` satirin verisiyle dolmalı. Ikame bozulursa
18
+ // hucredeki her open/edit linki gorunuste calisir ama tiklaninca 404 verir —
19
+ // tarayicida fark edilmesi zor, o yuzden burada kilitli.
20
+ const hrefOf = (configuration: CellConfiguration, item: Record<string, unknown>) =>
21
+ render(
22
+ <MemoryRouter>
23
+ <table>
24
+ <tbody>
25
+ <tr>
26
+ <td>
27
+ <LinkCell configuration={configuration} item={item} />
28
+ </td>
29
+ </tr>
30
+ </tbody>
31
+ </table>
32
+ </MemoryRouter>
33
+ )
34
+ .container.querySelector('a')
35
+ ?.getAttribute('href');
36
+
37
+ const linkCell = (over: Partial<CellConfiguration> = {}): CellConfiguration =>
38
+ ({
39
+ name: 'open',
40
+ type: 'link',
41
+ placeHolder: 'open',
42
+ ...over,
43
+ }) as CellConfiguration;
44
+
45
+ describe('LinkCell path parameters', () => {
46
+ it('substitutes a single :param from the row', () => {
47
+ expect(hrefOf(linkCell({ path: '/users/:id' } as never), { id: 42 })).toBe(
48
+ '/users/42'
49
+ );
50
+ });
51
+
52
+ it('substitutes every :param in the path', () => {
53
+ expect(
54
+ hrefOf(linkCell({ path: '/workspaces/:workspaceId/users/:id' } as never), {
55
+ workspaceId: 'ws-1',
56
+ id: 7,
57
+ })
58
+ ).toBe('/workspaces/ws-1/users/7');
59
+ });
60
+
61
+ it('leaves an unmatched :param in place instead of writing undefined', () => {
62
+ expect(hrefOf(linkCell({ path: '/users/:missing' } as never), { id: 1 })).toBe(
63
+ '/users/:missing'
64
+ );
65
+ });
66
+
67
+ it('passes a path with no parameters through untouched', () => {
68
+ expect(hrefOf(linkCell({ path: '/users' } as never), { id: 1 })).toBe('/users');
69
+ });
70
+ });
@@ -64,7 +64,7 @@ export function update<T>(endpoint: string, key = 'id'): OnSubmitFN<T> {
64
64
  export function updateFormData<T>(endpoint: string, key = 'id'): OnSubmitFN<T> {
65
65
  return async (data: T | FormData): Promise<T> => {
66
66
  const axiosInstance = getAxiosInstance();
67
- const id = (data as any)[key];
67
+ const id = (data as Record<string, unknown>)[key];
68
68
  const response = await axiosInstance.put<T>(`/${endpoint}/${id}`, data, {
69
69
  headers: {
70
70
  'Content-Type': 'multipart/form-data',
@@ -89,7 +89,7 @@ export function remove<T>(endpoint: string, key = 'id'): (data: T) => Promise<vo
89
89
  const id = (data as any)[key];
90
90
  await axiosInstance
91
91
  .delete<T>(`/${endpoint}/${id}`)
92
- .then((res: any) => res.data)
92
+ .then(res => res.data)
93
93
  .catch((err: AxiosError) => {
94
94
  const messageError = err.response?.data as { message: string };
95
95
  if (messageError?.message) {
@@ -8,12 +8,32 @@ interface LinkCellProps<T> {
8
8
  configuration: CellConfiguration;
9
9
  }
10
10
 
11
+ /**
12
+ * `@LinkCell({ path: '/users/:id' })` yazan model sahibi, satirin `id`'siyle dolmus
13
+ * bir adres bekliyor — router path'i zaten oyle okunuyor. Ikame edilmezse `:id`
14
+ * harfi harfine adrese giriyor ve hucredeki her open/edit linki 404'e cikiyor:
15
+ * gorunuste calisan, tiklaninca bozuk bir link. Eslesmeyen bir parametre oldugu
16
+ * gibi birakilir (silinmez) — boylece eksik alan gorunur kalir, sessizce
17
+ * `/users/undefined` uretilmez.
18
+ */
19
+ function resolvePath<T>(path: string, item: T): string {
20
+ return path.replace(/:(\w+)/g, (match, key: string) => {
21
+ const value = (item as Record<string, unknown>)[key];
22
+ return value != null ? String(value) : match;
23
+ });
24
+ }
25
+
11
26
  export function LinkCell<T>({ item, configuration }: LinkCellProps<T>) {
12
27
  const linkConfiguration = configuration as LinkCellConfiguration<T>;
13
28
  const value = item[configuration.name as keyof T] ?? 'Link';
14
29
 
15
30
  return (
16
- <Link to={linkConfiguration.path ?? linkConfiguration.url ?? ''}>
31
+ <Link
32
+ to={resolvePath(
33
+ linkConfiguration.path ?? linkConfiguration.url ?? '',
34
+ item
35
+ )}
36
+ >
17
37
  {linkConfiguration.onClick ? (
18
38
  <a
19
39
  className="util-cell-link"