Skip to content

Commit 9edbcab

Browse files
committed
Refactor UI to include personal account toogle
1 parent 7a03be4 commit 9edbcab

File tree

1 file changed

+152
-22
lines changed
  • packages/clerk-js/src/ui/components/devPrompts/EnableOrganizationsPrompt

1 file changed

+152
-22
lines changed

packages/clerk-js/src/ui/components/devPrompts/EnableOrganizationsPrompt/index.tsx

Lines changed: 152 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { useClerk } from '@clerk/shared/react';
22
import type { __internal_EnableOrganizationsPromptProps } from '@clerk/shared/types';
33
// eslint-disable-next-line no-restricted-imports
44
import { css } from '@emotion/react';
5-
import { useMemo, useState } from 'react';
5+
import { forwardRef, useMemo, useState } from 'react';
66

77
import { Modal } from '@/ui/elements/Modal';
8-
import { InternalThemeProvider } from '@/ui/styledSystem';
8+
import { common, InternalThemeProvider } from '@/ui/styledSystem';
99

1010
import { DevTools } from '../../../../core/resources/DevTools';
1111
import type { Environment } from '../../../../core/resources/Environment';
@@ -32,6 +32,7 @@ const EnableOrganizationsPromptInternal = ({
3232
const clerk = useClerk();
3333
const [isLoading, setIsLoading] = useState(false);
3434
const [isEnabled, setIsEnabled] = useState(false);
35+
const [allowPersonalAccount, setAllowPersonalAccount] = useState(false);
3536

3637
// @ts-expect-error - __unstable__environment is not typed
3738
const environment = clerk?.__unstable__environment as Environment | undefined;
@@ -70,6 +71,8 @@ const EnableOrganizationsPromptInternal = ({
7071
});
7172
};
7273

74+
const isComponent = !caller.startsWith('use');
75+
7376
return (
7477
<Portal>
7578
<Modal
@@ -80,6 +83,7 @@ const EnableOrganizationsPromptInternal = ({
8083
sx={() => ({
8184
display: 'flex',
8285
flexDirection: 'column',
86+
maxWidth: '30rem',
8387
})}
8488
>
8589
<Flex
@@ -200,7 +204,6 @@ const EnableOrganizationsPromptInternal = ({
200204
direction='col'
201205
sx={t => ({
202206
gap: t.sizes.$0x5,
203-
maxWidth: '18.75rem',
204207
})}
205208
>
206209
{isEnabled ? (
@@ -211,7 +214,7 @@ const EnableOrganizationsPromptInternal = ({
211214
color: #b4b4b4;
212215
font-size: 0.8125rem;
213216
font-weight: 400;
214-
line-height: 1.23;
217+
line-height: 1.3;
215218
`,
216219
]}
217220
>
@@ -224,7 +227,7 @@ const EnableOrganizationsPromptInternal = ({
224227
color: #a8a8ff;
225228
font-size: inherit;
226229
font-weight: 500;
227-
line-height: 1.5;
230+
line-height: 1.3;
228231
font-size: 0.8125rem;
229232
`,
230233
]}
@@ -250,20 +253,22 @@ const EnableOrganizationsPromptInternal = ({
250253
`,
251254
]}
252255
>
253-
To use{' '}
256+
To use the{' '}
254257
<code
255258
css={[
256259
basePromptElementStyles,
257260
css`
261+
font-size: 0.75rem;
258262
color: white;
259263
font-family: monospace;
260264
line-height: 1.23;
261265
`,
262266
]}
263267
>
264-
{caller}
265-
</code>
266-
, you&apos;ll need to enable the Organizations feature for your app first.
268+
{isComponent ? `<${caller} />` : caller}
269+
</code>{' '}
270+
{isComponent ? 'component' : 'hook'}, you&apos;ll need to enable the Organizations feature for your
271+
app first.
267272
</span>
268273

269274
<a
@@ -287,6 +292,16 @@ const EnableOrganizationsPromptInternal = ({
287292
</>
288293
)}
289294
</Flex>
295+
296+
{!isEnabled && (
297+
<Flex sx={t => ({ marginTop: t.sizes.$3 })}>
298+
<AllowPersonalAccountSwitch
299+
checked={allowPersonalAccount}
300+
onChange={() => setAllowPersonalAccount(!allowPersonalAccount)}
301+
isDisabled={false}
302+
/>
303+
</Flex>
304+
)}
290305
</Flex>
291306

292307
<span
@@ -299,30 +314,22 @@ const EnableOrganizationsPromptInternal = ({
299314
/>
300315

301316
<Flex
302-
direction='col'
303317
justify='center'
304318
sx={t => ({
305319
padding: `${t.sizes.$4} ${t.sizes.$6}`,
306320
gap: t.sizes.$3,
321+
justifyContent: 'flex-end',
307322
})}
308323
>
309324
{isEnabled ? (
310325
<PromptButton
311-
variant='outline'
326+
variant='solid'
312327
onClick={() => onSuccess?.()}
313328
>
314329
Continue
315330
</PromptButton>
316331
) : (
317332
<>
318-
<PromptButton
319-
variant='solid'
320-
onClick={handleEnableOrganizations}
321-
disabled={isLoading}
322-
>
323-
Enable Organizations
324-
</PromptButton>
325-
326333
<PromptButton
327334
variant='outline'
328335
onClick={() => {
@@ -332,6 +339,14 @@ const EnableOrganizationsPromptInternal = ({
332339
>
333340
I&apos;ll remove it myself
334341
</PromptButton>
342+
343+
<PromptButton
344+
variant='solid'
345+
onClick={handleEnableOrganizations}
346+
disabled={isLoading}
347+
>
348+
Enable Organizations
349+
</PromptButton>
335350
</>
336351
)}
337352
</Flex>
@@ -359,9 +374,7 @@ const mainCTAStyles = css`
359374
display: flex;
360375
align-items: center;
361376
justify-content: center;
362-
width: 100%;
363377
height: 1.75rem;
364-
max-width: 14.625rem;
365378
padding: 0.375rem 0.625rem;
366379
border-radius: 0.375rem;
367380
font-size: 0.75rem;
@@ -371,7 +384,6 @@ const mainCTAStyles = css`
371384
text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.32);
372385
white-space: nowrap;
373386
user-select: none;
374-
min-width: 100%;
375387
color: white;
376388
transition: all 120ms ease-in-out;
377389
@@ -456,3 +468,121 @@ const PromptButton = ({ variant = 'solid', ...props }: PromptButtonProps) => {
456468
/>
457469
);
458470
};
471+
472+
type AllowPersonalAccountSwitchProps = {
473+
checked: boolean;
474+
isDisabled: boolean;
475+
onChange: (checked: boolean) => void;
476+
};
477+
478+
const AllowPersonalAccountSwitch = forwardRef<HTMLDivElement, AllowPersonalAccountSwitchProps>(
479+
({ checked, onChange, isDisabled = false }, ref) => {
480+
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
481+
if (isDisabled) {
482+
return;
483+
}
484+
485+
onChange?.(e.target.checked);
486+
};
487+
488+
return (
489+
<Flex
490+
ref={ref}
491+
direction='row'
492+
align='center'
493+
as='label'
494+
gap={2}
495+
sx={t => ({
496+
isolation: 'isolate',
497+
width: 'fit-content',
498+
'&:has(input:focus-visible) > input + span': {
499+
...common.focusRingStyles(t),
500+
},
501+
})}
502+
>
503+
{/* The order of the elements is important here for the focus ring to work. The input is visually hidden, so the focus ring is applied to the span. */}
504+
<input
505+
type='checkbox'
506+
role='switch'
507+
disabled={isDisabled}
508+
checked={checked}
509+
onChange={handleChange}
510+
tabIndex={-1}
511+
style={{
512+
...common.visuallyHidden(),
513+
}}
514+
/>
515+
<Flex
516+
as='span'
517+
data-checked={checked}
518+
sx={t => ({
519+
minWidth: t.sizes.$7,
520+
alignSelf: 'flex-start',
521+
height: t.sizes.$4,
522+
alignItems: 'center',
523+
position: 'relative',
524+
borderColor: '#DBDBE0',
525+
backgroundColor: checked ? '#DBDBE0' : t.colors.$primary500,
526+
borderRadius: 999,
527+
transition: 'background-color 0.2s',
528+
opacity: isDisabled ? 0.6 : 1,
529+
cursor: isDisabled ? 'not-allowed' : 'pointer',
530+
outline: 'none',
531+
boxSizing: 'border-box',
532+
boxShadow:
533+
'0px 0px 6px 0px rgba(255, 255, 255, 0.04) inset, 0px 0px 0px 1px rgba(255, 255, 255, 0.04) inset, 0px 1px 0px 0px rgba(255, 255, 255, 0.04) inset, 0px 0px 0px 1px rgba(0, 0, 0, 0.1)',
534+
})}
535+
>
536+
<Flex
537+
sx={t => ({
538+
position: 'absolute',
539+
left: t.sizes.$0x5,
540+
width: t.sizes.$3,
541+
height: t.sizes.$3,
542+
borderRadius: '50%',
543+
backgroundColor: 'white',
544+
boxShadow: t.shadows.$switchControl,
545+
transform: `translateX(${checked ? t.sizes.$3 : 0})`,
546+
transition: 'transform 0.2s',
547+
zIndex: 1,
548+
})}
549+
/>
550+
</Flex>
551+
552+
<Flex
553+
direction='col'
554+
gap={1}
555+
>
556+
<p
557+
css={[
558+
basePromptElementStyles,
559+
css`
560+
font-size: 0.875rem;
561+
font-weight: 400;
562+
line-height: 1.23;
563+
color: white;
564+
`,
565+
]}
566+
>
567+
Allow personal account
568+
</p>
569+
570+
<span
571+
css={[
572+
basePromptElementStyles,
573+
css`
574+
color: #b4b4b4;
575+
font-size: 0.8125rem;
576+
font-weight: 400;
577+
line-height: 1.23;
578+
`,
579+
]}
580+
>
581+
This is an uncommon setting, meant for applications that sell to both organizations and individual users.
582+
Most B2B applications require users to be part of an organization, and should keep this setting disabled.
583+
</span>
584+
</Flex>
585+
</Flex>
586+
);
587+
},
588+
);

0 commit comments

Comments
 (0)