pejay-ui 1.1.0 → 1.2.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/README.md CHANGED
@@ -136,3 +136,8 @@ Below is the list of components you can add. Each has a copyable command block w
136
136
  ```bash
137
137
  npx pejay-ui add tanstack-query-client
138
138
  ```
139
+ * **`react-router-client`**: Bare-bone React Router client layout, routing structure, and route guard setup.
140
+ ```bash
141
+ npx pejay-ui add react-router-client
142
+ ```
143
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pejay-ui",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
5
  "description": "react ui components",
6
6
  "bin": {
@@ -22,7 +22,6 @@
22
22
  "dependencies": {
23
23
  "@babel/core": "^7.24.0",
24
24
  "@babel/preset-typescript": "^7.24.0",
25
- "@tanstack/react-query": "^5.100.14",
26
25
  "commander": "^12.0.0",
27
26
  "fs-extra": "^11.0.0",
28
27
  "inquirer": "^9.0.0"
@@ -39,6 +38,7 @@
39
38
  "homepage": "https://github.com/pejaybro/pejay-ui#readme",
40
39
  "devDependencies": {
41
40
  "@floating-ui/react": "^0.27.19",
41
+ "@tanstack/react-query": "^5.101.0",
42
42
  "@types/react": "^19.2.15",
43
43
  "@types/react-dom": "^19.2.3",
44
44
  "clsx": "^2.1.1",
@@ -46,6 +46,7 @@
46
46
  "lucide-react": "^1.17.0",
47
47
  "react": "^19.2.6",
48
48
  "react-dom": "^19.2.6",
49
+ "react-router-dom": "^7.16.0",
49
50
  "tailwind-merge": "^3.6.0",
50
51
  "tailwindcss": "^4.3.0",
51
52
  "typescript": "^6.0.3"
package/registry.json CHANGED
@@ -194,5 +194,11 @@
194
194
  "category": "tanstack-query",
195
195
  "files": ["templates/scaffolds/tanstack-query"],
196
196
  "peerDependencies": ["@tanstack/react-query"]
197
+ },
198
+ "react-router-client": {
199
+ "name": "ReactRouterClient",
200
+ "category": "react-router",
201
+ "files": ["templates/scaffolds/react-router"],
202
+ "peerDependencies": ["react-router-dom"]
197
203
  }
198
204
  }
@@ -0,0 +1,131 @@
1
+ import { useCallback, useMemo, useRef } from "react";
2
+ import { useSearchParams } from "react-router-dom";
3
+
4
+ export function useRouterSearch<T extends Record<string, any>>({
5
+ defaultParams,
6
+ }: {
7
+ defaultParams: T;
8
+ }) {
9
+ const [searchParams, setSearchParams] = useSearchParams();
10
+
11
+ const defaultRef = useRef(defaultParams);
12
+ defaultRef.current = defaultParams;
13
+
14
+ const search = useMemo(() => {
15
+ const result = { ...defaultRef.current };
16
+ const keys = Object.keys(defaultRef.current);
17
+
18
+ for (const key of keys) {
19
+ const fallback = defaultRef.current[key];
20
+ if (Array.isArray(fallback)) {
21
+ const val = searchParams.getAll(key);
22
+ if (val.length > 0) {
23
+ result[key as keyof T] = parseValue(val, fallback) as any;
24
+ }
25
+ } else {
26
+ const val = searchParams.get(key);
27
+ if (val !== null) {
28
+ result[key as keyof T] = parseValue(val, fallback) as any;
29
+ }
30
+ }
31
+ }
32
+ return result;
33
+ }, [searchParams]);
34
+
35
+ const setSearch = useCallback(
36
+ (
37
+ updates: Partial<Record<keyof T, any>>,
38
+ options?: { resetPage?: boolean; replace?: boolean }
39
+ ) => {
40
+ const next = new URLSearchParams(searchParams);
41
+ const keys = Object.keys(defaultRef.current);
42
+
43
+ for (const key of keys) {
44
+ const value = updates[key as keyof T];
45
+ if (value !== undefined) {
46
+ if (value === null || areEqual(value, defaultRef.current[key])) {
47
+ next.delete(key);
48
+ } else if (Array.isArray(value)) {
49
+ next.delete(key);
50
+ value.forEach((val) => {
51
+ if (val !== undefined && val !== null && val !== "") {
52
+ next.append(key, String(val));
53
+ }
54
+ });
55
+ } else {
56
+ next.set(key, String(value));
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Reset pagination
62
+ * when filters/search changes
63
+ */
64
+ if (options?.resetPage && "page" in defaultRef.current) {
65
+ next.delete("page");
66
+ }
67
+
68
+ setSearchParams(next, {
69
+ replace: options?.replace,
70
+ });
71
+ },
72
+ [searchParams, setSearchParams]
73
+ );
74
+
75
+ return { search, setSearch };
76
+ }
77
+
78
+ function areEqual(a: any, b: any) {
79
+ if (Array.isArray(a) && Array.isArray(b)) {
80
+ if (a.length !== b.length) return false;
81
+ const sortedA = [...a].sort();
82
+ const sortedB = [...b].sort();
83
+ return sortedA.every((val, index) => val === sortedB[index]);
84
+ }
85
+ return a === b;
86
+ }
87
+
88
+ function parseValue(value: string | string[], fallback: any): any {
89
+ if (Array.isArray(fallback)) {
90
+ const fallbackVal = fallback[0];
91
+ const valArray = Array.isArray(value) ? value : [value];
92
+ return valArray.map((item) => parseSingleValue(item, fallbackVal));
93
+ }
94
+ const singleVal = Array.isArray(value) ? value[0] : value;
95
+ return parseSingleValue(singleVal, fallback);
96
+ }
97
+
98
+ function parseSingleValue(value: string, fallback: any) {
99
+ if (typeof fallback === "number") {
100
+ const parsed = Number(value);
101
+ return Number.isNaN(parsed) ? fallback : parsed;
102
+ }
103
+
104
+ /**
105
+ * Boolean parsing
106
+ */
107
+ if (typeof fallback === "boolean") {
108
+ return value === "true";
109
+ }
110
+
111
+ /**
112
+ * String fallback
113
+ */
114
+ return value;
115
+ }
116
+
117
+ /*
118
+ # NOTE: Array parameter usage example
119
+
120
+ 1. Define the default parameter as an array:
121
+ export const DefaultSearch = {
122
+ color: [] as string[],
123
+ };
124
+
125
+ 2. Set values (e.g., in a component):
126
+ setSearch({ color: ["red", "blue"] });
127
+ // Resulting URL query: ?color=red&color=blue
128
+
129
+ 3. Read values (e.g., in a component):
130
+ const colors = search.color; // Returns ["red", "blue"]
131
+ */
@@ -0,0 +1,56 @@
1
+ import { Outlet } from "react-router-dom";
2
+
3
+ export function PrivateRoute() {
4
+ return <Outlet />;
5
+ }
6
+
7
+ /*
8
+ # NOTE : use this when you want to redirect user to login page if he is already logged out.
9
+ # Includes the premium "Redirect Back" pattern to preserve user navigation state.
10
+
11
+ import { Navigate, Outlet, useLocation } from "react-router-dom";
12
+
13
+ export default function PrivateRoute() {
14
+ const token = getLocalAuthToken();
15
+ const location = useLocation();
16
+
17
+ if (!token) {
18
+ // Passes the requested path inside search params so you can redirect back after successful login
19
+ return (
20
+ <Navigate
21
+ to={`/page_auth?redirectTo=${encodeURIComponent(location.pathname + location.search)}`}
22
+ replace
23
+ />
24
+ );
25
+ }
26
+ return <Outlet />;
27
+ }
28
+
29
+ # NOTE: HOW THE LOGIN COMPONENT CONSUMES THE "redirectTo" PARAMETER
30
+
31
+ ### The Bookmarking Lifecycle Flow:
32
+ 1. **Bookmarked Access:** A logged-out user tries to access a bookmarked private route directly:
33
+ `https://your-app.com/page_two`
34
+ 2. **Redirect to Auth:** The `PrivateRoute` guard intercepts the request, captures the target pathname (`/page_two`), and redirects them to the login screen with the target appended:
35
+ `https://your-app.com/page_auth?redirectTo=%2Fpage_two`
36
+ 3. **Login:** The user submits their login credentials.
37
+ 4. **Target Restoration:** Upon successful authentication, the Login page checks the URL for `redirectTo` and navigates the user straight to `/page_two` instead of the generic homepage `/`.
38
+
39
+ ### Code Implementation Example:
40
+ ```typescript
41
+ import { useNavigate, useSearchParams } from "react-router-dom";
42
+
43
+ export default function LoginPage() {
44
+ const navigate = useNavigate();
45
+ const [searchParams] = useSearchParams();
46
+
47
+ const handleLoginSuccess = () => {
48
+ // 1. Read the redirectTo value (e.g., "/page_two") or default to "/"
49
+ const destination = searchParams.get("redirectTo") || "/";
50
+
51
+ // 2. Redirect the user back to their bookmarked page instead of the root page!
52
+ navigate(destination, { replace: true });
53
+ };
54
+ }
55
+ ```
56
+ */
@@ -0,0 +1,21 @@
1
+ import { Outlet } from "react-router-dom";
2
+
3
+ export function PublicRoute() {
4
+ return <Outlet />;
5
+ }
6
+
7
+ /*
8
+
9
+ # NOTE : use this when you want to redirect user to main page if he is already logged in
10
+
11
+ import { Navigate, Outlet } from "react-router-dom";
12
+
13
+ export default function PublicRoute() {
14
+ const token = getLocalAuthToken();
15
+ if (token) {
16
+ return <Navigate to={PATH.desired_path_name} replace />;
17
+ }
18
+ return <Outlet />;
19
+ }
20
+
21
+ */
@@ -0,0 +1,186 @@
1
+ import { createBrowserRouter, RouteObject } from "react-router-dom";
2
+ import { PATH } from "./path";
3
+ import { PublicRoute } from "./guards/public.route";
4
+ import { PrivateRoute } from "./guards/private.route";
5
+ import { AuthLayout } from "./layouts/auth.layout";
6
+ import { ErrorLayout } from "./layouts/error.layout";
7
+ import { MainLayout } from "./layouts/main.layout";
8
+
9
+ const PublicRoutes: RouteObject[] = [
10
+ {
11
+ Component: PublicRoute,
12
+ children: [
13
+ {
14
+ Component: AuthLayout,
15
+ ErrorBoundary: ErrorLayout,
16
+ children: [
17
+ {
18
+ path: PATH.page_auth(),
19
+ lazy: async () => {
20
+ const module = await import("../routes/page-auth");
21
+ return { Component: module.default };
22
+ },
23
+ },
24
+ ],
25
+ },
26
+ ],
27
+ },
28
+ ];
29
+
30
+ const PrivateRoutes: RouteObject[] = [
31
+ {
32
+ Component: PrivateRoute,
33
+ children: [
34
+ {
35
+ Component: MainLayout,
36
+ ErrorBoundary: ErrorLayout,
37
+ children: [
38
+ {
39
+ path: PATH.page_root(),
40
+ ErrorBoundary: ErrorLayout,
41
+ lazy: async () => {
42
+ const module = await import("../routes/page-root");
43
+ return { Component: module.default };
44
+ },
45
+ },
46
+ {
47
+ path: PATH.page_one(),
48
+ ErrorBoundary: ErrorLayout,
49
+ lazy: async () => {
50
+ const module = await import("../routes/page-one");
51
+ return { Component: module.default };
52
+ },
53
+ },
54
+ {
55
+ path: PATH.page_two(),
56
+ ErrorBoundary: ErrorLayout,
57
+ lazy: async () => {
58
+ const module = await import("../routes/page-two");
59
+ return { Component: module.default };
60
+ },
61
+ },
62
+ ],
63
+ },
64
+ ],
65
+ },
66
+ ];
67
+
68
+ const NotFoundRoutes: RouteObject[] = [
69
+ {
70
+ path: PATH.not_found(), // Wildcard catch-all must be at the end of the root array
71
+ lazy: async () => {
72
+ const module = await import("../routes/not-found");
73
+ return { Component: module.default };
74
+ },
75
+ },
76
+ ];
77
+
78
+ export const router = createBrowserRouter([
79
+ ...PublicRoutes,
80
+ ...PrivateRoutes,
81
+ ...NotFoundRoutes,
82
+ ]);
83
+
84
+ /*
85
+
86
+ currently i am using new way of using react-router version like
87
+ Component: PrivateLayout,
88
+ lazy: async () => ...
89
+
90
+ the old way is
91
+
92
+ {
93
+ path: "/",
94
+ element: (
95
+ <PublicRoute>
96
+ <AuthLayout />
97
+ </PublicRoute>
98
+ ),
99
+ children: [
100
+ {
101
+ path: PATHS.LOGIN,
102
+ element: <Login />,
103
+ },
104
+ ],
105
+ }
106
+
107
+
108
+
109
+ | Feature | `errorElement` | `ErrorBoundary` |
110
+ | -------------------- | --------------------------------- | -------------------------- |
111
+ | What you pass | React Element | Component Function |
112
+ | Syntax | `errorElement: <ErrorPage />` | `ErrorBoundary: ErrorPage` |
113
+ | Requires JSX | Yes | No |
114
+ | Works in `.ts` file | No (unless `React.createElement`) | Yes |
115
+ | React Router version | v6.4+ | Newer preferred API |
116
+
117
+
118
+ ---------------------------------------------------------------------------
119
+
120
+ # ANCHOR : 2 ways to show the error boundry page data
121
+
122
+
123
+ # NOTE : WAY-1 inside layout i.e inplace of outlet
124
+ {
125
+ path: PATH.page_two(),
126
+ ErrorBoundary: ErrorLayout,
127
+ lazy: async () => {
128
+ const module = await import("../routes/page-two");
129
+ return { Component: module.default };
130
+ },
131
+ },
132
+
133
+ ---------------------------------------------------------------------------
134
+
135
+ # NOTE : WAY-2 replace entire layout with error boundary
136
+ {
137
+ Component: AuthLayout,
138
+ ErrorBoundary: ErrorLayout,
139
+ children: [
140
+ {
141
+ path: PATH.page_auth(),
142
+ lazy: async () => {
143
+ const module = await import("../routes/page-auth");
144
+ return { Component: module.default };
145
+ },
146
+ },
147
+ ],
148
+ },
149
+
150
+ ---------------------------------------------------------------------------
151
+
152
+ # NOTE : DYNAMIC PAGE TITLES (SEO BEST PRACTICE)
153
+ To set browser tab titles dynamically on page transitions, create a custom hook and call it inside your page route components:
154
+
155
+ ```typescript
156
+ import { useEffect } from "react";
157
+
158
+ export function useDocumentTitle(title: string) {
159
+ useEffect(() => {
160
+ const original = document.title;
161
+ document.title = `${title} | My App`;
162
+ return () => {
163
+ document.title = original;
164
+ };
165
+ }, [title]);
166
+ }
167
+
168
+ // In routes/page-one.tsx:
169
+ // useDocumentTitle("Analytics Page");
170
+ ```
171
+
172
+ ---------------------------------------------------------------------------
173
+
174
+ # NOTE : ROUTE-LEVEL SUSPENSE VS. COMPONENT-LEVEL SKELETONS
175
+
176
+ 1. **Why we place `<Suspense>` at the very top (wrapping `<RouterProvider>`):**
177
+ - **Code Chunk Loading:** Because we lazy-load routes (`lazy: async () => ...`), React Router has to download the page's `.js` chunk file from the server when navigating.
178
+ - **Preventing UI Freezes:** If you click a link and do not have a top-level Suspense boundary, the app will freeze or flicker on the old page until the file download finishes.
179
+ - **Best Practice:** Wrap the router in `<Suspense fallback={<TopBarProgressBar />} />` so the user gets instant progress feedback when clicking links.
180
+
181
+ 2. **Why we use Component-level skeletons:**
182
+ - **Data Loading:** Once the page code is downloaded and mounted, your API data fetching starts.
183
+ - **Interactive UI:** This is where you render custom skeleton loaders inside your page components while waiting for TanStack Query data, offering a premium and localized loading layout.
184
+ */
185
+
186
+
@@ -0,0 +1,5 @@
1
+ import { Outlet } from "react-router-dom";
2
+
3
+ export function AuthLayout() {
4
+ return <Outlet />;
5
+ }
@@ -0,0 +1,15 @@
1
+ import { Outlet, useRouteError } from "react-router-dom";
2
+
3
+ export function ErrorLayout() {
4
+ const error = useRouteError();
5
+ return (
6
+ <div>
7
+ <h1>Something went wrong!</h1>
8
+ <p>
9
+ {error instanceof Error
10
+ ? error.message
11
+ : "An unexpected error occurred"}
12
+ </p>
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,5 @@
1
+ import { Outlet } from "react-router-dom";
2
+
3
+ export function MainLayout() {
4
+ return <Outlet />;
5
+ }
@@ -0,0 +1,7 @@
1
+ export const PATH = {
2
+ page_root: () => "/" as const,
3
+ page_one: () => "/page_one" as const,
4
+ page_two: () => "/page_two" as const,
5
+ page_auth: () => "/page_auth" as const,
6
+ not_found: () => "*" as const,
7
+ };
@@ -0,0 +1,3 @@
1
+ export default function NotFound() {
2
+ return <div>Not Found</div>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function PageAuth() {
2
+ return <div>Page Auth</div>;
3
+ }
@@ -0,0 +1,85 @@
1
+ export default function PageOne() {
2
+ return <div>Page One</div>;
3
+ }
4
+
5
+ /*
6
+ # NOTE: FRAMEWORK-LEVEL META & PRERENDERING CONFIGURATION (SEO)
7
+
8
+ If you are using React Router v7 and want static HTML files compiled for SEO search crawlers, follow these steps:
9
+
10
+ -------------------------------------------------------------------------------------
11
+
12
+ 1. ROUTE META EXPORT (Add this inside this page file):
13
+ ```typescript
14
+ import type { MetaFunction } from "react-router-dom";
15
+
16
+ export const meta: MetaFunction = () => {
17
+ return [
18
+ { title: "Page One Analytics | My App" },
19
+ { name: "description", content: "Analyze product sales and user logs." },
20
+ ];
21
+ };
22
+ ```
23
+
24
+ -------------------------------------------------------------------------------------
25
+
26
+ 2. PRERENDER SETTINGS (Add this in your react-router.config.ts / vite.config.ts):
27
+ ```typescript
28
+ export default {
29
+ async prerender() {
30
+ return ["/", "/page_one", "/page_two"]; // Routes to generate as static HTML pages
31
+ },
32
+ };
33
+ ```
34
+
35
+ -------------------------------------------------------------------------------------
36
+
37
+ 3. HOW THE BUILD CHANGES (Build output comparison):
38
+
39
+ OLD BUILD (Standard Single Page Application):
40
+ - Outputs only a single empty "index.html" page.
41
+ - Crawlers see no initial page HTML or titles when visiting "/page_one" until JS loads.
42
+
43
+ NEW BUILD (Prerendered SPA):
44
+ - Outputs directory folders containing index.html files:
45
+ dist/
46
+ ├── index.html <- for "/"
47
+ ├── page_one/
48
+ │ └── index.html <- for "/page_one" (contains pre-rendered metadata)
49
+ └── page_two/
50
+ └── index.html <- for "/page_two" (contains pre-rendered metadata)
51
+
52
+ -------------------------------------------------------------------------------------
53
+
54
+ 4. SERVER SETTINGS (VPS Deployment):
55
+
56
+ For a standard VPS server (like Nginx), configure it to serve static files from your build directory.
57
+ Nginx will look for the folder-based index files (e.g., /page_one/index.html) first.
58
+
59
+ NGINX VPS CONFIGURATION EXAMPLE (/etc/nginx/sites-available/default):
60
+ ```nginx
61
+ server {
62
+ listen 80;
63
+ server_name your-app.com;
64
+
65
+ # Point to your build distribution directory on the VPS
66
+ root /var/www/your-app/dist;
67
+
68
+ index index.html;
69
+
70
+ location / {
71
+ # 1. Checks if exact file exists ($uri)
72
+ # 2. Checks if a directory index.html exists ($uri/) -> Serves pre-rendered SEO pages!
73
+ # 3. Falls back to root index.html if route is handled client-side
74
+ try_files $uri $uri/ /index.html;
75
+ }
76
+
77
+ # Optional: Proxy API requests directly to your Laravel Backend
78
+ location /api/ {
79
+ proxy_pass http://127.0.0.1:8000; # Address where your Laravel backend runs
80
+ proxy_set_header Host $host;
81
+ proxy_set_header X-Real-IP $remote_addr;
82
+ }
83
+ }
84
+ ```
85
+ */
@@ -0,0 +1,5 @@
1
+ export const PageOneDefaultSearchParam = {
2
+ param_one: "",
3
+ param_two: "",
4
+ param_three: "",
5
+ };
@@ -0,0 +1,3 @@
1
+ export default function PageRoot() {
2
+ return <div>Page Root</div>;
3
+ }
@@ -0,0 +1,3 @@
1
+ export default function PageTwo() {
2
+ return <div>Page Two</div>;
3
+ }
@@ -0,0 +1,5 @@
1
+ export const PageTwoDefaultSearchParam = {
2
+ param_one: "",
3
+ param_two: "",
4
+ param_three: "",
5
+ };
@@ -58,9 +58,28 @@ return <ModuleComponent data={data} />;
58
58
  }
59
59
 
60
60
  # NOTE : Wrapping
61
+ <ErrorBoundary fallback={<div>Failed to load Stats</div>}>
61
62
  <Suspense fallback={<FallBackComponent />}>
62
63
  <ModuleComponent />
63
64
  </Suspense>
65
+ </ErrorBoundary>
66
+
67
+ this is comp level error boundary in case of api fails then suspense bubbeling up will touch this first and stops
68
+ in case this is not present then the router level
69
+ so in this way we can only show a particaular comp failed to load not entore page
70
+
71
+
72
+
73
+
74
+
75
+ Manually (Option A - useQuery):
76
+
77
+ Code: if (error) return <ErrorComponent />
78
+ Behavior: Bypasses Error Boundaries. Component-level and router-level boundaries never trigger.
79
+ Automatically (Option B - useSuspenseQuery or useQuery with throwOnError: true):
80
+
81
+ Code: Component throws the error during render.
82
+ Behavior: The error bubbles up. It triggers the nearest Error Boundary (either your component-level boundary if you wrapped it, or falls back to the router-level boundary).
64
83
 
65
84
  ---------------------------------------------------------------------------------------------------
66
85
 
@@ -1,4 +1,8 @@
1
- import { queryOptions, infiniteQueryOptions, keepPreviousData } from "@tanstack/react-query";
1
+ import {
2
+ queryOptions,
3
+ infiniteQueryOptions,
4
+ keepPreviousData,
5
+ } from "@tanstack/react-query";
2
6
 
3
7
  import { ModuleKeys } from "./keys";
4
8
  import { ModuleMappers } from "./mappers";
@@ -15,7 +19,7 @@ export const ModuleQueries = {
15
19
  */
16
20
  return ModuleService.get_query_name_example();
17
21
  },
18
- select: (raw) => {
22
+ select: raw => {
19
23
  /*
20
24
  # NOTE: ModuleMappers.fetch_query_name_example() -> manipulates the data from api into desird format before returning to ui or page
21
25
  Using the 'select' property memoizes this transformation (only runs when cached data changes).
@@ -50,7 +54,7 @@ export const ModuleQueries = {
50
54
  return hasMore ? current_page + 1 : undefined;
51
55
  },
52
56
  initialPageParam: 1,
53
- select: (raw) => {
57
+ select: raw => {
54
58
  /*
55
59
  # NOTE: ModuleMappers.fetch_infinite_query_example() -> manipulates the data from api into desird format before returning to ui or page
56
60
  Using the 'select' property memoizes this transformation (only runs when cached data changes).
@@ -76,7 +80,7 @@ export const ModuleQueries = {
76
80
  */
77
81
  return ModuleService.get_query_with_params_example(params, signal);
78
82
  },
79
- select: (raw) => {
83
+ select: raw => {
80
84
  // Reusing the same mapper example for consistency
81
85
  return ModuleMappers.fetch_query_name_example(raw as any);
82
86
  },
@@ -89,7 +93,7 @@ export const ModuleQueries = {
89
93
  queryFn: ({ signal }) => {
90
94
  return ModuleService.get_query_by_id_example(id!, signal);
91
95
  },
92
- select: (raw) => {
96
+ select: raw => {
93
97
  return ModuleMappers.fetch_query_name_example(raw as any);
94
98
  },
95
99
  placeholderData: keepPreviousData,
@@ -100,13 +104,16 @@ export const ModuleQueries = {
100
104
  enabled: !!id,
101
105
  }),
102
106
 
103
- fetch_query_combo_example: (id: string | null | undefined, params: Record<string, any>) =>
107
+ fetch_query_combo_example: (
108
+ id: string | null | undefined,
109
+ params: Record<string, any>,
110
+ ) =>
104
111
  queryOptions({
105
112
  queryKey: ModuleKeys.fetch_query_combo_example(id || "", params),
106
113
  queryFn: ({ signal }) => {
107
114
  return ModuleService.get_query_combo_example(id!, params, signal);
108
115
  },
109
- select: (raw) => {
116
+ select: raw => {
110
117
  return ModuleMappers.fetch_query_name_example(raw as any);
111
118
  },
112
119
  placeholderData: keepPreviousData,
@@ -129,5 +136,21 @@ export const ModuleQueries = {
129
136
  - The `select` function is **automatically memoized** by TanStack Query.
130
137
  - It will ONLY re-run when the cached raw data changes.
131
138
  - If the component re-renders for other reasons (e.g., local UI states, parent re-renders, or window focus checks), TanStack Query skips the mapper execution completely and returns the already memoized mapped data instantly.
132
- */
133
139
 
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+
148
+
149
+
150
+ use this inside queries if you want to show error boundary at component level for perticular query when not using useSuspenseQuery or hook which provides suspense by default. by default it just catches the errror and shows the error in useMutation or useQuery hooks but in case of useSuspenseQuery it throws the error and bubbels up to the nearest error boundary.
151
+
152
+ throwOnError: (error) => {
153
+ // Throw to boundary for severe errors (like 404 Not Found or 500 Server Crashes)
154
+ return error.status === 404 || error.status >= 500;
155
+ }
156
+ */