shelflife-react-hooks 1.0.18 → 1.0.20

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.
Files changed (44) hide show
  1. package/dist/index.cjs.js +23 -0
  2. package/dist/index.cjs.js.map +1 -1
  3. package/dist/index.d.cts +2 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.esm.js +23 -0
  6. package/dist/index.esm.js.map +1 -1
  7. package/package.json +36 -36
  8. package/src/context/AuthContext.tsx +161 -161
  9. package/src/context/InviteContext.tsx +74 -74
  10. package/src/context/ProductContext.tsx +131 -121
  11. package/src/context/RunningLowContext.tsx +100 -100
  12. package/src/context/ShoppingListContext.tsx +76 -76
  13. package/src/context/StorageContext.tsx +105 -105
  14. package/src/context/StorageItemContext.tsx +157 -157
  15. package/src/context/StorageMemberContext.tsx +84 -84
  16. package/src/context/UserContext.tsx +109 -109
  17. package/src/context/__tests__/contexts.test.tsx +370 -370
  18. package/src/context/api/authApi.ts +155 -155
  19. package/src/context/api/inviteApi.ts +65 -65
  20. package/src/context/api/productApi.ts +223 -201
  21. package/src/context/api/requestState.ts +24 -24
  22. package/src/context/api/runningLowApi.ts +141 -141
  23. package/src/context/api/shoppingListApi.ts +161 -159
  24. package/src/context/api/storageApi.ts +166 -166
  25. package/src/context/api/storageItemApi.ts +260 -260
  26. package/src/context/api/storageMemberApi.ts +84 -84
  27. package/src/context/api/userApi.ts +161 -161
  28. package/src/context/http.ts +22 -22
  29. package/src/index.ts +21 -21
  30. package/src/type/PaginatedResponse.ts +8 -8
  31. package/src/type/auth.ts +79 -79
  32. package/src/type/base.ts +21 -21
  33. package/src/type/item.ts +12 -12
  34. package/src/type/member.ts +6 -6
  35. package/src/type/models.ts +56 -56
  36. package/src/type/product.ts +11 -11
  37. package/src/type/requests.ts +60 -60
  38. package/src/type/runninglow.ts +13 -13
  39. package/src/type/shoppingList.ts +13 -13
  40. package/src/type/storage.ts +7 -7
  41. package/src/type/user.ts +11 -11
  42. package/tsconfig.json +46 -46
  43. package/tsup.config.ts +10 -10
  44. package/vitest.config.ts +8 -8
@@ -1,261 +1,261 @@
1
- import type { AddItemError, EditItemError } from '../../type/item.js';
2
- import type { RunningLowNotification, StorageItem } from '../../type/models.js';
3
- import type { AddItemRequest, EditItemRequest } from '../../type/requests.js';
4
- import { buildAuthHeaders, normalizeBaseUrl, readJson } from '../http.js';
5
- import { runWithRequestState, type RequestStateHandlers } from './requestState.js';
6
-
7
- type StorageItemApiConfig = RequestStateHandlers & {
8
- baseUrl: string;
9
- token: string | null;
10
- setItems: (value: StorageItem[] | ((items: StorageItem[]) => StorageItem[])) => void;
11
- setExpiredItems: (value: StorageItem[]) => void;
12
- setAboutToExpireItems: (value: StorageItem[]) => void;
13
- setRunningLow: (value: RunningLowNotification[]) => void;
14
- };
15
-
16
- const updateById = (items: StorageItem[], updated: StorageItem): StorageItem[] => {
17
- const index = items.findIndex((item) => item.id === updated.id);
18
- if (index === -1) {
19
- return [updated, ...items];
20
- }
21
-
22
- const next = [...items];
23
- next[index] = updated;
24
- return next;
25
- };
26
-
27
- export const fetchItemsRequest = async (
28
- config: StorageItemApiConfig,
29
- storageId: number
30
- ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
31
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
32
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items`, {
33
- headers: buildAuthHeaders(config.token)
34
- });
35
-
36
- if (!response.ok) {
37
- throw new Error('Failed to fetch storage items');
38
- }
39
-
40
- const payload = await readJson<StorageItem[]>(response);
41
- if (payload) {
42
- config.setItems(payload);
43
- return payload;
44
- }
45
-
46
- return [];
47
- });
48
-
49
- export const addItemRequest = async (
50
- config: StorageItemApiConfig,
51
- storageId: number,
52
- dto: AddItemRequest
53
- ): Promise<StorageItem> => runWithRequestState(config, async () => {
54
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
55
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items`, {
56
- method: 'POST',
57
- headers: {
58
- ...buildAuthHeaders(config.token),
59
- 'Content-Type': 'application/json'
60
- },
61
- body: JSON.stringify(dto)
62
- });
63
-
64
- if (!response.ok) {
65
- const err = await readJson<AddItemError>(response);
66
-
67
- if (err?.productId || err?.expiresAt)
68
- throw err;
69
-
70
- throw new Error('Failed to add storage item');
71
- }
72
-
73
- const payload = await readJson<StorageItem>(response);
74
- if (!payload) {
75
- throw new Error('Add item response missing data');
76
- }
77
-
78
- config.setItems((previous) => [payload, ...previous]);
79
- return payload;
80
- });
81
-
82
- export const editItemRequest = async (
83
- config: StorageItemApiConfig,
84
- storageId: number,
85
- itemId: number,
86
- dto: EditItemRequest
87
- ): Promise<StorageItem> => runWithRequestState(config, async () => {
88
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
89
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items/${itemId}`, {
90
- method: 'PATCH',
91
- headers: {
92
- ...buildAuthHeaders(config.token),
93
- 'Content-Type': 'application/json'
94
- },
95
- body: JSON.stringify(dto)
96
- });
97
-
98
- if (!response.ok) {
99
- const err = await readJson<EditItemError>(response);
100
-
101
- if (err?.expiresAt)
102
- throw err;
103
-
104
- throw new Error('Failed to edit storage item');
105
- }
106
-
107
- const payload = await readJson<StorageItem>(response);
108
- if (!payload) {
109
- throw new Error('Edit item response missing data');
110
- }
111
-
112
- config.setItems((previous) => updateById(previous, payload));
113
- return payload;
114
- });
115
-
116
- export const deleteItemRequest = async (
117
- config: StorageItemApiConfig,
118
- storageId: number,
119
- itemId: number
120
- ): Promise<void> => runWithRequestState(config, async () => {
121
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
122
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items/${itemId}`, {
123
- method: 'DELETE',
124
- headers: buildAuthHeaders(config.token)
125
- });
126
-
127
- if (!response.ok) {
128
- throw new Error('Failed to delete storage item');
129
- }
130
-
131
- config.setItems((previous) => previous.filter((item) => item.id !== itemId));
132
- });
133
-
134
- export const fetchExpiredRequest = async (
135
- config: StorageItemApiConfig,
136
- storageId: number
137
- ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
138
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
139
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/expired`, {
140
- headers: buildAuthHeaders(config.token)
141
- });
142
-
143
- if (!response.ok) {
144
- throw new Error('Failed to fetch expired items');
145
- }
146
-
147
- const payload = await readJson<StorageItem[]>(response);
148
- if (payload) {
149
- config.setExpiredItems(payload);
150
- return payload;
151
- }
152
-
153
- return [];
154
- });
155
-
156
- export const fetchAboutToExpireRequest = async (
157
- config: StorageItemApiConfig,
158
- storageId: number
159
- ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
160
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
161
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/abouttoexpire`, {
162
- headers: buildAuthHeaders(config.token)
163
- });
164
-
165
- if (!response.ok) {
166
- throw new Error('Failed to fetch items about to expire');
167
- }
168
-
169
- const payload = await readJson<StorageItem[]>(response);
170
- if (payload) {
171
- config.setAboutToExpireItems(payload);
172
- return payload;
173
- }
174
-
175
- return [];
176
- });
177
-
178
- export const fetchAggregatedExpiredRequest = async (
179
- config: StorageItemApiConfig
180
- ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
181
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
182
- const response = await fetch(`${normalizedBaseUrl}/api/expired`, {
183
- headers: buildAuthHeaders(config.token)
184
- });
185
-
186
- if (!response.ok) {
187
- throw new Error('Failed to fetch expired items');
188
- }
189
-
190
- const payload = await readJson<StorageItem[]>(response);
191
- if (payload) {
192
- config.setExpiredItems(payload);
193
- return payload;
194
- }
195
-
196
- return [];
197
- });
198
-
199
- export const fetchAggregatedAboutToExpireRequest = async (
200
- config: StorageItemApiConfig
201
- ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
202
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
203
- const response = await fetch(`${normalizedBaseUrl}/api/abouttoexpire`, {
204
- headers: buildAuthHeaders(config.token)
205
- });
206
-
207
- if (!response.ok) {
208
- throw new Error('Failed to fetch items about to expire');
209
- }
210
-
211
- const payload = await readJson<StorageItem[]>(response);
212
- if (payload) {
213
- config.setAboutToExpireItems(payload);
214
- return payload;
215
- }
216
-
217
- return [];
218
- });
219
-
220
- export const fetchRunningLowRequest = async (
221
- config: StorageItemApiConfig,
222
- storageId: number
223
- ): Promise<RunningLowNotification[]> => runWithRequestState(config, async () => {
224
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
225
- const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/runninglow`, {
226
- headers: buildAuthHeaders(config.token)
227
- });
228
-
229
- if (!response.ok) {
230
- throw new Error('Failed to fetch running low items');
231
- }
232
-
233
- const payload = await readJson<RunningLowNotification[]>(response);
234
- if (payload) {
235
- config.setRunningLow(payload);
236
- return payload;
237
- }
238
-
239
- return [];
240
- });
241
-
242
- export const fetchAggregatedRunningLowRequest = async (
243
- config: StorageItemApiConfig
244
- ): Promise<RunningLowNotification[]> => runWithRequestState(config, async () => {
245
- const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
246
- const response = await fetch(`${normalizedBaseUrl}/api/runninglow`, {
247
- headers: buildAuthHeaders(config.token)
248
- });
249
-
250
- if (!response.ok) {
251
- throw new Error('Failed to fetch running low items');
252
- }
253
-
254
- const payload = await readJson<RunningLowNotification[]>(response);
255
- if (payload) {
256
- config.setRunningLow(payload);
257
- return payload;
258
- }
259
-
260
- return [];
1
+ import type { AddItemError, EditItemError } from '../../type/item.js';
2
+ import type { RunningLowNotification, StorageItem } from '../../type/models.js';
3
+ import type { AddItemRequest, EditItemRequest } from '../../type/requests.js';
4
+ import { buildAuthHeaders, normalizeBaseUrl, readJson } from '../http.js';
5
+ import { runWithRequestState, type RequestStateHandlers } from './requestState.js';
6
+
7
+ type StorageItemApiConfig = RequestStateHandlers & {
8
+ baseUrl: string;
9
+ token: string | null;
10
+ setItems: (value: StorageItem[] | ((items: StorageItem[]) => StorageItem[])) => void;
11
+ setExpiredItems: (value: StorageItem[]) => void;
12
+ setAboutToExpireItems: (value: StorageItem[]) => void;
13
+ setRunningLow: (value: RunningLowNotification[]) => void;
14
+ };
15
+
16
+ const updateById = (items: StorageItem[], updated: StorageItem): StorageItem[] => {
17
+ const index = items.findIndex((item) => item.id === updated.id);
18
+ if (index === -1) {
19
+ return [updated, ...items];
20
+ }
21
+
22
+ const next = [...items];
23
+ next[index] = updated;
24
+ return next;
25
+ };
26
+
27
+ export const fetchItemsRequest = async (
28
+ config: StorageItemApiConfig,
29
+ storageId: number
30
+ ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
31
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
32
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items`, {
33
+ headers: buildAuthHeaders(config.token)
34
+ });
35
+
36
+ if (!response.ok) {
37
+ throw new Error('Failed to fetch storage items');
38
+ }
39
+
40
+ const payload = await readJson<StorageItem[]>(response);
41
+ if (payload) {
42
+ config.setItems(payload);
43
+ return payload;
44
+ }
45
+
46
+ return [];
47
+ });
48
+
49
+ export const addItemRequest = async (
50
+ config: StorageItemApiConfig,
51
+ storageId: number,
52
+ dto: AddItemRequest
53
+ ): Promise<StorageItem> => runWithRequestState(config, async () => {
54
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
55
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items`, {
56
+ method: 'POST',
57
+ headers: {
58
+ ...buildAuthHeaders(config.token),
59
+ 'Content-Type': 'application/json'
60
+ },
61
+ body: JSON.stringify(dto)
62
+ });
63
+
64
+ if (!response.ok) {
65
+ const err = await readJson<AddItemError>(response);
66
+
67
+ if (err?.productId || err?.expiresAt)
68
+ throw err;
69
+
70
+ throw new Error('Failed to add storage item');
71
+ }
72
+
73
+ const payload = await readJson<StorageItem>(response);
74
+ if (!payload) {
75
+ throw new Error('Add item response missing data');
76
+ }
77
+
78
+ config.setItems((previous) => [payload, ...previous]);
79
+ return payload;
80
+ });
81
+
82
+ export const editItemRequest = async (
83
+ config: StorageItemApiConfig,
84
+ storageId: number,
85
+ itemId: number,
86
+ dto: EditItemRequest
87
+ ): Promise<StorageItem> => runWithRequestState(config, async () => {
88
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
89
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items/${itemId}`, {
90
+ method: 'PATCH',
91
+ headers: {
92
+ ...buildAuthHeaders(config.token),
93
+ 'Content-Type': 'application/json'
94
+ },
95
+ body: JSON.stringify(dto)
96
+ });
97
+
98
+ if (!response.ok) {
99
+ const err = await readJson<EditItemError>(response);
100
+
101
+ if (err?.expiresAt)
102
+ throw err;
103
+
104
+ throw new Error('Failed to edit storage item');
105
+ }
106
+
107
+ const payload = await readJson<StorageItem>(response);
108
+ if (!payload) {
109
+ throw new Error('Edit item response missing data');
110
+ }
111
+
112
+ config.setItems((previous) => updateById(previous, payload));
113
+ return payload;
114
+ });
115
+
116
+ export const deleteItemRequest = async (
117
+ config: StorageItemApiConfig,
118
+ storageId: number,
119
+ itemId: number
120
+ ): Promise<void> => runWithRequestState(config, async () => {
121
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
122
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/items/${itemId}`, {
123
+ method: 'DELETE',
124
+ headers: buildAuthHeaders(config.token)
125
+ });
126
+
127
+ if (!response.ok) {
128
+ throw new Error('Failed to delete storage item');
129
+ }
130
+
131
+ config.setItems((previous) => previous.filter((item) => item.id !== itemId));
132
+ });
133
+
134
+ export const fetchExpiredRequest = async (
135
+ config: StorageItemApiConfig,
136
+ storageId: number
137
+ ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
138
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
139
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/expired`, {
140
+ headers: buildAuthHeaders(config.token)
141
+ });
142
+
143
+ if (!response.ok) {
144
+ throw new Error('Failed to fetch expired items');
145
+ }
146
+
147
+ const payload = await readJson<StorageItem[]>(response);
148
+ if (payload) {
149
+ config.setExpiredItems(payload);
150
+ return payload;
151
+ }
152
+
153
+ return [];
154
+ });
155
+
156
+ export const fetchAboutToExpireRequest = async (
157
+ config: StorageItemApiConfig,
158
+ storageId: number
159
+ ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
160
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
161
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/abouttoexpire`, {
162
+ headers: buildAuthHeaders(config.token)
163
+ });
164
+
165
+ if (!response.ok) {
166
+ throw new Error('Failed to fetch items about to expire');
167
+ }
168
+
169
+ const payload = await readJson<StorageItem[]>(response);
170
+ if (payload) {
171
+ config.setAboutToExpireItems(payload);
172
+ return payload;
173
+ }
174
+
175
+ return [];
176
+ });
177
+
178
+ export const fetchAggregatedExpiredRequest = async (
179
+ config: StorageItemApiConfig
180
+ ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
181
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
182
+ const response = await fetch(`${normalizedBaseUrl}/api/expired`, {
183
+ headers: buildAuthHeaders(config.token)
184
+ });
185
+
186
+ if (!response.ok) {
187
+ throw new Error('Failed to fetch expired items');
188
+ }
189
+
190
+ const payload = await readJson<StorageItem[]>(response);
191
+ if (payload) {
192
+ config.setExpiredItems(payload);
193
+ return payload;
194
+ }
195
+
196
+ return [];
197
+ });
198
+
199
+ export const fetchAggregatedAboutToExpireRequest = async (
200
+ config: StorageItemApiConfig
201
+ ): Promise<StorageItem[]> => runWithRequestState(config, async () => {
202
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
203
+ const response = await fetch(`${normalizedBaseUrl}/api/abouttoexpire`, {
204
+ headers: buildAuthHeaders(config.token)
205
+ });
206
+
207
+ if (!response.ok) {
208
+ throw new Error('Failed to fetch items about to expire');
209
+ }
210
+
211
+ const payload = await readJson<StorageItem[]>(response);
212
+ if (payload) {
213
+ config.setAboutToExpireItems(payload);
214
+ return payload;
215
+ }
216
+
217
+ return [];
218
+ });
219
+
220
+ export const fetchRunningLowRequest = async (
221
+ config: StorageItemApiConfig,
222
+ storageId: number
223
+ ): Promise<RunningLowNotification[]> => runWithRequestState(config, async () => {
224
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
225
+ const response = await fetch(`${normalizedBaseUrl}/api/storages/${storageId}/runninglow`, {
226
+ headers: buildAuthHeaders(config.token)
227
+ });
228
+
229
+ if (!response.ok) {
230
+ throw new Error('Failed to fetch running low items');
231
+ }
232
+
233
+ const payload = await readJson<RunningLowNotification[]>(response);
234
+ if (payload) {
235
+ config.setRunningLow(payload);
236
+ return payload;
237
+ }
238
+
239
+ return [];
240
+ });
241
+
242
+ export const fetchAggregatedRunningLowRequest = async (
243
+ config: StorageItemApiConfig
244
+ ): Promise<RunningLowNotification[]> => runWithRequestState(config, async () => {
245
+ const normalizedBaseUrl = normalizeBaseUrl(config.baseUrl);
246
+ const response = await fetch(`${normalizedBaseUrl}/api/runninglow`, {
247
+ headers: buildAuthHeaders(config.token)
248
+ });
249
+
250
+ if (!response.ok) {
251
+ throw new Error('Failed to fetch running low items');
252
+ }
253
+
254
+ const payload = await readJson<RunningLowNotification[]>(response);
255
+ if (payload) {
256
+ config.setRunningLow(payload);
257
+ return payload;
258
+ }
259
+
260
+ return [];
261
261
  });