@htlkg/data 0.0.1 → 0.0.3
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 +53 -0
- package/dist/client/index.d.ts +117 -31
- package/dist/client/index.js +74 -22
- package/dist/client/index.js.map +1 -1
- package/dist/content-collections/index.js +20 -24
- package/dist/content-collections/index.js.map +1 -1
- package/dist/hooks/index.d.ts +113 -5
- package/dist/hooks/index.js +165 -182
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +8 -4
- package/dist/index.js +305 -182
- package/dist/index.js.map +1 -1
- package/dist/queries/index.d.ts +78 -1
- package/dist/queries/index.js +47 -0
- package/dist/queries/index.js.map +1 -1
- package/dist/stores/index.d.ts +106 -0
- package/dist/stores/index.js +108 -0
- package/dist/stores/index.js.map +1 -0
- package/package.json +60 -37
- package/src/client/__tests__/server.test.ts +100 -0
- package/src/client/client.md +91 -0
- package/src/client/index.test.ts +232 -0
- package/src/client/index.ts +145 -0
- package/src/client/server.ts +118 -0
- package/src/content-collections/content-collections.md +87 -0
- package/src/content-collections/generator.ts +314 -0
- package/src/content-collections/index.ts +32 -0
- package/src/content-collections/schemas.ts +75 -0
- package/src/content-collections/sync.ts +139 -0
- package/src/hooks/README.md +293 -0
- package/src/hooks/createDataHook.ts +208 -0
- package/src/hooks/data-hook-errors.property.test.ts +270 -0
- package/src/hooks/data-hook-filters.property.test.ts +263 -0
- package/src/hooks/data-hooks.property.test.ts +190 -0
- package/src/hooks/hooks.test.ts +76 -0
- package/src/hooks/index.ts +21 -0
- package/src/hooks/useAccounts.ts +66 -0
- package/src/hooks/useBrands.ts +95 -0
- package/src/hooks/useProducts.ts +88 -0
- package/src/hooks/useUsers.ts +89 -0
- package/src/index.ts +32 -0
- package/src/mutations/accounts.ts +127 -0
- package/src/mutations/brands.ts +133 -0
- package/src/mutations/index.ts +32 -0
- package/src/mutations/mutations.md +96 -0
- package/src/mutations/users.ts +136 -0
- package/src/queries/accounts.ts +121 -0
- package/src/queries/brands.ts +176 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/products.ts +282 -0
- package/src/queries/queries.md +88 -0
- package/src/queries/server-helpers.ts +114 -0
- package/src/queries/users.ts +199 -0
- package/src/stores/createStores.ts +148 -0
- package/src/stores/index.ts +15 -0
- package/src/stores/stores.md +104 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Property-Based Tests for Data Hooks
|
|
3
|
+
*
|
|
4
|
+
* Feature: htlkg-modular-architecture, Property 5: Data hooks return reactive state
|
|
5
|
+
* Validates: Requirements 11.1, 11.2, 11.3
|
|
6
|
+
*
|
|
7
|
+
* Tests that data hooks return reactive state with correct structure and behavior.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
11
|
+
import * as fc from "fast-check";
|
|
12
|
+
import { ref, isRef, isReactive, computed } from "vue";
|
|
13
|
+
import { useBrands } from "./useBrands";
|
|
14
|
+
import { useAccounts } from "./useAccounts";
|
|
15
|
+
import { useUsers } from "./useUsers";
|
|
16
|
+
import { useProducts } from "./useProducts";
|
|
17
|
+
|
|
18
|
+
// Mock the client module
|
|
19
|
+
vi.mock("../client", () => ({
|
|
20
|
+
generateClient: vi.fn(() => ({
|
|
21
|
+
models: {
|
|
22
|
+
Brand: {
|
|
23
|
+
list: vi.fn().mockResolvedValue({ data: [], errors: null }),
|
|
24
|
+
},
|
|
25
|
+
Account: {
|
|
26
|
+
list: vi.fn().mockResolvedValue({ data: [], errors: null }),
|
|
27
|
+
},
|
|
28
|
+
User: {
|
|
29
|
+
list: vi.fn().mockResolvedValue({ data: [], errors: null }),
|
|
30
|
+
},
|
|
31
|
+
Product: {
|
|
32
|
+
list: vi.fn().mockResolvedValue({ data: [], errors: null }),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
})),
|
|
36
|
+
}));
|
|
37
|
+
|
|
38
|
+
describe("Data Hooks - Property 5: Data hooks return reactive state", () => {
|
|
39
|
+
beforeEach(() => {
|
|
40
|
+
vi.clearAllMocks();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Property: For any data hook, calling it should return reactive refs
|
|
45
|
+
* for data, loading, error, and a refetch function
|
|
46
|
+
*/
|
|
47
|
+
it("useBrands returns reactive state structure", () => {
|
|
48
|
+
fc.assert(
|
|
49
|
+
fc.property(
|
|
50
|
+
fc.record({
|
|
51
|
+
autoFetch: fc.boolean(),
|
|
52
|
+
limit: fc.option(fc.integer({ min: 1, max: 100 }), { nil: undefined }),
|
|
53
|
+
}),
|
|
54
|
+
(options) => {
|
|
55
|
+
const result = useBrands(options);
|
|
56
|
+
|
|
57
|
+
// Check that all required properties exist
|
|
58
|
+
expect(result).toHaveProperty("brands");
|
|
59
|
+
expect(result).toHaveProperty("activeBrands");
|
|
60
|
+
expect(result).toHaveProperty("loading");
|
|
61
|
+
expect(result).toHaveProperty("error");
|
|
62
|
+
expect(result).toHaveProperty("refetch");
|
|
63
|
+
|
|
64
|
+
// Check that data properties are reactive refs
|
|
65
|
+
expect(isRef(result.brands)).toBe(true);
|
|
66
|
+
expect(isRef(result.loading)).toBe(true);
|
|
67
|
+
expect(isRef(result.error)).toBe(true);
|
|
68
|
+
|
|
69
|
+
// Check that activeBrands is a computed ref
|
|
70
|
+
expect(isRef(result.activeBrands)).toBe(true);
|
|
71
|
+
|
|
72
|
+
// Check that refetch is a function
|
|
73
|
+
expect(typeof result.refetch).toBe("function");
|
|
74
|
+
|
|
75
|
+
// Check initial state values
|
|
76
|
+
expect(Array.isArray(result.brands.value)).toBe(true);
|
|
77
|
+
expect(typeof result.loading.value).toBe("boolean");
|
|
78
|
+
expect(result.error.value === null || result.error.value instanceof Error).toBe(true);
|
|
79
|
+
},
|
|
80
|
+
),
|
|
81
|
+
{ numRuns: 100 },
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("useAccounts returns reactive state structure", () => {
|
|
86
|
+
fc.assert(
|
|
87
|
+
fc.property(
|
|
88
|
+
fc.record({
|
|
89
|
+
autoFetch: fc.boolean(),
|
|
90
|
+
limit: fc.option(fc.integer({ min: 1, max: 100 }), { nil: undefined }),
|
|
91
|
+
}),
|
|
92
|
+
(options) => {
|
|
93
|
+
const result = useAccounts(options);
|
|
94
|
+
|
|
95
|
+
// Check that all required properties exist
|
|
96
|
+
expect(result).toHaveProperty("accounts");
|
|
97
|
+
expect(result).toHaveProperty("loading");
|
|
98
|
+
expect(result).toHaveProperty("error");
|
|
99
|
+
expect(result).toHaveProperty("refetch");
|
|
100
|
+
|
|
101
|
+
// Check that data properties are reactive refs
|
|
102
|
+
expect(isRef(result.accounts)).toBe(true);
|
|
103
|
+
expect(isRef(result.loading)).toBe(true);
|
|
104
|
+
expect(isRef(result.error)).toBe(true);
|
|
105
|
+
|
|
106
|
+
// Check that refetch is a function
|
|
107
|
+
expect(typeof result.refetch).toBe("function");
|
|
108
|
+
|
|
109
|
+
// Check initial state values
|
|
110
|
+
expect(Array.isArray(result.accounts.value)).toBe(true);
|
|
111
|
+
expect(typeof result.loading.value).toBe("boolean");
|
|
112
|
+
expect(result.error.value === null || result.error.value instanceof Error).toBe(true);
|
|
113
|
+
},
|
|
114
|
+
),
|
|
115
|
+
{ numRuns: 100 },
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it("useUsers returns reactive state structure", () => {
|
|
120
|
+
fc.assert(
|
|
121
|
+
fc.property(
|
|
122
|
+
fc.record({
|
|
123
|
+
autoFetch: fc.boolean(),
|
|
124
|
+
limit: fc.option(fc.integer({ min: 1, max: 100 }), { nil: undefined }),
|
|
125
|
+
}),
|
|
126
|
+
(options) => {
|
|
127
|
+
const result = useUsers(options);
|
|
128
|
+
|
|
129
|
+
// Check that all required properties exist
|
|
130
|
+
expect(result).toHaveProperty("users");
|
|
131
|
+
expect(result).toHaveProperty("loading");
|
|
132
|
+
expect(result).toHaveProperty("error");
|
|
133
|
+
expect(result).toHaveProperty("refetch");
|
|
134
|
+
|
|
135
|
+
// Check that data properties are reactive refs
|
|
136
|
+
expect(isRef(result.users)).toBe(true);
|
|
137
|
+
expect(isRef(result.loading)).toBe(true);
|
|
138
|
+
expect(isRef(result.error)).toBe(true);
|
|
139
|
+
|
|
140
|
+
// Check that refetch is a function
|
|
141
|
+
expect(typeof result.refetch).toBe("function");
|
|
142
|
+
|
|
143
|
+
// Check initial state values
|
|
144
|
+
expect(Array.isArray(result.users.value)).toBe(true);
|
|
145
|
+
expect(typeof result.loading.value).toBe("boolean");
|
|
146
|
+
expect(result.error.value === null || result.error.value instanceof Error).toBe(true);
|
|
147
|
+
},
|
|
148
|
+
),
|
|
149
|
+
{ numRuns: 100 },
|
|
150
|
+
);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("useProducts returns reactive state structure", () => {
|
|
154
|
+
fc.assert(
|
|
155
|
+
fc.property(
|
|
156
|
+
fc.record({
|
|
157
|
+
autoFetch: fc.boolean(),
|
|
158
|
+
limit: fc.option(fc.integer({ min: 1, max: 100 }), { nil: undefined }),
|
|
159
|
+
}),
|
|
160
|
+
(options) => {
|
|
161
|
+
const result = useProducts(options);
|
|
162
|
+
|
|
163
|
+
// Check that all required properties exist
|
|
164
|
+
expect(result).toHaveProperty("products");
|
|
165
|
+
expect(result).toHaveProperty("activeProducts");
|
|
166
|
+
expect(result).toHaveProperty("loading");
|
|
167
|
+
expect(result).toHaveProperty("error");
|
|
168
|
+
expect(result).toHaveProperty("refetch");
|
|
169
|
+
|
|
170
|
+
// Check that data properties are reactive refs
|
|
171
|
+
expect(isRef(result.products)).toBe(true);
|
|
172
|
+
expect(isRef(result.loading)).toBe(true);
|
|
173
|
+
expect(isRef(result.error)).toBe(true);
|
|
174
|
+
|
|
175
|
+
// Check that activeProducts is a computed ref
|
|
176
|
+
expect(isRef(result.activeProducts)).toBe(true);
|
|
177
|
+
|
|
178
|
+
// Check that refetch is a function
|
|
179
|
+
expect(typeof result.refetch).toBe("function");
|
|
180
|
+
|
|
181
|
+
// Check initial state values
|
|
182
|
+
expect(Array.isArray(result.products.value)).toBe(true);
|
|
183
|
+
expect(typeof result.loading.value).toBe("boolean");
|
|
184
|
+
expect(result.error.value === null || result.error.value instanceof Error).toBe(true);
|
|
185
|
+
},
|
|
186
|
+
),
|
|
187
|
+
{ numRuns: 100 },
|
|
188
|
+
);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Hooks Tests
|
|
3
|
+
*
|
|
4
|
+
* Basic smoke tests to verify hooks can be imported and have correct structure.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { describe, it, expect } from "vitest";
|
|
8
|
+
import { useBrands, useAccounts, useUsers, useProducts } from "./index";
|
|
9
|
+
|
|
10
|
+
describe("Data Hooks", () => {
|
|
11
|
+
describe("useBrands", () => {
|
|
12
|
+
it("should be a function", () => {
|
|
13
|
+
expect(typeof useBrands).toBe("function");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("should return expected properties", () => {
|
|
17
|
+
const result = useBrands({ autoFetch: false });
|
|
18
|
+
|
|
19
|
+
expect(result).toHaveProperty("brands");
|
|
20
|
+
expect(result).toHaveProperty("activeBrands");
|
|
21
|
+
expect(result).toHaveProperty("loading");
|
|
22
|
+
expect(result).toHaveProperty("error");
|
|
23
|
+
expect(result).toHaveProperty("refetch");
|
|
24
|
+
expect(typeof result.refetch).toBe("function");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("useAccounts", () => {
|
|
29
|
+
it("should be a function", () => {
|
|
30
|
+
expect(typeof useAccounts).toBe("function");
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("should return expected properties", () => {
|
|
34
|
+
const result = useAccounts({ autoFetch: false });
|
|
35
|
+
|
|
36
|
+
expect(result).toHaveProperty("accounts");
|
|
37
|
+
expect(result).toHaveProperty("loading");
|
|
38
|
+
expect(result).toHaveProperty("error");
|
|
39
|
+
expect(result).toHaveProperty("refetch");
|
|
40
|
+
expect(typeof result.refetch).toBe("function");
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("useUsers", () => {
|
|
45
|
+
it("should be a function", () => {
|
|
46
|
+
expect(typeof useUsers).toBe("function");
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should return expected properties", () => {
|
|
50
|
+
const result = useUsers({ autoFetch: false });
|
|
51
|
+
|
|
52
|
+
expect(result).toHaveProperty("users");
|
|
53
|
+
expect(result).toHaveProperty("loading");
|
|
54
|
+
expect(result).toHaveProperty("error");
|
|
55
|
+
expect(result).toHaveProperty("refetch");
|
|
56
|
+
expect(typeof result.refetch).toBe("function");
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("useProducts", () => {
|
|
61
|
+
it("should be a function", () => {
|
|
62
|
+
expect(typeof useProducts).toBe("function");
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should return expected properties", () => {
|
|
66
|
+
const result = useProducts({ autoFetch: false });
|
|
67
|
+
|
|
68
|
+
expect(result).toHaveProperty("products");
|
|
69
|
+
expect(result).toHaveProperty("activeProducts");
|
|
70
|
+
expect(result).toHaveProperty("loading");
|
|
71
|
+
expect(result).toHaveProperty("error");
|
|
72
|
+
expect(result).toHaveProperty("refetch");
|
|
73
|
+
expect(typeof result.refetch).toBe("function");
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Data Hooks for @htlkg/data
|
|
3
|
+
*
|
|
4
|
+
* Vue composables for fetching and managing data with reactive state.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Hook factory for creating custom data hooks
|
|
8
|
+
export {
|
|
9
|
+
createDataHook,
|
|
10
|
+
resetClientInstance,
|
|
11
|
+
type CreateDataHookOptions,
|
|
12
|
+
type BaseHookOptions,
|
|
13
|
+
type DataHookReturn,
|
|
14
|
+
type InferHookReturn,
|
|
15
|
+
} from "./createDataHook";
|
|
16
|
+
|
|
17
|
+
// Pre-built hooks for common models
|
|
18
|
+
export { useBrands, type UseBrandsOptions, type UseBrandsReturn } from "./useBrands";
|
|
19
|
+
export { useAccounts, type UseAccountsOptions, type UseAccountsReturn } from "./useAccounts";
|
|
20
|
+
export { useUsers, type UseUsersOptions, type UseUsersReturn } from "./useUsers";
|
|
21
|
+
export { useProducts, type UseProductsOptions, type UseProductsReturn } from "./useProducts";
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAccounts Hook
|
|
3
|
+
*
|
|
4
|
+
* Vue composable for fetching and managing account data with reactive state.
|
|
5
|
+
* Provides loading states, error handling, and refetch capabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Ref } from "vue";
|
|
9
|
+
import type { Account } from "@htlkg/core/types";
|
|
10
|
+
import { createDataHook, type BaseHookOptions } from "./createDataHook";
|
|
11
|
+
|
|
12
|
+
export interface UseAccountsOptions extends BaseHookOptions {
|
|
13
|
+
/** Filter criteria for accounts */
|
|
14
|
+
filter?: any;
|
|
15
|
+
/** Limit number of results */
|
|
16
|
+
limit?: number;
|
|
17
|
+
/** Auto-fetch on mount (default: true) */
|
|
18
|
+
autoFetch?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface UseAccountsReturn {
|
|
22
|
+
/** Reactive array of accounts */
|
|
23
|
+
accounts: Ref<Account[]>;
|
|
24
|
+
/** Loading state */
|
|
25
|
+
loading: Ref<boolean>;
|
|
26
|
+
/** Error state */
|
|
27
|
+
error: Ref<Error | null>;
|
|
28
|
+
/** Refetch accounts */
|
|
29
|
+
refetch: () => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Internal hook created by factory
|
|
34
|
+
*/
|
|
35
|
+
const useAccountsInternal = createDataHook<Account, UseAccountsOptions>({
|
|
36
|
+
model: "Account",
|
|
37
|
+
dataPropertyName: "accounts",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Composable for fetching and managing accounts
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* import { useAccounts } from '@htlkg/data/hooks';
|
|
46
|
+
*
|
|
47
|
+
* const { accounts, loading, error, refetch } = useAccounts();
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @example With filters
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const { accounts, loading } = useAccounts({
|
|
53
|
+
* filter: { status: { eq: 'active' } },
|
|
54
|
+
* limit: 50
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export function useAccounts(options: UseAccountsOptions = {}): UseAccountsReturn {
|
|
59
|
+
const result = useAccountsInternal(options);
|
|
60
|
+
return {
|
|
61
|
+
accounts: result.accounts as Ref<Account[]>,
|
|
62
|
+
loading: result.loading,
|
|
63
|
+
error: result.error,
|
|
64
|
+
refetch: result.refetch,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useBrands Hook
|
|
3
|
+
*
|
|
4
|
+
* Vue composable for fetching and managing brand data with reactive state.
|
|
5
|
+
* Provides loading states, error handling, and refetch capabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Ref, ComputedRef } from "vue";
|
|
9
|
+
import type { Brand } from "@htlkg/core/types";
|
|
10
|
+
import { createDataHook, type BaseHookOptions } from "./createDataHook";
|
|
11
|
+
|
|
12
|
+
export interface UseBrandsOptions extends BaseHookOptions {
|
|
13
|
+
/** Filter criteria for brands */
|
|
14
|
+
filter?: any;
|
|
15
|
+
/** Limit number of results */
|
|
16
|
+
limit?: number;
|
|
17
|
+
/** Auto-fetch on mount (default: true) */
|
|
18
|
+
autoFetch?: boolean;
|
|
19
|
+
/** Filter by account ID */
|
|
20
|
+
accountId?: string;
|
|
21
|
+
/** Only active brands */
|
|
22
|
+
activeOnly?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface UseBrandsReturn {
|
|
26
|
+
/** Reactive array of brands */
|
|
27
|
+
brands: Ref<Brand[]>;
|
|
28
|
+
/** Computed array of active brands only */
|
|
29
|
+
activeBrands: ComputedRef<Brand[]>;
|
|
30
|
+
/** Loading state */
|
|
31
|
+
loading: Ref<boolean>;
|
|
32
|
+
/** Error state */
|
|
33
|
+
error: Ref<Error | null>;
|
|
34
|
+
/** Refetch brands */
|
|
35
|
+
refetch: () => Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build filter from hook options
|
|
40
|
+
*/
|
|
41
|
+
function buildFilter(options: UseBrandsOptions): any {
|
|
42
|
+
let filter: any = options.filter || {};
|
|
43
|
+
|
|
44
|
+
if (options.accountId) {
|
|
45
|
+
filter = { ...filter, accountId: { eq: options.accountId } };
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (options.activeOnly) {
|
|
49
|
+
filter = { ...filter, status: { eq: "active" } };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return Object.keys(filter).length > 0 ? filter : undefined;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Internal hook created by factory
|
|
57
|
+
*/
|
|
58
|
+
const useBrandsInternal = createDataHook<Brand, UseBrandsOptions, { activeBrands: Brand[] }>({
|
|
59
|
+
model: "Brand",
|
|
60
|
+
dataPropertyName: "brands",
|
|
61
|
+
buildFilter,
|
|
62
|
+
computedProperties: {
|
|
63
|
+
activeBrands: (brands) => brands.filter((b) => b.status === "active"),
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Composable for fetching and managing brands
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* import { useBrands } from '@htlkg/data/hooks';
|
|
73
|
+
*
|
|
74
|
+
* const { brands, loading, error, refetch } = useBrands();
|
|
75
|
+
* ```
|
|
76
|
+
*
|
|
77
|
+
* @example With filters
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const { brands, loading } = useBrands({
|
|
80
|
+
* accountId: 'account-123',
|
|
81
|
+
* activeOnly: true,
|
|
82
|
+
* limit: 50
|
|
83
|
+
* });
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export function useBrands(options: UseBrandsOptions = {}): UseBrandsReturn {
|
|
87
|
+
const result = useBrandsInternal(options);
|
|
88
|
+
return {
|
|
89
|
+
brands: result.brands as Ref<Brand[]>,
|
|
90
|
+
activeBrands: result.activeBrands as ComputedRef<Brand[]>,
|
|
91
|
+
loading: result.loading,
|
|
92
|
+
error: result.error,
|
|
93
|
+
refetch: result.refetch,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useProducts Hook
|
|
3
|
+
*
|
|
4
|
+
* Vue composable for fetching and managing product data with reactive state.
|
|
5
|
+
* Provides loading states, error handling, and refetch capabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Ref, ComputedRef } from "vue";
|
|
9
|
+
import type { Product } from "@htlkg/core/types";
|
|
10
|
+
import { createDataHook, type BaseHookOptions } from "./createDataHook";
|
|
11
|
+
|
|
12
|
+
export interface UseProductsOptions extends BaseHookOptions {
|
|
13
|
+
/** Filter criteria for products */
|
|
14
|
+
filter?: any;
|
|
15
|
+
/** Limit number of results */
|
|
16
|
+
limit?: number;
|
|
17
|
+
/** Auto-fetch on mount (default: true) */
|
|
18
|
+
autoFetch?: boolean;
|
|
19
|
+
/** Only active products */
|
|
20
|
+
activeOnly?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface UseProductsReturn {
|
|
24
|
+
/** Reactive array of products */
|
|
25
|
+
products: Ref<Product[]>;
|
|
26
|
+
/** Computed array of active products only */
|
|
27
|
+
activeProducts: ComputedRef<Product[]>;
|
|
28
|
+
/** Loading state */
|
|
29
|
+
loading: Ref<boolean>;
|
|
30
|
+
/** Error state */
|
|
31
|
+
error: Ref<Error | null>;
|
|
32
|
+
/** Refetch products */
|
|
33
|
+
refetch: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build filter from hook options
|
|
38
|
+
*/
|
|
39
|
+
function buildFilter(options: UseProductsOptions): any {
|
|
40
|
+
let filter: any = options.filter || {};
|
|
41
|
+
|
|
42
|
+
if (options.activeOnly) {
|
|
43
|
+
filter = { ...filter, isActive: { eq: true } };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return Object.keys(filter).length > 0 ? filter : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Internal hook created by factory
|
|
51
|
+
*/
|
|
52
|
+
const useProductsInternal = createDataHook<Product, UseProductsOptions, { activeProducts: Product[] }>({
|
|
53
|
+
model: "Product",
|
|
54
|
+
dataPropertyName: "products",
|
|
55
|
+
buildFilter,
|
|
56
|
+
computedProperties: {
|
|
57
|
+
activeProducts: (products) => products.filter((p) => p.isActive === true),
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Composable for fetching and managing products
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```typescript
|
|
66
|
+
* import { useProducts } from '@htlkg/data/hooks';
|
|
67
|
+
*
|
|
68
|
+
* const { products, loading, error, refetch } = useProducts();
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @example With filters
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const { products, loading } = useProducts({
|
|
74
|
+
* activeOnly: true,
|
|
75
|
+
* limit: 50
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export function useProducts(options: UseProductsOptions = {}): UseProductsReturn {
|
|
80
|
+
const result = useProductsInternal(options);
|
|
81
|
+
return {
|
|
82
|
+
products: result.products as Ref<Product[]>,
|
|
83
|
+
activeProducts: result.activeProducts as ComputedRef<Product[]>,
|
|
84
|
+
loading: result.loading,
|
|
85
|
+
error: result.error,
|
|
86
|
+
refetch: result.refetch,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useUsers Hook
|
|
3
|
+
*
|
|
4
|
+
* Vue composable for fetching and managing user data with reactive state.
|
|
5
|
+
* Provides loading states, error handling, and refetch capabilities.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Ref } from "vue";
|
|
9
|
+
import type { User } from "@htlkg/core/types";
|
|
10
|
+
import { createDataHook, type BaseHookOptions } from "./createDataHook";
|
|
11
|
+
|
|
12
|
+
export interface UseUsersOptions extends BaseHookOptions {
|
|
13
|
+
/** Filter criteria for users */
|
|
14
|
+
filter?: any;
|
|
15
|
+
/** Limit number of results */
|
|
16
|
+
limit?: number;
|
|
17
|
+
/** Auto-fetch on mount (default: true) */
|
|
18
|
+
autoFetch?: boolean;
|
|
19
|
+
/** Filter by brand ID */
|
|
20
|
+
brandId?: string;
|
|
21
|
+
/** Filter by account ID */
|
|
22
|
+
accountId?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface UseUsersReturn {
|
|
26
|
+
/** Reactive array of users */
|
|
27
|
+
users: Ref<User[]>;
|
|
28
|
+
/** Loading state */
|
|
29
|
+
loading: Ref<boolean>;
|
|
30
|
+
/** Error state */
|
|
31
|
+
error: Ref<Error | null>;
|
|
32
|
+
/** Refetch users */
|
|
33
|
+
refetch: () => Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Build filter from hook options
|
|
38
|
+
*/
|
|
39
|
+
function buildFilter(options: UseUsersOptions): any {
|
|
40
|
+
let filter: any = options.filter || {};
|
|
41
|
+
|
|
42
|
+
if (options.brandId) {
|
|
43
|
+
filter = { ...filter, brandIds: { contains: options.brandId } };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (options.accountId) {
|
|
47
|
+
filter = { ...filter, accountIds: { contains: options.accountId } };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return Object.keys(filter).length > 0 ? filter : undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Internal hook created by factory
|
|
55
|
+
*/
|
|
56
|
+
const useUsersInternal = createDataHook<User, UseUsersOptions>({
|
|
57
|
+
model: "User",
|
|
58
|
+
dataPropertyName: "users",
|
|
59
|
+
buildFilter,
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Composable for fetching and managing users
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* import { useUsers } from '@htlkg/data/hooks';
|
|
68
|
+
*
|
|
69
|
+
* const { users, loading, error, refetch } = useUsers();
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* @example With filters
|
|
73
|
+
* ```typescript
|
|
74
|
+
* const { users, loading } = useUsers({
|
|
75
|
+
* brandId: 'brand-123',
|
|
76
|
+
* accountId: 'account-456',
|
|
77
|
+
* limit: 50
|
|
78
|
+
* });
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export function useUsers(options: UseUsersOptions = {}): UseUsersReturn {
|
|
82
|
+
const result = useUsersInternal(options);
|
|
83
|
+
return {
|
|
84
|
+
users: result.users as Ref<User[]>,
|
|
85
|
+
loading: result.loading,
|
|
86
|
+
error: result.error,
|
|
87
|
+
refetch: result.refetch,
|
|
88
|
+
};
|
|
89
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @htlkg/data - Data Layer Module
|
|
3
|
+
*
|
|
4
|
+
* Provides GraphQL operations, Amplify integration, and reactive data hooks
|
|
5
|
+
* for the Hotelinking multi-tenant SaaS platform.
|
|
6
|
+
*
|
|
7
|
+
* @module @htlkg/data
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Client exports
|
|
11
|
+
export {
|
|
12
|
+
generateClient,
|
|
13
|
+
generateServerClient,
|
|
14
|
+
getSharedClient,
|
|
15
|
+
resetSharedClient,
|
|
16
|
+
type AstroAmplifyConfig,
|
|
17
|
+
} from "./client";
|
|
18
|
+
|
|
19
|
+
// Query exports
|
|
20
|
+
export * from "./queries";
|
|
21
|
+
|
|
22
|
+
// Mutation exports
|
|
23
|
+
export * from "./mutations";
|
|
24
|
+
|
|
25
|
+
// Hook exports
|
|
26
|
+
export * from "./hooks";
|
|
27
|
+
|
|
28
|
+
// Store factory exports
|
|
29
|
+
export * from "./stores";
|
|
30
|
+
|
|
31
|
+
// Content Collections exports (will be populated in task 3.8)
|
|
32
|
+
// export * from "./content-collections";
|