@owlmeans/web-panel 0.1.2 → 0.1.4

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 +88 -1160
  2. package/build/@/components/ui/alert.d.ts +10 -0
  3. package/build/@/components/ui/alert.d.ts.map +1 -0
  4. package/build/@/components/ui/alert.js +26 -0
  5. package/build/@/components/ui/alert.js.map +1 -0
  6. package/build/@/components/ui/button.d.ts +11 -0
  7. package/build/@/components/ui/button.d.ts.map +1 -0
  8. package/build/@/components/ui/button.js +32 -0
  9. package/build/@/components/ui/button.js.map +1 -0
  10. package/build/@/components/ui/card.d.ts +10 -0
  11. package/build/@/components/ui/card.d.ts.map +1 -0
  12. package/build/@/components/ui/card.js +25 -0
  13. package/build/@/components/ui/card.js.map +1 -0
  14. package/build/@/components/ui/input.d.ts +4 -0
  15. package/build/@/components/ui/input.d.ts.map +1 -0
  16. package/build/@/components/ui/input.js +7 -0
  17. package/build/@/components/ui/input.js.map +1 -0
  18. package/build/@/components/ui/label.d.ts +5 -0
  19. package/build/@/components/ui/label.d.ts.map +1 -0
  20. package/build/@/components/ui/label.js +8 -0
  21. package/build/@/components/ui/label.js.map +1 -0
  22. package/build/@/components/ui/progress.d.ts +5 -0
  23. package/build/@/components/ui/progress.d.ts.map +1 -0
  24. package/build/@/components/ui/progress.js +11 -0
  25. package/build/@/components/ui/progress.js.map +1 -0
  26. package/build/@/components/ui/separator.d.ts +5 -0
  27. package/build/@/components/ui/separator.d.ts.map +1 -0
  28. package/build/@/components/ui/separator.js +8 -0
  29. package/build/@/components/ui/separator.js.map +1 -0
  30. package/build/@/lib/utils.d.ts +3 -0
  31. package/build/@/lib/utils.d.ts.map +1 -0
  32. package/build/@/lib/utils.js +6 -0
  33. package/build/@/lib/utils.js.map +1 -0
  34. package/build/auth/exports.d.ts +4 -4
  35. package/build/auth/exports.d.ts.map +1 -1
  36. package/build/auth/exports.js +2 -2
  37. package/build/auth/exports.js.map +1 -1
  38. package/build/auth/modules.d.ts +1 -1
  39. package/build/auth/modules.d.ts.map +1 -1
  40. package/build/auth/plugins/basic-ed25519.js +2 -2
  41. package/build/auth/plugins/basic-ed25519.js.map +1 -1
  42. package/build/auth/plugins/re-captcha.d.ts.map +1 -1
  43. package/build/auth/plugins/re-captcha.js +4 -12
  44. package/build/auth/plugins/re-captcha.js.map +1 -1
  45. package/build/auth/plugins/tunnel-consumer.d.ts.map +1 -1
  46. package/build/auth/plugins/tunnel-consumer.js +28 -16
  47. package/build/auth/plugins/tunnel-consumer.js.map +1 -1
  48. package/build/components/block.d.ts.map +1 -1
  49. package/build/components/block.js +5 -8
  50. package/build/components/block.js.map +1 -1
  51. package/build/components/button/selector.d.ts.map +1 -1
  52. package/build/components/button/selector.js +1 -2
  53. package/build/components/button/selector.js.map +1 -1
  54. package/build/components/form/button/component.d.ts +1 -1
  55. package/build/components/form/button/component.d.ts.map +1 -1
  56. package/build/components/form/button/component.js +39 -8
  57. package/build/components/form/button/component.js.map +1 -1
  58. package/build/components/form/component.d.ts.map +1 -1
  59. package/build/components/form/component.js +10 -13
  60. package/build/components/form/component.js.map +1 -1
  61. package/build/components/form/text/component.d.ts +1 -1
  62. package/build/components/form/text/component.d.ts.map +1 -1
  63. package/build/components/form/text/component.js +23 -24
  64. package/build/components/form/text/component.js.map +1 -1
  65. package/build/components/form/types.d.ts +3 -2
  66. package/build/components/form/types.d.ts.map +1 -1
  67. package/build/components/helper.d.ts +11 -2
  68. package/build/components/helper.d.ts.map +1 -1
  69. package/build/components/helper.js +50 -40
  70. package/build/components/helper.js.map +1 -1
  71. package/build/components/layout/component.d.ts.map +1 -1
  72. package/build/components/layout/component.js +2 -3
  73. package/build/components/layout/component.js.map +1 -1
  74. package/build/components/layout/types.d.ts +3 -1
  75. package/build/components/layout/types.d.ts.map +1 -1
  76. package/build/components/link.d.ts.map +1 -1
  77. package/build/components/link.js +6 -4
  78. package/build/components/link.js.map +1 -1
  79. package/build/components/panel-app/component.d.ts.map +1 -1
  80. package/build/components/panel-app/component.js +4 -7
  81. package/build/components/panel-app/component.js.map +1 -1
  82. package/build/components/panel-app/types.d.ts +6 -2
  83. package/build/components/panel-app/types.d.ts.map +1 -1
  84. package/build/components/status.d.ts.map +1 -1
  85. package/build/components/status.js +18 -7
  86. package/build/components/status.js.map +1 -1
  87. package/build/components/text.d.ts.map +1 -1
  88. package/build/components/text.js +26 -3
  89. package/build/components/text.js.map +1 -1
  90. package/build/components/types.d.ts +18 -10
  91. package/build/components/types.d.ts.map +1 -1
  92. package/build/components/uploader/image.d.ts.map +1 -1
  93. package/build/components/uploader/image.js +11 -19
  94. package/build/components/uploader/image.js.map +1 -1
  95. package/build/exports.d.ts +3 -3
  96. package/build/exports.d.ts.map +1 -1
  97. package/build/exports.js +2 -2
  98. package/build/exports.js.map +1 -1
  99. package/build/main.d.ts +4 -2
  100. package/build/main.d.ts.map +1 -1
  101. package/build/main.js +4 -4
  102. package/build/main.js.map +1 -1
  103. package/build/modules.d.ts +1 -1
  104. package/build/modules.d.ts.map +1 -1
  105. package/components.json +21 -0
  106. package/package.json +53 -34
  107. package/src/@/components/ui/alert.tsx +70 -0
  108. package/src/@/components/ui/button.tsx +60 -0
  109. package/src/@/components/ui/card.tsx +93 -0
  110. package/src/@/components/ui/input.tsx +22 -0
  111. package/src/@/components/ui/label.tsx +23 -0
  112. package/src/@/components/ui/progress.tsx +44 -0
  113. package/src/@/components/ui/separator.tsx +27 -0
  114. package/src/@/globals.css +64 -0
  115. package/src/@/lib/utils.ts +6 -0
  116. package/src/auth/exports.ts +4 -4
  117. package/src/auth/plugins/basic-ed25519.tsx +4 -4
  118. package/src/auth/plugins/re-captcha.tsx +14 -22
  119. package/src/auth/plugins/tunnel-consumer.tsx +32 -24
  120. package/src/components/block.tsx +10 -14
  121. package/src/components/button/selector.tsx +9 -9
  122. package/src/components/form/button/component.tsx +54 -14
  123. package/src/components/form/component.tsx +23 -24
  124. package/src/components/form/text/component.tsx +39 -30
  125. package/src/components/form/types.ts +4 -3
  126. package/src/components/helper.ts +56 -42
  127. package/src/components/layout/component.tsx +2 -3
  128. package/src/components/layout/types.ts +3 -1
  129. package/src/components/link.tsx +19 -9
  130. package/src/components/panel-app/component.tsx +5 -9
  131. package/src/components/panel-app/types.ts +6 -2
  132. package/src/components/status.tsx +20 -9
  133. package/src/components/text.tsx +28 -9
  134. package/src/components/types.ts +22 -10
  135. package/src/components/uploader/image.tsx +23 -23
  136. package/src/exports.ts +3 -3
  137. package/src/main.tsx +8 -5
  138. package/tests/smoke.spec.ts +24 -0
  139. package/tsconfig.json +9 -11
@@ -0,0 +1,93 @@
1
+ // shadcn card — sourced from shadcn (new-york) 2026-05-23
2
+ import * as React from 'react'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ function Card({ className, ...props }: React.ComponentProps<"div">) {
7
+ return (
8
+ <div
9
+ data-slot="card"
10
+ className={cn(
11
+ "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
12
+ className
13
+ )}
14
+ {...props}
15
+ />
16
+ )
17
+ }
18
+
19
+ function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
20
+ return (
21
+ <div
22
+ data-slot="card-header"
23
+ className={cn(
24
+ "@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
25
+ className
26
+ )}
27
+ {...props}
28
+ />
29
+ )
30
+ }
31
+
32
+ function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
33
+ return (
34
+ <div
35
+ data-slot="card-title"
36
+ className={cn("leading-none font-semibold", className)}
37
+ {...props}
38
+ />
39
+ )
40
+ }
41
+
42
+ function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
43
+ return (
44
+ <div
45
+ data-slot="card-description"
46
+ className={cn("text-muted-foreground text-sm", className)}
47
+ {...props}
48
+ />
49
+ )
50
+ }
51
+
52
+ function CardAction({ className, ...props }: React.ComponentProps<"div">) {
53
+ return (
54
+ <div
55
+ data-slot="card-action"
56
+ className={cn(
57
+ "col-start-2 row-span-2 row-start-1 self-start justify-self-end",
58
+ className
59
+ )}
60
+ {...props}
61
+ />
62
+ )
63
+ }
64
+
65
+ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
66
+ return (
67
+ <div
68
+ data-slot="card-content"
69
+ className={cn("px-6", className)}
70
+ {...props}
71
+ />
72
+ )
73
+ }
74
+
75
+ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
76
+ return (
77
+ <div
78
+ data-slot="card-footer"
79
+ className={cn("flex items-center px-6 [.border-t]:pt-6", className)}
80
+ {...props}
81
+ />
82
+ )
83
+ }
84
+
85
+ export {
86
+ Card,
87
+ CardHeader,
88
+ CardFooter,
89
+ CardTitle,
90
+ CardAction,
91
+ CardDescription,
92
+ CardContent,
93
+ }
@@ -0,0 +1,22 @@
1
+ // shadcn input — sourced from shadcn (new-york) 2026-05-23
2
+ import * as React from 'react'
3
+
4
+ import { cn } from '@/lib/utils'
5
+
6
+ function Input({ className, type, ...props }: React.ComponentProps<"input">) {
7
+ return (
8
+ <input
9
+ type={type}
10
+ data-slot="input"
11
+ className={cn(
12
+ "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",
13
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
14
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
15
+ className
16
+ )}
17
+ {...props}
18
+ />
19
+ )
20
+ }
21
+
22
+ export { Input }
@@ -0,0 +1,23 @@
1
+ // shadcn label — sourced from shadcn (new-york) 2026-05-23
2
+ import * as React from 'react'
3
+ import * as LabelPrimitive from '@radix-ui/react-label'
4
+
5
+ import { cn } from '@/lib/utils'
6
+
7
+ function Label({
8
+ className,
9
+ ...props
10
+ }: React.ComponentProps<typeof LabelPrimitive.Root>) {
11
+ return (
12
+ <LabelPrimitive.Root
13
+ data-slot="label"
14
+ className={cn(
15
+ "flex items-center gap-2 text-sm leading-none font-medium select-none group-data-[disabled=true]:pointer-events-none group-data-[disabled=true]:opacity-50 peer-disabled:cursor-not-allowed peer-disabled:opacity-50",
16
+ className
17
+ )}
18
+ {...props}
19
+ />
20
+ )
21
+ }
22
+
23
+ export { Label }
@@ -0,0 +1,44 @@
1
+ // shadcn progress — sourced from shadcn (new-york) 2026-05-23
2
+ // Extended with `indeterminate` mode: when `value === undefined` (or not passed),
3
+ // the indicator animates left-to-right continuously via the
4
+ // `--animate-progress-indeterminate` token defined in globals.css.
5
+ import * as React from 'react'
6
+ import * as ProgressPrimitive from '@radix-ui/react-progress'
7
+
8
+ import { cn } from '@/lib/utils'
9
+
10
+ function Progress({
11
+ className,
12
+ value,
13
+ ...props
14
+ }: React.ComponentProps<typeof ProgressPrimitive.Root>) {
15
+ const indeterminate = value == null
16
+
17
+ return (
18
+ <ProgressPrimitive.Root
19
+ data-slot="progress"
20
+ className={cn(
21
+ "bg-primary/20 relative h-2 w-full overflow-hidden rounded-full",
22
+ className
23
+ )}
24
+ value={indeterminate ? undefined : value}
25
+ {...props}
26
+ >
27
+ <ProgressPrimitive.Indicator
28
+ data-slot="progress-indicator"
29
+ data-state={indeterminate ? 'indeterminate' : 'determinate'}
30
+ className={cn(
31
+ "bg-primary h-full w-full flex-1 transition-all",
32
+ indeterminate && "absolute inset-y-0 left-0 w-1/3 animate-[progress-indeterminate_1.5s_linear_infinite]"
33
+ )}
34
+ style={
35
+ indeterminate
36
+ ? undefined
37
+ : { transform: `translateX(-${100 - (value || 0)}%)` }
38
+ }
39
+ />
40
+ </ProgressPrimitive.Root>
41
+ )
42
+ }
43
+
44
+ export { Progress }
@@ -0,0 +1,27 @@
1
+ // shadcn separator — sourced from shadcn (new-york) 2026-05-23
2
+ import * as React from 'react'
3
+ import * as SeparatorPrimitive from '@radix-ui/react-separator'
4
+
5
+ import { cn } from '@/lib/utils'
6
+
7
+ function Separator({
8
+ className,
9
+ orientation = "horizontal",
10
+ decorative = true,
11
+ ...props
12
+ }: React.ComponentProps<typeof SeparatorPrimitive.Root>) {
13
+ return (
14
+ <SeparatorPrimitive.Root
15
+ data-slot="separator"
16
+ decorative={decorative}
17
+ orientation={orientation}
18
+ className={cn(
19
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
20
+ className
21
+ )}
22
+ {...props}
23
+ />
24
+ )
25
+ }
26
+
27
+ export { Separator }
@@ -0,0 +1,64 @@
1
+ @import "tailwindcss";
2
+
3
+ @source "../..";
4
+
5
+ @theme {
6
+ --color-background: oklch(1 0 0);
7
+ --color-foreground: oklch(0.145 0 0);
8
+
9
+ --color-card: oklch(1 0 0);
10
+ --color-card-foreground: oklch(0.145 0 0);
11
+
12
+ --color-popover: oklch(1 0 0);
13
+ --color-popover-foreground: oklch(0.145 0 0);
14
+
15
+ --color-primary: oklch(0.205 0 0);
16
+ --color-primary-foreground: oklch(0.985 0 0);
17
+
18
+ --color-secondary: oklch(0.97 0 0);
19
+ --color-secondary-foreground: oklch(0.205 0 0);
20
+
21
+ --color-muted: oklch(0.97 0 0);
22
+ --color-muted-foreground: oklch(0.556 0 0);
23
+
24
+ --color-accent: oklch(0.97 0 0);
25
+ --color-accent-foreground: oklch(0.205 0 0);
26
+
27
+ --color-destructive: oklch(0.577 0.245 27.325);
28
+ --color-success: oklch(0.62 0.18 145);
29
+
30
+ --color-border: oklch(0.922 0 0);
31
+ --color-input: oklch(0.922 0 0);
32
+ --color-ring: oklch(0.708 0 0);
33
+
34
+ --radius: 0.625rem;
35
+
36
+ --animate-progress-indeterminate: progress-indeterminate 1.5s linear infinite;
37
+
38
+ @keyframes progress-indeterminate {
39
+ 0% { transform: translateX(-100%); }
40
+ 100% { transform: translateX(100%); }
41
+ }
42
+ }
43
+
44
+ .dark {
45
+ --color-background: oklch(0.145 0 0);
46
+ --color-foreground: oklch(0.985 0 0);
47
+ --color-card: oklch(0.205 0 0);
48
+ --color-card-foreground: oklch(0.985 0 0);
49
+ --color-popover: oklch(0.205 0 0);
50
+ --color-popover-foreground: oklch(0.985 0 0);
51
+ --color-primary: oklch(0.922 0 0);
52
+ --color-primary-foreground: oklch(0.205 0 0);
53
+ --color-secondary: oklch(0.269 0 0);
54
+ --color-secondary-foreground: oklch(0.985 0 0);
55
+ --color-muted: oklch(0.269 0 0);
56
+ --color-muted-foreground: oklch(0.708 0 0);
57
+ --color-accent: oklch(0.269 0 0);
58
+ --color-accent-foreground: oklch(0.985 0 0);
59
+ --color-destructive: oklch(0.704 0.191 22.216);
60
+ --color-success: oklch(0.7 0.18 145);
61
+ --color-border: oklch(1 0 0 / 10%);
62
+ --color-input: oklch(1 0 0 / 15%);
63
+ --color-ring: oklch(0.556 0 0);
64
+ }
@@ -0,0 +1,6 @@
1
+ import { clsx, type ClassValue } from 'clsx'
2
+ import { twMerge } from 'tailwind-merge'
3
+
4
+ export function cn(...inputs: ClassValue[]) {
5
+ return twMerge(clsx(inputs))
6
+ }
@@ -3,11 +3,11 @@ export { handler, useModule, useValue, useNavigate } from '@owlmeans/client'
3
3
  export type { Navigator } from '@owlmeans/client'
4
4
  export { config } from '@owlmeans/client-context'
5
5
  export { service } from '@owlmeans/config'
6
- export { guard, parent, ModuleOutcome, clone } from '@owlmeans/module'
7
- export type { AbstractRequest } from '@owlmeans/module'
6
+ export { guard, parent, EntrypointOutcome, clone } from '@owlmeans/entrypoint'
7
+ export type { AbstractRequest } from '@owlmeans/entrypoint'
8
8
  export { addWebService } from '@owlmeans/client-config'
9
- export { module, elevate, provideRequest, stab } from '@owlmeans/client-module'
10
- export type { ClientModule as Module, ClientRequest as Request } from '@owlmeans/client-module'
9
+ export { entrypoint, elevate, provideRequest, stab } from '@owlmeans/client-entrypoint'
10
+ export type { ClientEntrypoint as Module, ClientRequest as Request } from '@owlmeans/client-entrypoint'
11
11
  export { route as croute } from '@owlmeans/client-route'
12
12
  export { route, frontend, RouteMethod } from '@owlmeans/route'
13
13
  export type { ResolvedServiceRoute as ServiceRoute } from '@owlmeans/route'
@@ -1,7 +1,7 @@
1
1
  import type { Ed22519BasicAuthUIPluginForm as FormData } from '@owlmeans/client-panel/auth/plugins'
2
2
  import type { AuthenticationControl, AuthenticationRenderer } from '@owlmeans/client-auth/manager'
3
3
  import type { AppContext } from '../types.js'
4
- import type { ClientModule } from '@owlmeans/client-module'
4
+ import type { ClientEntrypoint } from '@owlmeans/client-entrypoint'
5
5
  import type { AuthRequest } from '@owlmeans/auth-common'
6
6
 
7
7
  import { Ed22519BasicAuthUIPluginFormSchema as Schema } from '@owlmeans/client-panel/auth/plugins'
@@ -9,7 +9,7 @@ import { AuthenticationStage, DISPATCHER } from '@owlmeans/auth'
9
9
  import { Form } from '../../components/form/index.js'
10
10
  import { TextInput } from '../../components/form/text/index.js'
11
11
  import { useCallback } from 'react'
12
- import LinearProgress from '@mui/material/LinearProgress'
12
+ import { Progress } from '@/components/ui/progress'
13
13
  import { BlockScaling } from '@owlmeans/client-panel'
14
14
  import { useContext } from '@owlmeans/web-client'
15
15
 
@@ -24,7 +24,7 @@ export const Ed22519BasicAuthUIPlugin: AuthenticationRenderer = ({ type, stage,
24
24
  name => <TextInput key={name} name={name} label />
25
25
  )
26
26
  default:
27
- return <LinearProgress />
27
+ return <Progress />
28
28
  }
29
29
  }, [stage])
30
30
 
@@ -46,7 +46,7 @@ const createSubmit = (context: AppContext, control: AuthenticationControl) => as
46
46
  return
47
47
  }
48
48
 
49
- const [url] = await context.module<ClientModule<string, AuthRequest>>(DISPATCHER)
49
+ const [url] = await context.module<ClientEntrypoint<string, AuthRequest>>(DISPATCHER)
50
50
  .call({ query: token })
51
51
 
52
52
  control.setStage?.(control.stage = AuthenticationStage.Authenticated)
@@ -1,6 +1,5 @@
1
1
  import type { AuthenticationControl, AuthenticationRenderer } from '@owlmeans/client-auth/manager'
2
2
 
3
- import LinearProgress from '@mui/material/LinearProgress'
4
3
  import { AuthenticationStage, AuthorizationError, CMOD_RECAPTCHA, DISPATCHER, GUEST_ID } from '@owlmeans/auth'
5
4
  import { useValue } from '@owlmeans/client'
6
5
  import { PLUGINS } from '@owlmeans/client-context'
@@ -8,9 +7,7 @@ import ReCAPTCHA from 'react-google-recaptcha'
8
7
  import { useCallback, useState } from 'react'
9
8
  import { PanelContext } from '@owlmeans/client-panel'
10
9
  import { Text } from '../../components/text.js'
11
- import Stack from '@mui/material/Stack'
12
- import Box from '@mui/material/Box'
13
- import type { SxProps } from '@mui/material/styles'
10
+ import { Progress } from '@/components/ui/progress'
14
11
  import type { AppContext, Module } from '@owlmeans/web-client'
15
12
  import { useContext } from '@owlmeans/web-client'
16
13
  import type { AuthRequest } from '@owlmeans/auth-common'
@@ -21,33 +18,28 @@ export const ReCaptchaAuthUIPlugin: AuthenticationRenderer = ({ stage, control }
21
18
  const config = useValue(() => context.getConfigResource(PLUGINS).get(CMOD_RECAPTCHA), [])
22
19
  const finish = useCallback(createFinish(context, control), [])
23
20
 
24
- const style: SxProps = {
25
- maxWidth: { xs: '100%', sm: '100%', md: '50%' }
26
- }
27
- const loadingStyle: SxProps = {
28
- width: { xs: '100%', sm: '100%', md: '50%' }
29
- }
30
-
31
21
  const content = () => {
32
22
  switch (config?.value != null ? stage : null) {
33
23
  case AuthenticationStage.Authenticate:
34
- return <Stack direction="column" sx={style} justifyContent="center" alignItems="center">
35
- <Text name="guideline" center />
36
- <Box sx={{ pt: 2 }}>
37
- <ReCAPTCHA sitekey={config?.value as string ?? ''} onChange={finish}
38
- asyncScriptOnLoad={() => setLoading(false)} />
39
- </Box>
40
- </Stack>
24
+ return (
25
+ <div className="flex flex-col items-center justify-center max-w-full md:max-w-[50%]">
26
+ <Text name="guideline" center />
27
+ <div className="pt-4">
28
+ <ReCAPTCHA sitekey={config?.value as string ?? ''} onChange={finish}
29
+ asyncScriptOnLoad={() => setLoading(false)} />
30
+ </div>
31
+ </div>
32
+ )
41
33
  default:
42
- return <LinearProgress sx={loadingStyle} />
34
+ return <Progress className="w-full md:w-1/2" />
43
35
  }
44
36
  }
45
37
 
46
38
  return <PanelContext ns="lib" prefix="re-captcha" resource="client-panel-auth">
47
- <Stack direction="column" justifyContent="center" alignItems="center">
39
+ <div className="flex flex-col items-center justify-center">
48
40
  {content()}
49
- {loading && <LinearProgress sx={loadingStyle} />}
50
- </Stack>
41
+ {loading && <Progress className="w-full md:w-1/2" />}
42
+ </div>
51
43
  </PanelContext>
52
44
  }
53
45
 
@@ -1,21 +1,38 @@
1
- import LinearProgress from '@mui/material/LinearProgress'
2
1
  import { AuthenticationStage } from '@owlmeans/auth'
3
2
  import type { RelyToken } from '@owlmeans/auth'
4
3
  import type { TunnelAuthenticationRenderer } from '@owlmeans/client-auth/manager/plugins'
5
4
  import { PinSchema } from '@owlmeans/client-auth/manager/plugins'
6
5
  import { Form } from '../../components/form/component.js'
7
6
  import { TextInput } from '../../components/form/text/component.js'
8
- // import { Text } from '../../components/text.js'
9
- import { useMemo } from 'react'
7
+ import { useEffect, useMemo, useState } from 'react'
10
8
  import { EnvelopeKind, makeEnvelopeModel } from '@owlmeans/basic-envelope'
11
9
  import { Block } from '../../components/block.js'
12
10
  import { BlockScaling } from '@owlmeans/client-panel'
13
11
  import { Status } from '../../components/status.js'
14
- import type { SxProps } from '@mui/material/styles'
15
- import { useTheme } from '@mui/material/styles'
16
12
  import { Button } from '../../components/form/button/component.js'
17
13
  import { QRCodeCanvas } from 'qrcode.react'
18
- import Box from '@mui/material/Box'
14
+ import { Progress } from '@/components/ui/progress'
15
+
16
+ /**
17
+ * Read a CSS variable from `:root` (consumer-owned theme). Falls back to
18
+ * a sane default if the variable is undefined.
19
+ */
20
+ const readCssVar = (name: string, fallback: string): string => {
21
+ if (typeof window === 'undefined') return fallback
22
+ const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim()
23
+ return value !== '' ? value : fallback
24
+ }
25
+
26
+ const useThemeColors = () => {
27
+ const [colors, setColors] = useState({ fg: '#000000', bg: '#ffffff' })
28
+ useEffect(() => {
29
+ setColors({
30
+ fg: readCssVar('--color-primary', '#000000'),
31
+ bg: readCssVar('--color-card', '#ffffff'),
32
+ })
33
+ }, [])
34
+ return colors
35
+ }
19
36
 
20
37
  export const TunnelConsumerUIPlugin: TunnelAuthenticationRenderer = ({ type, stage, control, params, submit }) => {
21
38
  const rely = useMemo(() => {
@@ -28,38 +45,29 @@ export const TunnelConsumerUIPlugin: TunnelAuthenticationRenderer = ({ type, sta
28
45
  ).message()
29
46
  }, [stage])
30
47
 
31
- const theme = useTheme()
32
- const prefix = "prefix" in params ? params.prefix as string : ""
33
- const i18n = { ns: "lib", resource: 'client-panel-auth' }
34
-
35
- const loadingStyle: SxProps = {
36
- width: { xs: '100%', sm: '100%', md: '50%' }
37
- }
48
+ const { fg, bg } = useThemeColors()
49
+ const prefix = 'prefix' in params ? params.prefix as string : ''
50
+ const i18n = { ns: 'lib', resource: 'client-panel-auth' }
38
51
 
39
52
  switch (stage) {
40
53
  case AuthenticationStage.Authenticate:
41
54
  return <Form decorate name={type} validation={PinSchema} onSubmit={submit} i18n={i18n}>
42
- <Box width="fit-content" margin="auto">
55
+ <div className="w-fit mx-auto">
43
56
  {rely?.token != null && <QRCodeCanvas size={256}
44
- value={`${prefix}${rely?.token ?? ""}`}
45
- fgColor={theme.palette.primary.dark}
46
- bgColor={theme.palette.background.paper}
57
+ value={`${prefix}${rely?.token ?? ''}`}
58
+ fgColor={fg}
59
+ bgColor={bg}
47
60
  />}
48
- </Box>
49
- {/* <Text>{rely?.token}</Text> */}
61
+ </div>
50
62
  <TextInput name="pin" label hint />
51
63
  </Form>
52
64
  case AuthenticationStage.Error:
53
65
  return <Block horizontal={BlockScaling.Half} i18n={i18n} Actions={() =>
54
- // @TODO document reload looks a bit dirty - cause we can loose the flow
55
66
  <Button label="reset" onClick={async () => { document.location.reload() }} />
56
- // <Button label="reset" onClick={async () => {
57
- // await control.updateStage(AuthenticationStage.Authenticate)
58
- // }} />
59
67
  }>
60
68
  <Status error={control.error} ok={false} />
61
69
  </Block>
62
70
  }
63
71
 
64
- return <LinearProgress sx={loadingStyle} />
72
+ return <Progress className="w-full md:w-1/2" />
65
73
  }
@@ -1,27 +1,23 @@
1
1
  import { useMemo } from 'react'
2
2
  import type { FC } from 'react'
3
3
  import type { BlockProps } from './types.js'
4
- import Card from '@mui/material/Card'
5
- import CardContent from '@mui/material/CardContent'
6
4
  import { PanelContext, usePanelHelper } from '@owlmeans/client-panel'
7
- import type { SxProps } from '@mui/material/styles'
8
- import { useTheme } from '@mui/material/styles'
9
- import CardActions from '@mui/material/CardActions'
5
+ import { Card, CardContent, CardFooter } from '@/components/ui/card'
6
+ import { cn } from '@/lib/utils'
10
7
  import { scalingToStyles } from './helper.js'
11
8
 
12
- export const Block: FC<BlockProps> = ({ children, horizontal, vertical, Actions, i18n, styles }) => {
13
- const theme = useTheme()
14
-
15
- const style: SxProps = useMemo(() => scalingToStyles(horizontal, vertical, theme), [horizontal])
16
-
9
+ export const Block: FC<BlockProps> = ({ children, horizontal, vertical, Actions, i18n, className, style }) => {
10
+ const scaling = useMemo(() => scalingToStyles(horizontal, vertical), [horizontal, vertical])
17
11
  const panelProps = { ...usePanelHelper(), ...i18n }
18
12
 
19
13
  return <PanelContext {...panelProps}>
20
- <Card sx={{ ...style, ...styles } as SxProps}>
14
+ <Card className={cn(scaling, className)} style={style}>
21
15
  <CardContent>{children}</CardContent>
22
- {Actions != null && <CardActions sx={{ flexDirection: "row", justifyContent: "flex-end", pr: 2, pb: 2 }}>
23
- <Actions />
24
- </CardActions>}
16
+ {Actions != null && (
17
+ <CardFooter className="flex flex-row justify-end gap-2 pr-4 pb-2">
18
+ <Actions />
19
+ </CardFooter>
20
+ )}
25
21
  </Card>
26
22
  </PanelContext>
27
23
  }
@@ -1,16 +1,16 @@
1
-
2
- import ButtonGroup from '@mui/material/ButtonGroup'
3
1
  import type { FC } from 'react'
4
2
  import type { SelectorProps } from './types.js'
5
3
  import { Button } from '../form/button/component.js'
6
4
 
7
5
  export const ButtonSelector: FC<SelectorProps> = ({ name, options, current, onSelect }) => {
8
6
  const prefix = name != null ? `${name}.` : ''
9
- return <ButtonGroup>
10
- {options.map(
11
- option => <Button key={option} label={`${prefix}${option}`}
12
- onClick={() => onSelect?.(option)}
13
- variant={current === option ? 'contained' : 'outlined'} />
14
- )}
15
- </ButtonGroup>
7
+ return (
8
+ <div className="inline-flex rounded-md shadow-xs [&>button:not(:first-child)]:rounded-l-none [&>button:not(:last-child)]:rounded-r-none [&>button:not(:last-child)]:border-r-0">
9
+ {options.map(option =>
10
+ <Button key={option} label={`${prefix}${option}`}
11
+ onClick={() => onSelect?.(option)}
12
+ variant={current === option ? 'contained' : 'outlined'} />
13
+ )}
14
+ </div>
15
+ )
16
16
  }
@@ -1,15 +1,48 @@
1
1
  import { useMemo } from 'react'
2
2
  import type { FC } from 'react'
3
3
  import { memo } from 'react'
4
- import type { ButtonProps, SubmitProps } from './types'
5
-
6
- import MUIButton from '@mui/material/Button'
7
- import CircularProgress from '@mui/material/CircularProgress'
8
-
4
+ import type { ButtonProps, SubmitProps } from './types.js'
9
5
  import { useFormContext } from 'react-hook-form'
6
+ import { Loader2 } from 'lucide-react'
10
7
  import { I18nProps, useCommonI18n, useI18nApp, useI18nLib } from '@owlmeans/client-i18n'
11
8
  import { useContext } from '@owlmeans/client'
12
9
  import { useFormI18n, usePanelHelper } from '@owlmeans/client-panel'
10
+ import { Button as UIButton } from '@/components/ui/button'
11
+ import { cn } from '@/lib/utils'
12
+
13
+ /**
14
+ * MUI → shadcn variant mapping:
15
+ * contained → default
16
+ * outlined → outline
17
+ * text → ghost
18
+ * Any other value is forwarded as-is (shadcn variants: default, destructive,
19
+ * outline, secondary, ghost, link).
20
+ */
21
+ const mapVariant = (v: string | undefined): 'default' | 'destructive' | 'outline' | 'secondary' | 'ghost' | 'link' => {
22
+ switch (v) {
23
+ case undefined:
24
+ case 'contained': return 'default'
25
+ case 'outlined': return 'outline'
26
+ case 'text': return 'ghost'
27
+ case 'destructive':
28
+ case 'outline':
29
+ case 'secondary':
30
+ case 'ghost':
31
+ case 'link':
32
+ case 'default':
33
+ return v
34
+ default: return 'default'
35
+ }
36
+ }
37
+
38
+ const mapSize = (s: ButtonProps['size']): 'sm' | 'default' | 'lg' => {
39
+ switch (s) {
40
+ case 'small': return 'sm'
41
+ case 'large': return 'lg'
42
+ case 'medium':
43
+ default: return 'default'
44
+ }
45
+ }
13
46
 
14
47
  export const Button: FC<ButtonProps> = memo(({ label, onClick, i18n, loader, size, fullWidth, variant = 'contained' }) => {
15
48
  const context = useContext()
@@ -25,15 +58,22 @@ export const Button: FC<ButtonProps> = memo(({ label, onClick, i18n, loader, siz
25
58
  defaultValue: appT(label, { defaultValue: libT(label) })
26
59
  }), [i18n?.suppress, label])
27
60
 
28
- size = size ?? 'medium'
29
- const progressSize = size === 'large'
30
- ? 20
31
- : size === 'medium' ? 16 : 14
61
+ const disabled = loader != null && loader.opened === true
62
+ const showLoader = disabled
32
63
 
33
- return <MUIButton variant={variant as any} size={size} fullWidth={fullWidth}
34
- startIcon={loader != null && loader.opened === true ? <CircularProgress size={progressSize} /> : undefined}
35
- disabled={loader != null && loader.opened === true}
36
- onClick={onClick}>{label}</MUIButton>
64
+ return (
65
+ <UIButton
66
+ type="button"
67
+ variant={mapVariant(variant)}
68
+ size={mapSize(size)}
69
+ disabled={disabled}
70
+ onClick={onClick}
71
+ className={cn(fullWidth && 'w-full')}
72
+ >
73
+ {showLoader && <Loader2 className="animate-spin" aria-hidden />}
74
+ {label}
75
+ </UIButton>
76
+ )
37
77
  })
38
78
 
39
79
  export const SubmitButton: FC<SubmitProps> = memo((props) => {
@@ -42,7 +82,7 @@ export const SubmitButton: FC<SubmitProps> = memo((props) => {
42
82
  const t = useFormI18n()
43
83
 
44
84
  label = label ?? 'submit'
45
- const _i18n: I18nProps["i18n"] = { ...i18n }
85
+ const _i18n: I18nProps['i18n'] = { ...i18n }
46
86
  _i18n.suppress = true
47
87
 
48
88
  return <Button {...props} label={t(label)} i18n={_i18n}