@superbright/indexeddb-orm 0.1.1
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/.prettierignore +9 -0
- package/.prettierrc.cjs +13 -0
- package/CONSUMING_APP_GUIDE.md +164 -0
- package/MIGRATION_SUMMARY.md +157 -0
- package/README.md +45 -0
- package/docs/app-store-guide.md +160 -0
- package/docs/property-store.md +192 -0
- package/docs/structured-store-migration.md +129 -0
- package/examples/property-store-migration.ts +164 -0
- package/index.html +29 -0
- package/package.json +53 -0
- package/playground/main.ts +38 -0
- package/src/adapters/dexie.ts +28 -0
- package/src/adapters/structured-store.ts +85 -0
- package/src/adapters/zustand-app.ts +221 -0
- package/src/adapters/zustand-unified.ts +342 -0
- package/src/adapters/zustand.ts +142 -0
- package/src/api/app.ts +270 -0
- package/src/api/favorites.ts +64 -0
- package/src/api/properties.ts +293 -0
- package/src/api/users.ts +66 -0
- package/src/db.ts +185 -0
- package/src/debug.ts +25 -0
- package/src/errors.ts +13 -0
- package/src/index.ts +34 -0
- package/src/schema.ts +48 -0
- package/src/storage.ts +71 -0
- package/src/stores/property.ts +133 -0
- package/src/stores/unified.ts +507 -0
- package/src/units/favorites.ts +19 -0
- package/src/validation.ts +32 -0
- package/test-export.js +6 -0
- package/tests/orm.spec.ts +17 -0
- package/tests/setup.ts +2 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.json +29 -0
- package/vite.config.ts +16 -0
- package/vitest.config.ts +9 -0
package/.prettierignore
ADDED
package/.prettierrc.cjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** @type {import("prettier").Config} */
|
|
2
|
+
export default {
|
|
3
|
+
printWidth: 100,
|
|
4
|
+
tabWidth: 2,
|
|
5
|
+
useTabs: false,
|
|
6
|
+
semi: true,
|
|
7
|
+
singleQuote: true,
|
|
8
|
+
trailingComma: "all",
|
|
9
|
+
bracketSpacing: true,
|
|
10
|
+
arrowParens: "always",
|
|
11
|
+
endOfLine: "lf",
|
|
12
|
+
plugins: ["prettier-plugin-packagejson"],
|
|
13
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consuming App Integration Guide
|
|
3
|
+
*
|
|
4
|
+
* Replace your existing stores with a single unified ORM-backed store
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// 1. Remove these old imports:
|
|
8
|
+
// import { persist, createJSONStorage } from "zustand/middleware";
|
|
9
|
+
// import { get, set, del } from "idb-keyval";
|
|
10
|
+
// import { createORMStringStorage } from "@superbright/indexeddb-orm";
|
|
11
|
+
|
|
12
|
+
// 2. Add these new imports for the UNIFIED STORE:
|
|
13
|
+
import { create } from "zustand";
|
|
14
|
+
import { devtools } from "zustand/middleware";
|
|
15
|
+
import {
|
|
16
|
+
createZustandPropertyStore, // More intuitive name (alias for createZustandUnifiedStore)
|
|
17
|
+
createUseUnitState,
|
|
18
|
+
type ZustandUnifiedStoreState,
|
|
19
|
+
type Filters,
|
|
20
|
+
type QueryParams,
|
|
21
|
+
type UnitData,
|
|
22
|
+
type TourContactData
|
|
23
|
+
} from "@superbright/indexeddb-orm";
|
|
24
|
+
|
|
25
|
+
// 3. Replace BOTH your property store AND app store with one unified store:
|
|
26
|
+
|
|
27
|
+
// This replaces both usePropertyStore AND useStore
|
|
28
|
+
export const useStore = create<ZustandUnifiedStoreState>()(
|
|
29
|
+
devtools(
|
|
30
|
+
createZustandPropertyStore({
|
|
31
|
+
// Optional: callback for when filters are updated
|
|
32
|
+
onFilterUpdate: (apiParams: QueryParams) => {
|
|
33
|
+
// Replace your updateParams call here
|
|
34
|
+
// updateParams(apiParams);
|
|
35
|
+
console.log("Filters updated:", apiParams);
|
|
36
|
+
}
|
|
37
|
+
}),
|
|
38
|
+
{ name: "property-store" }
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
// 4. Create the unit state hook (same as before):
|
|
43
|
+
export const useUnitState = createUseUnitState()(useStore);
|
|
44
|
+
|
|
45
|
+
// 5. Export store instance (replaces both propertyStore.getState and old useStore.getState):
|
|
46
|
+
export const propertyStore = useStore.getState;
|
|
47
|
+
|
|
48
|
+
// 6. Initialize the unified store in your app startup (e.g., main.tsx or App.tsx):
|
|
49
|
+
/*
|
|
50
|
+
import { useStore } from './stores/propertyStore'; // Keep the same file name
|
|
51
|
+
|
|
52
|
+
async function initializeApp() {
|
|
53
|
+
// Initialize and hydrate the unified store with data from IndexedDB
|
|
54
|
+
await useStore.getState()._initialize();
|
|
55
|
+
await useStore.getState()._hydrate();
|
|
56
|
+
|
|
57
|
+
// ... rest of your app initialization
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Call this on app startup
|
|
61
|
+
initializeApp();
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
// 7. Usage in components remains largely the same:
|
|
65
|
+
/*
|
|
66
|
+
function MyComponent() {
|
|
67
|
+
const { data, propertyId } = usePropertyStore();
|
|
68
|
+
const unitState = useUnitState("unit-123");
|
|
69
|
+
|
|
70
|
+
// NOTE: These are now async operations
|
|
71
|
+
const handleToggleFavorite = async () => {
|
|
72
|
+
await usePropertyStore.getState().toggleFavorite("unit-123");
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleMarkViewed = async () => {
|
|
76
|
+
await usePropertyStore.getState().markUnitAsViewed("unit-123", "property-slug");
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return (
|
|
80
|
+
<div>
|
|
81
|
+
<p>Property: {propertyId}</p>
|
|
82
|
+
<p>Is favorite: {unitState.isFavorite}</p>
|
|
83
|
+
<p>Viewed: {unitState.viewedDate}</p>
|
|
84
|
+
<button onClick={handleToggleFavorite}>Toggle Favorite</button>
|
|
85
|
+
<button onClick={handleMarkViewed}>Mark as Viewed</button>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
// 7. Usage examples - the API combines both stores:
|
|
92
|
+
/*
|
|
93
|
+
function PropertyComponent() {
|
|
94
|
+
const {
|
|
95
|
+
// Property data (was usePropertyStore)
|
|
96
|
+
properties,
|
|
97
|
+
currentPropertyId,
|
|
98
|
+
currentPropertySlug,
|
|
99
|
+
|
|
100
|
+
// App data (was useStore)
|
|
101
|
+
units,
|
|
102
|
+
filters,
|
|
103
|
+
resultsMode,
|
|
104
|
+
sortBy
|
|
105
|
+
} = useStore();
|
|
106
|
+
|
|
107
|
+
const unitState = useUnitState("unit-123");
|
|
108
|
+
|
|
109
|
+
// Property operations
|
|
110
|
+
const handleToggleFavorite = async () => {
|
|
111
|
+
await useStore.getState().toggleFavorite("unit-123");
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const handleMarkViewed = async () => {
|
|
115
|
+
await useStore.getState().markUnitAsViewed("unit-123", "property-slug");
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// App operations
|
|
119
|
+
const handleFilterChange = async () => {
|
|
120
|
+
await useStore.getState().setFilters({ bedrooms: [1, 2] });
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const handleSetUnitData = async () => {
|
|
124
|
+
await useStore.getState().setUnitData("unit-123", { isFavorite: true });
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
return (
|
|
128
|
+
<div>
|
|
129
|
+
<p>Property: {currentPropertyId}</p>
|
|
130
|
+
<p>Unit is favorite: {unitState.isFavorite}</p>
|
|
131
|
+
<p>Results mode: {resultsMode}</p>
|
|
132
|
+
<button onClick={handleToggleFavorite}>Toggle Favorite</button>
|
|
133
|
+
<button onClick={handleFilterChange}>Set Filters</button>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
*/
|
|
138
|
+
|
|
139
|
+
// 8. Key breaking changes to be aware of:
|
|
140
|
+
/*
|
|
141
|
+
BREAKING CHANGES:
|
|
142
|
+
- Two stores combined into one unified store
|
|
143
|
+
- All store methods are now async (return Promise<void>)
|
|
144
|
+
- Must call _initialize() and _hydrate() on app startup
|
|
145
|
+
- No more persist middleware configuration needed
|
|
146
|
+
- No more custom IndexedDB storage setup
|
|
147
|
+
- Property data is now in 'properties' object, current property tracked separately
|
|
148
|
+
- toggleFavorite() no longer takes propertyId parameter (uses currentPropertyId)
|
|
149
|
+
- loadPersistedFilters() is now redundant (always loads from IndexedDB)
|
|
150
|
+
- availabilityOptions array should be handled in UI components
|
|
151
|
+
- updateParams callback is now handled in store configuration
|
|
152
|
+
|
|
153
|
+
BENEFITS:
|
|
154
|
+
- Single source of truth for all app state
|
|
155
|
+
- Better type safety with Zod validation
|
|
156
|
+
- Centralized storage logic in ORM
|
|
157
|
+
- Better error handling and validation
|
|
158
|
+
- No more manual storage configuration
|
|
159
|
+
- More consistent API across all operations
|
|
160
|
+
- Better performance with native IndexedDB operations
|
|
161
|
+
- Automatic state synchronization
|
|
162
|
+
- Built-in callbacks for external integrations
|
|
163
|
+
- Easier state management and debugging
|
|
164
|
+
*/
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# Complete Store Migration Summary
|
|
2
|
+
|
|
3
|
+
This document provides a complete overview of migrating both the Property Store and App Store from the consuming application into a single unified IndexedDB ORM store.
|
|
4
|
+
|
|
5
|
+
## What Was Extracted
|
|
6
|
+
|
|
7
|
+
### Unified Store (Combines Both Previous Stores)
|
|
8
|
+
- **Original**: Two separate Zustand stores (property + app) with different storage mechanisms
|
|
9
|
+
- **New**: Single unified ORM-backed store with Zustand adapter
|
|
10
|
+
- **Key Features**:
|
|
11
|
+
- **Property data**: Multiple properties, current property tracking, favorites, viewed units, tour contacts, questionnaire results
|
|
12
|
+
- **App data**: Unit data, filters, temp filters, API filters, results mode, sorting, questionnaire values
|
|
13
|
+
|
|
14
|
+
## Architecture Changes
|
|
15
|
+
|
|
16
|
+
### Before (Consuming App)
|
|
17
|
+
```
|
|
18
|
+
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
19
|
+
│ Property Store │ │ Persist │ │ ORM String │
|
|
20
|
+
│ (Zustand) │───▶│ Middleware │───▶│ Storage │
|
|
21
|
+
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
22
|
+
|
|
23
|
+
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
24
|
+
│ App Store │ │ Persist │ │ idb-keyval │
|
|
25
|
+
│ (Zustand) │───▶│ Middleware │───▶│ Custom Storage │
|
|
26
|
+
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### After (Unified ORM-Centric)
|
|
30
|
+
```
|
|
31
|
+
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
|
|
32
|
+
│ Zustand │ │ Unified ORM │ │ IndexedDB │
|
|
33
|
+
│ Store │───▶│ Store Adapter │───▶│ (Native) │
|
|
34
|
+
│ (UI Layer) │ │ │ │ │
|
|
35
|
+
└─────────────────┘ └──────────────────┘ └─────────────────┘
|
|
36
|
+
│
|
|
37
|
+
▼
|
|
38
|
+
┌──────────────────┐
|
|
39
|
+
│ Direct ORM API │
|
|
40
|
+
│ (Server/Logic) │
|
|
41
|
+
└──────────────────┘
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## New Unified ORM API
|
|
45
|
+
|
|
46
|
+
### UnifiedStore (Direct ORM Usage)
|
|
47
|
+
```typescript
|
|
48
|
+
import { store } from "@superbright/indexeddb-orm";
|
|
49
|
+
|
|
50
|
+
// Property operations
|
|
51
|
+
await store.initializeProperty("prop-123", "property-slug");
|
|
52
|
+
await store.setCurrentProperty("prop-123", "property-slug");
|
|
53
|
+
await store.toggleFavorite("unit-456");
|
|
54
|
+
await store.markUnitAsViewed("unit-456", "property-slug");
|
|
55
|
+
|
|
56
|
+
// App operations
|
|
57
|
+
await store.setUnitData("unit-123", { isFavorite: true });
|
|
58
|
+
await store.setFilters({ bedrooms: [1, 2], cost: 2000 });
|
|
59
|
+
await store.setResultsMode("favorites");
|
|
60
|
+
|
|
61
|
+
// Get data
|
|
62
|
+
const unitState = await store.getUnitState("unit-456");
|
|
63
|
+
const fullState = await store.getFullState();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Zustand Integration
|
|
67
|
+
|
|
68
|
+
### Unified Store (Replaces Both Previous Stores)
|
|
69
|
+
```typescript
|
|
70
|
+
import { create } from "zustand";
|
|
71
|
+
import { devtools } from "zustand/middleware";
|
|
72
|
+
import { createZustandUnifiedStore } from "@superbright/indexeddb-orm";
|
|
73
|
+
|
|
74
|
+
// This replaces BOTH usePropertyStore AND useStore
|
|
75
|
+
export const useStore = create()(
|
|
76
|
+
devtools(
|
|
77
|
+
createZustandUnifiedStore({
|
|
78
|
+
onFilterUpdate: (apiParams) => {
|
|
79
|
+
// Handle filter updates (e.g., call updateParams)
|
|
80
|
+
updateParams(apiParams);
|
|
81
|
+
}
|
|
82
|
+
}),
|
|
83
|
+
{ name: "property-store" } // Keep familiar naming
|
|
84
|
+
)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
// Usage combines both property and app data
|
|
88
|
+
const {
|
|
89
|
+
// Property data
|
|
90
|
+
properties, currentPropertyId, currentPropertySlug,
|
|
91
|
+
// App data
|
|
92
|
+
units, filters, resultsMode, sortBy
|
|
93
|
+
} = useStore();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Migration Checklist
|
|
97
|
+
|
|
98
|
+
### ✅ Completed in ORM
|
|
99
|
+
- [x] UnifiedStore class combining both property and app functionality
|
|
100
|
+
- [x] Zustand adapter for unified store
|
|
101
|
+
- [x] Type definitions with Zod validation
|
|
102
|
+
- [x] Schema exports and integration
|
|
103
|
+
- [x] Legacy compatibility methods for smooth migration
|
|
104
|
+
- [x] Documentation and migration guides
|
|
105
|
+
- [x] Example usage code
|
|
106
|
+
- [x] Single storage mechanism for all data
|
|
107
|
+
|
|
108
|
+
### 🔄 Required in Consuming App
|
|
109
|
+
- [ ] Remove old persist middleware imports
|
|
110
|
+
- [ ] Remove custom storage setup (idb-keyval, createORMStringStorage)
|
|
111
|
+
- [ ] Replace BOTH stores with single unified store
|
|
112
|
+
- [ ] Update imports to use unified store types
|
|
113
|
+
- [ ] Add store initialization calls in app startup
|
|
114
|
+
- [ ] Update component handlers to be async
|
|
115
|
+
- [ ] Handle updateParams callback in store configuration
|
|
116
|
+
- [ ] Remove availabilityOptions from store state (handle in UI)
|
|
117
|
+
- [ ] Update component state selectors (properties vs data)
|
|
118
|
+
- [ ] Test all existing functionality
|
|
119
|
+
|
|
120
|
+
## Benefits
|
|
121
|
+
|
|
122
|
+
1. **Single Source of Truth**: All app state in one unified store
|
|
123
|
+
2. **Centralized Logic**: All storage logic lives in the ORM
|
|
124
|
+
3. **Type Safety**: Full TypeScript + Zod validation
|
|
125
|
+
4. **Better Performance**: Direct IndexedDB operations
|
|
126
|
+
5. **Simplified Architecture**: One store instead of two
|
|
127
|
+
6. **Testability**: Easier to mock and test centralized logic
|
|
128
|
+
7. **Flexibility**: Can use direct ORM API or Zustand adapters
|
|
129
|
+
8. **Error Handling**: Built-in validation and error handling
|
|
130
|
+
9. **Consistent API**: Same patterns for all operations
|
|
131
|
+
10. **Easier Debugging**: Single store to inspect and debug
|
|
132
|
+
|
|
133
|
+
## Breaking Changes Summary
|
|
134
|
+
|
|
135
|
+
### All Stores
|
|
136
|
+
- All methods are now async
|
|
137
|
+
- Must call initialization methods on app startup
|
|
138
|
+
- No more persist middleware configuration
|
|
139
|
+
|
|
140
|
+
### Property Store Specific
|
|
141
|
+
- `toggleFavorite()` no longer takes propertyId parameter
|
|
142
|
+
- Questionnaire results can be any type (not just IFilters)
|
|
143
|
+
|
|
144
|
+
### App Store Specific
|
|
145
|
+
- `loadPersistedFilters()` is redundant (always loads from IndexedDB)
|
|
146
|
+
- `availabilityOptions` should be handled in UI components
|
|
147
|
+
- `updateParams` callback moved to store configuration
|
|
148
|
+
|
|
149
|
+
## Next Steps
|
|
150
|
+
|
|
151
|
+
1. **Update Consuming App**: Follow the migration guides to update both stores
|
|
152
|
+
2. **Test Thoroughly**: Ensure all existing functionality works
|
|
153
|
+
3. **Remove Dead Code**: Clean up old storage-related code
|
|
154
|
+
4. **Add Error Handling**: Leverage the improved error handling in the ORM
|
|
155
|
+
5. **Consider Direct ORM Usage**: For server-side or complex logic, consider using the ORM APIs directly
|
|
156
|
+
|
|
157
|
+
The migration provides a solid foundation for scalable, type-safe data management while maintaining compatibility with existing Zustand-based UI patterns.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# indexeddb-orm-starter
|
|
2
|
+
|
|
3
|
+
Minimal **Vite + TypeScript** starter for an IndexedDB ORM using **Dexie** + **Zod**, with a built-in browser playground.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- Library build via Vite (ES + CJS) with type declarations (tsc)
|
|
7
|
+
- Dexie-powered tables: `users`, `properties`, `units`, and a `kv` store
|
|
8
|
+
- Helpers for **favourites** and **preferences**
|
|
9
|
+
- Schema version manifest + reset on incompatible version
|
|
10
|
+
- Vitest + fake-indexeddb tests
|
|
11
|
+
- Browser playground to inspect data
|
|
12
|
+
|
|
13
|
+
## Dev / Playground
|
|
14
|
+
```bash
|
|
15
|
+
pnpm i
|
|
16
|
+
pnpm dev
|
|
17
|
+
# Visit http://localhost:5173 (or printed URL)
|
|
18
|
+
# Use the buttons to write/dump/export, then open DevTools → Application → IndexedDB → inresi-orm
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Build & Test
|
|
22
|
+
```bash
|
|
23
|
+
pnpm build
|
|
24
|
+
pnpm test
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Programmatic usage
|
|
28
|
+
```ts
|
|
29
|
+
import {
|
|
30
|
+
getDB,
|
|
31
|
+
getFavouritedUnits, setFavouritedUnits,
|
|
32
|
+
getPreferences, setPreferences,
|
|
33
|
+
upsertUser, getUser,
|
|
34
|
+
debugDump, exportJSON
|
|
35
|
+
} from "indexeddb-orm-starter";
|
|
36
|
+
|
|
37
|
+
await getDB();
|
|
38
|
+
await setFavouritedUnits(["u1","u2"]);
|
|
39
|
+
console.log(await getFavouritedUnits());
|
|
40
|
+
await exportJSON();
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Notes
|
|
44
|
+
- Increment `SCHEMA_VERSION` in `src/schema.ts` to force a reset (basic strategy).
|
|
45
|
+
- Add entities/repositories in `src/api/*` following the same pattern.
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App Store Integration Guide
|
|
3
|
+
*
|
|
4
|
+
* Replace your existing app store (useStore) with this new ORM-backed implementation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// 1. Remove these old imports:
|
|
8
|
+
// import { persist, createJSONStorage } from "zustand/middleware";
|
|
9
|
+
// import { get, set, del } from "idb-keyval";
|
|
10
|
+
// import { createORMStringStorage } from "@superbright/indexeddb-orm";
|
|
11
|
+
|
|
12
|
+
// 2. Add these new imports:
|
|
13
|
+
import { create } from "zustand";
|
|
14
|
+
import { devtools } from "zustand/middleware";
|
|
15
|
+
import {
|
|
16
|
+
createZustandAppStore,
|
|
17
|
+
type ZustandAppStoreState,
|
|
18
|
+
type Filters,
|
|
19
|
+
type QueryParams,
|
|
20
|
+
type UnitData
|
|
21
|
+
} from "@superbright/indexeddb-orm";
|
|
22
|
+
|
|
23
|
+
// 3. Replace your store creation:
|
|
24
|
+
export const useStore = create<ZustandAppStoreState>()(
|
|
25
|
+
devtools(
|
|
26
|
+
createZustandAppStore({
|
|
27
|
+
// Optional: callback for when filters are updated
|
|
28
|
+
onFilterUpdate: (apiParams: QueryParams) => {
|
|
29
|
+
// This replaces your updateParams call
|
|
30
|
+
// Import and call your updateParams function here
|
|
31
|
+
// updateParams(apiParams);
|
|
32
|
+
console.log("Filters updated:", apiParams);
|
|
33
|
+
}
|
|
34
|
+
}),
|
|
35
|
+
{ name: "app-store" }
|
|
36
|
+
)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// 4. Initialize the store in your app startup (e.g., main.tsx or App.tsx):
|
|
40
|
+
/*
|
|
41
|
+
import { useStore } from './stores/appStore';
|
|
42
|
+
|
|
43
|
+
async function initializeApp() {
|
|
44
|
+
// Initialize and hydrate the store with data from IndexedDB
|
|
45
|
+
await useStore.getState()._initialize();
|
|
46
|
+
await useStore.getState()._hydrate();
|
|
47
|
+
|
|
48
|
+
// ... rest of your app initialization
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Call this on app startup
|
|
52
|
+
initializeApp();
|
|
53
|
+
*/
|
|
54
|
+
|
|
55
|
+
// 5. Usage in components remains largely the same, but methods are now async:
|
|
56
|
+
/*
|
|
57
|
+
function FiltersComponent() {
|
|
58
|
+
const {
|
|
59
|
+
filters,
|
|
60
|
+
tempFilters,
|
|
61
|
+
resultsMode,
|
|
62
|
+
sortBy,
|
|
63
|
+
filtersLoaded
|
|
64
|
+
} = useStore();
|
|
65
|
+
|
|
66
|
+
// NOTE: These are now async operations
|
|
67
|
+
const handleFilterChange = async (key: keyof Filters, value: any) => {
|
|
68
|
+
await useStore.getState().handleTempFilterChange(key, value);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
const handleCommitFilter = async () => {
|
|
72
|
+
await useStore.getState().submitFilterUpdate();
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleResultsModeChange = async (mode: "all" | "bestFit" | "closestMatch" | "favorites") => {
|
|
76
|
+
await useStore.getState().setResultsMode(mode);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleSortChange = async (sortBy: "relevance" | "newest" | "priceLowToHigh" | "priceHighToLow") => {
|
|
80
|
+
await useStore.getState().setSortBy(sortBy);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<div>
|
|
85
|
+
<p>Filters loaded: {filtersLoaded}</p>
|
|
86
|
+
<p>Current bedrooms: {filters.bedrooms?.join(", ")}</p>
|
|
87
|
+
<p>Results mode: {resultsMode}</p>
|
|
88
|
+
<p>Sort by: {sortBy}</p>
|
|
89
|
+
<button onClick={() => handleFilterChange("bedrooms", [1, 2])}>
|
|
90
|
+
Set 1-2 bedrooms
|
|
91
|
+
</button>
|
|
92
|
+
<button onClick={handleCommitFilter}>Apply Filters</button>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function UnitComponent({ unitId }: { unitId: string }) {
|
|
98
|
+
const { data } = useStore();
|
|
99
|
+
const unitData = data[unitId];
|
|
100
|
+
|
|
101
|
+
const handleToggleFavorite = async () => {
|
|
102
|
+
const currentData = data[unitId] || {};
|
|
103
|
+
await useStore.getState().setData(unitId, {
|
|
104
|
+
...currentData,
|
|
105
|
+
isFavorite: !currentData.isFavorite
|
|
106
|
+
});
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const handleMarkViewed = async () => {
|
|
110
|
+
const today = new Date();
|
|
111
|
+
const formattedDate = `${String(today.getMonth() + 1).padStart(2, "0")}/${String(today.getDate()).padStart(2, "0")}`;
|
|
112
|
+
|
|
113
|
+
const currentData = data[unitId] || {};
|
|
114
|
+
await useStore.getState().setData(unitId, {
|
|
115
|
+
...currentData,
|
|
116
|
+
viewedDate: formattedDate
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div>
|
|
122
|
+
<p>Unit: {unitId}</p>
|
|
123
|
+
<p>Is favorite: {unitData?.isFavorite ? "Yes" : "No"}</p>
|
|
124
|
+
<p>Viewed: {unitData?.viewedDate || "Never"}</p>
|
|
125
|
+
<button onClick={handleToggleFavorite}>Toggle Favorite</button>
|
|
126
|
+
<button onClick={handleMarkViewed}>Mark as Viewed</button>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
*/
|
|
131
|
+
|
|
132
|
+
// 6. Key breaking changes to be aware of:
|
|
133
|
+
/*
|
|
134
|
+
BREAKING CHANGES:
|
|
135
|
+
- All store methods are now async (return Promise<void>)
|
|
136
|
+
- Must call _initialize() and _hydrate() on app startup
|
|
137
|
+
- No more persist middleware configuration needed
|
|
138
|
+
- No more custom IndexedDB storage setup
|
|
139
|
+
- loadPersistedFilters() is now redundant (always loads from IndexedDB)
|
|
140
|
+
- availabilityOptions array is not stored (should be handled in UI)
|
|
141
|
+
|
|
142
|
+
BENEFITS:
|
|
143
|
+
- Better type safety with Zod validation
|
|
144
|
+
- Centralized storage logic in ORM
|
|
145
|
+
- Better error handling and validation
|
|
146
|
+
- No more manual storage configuration
|
|
147
|
+
- More consistent API across different data types
|
|
148
|
+
- Better performance with native IndexedDB operations
|
|
149
|
+
- Automatic state synchronization
|
|
150
|
+
- Built-in filter update callbacks
|
|
151
|
+
|
|
152
|
+
MIGRATION NOTES:
|
|
153
|
+
1. The store structure remains the same, but all operations are async
|
|
154
|
+
2. Remove custom IndexedDB setup (idb-keyval, custom storage)
|
|
155
|
+
3. Replace persist middleware with ORM storage
|
|
156
|
+
4. Add initialization calls in app startup
|
|
157
|
+
5. Update component handlers to be async
|
|
158
|
+
6. Handle updateParams callback in store configuration
|
|
159
|
+
7. Remove availabilityOptions from store (handle in UI components)
|
|
160
|
+
*/
|