@lasterp/shared 1.0.0-alpha.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/index.cjs +387 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +298 -0
- package/dist/index.d.ts +298 -0
- package/dist/index.js +339 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -0
- package/src/client/context.tsx +74 -0
- package/src/client/core.ts +261 -0
- package/src/client/hooks.tsx +57 -0
- package/src/client/index.ts +21 -0
- package/src/client/storage.ts +76 -0
- package/src/client/types.ts +24 -0
- package/src/common/index.ts +1 -0
- package/src/common/types.ts +15 -0
- package/src/design/block/types.ts +8 -0
- package/src/design/globals/footer.ts +20 -0
- package/src/design/globals/header.ts +43 -0
- package/src/design/globals/types.ts +7 -0
- package/src/design/index.ts +8 -0
- package/src/design/page/api.ts +11 -0
- package/src/design/page/types.ts +15 -0
- package/src/index.ts +5 -0
- package/src/lasterp/catalog/types.ts +24 -0
- package/src/lasterp/index.ts +5 -0
- package/src/lasterp/shop/api.ts +32 -0
- package/src/lasterp/shop/hooks.tsx +42 -0
- package/src/lasterp/shop/types.ts +42 -0
- package/src/utils/catalog.ts +8 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/types.ts +3 -0
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { camelizeKeys, decamelizeKeys } from 'humps';
|
|
2
|
+
import type { FrappeResponse, FrappeError, AuthOptions } from './types';
|
|
3
|
+
|
|
4
|
+
async function request<T = any>(
|
|
5
|
+
baseUrl: string,
|
|
6
|
+
endpoint: string,
|
|
7
|
+
options: RequestInit = {},
|
|
8
|
+
auth?: AuthOptions
|
|
9
|
+
): Promise<T> {
|
|
10
|
+
const url = `${baseUrl}${endpoint}`;
|
|
11
|
+
|
|
12
|
+
const headers: Record<string, string> = {
|
|
13
|
+
'Content-Type': 'application/json',
|
|
14
|
+
Accept: 'application/json',
|
|
15
|
+
...(options.headers as Record<string, string>),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
if (auth?.token) {
|
|
19
|
+
headers['Authorization'] = `Bearer ${auth.token}`;
|
|
20
|
+
} else if (auth?.apiKey) {
|
|
21
|
+
headers['Authorization'] = `token ${auth.apiKey.key}:${auth.apiKey.secret}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const response = await fetch(url, {
|
|
25
|
+
...options,
|
|
26
|
+
headers,
|
|
27
|
+
credentials: 'include',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const rawData: FrappeResponse<any> = await response.json();
|
|
31
|
+
|
|
32
|
+
if (!response.ok || rawData.exc) {
|
|
33
|
+
// Create a proper Error object for better serialization in Next.js
|
|
34
|
+
const errorMessage = rawData._error_message || rawData.exc || 'Request failed';
|
|
35
|
+
const error = new Error(errorMessage) as Error & FrappeError;
|
|
36
|
+
|
|
37
|
+
error.message = errorMessage;
|
|
38
|
+
error.statusCode = response.status;
|
|
39
|
+
|
|
40
|
+
if (rawData.exc) {
|
|
41
|
+
error.exc = rawData.exc;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (rawData._server_messages) {
|
|
45
|
+
error.serverMessages = rawData._server_messages;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (rawData.exc_type) {
|
|
49
|
+
error.excType = rawData.exc_type;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
throw error;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const result = rawData.message ?? rawData;
|
|
56
|
+
return camelizeKeys(result) as T;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export async function frappeCall<T = any>(
|
|
60
|
+
baseUrl: string,
|
|
61
|
+
method: string,
|
|
62
|
+
args?: Record<string, any>,
|
|
63
|
+
auth?: AuthOptions
|
|
64
|
+
): Promise<T> {
|
|
65
|
+
const snakeArgs = decamelizeKeys(args ?? {});
|
|
66
|
+
|
|
67
|
+
return request<T>(
|
|
68
|
+
baseUrl,
|
|
69
|
+
`/api/method/${method}`,
|
|
70
|
+
{
|
|
71
|
+
method: 'POST',
|
|
72
|
+
body: JSON.stringify(snakeArgs),
|
|
73
|
+
},
|
|
74
|
+
auth
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function frappeCallGet<T = any>(
|
|
79
|
+
baseUrl: string,
|
|
80
|
+
method: string,
|
|
81
|
+
args?: Record<string, any>,
|
|
82
|
+
auth?: AuthOptions
|
|
83
|
+
): Promise<T> {
|
|
84
|
+
const snakeArgs = decamelizeKeys(args ?? {});
|
|
85
|
+
const queryParams = new URLSearchParams();
|
|
86
|
+
|
|
87
|
+
Object.entries(snakeArgs).forEach(([key, value]) => {
|
|
88
|
+
queryParams.append(
|
|
89
|
+
key,
|
|
90
|
+
typeof value === 'string' ? value : JSON.stringify(value)
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const endpoint = `/api/method/${method}?${queryParams.toString()}`;
|
|
95
|
+
return request<T>(baseUrl, endpoint, { method: 'GET' }, auth);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function getList<T = any>(
|
|
99
|
+
baseUrl: string,
|
|
100
|
+
params: {
|
|
101
|
+
doctype: string;
|
|
102
|
+
fields?: string[];
|
|
103
|
+
filters?: Record<string, any>;
|
|
104
|
+
orderBy?: string;
|
|
105
|
+
limitStart?: number;
|
|
106
|
+
limitPageLength?: number;
|
|
107
|
+
},
|
|
108
|
+
auth?: AuthOptions
|
|
109
|
+
): Promise<T[]> {
|
|
110
|
+
const {
|
|
111
|
+
doctype,
|
|
112
|
+
fields = ['name'],
|
|
113
|
+
filters = {},
|
|
114
|
+
orderBy = 'modified desc',
|
|
115
|
+
limitStart = 0,
|
|
116
|
+
limitPageLength = 20,
|
|
117
|
+
} = params;
|
|
118
|
+
|
|
119
|
+
const snakeFilters = decamelizeKeys(filters);
|
|
120
|
+
|
|
121
|
+
const queryParams = new URLSearchParams({
|
|
122
|
+
fields: JSON.stringify(fields),
|
|
123
|
+
filters: JSON.stringify(snakeFilters),
|
|
124
|
+
order_by: orderBy,
|
|
125
|
+
limit_start: limitStart.toString(),
|
|
126
|
+
limit_page_length: limitPageLength.toString(),
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const endpoint = `/api/resource/${doctype}?${queryParams.toString()}`;
|
|
130
|
+
const response = await request<{ data: T[] }>(baseUrl, endpoint, {}, auth);
|
|
131
|
+
return response.data || [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export async function getDoc<T = any>(
|
|
135
|
+
baseUrl: string,
|
|
136
|
+
doctype: string,
|
|
137
|
+
name: string,
|
|
138
|
+
auth?: AuthOptions
|
|
139
|
+
): Promise<T> {
|
|
140
|
+
const endpoint = `/api/resource/${doctype}/${encodeURIComponent(name)}`;
|
|
141
|
+
const response = await request<{ data: T }>(baseUrl, endpoint, {}, auth);
|
|
142
|
+
return response.data;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export async function createDoc<T = any>(
|
|
146
|
+
baseUrl: string,
|
|
147
|
+
doctype: string,
|
|
148
|
+
doc: Record<string, any>,
|
|
149
|
+
auth?: AuthOptions
|
|
150
|
+
): Promise<T> {
|
|
151
|
+
const snakeDoc = decamelizeKeys(doc);
|
|
152
|
+
const endpoint = `/api/resource/${doctype}`;
|
|
153
|
+
const response = await request<{ data: T }>(
|
|
154
|
+
baseUrl,
|
|
155
|
+
endpoint,
|
|
156
|
+
{
|
|
157
|
+
method: 'POST',
|
|
158
|
+
body: JSON.stringify(snakeDoc),
|
|
159
|
+
},
|
|
160
|
+
auth
|
|
161
|
+
);
|
|
162
|
+
return response.data;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function updateDoc<T = any>(
|
|
166
|
+
baseUrl: string,
|
|
167
|
+
doctype: string,
|
|
168
|
+
name: string,
|
|
169
|
+
doc: Record<string, any>,
|
|
170
|
+
auth?: AuthOptions
|
|
171
|
+
): Promise<T> {
|
|
172
|
+
const snakeDoc = decamelizeKeys(doc);
|
|
173
|
+
const endpoint = `/api/resource/${doctype}/${encodeURIComponent(name)}`;
|
|
174
|
+
const response = await request<{ data: T }>(
|
|
175
|
+
baseUrl,
|
|
176
|
+
endpoint,
|
|
177
|
+
{
|
|
178
|
+
method: 'PUT',
|
|
179
|
+
body: JSON.stringify(snakeDoc),
|
|
180
|
+
},
|
|
181
|
+
auth
|
|
182
|
+
);
|
|
183
|
+
return response.data;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export async function deleteDoc(
|
|
187
|
+
baseUrl: string,
|
|
188
|
+
doctype: string,
|
|
189
|
+
name: string,
|
|
190
|
+
auth?: AuthOptions
|
|
191
|
+
): Promise<void> {
|
|
192
|
+
const endpoint = `/api/resource/${doctype}/${encodeURIComponent(name)}`;
|
|
193
|
+
await request(
|
|
194
|
+
baseUrl,
|
|
195
|
+
endpoint,
|
|
196
|
+
{
|
|
197
|
+
method: 'DELETE',
|
|
198
|
+
},
|
|
199
|
+
auth
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export async function getValue<T = any>(
|
|
204
|
+
baseUrl: string,
|
|
205
|
+
doctype: string,
|
|
206
|
+
name: string,
|
|
207
|
+
fieldname: string,
|
|
208
|
+
auth?: AuthOptions
|
|
209
|
+
): Promise<T> {
|
|
210
|
+
const endpoint = `/api/resource/${doctype}/${encodeURIComponent(name)}?fields=["${fieldname}"]`;
|
|
211
|
+
const response = await request<{ data: Record<string, T> }>(
|
|
212
|
+
baseUrl,
|
|
213
|
+
endpoint,
|
|
214
|
+
{},
|
|
215
|
+
auth
|
|
216
|
+
);
|
|
217
|
+
const value = response.data[fieldname];
|
|
218
|
+
|
|
219
|
+
if (value === undefined) {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Field "${fieldname}" not found in document "${doctype}/${name}"`
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return value;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
export async function setValue<T = any>(
|
|
229
|
+
baseUrl: string,
|
|
230
|
+
doctype: string,
|
|
231
|
+
name: string,
|
|
232
|
+
fieldname: string,
|
|
233
|
+
value: any,
|
|
234
|
+
auth?: AuthOptions
|
|
235
|
+
): Promise<T> {
|
|
236
|
+
return updateDoc<T>(baseUrl, doctype, name, { [fieldname]: value }, auth);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export async function getCount(
|
|
240
|
+
baseUrl: string,
|
|
241
|
+
doctype: string,
|
|
242
|
+
filters: Record<string, any> = {},
|
|
243
|
+
auth?: AuthOptions
|
|
244
|
+
): Promise<number> {
|
|
245
|
+
return frappeCall<number>(
|
|
246
|
+
baseUrl,
|
|
247
|
+
'frappe.client.get_count',
|
|
248
|
+
{
|
|
249
|
+
doctype,
|
|
250
|
+
filters,
|
|
251
|
+
},
|
|
252
|
+
auth
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export async function getCurrentUser(
|
|
257
|
+
baseUrl: string,
|
|
258
|
+
auth?: AuthOptions
|
|
259
|
+
): Promise<any> {
|
|
260
|
+
return frappeCallGet(baseUrl, 'frappe.auth.get_logged_user', {}, auth);
|
|
261
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useFrappeConfig } from './context';
|
|
4
|
+
import {
|
|
5
|
+
frappeCall as frappeCallCore,
|
|
6
|
+
frappeCallGet as frappeCallGetCore,
|
|
7
|
+
getDoc as getDocCore,
|
|
8
|
+
getList as getListCore,
|
|
9
|
+
createDoc as createDocCore,
|
|
10
|
+
updateDoc as updateDocCore,
|
|
11
|
+
deleteDoc as deleteDocCore,
|
|
12
|
+
} from './core';
|
|
13
|
+
import type { AuthOptions } from './types';
|
|
14
|
+
|
|
15
|
+
export function useFrappe() {
|
|
16
|
+
const { baseUrl, token, apiKey } = useFrappeConfig();
|
|
17
|
+
|
|
18
|
+
const auth: AuthOptions = {
|
|
19
|
+
...(token && { token }),
|
|
20
|
+
...(apiKey && { apiKey }),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
call: <T = any>(method: string, args?: Record<string, any>) =>
|
|
25
|
+
frappeCallCore<T>(baseUrl, method, args, auth),
|
|
26
|
+
|
|
27
|
+
callGet: <T = any>(method: string, args?: Record<string, any>) =>
|
|
28
|
+
frappeCallGetCore<T>(baseUrl, method, args, auth),
|
|
29
|
+
|
|
30
|
+
getList: <T = any>(params: {
|
|
31
|
+
doctype: string;
|
|
32
|
+
fields?: string[];
|
|
33
|
+
filters?: Record<string, any>;
|
|
34
|
+
orderBy?: string;
|
|
35
|
+
limitStart?: number;
|
|
36
|
+
limitPageLength?: number;
|
|
37
|
+
}) => getListCore<T>(baseUrl, params, auth),
|
|
38
|
+
|
|
39
|
+
getDoc: <T = any>(doctype: string, name: string) =>
|
|
40
|
+
getDocCore<T>(baseUrl, doctype, name, auth),
|
|
41
|
+
|
|
42
|
+
createDoc: <T = any>(doctype: string, doc: Record<string, any>) =>
|
|
43
|
+
createDocCore<T>(baseUrl, doctype, doc, auth),
|
|
44
|
+
|
|
45
|
+
updateDoc: <T = any>(
|
|
46
|
+
doctype: string,
|
|
47
|
+
name: string,
|
|
48
|
+
doc: Record<string, any>
|
|
49
|
+
) => updateDocCore<T>(baseUrl, doctype, name, doc, auth),
|
|
50
|
+
|
|
51
|
+
deleteDoc: (doctype: string, name: string) =>
|
|
52
|
+
deleteDocCore(baseUrl, doctype, name, auth),
|
|
53
|
+
|
|
54
|
+
baseUrl,
|
|
55
|
+
auth,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export {
|
|
2
|
+
frappeCall,
|
|
3
|
+
frappeCallGet,
|
|
4
|
+
getList,
|
|
5
|
+
getDoc,
|
|
6
|
+
createDoc,
|
|
7
|
+
updateDoc,
|
|
8
|
+
deleteDoc,
|
|
9
|
+
getValue,
|
|
10
|
+
setValue,
|
|
11
|
+
getCount,
|
|
12
|
+
getCurrentUser,
|
|
13
|
+
} from './core';
|
|
14
|
+
|
|
15
|
+
export type { FrappeResponse, FrappeError, AuthOptions } from './types';
|
|
16
|
+
|
|
17
|
+
export { FrappeProvider, useFrappeConfig } from './context';
|
|
18
|
+
export type { FrappeContextValue, FrappeProviderProps } from './context';
|
|
19
|
+
export { useFrappe } from './hooks';
|
|
20
|
+
|
|
21
|
+
export * from './storage';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export interface StorageAdapter {
|
|
2
|
+
getItem(key: string): string | null | Promise<string | null>;
|
|
3
|
+
setItem(key: string, value: string): void | Promise<void>;
|
|
4
|
+
removeItem(key: string): void | Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const webStorageAdapter: StorageAdapter = {
|
|
8
|
+
getItem: (key: string) => {
|
|
9
|
+
if (typeof window === 'undefined') return null;
|
|
10
|
+
return localStorage.getItem(key);
|
|
11
|
+
},
|
|
12
|
+
setItem: (key: string, value: string) => {
|
|
13
|
+
if (typeof window === 'undefined') return;
|
|
14
|
+
localStorage.setItem(key, value);
|
|
15
|
+
},
|
|
16
|
+
removeItem: (key: string) => {
|
|
17
|
+
if (typeof window === 'undefined') return;
|
|
18
|
+
localStorage.removeItem(key);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let currentAdapter: StorageAdapter = webStorageAdapter;
|
|
23
|
+
|
|
24
|
+
export function setStorageAdapter(adapter: StorageAdapter) {
|
|
25
|
+
currentAdapter = adapter;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getStorageAdapter(): StorageAdapter {
|
|
29
|
+
return currentAdapter;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const TOKEN_KEY = 'frappe_token';
|
|
33
|
+
const API_KEY_KEY = 'frappe_api_key';
|
|
34
|
+
const API_SECRET_KEY = 'frappe_api_secret';
|
|
35
|
+
|
|
36
|
+
export function getToken(): string | null | Promise<string | null> {
|
|
37
|
+
return currentAdapter.getItem(TOKEN_KEY);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function setToken(token: string): void | Promise<void> {
|
|
41
|
+
return currentAdapter.setItem(TOKEN_KEY, token);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function clearToken(): void | Promise<void> {
|
|
45
|
+
return currentAdapter.removeItem(TOKEN_KEY);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function getApiKey(): Promise<{ key: string; secret: string } | null> {
|
|
49
|
+
const key = await currentAdapter.getItem(API_KEY_KEY);
|
|
50
|
+
const secret = await currentAdapter.getItem(API_SECRET_KEY);
|
|
51
|
+
|
|
52
|
+
if (!key || !secret) return null;
|
|
53
|
+
|
|
54
|
+
return { key, secret };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function setApiKey(key: string, secret: string): Promise<void> {
|
|
58
|
+
await currentAdapter.setItem(API_KEY_KEY, key);
|
|
59
|
+
await currentAdapter.setItem(API_SECRET_KEY, secret);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function clearApiKey(): Promise<void> {
|
|
63
|
+
await currentAdapter.removeItem(API_KEY_KEY);
|
|
64
|
+
await currentAdapter.removeItem(API_SECRET_KEY);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export async function clearAuth(): Promise<void> {
|
|
68
|
+
await clearToken();
|
|
69
|
+
await clearApiKey();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function isAuthenticated(): Promise<boolean> {
|
|
73
|
+
const token = await getToken();
|
|
74
|
+
const apiKey = await getApiKey();
|
|
75
|
+
return !!token || !!apiKey;
|
|
76
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface FrappeResponse<T = any> {
|
|
2
|
+
message?: T;
|
|
3
|
+
docs?: T[];
|
|
4
|
+
exc?: string;
|
|
5
|
+
exc_type?: string;
|
|
6
|
+
_server_messages?: string;
|
|
7
|
+
_error_message?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface FrappeError {
|
|
11
|
+
message: string;
|
|
12
|
+
statusCode?: number;
|
|
13
|
+
exc?: string;
|
|
14
|
+
excType?: string;
|
|
15
|
+
serverMessages?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AuthOptions {
|
|
19
|
+
token?: string;
|
|
20
|
+
apiKey?: {
|
|
21
|
+
key: string;
|
|
22
|
+
secret: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type { FrappeDoc, FrappeChildDoc } from './types';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface FrappeDoc {
|
|
2
|
+
name: string;
|
|
3
|
+
creation?: string;
|
|
4
|
+
modified?: string;
|
|
5
|
+
modifiedBy?: string;
|
|
6
|
+
owner?: string;
|
|
7
|
+
docStatus?: number;
|
|
8
|
+
idx?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FrappeChildDoc extends FrappeDoc {
|
|
12
|
+
parent: string;
|
|
13
|
+
parentType: string;
|
|
14
|
+
parentField: string;
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
|
|
2
|
+
export interface FooterItemGroup {
|
|
3
|
+
title: string;
|
|
4
|
+
items: FooterItem[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface FooterItem {
|
|
8
|
+
label: string;
|
|
9
|
+
link?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface Footer {
|
|
13
|
+
footerType?: string;
|
|
14
|
+
groups: FooterItemGroup[];
|
|
15
|
+
copyright?: string;
|
|
16
|
+
address?: string;
|
|
17
|
+
country?: string;
|
|
18
|
+
phone?: string;
|
|
19
|
+
email?: string;
|
|
20
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface Brand {
|
|
2
|
+
brandImage?: string;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export interface NavbarItem {
|
|
6
|
+
label: string;
|
|
7
|
+
enableDropdown: boolean;
|
|
8
|
+
enableLink: boolean;
|
|
9
|
+
link?: string;
|
|
10
|
+
dropdownDescription?: string;
|
|
11
|
+
dropdownCta?: string;
|
|
12
|
+
groups?: NavbarSubItemGroup[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface NavbarSubItemGroup {
|
|
16
|
+
title?: string;
|
|
17
|
+
items: NavbarSubItem[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface NavbarSubItem {
|
|
21
|
+
label: string;
|
|
22
|
+
description?: string;
|
|
23
|
+
image?: string;
|
|
24
|
+
link?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface Topbar {
|
|
28
|
+
topbarEnabled?: boolean;
|
|
29
|
+
items: TopbarItem[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface TopbarItem {
|
|
33
|
+
icon?: string;
|
|
34
|
+
label: string;
|
|
35
|
+
link?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface Header {
|
|
39
|
+
brand: Brand;
|
|
40
|
+
headerType?: string;
|
|
41
|
+
tabs: NavbarItem[];
|
|
42
|
+
topbar: Topbar;
|
|
43
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getDoc } from '../../client'
|
|
2
|
+
import type { AuthOptions } from '../../client'
|
|
3
|
+
import type { Page } from './types'
|
|
4
|
+
|
|
5
|
+
export async function getPage(
|
|
6
|
+
baseUrl: string,
|
|
7
|
+
slug: string,
|
|
8
|
+
auth?: AuthOptions
|
|
9
|
+
): Promise<Page> {
|
|
10
|
+
return getDoc<Page>(baseUrl, 'Design Page', slug, auth)
|
|
11
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface Item {
|
|
2
|
+
itemCode: string
|
|
3
|
+
region: string
|
|
4
|
+
grade: string
|
|
5
|
+
gradeIssuer: string
|
|
6
|
+
color: string
|
|
7
|
+
storage: string
|
|
8
|
+
memory: string
|
|
9
|
+
network: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ItemVariant extends Item {
|
|
13
|
+
itemVariant: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ModelNumber {
|
|
17
|
+
modelNumber: string
|
|
18
|
+
simCardType: string
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface Colour {
|
|
22
|
+
name: string
|
|
23
|
+
color: string
|
|
24
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { frappeCall } from '../../client';
|
|
2
|
+
import type { AuthOptions } from '../../client';
|
|
3
|
+
import type { ShopContext, ProductContext } from './types';
|
|
4
|
+
|
|
5
|
+
export async function getShopContext(
|
|
6
|
+
baseUrl: string,
|
|
7
|
+
params?: {
|
|
8
|
+
categoryName?: string;
|
|
9
|
+
gradeName?: string;
|
|
10
|
+
},
|
|
11
|
+
auth?: AuthOptions
|
|
12
|
+
): Promise<ShopContext> {
|
|
13
|
+
return frappeCall<ShopContext>(
|
|
14
|
+
baseUrl,
|
|
15
|
+
'lasterp.shop.controllers.shop_controller.get_context',
|
|
16
|
+
params,
|
|
17
|
+
auth
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function getProductContext(
|
|
22
|
+
baseUrl: string,
|
|
23
|
+
productName: string,
|
|
24
|
+
auth?: AuthOptions
|
|
25
|
+
): Promise<ProductContext> {
|
|
26
|
+
return frappeCall<ProductContext>(
|
|
27
|
+
baseUrl,
|
|
28
|
+
'lasterp.shop.controllers.product_controller.get_context',
|
|
29
|
+
{ productName },
|
|
30
|
+
auth
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useQuery, type UseQueryOptions } from '@tanstack/react-query';
|
|
4
|
+
import { useFrappe } from '../../client';
|
|
5
|
+
import { getShopContext, getProductContext } from './api';
|
|
6
|
+
import type { ShopContext, ProductContext } from './types';
|
|
7
|
+
|
|
8
|
+
export function useShopContext(
|
|
9
|
+
params?: {
|
|
10
|
+
categoryName?: string;
|
|
11
|
+
gradeName?: string;
|
|
12
|
+
},
|
|
13
|
+
options?: Omit<
|
|
14
|
+
UseQueryOptions<ShopContext, Error>,
|
|
15
|
+
'queryKey' | 'queryFn'
|
|
16
|
+
>
|
|
17
|
+
) {
|
|
18
|
+
const { baseUrl, auth } = useFrappe();
|
|
19
|
+
|
|
20
|
+
return useQuery<ShopContext, Error>({
|
|
21
|
+
queryKey: ['shop-context', params],
|
|
22
|
+
queryFn: () => getShopContext(baseUrl, params, auth),
|
|
23
|
+
...options,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useProductContext(
|
|
28
|
+
productName: string,
|
|
29
|
+
options?: Omit<
|
|
30
|
+
UseQueryOptions<ProductContext, Error>,
|
|
31
|
+
'queryKey' | 'queryFn'
|
|
32
|
+
>
|
|
33
|
+
) {
|
|
34
|
+
const { baseUrl, auth } = useFrappe();
|
|
35
|
+
|
|
36
|
+
return useQuery<ProductContext, Error>({
|
|
37
|
+
queryKey: ['product-context', productName],
|
|
38
|
+
queryFn: () => getProductContext(baseUrl, productName, auth),
|
|
39
|
+
enabled: !!productName,
|
|
40
|
+
...options,
|
|
41
|
+
});
|
|
42
|
+
}
|