@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.
Files changed (70) hide show
  1. package/dist/chunk-27APPAJN.cjs +24 -0
  2. package/dist/chunk-27APPAJN.cjs.map +1 -0
  3. package/dist/{chunk-XGL5FKIK.js → chunk-D6BNDYZK.js} +114 -4
  4. package/dist/chunk-D6BNDYZK.js.map +1 -0
  5. package/dist/chunk-MJNXIEV2.js +24 -0
  6. package/dist/chunk-MJNXIEV2.js.map +1 -0
  7. package/dist/{chunk-25LVV26X.cjs → chunk-NLXY4QZE.cjs} +114 -4
  8. package/dist/chunk-NLXY4QZE.cjs.map +1 -0
  9. package/dist/{chunk-E6Q6UGDK.js → chunk-OFCRX24Y.js} +383 -569
  10. package/dist/chunk-OFCRX24Y.js.map +1 -0
  11. package/dist/{chunk-3YH2M76N.cjs → chunk-RDXOQWBY.cjs} +556 -742
  12. package/dist/chunk-RDXOQWBY.cjs.map +1 -0
  13. package/dist/components/announcement-bar.d.ts.map +1 -1
  14. package/dist/components/features/index.cjs +4 -3
  15. package/dist/components/features/index.cjs.map +1 -1
  16. package/dist/components/features/index.js +3 -2
  17. package/dist/components/index.cjs +4 -3
  18. package/dist/components/index.cjs.map +1 -1
  19. package/dist/components/index.js +3 -2
  20. package/dist/components/navigation/index.cjs +4 -3
  21. package/dist/components/navigation/index.cjs.map +1 -1
  22. package/dist/components/navigation/index.js +3 -2
  23. package/dist/components/navigation/navigation-sidebar.d.ts.map +1 -1
  24. package/dist/components/shared/product-release/product-release-card-skeleton.d.ts +1 -1
  25. package/dist/components/shared/product-release/product-release-card-skeleton.d.ts.map +1 -1
  26. package/dist/components/shared/product-release/product-release-card.d.ts +19 -12
  27. package/dist/components/shared/product-release/product-release-card.d.ts.map +1 -1
  28. package/dist/components/ui/index.cjs +4 -3
  29. package/dist/components/ui/index.cjs.map +1 -1
  30. package/dist/components/ui/index.js +3 -2
  31. package/dist/contexts/endpoints-runtime-context.d.ts +28 -0
  32. package/dist/contexts/endpoints-runtime-context.d.ts.map +1 -0
  33. package/dist/contexts/index.cjs +13 -0
  34. package/dist/contexts/index.cjs.map +1 -0
  35. package/dist/contexts/index.d.ts +24 -0
  36. package/dist/contexts/index.d.ts.map +1 -0
  37. package/dist/contexts/index.js +13 -0
  38. package/dist/contexts/index.js.map +1 -0
  39. package/dist/hooks/index.cjs +5 -2
  40. package/dist/hooks/index.cjs.map +1 -1
  41. package/dist/hooks/index.d.ts +1 -0
  42. package/dist/hooks/index.d.ts.map +1 -1
  43. package/dist/hooks/index.js +4 -1
  44. package/dist/hooks/use-access-code-integration.d.ts +48 -0
  45. package/dist/hooks/use-access-code-integration.d.ts.map +1 -0
  46. package/dist/hooks/use-contact-submission.d.ts.map +1 -1
  47. package/dist/index.cjs +4 -3
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.js +7 -6
  50. package/dist/utils/access-code-client.d.ts +21 -37
  51. package/dist/utils/access-code-client.d.ts.map +1 -1
  52. package/dist/utils/index.cjs +10 -51
  53. package/dist/utils/index.cjs.map +1 -1
  54. package/dist/utils/index.js +11 -47
  55. package/dist/utils/index.js.map +1 -1
  56. package/package.json +7 -1
  57. package/src/components/announcement-bar.tsx +25 -3
  58. package/src/components/navigation/navigation-sidebar.tsx +3 -1
  59. package/src/components/shared/product-release/product-release-card-skeleton.tsx +66 -70
  60. package/src/components/shared/product-release/product-release-card.tsx +194 -242
  61. package/src/contexts/endpoints-runtime-context.tsx +68 -0
  62. package/src/contexts/index.ts +29 -0
  63. package/src/hooks/index.ts +6 -0
  64. package/src/hooks/use-access-code-integration.ts +107 -0
  65. package/src/hooks/use-contact-submission.ts +6 -2
  66. package/src/utils/access-code-client.ts +32 -75
  67. package/dist/chunk-25LVV26X.cjs.map +0 -1
  68. package/dist/chunk-3YH2M76N.cjs.map +0 -1
  69. package/dist/chunk-E6Q6UGDK.js.map +0 -1
  70. 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('/api/contact', {
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
- * Simple client-side utilities for integrating access code validation
5
- * and consumption into registration forms.
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('/api/validate-access-code', {
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('/api/consume-access-code', {
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
- * Access Code Integration Hook (for React components)
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.