Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/old-banks-hope.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@asgardeo/react': patch
---

Fix Sign up
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,6 @@ const createAuthComponentFromFlow = (
): ReactElement | null => {
const key: string | number = options.key || component.id;

if (authType === 'signin') {
console.log('Creating sign-in component for:', component);
}

switch (component.type) {
case EmbeddedFlowComponentType.TextInput:
case EmbeddedFlowComponentType.PasswordInput:
Expand Down Expand Up @@ -173,9 +169,8 @@ const createAuthComponentFromFlow = (
if (options.onSubmit) {
const formData: Record<string, any> = {};
Object.keys(formValues).forEach(field => {
if (formValues[field]) {
formData[field] = formValues[field];
}
// Include all values, even empty strings, to ensure proper submission
formData[field] = formValues[field];
});
options.onSubmit(component, formData, shouldSkipValidation);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,12 @@ const BaseSignUpContent: FC<BaseSignUpProps> = ({
const processComponents = (comps: any[]) => {
comps.forEach(component => {
if (component.type === EmbeddedFlowComponentType.TextInput) {
// Use component.ref (mapped identifier) as the field name instead of component.id
// This ensures form field names match what the input components use
const fieldName = component.ref || component.id;

fields.push({
name: component.id,
name: fieldName,
required: component.required || false,
initialValue: '',
validator: (value: string) => {
Expand Down Expand Up @@ -457,7 +461,7 @@ const BaseSignUpContent: FC<BaseSignUpProps> = ({
const payload: EmbeddedFlowExecuteRequestPayload = {
...(currentFlow.flowId && {flowId: currentFlow.flowId}),
flowType: (currentFlow as any).flowType || 'REGISTRATION',
...(component.id && {action: component.id}),
...(component.id && {actionId: component.id}),
inputs: filteredInputs,
} as any;

Expand Down
106 changes: 104 additions & 2 deletions packages/react/src/utils/v2/flowTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,101 @@ export interface FlowTransformOptions {
resolveTranslations?: boolean;
}

/**
* Create a mapping from ref to identifier based on data.inputs array.
* This handles cases where meta.components use 'ref' to reference inputs,
* and data.inputs contain the actual 'identifier' field.
*
* @param response - The flow response object
* @returns Map of ref to identifier
*/
const createInputRefMapping = (response: any): Map<string, string> => {
const mapping = new Map<string, string>();

if (response?.data?.inputs && Array.isArray(response.data.inputs)) {
response.data.inputs.forEach((input: any) => {
if (input.ref && input.identifier) {
mapping.set(input.ref, input.identifier);
}
});
}

return mapping;
};

/**
* Create a mapping from action ref to nextNode based on data.actions array.
* This handles cases where meta.components reference actions by ref,
* and data.actions contain the actual nextNode field for routing.
*
* @param response - The flow response object
* @returns Map of action ref to nextNode
*/
const createActionRefMapping = (response: any): Map<string, string> => {
const mapping = new Map<string, string>();

if (response?.data?.actions && Array.isArray(response.data.actions)) {
response.data.actions.forEach((action: any) => {
if (action.ref && action.nextNode) {
mapping.set(action.ref, action.nextNode);
}
});
}

return mapping;
};

/**
* Apply input ref mapping to components recursively.
* This ensures that component.ref values are mapped to the correct identifier
* from data.inputs, enabling proper form submission.
*
* @param components - Array of components to transform
* @param refMapping - Map of ref to identifier
* @param actionMapping - Map of action ref to nextNode
* @returns Transformed components with correct identifiers and action references
*/
const applyInputRefMapping = (
components: EmbeddedFlowComponent[],
refMapping: Map<string, string>,
actionMapping: Map<string, string>,
): EmbeddedFlowComponent[] => {
return components.map(component => {
const transformedComponent = {...component} as EmbeddedFlowComponent & {actionRef?: string};

// If this component has a ref that maps to an identifier, update it
if (transformedComponent.ref && refMapping.has(transformedComponent.ref)) {
transformedComponent.ref = refMapping.get(transformedComponent.ref);
}

// If this is an action component, map its id to the nextNode
// Store the nextNode reference as actionRef property for later use
if (
transformedComponent.type === 'ACTION' &&
transformedComponent.id &&
actionMapping.has(transformedComponent.id)
) {
transformedComponent.actionRef = actionMapping.get(transformedComponent.id);
}

// Recursively apply to nested components
if (transformedComponent.components && Array.isArray(transformedComponent.components)) {
transformedComponent.components = applyInputRefMapping(
transformedComponent.components,
refMapping,
actionMapping,
);
}

return transformedComponent;
});
};

/**
* Transform and resolve translations in components from flow response.
* This function extracts components from the response meta structure and optionally resolves
* any translation strings within them.
* any translation strings within them. It also handles mapping of input refs to identifiers
* and action refs to nextNode values.
*
* @param response - The flow response object containing components in meta structure
* @param t - Translation function from useTranslation hook
Expand All @@ -98,7 +189,18 @@ export const transformComponents = (
return [];
}

const components: EmbeddedFlowComponent[] = response.data.meta.components;
let components: EmbeddedFlowComponent[] = response.data.meta.components;

// Create mapping from ref to identifier based on data.inputs
const refMapping = createInputRefMapping(response);

// Create mapping from action ref to nextNode based on data.actions
const actionMapping = createActionRefMapping(response);

// Apply ref and action mapping if there are any mappings
if (refMapping.size > 0 || actionMapping.size > 0) {
components = applyInputRefMapping(components, refMapping, actionMapping);
}

return resolveTranslations ? resolveTranslationsInArray(components, t) : components;
};
Expand Down
Loading