@flamingo-stack/openframe-frontend-core 0.0.203 → 0.0.204-snapshot.20260523020255
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/chunk-27APPAJN.cjs +24 -0
- package/dist/chunk-27APPAJN.cjs.map +1 -0
- package/dist/{chunk-XGL5FKIK.js → chunk-D6BNDYZK.js} +114 -4
- package/dist/chunk-D6BNDYZK.js.map +1 -0
- package/dist/chunk-MJNXIEV2.js +24 -0
- package/dist/chunk-MJNXIEV2.js.map +1 -0
- package/dist/{chunk-25LVV26X.cjs → chunk-NLXY4QZE.cjs} +114 -4
- package/dist/chunk-NLXY4QZE.cjs.map +1 -0
- package/dist/{chunk-E6Q6UGDK.js → chunk-OFCRX24Y.js} +383 -569
- package/dist/chunk-OFCRX24Y.js.map +1 -0
- package/dist/{chunk-3YH2M76N.cjs → chunk-RDXOQWBY.cjs} +556 -742
- package/dist/chunk-RDXOQWBY.cjs.map +1 -0
- package/dist/components/announcement-bar.d.ts.map +1 -1
- package/dist/components/features/index.cjs +4 -3
- package/dist/components/features/index.cjs.map +1 -1
- package/dist/components/features/index.js +3 -2
- package/dist/components/index.cjs +4 -3
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +3 -2
- package/dist/components/navigation/index.cjs +4 -3
- package/dist/components/navigation/index.cjs.map +1 -1
- package/dist/components/navigation/index.js +3 -2
- package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
- package/dist/components/shared/product-release/product-release-card-skeleton.d.ts +1 -1
- package/dist/components/shared/product-release/product-release-card-skeleton.d.ts.map +1 -1
- package/dist/components/shared/product-release/product-release-card.d.ts +19 -12
- package/dist/components/shared/product-release/product-release-card.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +4 -3
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +3 -2
- package/dist/contexts/endpoints-runtime-context.d.ts +28 -0
- package/dist/contexts/endpoints-runtime-context.d.ts.map +1 -0
- package/dist/contexts/index.cjs +13 -0
- package/dist/contexts/index.cjs.map +1 -0
- package/dist/contexts/index.d.ts +24 -0
- package/dist/contexts/index.d.ts.map +1 -0
- package/dist/contexts/index.js +13 -0
- package/dist/contexts/index.js.map +1 -0
- package/dist/hooks/index.cjs +5 -2
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -1
- package/dist/hooks/use-access-code-integration.d.ts +48 -0
- package/dist/hooks/use-access-code-integration.d.ts.map +1 -0
- package/dist/hooks/use-contact-submission.d.ts.map +1 -1
- package/dist/index.cjs +4 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +7 -6
- package/dist/utils/access-code-client.d.ts +21 -37
- package/dist/utils/access-code-client.d.ts.map +1 -1
- package/dist/utils/index.cjs +10 -51
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +11 -47
- package/dist/utils/index.js.map +1 -1
- package/package.json +7 -1
- package/src/components/announcement-bar.tsx +25 -3
- package/src/components/navigation/navigation-sidebar.tsx +3 -1
- package/src/components/shared/product-release/product-release-card-skeleton.tsx +66 -70
- package/src/components/shared/product-release/product-release-card.tsx +194 -242
- package/src/contexts/endpoints-runtime-context.tsx +68 -0
- package/src/contexts/index.ts +29 -0
- package/src/hooks/index.ts +6 -0
- package/src/hooks/use-access-code-integration.ts +107 -0
- package/src/hooks/use-contact-submission.ts +6 -2
- package/src/utils/access-code-client.ts +32 -75
- package/dist/chunk-25LVV26X.cjs.map +0 -1
- package/dist/chunk-3YH2M76N.cjs.map +0 -1
- package/dist/chunk-E6Q6UGDK.js.map +0 -1
- package/dist/chunk-XGL5FKIK.js.map +0 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Access Code Integration Hook
|
|
5
|
+
*
|
|
6
|
+
* React-side wrapper around the pure `access-code-client` utilities.
|
|
7
|
+
* Lives in `hooks/` (client bundle) so the `createContext()` call in
|
|
8
|
+
* `endpoints-runtime-context` doesn't get pulled into the server-safe
|
|
9
|
+
* `utils/index` bundle.
|
|
10
|
+
*
|
|
11
|
+
* The pure standalone functions (`validateAccessCode`,
|
|
12
|
+
* `consumeAccessCode`, `validateAndConsumeAccessCode`) remain importable
|
|
13
|
+
* from `@flamingo-stack/openframe-frontend-core/utils` — they take the
|
|
14
|
+
* endpoints object as an argument. This hook binds those endpoints from
|
|
15
|
+
* `EndpointsRuntimeContext` so React callers don't have to plumb URLs.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import React from 'react'
|
|
19
|
+
|
|
20
|
+
import { useRequiredEndpointsRuntime } from '../contexts/endpoints-runtime-context'
|
|
21
|
+
import {
|
|
22
|
+
validateAccessCode,
|
|
23
|
+
consumeAccessCode,
|
|
24
|
+
validateAndConsumeAccessCode,
|
|
25
|
+
} from '../utils/access-code-client'
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Resolves access-code endpoints from `EndpointsRuntimeContext` (throws
|
|
29
|
+
* if no provider is mounted) and exposes loading-state-aware wrappers
|
|
30
|
+
* around the standalone helpers in `utils/access-code-client`.
|
|
31
|
+
*
|
|
32
|
+
* @returns the following fields. The `validate` / `consume` /
|
|
33
|
+
* `validateAndConsume` functions and the returned object identity are
|
|
34
|
+
* NOT memoized — they're re-created each render. Wrap with
|
|
35
|
+
* `useCallback` / `useMemo` at the call site if downstream effect
|
|
36
|
+
* dep arrays depend on stable identities.
|
|
37
|
+
* - `validate(email, code)`: validates only.
|
|
38
|
+
* - `consume(email, code)`: consumes only.
|
|
39
|
+
* - `validateAndConsume(email, code)`: one-step validate-then-consume.
|
|
40
|
+
* - `isValidating: boolean`: a validate call is in flight.
|
|
41
|
+
* - `isConsuming: boolean`: a consume call is in flight.
|
|
42
|
+
* - `isProcessing: boolean`: convenience — `isValidating || isConsuming`.
|
|
43
|
+
* Use this for a single "in-flight" indicator on UI affordances that
|
|
44
|
+
* should disable during both phases.
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* const { validate, consume, isProcessing } = useAccessCodeIntegration();
|
|
48
|
+
*
|
|
49
|
+
* const handleRegistration = async (formData) => {
|
|
50
|
+
* const validation = await validate(formData.email, formData.accessCode);
|
|
51
|
+
* if (!validation.valid) {
|
|
52
|
+
* setError(validation.message);
|
|
53
|
+
* return;
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* // Process registration...
|
|
57
|
+
* const registrationResult = await registerUser(formData);
|
|
58
|
+
*
|
|
59
|
+
* if (registrationResult.success) {
|
|
60
|
+
* await consume(formData.email, formData.accessCode);
|
|
61
|
+
* }
|
|
62
|
+
* };
|
|
63
|
+
*/
|
|
64
|
+
export function useAccessCodeIntegration() {
|
|
65
|
+
const runtime = useRequiredEndpointsRuntime()
|
|
66
|
+
const endpoints = runtime.accessCode
|
|
67
|
+
const [isValidating, setIsValidating] = React.useState(false)
|
|
68
|
+
const [isConsuming, setIsConsuming] = React.useState(false)
|
|
69
|
+
|
|
70
|
+
const validate = async (email: string, code: string) => {
|
|
71
|
+
setIsValidating(true)
|
|
72
|
+
try {
|
|
73
|
+
return await validateAccessCode(email, code, endpoints)
|
|
74
|
+
} finally {
|
|
75
|
+
setIsValidating(false)
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const consume = async (email: string, code: string) => {
|
|
80
|
+
setIsConsuming(true)
|
|
81
|
+
try {
|
|
82
|
+
return await consumeAccessCode(email, code, endpoints)
|
|
83
|
+
} finally {
|
|
84
|
+
setIsConsuming(false)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const validateAndConsume = async (email: string, code: string) => {
|
|
89
|
+
setIsValidating(true)
|
|
90
|
+
setIsConsuming(true)
|
|
91
|
+
try {
|
|
92
|
+
return await validateAndConsumeAccessCode(email, code, endpoints)
|
|
93
|
+
} finally {
|
|
94
|
+
setIsValidating(false)
|
|
95
|
+
setIsConsuming(false)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
validate,
|
|
101
|
+
consume,
|
|
102
|
+
validateAndConsume,
|
|
103
|
+
isValidating,
|
|
104
|
+
isConsuming,
|
|
105
|
+
isProcessing: isValidating || isConsuming,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useState, useCallback, useEffect } from 'react';
|
|
2
2
|
import { useToast } from "./use-toast";
|
|
3
3
|
import { useRouter } from 'next/navigation';
|
|
4
|
+
import { useRequiredEndpointsRuntime } from '../contexts/endpoints-runtime-context';
|
|
4
5
|
|
|
5
6
|
interface ContactSubmissionOptions {
|
|
6
7
|
userId?: string;
|
|
@@ -44,6 +45,9 @@ export function useContactSubmission(options: ContactSubmissionOptions = {}) {
|
|
|
44
45
|
const { userId, successRedirectUrl, successToastMessage, onSuccess } = options;
|
|
45
46
|
const { toast } = useToast();
|
|
46
47
|
const router = useRouter();
|
|
48
|
+
// Endpoint URL injected via context — hub provides hub default, embedded
|
|
49
|
+
// app provides its proxied path. Throws if no provider is mounted.
|
|
50
|
+
const { contactUrl } = useRequiredEndpointsRuntime();
|
|
47
51
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
48
52
|
const [isSuccess, setIsSuccess] = useState(false);
|
|
49
53
|
|
|
@@ -53,7 +57,7 @@ export function useContactSubmission(options: ContactSubmissionOptions = {}) {
|
|
|
53
57
|
setIsSubmitting(true);
|
|
54
58
|
|
|
55
59
|
try {
|
|
56
|
-
const response = await fetch(
|
|
60
|
+
const response = await fetch(contactUrl, {
|
|
57
61
|
method: 'POST',
|
|
58
62
|
headers: { 'Content-Type': 'application/json' },
|
|
59
63
|
body: JSON.stringify({
|
|
@@ -90,7 +94,7 @@ export function useContactSubmission(options: ContactSubmissionOptions = {}) {
|
|
|
90
94
|
} finally {
|
|
91
95
|
setIsSubmitting(false);
|
|
92
96
|
}
|
|
93
|
-
}, [isSubmitting, toast, userId, successToastMessage]);
|
|
97
|
+
}, [isSubmitting, toast, userId, successToastMessage, contactUrl]);
|
|
94
98
|
|
|
95
99
|
// Handle redirect after success
|
|
96
100
|
useEffect(() => {
|
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Access Code Client Utilities
|
|
2
|
+
* Access Code Client Utilities — pure standalone functions.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Endpoint paths are NOT hardcoded — every function takes an
|
|
5
|
+
* `endpoints` argument. The React-side wrapper that binds them from
|
|
6
|
+
* `EndpointsRuntimeContext` lives separately at
|
|
7
|
+
* `hooks/use-access-code-integration.ts` (`useAccessCodeIntegration`).
|
|
8
|
+
*
|
|
9
|
+
* Keep this file **free of React imports** — it lives in the
|
|
10
|
+
* server-safe `utils/index` tsup bundle. Any module-top-level call
|
|
11
|
+
* into `createContext()` (which the runtime context file does) would
|
|
12
|
+
* be pulled into the server bundle and crash SSR with
|
|
13
|
+
* `createContext is not a function`.
|
|
6
14
|
*/
|
|
7
15
|
|
|
8
|
-
import React from 'react';
|
|
9
16
|
import {
|
|
10
17
|
AccessCodeValidation,
|
|
11
18
|
AccessCodeValidationResponse,
|
|
12
19
|
AccessCodeConsumptionResponse
|
|
13
20
|
} from '../types/access-code-cohorts';
|
|
14
21
|
|
|
22
|
+
/** Endpoints required by the standalone client utilities. The
|
|
23
|
+
* `useAccessCodeIntegration` hook (in `hooks/`) resolves these from
|
|
24
|
+
* `EndpointsRuntimeContext.accessCode` automatically. */
|
|
25
|
+
export interface AccessCodeEndpoints {
|
|
26
|
+
validateUrl: string
|
|
27
|
+
consumeUrl: string
|
|
28
|
+
}
|
|
29
|
+
|
|
15
30
|
/**
|
|
16
31
|
* Validate an access code for a given email
|
|
17
32
|
*
|
|
@@ -31,10 +46,11 @@ import {
|
|
|
31
46
|
*/
|
|
32
47
|
export async function validateAccessCode(
|
|
33
48
|
email: string,
|
|
34
|
-
code: string
|
|
49
|
+
code: string,
|
|
50
|
+
endpoints: AccessCodeEndpoints,
|
|
35
51
|
): Promise<AccessCodeValidationResponse> {
|
|
36
52
|
try {
|
|
37
|
-
const response = await fetch(
|
|
53
|
+
const response = await fetch(endpoints.validateUrl, {
|
|
38
54
|
method: 'POST',
|
|
39
55
|
headers: {
|
|
40
56
|
'Content-Type': 'application/json',
|
|
@@ -77,10 +93,11 @@ export async function validateAccessCode(
|
|
|
77
93
|
*/
|
|
78
94
|
export async function consumeAccessCode(
|
|
79
95
|
email: string,
|
|
80
|
-
code: string
|
|
96
|
+
code: string,
|
|
97
|
+
endpoints: AccessCodeEndpoints,
|
|
81
98
|
): Promise<AccessCodeConsumptionResponse> {
|
|
82
99
|
try {
|
|
83
|
-
const response = await fetch(
|
|
100
|
+
const response = await fetch(endpoints.consumeUrl, {
|
|
84
101
|
method: 'POST',
|
|
85
102
|
headers: {
|
|
86
103
|
'Content-Type': 'application/json',
|
|
@@ -125,17 +142,18 @@ export async function consumeAccessCode(
|
|
|
125
142
|
*/
|
|
126
143
|
export async function validateAndConsumeAccessCode(
|
|
127
144
|
email: string,
|
|
128
|
-
code: string
|
|
145
|
+
code: string,
|
|
146
|
+
endpoints: AccessCodeEndpoints,
|
|
129
147
|
): Promise<AccessCodeValidationResponse & { consumed?: boolean }> {
|
|
130
148
|
// First validate
|
|
131
|
-
const validation = await validateAccessCode(email, code);
|
|
149
|
+
const validation = await validateAccessCode(email, code, endpoints);
|
|
132
150
|
|
|
133
151
|
if (!validation.valid) {
|
|
134
152
|
return validation;
|
|
135
153
|
}
|
|
136
154
|
|
|
137
155
|
// If valid, consume the code
|
|
138
|
-
const consumption = await consumeAccessCode(email, code);
|
|
156
|
+
const consumption = await consumeAccessCode(email, code, endpoints);
|
|
139
157
|
|
|
140
158
|
return {
|
|
141
159
|
...validation,
|
|
@@ -146,67 +164,6 @@ export async function validateAndConsumeAccessCode(
|
|
|
146
164
|
};
|
|
147
165
|
}
|
|
148
166
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
* @example
|
|
153
|
-
* const { validate, consume, isValidating, isConsuming } = useAccessCodeIntegration();
|
|
154
|
-
*
|
|
155
|
-
* const handleRegistration = async (formData) => {
|
|
156
|
-
* const validation = await validate(formData.email, formData.accessCode);
|
|
157
|
-
* if (!validation.valid) {
|
|
158
|
-
* setError(validation.message);
|
|
159
|
-
* return;
|
|
160
|
-
* }
|
|
161
|
-
*
|
|
162
|
-
* // Process registration...
|
|
163
|
-
* const registrationResult = await registerUser(formData);
|
|
164
|
-
*
|
|
165
|
-
* if (registrationResult.success) {
|
|
166
|
-
* await consume(formData.email, formData.accessCode);
|
|
167
|
-
* }
|
|
168
|
-
* };
|
|
169
|
-
*/
|
|
170
|
-
export function useAccessCodeIntegration() {
|
|
171
|
-
const [isValidating, setIsValidating] = React.useState(false);
|
|
172
|
-
const [isConsuming, setIsConsuming] = React.useState(false);
|
|
173
|
-
|
|
174
|
-
const validate = async (email: string, code: string) => {
|
|
175
|
-
setIsValidating(true);
|
|
176
|
-
try {
|
|
177
|
-
return await validateAccessCode(email, code);
|
|
178
|
-
} finally {
|
|
179
|
-
setIsValidating(false);
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
const consume = async (email: string, code: string) => {
|
|
184
|
-
setIsConsuming(true);
|
|
185
|
-
try {
|
|
186
|
-
return await consumeAccessCode(email, code);
|
|
187
|
-
} finally {
|
|
188
|
-
setIsConsuming(false);
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const validateAndConsume = async (email: string, code: string) => {
|
|
193
|
-
setIsValidating(true);
|
|
194
|
-
setIsConsuming(true);
|
|
195
|
-
try {
|
|
196
|
-
return await validateAndConsumeAccessCode(email, code);
|
|
197
|
-
} finally {
|
|
198
|
-
setIsValidating(false);
|
|
199
|
-
setIsConsuming(false);
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
return {
|
|
204
|
-
validate,
|
|
205
|
-
consume,
|
|
206
|
-
validateAndConsume,
|
|
207
|
-
isValidating,
|
|
208
|
-
isConsuming,
|
|
209
|
-
isProcessing: isValidating || isConsuming,
|
|
210
|
-
};
|
|
211
|
-
}
|
|
212
|
-
|
|
167
|
+
// `useAccessCodeIntegration` (the React-side wrapper) lives in
|
|
168
|
+
// `hooks/use-access-code-integration.ts`. It binds the endpoints from
|
|
169
|
+
// `EndpointsRuntimeContext` so React callers don't have to plumb URLs.
|