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
7 changes: 7 additions & 0 deletions packages/i18n/src/models/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ export interface I18nTranslations {
'elements.fields.email.label': string;
'elements.fields.email.placeholder': string;
'elements.fields.organization.name.label': string;
'elements.fields.organization.name.placeholder'?: string;
'elements.fields.organization.handle.label': string;
'elements.fields.organization.handle.placeholder'?: string;
'elements.fields.organization.description.label': string;
'elements.fields.organization.select.label': string;
'elements.fields.organization.select.placeholder': string;
Expand Down Expand Up @@ -135,7 +137,10 @@ export interface I18nTranslations {
'organization.switcher.loading.more': string;
'organization.switcher.no.organizations': string;
'organization.switcher.error.prefix': string;
'organization.list.heading'?: string;
'organization.list.subheading'?: string;
'organization.profile.heading': string;
'organization.profile.subheading'?: string;
'organization.profile.loading': string;
'organization.profile.error': string;

Expand All @@ -144,6 +149,8 @@ export interface I18nTranslations {
/* |---------------------------------------------------------------| */

'organization.create.heading': string;
'organization.create.subheading'?: string;
'organization.create.description.placeholder'?: string;
'organization.create.buttons.create_organization.text': string;
'organization.create.buttons.create_organization.loading.text': string;
'organization.create.buttons.cancel.text': string;
Expand Down
8 changes: 8 additions & 0 deletions packages/i18n/src/translations/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ const translations: I18nTranslations = {
'elements.fields.email.label': 'Email',
'elements.fields.email.placeholder': 'Enter your email',
'elements.fields.organization.name.label': 'Organization Name',
'elements.fields.organization.name.placeholder': 'Organization Name',
'elements.fields.organization.handle.label': 'Organization Handle',
'elements.fields.organization.handle.placeholder': 'my-organization',
'elements.fields.organization.description.label': 'Organization Description',
'elements.fields.organization.select.label': 'Select Organization',
'elements.fields.organization.select.placeholder': 'Choose an organization',
Expand Down Expand Up @@ -139,11 +141,17 @@ const translations: I18nTranslations = {
'organization.switcher.no.organizations': 'No organizations found',
'organization.switcher.error.prefix': 'Error:',

'organization.list.heading': 'Organizations List',
'organization.list.subheading': 'View list of organizations.',

'organization.profile.heading': 'Organization Profile',
'organization.profile.subheading': 'Manage your organization details.',
'organization.profile.loading': 'Loading organization...',
'organization.profile.error': 'Failed to load organization',

'organization.create.heading': 'Create Organization',
'organization.create.subheading': 'Set up a new organization workspace.',
'organization.create.description.placeholder': 'Enter organization description',
'organization.create.buttons.create_organization.text': 'Create Organization',
'organization.create.buttons.create_organization.loading.text': 'Creating...',
'organization.create.buttons.cancel.text': 'Cancel',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,26 @@ const useStyles = (theme: Theme, colorScheme: string): Record<string, string> =>
gap: calc(${theme.vars.spacing.unit} * 2);
`;

const headerContainer: string = css`
margin-bottom: calc(${theme.vars.spacing.unit} * 3);
padding-bottom: calc(${theme.vars.spacing.unit} * 2);
border-bottom: 1px solid ${theme.vars.colors.border};
`;

const title: string = css`
font-size: 1.75rem;
font-weight: 700;
color: ${theme.vars.colors.text.primary};
margin: 0 0 6px 0;
letter-spacing: -0.01em;
`;

const subtitle: string = css`
font-size: 0.875rem;
color: ${theme.vars.colors.text.secondary};
margin: 0;
`;

const form: string = css`
display: flex;
flex-direction: column;
Expand All @@ -63,13 +83,27 @@ const useStyles = (theme: Theme, colorScheme: string): Record<string, string> =>

const field: string = css`
display: flex;
align-items: center;
align-items: flex-start;
padding: ${theme.vars.spacing.unit} 0;
border-bottom: 1px solid ${theme.vars.colors.border};
min-height: 32px;
`;

const label: string = css`
font-size: 0.875rem;
font-weight: 600;
color: ${theme.vars.colors.text.primary};
width: 200px;
min-width: 200px;
flex-shrink: 0;
text-align: left;
padding-right: calc(${theme.vars.spacing.unit} * 2);
padding-top: calc(${theme.vars.spacing.unit} * 0.75);
white-space: nowrap;
line-height: 1.4;
`;

const fieldGroup: string = css`
flex: 1;
display: flex;
flex-direction: column;
gap: calc(${theme.vars.spacing.unit} * 0.5);
Expand All @@ -87,6 +121,8 @@ const useStyles = (theme: Theme, colorScheme: string): Record<string, string> =>
min-height: 80px;
resize: vertical;
outline: none;
box-sizing: border-box;
transition: border-color 0.2s, box-shadow 0.2s;
&:focus {
border-color: ${theme.vars.colors.primary.main};
box-shadow: 0 0 0 2px ${theme.vars.colors.primary.main}20;
Expand Down Expand Up @@ -153,12 +189,16 @@ const useStyles = (theme: Theme, colorScheme: string): Record<string, string> =>
fieldGroup,
form,
header,
headerContainer,
infoContainer,
input,
label,
popup,
root,
subtitle,
textarea,
textareaError,
title,
value,
};
}, [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import DialogPrimitive from '../../primitives/Dialog/Dialog';
import FormControl from '../../primitives/FormControl/FormControl';
import InputLabel from '../../primitives/InputLabel/InputLabel';
import TextField from '../../primitives/TextField/TextField';
import Typography from '../../primitives/Typography/Typography';

const logger: ReturnType<typeof createPackageComponentLogger> = createPackageComponentLogger(
'@asgardeo/react',
Expand Down Expand Up @@ -194,56 +195,90 @@ export const BaseCreateOrganization: FC<BaseCreateOrganizationProps> = ({
}
};

const getTranslation = (key: string, fallback: string): string => {
const text: string = t(key);
return text && text !== key ? text : fallback;
};

const createOrganizationContent: ReactElement = (
<div className={cx(styles['root'], cardLayout && styles['card'], className)} style={style}>
<div className={cx(styles['content'])}>
{mode !== 'popup' && (
<div className={cx(styles['headerContainer'])}>
<Typography variant="h5" component="h1" className={cx(styles['title'])}>
{getTranslation('organization.create.heading', title || 'Create organization')}
</Typography>
<Typography variant="body2" color="textSecondary" className={cx(styles['subtitle'])}>
{getTranslation('organization.create.subheading', 'Set up a new organization workspace.')}
</Typography>
</div>
)}
<form id="create-organization-form" className={cx(styles['form'])} onSubmit={handleSubmit}>
{error && (
<AlertPrimitive variant="error" className={styles['errorAlert']}>
<AlertPrimitive.Title>Error</AlertPrimitive.Title>
<AlertPrimitive.Description>{error}</AlertPrimitive.Description>
</AlertPrimitive>
)}
<div className={cx(styles['fieldGroup'])}>
<TextField
label={`${t('elements.fields.organization.name.label')}`}
placeholder={t('elements.fields.organization.name.placeholder')}
value={formData.name}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleNameChange(e.target.value)}
disabled={loading}
required
error={formErrors.name}
className={cx(styles['input'])}
/>
</div>
<div className={cx(styles['fieldGroup'])}>
<TextField
label={`${t('elements.fields.organization.handle.label') || 'Organization Handle'}`}
placeholder={t('elements.fields.organization.handle.placeholder') || 'my-organization'}
value={formData.handle}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleInputChange('handle', e.target.value)}
disabled={loading}
required
error={formErrors.handle}
helperText="This will be your organization's unique identifier. Only lowercase letters, numbers, and hyphens are allowed."
className={cx(styles['input'])}
/>

<div className={cx(styles['field'])}>
<InputLabel required className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.name.label', 'Organization Name')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<TextField
placeholder={getTranslation('elements.fields.organization.name.placeholder', 'Organization Name')}
value={formData.name}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleNameChange(e.target.value)}
disabled={loading}
required
error={formErrors.name}
className={cx(styles['input'])}
/>
</div>
</div>
<div className={cx(styles['fieldGroup'])}>
<FormControl error={formErrors.description}>
<InputLabel required>{t('elements.fields.organization.description.label')}</InputLabel>
<textarea
className={cx(styles['textarea'], formErrors.description && styles['textareaError'])}
placeholder={t('organization.create.description.placeholder')}
value={formData.description}
onChange={(e: ChangeEvent<HTMLTextAreaElement>): void =>
handleInputChange('description', e.target.value)
}

<div className={cx(styles['field'])}>
<InputLabel required className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.handle.label', 'Organization Handle')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<TextField
placeholder={getTranslation('elements.fields.organization.handle.placeholder', 'my-organization')}
value={formData.handle}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleInputChange('handle', e.target.value)}
disabled={loading}
required
error={formErrors.handle}
helperText="This will be your organization's unique identifier. Only lowercase letters, numbers, and hyphens are allowed."
className={cx(styles['input'])}
/>
</FormControl>
</div>
</div>

<div className={cx(styles['field'])}>
<InputLabel required className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.description.label', 'Organization Description')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<FormControl error={formErrors.description}>
<textarea
className={cx(styles['textarea'], formErrors.description && styles['textareaError'])}
placeholder={getTranslation(
'organization.create.description.placeholder',
'Enter organization description',
)}
value={formData.description}
onChange={(e: ChangeEvent<HTMLTextAreaElement>): void =>
handleInputChange('description', e.target.value)
}
disabled={loading}
required
/>
</FormControl>
</div>
Comment on lines +224 to +279

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Associate each label with its input.

InputLabel now renders as a sibling <label> without htmlFor; the controls have no matching IDs. Label clicks will not focus the fields, and assistive technology cannot reliably derive their names.

Proposed fix
- <InputLabel required className={cx(styles['label'])}>
+ <InputLabel required htmlFor="organization-name" className={cx(styles['label'])}>
    ...
  <TextField
+   id="organization-name"
    ...

- <InputLabel required className={cx(styles['label'])}>
+ <InputLabel required htmlFor="organization-handle" className={cx(styles['label'])}>
    ...
  <TextField
+   id="organization-handle"
    ...

- <InputLabel required className={cx(styles['label'])}>
+ <InputLabel required htmlFor="organization-description" className={cx(styles['label'])}>
    ...
  <textarea
+   id="organization-description"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div className={cx(styles['field'])}>
<InputLabel required className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.name.label', 'Organization Name')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<TextField
placeholder={getTranslation('elements.fields.organization.name.placeholder', 'Organization Name')}
value={formData.name}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleNameChange(e.target.value)}
disabled={loading}
required
error={formErrors.name}
className={cx(styles['input'])}
/>
</div>
</div>
<div className={cx(styles['fieldGroup'])}>
<FormControl error={formErrors.description}>
<InputLabel required>{t('elements.fields.organization.description.label')}</InputLabel>
<textarea
className={cx(styles['textarea'], formErrors.description && styles['textareaError'])}
placeholder={t('organization.create.description.placeholder')}
value={formData.description}
onChange={(e: ChangeEvent<HTMLTextAreaElement>): void =>
handleInputChange('description', e.target.value)
}
<div className={cx(styles['field'])}>
<InputLabel required className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.handle.label', 'Organization Handle')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<TextField
placeholder={getTranslation('elements.fields.organization.handle.placeholder', 'my-organization')}
value={formData.handle}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleInputChange('handle', e.target.value)}
disabled={loading}
required
error={formErrors.handle}
helperText="This will be your organization's unique identifier. Only lowercase letters, numbers, and hyphens are allowed."
className={cx(styles['input'])}
/>
</FormControl>
</div>
</div>
<div className={cx(styles['field'])}>
<InputLabel required className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.description.label', 'Organization Description')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<FormControl error={formErrors.description}>
<textarea
className={cx(styles['textarea'], formErrors.description && styles['textareaError'])}
placeholder={getTranslation(
'organization.create.description.placeholder',
'Enter organization description',
)}
value={formData.description}
onChange={(e: ChangeEvent<HTMLTextAreaElement>): void =>
handleInputChange('description', e.target.value)
}
disabled={loading}
required
/>
</FormControl>
</div>
<div className={cx(styles['field'])}>
<InputLabel required htmlFor="organization-name" className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.name.label', 'Organization Name')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<TextField
id="organization-name"
placeholder={getTranslation('elements.fields.organization.name.placeholder', 'Organization Name')}
value={formData.name}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleNameChange(e.target.value)}
disabled={loading}
required
error={formErrors.name}
className={cx(styles['input'])}
/>
</div>
</div>
<div className={cx(styles['field'])}>
<InputLabel required htmlFor="organization-handle" className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.handle.label', 'Organization Handle')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<TextField
id="organization-handle"
placeholder={getTranslation('elements.fields.organization.handle.placeholder', 'my-organization')}
value={formData.handle}
onChange={(e: ChangeEvent<HTMLInputElement>): void => handleInputChange('handle', e.target.value)}
disabled={loading}
required
error={formErrors.handle}
helperText="This will be your organization's unique identifier. Only lowercase letters, numbers, and hyphens are allowed."
className={cx(styles['input'])}
/>
</div>
</div>
<div className={cx(styles['field'])}>
<InputLabel required htmlFor="organization-description" className={cx(styles['label'])}>
{getTranslation('elements.fields.organization.description.label', 'Organization Description')}
</InputLabel>
<div className={cx(styles['fieldGroup'])}>
<FormControl error={formErrors.description}>
<textarea
id="organization-description"
className={cx(styles['textarea'], formErrors.description && styles['textareaError'])}
placeholder={getTranslation(
'organization.create.description.placeholder',
'Enter organization description',
)}
value={formData.description}
onChange={(e: ChangeEvent<HTMLTextAreaElement>): void =>
handleInputChange('description', e.target.value)
}
disabled={loading}
required
/>
</FormControl>
</div>
</div>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/react/src/components/presentation/CreateOrganization/BaseCreateOrganization.tsx`
around lines 224 - 279, Associate each InputLabel with its corresponding control
in the organization form: add matching unique IDs to the name TextField, handle
TextField, and description textarea, and set each InputLabel’s htmlFor to the
respective ID. Preserve the existing validation, values, and event handlers.

</div>

{renderAdditionalFields && renderAdditionalFields()}
</form>
<div className={cx(styles['actions'])}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,21 @@ const useStyles = (theme: Theme, colorScheme: string): Record<string, string> =>
border-bottom: 1px solid ${theme.vars.colors.border};
`;

const headerContainer: string = css`
margin-bottom: calc(${theme.vars.spacing.unit} * 3);
padding-bottom: calc(${theme.vars.spacing.unit} * 2);
border-bottom: 1px solid ${theme.vars.colors.border};
`;
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const headerInfo: string = css`
flex: 1;
`;

const title: string = css`
font-size: 1.5rem;
font-weight: 600;
margin: 0 0 8px 0;
font-size: 1.75rem;
font-weight: 700;
color: ${theme.vars.colors.text.primary};
margin: 0 0 calc(${theme.vars.spacing.unit} * 0.5) 0;
`;

const subtitle: string = css`
Expand Down Expand Up @@ -239,7 +245,7 @@ const useStyles = (theme: Theme, colorScheme: string): Record<string, string> =>
errorContainer,
errorMargin,
header,
headerInfo,
headerContainer,
listContainer,
loadMoreButton,
loadMoreMargin,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ export interface BaseOrganizationListProps {
*/
style?: CSSProperties;

/**
* Subtitle for the component
*/
subheading?: string;

/**
* Title for the popup dialog (only used in popup mode)
*/
Expand Down Expand Up @@ -274,7 +279,8 @@ export const BaseOrganizationList: FC<BaseOrganizationListProps> = ({
renderLoadMore,
renderOrganization,
style,
title = 'Organizations',
title,
subheading,
showStatus,
preferences,
}: BaseOrganizationListProps): ReactElement => {
Expand Down Expand Up @@ -371,8 +377,24 @@ export const BaseOrganizationList: FC<BaseOrganizationListProps> = ({
return emptyContent;
}

const getTranslation = (key: string, fallback: string): string => {
const text: string = t(key);
return text && text !== key ? text : fallback;
};

const organizationListContent: ReactElement = (
<div className={cx(styles['root'], className)} style={style}>
{mode !== 'popup' && (
<div className={cx(styles['headerContainer'])}>
<Typography variant="h5" component="h1" className={cx(styles['title'])}>
{getTranslation('organization.list.heading', title || 'Organizations List')}
</Typography>
<Typography variant="body2" color="textSecondary" className={cx(styles['subtitle'])}>
{getTranslation('organization.list.subheading', subheading || 'View list of organizations.')}
</Typography>
</div>
)}

{/* Header with total count and refresh button */}
<div className={cx(styles['header'])}>
<div className={cx(styles['headerInfo'])}>
Expand Down
Loading
Loading