@salesforce/webapp-template-feature-react-authentication-experimental 1.44.0 → 1.45.0

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 (41) hide show
  1. package/dist/CHANGELOG.md +8 -0
  2. package/dist/force-app/main/default/webapplications/feature-react-authentication/package-lock.json +1173 -401
  3. package/dist/force-app/main/default/webapplications/feature-react-authentication/package.json +1 -3
  4. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/app.tsx +2 -5
  5. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/auth/authHelpers.ts +73 -0
  6. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/{utils → components/auth}/authenticationConfig.ts +9 -0
  7. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/auth/{authentication-route.tsx → authenticationRouteLayout.tsx} +1 -1
  8. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/auth/{private-route.tsx → privateRouteLayout.tsx} +1 -1
  9. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/auth/sessionTimeout/SessionTimeoutValidator.tsx +616 -0
  10. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/auth/sessionTimeout/sessionTimeService.ts +161 -0
  11. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/auth/sessionTimeout/sessionTimeoutConfig.ts +77 -0
  12. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/alert.tsx +17 -13
  13. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/button.tsx +35 -22
  14. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/card.tsx +27 -12
  15. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/dialog.tsx +143 -0
  16. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/field.tsx +157 -46
  17. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/index.ts +1 -0
  18. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/input.tsx +3 -3
  19. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/label.tsx +2 -2
  20. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/pagination.tsx +87 -74
  21. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/select.tsx +156 -124
  22. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/separator.tsx +26 -0
  23. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/skeleton.tsx +1 -0
  24. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/spinner.tsx +5 -16
  25. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/table.tsx +68 -95
  26. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/components/ui/tabs.tsx +47 -84
  27. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/context/AuthContext.tsx +12 -0
  28. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/hooks/form.tsx +1 -1
  29. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/hooks/useCountdownTimer.ts +266 -0
  30. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/hooks/useRetryWithBackoff.ts +109 -0
  31. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/layouts/AuthAppLayout.tsx +12 -0
  32. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/pages/ChangePassword.tsx +3 -2
  33. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/pages/ForgotPassword.tsx +1 -1
  34. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/pages/Login.tsx +3 -3
  35. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/pages/Profile.tsx +3 -2
  36. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/pages/Register.tsx +4 -5
  37. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/pages/ResetPassword.tsx +3 -2
  38. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/routes.tsx +5 -5
  39. package/dist/force-app/main/default/webapplications/feature-react-authentication/src/utils/helpers.ts +0 -74
  40. package/dist/package.json +1 -1
  41. package/package.json +3 -3
@@ -1,22 +1,102 @@
1
- import * as React from "react";
1
+ import { useMemo } from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
2
3
 
3
4
  import { cn } from "../../lib/utils";
4
5
  import { Label } from "./label";
6
+ import { Separator } from "./separator";
7
+
8
+ function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
9
+ return (
10
+ <fieldset
11
+ data-slot="field-set"
12
+ className={cn(
13
+ "gap-4 has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3 flex flex-col",
14
+ className,
15
+ )}
16
+ {...props}
17
+ />
18
+ );
19
+ }
20
+
21
+ function FieldLegend({
22
+ className,
23
+ variant = "legend",
24
+ ...props
25
+ }: React.ComponentProps<"legend"> & { variant?: "legend" | "label" }) {
26
+ return (
27
+ <legend
28
+ data-slot="field-legend"
29
+ data-variant={variant}
30
+ className={cn(
31
+ "mb-1.5 font-medium data-[variant=label]:text-sm data-[variant=legend]:text-base",
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
40
+ return (
41
+ <div
42
+ data-slot="field-group"
43
+ className={cn(
44
+ "gap-5 data-[slot=checkbox-group]:gap-3 *:data-[slot=field-group]:gap-4 group/field-group @container/field-group flex w-full flex-col",
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ );
50
+ }
51
+
52
+ const fieldVariants = cva("data-[invalid=true]:text-destructive gap-2 group/field flex w-full", {
53
+ variants: {
54
+ orientation: {
55
+ vertical: "flex-col *:w-full [&>.sr-only]:w-auto",
56
+ horizontal:
57
+ "flex-row items-center *:data-[slot=field-label]:flex-auto has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
58
+ responsive:
59
+ "flex-col *:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:*:w-auto @md/field-group:*:data-[slot=field-label]:flex-auto @md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
60
+ },
61
+ },
62
+ defaultVariants: {
63
+ orientation: "vertical",
64
+ },
65
+ });
5
66
 
6
67
  function Field({
7
68
  className,
8
69
  orientation = "vertical",
9
70
  ...props
10
- }: React.ComponentProps<"div"> & {
11
- orientation?: "horizontal" | "vertical";
12
- }) {
71
+ }: React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>) {
13
72
  return (
14
73
  <div
74
+ role="group"
15
75
  data-slot="field"
16
76
  data-orientation={orientation}
77
+ className={cn(fieldVariants({ orientation }), className)}
78
+ {...props}
79
+ />
80
+ );
81
+ }
82
+
83
+ function FieldContent({ className, ...props }: React.ComponentProps<"div">) {
84
+ return (
85
+ <div
86
+ data-slot="field-content"
87
+ className={cn("gap-0.5 group/field-content flex flex-1 flex-col leading-snug", className)}
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
94
+ return (
95
+ <Label
96
+ data-slot="field-label"
17
97
  className={cn(
18
- "grid gap-2",
19
- orientation === "horizontal" && "flex flex-wrap items-center gap-x-3 gap-y-1.5",
98
+ "has-data-checked:bg-primary/5 has-data-checked:border-primary/30 dark:has-data-checked:border-primary/20 dark:has-data-checked:bg-primary/10 gap-2 group-data-[disabled=true]/field:opacity-50 has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border *:data-[slot=field]:p-2.5 group/field-label peer/field-label flex w-fit leading-snug",
99
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col",
20
100
  className,
21
101
  )}
22
102
  {...props}
@@ -24,12 +104,12 @@ function Field({
24
104
  );
25
105
  }
26
106
 
27
- function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
107
+ function FieldTitle({ className, ...props }: React.ComponentProps<"div">) {
28
108
  return (
29
- <Label
109
+ <div
30
110
  data-slot="field-label"
31
111
  className={cn(
32
- "data-[disabled]:opacity-50 group-data-[disabled]:opacity-50 peer-disabled:opacity-50",
112
+ "gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50 flex w-fit items-center leading-snug",
33
113
  className,
34
114
  )}
35
115
  {...props}
@@ -41,71 +121,102 @@ function FieldDescription({ className, ...props }: React.ComponentProps<"p">) {
41
121
  return (
42
122
  <p
43
123
  data-slot="field-description"
44
- className={cn("text-muted-foreground text-sm", className)}
124
+ className={cn(
125
+ "text-muted-foreground text-left text-sm [[data-variant=legend]+&]:-mt-1.5 leading-normal font-normal group-has-data-horizontal/field:text-balance",
126
+ "last:mt-0 nth-last-2:-mt-1",
127
+ "[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4",
128
+ className,
129
+ )}
45
130
  {...props}
46
131
  />
47
132
  );
48
133
  }
49
134
 
50
- function FieldError({
135
+ function FieldSeparator({
136
+ children,
51
137
  className,
52
- errors,
53
138
  ...props
54
- }: React.ComponentProps<"p"> & { errors?: (Error | undefined | null)[] }) {
55
- const errorMessages = errors?.filter(Boolean).map((e) => e?.message);
56
-
57
- if (!errorMessages?.length) {
58
- return null;
59
- }
60
-
139
+ }: React.ComponentProps<"div"> & {
140
+ children?: React.ReactNode;
141
+ }) {
61
142
  return (
62
- <p
63
- data-slot="field-error"
64
- className={cn("text-destructive text-sm", className)}
65
- aria-live="polite"
143
+ <div
144
+ data-slot="field-separator"
145
+ data-content={!!children}
146
+ className={cn(
147
+ "-my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2 relative",
148
+ className,
149
+ )}
66
150
  {...props}
67
151
  >
68
- {errorMessages.join(", ")}
69
- </p>
152
+ <Separator className="absolute inset-0 top-1/2" />
153
+ {children && (
154
+ <span
155
+ className="text-muted-foreground px-2 bg-background relative mx-auto block w-fit"
156
+ data-slot="field-separator-content"
157
+ >
158
+ {children}
159
+ </span>
160
+ )}
161
+ </div>
70
162
  );
71
163
  }
72
164
 
73
- function FieldGroup({ className, ...props }: React.ComponentProps<"div">) {
74
- return <div data-slot="field-group" className={cn("grid gap-6", className)} {...props} />;
75
- }
165
+ function FieldError({
166
+ className,
167
+ children,
168
+ errors,
169
+ ...props
170
+ }: React.ComponentProps<"div"> & {
171
+ errors?: Array<{ message?: string } | undefined>;
172
+ }) {
173
+ const content = useMemo(() => {
174
+ if (children) {
175
+ return children;
176
+ }
76
177
 
77
- function FieldSet({ className, ...props }: React.ComponentProps<"fieldset">) {
78
- return <fieldset data-slot="field-set" className={cn("grid gap-4", className)} {...props} />;
79
- }
178
+ if (!errors?.length) {
179
+ return null;
180
+ }
80
181
 
81
- function FieldLegend({ className, ...props }: React.ComponentProps<"legend">) {
82
- return (
83
- <legend
84
- data-slot="field-legend"
85
- className={cn("text-foreground font-medium leading-none", className)}
86
- {...props}
87
- />
88
- );
89
- }
182
+ const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
183
+
184
+ if (uniqueErrors?.length == 1) {
185
+ return uniqueErrors[0]?.message;
186
+ }
187
+
188
+ return (
189
+ <ul className="ml-4 flex list-disc flex-col gap-1">
190
+ {uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
191
+ </ul>
192
+ );
193
+ }, [children, errors]);
194
+
195
+ if (!content) {
196
+ return null;
197
+ }
90
198
 
91
- function FieldSeparator({ className, ...props }: React.ComponentProps<"div">) {
92
199
  return (
93
200
  <div
94
- data-slot="field-separator"
95
- role="separator"
96
- className={cn("bg-border -mx-6 h-px", className)}
201
+ role="alert"
202
+ data-slot="field-error"
203
+ className={cn("text-destructive text-sm font-normal", className)}
97
204
  {...props}
98
- />
205
+ >
206
+ {content}
207
+ </div>
99
208
  );
100
209
  }
101
210
 
102
211
  export {
103
212
  Field,
213
+ FieldLabel,
104
214
  FieldDescription,
105
215
  FieldError,
106
216
  FieldGroup,
107
- FieldLabel,
108
217
  FieldLegend,
109
218
  FieldSeparator,
110
219
  FieldSet,
220
+ FieldContent,
221
+ FieldTitle,
111
222
  };
@@ -69,3 +69,4 @@ export {
69
69
  PaginationNext,
70
70
  PaginationPrevious,
71
71
  } from "./pagination";
72
+ export { Separator } from "./separator";
@@ -1,3 +1,5 @@
1
+ import * as React from "react";
2
+
1
3
  import { cn } from "../../lib/utils";
2
4
 
3
5
  function Input({ className, type, ...props }: React.ComponentProps<"input">) {
@@ -6,9 +8,7 @@ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
6
8
  type={type}
7
9
  data-slot="input"
8
10
  className={cn(
9
- "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input flex h-9 w-full min-w-0 rounded-md border bg-transparent px-3 py-1 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
10
- "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
11
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
11
+ "dark:bg-input/30 border-input focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:aria-invalid:border-destructive/50 disabled:bg-input/50 dark:disabled:bg-input/80 h-8 rounded-lg border bg-transparent px-2.5 py-1 text-base transition-colors file:h-6 file:text-sm file:font-medium focus-visible:ring-3 aria-invalid:ring-3 md:text-sm file:text-foreground placeholder:text-muted-foreground w-full min-w-0 outline-none file:inline-flex file:border-0 file:bg-transparent disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50",
12
12
  className,
13
13
  )}
14
14
  {...props}
@@ -1,5 +1,5 @@
1
1
  import * as React from "react";
2
- import * as LabelPrimitive from "@radix-ui/react-label";
2
+ import { Label as LabelPrimitive } from "radix-ui";
3
3
 
4
4
  import { cn } from "../../lib/utils";
5
5
 
@@ -8,7 +8,7 @@ function Label({ className, ...props }: React.ComponentProps<typeof LabelPrimiti
8
8
  <LabelPrimitive.Root
9
9
  data-slot="label"
10
10
  className={cn(
11
- "flex items-center gap-2 text-sm font-medium leading-none select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
11
+ "gap-2 text-sm leading-none font-medium group-data-[disabled=true]:opacity-50 peer-disabled:opacity-50 flex items-center select-none group-data-[disabled=true]:pointer-events-none peer-disabled:cursor-not-allowed",
12
12
  className,
13
13
  )}
14
14
  {...props}
@@ -1,92 +1,105 @@
1
1
  import * as React from "react";
2
- import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
3
2
 
4
3
  import { cn } from "../../lib/utils";
5
- import { Button, buttonVariants } from "./button";
4
+ import { Button } from "./button";
5
+ import { ChevronLeftIcon, ChevronRightIcon, MoreHorizontalIcon } from "lucide-react";
6
6
 
7
- const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
8
- <nav
9
- role="navigation"
10
- aria-label="pagination"
11
- className={cn("flex justify-center", className)}
12
- {...props}
13
- />
14
- );
15
- Pagination.displayName = "Pagination";
7
+ function Pagination({ className, ...props }: React.ComponentProps<"nav">) {
8
+ return (
9
+ <nav
10
+ role="navigation"
11
+ aria-label="pagination"
12
+ data-slot="pagination"
13
+ className={cn("mx-auto flex w-full justify-center", className)}
14
+ {...props}
15
+ />
16
+ );
17
+ }
16
18
 
17
- const PaginationContent = React.forwardRef<HTMLUListElement, React.ComponentProps<"ul">>(
18
- ({ className, ...props }, ref) => (
19
- <ul ref={ref} className={cn("flex flex-row items-center gap-1", className)} {...props} />
20
- ),
21
- );
22
- PaginationContent.displayName = "PaginationContent";
19
+ function PaginationContent({ className, ...props }: React.ComponentProps<"ul">) {
20
+ return (
21
+ <ul
22
+ data-slot="pagination-content"
23
+ className={cn("gap-0.5 flex items-center", className)}
24
+ {...props}
25
+ />
26
+ );
27
+ }
23
28
 
24
- const PaginationItem = React.forwardRef<HTMLLIElement, React.ComponentProps<"li">>(
25
- ({ className, ...props }, ref) => <li ref={ref} className={cn("", className)} {...props} />,
26
- );
27
- PaginationItem.displayName = "PaginationItem";
29
+ function PaginationItem({ ...props }: React.ComponentProps<"li">) {
30
+ return <li data-slot="pagination-item" {...props} />;
31
+ }
28
32
 
29
33
  type PaginationLinkProps = {
30
34
  isActive?: boolean;
31
- } & React.ComponentProps<"button">;
35
+ } & Pick<React.ComponentProps<typeof Button>, "size"> &
36
+ React.ComponentProps<"a">;
32
37
 
33
- const PaginationLink = ({ className, isActive, disabled, ...props }: PaginationLinkProps) => (
34
- <button
35
- aria-current={isActive ? "page" : undefined}
36
- disabled={disabled}
37
- className={cn(
38
- buttonVariants({
39
- variant: isActive ? "default" : "outline",
40
- size: "sm",
41
- }),
42
- className,
43
- )}
44
- {...props}
45
- />
46
- );
47
- PaginationLink.displayName = "PaginationLink";
38
+ function PaginationLink({ className, isActive, size = "icon", ...props }: PaginationLinkProps) {
39
+ return (
40
+ <Button asChild variant={isActive ? "outline" : "ghost"} size={size} className={cn(className)}>
41
+ <a
42
+ aria-current={isActive ? "page" : undefined}
43
+ data-slot="pagination-link"
44
+ data-active={isActive}
45
+ {...props}
46
+ />
47
+ </Button>
48
+ );
49
+ }
48
50
 
49
- const PaginationPrevious = ({
51
+ function PaginationPrevious({
50
52
  className,
51
- disabled,
53
+ text = "Previous",
52
54
  ...props
53
- }: React.ComponentProps<typeof Button>) => (
54
- <PaginationLink
55
- aria-label="Go to previous page"
56
- className={cn("gap-1 pl-2.5", className)}
57
- disabled={disabled}
58
- {...props}
59
- >
60
- <ChevronLeft className="h-4 w-4" />
61
- <span>Previous</span>
62
- </PaginationLink>
63
- );
64
- PaginationPrevious.displayName = "PaginationPrevious";
55
+ }: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
56
+ return (
57
+ <PaginationLink
58
+ aria-label="Go to previous page"
59
+ size="default"
60
+ className={cn("pl-1.5!", className)}
61
+ {...props}
62
+ >
63
+ <ChevronLeftIcon data-icon="inline-start" className="cn-rtl-flip" />
64
+ <span className="hidden sm:block">{text}</span>
65
+ </PaginationLink>
66
+ );
67
+ }
65
68
 
66
- const PaginationNext = ({ className, disabled, ...props }: React.ComponentProps<typeof Button>) => (
67
- <PaginationLink
68
- aria-label="Go to next page"
69
- className={cn("gap-1 pr-2.5", className)}
70
- disabled={disabled}
71
- {...props}
72
- >
73
- <span>Next</span>
74
- <ChevronRight className="h-4 w-4" />
75
- </PaginationLink>
76
- );
77
- PaginationNext.displayName = "PaginationNext";
69
+ function PaginationNext({
70
+ className,
71
+ text = "Next",
72
+ ...props
73
+ }: React.ComponentProps<typeof PaginationLink> & { text?: string }) {
74
+ return (
75
+ <PaginationLink
76
+ aria-label="Go to next page"
77
+ size="default"
78
+ className={cn("pr-1.5!", className)}
79
+ {...props}
80
+ >
81
+ <span className="hidden sm:block">{text}</span>
82
+ <ChevronRightIcon data-icon="inline-end" className="cn-rtl-flip" />
83
+ </PaginationLink>
84
+ );
85
+ }
78
86
 
79
- const PaginationEllipsis = ({ className, ...props }: React.ComponentProps<"span">) => (
80
- <span
81
- aria-hidden
82
- className={cn("flex h-9 w-9 items-center justify-center", className)}
83
- {...props}
84
- >
85
- <MoreHorizontal className="h-4 w-4" />
86
- <span className="sr-only">More pages</span>
87
- </span>
88
- );
89
- PaginationEllipsis.displayName = "PaginationEllipsis";
87
+ function PaginationEllipsis({ className, ...props }: React.ComponentProps<"span">) {
88
+ return (
89
+ <span
90
+ aria-hidden
91
+ data-slot="pagination-ellipsis"
92
+ className={cn(
93
+ "size-8 [&_svg:not([class*='size-'])]:size-4 flex items-center justify-center",
94
+ className,
95
+ )}
96
+ {...props}
97
+ >
98
+ <MoreHorizontalIcon />
99
+ <span className="sr-only">More pages</span>
100
+ </span>
101
+ );
102
+ }
90
103
 
91
104
  export {
92
105
  Pagination,