@nocobase/portal-template-default 1.0.4 → 1.0.6

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 (139) hide show
  1. package/README.MD +77 -0
  2. package/package.json +23 -17
  3. package/registry/nocobase-acl/demo/prompt-generator.tsx +6 -39
  4. package/registry/nocobase-acl/demo/role-switcher-prompt-generator.tsx +6 -42
  5. package/registry/nocobase-ai/demo/page-context-prompt-generator.tsx +1 -1
  6. package/registry/nocobase-ai/demo/prompt-card.tsx +7 -28
  7. package/registry/nocobase-ai/demo/prompt-generator.tsx +9 -30
  8. package/registry/nocobase-ai/demo/shortcut.tsx +1 -1
  9. package/registry/nocobase-ai/demo/tool-cards.tsx +10 -30
  10. package/registry/nocobase-auth-cas/README.md +6 -0
  11. package/registry/nocobase-auth-cas/cas-sign-in-button.tsx +41 -0
  12. package/registry/nocobase-auth-cas/demo.tsx +20 -0
  13. package/registry/nocobase-auth-cas/extension.tsx +46 -0
  14. package/registry/nocobase-auth-cas/index.ts +1 -0
  15. package/registry/nocobase-auth-cas/use-cas-sign-in.ts +29 -0
  16. package/registry/nocobase-auth-dingtalk/README.md +7 -0
  17. package/registry/nocobase-auth-dingtalk/auto-login-provider.tsx +154 -0
  18. package/registry/nocobase-auth-dingtalk/demo.tsx +23 -0
  19. package/registry/nocobase-auth-dingtalk/dingtalk-sign-in-button.tsx +45 -0
  20. package/registry/nocobase-auth-dingtalk/extension.tsx +55 -0
  21. package/registry/nocobase-auth-dingtalk/index.ts +4 -0
  22. package/registry/nocobase-auth-dingtalk/use-dingtalk-sign-in.ts +64 -0
  23. package/registry/nocobase-auth-ldap/README.md +6 -0
  24. package/registry/nocobase-auth-ldap/demo.tsx +21 -0
  25. package/registry/nocobase-auth-ldap/extension.tsx +46 -0
  26. package/registry/nocobase-auth-ldap/index.ts +1 -0
  27. package/registry/nocobase-auth-ldap/ldap-sign-in-form.tsx +69 -0
  28. package/registry/nocobase-auth-ldap/use-ldap-sign-in.ts +5 -0
  29. package/registry/nocobase-auth-oidc/README.md +11 -0
  30. package/registry/nocobase-auth-oidc/auto-redirect-provider.tsx +13 -0
  31. package/registry/nocobase-auth-oidc/demo.tsx +20 -0
  32. package/registry/nocobase-auth-oidc/extension.tsx +51 -0
  33. package/registry/nocobase-auth-oidc/index.ts +1 -0
  34. package/registry/nocobase-auth-oidc/oidc-sign-in-button.tsx +44 -0
  35. package/registry/nocobase-auth-oidc/use-oidc-sign-in.ts +58 -0
  36. package/registry/nocobase-auth-saml/README.md +6 -0
  37. package/registry/nocobase-auth-saml/auto-redirect-provider.tsx +13 -0
  38. package/registry/nocobase-auth-saml/demo.tsx +20 -0
  39. package/registry/nocobase-auth-saml/extension.tsx +51 -0
  40. package/registry/nocobase-auth-saml/index.ts +1 -0
  41. package/registry/nocobase-auth-saml/saml-sign-in-button.tsx +43 -0
  42. package/registry/nocobase-auth-saml/use-saml-sign-in.ts +51 -0
  43. package/registry/nocobase-auth-sms/README.md +7 -0
  44. package/registry/nocobase-auth-sms/demo.tsx +25 -0
  45. package/registry/nocobase-auth-sms/extension.tsx +46 -0
  46. package/registry/nocobase-auth-sms/index.ts +1 -0
  47. package/registry/nocobase-auth-sms/sms-sign-in-form.tsx +107 -0
  48. package/registry/nocobase-auth-sms/use-sms-sign-in.ts +88 -0
  49. package/registry/nocobase-auth-wecom/README.md +6 -0
  50. package/registry/nocobase-auth-wecom/auto-login-provider.tsx +21 -0
  51. package/registry/nocobase-auth-wecom/demo.tsx +23 -0
  52. package/registry/nocobase-auth-wecom/extension.tsx +49 -0
  53. package/registry/nocobase-auth-wecom/index.ts +1 -0
  54. package/registry/nocobase-auth-wecom/use-wecom-sign-in.ts +62 -0
  55. package/registry/nocobase-auth-wecom/wecom-sign-in-button.tsx +70 -0
  56. package/registry/nocobase-i18n/demo/prompt-generator.tsx +30 -54
  57. package/registry/nocobase-route-surfaces/demo/prompt-generator.tsx +18 -21
  58. package/registry.config.json +112 -0
  59. package/scripts/build-html.mjs +106 -0
  60. package/src/App.tsx +34 -8
  61. package/src/app/extension.ts +4 -0
  62. package/src/app/extensions.tsx +24 -1
  63. package/src/components/auth/authenticator-adapters.ts +20 -0
  64. package/src/components/auth/auto-redirect-provider.tsx +84 -0
  65. package/src/components/auth/basic-sign-in-form.tsx +101 -0
  66. package/src/components/auth/default-sign-in-page.tsx +20 -0
  67. package/src/components/auth/demo/auth-demo-page.tsx +236 -0
  68. package/src/components/auth/demo/auth-demo-prompt-generator.tsx +93 -0
  69. package/src/components/auth/demo/auth-demo-route.tsx +11 -0
  70. package/src/components/auth/demo/auth-method-demo.tsx +48 -0
  71. package/src/components/auth/demo/index.ts +4 -0
  72. package/src/components/auth/dynamic-sign-in.tsx +191 -0
  73. package/src/components/auth/forgot-password-form.tsx +13 -5
  74. package/src/components/auth/index.ts +13 -0
  75. package/src/components/auth/sign-in-form.tsx +1 -111
  76. package/src/components/auth/sign-up-form.tsx +14 -37
  77. package/src/components/auth/types.ts +38 -0
  78. package/src/components/auth/use-authenticator-sign-in.ts +14 -0
  79. package/src/components/auth/use-public-authenticators.ts +26 -0
  80. package/src/components/data-table/data-table-filter.tsx +107 -153
  81. package/src/components/demo/prompt-output.tsx +71 -0
  82. package/src/components/ui/alert-dialog.tsx +2 -0
  83. package/src/components/ui/attachment.tsx +207 -0
  84. package/src/components/ui/bubble.tsx +128 -0
  85. package/src/components/ui/button-group.tsx +87 -0
  86. package/src/components/ui/button.tsx +6 -6
  87. package/src/components/ui/calendar.tsx +0 -2
  88. package/src/components/ui/card.tsx +1 -1
  89. package/src/components/ui/carousel.tsx +2 -0
  90. package/src/components/ui/combobox.tsx +297 -0
  91. package/src/components/ui/command.tsx +43 -41
  92. package/src/components/ui/dialog.tsx +0 -2
  93. package/src/components/ui/direction.tsx +4 -0
  94. package/src/components/ui/drawer.tsx +2 -0
  95. package/src/components/ui/dropdown-menu.tsx +0 -2
  96. package/src/components/ui/empty.tsx +104 -0
  97. package/src/components/ui/field.tsx +238 -0
  98. package/src/components/ui/hover-card.tsx +2 -0
  99. package/src/components/ui/input-group.tsx +0 -2
  100. package/src/components/ui/input-otp.tsx +0 -2
  101. package/src/components/ui/input.tsx +1 -1
  102. package/src/components/ui/item.tsx +201 -0
  103. package/src/components/ui/kbd.tsx +26 -0
  104. package/src/components/ui/label.tsx +2 -0
  105. package/src/components/ui/marker.tsx +71 -0
  106. package/src/components/ui/message-scroller.tsx +131 -0
  107. package/src/components/ui/message.tsx +92 -0
  108. package/src/components/ui/native-select.tsx +61 -0
  109. package/src/components/ui/popover.tsx +0 -2
  110. package/src/components/ui/progress.tsx +2 -0
  111. package/src/components/ui/radio-group.tsx +0 -2
  112. package/src/components/ui/resizable.tsx +2 -0
  113. package/src/components/ui/scroll-area.tsx +0 -2
  114. package/src/components/ui/select.tsx +6 -4
  115. package/src/components/ui/separator.tsx +0 -2
  116. package/src/components/ui/sheet.tsx +21 -22
  117. package/src/components/ui/sidebar.tsx +1 -3
  118. package/src/components/ui/sonner.tsx +2 -0
  119. package/src/components/ui/spinner.tsx +10 -0
  120. package/src/components/ui/switch.tsx +0 -2
  121. package/src/components/ui/table.tsx +4 -2
  122. package/src/components/ui/tabs.tsx +0 -2
  123. package/src/components/ui/textarea.tsx +1 -1
  124. package/src/components/ui/toggle-group.tsx +2 -0
  125. package/src/components/ui/toggle.tsx +2 -0
  126. package/src/components/ui/tooltip.tsx +0 -2
  127. package/src/index.tsx +3 -0
  128. package/src/lib/nocobase/client.ts +32 -2
  129. package/src/lib/utils.ts +2 -1
  130. package/src/locales/en-US.ts +4 -2
  131. package/src/locales/zh-CN.ts +4 -2
  132. package/src/pages/login/index.tsx +2 -2
  133. package/src/pages/users/list.tsx +144 -16
  134. package/src/providers/auth-callback.ts +21 -0
  135. package/src/providers/auth.ts +108 -22
  136. package/src/providers/constants.ts +4 -1
  137. package/src/providers/runtime-config.ts +19 -0
  138. package/src/vite-env.d.ts +5 -0
  139. package/vite.config.ts +55 -2
@@ -0,0 +1,191 @@
1
+ import { AlertCircle, KeyRound } from "lucide-react";
2
+ import { Fragment, Suspense, useEffect, useMemo } from "react";
3
+
4
+ import { authenticatorAdapterMap } from "@/components/auth/authenticator-adapters";
5
+ import type {
6
+ Authenticator,
7
+ RenderAuthenticator,
8
+ } from "@/components/auth/types";
9
+ import { usePublicAuthenticators } from "@/components/auth/use-public-authenticators";
10
+ import {
11
+ Alert,
12
+ AlertDescription,
13
+ AlertTitle,
14
+ } from "@/components/ui/alert";
15
+ import {
16
+ Empty,
17
+ EmptyDescription,
18
+ EmptyHeader,
19
+ EmptyMedia,
20
+ EmptyTitle,
21
+ } from "@/components/ui/empty";
22
+ import { Separator } from "@/components/ui/separator";
23
+ import { Spinner } from "@/components/ui/spinner";
24
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
25
+ import { resolveTranslatableText } from "@/lib/i18n";
26
+
27
+ type DynamicSignInProps = {
28
+ renderAuthenticator?: RenderAuthenticator;
29
+ };
30
+
31
+ type ResolvedAuthenticator = {
32
+ authenticator: Authenticator;
33
+ placement: "form" | "button";
34
+ element: React.ReactNode;
35
+ };
36
+
37
+ function AuthenticatorFallback() {
38
+ return (
39
+ <div className="flex min-h-32 items-center justify-center">
40
+ <Spinner className="size-5 text-muted-foreground" />
41
+ </div>
42
+ );
43
+ }
44
+
45
+ export function DynamicSignIn({
46
+ renderAuthenticator,
47
+ }: DynamicSignInProps) {
48
+ const { data: authenticators = [], error, isPending } =
49
+ usePublicAuthenticators();
50
+
51
+ const unsupportedAuthTypes = useMemo(
52
+ () =>
53
+ Array.from(
54
+ new Set(
55
+ authenticators
56
+ .filter(
57
+ (authenticator) =>
58
+ !authenticatorAdapterMap.has(authenticator.authType)
59
+ )
60
+ .map((authenticator) => authenticator.authType)
61
+ )
62
+ ),
63
+ [authenticators]
64
+ );
65
+
66
+ useEffect(() => {
67
+ if (!import.meta.env.DEV || unsupportedAuthTypes.length === 0) return;
68
+ console.warn(
69
+ `[auth] No frontend adapter is installed for: ${unsupportedAuthTypes.join(
70
+ ", "
71
+ )}`
72
+ );
73
+ }, [unsupportedAuthTypes]);
74
+
75
+ const resolved = useMemo<ResolvedAuthenticator[]>(() => {
76
+ return authenticators.flatMap((authenticator) => {
77
+ const adapter = authenticatorAdapterMap.get(authenticator.authType);
78
+ if (!adapter) return [];
79
+
80
+ const Component = adapter.Component;
81
+ const defaultElement = (
82
+ <Suspense fallback={<AuthenticatorFallback />}>
83
+ <Component authenticator={authenticator} />
84
+ </Suspense>
85
+ );
86
+ const element = renderAuthenticator
87
+ ? renderAuthenticator({ authenticator, adapter, defaultElement })
88
+ : defaultElement;
89
+
90
+ if (element === null || element === false || element === undefined) {
91
+ return [];
92
+ }
93
+
94
+ return [{ authenticator, placement: adapter.placement, element }];
95
+ });
96
+ }, [authenticators, renderAuthenticator]);
97
+
98
+ if (isPending) {
99
+ return (
100
+ <div className="flex min-h-64 items-center justify-center">
101
+ <Spinner className="size-6 text-muted-foreground" />
102
+ </div>
103
+ );
104
+ }
105
+
106
+ if (error) {
107
+ return (
108
+ <Alert variant="destructive">
109
+ <AlertCircle />
110
+ <AlertTitle>Unable to load sign-in methods</AlertTitle>
111
+ <AlertDescription>
112
+ {import.meta.env.DEV && error instanceof Error
113
+ ? error.message
114
+ : "Please try again or contact your administrator."}
115
+ </AlertDescription>
116
+ </Alert>
117
+ );
118
+ }
119
+
120
+ const forms = resolved.filter((item) => item.placement === "form");
121
+ const buttons = resolved.filter((item) => item.placement === "button");
122
+
123
+ if (resolved.length === 0) {
124
+ return (
125
+ <Empty className="min-h-64 border">
126
+ <EmptyHeader>
127
+ <EmptyMedia variant="icon">
128
+ <KeyRound />
129
+ </EmptyMedia>
130
+ <EmptyTitle>No supported sign-in methods</EmptyTitle>
131
+ <EmptyDescription>
132
+ No sign-in method is currently available. Contact your
133
+ administrator for access.
134
+ </EmptyDescription>
135
+ </EmptyHeader>
136
+ </Empty>
137
+ );
138
+ }
139
+
140
+ return (
141
+ <div className="space-y-6">
142
+ {forms.length === 1 ? (
143
+ forms[0].element
144
+ ) : forms.length > 1 ? (
145
+ <Tabs defaultValue={forms[0].authenticator.name}>
146
+ <TabsList className="w-full">
147
+ {forms.map(({ authenticator }) => (
148
+ <TabsTrigger
149
+ key={authenticator.name}
150
+ value={authenticator.name}
151
+ >
152
+ {resolveTranslatableText(
153
+ authenticator.title ||
154
+ authenticator.authTypeTitle ||
155
+ authenticator.authType
156
+ )}
157
+ </TabsTrigger>
158
+ ))}
159
+ </TabsList>
160
+ {forms.map(({ authenticator, element }) => (
161
+ <TabsContent
162
+ key={authenticator.name}
163
+ value={authenticator.name}
164
+ >
165
+ {element}
166
+ </TabsContent>
167
+ ))}
168
+ </Tabs>
169
+ ) : null}
170
+
171
+ {buttons.length > 0 && (
172
+ <Fragment>
173
+ {forms.length > 0 && (
174
+ <div className="flex items-center gap-4 py-1">
175
+ <Separator className="flex-1" />
176
+ <span className="text-xs text-muted-foreground">
177
+ Or continue with
178
+ </span>
179
+ <Separator className="flex-1" />
180
+ </div>
181
+ )}
182
+ <div className="grid gap-3">
183
+ {buttons.map(({ authenticator, element }) => (
184
+ <Fragment key={authenticator.name}>{element}</Fragment>
185
+ ))}
186
+ </div>
187
+ </Fragment>
188
+ )}
189
+ </div>
190
+ );
191
+ }
@@ -3,6 +3,7 @@
3
3
  import { useState } from "react";
4
4
  import { ArrowLeft } from "lucide-react";
5
5
  import { useForgotPassword, useLink } from "@refinedev/core";
6
+ import { useSearchParams } from "react-router";
6
7
 
7
8
  import { AuthLayout } from "@/components/auth/auth-layout";
8
9
  import { Button } from "@/components/ui/button";
@@ -12,11 +13,15 @@ import { Label } from "@/components/ui/label";
12
13
  export const ForgotPasswordForm = () => {
13
14
  const [email, setEmail] = useState("");
14
15
  const Link = useLink();
15
- const { mutate: forgotPassword } = useForgotPassword();
16
+ const [searchParams] = useSearchParams();
17
+ const { mutate: forgotPassword, isPending } = useForgotPassword();
16
18
 
17
19
  const handleForgotPassword = (event: React.FormEvent<HTMLFormElement>) => {
18
20
  event.preventDefault();
19
- forgotPassword({ email });
21
+ forgotPassword({
22
+ email,
23
+ authenticator: searchParams.get("name") ?? undefined,
24
+ });
20
25
  };
21
26
 
22
27
  return (
@@ -44,11 +49,14 @@ export const ForgotPasswordForm = () => {
44
49
  autoComplete="email"
45
50
  autoFocus
46
51
  required
47
- className="h-11 rounded-lg"
48
52
  />
49
53
  </div>
50
- <Button type="submit" size="lg" className="h-11 w-full rounded-lg">
51
- Send reset link
54
+ <Button
55
+ type="submit"
56
+ className="w-full"
57
+ disabled={isPending}
58
+ >
59
+ {isPending ? "Sending…" : "Send reset link"}
52
60
  </Button>
53
61
  </form>
54
62
  </AuthLayout>
@@ -0,0 +1,13 @@
1
+ export { BasicSignInForm } from "./basic-sign-in-form";
2
+ export { DefaultSignInPage } from "./default-sign-in-page";
3
+ export { DynamicSignIn } from "./dynamic-sign-in";
4
+ export { AuthAutoRedirectProvider } from "./auto-redirect-provider";
5
+ export { useAuthenticatorSignIn } from "./use-authenticator-sign-in";
6
+ export { usePublicAuthenticators } from "./use-public-authenticators";
7
+ export type {
8
+ Authenticator,
9
+ AuthenticatorAdapter,
10
+ AuthenticatorComponentProps,
11
+ RenderAuthenticator,
12
+ RenderAuthenticatorContext,
13
+ } from "./types";
@@ -1,111 +1 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { Chrome, Github } from "lucide-react";
5
- import { useLink, useLogin } from "@refinedev/core";
6
-
7
- import { AuthLayout } from "@/components/auth/auth-layout";
8
- import { InputPassword } from "@/components/auth/input-password";
9
- import { Button } from "@/components/ui/button";
10
- import { Input } from "@/components/ui/input";
11
- import { Label } from "@/components/ui/label";
12
-
13
- export const SignInForm = () => {
14
- const [account, setAccount] = useState("");
15
- const [password, setPassword] = useState("");
16
- const Link = useLink();
17
- const { mutate: login } = useLogin();
18
-
19
- const handleSignIn = (event: React.FormEvent<HTMLFormElement>) => {
20
- event.preventDefault();
21
- login({ username: account, password });
22
- };
23
-
24
- return (
25
- <AuthLayout
26
- title="Welcome back"
27
- description="Use your NocoBase account to continue."
28
- >
29
- <form onSubmit={handleSignIn} className="space-y-5">
30
- <div className="space-y-2">
31
- <Label htmlFor="account">Username or email</Label>
32
- <Input
33
- id="account"
34
- type="text"
35
- value={account}
36
- onChange={(event) => setAccount(event.target.value)}
37
- autoComplete="username"
38
- autoFocus
39
- required
40
- className="h-11 rounded-lg"
41
- />
42
- </div>
43
-
44
- <div className="space-y-2">
45
- <Label htmlFor="password">Password</Label>
46
- <InputPassword
47
- id="password"
48
- value={password}
49
- onChange={(event) => setPassword(event.target.value)}
50
- autoComplete="current-password"
51
- required
52
- className="h-11 rounded-lg"
53
- />
54
- </div>
55
-
56
- <Button type="submit" size="lg" className="h-11 w-full rounded-lg">
57
- Sign in
58
- </Button>
59
-
60
- <div className="flex flex-wrap items-center justify-between gap-x-4 gap-y-2 text-sm text-muted-foreground">
61
- <Link
62
- to="/forgot-password"
63
- className="transition-colors hover:text-foreground hover:underline hover:underline-offset-4"
64
- >
65
- Forgot password?
66
- </Link>
67
- <span>
68
- No account?{" "}
69
- <Link
70
- to="/register"
71
- className="font-semibold text-foreground underline underline-offset-4"
72
- >
73
- Sign up
74
- </Link>
75
- </span>
76
- </div>
77
-
78
- <div className="flex items-center gap-4 py-1">
79
- <div className="h-px flex-1 bg-border" />
80
- <span className="text-xs text-muted-foreground">
81
- Or continue with
82
- </span>
83
- <div className="h-px flex-1 bg-border" />
84
- </div>
85
-
86
- <div className="grid grid-cols-2 gap-3">
87
- <Button
88
- type="button"
89
- variant="outline"
90
- className="h-10 rounded-lg"
91
- onClick={() => login({ providerName: "google" })}
92
- >
93
- <Chrome />
94
- Google
95
- </Button>
96
- <Button
97
- type="button"
98
- variant="outline"
99
- className="h-10 rounded-lg"
100
- onClick={() => login({ providerName: "github" })}
101
- >
102
- <Github />
103
- GitHub
104
- </Button>
105
- </div>
106
- </form>
107
- </AuthLayout>
108
- );
109
- };
110
-
111
- SignInForm.displayName = "SignInForm";
1
+ export { DefaultSignInPage as SignInForm } from "./default-sign-in-page";
@@ -1,8 +1,8 @@
1
1
  "use client";
2
2
 
3
3
  import { useState } from "react";
4
- import { Chrome, Github } from "lucide-react";
5
4
  import { useLink, useNotification, useRegister } from "@refinedev/core";
5
+ import { useSearchParams } from "react-router";
6
6
 
7
7
  import { AuthLayout } from "@/components/auth/auth-layout";
8
8
  import { InputPassword } from "@/components/auth/input-password";
@@ -16,7 +16,8 @@ export const SignUpForm = () => {
16
16
  const [confirmPassword, setConfirmPassword] = useState("");
17
17
  const { open } = useNotification();
18
18
  const Link = useLink();
19
- const { mutate: register } = useRegister();
19
+ const [searchParams] = useSearchParams();
20
+ const { mutate: register, isPending } = useRegister();
20
21
 
21
22
  const handleSignUp = (event: React.FormEvent<HTMLFormElement>) => {
22
23
  event.preventDefault();
@@ -30,7 +31,11 @@ export const SignUpForm = () => {
30
31
  return;
31
32
  }
32
33
 
33
- register({ email, password });
34
+ register({
35
+ email,
36
+ password,
37
+ authenticator: searchParams.get("name") ?? undefined,
38
+ });
34
39
  };
35
40
 
36
41
  return (
@@ -49,7 +54,6 @@ export const SignUpForm = () => {
49
54
  autoComplete="email"
50
55
  autoFocus
51
56
  required
52
- className="h-11 rounded-lg"
53
57
  />
54
58
  </div>
55
59
 
@@ -61,7 +65,6 @@ export const SignUpForm = () => {
61
65
  onChange={(event) => setPassword(event.target.value)}
62
66
  autoComplete="new-password"
63
67
  required
64
- className="h-11 rounded-lg"
65
68
  />
66
69
  </div>
67
70
 
@@ -73,12 +76,15 @@ export const SignUpForm = () => {
73
76
  onChange={(event) => setConfirmPassword(event.target.value)}
74
77
  autoComplete="new-password"
75
78
  required
76
- className="h-11 rounded-lg"
77
79
  />
78
80
  </div>
79
81
 
80
- <Button type="submit" size="lg" className="h-11 w-full rounded-lg">
81
- Sign up
82
+ <Button
83
+ type="submit"
84
+ className="w-full"
85
+ disabled={isPending}
86
+ >
87
+ {isPending ? "Creating account…" : "Sign up"}
82
88
  </Button>
83
89
 
84
90
  <p className="text-center text-sm text-muted-foreground">
@@ -90,35 +96,6 @@ export const SignUpForm = () => {
90
96
  Sign in
91
97
  </Link>
92
98
  </p>
93
-
94
- <div className="flex items-center gap-4 py-1">
95
- <div className="h-px flex-1 bg-border" />
96
- <span className="text-xs text-muted-foreground">
97
- Or continue with
98
- </span>
99
- <div className="h-px flex-1 bg-border" />
100
- </div>
101
-
102
- <div className="grid grid-cols-2 gap-3">
103
- <Button
104
- type="button"
105
- variant="outline"
106
- className="h-10 rounded-lg"
107
- onClick={() => register({ providerName: "google" })}
108
- >
109
- <Chrome />
110
- Google
111
- </Button>
112
- <Button
113
- type="button"
114
- variant="outline"
115
- className="h-10 rounded-lg"
116
- onClick={() => register({ providerName: "github" })}
117
- >
118
- <Github />
119
- GitHub
120
- </Button>
121
- </div>
122
99
  </form>
123
100
  </AuthLayout>
124
101
  );
@@ -0,0 +1,38 @@
1
+ import type {
2
+ ComponentType,
3
+ LazyExoticComponent,
4
+ ReactNode,
5
+ } from "react";
6
+
7
+ export type Authenticator = {
8
+ name: string;
9
+ authType: string;
10
+ authTypeTitle?: string;
11
+ title?: string;
12
+ options?: Record<string, unknown>;
13
+ sort?: number;
14
+ };
15
+
16
+ export type AuthenticatorComponentProps = {
17
+ authenticator: Authenticator;
18
+ };
19
+
20
+ type AuthenticatorComponent = ComponentType<AuthenticatorComponentProps>;
21
+
22
+ export type AuthenticatorAdapter = {
23
+ authType: string;
24
+ placement: "form" | "button";
25
+ Component:
26
+ | AuthenticatorComponent
27
+ | LazyExoticComponent<AuthenticatorComponent>;
28
+ };
29
+
30
+ export type RenderAuthenticatorContext = {
31
+ authenticator: Authenticator;
32
+ adapter: AuthenticatorAdapter;
33
+ defaultElement: ReactNode;
34
+ };
35
+
36
+ export type RenderAuthenticator = (
37
+ context: RenderAuthenticatorContext
38
+ ) => ReactNode;
@@ -0,0 +1,14 @@
1
+ import { useLogin } from "@refinedev/core";
2
+
3
+ type SignInValues = Record<string, unknown>;
4
+
5
+ export function useAuthenticatorSignIn(authenticator: string) {
6
+ const mutation = useLogin<SignInValues & { authenticator: string }>();
7
+
8
+ return {
9
+ signIn: (values: SignInValues) =>
10
+ mutation.mutateAsync({ ...values, authenticator }),
11
+ isPending: mutation.isPending,
12
+ error: mutation.error,
13
+ };
14
+ }
@@ -0,0 +1,26 @@
1
+ import { useQuery } from "@tanstack/react-query";
2
+
3
+ import { nocobaseClient } from "@/lib/nocobase/client";
4
+ import type { Authenticator } from "@/components/auth/types";
5
+
6
+ export const publicAuthenticatorsQueryKey = [
7
+ "nocobase",
8
+ "public-authenticators",
9
+ ] as const;
10
+
11
+ export function usePublicAuthenticators() {
12
+ return useQuery({
13
+ queryKey: publicAuthenticatorsQueryKey,
14
+ queryFn: ({ signal }) =>
15
+ nocobaseClient.action<Authenticator[]>("authenticators", "publicList", {
16
+ method: "GET",
17
+ signal,
18
+ authenticator: null,
19
+ includeRole: false,
20
+ withAclMeta: false,
21
+ }),
22
+ staleTime: 0,
23
+ refetchOnMount: "always",
24
+ retry: false,
25
+ });
26
+ }