@niledatabase/react 2.3.0-alpha.1 → 2.3.0-alpha.3

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.
@@ -1,2 +1,1225 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("@niledatabase/browser"),r=require("@tanstack/react-query"),n=require("@mui/material/CssBaseline"),a=require("@mui/joy/styles"),o=require("@mui/joy/Box"),l=require("@mui/joy/Button"),i=require("@mui/joy/Stack"),c=require("@mui/joy/Typography"),s=require("@mui/joy/Alert"),u=require("react-hook-form"),m=require("@mui/joy/Input"),d=require("@mui/joy/FormControl"),p=require("@mui/joy/FormHelperText"),b=require("@mui/icons-material/Error"),g=require("@mui/joy/FormLabel"),h=require("@mui/joy/Select"),f=require("@mui/joy/Option"),E=require("@mui/joy/Tooltip"),x=require("@mui/joy"),y=require("@mui/joy/Checkbox"),w=require("@mui/joy/List"),v=require("@mui/joy/ListItem"),T=require("@mui/x-data-grid"),S=require("@mui/icons-material/Add"),C=require("lodash/isNull"),q=require("lodash/isUndefined"),k=require("@mui/icons-material/CopyAll"),F=require("@mui/icons-material/CheckCircleOutlined");function A(e){var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var n=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,n.get?n:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,t}var j=A(e);function P({theme:t,children:r,slotProps:o}){return e.createElement(a.CssVarsProvider,{...o,theme:t},e.createElement(n,{enableColorScheme:!0}),r)}const I=new r.QueryClient,M={api:new t({basePath:"https://api.thenile.dev",credentials:"include"}),basePath:""},U=e.createContext(M),{Provider:O}=U,R=({children:t})=>e.createElement(r.QueryClientProvider,{client:I},t),B=()=>e.useContext(U),V=()=>{const{basePath:t,tenantId:r}=B();return e.useMemo((()=>({tenantId:r,basePath:t})),[t,r])},D=()=>B().api;var L;function N(){return N=Object.assign?Object.assign.bind():function(e){for(var t=1;t<arguments.length;t++){var r=arguments[t];for(var n in r)Object.prototype.hasOwnProperty.call(r,n)&&(e[n]=r[n])}return e},N.apply(this,arguments)}var $=function(e){return j.createElement("svg",N({xmlns:"http://www.w3.org/2000/svg",width:18,height:18},e),L||(L=j.createElement("g",{fillRule:"evenodd"},j.createElement("path",{fill:"#EA4335",d:"M9 3.48c1.69 0 2.83.73 3.48 1.34l2.54-2.48C13.46.89 11.43 0 9 0 5.48 0 2.44 2.02.96 4.96l2.91 2.26C4.6 5.05 6.62 3.48 9 3.48"}),j.createElement("path",{fill:"#4285F4",d:"M17.64 9.2c0-.74-.06-1.28-.19-1.84H9v3.34h4.96c-.1.83-.64 2.08-1.84 2.92l2.84 2.2c1.7-1.57 2.68-3.88 2.68-6.62"}),j.createElement("path",{fill:"#FBBC05",d:"M3.88 10.78A5.5 5.5 0 0 1 3.58 9c0-.62.11-1.22.29-1.78L.96 4.96A9 9 0 0 0 0 9c0 1.45.35 2.82.96 4.04z"}),j.createElement("path",{fill:"#34A853",d:"M9 18c2.43 0 4.47-.8 5.96-2.18l-2.84-2.2c-.76.53-1.78.9-3.12.9-2.38 0-4.4-1.57-5.12-3.74L.97 13.04C2.45 15.98 5.48 18 9 18"}),j.createElement("path",{fill:"none",d:"M0 0h18v18H0z"}))))};function z(e){const{options:t,attribute:r,display:n,helperText:a}=e,{watch:l,control:s}=u.useFormContext(),m=l(r.name),d={};return a&&(d.color="danger"),j.createElement(u.Controller,{name:r.name,rules:{required:Boolean(r.required)},control:s,render:({field:e})=>j.createElement(i,null,j.createElement(g,{htmlFor:`${e.name}`},n.label),j.createElement(o,{role:"group","aria-labelledby":r.name,sx:{borderRadius:"var(--joy-radius-sm)",p:.5,border:a?"1px solid var(--joy-palette-danger-outlinedBorder)":"none"}},j.createElement(w,{orientation:"horizontal",wrap:!0,sx:{"--List-gap":"8px"}},t.map((n=>(d.id=String(n.value),j.createElement(v,{key:`${n.value}-${n.label}`},j.createElement(y,{overlay:t.length>1,...d,checked:m.includes(n.value),disableIcon:t.length>1,variant:"soft",label:n.label,onChange:t=>{if(r.allowMultiple)if(t.target.checked)m?e.onChange(m.concat(n.value)):e.onChange([n.value]);else{const t=m.filter((e=>e!==n.value));t.length>0?e.onChange(t):e.onChange("")}else t.target.checked?e.onChange(n.value):e.onChange("")}}))))))),j.createElement(c,{sx:{color:"var(--joy-palette-danger-500)"},level:"body-sm"},a))})}var G;exports.FormAttributeType=void 0,(G=exports.FormAttributeType||(exports.FormAttributeType={})).Text="text",G.Password="password",G.Select="select",G.Number="number",G.Float="float",G.Checkbox="checkbox",G.Switch="switch";const H=e=>!0===e.allowMultiple&&!Array.isArray(e.defaultValue)&&e.defaultValue?"number"==typeof e.defaultValue?[e.defaultValue]:[String(e.defaultValue)]:e.defaultValue??"";function W(t){const{error:r,attr:n}=t;return r?e.createElement(E,{title:r,color:"danger",sx:{cursor:"pointer"}},e.createElement(g,null,n.label??n.name,e.createElement(b,{sx:{ml:.5,"--Icon-color":"#c41c1c"},fontSize:"small"}))):e.createElement(g,null,n.label??n.name)}function Q(t){const{mutation:r,buttonText:n,attributes:a,cancelButton:c,loading:s,successMessage:b}=t,g=e.useMemo((()=>a.reduce(((e,t)=>(e[t.name]=H(t),e)),{})),[a]),E=u.useForm({defaultValues:g}),{register:y,control:w,handleSubmit:v,formState:{errors:T}}=E,S=e.useCallback((e=>{r.mutate(e)}),[r]);return e.createElement(u.FormProvider,{...E},e.createElement(i,{component:"form",onSubmit:v((e=>S(e))),spacing:2},a.map((t=>{const r={},n={key:t.name,label:t.label??t.name,id:t.label??t.name,placeholder:t.placeholder??t.label??t.name,error:Boolean(T[t.name]),disabled:Boolean(t.disabled)},a=t.options??[],l=t.helpText??"";let c="";switch(t.required&&(c=T[t.name]?`${t.label??t.name} is required`:"",r.required=!0),t.type){case exports.FormAttributeType.Switch:return e.createElement(d,{key:n.key,id:n.id,orientation:"horizontal",sx:{alignItems:"center"}},e.createElement(o,null,e.createElement(W,{error:c,attr:t}),e.createElement(p,{id:`${t.name}-helper-text`},l)),e.createElement(u.Controller,{control:w,rules:{required:Boolean(t.required)},name:t.name,render:({field:r})=>{const n={};return T[t.name]&&(n.color="danger"),e.createElement(x.Switch,{id:`switch-field-${t.name}`,...n,...r,checked:Boolean(r.value),onChange:e=>{r.onChange(e.target.checked)},color:r.value?"success":"neutral",endDecorator:r.value?a[0].label:a[1].label})}}));case exports.FormAttributeType.Checkbox:return e.createElement(z,{key:n.key,attribute:t,display:n,options:a,helperText:l});case exports.FormAttributeType.Select:return e.createElement(d,{key:n.key,id:n.id},e.createElement(W,{error:c,attr:t}),e.createElement(u.Controller,{control:w,rules:{required:Boolean(t.required)},name:t.name,render:({field:r})=>{const o={};T[t.name]&&(o.color="danger");const c=String(r.value);return e.createElement(i,null,e.createElement(h,{id:`select-field-${t.name}`,placeholder:`${n.placeholder}...`,...o,...r,value:c,onChange:(e,t)=>{r.onChange(t)}},a.map((t=>e.createElement(f,{key:String(t.value??""),value:t.value},t.label)))),e.createElement(p,{id:`${t.name}-helper-text`},l))}}));case exports.FormAttributeType.Password:return e.createElement(d,{key:n.key,id:n.id},e.createElement(W,{error:c,attr:t}),e.createElement(m,{...n,...y(t.name,r),type:exports.FormAttributeType.Password}),e.createElement(p,{id:`${t.name}-helper-text`},l));case exports.FormAttributeType.Number:return e.createElement(d,{key:n.key,id:n.id},e.createElement(W,{error:c,attr:t}),e.createElement(m,{...n,...y(t.name,r),type:exports.FormAttributeType.Number}),e.createElement(p,{id:`${t.name}-helper-text`},l));case exports.FormAttributeType.Text:default:return e.createElement(d,{key:n.key,id:n.id},e.createElement(W,{error:c,attr:t}),e.createElement(m,{...n,...y(t.name,r)}),e.createElement(p,{id:`${t.name}-helper-text`},l))}})),c?e.createElement(i,{spacing:2,direction:"row"},c,e.createElement(o,null,e.createElement(l,{type:"submit"},n))):e.createElement(o,null,e.createElement(i,{direction:"row",gap:2},e.createElement(l,{type:"submit",loading:s},n),b))))}function _(t){const{open:n,setOpen:a,refetch:o}=t,{tenantId:l}=V(),i=D(),[c,s]=e.useState(),{watch:m,register:d,handleSubmit:p}=u.useForm(),b=m("email");e.useEffect((()=>{null!=c&&s()}),[b]);const g=r.useMutation((e=>i.users.createTenantUser({signUpRequest:e,tenantId:String(l)})),{onSuccess(e){o&&o(e),a(!1)},onError(e){e instanceof Error&&s(e.message)}}),h=e.useCallback((async e=>{s(""),g.mutate(e)}),[g]);return e.createElement(x.Modal,{open:n},e.createElement(x.ModalDialog,null,e.createElement(x.Stack,{spacing:2},e.createElement(x.Typography,{level:"h4"},"Create user"),e.createElement(e.Fragment,null,c&&e.createElement(x.Typography,{color:"danger"},c)),e.createElement(x.Stack,{component:"form",sx:{width:"40ch"},spacing:1,onSubmit:p((e=>h(e)))},e.createElement(x.FormControl,{sx:{"--FormHelperText-color":"var(--joy-palette-danger-500)"}},e.createElement(x.FormLabel,{htmlFor:"email"},"Email"),e.createElement(x.Input,{...d("email"),fullWidth:!0,size:"lg",id:"email",name:"email",autoComplete:"current-email",required:!0,error:Boolean(c)})),e.createElement(x.FormControl,{sx:{"--FormHelperText-color":"var(--joy-palette-danger-500)"}},e.createElement(x.FormLabel,{htmlFor:"password"},"Password"),e.createElement(x.Input,{...d("password"),fullWidth:!0,size:"lg",id:"password",autoComplete:"current-password",type:"password",required:!0})),e.createElement(x.Stack,{direction:"row",sx:{pt:2},spacing:2},e.createElement(x.Button,{onClick:()=>a(!1),variant:"plain"},"Cancel"),e.createElement(x.Button,{type:"submit"},"Create"))))))}function J(t){const{allowCreation:r,buttonText:n,onUserCreateSuccess:a}=t,[o,c]=e.useState(!1);return r?e.createElement(i,{alignItems:"flex-end",gap:1},e.createElement(_,{open:o,setOpen:c,refetch:a}),e.createElement(l,{startDecorator:e.createElement(S,null),size:"sm",onClick:()=>c(!0)},n)):null}const K=e=>Object.keys(e).reduce(((t,r)=>{const n=e[r];return n instanceof Set?t[r]=Array.from(n).join(", "):Array.isArray(n)?t[r]=n.join(", "):t[r]=n,t}),{}),X=(e,t,r)=>{if(!e)return[[],[]];const n=e.map(K),a=Object.keys(n[0]),o={},l=a?.map((e=>{const a=function(e,t,r){let n=r&&e?r.measureText(String(e)).width:50;n+=27;let a=n;return a=16+Math.ceil(r?r.measureText(t.reduce(((t,r)=>{let n=r[String(e)];return(C(n)||q(n))&&(n=""),n=n?.toString(),t.length>n.length?t:n}),"")).width:50),a<n&&(a=n),a+=8,a}(e,n,t),l=e.slice();if(r.includes(l))return null==o[l]?o[l]=l.length:o[l]+=1,{field:l.padEnd(o[l]),headerName:l.padEnd(o[l]),width:a}})).filter(Boolean)??[];return[l,n]};function Y(t,r){const n=function(){const[t,r]=e.useState();return e.useEffect((()=>{const e=document.createElement("canvas").getContext("2d");e&&(e.font="18px Roboto",r(e))}),[]),t}(),[a,o]=e.useMemo((()=>X(t,n,r)),[t,n,r]);return[a,o]}function Z(t){const{config:n,providerName:a,onSuccess:o,onError:l,allowEdit:u=!0,configurationGuide:m}=t,d=D(),[p,b]=e.useState(!1),[g,h]=e.useState(!1),[f,E]=e.useState(n),x=e.useRef(),y=e.useMemo((()=>{const e=[{name:"enabled",label:"Allow Okta logins",type:exports.FormAttributeType.Switch,defaultValue:!0===f?.enabled,options:[{label:"Enabled"},{label:"Disabled"}],disabled:!u},{name:"clientId",label:"Client id",type:exports.FormAttributeType.Text,defaultValue:f?.clientId??"",required:!0,disabled:!u},{name:"configUrl",label:"Config url",type:exports.FormAttributeType.Text,defaultValue:f?.configUrl??"",helpText:"The URL of the .well-known/openid-configuration for the identity provider",required:!0,disabled:!u},{name:"emailDomains",label:"Email domains",type:exports.FormAttributeType.Text,defaultValue:f?.emailDomains?.join(", ")??"",required:!0,helpText:"A comma seperated list of email domains (yourDomain.com) to be used",disabled:!u}];return f?.clientId||e.splice(2,0,{name:"clientSecret",label:"Client secret",type:exports.FormAttributeType.Password,defaultValue:"",required:!0,disabled:!u}),e}),[u,f?.clientId,f?.configUrl,f?.emailDomains,f?.enabled]),w=r.useMutation((e=>{b(!0);const t={providerName:a.toLowerCase(),updateProviderRequest:{...e,emailDomains:e.emailDomains.split(",")}};return null!=f?d.auth.updateProvider(t):d.auth.createProvider(t)}),{onSuccess:(e,t)=>{E(e),h(!0),o&&o(e,t)},onError:l,onSettled:(e,t,r)=>{b(!1),x.current&&clearTimeout(x.current),x.current=setTimeout((()=>{h(!1)}),3e3),e||(t&&!t?.message.includes("Unterminated string")||E({enabled:r.enabled,clientId:r.clientId,configUrl:r.configUrl,emailDomains:r.emailDomains.split(", ")}),h(!0),o&&o(e,r))}});return e.useEffect((()=>{})),e.createElement(i,{gap:2,position:"relative"},e.createElement(c,{level:"h4"},"Step 1"),m,e.createElement(c,{level:"h4"},"Step 2"),e.createElement(Q,{mutation:w,buttonText:"Update",attributes:y,loading:p,successMessage:e.createElement(s,{color:"success",sx:{opacity:g?1:0,transition:"opacity 200ms",height:"0.9rem"},startDecorator:e.createElement(F,null)},e.createElement(c,{textAlign:"center",fontSize:"sm"},"Provider updated"))}))}function ee({callbackUrl:t}){const[r,n]=e.useState(!1),a=e.useRef();return e.useEffect((()=>{a.current&&clearTimeout(a.current),a.current=setTimeout((()=>{n(!1)}),3250)}),[r]),e.createElement(i,{gap:2},e.createElement(c,null,"In order for Okta to redirect properly, provide the following URL as the"," ",e.createElement(o,{component:"span",sx:{fontFamily:"monospace"}},"Sign-in redirect URIs")," ","in the admin configuration of your application."),e.createElement(m,{onClick:async()=>{t&&(await navigator.clipboard.writeText(t),n(!0))},sx:e=>({input:{cursor:"pointer"},span:{cursor:"pointer"},"&:hover svg":{"--Icon-color":e.palette.primary[500]}}),value:t,readOnly:!0,endDecorator:e.createElement(E,{title:"Copy Okta redirect URL"},e.createElement(o,{position:"relative",width:r?"82px":"24px",height:"24px"},e.createElement(o,{position:"absolute",top:"0",left:"0",sx:{opacity:r?0:1,transition:"opacity 300ms"}},e.createElement(k,null)),e.createElement(o,{position:"absolute",top:"0",left:"0",sx:{opacity:r?1:0,transition:"opacity 300ms"}},e.createElement(i,{direction:"row",gap:1},e.createElement(F,null),e.createElement(c,{color:"primary"},"Copied!")))))}))}exports.GoogleLoginButton=function(t){const{databaseId:r,newTenantName:n}=t,{basePath:a}=V(),s=`${a}/databases/${encodeURIComponent(r??"")}/users/oidc/google/login`,u=n?"?newTenant="+encodeURIComponent(n):"",m=(t?.href??s)+u;return e.createElement(o,{component:"a",href:m,display:"flex",flex:1,sx:{textDecoration:"none"}},e.createElement(o,null,e.createElement(l,{sx:{padding:0,textTransform:"initial",flex:1},"aria-label":"log in with google"},e.createElement(i,{direction:"row",alignItems:"center",p:0,flex:1,fontFamily:"Roboto, sans-serif",fontSize:"14px",display:"inline-flex",color:"rgb(255 255, 255)",boxShadow:"rgb(0 0 0 / 24%) 0px 2px 2px 0px rgb(0 0 0 / 24%) 0px 0px 1px 0px",borderRadius:"4px",border:"1px solid transparent",fontWeight:"500",sx:{backgroundColor:"rgb(66 133, 244)"}},e.createElement(o,{padding:"11px",display:"flex",border:"1px solid rgb(66, 133, 244)",borderRadius:"4px",sx:{background:"rgb(255, 255, 255)"}},e.createElement($,{"aria-hidden":"true"})),e.createElement(o,{padding:"10px",flex:1},e.createElement(c,{sx:{color:"white"},fontWeight:700,fontFamily:"Roboto, sans-serif",fontSize:"14px",height:"20px"},"Continue with Google"))))))},exports.NileProvider=r=>{const{children:n,theme:a,slotProps:o,tenantId:l,QueryProvider:i=R,basePath:c="https://api.thenile.dev",api:s}=r,u=e.useMemo((()=>({api:s??new t({basePath:c,credentials:"include"}),tenantId:String(l),basePath:c})),[s,c,l]);return e.createElement(i,null,e.createElement(P,{slotProps:o?.provider,theme:a},e.createElement(O,{value:u},n)))},exports.Okta=function(t){const{callbackUrl:r,providers:n,...a}=t;if(!n)return null;const o=n?.find((e=>"okta"===e.provider));return e.createElement(Z,{...a,config:o,providerName:"Okta",configurationGuide:e.createElement(ee,{callbackUrl:r})})},exports.SSOForm=Z,exports.SingleSignOnForm=function(t){const{attributes:n,onSuccess:a,onError:o,beforeMutate:l,nextButtonText:i="Next",loginButtonText:c="Log in",disableSSO:s=!1}=t,u=D(),[m,d]=e.useState(s?c:i),p=r.useMutation((async e=>{const t=(l&&l(e))??e;return await u.auth.login({loginRequest:{email:t.email,password:t.password},sso:!s})}),{onSuccess:(e,t)=>{e&&(e?.redirectURI?window.location.href=e.redirectURI:m!==c?d(c):a&&a(e,t))},onError:(e,t)=>{m===c?o&&o(e,t):d(c)}}),b=e.useMemo((()=>{const e=[{name:"email",label:"Email",type:exports.FormAttributeType.Text,defaultValue:"",required:!0}];return m===c&&e.push({name:"password",label:"Password",type:exports.FormAttributeType.Password,defaultValue:"",required:!0}),n&&n.length>0?e.concat(n):e}),[n,m,c]);return e.createElement(Q,{mutation:p,buttonText:m,attributes:b})},exports.UserLoginForm=function(t){const[n,a]=e.useState(),{attributes:o,onSuccess:l,onError:c,beforeMutate:u}=t,m=D(),d=r.useMutation((async e=>{a(void 0);const t=(u&&u(e))??e;return await m.auth.login({loginRequest:t})}),{onSuccess:l,onError:c}),p=e.useMemo((()=>{const e=[{name:"email",label:"Email",type:exports.FormAttributeType.Text,defaultValue:"",required:!0},{name:"password",label:"Password",type:exports.FormAttributeType.Password,defaultValue:"",required:!0}];return o&&o.length>0?e.concat(o):e}),[o]);return e.createElement(i,{gap:2},n?e.createElement(s,{color:"danger"},n):null,e.createElement(Q,{mutation:d,buttonText:"Log in",attributes:p}))},exports.UserSignupForm=function(t){const[n,a]=e.useState(),{buttonText:o="Sign up",onSuccess:l,onError:c,attributes:u,beforeMutate:m}=t,d=D(),p=r.useMutation((async e=>{a(void 0);const t=(m&&m(e))??e,{email:r,password:n,preferredName:o,newTenant:l,...i}=t;return Object.keys(i).length>0&&console.warn("additional metadata not supported yet."),d.auth.signUp({signUpRequest:{email:r,password:n,preferredName:o,newTenant:l}})}),{onSuccess:l,onError:(e,t)=>{a(e.message),c&&c(e,t)}}),b=e.useMemo((()=>{const e=[{name:"email",label:"Email",type:exports.FormAttributeType.Text,defaultValue:"",required:!0},{name:"password",label:"Password",type:exports.FormAttributeType.Password,defaultValue:"",required:!0}];return u&&u.length>0?e.concat(u):e}),[u]);return e.createElement(i,{gap:2},n?e.createElement(s,{color:"danger"},n):null,e.createElement(Q,{mutation:p,buttonText:o,attributes:b}))},exports.UserTenantList=function(t){const{data:r,allowCreation:n=!0,buttonText:a="Add a user",onUserCreateSuccess:o,slots:l,include:c=["email","preferedName"]}=t,s={width:"100%",height:"100%",...l?.dataGrid??{}},[u,m]=Y(r,c);return e.createElement(i,{flex:1},e.createElement(J,{allowCreation:n,buttonText:a,onUserCreateSuccess:o}),e.createElement(T.DataGrid,{sx:s,rows:m,columns:u,hideFooter:!0}))};
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var React = require('react');
6
+ var Browser = require('@niledatabase/browser');
7
+ var reactQuery = require('@tanstack/react-query');
8
+ var CssBaseline = require('@mui/material/CssBaseline');
9
+ var styles = require('@mui/joy/styles');
10
+ var Box = require('@mui/joy/Box');
11
+ var Button = require('@mui/joy/Button');
12
+ var Stack = require('@mui/joy/Stack');
13
+ var Typography = require('@mui/joy/Typography');
14
+ var Alert = require('@mui/joy/Alert');
15
+ var reactHookForm = require('react-hook-form');
16
+ var Input = require('@mui/joy/Input');
17
+ var FormControl = require('@mui/joy/FormControl');
18
+ var FormHelperText = require('@mui/joy/FormHelperText');
19
+ var Error$1 = require('@mui/icons-material/Error');
20
+ var FormLabel = require('@mui/joy/FormLabel');
21
+ var Select = require('@mui/joy/Select');
22
+ var Option = require('@mui/joy/Option');
23
+ var Tooltip = require('@mui/joy/Tooltip');
24
+ var joy = require('@mui/joy');
25
+ var Checkbox = require('@mui/joy/Checkbox');
26
+ var List = require('@mui/joy/List');
27
+ var ListItem = require('@mui/joy/ListItem');
28
+ var xDataGrid = require('@mui/x-data-grid');
29
+ var Add = require('@mui/icons-material/Add');
30
+ var CopyAll = require('@mui/icons-material/CopyAll');
31
+ var CheckCircleOutlined = require('@mui/icons-material/CheckCircleOutlined');
32
+
33
+ function _interopNamespaceDefault(e) {
34
+ var n = Object.create(null);
35
+ if (e) {
36
+ Object.keys(e).forEach(function (k) {
37
+ if (k !== 'default') {
38
+ var d = Object.getOwnPropertyDescriptor(e, k);
39
+ Object.defineProperty(n, k, d.get ? d : {
40
+ enumerable: true,
41
+ get: function () { return e[k]; }
42
+ });
43
+ }
44
+ });
45
+ }
46
+ n.default = e;
47
+ return n;
48
+ }
49
+
50
+ var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
51
+
52
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
53
+ function Themer({
54
+ theme,
55
+ children,
56
+ slotProps
57
+ }) {
58
+ return /*#__PURE__*/React.createElement(styles.CssVarsProvider, {
59
+ ...slotProps,
60
+ theme: theme
61
+ }, /*#__PURE__*/React.createElement(CssBaseline, {
62
+ enableColorScheme: true
63
+ }), children);
64
+ }
65
+
66
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
67
+ const queryClient = /*#__PURE__*/new reactQuery.QueryClient();
68
+ const defaultContext = {
69
+ api: /*#__PURE__*/new Browser({
70
+ basePath: 'https://api.thenile.dev',
71
+ credentials: 'include'
72
+ }),
73
+ basePath: ''
74
+ };
75
+ const context = /*#__PURE__*/React.createContext(defaultContext);
76
+ const {
77
+ Provider
78
+ } = context;
79
+ const BaseQueryProvider = ({
80
+ children
81
+ }) => {
82
+ return /*#__PURE__*/React.createElement(reactQuery.QueryClientProvider, {
83
+ client: queryClient
84
+ }, children);
85
+ };
86
+ const NileProvider = props => {
87
+ const {
88
+ children,
89
+ theme,
90
+ slotProps,
91
+ tenantId,
92
+ QueryProvider = BaseQueryProvider,
93
+ basePath = 'https://api.thenile.dev',
94
+ api
95
+ } = props;
96
+ const values = React.useMemo(() => {
97
+ return {
98
+ api: api ?? new Browser({
99
+ basePath,
100
+ credentials: 'include'
101
+ }),
102
+ tenantId: String(tenantId),
103
+ basePath
104
+ };
105
+ }, [api, basePath, tenantId]);
106
+ return /*#__PURE__*/React.createElement(QueryProvider, null, /*#__PURE__*/React.createElement(Themer, {
107
+ slotProps: slotProps?.provider,
108
+ theme: theme
109
+ }, /*#__PURE__*/React.createElement(Provider, {
110
+ value: values
111
+ }, children)));
112
+ };
113
+ const useNileContext = () => {
114
+ return React.useContext(context);
115
+ };
116
+ const useNileConfig = () => {
117
+ const {
118
+ basePath,
119
+ tenantId
120
+ } = useNileContext();
121
+ return React.useMemo(() => ({
122
+ tenantId,
123
+ basePath
124
+ }), [basePath, tenantId]);
125
+ };
126
+ const useApi = () => {
127
+ return useNileContext().api;
128
+ };
129
+
130
+ var _g;
131
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
132
+ var SvgGoogle = function SvgGoogle(props) {
133
+ return /*#__PURE__*/React__namespace.createElement("svg", _extends({
134
+ xmlns: "http://www.w3.org/2000/svg",
135
+ width: 18,
136
+ height: 18
137
+ }, props), _g || (_g = /*#__PURE__*/React__namespace.createElement("g", {
138
+ fillRule: "evenodd"
139
+ }, /*#__PURE__*/React__namespace.createElement("path", {
140
+ fill: "#EA4335",
141
+ d: "M9 3.48c1.69 0 2.83.73 3.48 1.34l2.54-2.48C13.46.89 11.43 0 9 0 5.48 0 2.44 2.02.96 4.96l2.91 2.26C4.6 5.05 6.62 3.48 9 3.48"
142
+ }), /*#__PURE__*/React__namespace.createElement("path", {
143
+ fill: "#4285F4",
144
+ d: "M17.64 9.2c0-.74-.06-1.28-.19-1.84H9v3.34h4.96c-.1.83-.64 2.08-1.84 2.92l2.84 2.2c1.7-1.57 2.68-3.88 2.68-6.62"
145
+ }), /*#__PURE__*/React__namespace.createElement("path", {
146
+ fill: "#FBBC05",
147
+ d: "M3.88 10.78A5.5 5.5 0 0 1 3.58 9c0-.62.11-1.22.29-1.78L.96 4.96A9 9 0 0 0 0 9c0 1.45.35 2.82.96 4.04z"
148
+ }), /*#__PURE__*/React__namespace.createElement("path", {
149
+ fill: "#34A853",
150
+ d: "M9 18c2.43 0 4.47-.8 5.96-2.18l-2.84-2.2c-.76.53-1.78.9-3.12.9-2.38 0-4.4-1.57-5.12-3.74L.97 13.04C2.45 15.98 5.48 18 9 18"
151
+ }), /*#__PURE__*/React__namespace.createElement("path", {
152
+ fill: "none",
153
+ d: "M0 0h18v18H0z"
154
+ }))));
155
+ };
156
+
157
+ const LOGIN_PATH = 'users/oidc/google/login';
158
+ /**
159
+ * A component for a Google login button, according to their design language.
160
+ * This works when an identity provider is configured in the admin dashboard.
161
+ * @param props href: a string to override the URL provided by the context
162
+ * @returns a JSX.Element to render
163
+ */
164
+ function GoogleSSOButton(props) {
165
+ const {
166
+ databaseId,
167
+ newTenantName
168
+ } = props;
169
+ const {
170
+ basePath
171
+ } = useNileConfig();
172
+ const encodedDatabase = encodeURIComponent(databaseId ?? '');
173
+ const contextHref = `${basePath}/databases/${encodedDatabase}/${LOGIN_PATH}`;
174
+ const query = newTenantName ? '?newTenant=' + encodeURIComponent(newTenantName) : '';
175
+ const href = (props?.href ?? contextHref) + query;
176
+ return /*#__PURE__*/React.createElement(Box, {
177
+ component: "a",
178
+ href: href,
179
+ display: "flex",
180
+ flex: 1,
181
+ sx: {
182
+ textDecoration: 'none'
183
+ }
184
+ }, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Button, {
185
+ sx: {
186
+ padding: 0,
187
+ textTransform: 'initial',
188
+ flex: 1
189
+ },
190
+ "aria-label": "log in with google"
191
+ }, /*#__PURE__*/React.createElement(Stack, {
192
+ direction: "row",
193
+ alignItems: "center",
194
+ p: 0,
195
+ flex: 1,
196
+ fontFamily: "Roboto, sans-serif",
197
+ fontSize: "14px",
198
+ display: "inline-flex",
199
+ color: "rgb(255 255, 255)",
200
+ boxShadow: "rgb(0 0 0 / 24%) 0px 2px 2px 0px rgb(0 0 0 / 24%) 0px 0px 1px 0px",
201
+ borderRadius: "4px",
202
+ border: "1px solid transparent",
203
+ fontWeight: "500",
204
+ sx: {
205
+ backgroundColor: 'rgb(66 133, 244)'
206
+ }
207
+ }, /*#__PURE__*/React.createElement(Box, {
208
+ padding: "11px",
209
+ display: "flex",
210
+ border: "1px solid rgb(66, 133, 244)",
211
+ borderRadius: "4px",
212
+ sx: {
213
+ background: 'rgb(255, 255, 255)'
214
+ }
215
+ }, /*#__PURE__*/React.createElement(SvgGoogle, {
216
+ "aria-hidden": "true"
217
+ })), /*#__PURE__*/React.createElement(Box, {
218
+ padding: "10px",
219
+ flex: 1
220
+ }, /*#__PURE__*/React.createElement(Typography, {
221
+ sx: {
222
+ color: 'white'
223
+ },
224
+ fontWeight: 700,
225
+ fontFamily: "Roboto, sans-serif",
226
+ fontSize: "14px",
227
+ height: "20px"
228
+ }, "Continue with Google"))))));
229
+ }
230
+
231
+ function CheckGroup(props) {
232
+ const {
233
+ options,
234
+ attribute,
235
+ display,
236
+ helperText
237
+ } = props;
238
+ const {
239
+ watch,
240
+ control
241
+ } = reactHookForm.useFormContext();
242
+ const currentVals = watch(attribute.name);
243
+ const checkProps = {};
244
+ if (helperText) {
245
+ checkProps.color = 'danger';
246
+ }
247
+ return /*#__PURE__*/React__namespace.createElement(reactHookForm.Controller, {
248
+ name: attribute.name,
249
+ rules: {
250
+ required: Boolean(attribute.required)
251
+ },
252
+ control: control,
253
+ render: ({
254
+ field
255
+ }) => {
256
+ return /*#__PURE__*/React__namespace.createElement(Stack, null, /*#__PURE__*/React__namespace.createElement(FormLabel, {
257
+ htmlFor: `${field.name}`
258
+ }, display.label), /*#__PURE__*/React__namespace.createElement(Box, {
259
+ role: "group",
260
+ "aria-labelledby": attribute.name,
261
+ sx: {
262
+ borderRadius: 'var(--joy-radius-sm)',
263
+ p: 0.5,
264
+ border: helperText ? '1px solid var(--joy-palette-danger-outlinedBorder)' : 'none'
265
+ }
266
+ }, /*#__PURE__*/React__namespace.createElement(List, {
267
+ orientation: "horizontal",
268
+ wrap: true,
269
+ sx: {
270
+ '--List-gap': '8px'
271
+ }
272
+ }, options.map(item => {
273
+ checkProps.id = String(item.value);
274
+ return /*#__PURE__*/React__namespace.createElement(ListItem, {
275
+ key: `${item.value}-${item.label}`
276
+ }, /*#__PURE__*/React__namespace.createElement(Checkbox, {
277
+ overlay: options.length > 1,
278
+ ...checkProps,
279
+ checked: currentVals.includes(item.value),
280
+ disableIcon: options.length > 1,
281
+ variant: "soft",
282
+ label: item.label,
283
+ onChange: event => {
284
+ if (attribute.allowMultiple) {
285
+ if (event.target.checked) {
286
+ if (!currentVals) {
287
+ field.onChange([item.value]);
288
+ } else {
289
+ field.onChange(currentVals.concat(item.value));
290
+ }
291
+ } else {
292
+ const remaining = currentVals.filter(val => val !== item.value);
293
+ if (remaining.length > 0) {
294
+ field.onChange(remaining);
295
+ } else {
296
+ field.onChange('');
297
+ }
298
+ }
299
+ } else {
300
+ if (event.target.checked) {
301
+ field.onChange(item.value);
302
+ } else {
303
+ field.onChange('');
304
+ }
305
+ }
306
+ }
307
+ }));
308
+ }))), /*#__PURE__*/React__namespace.createElement(Typography, {
309
+ sx: {
310
+ color: 'var(--joy-palette-danger-500)'
311
+ },
312
+ level: "body-sm"
313
+ }, helperText));
314
+ }
315
+ });
316
+ }
317
+
318
+ exports.FormAttributeType = void 0;
319
+ (function (AttributeType) {
320
+ AttributeType["Text"] = "text";
321
+ AttributeType["Password"] = "password";
322
+ AttributeType["Select"] = "select";
323
+ AttributeType["Number"] = "number";
324
+ AttributeType["Float"] = "float";
325
+ AttributeType["Checkbox"] = "checkbox";
326
+ AttributeType["Switch"] = "switch";
327
+ })(exports.FormAttributeType || (exports.FormAttributeType = {}));
328
+
329
+ const getAttributeDefault = attribute => {
330
+ // have to look to see if it is an enum
331
+ if (attribute.allowMultiple === true) {
332
+ if (!Array.isArray(attribute.defaultValue) && attribute.defaultValue) {
333
+ if (typeof attribute.defaultValue === 'number') {
334
+ return [attribute.defaultValue];
335
+ }
336
+ return [String(attribute.defaultValue)];
337
+ }
338
+ }
339
+ return attribute.defaultValue ?? '';
340
+ };
341
+ function Labler(props) {
342
+ const {
343
+ error,
344
+ attr
345
+ } = props;
346
+ if (error) {
347
+ return /*#__PURE__*/React.createElement(Tooltip, {
348
+ title: error,
349
+ color: "danger",
350
+ sx: {
351
+ cursor: 'pointer'
352
+ }
353
+ }, /*#__PURE__*/React.createElement(FormLabel, null, attr.label ?? attr.name, /*#__PURE__*/React.createElement(Error$1, {
354
+ sx: {
355
+ ml: 0.5,
356
+ '--Icon-color': '#c41c1c'
357
+ },
358
+ fontSize: "small"
359
+ })));
360
+ }
361
+ return /*#__PURE__*/React.createElement(FormLabel, null, attr.label ?? attr.name);
362
+ }
363
+ function SimpleForm(props) {
364
+ const {
365
+ mutation,
366
+ buttonText,
367
+ attributes,
368
+ cancelButton,
369
+ loading,
370
+ successMessage
371
+ } = props;
372
+ const defaultValues = React.useMemo(() => attributes.reduce((accum, attr) => {
373
+ accum[attr.name] = getAttributeDefault(attr);
374
+ return accum;
375
+ }, {}), [attributes]);
376
+ const methods = reactHookForm.useForm({
377
+ defaultValues
378
+ });
379
+ const {
380
+ register,
381
+ control,
382
+ handleSubmit,
383
+ formState: {
384
+ errors
385
+ }
386
+ } = methods;
387
+ const onSubmit = React.useCallback(
388
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
389
+ data => {
390
+ mutation.mutate(data);
391
+ }, [mutation]);
392
+ return /*#__PURE__*/React.createElement(reactHookForm.FormProvider, {
393
+ ...methods
394
+ }, /*#__PURE__*/React.createElement(Stack, {
395
+ component: "form",
396
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
397
+ onSubmit: handleSubmit(data => onSubmit(data)),
398
+ spacing: 2
399
+ }, attributes.map(attr => {
400
+ const fieldConfig = {};
401
+ const display = {
402
+ key: attr.name,
403
+ label: attr.label ?? attr.name,
404
+ id: attr.label ?? attr.name,
405
+ placeholder: attr.placeholder ?? attr.label ?? attr.name,
406
+ error: Boolean(errors[attr.name]),
407
+ disabled: Boolean(attr.disabled)
408
+ };
409
+ const options = attr.options ?? [];
410
+ const helperText = attr.helpText ?? '';
411
+ let error = '';
412
+ if (attr.required) {
413
+ error = errors[attr.name] ? `${attr.label ?? attr.name} is required` : '';
414
+ fieldConfig.required = true;
415
+ }
416
+ switch (attr.type) {
417
+ case exports.FormAttributeType.Switch:
418
+ return /*#__PURE__*/React.createElement(FormControl, {
419
+ key: display.key,
420
+ id: display.id,
421
+ orientation: "horizontal",
422
+ sx: {
423
+ alignItems: 'center'
424
+ }
425
+ }, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Labler, {
426
+ error: error,
427
+ attr: attr
428
+ }), /*#__PURE__*/React.createElement(FormHelperText, {
429
+ id: `${attr.name}-helper-text`
430
+ }, helperText)), /*#__PURE__*/React.createElement(reactHookForm.Controller, {
431
+ control: control,
432
+ rules: {
433
+ required: Boolean(attr.required)
434
+ },
435
+ name: attr.name,
436
+ render: ({
437
+ field
438
+ }) => {
439
+ const color = {};
440
+ if (errors[attr.name]) {
441
+ color.color = 'danger';
442
+ }
443
+ return /*#__PURE__*/React.createElement(joy.Switch, {
444
+ id: `switch-field-${attr.name}`,
445
+ ...color,
446
+ ...field,
447
+ checked: Boolean(field.value),
448
+ onChange: event => {
449
+ field.onChange(event.target.checked);
450
+ },
451
+ color: field.value ? 'success' : 'neutral',
452
+ endDecorator: field.value ? options[0].label : options[1].label
453
+ });
454
+ }
455
+ }));
456
+ case exports.FormAttributeType.Checkbox:
457
+ return /*#__PURE__*/React.createElement(CheckGroup, {
458
+ key: display.key,
459
+ attribute: attr,
460
+ display: display,
461
+ options: options,
462
+ helperText: helperText
463
+ });
464
+ case exports.FormAttributeType.Select:
465
+ return /*#__PURE__*/React.createElement(FormControl, {
466
+ key: display.key,
467
+ id: display.id
468
+ }, /*#__PURE__*/React.createElement(Labler, {
469
+ error: error,
470
+ attr: attr
471
+ }), /*#__PURE__*/React.createElement(reactHookForm.Controller, {
472
+ control: control,
473
+ rules: {
474
+ required: Boolean(attr.required)
475
+ },
476
+ name: attr.name,
477
+ render: ({
478
+ field
479
+ }) => {
480
+ const color = {};
481
+ if (errors[attr.name]) {
482
+ color.color = 'danger';
483
+ }
484
+ const value = String(field.value);
485
+ return /*#__PURE__*/React.createElement(Stack, null, /*#__PURE__*/React.createElement(Select, {
486
+ id: `select-field-${attr.name}`,
487
+ placeholder: `${display.placeholder}...`,
488
+ ...color,
489
+ ...field,
490
+ value: value,
491
+ onChange: (_, newValue) => {
492
+ field.onChange(newValue);
493
+ }
494
+ }, options.map(option => {
495
+ return /*#__PURE__*/React.createElement(Option, {
496
+ key: String(option.value ?? ''),
497
+ value: option.value
498
+ }, option.label);
499
+ })), /*#__PURE__*/React.createElement(FormHelperText, {
500
+ id: `${attr.name}-helper-text`
501
+ }, helperText));
502
+ }
503
+ }));
504
+ case exports.FormAttributeType.Password:
505
+ return /*#__PURE__*/React.createElement(FormControl, {
506
+ key: display.key,
507
+ id: display.id
508
+ }, /*#__PURE__*/React.createElement(Labler, {
509
+ error: error,
510
+ attr: attr
511
+ }), /*#__PURE__*/React.createElement(Input, {
512
+ ...display,
513
+ ...register(attr.name, fieldConfig),
514
+ type: exports.FormAttributeType.Password
515
+ }), /*#__PURE__*/React.createElement(FormHelperText, {
516
+ id: `${attr.name}-helper-text`
517
+ }, helperText));
518
+ case exports.FormAttributeType.Number:
519
+ return /*#__PURE__*/React.createElement(FormControl, {
520
+ key: display.key,
521
+ id: display.id
522
+ }, /*#__PURE__*/React.createElement(Labler, {
523
+ error: error,
524
+ attr: attr
525
+ }), /*#__PURE__*/React.createElement(Input, {
526
+ ...display,
527
+ ...register(attr.name, fieldConfig),
528
+ type: exports.FormAttributeType.Number
529
+ }), /*#__PURE__*/React.createElement(FormHelperText, {
530
+ id: `${attr.name}-helper-text`
531
+ }, helperText));
532
+ case exports.FormAttributeType.Text:
533
+ default:
534
+ return /*#__PURE__*/React.createElement(FormControl, {
535
+ key: display.key,
536
+ id: display.id
537
+ }, /*#__PURE__*/React.createElement(Labler, {
538
+ error: error,
539
+ attr: attr
540
+ }), /*#__PURE__*/React.createElement(Input, {
541
+ ...display,
542
+ ...register(attr.name, fieldConfig)
543
+ }), /*#__PURE__*/React.createElement(FormHelperText, {
544
+ id: `${attr.name}-helper-text`
545
+ }, helperText));
546
+ }
547
+ }), cancelButton ? ( /*#__PURE__*/React.createElement(Stack, {
548
+ spacing: 2,
549
+ direction: "row"
550
+ }, cancelButton, /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Button, {
551
+ type: "submit"
552
+ }, buttonText)))) : ( /*#__PURE__*/React.createElement(Box, null, /*#__PURE__*/React.createElement(Stack, {
553
+ direction: "row",
554
+ gap: 2
555
+ }, /*#__PURE__*/React.createElement(Button, {
556
+ type: "submit",
557
+ loading: loading
558
+ }, buttonText), successMessage)))));
559
+ }
560
+
561
+ function SignUpForm(props) {
562
+ const [error, setError] = React.useState();
563
+ const {
564
+ buttonText = 'Sign up',
565
+ onSuccess,
566
+ onError,
567
+ attributes,
568
+ beforeMutate
569
+ } = props;
570
+ const api = useApi();
571
+ const mutation = reactQuery.useMutation(async _data => {
572
+ setError(undefined);
573
+ const possibleData = beforeMutate && beforeMutate(_data);
574
+ const data = possibleData ?? _data;
575
+ const {
576
+ email,
577
+ password,
578
+ preferredName,
579
+ newTenant,
580
+ ...metadata
581
+ } = data;
582
+ if (Object.keys(metadata).length > 0) {
583
+ // eslint-disable-next-line no-console
584
+ console.warn('additional metadata not supported yet.');
585
+ }
586
+ return api.auth.signUp({
587
+ signUpRequest: {
588
+ email,
589
+ password,
590
+ preferredName,
591
+ newTenant
592
+ }
593
+ });
594
+ }, {
595
+ onSuccess,
596
+ onError: (e, vars) => {
597
+ setError(e.message);
598
+ onError && onError(e, vars);
599
+ }
600
+ });
601
+ const completeAttributes = React.useMemo(() => {
602
+ const mainAttributes = [{
603
+ name: 'email',
604
+ label: 'Email',
605
+ type: exports.FormAttributeType.Text,
606
+ defaultValue: '',
607
+ required: true
608
+ }, {
609
+ name: 'password',
610
+ label: 'Password',
611
+ type: exports.FormAttributeType.Password,
612
+ defaultValue: '',
613
+ required: true
614
+ }];
615
+ if (attributes && attributes.length > 0) {
616
+ return mainAttributes.concat(attributes);
617
+ }
618
+ return mainAttributes;
619
+ }, [attributes]);
620
+ return /*#__PURE__*/React.createElement(Stack, {
621
+ gap: 2
622
+ }, error ? /*#__PURE__*/React.createElement(Alert, {
623
+ color: "danger"
624
+ }, error) : null, /*#__PURE__*/React.createElement(SimpleForm, {
625
+ mutation: mutation,
626
+ buttonText: buttonText,
627
+ attributes: completeAttributes
628
+ }));
629
+ }
630
+
631
+ function LoginForm(props) {
632
+ const [error, setError] = React.useState();
633
+ const {
634
+ attributes,
635
+ onSuccess,
636
+ onError,
637
+ beforeMutate
638
+ } = props;
639
+ const api = useApi();
640
+ const mutation = reactQuery.useMutation(async _data => {
641
+ setError(undefined);
642
+ const possibleData = beforeMutate && beforeMutate(_data);
643
+ const data = possibleData ?? _data;
644
+ return await api.auth.login({
645
+ loginRequest: data
646
+ });
647
+ }, {
648
+ onSuccess,
649
+ onError
650
+ });
651
+ const completeAttributes = React.useMemo(() => {
652
+ const mainAttributes = [{
653
+ name: 'email',
654
+ label: 'Email',
655
+ type: exports.FormAttributeType.Text,
656
+ defaultValue: '',
657
+ required: true
658
+ }, {
659
+ name: 'password',
660
+ label: 'Password',
661
+ type: exports.FormAttributeType.Password,
662
+ defaultValue: '',
663
+ required: true
664
+ }];
665
+ if (attributes && attributes.length > 0) {
666
+ return mainAttributes.concat(attributes);
667
+ }
668
+ return mainAttributes;
669
+ }, [attributes]);
670
+ return /*#__PURE__*/React.createElement(Stack, {
671
+ gap: 2
672
+ }, error ? /*#__PURE__*/React.createElement(Alert, {
673
+ color: "danger"
674
+ }, error) : null, /*#__PURE__*/React.createElement(SimpleForm, {
675
+ mutation: mutation,
676
+ buttonText: "Log in",
677
+ attributes: completeAttributes
678
+ }));
679
+ }
680
+
681
+ function SingleSignOnForm(props) {
682
+ const {
683
+ attributes,
684
+ onSuccess,
685
+ onError,
686
+ beforeMutate,
687
+ nextButtonText = 'Next',
688
+ loginButtonText = 'Log in',
689
+ disableSSO = false
690
+ } = props;
691
+ const api = useApi();
692
+ const [buttonText, setButtonText] = React.useState(disableSSO ? loginButtonText : nextButtonText);
693
+ const mutation = reactQuery.useMutation(async _data => {
694
+ const possibleData = beforeMutate && beforeMutate(_data);
695
+ const data = possibleData ?? _data;
696
+ return await api.auth.login({
697
+ loginRequest: {
698
+ email: data.email,
699
+ password: data.password
700
+ },
701
+ sso: !disableSSO
702
+ });
703
+ }, {
704
+ onSuccess: (token, data) => {
705
+ if (token) {
706
+ if (token?.redirectURI) {
707
+ window.location.href = token.redirectURI;
708
+ } else if (buttonText !== loginButtonText) {
709
+ setButtonText(loginButtonText);
710
+ } else {
711
+ onSuccess && onSuccess(token, data);
712
+ }
713
+ }
714
+ },
715
+ onError: (error, data) => {
716
+ // it is possible SSO failed, so only show errors on if the password is available
717
+ if (buttonText === loginButtonText) {
718
+ onError && onError(error, data);
719
+ } else {
720
+ setButtonText(loginButtonText);
721
+ }
722
+ }
723
+ });
724
+ const completeAttributes = React.useMemo(() => {
725
+ const mainAttributes = [{
726
+ name: 'email',
727
+ label: 'Email',
728
+ type: exports.FormAttributeType.Text,
729
+ defaultValue: '',
730
+ required: true
731
+ }];
732
+ if (buttonText === loginButtonText) {
733
+ mainAttributes.push({
734
+ name: 'password',
735
+ label: 'Password',
736
+ type: exports.FormAttributeType.Password,
737
+ defaultValue: '',
738
+ required: true
739
+ });
740
+ }
741
+ if (attributes && attributes.length > 0) {
742
+ return mainAttributes.concat(attributes);
743
+ }
744
+ return mainAttributes;
745
+ }, [attributes, buttonText, loginButtonText]);
746
+ return /*#__PURE__*/React.createElement(SimpleForm, {
747
+ mutation: mutation,
748
+ buttonText: buttonText,
749
+ attributes: completeAttributes
750
+ });
751
+ }
752
+
753
+ function AddUser(props) {
754
+ const {
755
+ open,
756
+ setOpen,
757
+ refetch
758
+ } = props;
759
+ const {
760
+ tenantId
761
+ } = useNileConfig();
762
+ const api = useApi();
763
+ const [errorText, setErrorText] = React.useState();
764
+ const {
765
+ watch,
766
+ register,
767
+ handleSubmit
768
+ } = reactHookForm.useForm();
769
+ const email = watch('email');
770
+ React.useEffect(() => {
771
+ if (errorText != null) {
772
+ setErrorText();
773
+ }
774
+ // if email changes, no more error
775
+ // eslint-disable-next-line react-hooks/exhaustive-deps
776
+ }, [email]);
777
+ const mutation = reactQuery.useMutation(data => api.users.createTenantUser({
778
+ signUpRequest: data,
779
+ tenantId: String(tenantId)
780
+ }), {
781
+ onSuccess(data) {
782
+ refetch && refetch(data);
783
+ setOpen(false);
784
+ },
785
+ onError(e) {
786
+ if (e instanceof Error) {
787
+ setErrorText(e.message);
788
+ }
789
+ }
790
+ });
791
+ const handleUpdate = React.useCallback(async data => {
792
+ setErrorText('');
793
+ mutation.mutate(data);
794
+ }, [mutation]);
795
+ return /*#__PURE__*/React.createElement(joy.Modal, {
796
+ open: open
797
+ }, /*#__PURE__*/React.createElement(joy.ModalDialog, null, /*#__PURE__*/React.createElement(joy.Stack, {
798
+ spacing: 2
799
+ }, /*#__PURE__*/React.createElement(joy.Typography, {
800
+ level: "h4"
801
+ }, "Create user"), /*#__PURE__*/React.createElement(React.Fragment, null, errorText && /*#__PURE__*/React.createElement(joy.Typography, {
802
+ color: "danger"
803
+ }, errorText)), /*#__PURE__*/React.createElement(joy.Stack, {
804
+ component: "form",
805
+ sx: {
806
+ width: '40ch'
807
+ },
808
+ spacing: 1,
809
+ onSubmit: handleSubmit(data => handleUpdate(data))
810
+ }, /*#__PURE__*/React.createElement(joy.FormControl, {
811
+ sx: {
812
+ '--FormHelperText-color': 'var(--joy-palette-danger-500)'
813
+ }
814
+ }, /*#__PURE__*/React.createElement(joy.FormLabel, {
815
+ htmlFor: "email"
816
+ }, "Email"), /*#__PURE__*/React.createElement(joy.Input, {
817
+ ...register('email'),
818
+ fullWidth: true,
819
+ size: "lg",
820
+ id: "email",
821
+ name: "email",
822
+ autoComplete: "current-email",
823
+ required: true,
824
+ error: Boolean(errorText)
825
+ })), /*#__PURE__*/React.createElement(joy.FormControl, {
826
+ sx: {
827
+ '--FormHelperText-color': 'var(--joy-palette-danger-500)'
828
+ }
829
+ }, /*#__PURE__*/React.createElement(joy.FormLabel, {
830
+ htmlFor: "password"
831
+ }, "Password"), /*#__PURE__*/React.createElement(joy.Input, {
832
+ ...register('password'),
833
+ fullWidth: true,
834
+ size: "lg",
835
+ id: "password",
836
+ autoComplete: "current-password",
837
+ type: "password",
838
+ required: true
839
+ })), /*#__PURE__*/React.createElement(joy.Stack, {
840
+ direction: "row",
841
+ sx: {
842
+ pt: 2
843
+ },
844
+ spacing: 2
845
+ }, /*#__PURE__*/React.createElement(joy.Button, {
846
+ onClick: () => setOpen(false),
847
+ variant: "plain"
848
+ }, "Cancel"), /*#__PURE__*/React.createElement(joy.Button, {
849
+ type: "submit"
850
+ }, "Create"))))));
851
+ }
852
+
853
+ function CreateUser(props) {
854
+ const {
855
+ allowCreation,
856
+ buttonText,
857
+ onUserCreateSuccess
858
+ } = props;
859
+ const [open, setOpen] = React.useState(false);
860
+ if (!allowCreation) {
861
+ return null;
862
+ }
863
+ return /*#__PURE__*/React.createElement(Stack, {
864
+ alignItems: "flex-end",
865
+ gap: 1
866
+ }, /*#__PURE__*/React.createElement(AddUser, {
867
+ open: open,
868
+ setOpen: setOpen,
869
+ refetch: onUserCreateSuccess
870
+ }), /*#__PURE__*/React.createElement(Button, {
871
+ startDecorator: /*#__PURE__*/React.createElement(Add, null),
872
+ size: "sm",
873
+ onClick: () => setOpen(true)
874
+ }, buttonText));
875
+ }
876
+
877
+ function getColumnSize(column, rows, canvasContext) {
878
+ const dataWidthReducer = (longest,
879
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
880
+ nextRow) => {
881
+ let value = nextRow[String(column)];
882
+ if (value == null) {
883
+ value = '';
884
+ }
885
+ value = value?.toString();
886
+ return longest.length > value.length ? longest : value;
887
+ };
888
+ let columnHeaderLen = canvasContext && column ? canvasContext.measureText(String(column)).width : 50;
889
+ /* padding 12, icon-width 15 */
890
+ columnHeaderLen += 15 + 12;
891
+ let width = columnHeaderLen;
892
+ width = 16 + Math.ceil(canvasContext ? canvasContext.measureText(rows.reduce(dataWidthReducer, '')).width : 50);
893
+ if (width < columnHeaderLen) {
894
+ width = columnHeaderLen;
895
+ }
896
+ /* Gracefull */
897
+ width += 8;
898
+ return width;
899
+ }
900
+
901
+ function useTextSizer() {
902
+ const [ctx, setCtx] = React.useState();
903
+ React.useEffect(() => {
904
+ const canvas = document.createElement('canvas');
905
+ const canvasContext = canvas.getContext('2d');
906
+ if (canvasContext) {
907
+ canvasContext.font = '18px Roboto';
908
+ setCtx(canvasContext);
909
+ }
910
+ }, []);
911
+ return ctx;
912
+ }
913
+
914
+ const makeRenderable = vals => {
915
+ return Object.keys(vals).reduce((cleaned, key) => {
916
+ const val = vals[key];
917
+ if (val instanceof Set) {
918
+ cleaned[key] = Array.from(val).join(', ');
919
+ } else if (Array.isArray(val)) {
920
+ cleaned[key] = val.join(', ');
921
+ } else {
922
+ cleaned[key] = val;
923
+ }
924
+ return cleaned;
925
+ }, {});
926
+ };
927
+ const parseResults = (data, ctx, include) => {
928
+ if (!data) {
929
+ return [[], []];
930
+ }
931
+ const rows = data.map(makeRenderable);
932
+ const fields = Object.keys(rows[0]);
933
+ const existentCols = {};
934
+ const mapCols = col => {
935
+ const width = getColumnSize(col, rows, ctx);
936
+ const name = col.slice();
937
+ if (include.includes(name)) {
938
+ // add spaces to the end of column names so they are not duplicated in the UI
939
+ if (existentCols[name] == null) {
940
+ existentCols[name] = name.length;
941
+ } else {
942
+ existentCols[name] += 1;
943
+ }
944
+ return {
945
+ field: name.padEnd(existentCols[name]),
946
+ headerName: name.padEnd(existentCols[name]),
947
+ width
948
+ };
949
+ }
950
+ };
951
+ const cols = fields?.map(mapCols).filter(Boolean) ?? [];
952
+ return [cols, rows];
953
+ };
954
+ function useDataParser(data, include) {
955
+ const ctx = useTextSizer();
956
+ const [cols, rows] = React.useMemo(() => parseResults(data, ctx, include), [data, ctx, include]);
957
+ return [cols, rows];
958
+ }
959
+
960
+ function UserList(props) {
961
+ const {
962
+ data,
963
+ allowCreation = true,
964
+ buttonText = 'Add a user',
965
+ onUserCreateSuccess,
966
+ slots,
967
+ include = ['email', 'preferedName']
968
+ } = props;
969
+ const dataGridSx = {
970
+ width: '100%',
971
+ height: '100%',
972
+ ...(slots?.dataGrid ?? {})
973
+ };
974
+ const [columns, rows] = useDataParser(data, include);
975
+ return /*#__PURE__*/React.createElement(Stack, {
976
+ flex: 1
977
+ }, /*#__PURE__*/React.createElement(CreateUser, {
978
+ allowCreation: allowCreation,
979
+ buttonText: buttonText,
980
+ onUserCreateSuccess: onUserCreateSuccess
981
+ }), /*#__PURE__*/React.createElement(xDataGrid.DataGrid, {
982
+ sx: dataGridSx,
983
+ rows: rows,
984
+ columns: columns,
985
+ hideFooter: true
986
+ }));
987
+ }
988
+
989
+ function BaseSSOForm(props) {
990
+ const {
991
+ config,
992
+ providerName,
993
+ onSuccess,
994
+ onError,
995
+ allowEdit = true,
996
+ configurationGuide
997
+ } = props;
998
+ const api = useApi();
999
+ const [loading, setLoading] = React.useState(false);
1000
+ const [success, setSuccess] = React.useState(false);
1001
+ const [optimisticConfig, setConfig] = React.useState(config);
1002
+ const timer = React.useRef();
1003
+ const attributes = React.useMemo(() => {
1004
+ const attributes = [{
1005
+ name: 'enabled',
1006
+ label: 'Allow Okta logins',
1007
+ type: exports.FormAttributeType.Switch,
1008
+ defaultValue: optimisticConfig?.enabled === true,
1009
+ options: [{
1010
+ label: 'Enabled'
1011
+ }, {
1012
+ label: 'Disabled'
1013
+ }],
1014
+ disabled: !allowEdit
1015
+ }, {
1016
+ name: 'clientId',
1017
+ label: 'Client id',
1018
+ type: exports.FormAttributeType.Text,
1019
+ defaultValue: optimisticConfig?.clientId ?? '',
1020
+ required: true,
1021
+ disabled: !allowEdit
1022
+ }, {
1023
+ name: 'configUrl',
1024
+ label: 'Config url',
1025
+ type: exports.FormAttributeType.Text,
1026
+ defaultValue: optimisticConfig?.configUrl ?? '',
1027
+ helpText: 'The URL of the .well-known/openid-configuration for the identity provider',
1028
+ required: true,
1029
+ disabled: !allowEdit
1030
+ }, {
1031
+ name: 'emailDomains',
1032
+ label: 'Email domains',
1033
+ type: exports.FormAttributeType.Text,
1034
+ defaultValue: optimisticConfig?.emailDomains?.join(', ') ?? '',
1035
+ required: true,
1036
+ helpText: 'A comma seperated list of email domains (yourDomain.com) to be used',
1037
+ disabled: !allowEdit
1038
+ }];
1039
+ if (!optimisticConfig?.clientId) {
1040
+ attributes.splice(2, 0, {
1041
+ name: 'clientSecret',
1042
+ label: 'Client secret',
1043
+ type: exports.FormAttributeType.Password,
1044
+ defaultValue: '',
1045
+ required: true,
1046
+ disabled: !allowEdit
1047
+ });
1048
+ }
1049
+ return attributes;
1050
+ }, [allowEdit, optimisticConfig?.clientId, optimisticConfig?.configUrl, optimisticConfig?.emailDomains, optimisticConfig?.enabled]);
1051
+ const handleTimer = () => {
1052
+ if (timer.current) {
1053
+ clearTimeout(timer.current);
1054
+ }
1055
+ timer.current = setTimeout(() => {
1056
+ setSuccess(false);
1057
+ }, 3000);
1058
+ };
1059
+ const mutation = reactQuery.useMutation(ssoRequest => {
1060
+ setLoading(true);
1061
+ const payload = {
1062
+ providerName: providerName.toLowerCase(),
1063
+ updateProviderRequest: {
1064
+ ...ssoRequest,
1065
+ emailDomains: ssoRequest.emailDomains.split(',')
1066
+ }
1067
+ };
1068
+ if (optimisticConfig != null) {
1069
+ return api.auth.updateProvider(payload);
1070
+ } else {
1071
+ return api.auth.createProvider(payload);
1072
+ }
1073
+ }, {
1074
+ onSuccess: (data, vars) => {
1075
+ setConfig(data);
1076
+ setSuccess(true);
1077
+ onSuccess && onSuccess(data, vars);
1078
+ },
1079
+ onError,
1080
+ onSettled: (data, error, vars) => {
1081
+ setLoading(false);
1082
+ handleTimer();
1083
+ if (!data) {
1084
+ if (!error || error?.message.includes('Unterminated string')) {
1085
+ // something unexpected happened on the BE, but it's non-fatal
1086
+ setConfig({
1087
+ enabled: vars.enabled,
1088
+ clientId: vars.clientId,
1089
+ configUrl: vars.configUrl,
1090
+ emailDomains: vars.emailDomains.split(', ')
1091
+ });
1092
+ }
1093
+ setSuccess(true);
1094
+ onSuccess && onSuccess(data, vars);
1095
+ }
1096
+ }
1097
+ });
1098
+ React.useEffect(() => {
1099
+ });
1100
+ return /*#__PURE__*/React.createElement(Stack, {
1101
+ gap: 2,
1102
+ position: "relative"
1103
+ }, /*#__PURE__*/React.createElement(Typography, {
1104
+ level: "h4"
1105
+ }, "Step 1"), configurationGuide, /*#__PURE__*/React.createElement(Typography, {
1106
+ level: "h4"
1107
+ }, "Step 2"), /*#__PURE__*/React.createElement(SimpleForm, {
1108
+ mutation: mutation,
1109
+ buttonText: "Update",
1110
+ attributes: attributes,
1111
+ loading: loading,
1112
+ successMessage: /*#__PURE__*/React.createElement(Alert, {
1113
+ color: "success",
1114
+ sx: {
1115
+ opacity: success ? 1 : 0,
1116
+ transition: 'opacity 200ms',
1117
+ height: '0.9rem'
1118
+ },
1119
+ startDecorator: /*#__PURE__*/React.createElement(CheckCircleOutlined, null)
1120
+ }, /*#__PURE__*/React.createElement(Typography, {
1121
+ textAlign: "center",
1122
+ fontSize: "sm"
1123
+ }, "Provider updated"))
1124
+ }));
1125
+ }
1126
+
1127
+ function ConfigGuide({
1128
+ callbackUrl
1129
+ }) {
1130
+ const [copied, setCopied] = React.useState(false);
1131
+ const timer = React.useRef();
1132
+ React.useEffect(() => {
1133
+ if (timer.current) {
1134
+ clearTimeout(timer.current);
1135
+ }
1136
+ timer.current = setTimeout(() => {
1137
+ setCopied(false);
1138
+ }, 3250);
1139
+ }, [copied]);
1140
+ return /*#__PURE__*/React.createElement(Stack, {
1141
+ gap: 2
1142
+ }, /*#__PURE__*/React.createElement(Typography, null, "In order for Okta to redirect properly, provide the following URL as the", ' ', /*#__PURE__*/React.createElement(Box, {
1143
+ component: "span",
1144
+ sx: {
1145
+ fontFamily: 'monospace'
1146
+ }
1147
+ }, "Sign-in redirect URIs"), ' ', "in the admin configuration of your application."), /*#__PURE__*/React.createElement(Input, {
1148
+ onClick: async () => {
1149
+ if (callbackUrl) {
1150
+ await navigator.clipboard.writeText(callbackUrl);
1151
+ setCopied(true);
1152
+ }
1153
+ },
1154
+ sx: theme => ({
1155
+ input: {
1156
+ cursor: 'pointer'
1157
+ },
1158
+ span: {
1159
+ cursor: 'pointer'
1160
+ },
1161
+ '&:hover svg': {
1162
+ '--Icon-color': theme.palette.primary[500]
1163
+ }
1164
+ }),
1165
+ value: callbackUrl,
1166
+ readOnly: true,
1167
+ endDecorator: /*#__PURE__*/React.createElement(Tooltip, {
1168
+ title: "Copy Okta redirect URL"
1169
+ }, /*#__PURE__*/React.createElement(Box, {
1170
+ position: "relative",
1171
+ width: copied ? '82px' : '24px',
1172
+ height: "24px"
1173
+ }, /*#__PURE__*/React.createElement(Box, {
1174
+ position: "absolute",
1175
+ top: "0",
1176
+ left: "0",
1177
+ sx: {
1178
+ opacity: copied ? 0 : 1,
1179
+ transition: 'opacity 300ms'
1180
+ }
1181
+ }, /*#__PURE__*/React.createElement(CopyAll, null)), /*#__PURE__*/React.createElement(Box, {
1182
+ position: "absolute",
1183
+ top: "0",
1184
+ left: "0",
1185
+ sx: {
1186
+ opacity: !copied ? 0 : 1,
1187
+ transition: 'opacity 300ms'
1188
+ }
1189
+ }, /*#__PURE__*/React.createElement(Stack, {
1190
+ direction: "row",
1191
+ gap: 1
1192
+ }, /*#__PURE__*/React.createElement(CheckCircleOutlined, null), /*#__PURE__*/React.createElement(Typography, {
1193
+ color: "primary"
1194
+ }, "Copied!")))))
1195
+ }));
1196
+ }
1197
+ function Okta(props) {
1198
+ const {
1199
+ callbackUrl,
1200
+ providers,
1201
+ ...remaining
1202
+ } = props;
1203
+ if (!providers) {
1204
+ return null;
1205
+ }
1206
+ const config = providers?.find(provider => provider.provider === 'okta');
1207
+ return /*#__PURE__*/React.createElement(BaseSSOForm, {
1208
+ ...remaining,
1209
+ config: config,
1210
+ providerName: "Okta",
1211
+ configurationGuide: /*#__PURE__*/React.createElement(ConfigGuide, {
1212
+ callbackUrl: callbackUrl
1213
+ })
1214
+ });
1215
+ }
1216
+
1217
+ exports.GoogleLoginButton = GoogleSSOButton;
1218
+ exports.NileProvider = NileProvider;
1219
+ exports.Okta = Okta;
1220
+ exports.SSOForm = BaseSSOForm;
1221
+ exports.SingleSignOnForm = SingleSignOnForm;
1222
+ exports.UserLoginForm = LoginForm;
1223
+ exports.UserSignupForm = SignUpForm;
1224
+ exports.UserTenantList = UserList;
2
1225
  //# sourceMappingURL=react.cjs.development.js.map