@tinyrack/issuary-server 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/public/assets/{2fa-waFAdIa0.js → 2fa-C9wUlSBS.js} +2 -2
- package/public/assets/{2fa-waFAdIa0.js.map → 2fa-C9wUlSBS.js.map} +1 -1
- package/public/assets/2fa-DFTcWEMb.js +2 -0
- package/public/assets/{2fa-BlpDkf-r.js.map → 2fa-DFTcWEMb.js.map} +1 -1
- package/public/assets/{2fa-DIpGlpoI.js → 2fa-TtshNxXv.js} +2 -2
- package/public/assets/{2fa-DIpGlpoI.js.map → 2fa-TtshNxXv.js.map} +1 -1
- package/public/assets/2fa-huNZDYU9.js +2 -0
- package/public/assets/{2fa-tzTSqiOS.js.map → 2fa-huNZDYU9.js.map} +1 -1
- package/public/assets/{DialogHandle-ChG5Sj7T.js → DialogHandle-DICBMcjK.js} +2 -2
- package/public/assets/{DialogHandle-ChG5Sj7T.js.map → DialogHandle-DICBMcjK.js.map} +1 -1
- package/public/assets/{admin-Ii29X08B.js → admin-CFZaO1bM.js} +2 -2
- package/public/assets/{admin-Ii29X08B.js.map → admin-CFZaO1bM.js.map} +1 -1
- package/public/assets/{admin-users-d7tT-cjc.js → admin-users-BK_BN8jm.js} +2 -2
- package/public/assets/{admin-users-d7tT-cjc.js.map → admin-users-BK_BN8jm.js.map} +1 -1
- package/public/assets/alert-leqyvHOp.js.map +1 -1
- package/public/assets/auth-choice-row-CXuyJgkR.js +2 -0
- package/public/assets/{auth-choice-row-DUvFyo7b.js.map → auth-choice-row-CXuyJgkR.js.map} +1 -1
- package/public/assets/{auth-field-BSlAWkbx.js → auth-field-CWyXGKoC.js} +2 -2
- package/public/assets/{auth-field-BSlAWkbx.js.map → auth-field-CWyXGKoC.js.map} +1 -1
- package/public/assets/{auth-layout-CcJy6dVY.js → auth-layout-CiNn2-Nb.js} +2 -2
- package/public/assets/{auth-layout-CcJy6dVY.js.map → auth-layout-CiNn2-Nb.js.map} +1 -1
- package/public/assets/{auth-page-header-ZLhtQ7Et.js → auth-page-header-DgGAaIiP.js} +2 -2
- package/public/assets/{auth-page-header-ZLhtQ7Et.js.map → auth-page-header-DgGAaIiP.js.map} +1 -1
- package/public/assets/{authorization-context-banner-DfkjwE93.js → authorization-context-banner-DznMDPOX.js} +2 -2
- package/public/assets/{authorization-context-banner-DfkjwE93.js.map → authorization-context-banner-DznMDPOX.js.map} +1 -1
- package/public/assets/{avatar-D-70MKp8.js → avatar-D-FRGa1m.js} +2 -2
- package/public/assets/{avatar-D-70MKp8.js.map → avatar-D-FRGa1m.js.map} +1 -1
- package/public/assets/badge-CsOfUji0.js.map +1 -1
- package/public/assets/{checkbox-C3UmXoKz.js → checkbox-C90dDDza.js} +2 -2
- package/public/assets/{checkbox-C3UmXoKz.js.map → checkbox-C90dDDza.js.map} +1 -1
- package/public/assets/config-DTQCUlfZ.js.map +1 -1
- package/public/assets/{consent-D_yGE8TR.js → consent-B9sQEyO8.js} +2 -2
- package/public/assets/{consent-D_yGE8TR.js.map → consent-B9sQEyO8.js.map} +1 -1
- package/public/assets/{consent-BJgO9pEV.js → consent-RtCi_85E.js} +2 -2
- package/public/assets/{consent-BJgO9pEV.js.map → consent-RtCi_85E.js.map} +1 -1
- package/public/assets/createLucideIcon-BPpAIXR_.js.map +1 -1
- package/public/assets/{email-DAa8JGoC.js → email-8RKQzeig.js} +2 -2
- package/public/assets/{email-DAa8JGoC.js.map → email-8RKQzeig.js.map} +1 -1
- package/public/assets/email-huNZDYU9.js +2 -0
- package/public/assets/{email-tzTSqiOS.js.map → email-huNZDYU9.js.map} +1 -1
- package/public/assets/{error-Dj4QQ8vW.js → error-BYe3gUMO.js} +2 -2
- package/public/assets/{error-Dj4QQ8vW.js.map → error-BYe3gUMO.js.map} +1 -1
- package/public/assets/{field-CEs4ugVJ.js → field-BMT214_m.js} +2 -2
- package/public/assets/{field-CEs4ugVJ.js.map → field-BMT214_m.js.map} +1 -1
- package/public/assets/{forgot-CYMAWPVu.js → forgot-BenoOUxC.js} +2 -2
- package/public/assets/{forgot-CYMAWPVu.js.map → forgot-BenoOUxC.js.map} +1 -1
- package/public/assets/forgot-huNZDYU9.js +2 -0
- package/public/assets/{forgot-tzTSqiOS.js.map → forgot-huNZDYU9.js.map} +1 -1
- package/public/assets/{index-H6CJKjiN.js → index-CKpnrw9M.js} +3 -3
- package/public/assets/index-CKpnrw9M.js.map +1 -0
- package/public/assets/{index-kD9gQA-w.css → index-Ct9o0oCZ.css} +1 -1
- package/public/assets/{initial-avatar-DjSqgENY.js → initial-avatar-E5DK9Igm.js} +2 -2
- package/public/assets/{initial-avatar-DjSqgENY.js.map → initial-avatar-E5DK9Igm.js.map} +1 -1
- package/public/assets/{input-BHPUSlfp.js → input-CxVeC8Pn.js} +2 -2
- package/public/assets/{input-BHPUSlfp.js.map → input-CxVeC8Pn.js.map} +1 -1
- package/public/assets/link-C-WdTj3F.js.map +1 -1
- package/public/assets/{login-CBinfk4L.js → login-1YuiZD30.js} +2 -2
- package/public/assets/{login-CBinfk4L.js.map → login-1YuiZD30.js.map} +1 -1
- package/public/assets/login-huNZDYU9.js +2 -0
- package/public/assets/{login-tzTSqiOS.js.map → login-huNZDYU9.js.map} +1 -1
- package/public/assets/{modal-CwoBUXp3.js → modal-tByFpy18.js} +2 -2
- package/public/assets/{modal-CwoBUXp3.js.map → modal-tByFpy18.js.map} +1 -1
- package/public/assets/{passkey-V_tav53p.js → passkey-CgAHaQ1p.js} +2 -2
- package/public/assets/{passkey-V_tav53p.js.map → passkey-CgAHaQ1p.js.map} +1 -1
- package/public/assets/passkey-huNZDYU9.js +2 -0
- package/public/assets/{passkey-tzTSqiOS.js.map → passkey-huNZDYU9.js.map} +1 -1
- package/public/assets/{passkey-Jw2CRzJp.js → passkey-v9Pzv-4A.js} +2 -2
- package/public/assets/{passkey-Jw2CRzJp.js.map → passkey-v9Pzv-4A.js.map} +1 -1
- package/public/assets/{password-6QOqTV_s.js → password-Cuh9Hnjp.js} +2 -2
- package/public/assets/{password-6QOqTV_s.js.map → password-Cuh9Hnjp.js.map} +1 -1
- package/public/assets/password-huNZDYU9.js +2 -0
- package/public/assets/{password-tzTSqiOS.js.map → password-huNZDYU9.js.map} +1 -1
- package/public/assets/{password-strength-DtGMHOD-.js → password-strength-BIPcWiM_.js} +2 -2
- package/public/assets/{password-strength-DtGMHOD-.js.map → password-strength-BIPcWiM_.js.map} +1 -1
- package/public/assets/{pin-input-DdhzDNy9.js → pin-input-CITrr-Yc.js} +2 -2
- package/public/assets/{pin-input-DdhzDNy9.js.map → pin-input-CITrr-Yc.js.map} +1 -1
- package/public/assets/{profile-DVjIWt8X.js → profile-BHPL9Px_.js} +2 -2
- package/public/assets/{profile-DVjIWt8X.js.map → profile-BHPL9Px_.js.map} +1 -1
- package/public/assets/{profile-SHr6F-7b.js → profile-C8KaELVe.js} +2 -2
- package/public/assets/{profile-SHr6F-7b.js.map → profile-C8KaELVe.js.map} +1 -1
- package/public/assets/{recovery-CvNl-yt9.js → recovery-CVROV9nj.js} +2 -2
- package/public/assets/{recovery-CvNl-yt9.js.map → recovery-CVROV9nj.js.map} +1 -1
- package/public/assets/{register-SFXa5EeL.js → register-cpxjX3qr.js} +2 -2
- package/public/assets/{register-SFXa5EeL.js.map → register-cpxjX3qr.js.map} +1 -1
- package/public/assets/register-huNZDYU9.js +2 -0
- package/public/assets/{register-tzTSqiOS.js.map → register-huNZDYU9.js.map} +1 -1
- package/public/assets/reset-huNZDYU9.js +2 -0
- package/public/assets/{reset-tzTSqiOS.js.map → reset-huNZDYU9.js.map} +1 -1
- package/public/assets/{reset-l-pLzjwe.js → reset-iNqYdsoa.js} +2 -2
- package/public/assets/{reset-l-pLzjwe.js.map → reset-iNqYdsoa.js.map} +1 -1
- package/public/assets/{select-D2H87qXJ.js → select-DdjQtcLJ.js} +2 -2
- package/public/assets/{select-D2H87qXJ.js.map → select-DdjQtcLJ.js.map} +1 -1
- package/public/assets/select-huNZDYU9.js +2 -0
- package/public/assets/{select-tzTSqiOS.js.map → select-huNZDYU9.js.map} +1 -1
- package/public/assets/separator-B0Bvh1he.js.map +1 -1
- package/public/assets/{shell-header-bar-BPgCjFPv.js → shell-header-bar-JcFhS7mb.js} +2 -2
- package/public/assets/{shell-header-bar-BPgCjFPv.js.map → shell-header-bar-JcFhS7mb.js.map} +1 -1
- package/public/assets/{terms-DVjIWt8X.js → terms-BHPL9Px_.js} +2 -2
- package/public/assets/{terms-DVjIWt8X.js.map → terms-BHPL9Px_.js.map} +1 -1
- package/public/assets/{terms-D5rfQhog.js → terms-BjwMWDFp.js} +2 -2
- package/public/assets/{terms-D5rfQhog.js.map → terms-BjwMWDFp.js.map} +1 -1
- package/public/assets/terms-checkbox-list-DNOq4Pb2.js +2 -0
- package/public/assets/terms-checkbox-list-DNOq4Pb2.js.map +1 -0
- package/public/assets/{totp-wY0Zji8E.js → totp-D_EWG1h8.js} +2 -2
- package/public/assets/{totp-wY0Zji8E.js.map → totp-D_EWG1h8.js.map} +1 -1
- package/public/assets/{totp-BbQi-gsF.js → totp-DetFwOkk.js} +2 -2
- package/public/assets/{totp-BbQi-gsF.js.map → totp-DetFwOkk.js.map} +1 -1
- package/public/assets/use-totp-setup-DtlgPUPJ.js +3 -0
- package/public/assets/use-totp-setup-DtlgPUPJ.js.map +1 -0
- package/public/assets/{useAriaLabelledBy-9o83pv9X.js → useAriaLabelledBy-IIwdHUss.js} +2 -2
- package/public/assets/{useAriaLabelledBy-9o83pv9X.js.map → useAriaLabelledBy-IIwdHUss.js.map} +1 -1
- package/public/assets/useQuery-Ccv_nzQl.js +2 -0
- package/public/assets/{useQuery-CI5Ukuae.js.map → useQuery-Ccv_nzQl.js.map} +1 -1
- package/public/assets/{users-DqCnblEo.js → users-B1gvPM_x.js} +2 -2
- package/public/assets/{users-DqCnblEo.js.map → users-B1gvPM_x.js.map} +1 -1
- package/public/index.html +2 -2
- package/public/assets/2fa-BlpDkf-r.js +0 -2
- package/public/assets/2fa-tzTSqiOS.js +0 -2
- package/public/assets/auth-choice-row-DUvFyo7b.js +0 -2
- package/public/assets/email-tzTSqiOS.js +0 -2
- package/public/assets/forgot-tzTSqiOS.js +0 -2
- package/public/assets/index-H6CJKjiN.js.map +0 -1
- package/public/assets/login-tzTSqiOS.js +0 -2
- package/public/assets/passkey-tzTSqiOS.js +0 -2
- package/public/assets/password-tzTSqiOS.js +0 -2
- package/public/assets/register-tzTSqiOS.js +0 -2
- package/public/assets/reset-tzTSqiOS.js +0 -2
- package/public/assets/select-tzTSqiOS.js +0 -2
- package/public/assets/terms-checkbox-list-BKJcS2pJ.js +0 -2
- package/public/assets/terms-checkbox-list-BKJcS2pJ.js.map +0 -1
- package/public/assets/use-totp-setup-OH_GTMxA.js +0 -3
- package/public/assets/use-totp-setup-OH_GTMxA.js.map +0 -1
- package/public/assets/useQuery-CI5Ukuae.js +0 -2
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{q as e}from"./createLucideIcon-BPpAIXR_.js";import{t}from"./avatar-D-
|
|
2
|
-
//# sourceMappingURL=initial-avatar-
|
|
1
|
+
import{q as e}from"./createLucideIcon-BPpAIXR_.js";import{t}from"./avatar-D-FRGa1m.js";var n=e(),r={sm:{"--tr-avatar-size":`2rem`,"--tr-avatar-font-size":`var(--tinyrack-text-sm)`},md:{"--tr-avatar-size":`3rem`,"--tr-avatar-font-size":`var(--tinyrack-text-xl)`},lg:{"--tr-avatar-size":`4rem`,"--tr-avatar-font-size":`var(--tinyrack-text-2xl)`}};function i({email:e,size:i=`md`}){let a=e.charAt(0).toUpperCase();return(0,n.jsx)(t.Root,{className:`shrink-0 font-tinyrack-bold`,shape:`circle`,style:{...r[i],"--tr-avatar-background":`var(--tinyrack-primary)`,"--tr-avatar-color":`var(--tinyrack-on-primary)`,"--tr-avatar-border":`transparent`},children:(0,n.jsx)(t.Fallback,{children:a})})}export{i as t};
|
|
2
|
+
//# sourceMappingURL=initial-avatar-E5DK9Igm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"initial-avatar-
|
|
1
|
+
{"version":3,"file":"initial-avatar-E5DK9Igm.js","names":[],"sources":["../../../frontend/src/components/ui/initial-avatar.tsx"],"sourcesContent":["import { TRAvatar } from '@tinyrack/ui/components/avatar';\nimport type { CSSProperties } from 'react';\n\ntype InitialAvatarProps = {\n email: string;\n size?: 'sm' | 'md' | 'lg';\n};\n\n/**\n * Brand-colored initial avatar built on the design system's `TRAvatar`.\n * Size, font size and the primary color pair are applied through the\n * component's own `--tr-avatar-*` custom properties so it composes the\n * design system rather than re-implementing an avatar.\n */\nconst sizeVars: Record<\n NonNullable<InitialAvatarProps['size']>,\n CSSProperties\n> = {\n sm: {\n '--tr-avatar-size': '2rem',\n '--tr-avatar-font-size': 'var(--tinyrack-text-sm)',\n } as CSSProperties,\n md: {\n '--tr-avatar-size': '3rem',\n '--tr-avatar-font-size': 'var(--tinyrack-text-xl)',\n } as CSSProperties,\n lg: {\n '--tr-avatar-size': '4rem',\n '--tr-avatar-font-size': 'var(--tinyrack-text-2xl)',\n } as CSSProperties,\n};\n\nexport function InitialAvatar({ email, size = 'md' }: InitialAvatarProps) {\n const initial = email.charAt(0).toUpperCase();\n\n return (\n <TRAvatar.Root\n className=\"shrink-0 font-tinyrack-bold\"\n shape=\"circle\"\n style={\n {\n ...sizeVars[size],\n '--tr-avatar-background': 'var(--tinyrack-primary)',\n '--tr-avatar-color': 'var(--tinyrack-on-primary)',\n '--tr-avatar-border': 'transparent',\n } as CSSProperties\n }\n >\n <TRAvatar.Fallback>{initial}</TRAvatar.Fallback>\n </TRAvatar.Root>\n );\n}\n"],"mappings":"iGAcM,EAGF,CACF,GAAI,CACF,mBAAoB,OACpB,wBAAyB,yBAC3B,EACA,GAAI,CACF,mBAAoB,OACpB,wBAAyB,yBAC3B,EACA,GAAI,CACF,mBAAoB,OACpB,wBAAyB,0BAC3B,CACF,EAEA,SAAgB,EAAc,CAAE,QAAO,OAAO,MAA4B,CACxE,IAAM,EAAU,EAAM,OAAO,CAAC,CAAC,CAAC,YAAY,EAE5C,OACE,EAAA,EAAA,IAAA,CAAC,EAAS,KAAV,CACE,UAAU,8BACV,MAAM,SACN,MACE,CACE,GAAG,EAAS,GACZ,yBAA0B,0BAC1B,oBAAqB,6BACrB,qBAAsB,aACxB,YAGF,EAAA,EAAA,IAAA,CAAC,EAAS,SAAV,CAAA,SAAoB,CAA2B,CAAA,CAClC,CAAA,CAEnB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{G as e,J as t,W as n,Z as r,q as i}from"./createLucideIcon-BPpAIXR_.js";import{xn as a}from"./config-DTQCUlfZ.js";import{n as o}from"./field-
|
|
2
|
-
//# sourceMappingURL=input-
|
|
1
|
+
import{G as e,J as t,W as n,Z as r,q as i}from"./createLucideIcon-BPpAIXR_.js";import{xn as a}from"./config-DTQCUlfZ.js";import{n as o}from"./field-BMT214_m.js";var s=i(),c=r(t(),1),l=(0,c.createContext)(!1);function u(){return(0,c.useContext)(l)}var d=c.forwardRef(function(e,t){return(0,s.jsx)(o,{ref:t,...e})});function f({className:t,uiSize:n=`md`,appearance:r=`solid`,...i}){a();let o=u();return(0,s.jsx)(d,{...i,className:e(o?`tr-input tr-input-group-input`:`tr-input`,t),"data-appearance":r,"data-ui-size":n})}function p({className:e,type:t=`button`,...r}){return(0,s.jsx)(`button`,{...r,className:n(`tr-input-group-action`,e),type:t})}function m({className:e,side:t=`start`,...r}){return(0,s.jsx)(`span`,{...r,className:n(`tr-input-group-adornment`,e),"data-side":t})}function h({children:e,className:t,uiSize:r=`md`,...i}){return a(),(0,s.jsx)(`div`,{...i,className:n(`tr-input-group`,t),"data-ui-size":r,children:(0,s.jsx)(l,{value:!0,children:e})})}var g=Object.assign(f,{Group:h,Adornment:m,Action:p});export{g as t};
|
|
2
|
+
//# sourceMappingURL=input-CxVeC8Pn.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"input-
|
|
1
|
+
{"version":3,"file":"input-CxVeC8Pn.js","names":["Field.Control","TRInput"],"sources":["../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/input/input-group-context.js","../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/input/Input.mjs","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/input/input.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/input/input-action.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/input/input-adornment.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/input/input-group.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/input/index.js"],"sourcesContent":["\"use client\";\nimport { createContext, useContext } from \"react\";\n//#region src/components/input/input-group-context.ts\n/**\n* Lets an `Input` discover that it is rendered inside a `Group` so it can take\n* the group's control styling itself. Without it a caller would have to write\n* the internal `tr-input-group-input` class by hand, which is the thing the\n* group is meant to spare them.\n*/\nconst TRInputGroupContext = createContext(false);\nfunction useIsInsideTRInputGroup() {\n\treturn useContext(TRInputGroupContext);\n}\n//#endregion\nexport { TRInputGroupContext, useIsInsideTRInputGroup };\n\n//# sourceMappingURL=input-group-context.js.map","'use client';\n\nimport * as React from 'react';\nimport { Field } from \"../field/index.mjs\";\n\n/**\n * A native input element that automatically works with [Field](https://base-ui.com/react/components/field).\n * Renders an `<input>` element.\n *\n * Documentation: [Base UI Input](https://base-ui.com/react/components/input)\n */\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport const Input = /*#__PURE__*/React.forwardRef(function Input(props, forwardedRef) {\n return /*#__PURE__*/_jsx(Field.Control, {\n ref: forwardedRef,\n ...props\n });\n});\nif (process.env.NODE_ENV !== \"production\") Input.displayName = \"Input\";","\"use client\";\nimport { useTinyrackFocusModality } from \"../../providers/focus-modality/focus-modality-provider.js\";\nimport { mergeComponentClassName } from \"../../internal/component-class-name.js\";\nimport { useIsInsideTRInputGroup } from \"./input-group-context.js\";\nimport { jsx } from \"react/jsx-runtime\";\nimport { Input } from \"@base-ui/react/input\";\n//#region src/components/input/input.tsx\nfunction TRInput({ className, uiSize = \"md\", appearance = \"solid\", ...props }) {\n\tuseTinyrackFocusModality();\n\tconst insideGroup = useIsInsideTRInputGroup();\n\treturn /* @__PURE__ */ jsx(Input, {\n\t\t...props,\n\t\tclassName: mergeComponentClassName(insideGroup ? \"tr-input tr-input-group-input\" : \"tr-input\", className),\n\t\t\"data-appearance\": appearance,\n\t\t\"data-ui-size\": uiSize\n\t});\n}\n//#endregion\nexport { TRInput };\n\n//# sourceMappingURL=input.js.map","import { mergeClassNames } from \"../../internal/component-class-name.js\";\nimport { jsx } from \"react/jsx-runtime\";\n//#region src/components/input/input-action.tsx\n/**\n* A control at the edge of a `Group` — reveal a password, clear the value, run\n* a search.\n*\n* Defaults to `type=\"button\"` so it cannot accidentally submit the surrounding\n* form. It is a real button and stays in the tab order, so give it an\n* accessible name.\n*/\nfunction TRInputAction({ className, type = \"button\", ...props }) {\n\treturn /* @__PURE__ */ jsx(\"button\", {\n\t\t...props,\n\t\tclassName: mergeClassNames(\"tr-input-group-action\", className),\n\t\ttype\n\t});\n}\n//#endregion\nexport { TRInputAction };\n\n//# sourceMappingURL=input-action.js.map","import { mergeClassNames } from \"../../internal/component-class-name.js\";\nimport { jsx } from \"react/jsx-runtime\";\n//#region src/components/input/input-adornment.tsx\n/**\n* Static content inside a `Group` — a leading icon, a unit, a prefix.\n*\n* Not interactive: it does not take pointer events, so clicking it still\n* focuses the input. Use `Action` for something the user can press, and mark\n* decorative icons `aria-hidden` so the field keeps a single accessible name.\n*/\nfunction TRInputAdornment({ className, side = \"start\", ...props }) {\n\treturn /* @__PURE__ */ jsx(\"span\", {\n\t\t...props,\n\t\tclassName: mergeClassNames(\"tr-input-group-adornment\", className),\n\t\t\"data-side\": side\n\t});\n}\n//#endregion\nexport { TRInputAdornment };\n\n//# sourceMappingURL=input-adornment.js.map","\"use client\";\nimport { useTinyrackFocusModality } from \"../../providers/focus-modality/focus-modality-provider.js\";\nimport { mergeClassNames } from \"../../internal/component-class-name.js\";\nimport { TRInputGroupContext } from \"./input-group-context.js\";\nimport { jsx } from \"react/jsx-runtime\";\n//#region src/components/input/input-group.tsx\n/**\n* Frames an input together with adornments and actions so they read as one\n* control: the border, background, and focus ring belong to the group, and the\n* input inside it goes flat.\n*\n* The group owns the control height, so size it here rather than on the input.\n*/\nfunction TRInputGroup({ children, className, uiSize = \"md\", ...props }) {\n\tuseTinyrackFocusModality();\n\treturn /* @__PURE__ */ jsx(\"div\", {\n\t\t...props,\n\t\tclassName: mergeClassNames(\"tr-input-group\", className),\n\t\t\"data-ui-size\": uiSize,\n\t\tchildren: /* @__PURE__ */ jsx(TRInputGroupContext, {\n\t\t\tvalue: true,\n\t\t\tchildren\n\t\t})\n\t});\n}\n//#endregion\nexport { TRInputGroup };\n\n//# sourceMappingURL=input-group.js.map","import { TRInput as TRInput$1 } from \"./input.js\";\nimport { TRInputAction } from \"./input-action.js\";\nimport { TRInputAdornment } from \"./input-adornment.js\";\nimport { TRInputGroup } from \"./input-group.js\";\n//#region src/components/input/index.tsx\n/**\n* `TRInput` stays directly renderable for the common single-control case and\n* carries the group parts as a namespace, so adding an adornment never means\n* changing the element you already render.\n*/\nconst TRInput = Object.assign(TRInput$1, {\n\tGroup: TRInputGroup,\n\tAdornment: TRInputAdornment,\n\tAction: TRInputAction\n});\n//#endregion\nexport { TRInput, TRInputAction, TRInputAdornment, TRInputGroup };\n\n//# sourceMappingURL=index.js.map"],"x_google_ignoreList":[0,1,2,3,4,5,6],"mappings":"sLASA,GAAA,EAAA,EAAA,cAAA,CAAA,EAAA,EACA,SAAA,GAAA,0BAEA,CCAA,IAAa,EAAqB,EAAM,WAAW,SAAe,EAAO,EAAc,CACrF,OAAoB,EAAA,EAAA,IAAA,CAAKA,EAAe,CACtC,IAAK,EACL,GAAG,CACL,CAAC,CACH,CAAC,ECVD,SAASC,EAAQ,CAAE,YAAW,SAAS,KAAM,aAAa,QAAS,GAAG,GAAS,CAC9E,EAAyB,EACzB,IAAM,EAAc,EAAwB,EAC5C,OAAuB,EAAA,EAAA,IAAA,CAAI,EAAO,CACjC,GAAG,EACH,UAAW,EAAwB,EAAc,gCAAkC,WAAY,CAAS,EACxG,kBAAmB,EACnB,eAAgB,CACjB,CAAC,CACF,CCLA,SAAS,EAAc,CAAE,YAAW,OAAO,SAAU,GAAG,GAAS,CAChE,OAAuB,EAAA,EAAA,IAAA,CAAI,SAAU,CACpC,GAAG,EACH,UAAW,EAAgB,wBAAyB,CAAS,EAC7D,MACD,CAAC,CACF,CCPA,SAAS,EAAiB,CAAE,YAAW,OAAO,QAAS,GAAG,GAAS,CAClE,OAAuB,EAAA,EAAA,IAAA,CAAI,OAAQ,CAClC,GAAG,EACH,UAAW,EAAgB,2BAA4B,CAAS,EAChE,YAAa,CACd,CAAC,CACF,CCHA,SAAS,EAAa,CAAE,WAAU,YAAW,SAAS,KAAM,GAAG,GAAS,CAEvE,OADA,EAAyB,GACF,EAAA,EAAA,IAAA,CAAI,MAAO,CACjC,GAAG,EACH,UAAW,EAAgB,iBAAkB,CAAS,EACtD,eAAgB,EAChB,UAA0B,EAAA,EAAA,IAAA,CAAI,EAAqB,CAClD,MAAO,GACP,UACD,CAAC,CACF,CAAC,CACF,CCdA,IAAM,EAAU,OAAO,OAAO,EAAW,CACxC,MAAO,EACP,UAAW,EACX,OAAQ,CACT,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link-C-WdTj3F.js","names":[],"sources":["../../../../node_modules/.pnpm/@tinyrack+ui@0.
|
|
1
|
+
{"version":3,"file":"link-C-WdTj3F.js","names":[],"sources":["../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/link/link.js"],"sourcesContent":["\"use client\";\nimport { mergeClassNames } from \"../../internal/component-class-name.js\";\nimport { cloneElement, isValidElement } from \"react\";\nimport { useRender } from \"@base-ui/react/use-render\";\n//#region src/components/link/link.tsx\nfunction TRLink({ className, disabled = false, href, onClick, onKeyDown, ref, render, underline = \"hover\", variant = \"default\", ...props }) {\n\tconst resolvedRender = disabled && isValidElement(render) ? cloneElement(render, { href: void 0 }) : render;\n\treturn useRender({\n\t\tdefaultTagName: \"a\",\n\t\tprops: {\n\t\t\t...props,\n\t\t\t\"aria-disabled\": disabled || void 0,\n\t\t\tclassName: mergeClassNames(\"tr-link\", className),\n\t\t\t\"data-disabled\": disabled ? \"\" : void 0,\n\t\t\t\"data-underline\": underline,\n\t\t\t\"data-variant\": variant,\n\t\t\thref: disabled ? void 0 : href,\n\t\t\tonClick(event) {\n\t\t\t\tif (disabled) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tonClick?.(event);\n\t\t\t},\n\t\t\tonKeyDown(event) {\n\t\t\t\tif (disabled && (event.key === \"Enter\" || event.key === \" \")) {\n\t\t\t\t\tevent.preventDefault();\n\t\t\t\t\tevent.stopPropagation();\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tonKeyDown?.(event);\n\t\t\t},\n\t\t\ttabIndex: disabled ? -1 : props.tabIndex\n\t\t},\n\t\tref,\n\t\trender: resolvedRender\n\t});\n}\n//#endregion\nexport { TRLink };\n\n//# sourceMappingURL=link.js.map"],"x_google_ignoreList":[0],"mappings":"uFAKA,SAAS,EAAO,CAAE,YAAW,WAAW,GAAO,OAAM,UAAS,YAAW,MAAK,SAAQ,YAAY,QAAS,UAAU,UAAW,GAAG,GAAS,CAC3I,IAAM,EAAiB,IAAA,EAAA,EAAA,eAAA,CAA2B,CAAM,GAAA,EAAA,EAAA,aAAA,CAAiB,EAAQ,CAAE,KAAM,IAAK,EAAE,CAAC,EAAI,EACrG,OAAO,EAAU,CAChB,eAAgB,IAChB,MAAO,CACN,GAAG,EACH,gBAAiB,GAAY,IAAK,GAClC,UAAW,EAAgB,UAAW,CAAS,EAC/C,gBAAiB,EAAW,GAAK,IAAK,GACtC,iBAAkB,EAClB,eAAgB,EAChB,KAAM,EAAW,IAAK,GAAI,EAC1B,QAAQ,EAAO,CACd,GAAI,EAAU,CACb,EAAM,eAAe,EACrB,EAAM,gBAAgB,EACtB,MACD,CACA,IAAU,CAAK,CAChB,EACA,UAAU,EAAO,CAChB,GAAI,IAAa,EAAM,MAAQ,SAAW,EAAM,MAAQ,KAAM,CAC7D,EAAM,eAAe,EACrB,EAAM,gBAAgB,EACtB,MACD,CACA,IAAY,CAAK,CAClB,EACA,SAAU,EAAW,GAAK,EAAM,QACjC,EACA,MACA,OAAQ,CACT,CAAC,CACF"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{J as e,Z as t,i as n,n as r,q as i,s as a}from"./createLucideIcon-BPpAIXR_.js";import{On as o,Sr as s,t as c}from"./config-DTQCUlfZ.js";import{t as l}from"./useMutation-BWZ0G4Td.js";import{nt as u}from"./shell-header-bar-
|
|
2
|
-
//# sourceMappingURL=login-
|
|
1
|
+
import{J as e,Z as t,i as n,n as r,q as i,s as a}from"./createLucideIcon-BPpAIXR_.js";import{On as o,Sr as s,t as c}from"./config-DTQCUlfZ.js";import{t as l}from"./useMutation-BWZ0G4Td.js";import{nt as u}from"./shell-header-bar-JcFhS7mb.js";import{t as d}from"./fingerprint-pattern-DP62Vk8x.js";import{t as f}from"./authorization-context-banner-DznMDPOX.js";import{a as p,n as m,r as h,t as g}from"./provider-logos-u1YURWQr.js";import{t as _}from"./mail-D0B_1Dzd.js";import{F as v,N as y,R as b,S as x,b as S,j as C,z as w}from"./index-CKpnrw9M.js";import{t as T}from"./auth-page-header-DgGAaIiP.js";import{t as E}from"./alert-leqyvHOp.js";import{t as D}from"./auth-layout-CiNn2-Nb.js";import{t as O}from"./promise-OpBtq8tG.js";import{t as k}from"./passkey-error-ZqqPQUta.js";import{t as A}from"./passkey-J1qnYp9A.js";var j=t(e()),M=i(),N={google:(0,M.jsx)(h,{className:`size-tinyrack-xl`}),github:(0,M.jsx)(m,{className:`size-tinyrack-xl`}),apple:(0,M.jsx)(g,{className:`size-tinyrack-xl`})};function P({icon:e,label:t,providerType:n,render:i,onClick:o,isLoading:s,disabled:c}){let l;l=n&&n!==`generic_oauth`?N[n]:typeof e==`string`?(0,M.jsx)(`img`,{alt:``,className:`size-tinyrack-xl`,src:e}):e||(0,M.jsx)(p,{"aria-hidden":!0,className:`size-tinyrack-xl`});let u=(0,M.jsxs)(M.Fragment,{children:[(0,M.jsx)(`span`,{className:`flex size-tinyrack-xl shrink-0 items-center justify-center`,children:l}),(0,M.jsx)(r,{truncate:!0,variant:`body`,children:t})]}),d=`w-full justify-start gap-tinyrack-md transition-colors duration-tinyrack-fast ease-tinyrack-standard`;return i?(0,M.jsx)(w,{appearance:`outline`,className:`cursor-pointer ${d}`,intent:`neutral`,render:i,uiSize:`lg`,children:u}):(0,M.jsx)(a,{appearance:`outline`,className:d,disabled:c,intent:`neutral`,loading:s,onClick:o,type:`button`,uiSize:`lg`,children:u})}var F={access_denied:`oauth.error.accessDenied`,temporarily_unavailable:`oauth.error.temporarilyUnavailable`,server_error:`oauth.error.serverError`,registration_email_not_allowed:`oauth.error.registrationEmailNotAllowed`};function I(){let{t:e,i18n:t}=n(),r=o(),i=s(),a=S.useSearch(),p=a.lang??t.language,{data:m}=u(c),h=m.registration.signup_notice?.[p]??m.registration.signup_notice?.[m.i18n.fallback_language],g=m.identity_providers,w=a.oauth_error,N=w?e(F[w]??`oauth.error.failed`):void 0,I=m.auth.password.enabled,L=m.auth.passkey.enabled,[R,z]=(0,j.useState)(null),B=async e=>{e.user&&(i.setQueryData(x.queryKey,e),await O(),v(a)?window.location.href=C(a):r.navigate({to:`/profile`})),i.invalidateQueries({queryKey:x.queryKey})},V=l({...A,onSuccess:e=>{z(null),B(e)},onError:t=>{if(k(t)===`unsupported`){z(e(`login.passkey.error.unsupported`));return}z(e(`login.passkey.error.failed`))},onSettled:()=>{i.invalidateQueries({queryKey:x.queryKey})}}),H=e=>{let t=`/api/oauth/${e}/authorize?mode=login`;if(v(a)){let e=new URL(C(a)),n=`${e.pathname}${e.search}${e.hash}`;t+=`&return_url=${encodeURIComponent(n)}`}return t};return(0,M.jsxs)(D,{children:[(0,M.jsx)(T,{subtitle:e(`login.selectMethod.subtitle`)||void 0,title:e(`login.title`)}),(0,M.jsx)(f,{search:a}),N&&(0,M.jsx)(E,{icon:b,type:`error`,children:N}),R&&(0,M.jsx)(E,{icon:b,type:`error`,children:R}),(0,M.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-sm`,children:[g.map(e=>(0,M.jsx)(P,{icon:e.icon_url,label:e.display_name,providerType:e.type,render:(0,M.jsx)(`a`,{href:H(e.id)})},e.id)),I&&(0,M.jsx)(P,{icon:(0,M.jsx)(_,{"aria-hidden":!0,className:`size-tinyrack-xl`}),label:e(`login.method.password`),render:(0,M.jsx)(`a`,{href:(()=>{let e=new URLSearchParams;for(let[t,n]of Object.entries(y(a)))if(n!==void 0){let r=String(n);e.set(t,t===`account_selected`?decodeURIComponent(r).replaceAll(`"`,``):r)}let t=e.toString();return t?`/login/password?${t}`:`/login/password`})()})}),L&&(0,M.jsx)(P,{disabled:V.isPending,icon:(0,M.jsx)(d,{"aria-hidden":!0,className:`size-tinyrack-xl`}),isLoading:V.isPending,label:e(`login.method.passkey`),onClick:()=>{z(null),V.mutate()}})]}),h&&(0,M.jsx)(`div`,{className:`prose prose-sm text-center text-tinyrack-text-muted text-tinyrack-xs! **:text-tinyrack-xs!`,dangerouslySetInnerHTML:{__html:h}})]})}export{I as component};
|
|
2
|
+
//# sourceMappingURL=login-1YuiZD30.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-CBinfk4L.js","names":["useMutation","useQueryClient","useSuspenseQuery","useRouter","CircleAlertIcon","FingerprintIcon","MailIcon","useState","useTranslation","AuthMethodTile","AuthPageHeader","AuthorizationContextBanner","Alert","AuthLayout","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","classifyPasskeyError","tick","appConfigQueryOptions","authenticateWithPasskeyMutationOptions","getSessionQueryOptions","OAUTH_ERROR_I18N_MAP","Record","access_denied","temporarily_unavailable","server_error","registration_email_not_allowed","Route","Login","t","i18n","router","queryClient","search","useSearch","lang","language","data","configData","implicitNotice","registration","signup_notice","fallback_language","oauthProviders","identity_providers","oauthError","oauth_error","oauthErrorMessage","undefined","isPasswordAuthEnabled","auth","password","enabled","isPasskeyEnabled","passkey","passkeyError","setPasskeyError","handlePasskeySuccess","AuthResponse","user","setQueryData","queryKey","window","location","href","navigate","to","invalidateQueries","passkeyLoginMutation","onSuccess","onError","error","reason","onSettled","buildOAuthUrl","providerId","oauthUrl","authUrl","URL","returnUrl","pathname","hash","encodeURIComponent","buildPasswordLoginHref","params","URLSearchParams","key","value","Object","entries","stringValue","String","set","decodeURIComponent","replaceAll","query","toString","map","provider","icon_url","id","display_name","type","isPending","mutate","__html","component"],"sources":["../../../frontend/src/components/auth/auth-method-tile.tsx","../../../frontend/src/routes/login/index.tsx?tsr-split=component"],"sourcesContent":["import { TRButton } from '@tinyrack/ui/components/button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { LinkIcon } from 'lucide-react';\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n AppleLogo,\n GithubLogo,\n GoogleLogo,\n} from '#frontend/components/auth/provider-logos.tsx';\nimport type { OAuthProviderType } from '#frontend/queries/config.ts';\n\ntype AuthMethodTileBaseProps = {\n /** A provider icon URL, or a ready-made element. */\n icon?: string | ReactNode;\n label: string;\n providerType?: OAuthProviderType;\n};\n\ntype AuthMethodTileProps = AuthMethodTileBaseProps &\n (\n | {\n /** Navigating method (OAuth, or the email form). Must be a real anchor. */\n render: ReactElement;\n onClick?: never;\n isLoading?: never;\n disabled?: never;\n }\n | {\n render?: never;\n /** In-page method (passkey), which runs a ceremony instead of navigating. */\n onClick: () => void;\n isLoading?: boolean;\n disabled?: boolean;\n }\n );\n\nconst PROVIDER_LOGOS: Record<\n Exclude<OAuthProviderType, 'generic_oauth'>,\n ReactNode\n> = {\n google: <GoogleLogo className=\"size-tinyrack-xl\" />,\n github: <GithubLogo className=\"size-tinyrack-xl\" />,\n apple: <AppleLogo className=\"size-tinyrack-xl\" />,\n};\n\n/**\n * One sign-in method on the method chooser.\n *\n * Full-width rows at every breakpoint. The previous three-across grid gave\n * each tile a third of the card, so provider names wrapped onto two lines and\n * the icons and labels never lined up between tiles.\n */\nexport function AuthMethodTile({\n icon,\n label,\n providerType,\n render,\n onClick,\n isLoading,\n disabled,\n}: AuthMethodTileProps) {\n let leading: ReactNode;\n if (providerType && providerType !== 'generic_oauth') {\n leading = PROVIDER_LOGOS[providerType];\n } else if (typeof icon === 'string') {\n leading = <img alt=\"\" className=\"size-tinyrack-xl\" src={icon} />;\n } else if (icon) {\n leading = icon;\n } else {\n leading = <LinkIcon aria-hidden className=\"size-tinyrack-xl\" />;\n }\n\n const content = (\n <>\n <span className=\"flex size-tinyrack-xl shrink-0 items-center justify-center\">\n {leading}\n </span>\n <TRText truncate variant=\"body\">\n {label}\n </TRText>\n </>\n );\n\n const className =\n 'w-full justify-start gap-tinyrack-md transition-colors duration-tinyrack-fast ease-tinyrack-standard';\n\n if (render) {\n return (\n <TRLinkButton\n appearance=\"outline\"\n className={`cursor-pointer ${className}`}\n intent=\"neutral\"\n render={render}\n uiSize=\"lg\"\n >\n {content}\n </TRLinkButton>\n );\n }\n\n return (\n <TRButton\n appearance=\"outline\"\n className={className}\n disabled={disabled}\n intent=\"neutral\"\n loading={isLoading}\n onClick={onClick}\n type=\"button\"\n uiSize=\"lg\"\n >\n {content}\n </TRButton>\n );\n}\n","import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, redirect, useRouter } from '@tanstack/react-router';\nimport { CircleAlertIcon, FingerprintIcon, MailIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthMethodTile } from '#frontend/components/auth/auth-method-tile.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { AuthorizationContextBanner } from '#frontend/components/auth/authorization-context-banner.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n hasAuthorizationContext,\n isOAuthFlow,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport { classifyPasskeyError } from '#frontend/libs/passkey-error.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getAuthorizationContextQueryOptions } from '#frontend/queries/authorization-context.ts';\nimport {\n type AppConfigs,\n appConfigQueryOptions,\n} from '#frontend/queries/config.ts';\nimport { authenticateWithPasskeyMutationOptions } from '#frontend/queries/passkey.ts';\nimport {\n type AuthResponse,\n getSessionQueryOptions,\n} from '#frontend/queries/session.ts';\n\nconst SearchSchema = OAuthSearchSchema.extend({\n oauth_error: z.string().optional(),\n oauth_error_description: z.string().optional(),\n});\n\nconst OAUTH_ERROR_I18N_MAP: Record<string, string> = {\n access_denied: 'oauth.error.accessDenied',\n temporarily_unavailable: 'oauth.error.temporarilyUnavailable',\n server_error: 'oauth.error.serverError',\n registration_email_not_allowed: 'oauth.error.registrationEmailNotAllowed',\n};\n\nexport const Route = createFileRoute('/login/')({\n component: Login,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n loaderDeps: ({ search }) => ({\n search,\n }),\n loader: async ({ context, deps }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n\n if (shouldRedirectToPassword(config, deps.search)) {\n throw redirect({\n to: '/login/password',\n search: extractOAuthParams(deps.search),\n replace: true,\n });\n }\n\n if (hasAuthorizationContext(deps.search)) {\n await context.queryClient.ensureQueryData(\n getAuthorizationContextQueryOptions(deps.search),\n );\n }\n },\n});\n\nfunction shouldRedirectToPassword(\n config: AppConfigs,\n search: z.infer<typeof SearchSchema>,\n): boolean {\n if (search.oauth_error) {\n return false;\n }\n\n return (\n config.auth.password.enabled &&\n !config.auth.passkey.enabled &&\n config.identity_providers.length === 0\n );\n}\n\nfunction Login() {\n const { t, i18n } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const lang = search.lang ?? i18n.language;\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n\n // Get implicit notice from config for OAuth signup\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n const oauthProviders = configData.identity_providers;\n\n const oauthError = search.oauth_error;\n const oauthErrorMessage = oauthError\n ? t(OAUTH_ERROR_I18N_MAP[oauthError] ?? 'oauth.error.failed')\n : undefined;\n\n const isPasswordAuthEnabled = configData.auth.password.enabled;\n const isPasskeyEnabled = configData.auth.passkey.enabled;\n const [passkeyError, setPasskeyError] = useState<string | null>(null);\n\n const handlePasskeySuccess = async (data: AuthResponse) => {\n if (data.user) {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, data);\n await tick();\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n }\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n };\n\n const passkeyLoginMutation = useMutation({\n ...authenticateWithPasskeyMutationOptions,\n onSuccess: (data) => {\n setPasskeyError(null);\n handlePasskeySuccess(data);\n },\n onError: (error) => {\n const reason = classifyPasskeyError(error);\n if (reason === 'unsupported') {\n setPasskeyError(t('login.passkey.error.unsupported'));\n return;\n }\n\n setPasskeyError(t('login.passkey.error.failed'));\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const buildOAuthUrl = (providerId: string) => {\n let oauthUrl = `/api/oauth/${providerId}/authorize?mode=login`;\n\n if (isOAuthFlow(search)) {\n const authUrl = new URL(buildAuthenticatedAuthorizeUrl(search));\n const returnUrl = `${authUrl.pathname}${authUrl.search}${authUrl.hash}`;\n oauthUrl += `&return_url=${encodeURIComponent(returnUrl)}`;\n }\n\n return oauthUrl;\n };\n\n const buildPasswordLoginHref = () => {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(extractOAuthParams(search))) {\n if (value !== undefined) {\n const stringValue = String(value);\n params.set(\n key,\n key === 'account_selected'\n ? decodeURIComponent(stringValue).replaceAll('\"', '')\n : stringValue,\n );\n }\n }\n const query = params.toString();\n return query ? `/login/password?${query}` : '/login/password';\n };\n\n return (\n <AuthLayout>\n {/*\n Branding title and subtitle are rendered once, by the brand panel in\n the layout. Repeating them here would give the page two copies of the\n product name and two heading nodes with the same text.\n */}\n <AuthPageHeader\n subtitle={t('login.selectMethod.subtitle') || undefined}\n title={t('login.title')}\n />\n\n <AuthorizationContextBanner search={search} />\n\n {oauthErrorMessage && (\n <Alert icon={CircleAlertIcon} type=\"error\">\n {oauthErrorMessage}\n </Alert>\n )}\n\n {passkeyError && (\n <Alert icon={CircleAlertIcon} type=\"error\">\n {passkeyError}\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n {oauthProviders.map((provider) => (\n <AuthMethodTile\n icon={provider.icon_url}\n key={provider.id}\n label={provider.display_name}\n providerType={provider.type}\n // Leaves the SPA for the provider, so a plain anchor rather than a\n // router link. No children here — the tile supplies them, and an\n // element passed to `render` would override them.\n render={<a href={buildOAuthUrl(provider.id)} />}\n />\n ))}\n\n {isPasswordAuthEnabled && (\n <AuthMethodTile\n icon={<MailIcon aria-hidden className=\"size-tinyrack-xl\" />}\n label={t('login.method.password')}\n render={<a href={buildPasswordLoginHref()} />}\n />\n )}\n\n {isPasskeyEnabled && (\n <AuthMethodTile\n disabled={passkeyLoginMutation.isPending}\n icon={<FingerprintIcon aria-hidden className=\"size-tinyrack-xl\" />}\n isLoading={passkeyLoginMutation.isPending}\n label={t('login.method.passkey')}\n onClick={() => {\n setPasskeyError(null);\n passkeyLoginMutation.mutate();\n }}\n />\n )}\n </div>\n\n {implicitNotice && (\n <div\n className=\"prose prose-sm text-center text-tinyrack-text-muted text-tinyrack-xs! **:text-tinyrack-xs!\"\n dangerouslySetInnerHTML={{ __html: implicitNotice }}\n />\n )}\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"login-1YuiZD30.js","names":["useMutation","useQueryClient","useSuspenseQuery","useRouter","CircleAlertIcon","FingerprintIcon","MailIcon","useState","useTranslation","AuthMethodTile","AuthPageHeader","AuthorizationContextBanner","Alert","AuthLayout","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","classifyPasskeyError","tick","appConfigQueryOptions","authenticateWithPasskeyMutationOptions","getSessionQueryOptions","OAUTH_ERROR_I18N_MAP","Record","access_denied","temporarily_unavailable","server_error","registration_email_not_allowed","Route","Login","t","i18n","router","queryClient","search","useSearch","lang","language","data","configData","implicitNotice","registration","signup_notice","fallback_language","oauthProviders","identity_providers","oauthError","oauth_error","oauthErrorMessage","undefined","isPasswordAuthEnabled","auth","password","enabled","isPasskeyEnabled","passkey","passkeyError","setPasskeyError","handlePasskeySuccess","AuthResponse","user","setQueryData","queryKey","window","location","href","navigate","to","invalidateQueries","passkeyLoginMutation","onSuccess","onError","error","reason","onSettled","buildOAuthUrl","providerId","oauthUrl","authUrl","URL","returnUrl","pathname","hash","encodeURIComponent","buildPasswordLoginHref","params","URLSearchParams","key","value","Object","entries","stringValue","String","set","decodeURIComponent","replaceAll","query","toString","map","provider","icon_url","id","display_name","type","isPending","mutate","__html","component"],"sources":["../../../frontend/src/components/auth/auth-method-tile.tsx","../../../frontend/src/routes/login/index.tsx?tsr-split=component"],"sourcesContent":["import { TRButton } from '@tinyrack/ui/components/button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport { LinkIcon } from 'lucide-react';\nimport type { ReactElement, ReactNode } from 'react';\nimport {\n AppleLogo,\n GithubLogo,\n GoogleLogo,\n} from '#frontend/components/auth/provider-logos.tsx';\nimport type { OAuthProviderType } from '#frontend/queries/config.ts';\n\ntype AuthMethodTileBaseProps = {\n /** A provider icon URL, or a ready-made element. */\n icon?: string | ReactNode;\n label: string;\n providerType?: OAuthProviderType;\n};\n\ntype AuthMethodTileProps = AuthMethodTileBaseProps &\n (\n | {\n /** Navigating method (OAuth, or the email form). Must be a real anchor. */\n render: ReactElement;\n onClick?: never;\n isLoading?: never;\n disabled?: never;\n }\n | {\n render?: never;\n /** In-page method (passkey), which runs a ceremony instead of navigating. */\n onClick: () => void;\n isLoading?: boolean;\n disabled?: boolean;\n }\n );\n\nconst PROVIDER_LOGOS: Record<\n Exclude<OAuthProviderType, 'generic_oauth'>,\n ReactNode\n> = {\n google: <GoogleLogo className=\"size-tinyrack-xl\" />,\n github: <GithubLogo className=\"size-tinyrack-xl\" />,\n apple: <AppleLogo className=\"size-tinyrack-xl\" />,\n};\n\n/**\n * One sign-in method on the method chooser.\n *\n * Full-width rows at every breakpoint. The previous three-across grid gave\n * each tile a third of the card, so provider names wrapped onto two lines and\n * the icons and labels never lined up between tiles.\n */\nexport function AuthMethodTile({\n icon,\n label,\n providerType,\n render,\n onClick,\n isLoading,\n disabled,\n}: AuthMethodTileProps) {\n let leading: ReactNode;\n if (providerType && providerType !== 'generic_oauth') {\n leading = PROVIDER_LOGOS[providerType];\n } else if (typeof icon === 'string') {\n leading = <img alt=\"\" className=\"size-tinyrack-xl\" src={icon} />;\n } else if (icon) {\n leading = icon;\n } else {\n leading = <LinkIcon aria-hidden className=\"size-tinyrack-xl\" />;\n }\n\n const content = (\n <>\n <span className=\"flex size-tinyrack-xl shrink-0 items-center justify-center\">\n {leading}\n </span>\n <TRText truncate variant=\"body\">\n {label}\n </TRText>\n </>\n );\n\n const className =\n 'w-full justify-start gap-tinyrack-md transition-colors duration-tinyrack-fast ease-tinyrack-standard';\n\n if (render) {\n return (\n <TRLinkButton\n appearance=\"outline\"\n className={`cursor-pointer ${className}`}\n intent=\"neutral\"\n render={render}\n uiSize=\"lg\"\n >\n {content}\n </TRLinkButton>\n );\n }\n\n return (\n <TRButton\n appearance=\"outline\"\n className={className}\n disabled={disabled}\n intent=\"neutral\"\n loading={isLoading}\n onClick={onClick}\n type=\"button\"\n uiSize=\"lg\"\n >\n {content}\n </TRButton>\n );\n}\n","import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, redirect, useRouter } from '@tanstack/react-router';\nimport { CircleAlertIcon, FingerprintIcon, MailIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthMethodTile } from '#frontend/components/auth/auth-method-tile.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { AuthorizationContextBanner } from '#frontend/components/auth/authorization-context-banner.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n hasAuthorizationContext,\n isOAuthFlow,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport { classifyPasskeyError } from '#frontend/libs/passkey-error.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getAuthorizationContextQueryOptions } from '#frontend/queries/authorization-context.ts';\nimport {\n type AppConfigs,\n appConfigQueryOptions,\n} from '#frontend/queries/config.ts';\nimport { authenticateWithPasskeyMutationOptions } from '#frontend/queries/passkey.ts';\nimport {\n type AuthResponse,\n getSessionQueryOptions,\n} from '#frontend/queries/session.ts';\n\nconst SearchSchema = OAuthSearchSchema.extend({\n oauth_error: z.string().optional(),\n oauth_error_description: z.string().optional(),\n});\n\nconst OAUTH_ERROR_I18N_MAP: Record<string, string> = {\n access_denied: 'oauth.error.accessDenied',\n temporarily_unavailable: 'oauth.error.temporarilyUnavailable',\n server_error: 'oauth.error.serverError',\n registration_email_not_allowed: 'oauth.error.registrationEmailNotAllowed',\n};\n\nexport const Route = createFileRoute('/login/')({\n component: Login,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n loaderDeps: ({ search }) => ({\n search,\n }),\n loader: async ({ context, deps }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n\n if (shouldRedirectToPassword(config, deps.search)) {\n throw redirect({\n to: '/login/password',\n search: extractOAuthParams(deps.search),\n replace: true,\n });\n }\n\n if (hasAuthorizationContext(deps.search)) {\n await context.queryClient.ensureQueryData(\n getAuthorizationContextQueryOptions(deps.search),\n );\n }\n },\n});\n\nfunction shouldRedirectToPassword(\n config: AppConfigs,\n search: z.infer<typeof SearchSchema>,\n): boolean {\n if (search.oauth_error) {\n return false;\n }\n\n return (\n config.auth.password.enabled &&\n !config.auth.passkey.enabled &&\n config.identity_providers.length === 0\n );\n}\n\nfunction Login() {\n const { t, i18n } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const lang = search.lang ?? i18n.language;\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n\n // Get implicit notice from config for OAuth signup\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n const oauthProviders = configData.identity_providers;\n\n const oauthError = search.oauth_error;\n const oauthErrorMessage = oauthError\n ? t(OAUTH_ERROR_I18N_MAP[oauthError] ?? 'oauth.error.failed')\n : undefined;\n\n const isPasswordAuthEnabled = configData.auth.password.enabled;\n const isPasskeyEnabled = configData.auth.passkey.enabled;\n const [passkeyError, setPasskeyError] = useState<string | null>(null);\n\n const handlePasskeySuccess = async (data: AuthResponse) => {\n if (data.user) {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, data);\n await tick();\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n }\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n };\n\n const passkeyLoginMutation = useMutation({\n ...authenticateWithPasskeyMutationOptions,\n onSuccess: (data) => {\n setPasskeyError(null);\n handlePasskeySuccess(data);\n },\n onError: (error) => {\n const reason = classifyPasskeyError(error);\n if (reason === 'unsupported') {\n setPasskeyError(t('login.passkey.error.unsupported'));\n return;\n }\n\n setPasskeyError(t('login.passkey.error.failed'));\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const buildOAuthUrl = (providerId: string) => {\n let oauthUrl = `/api/oauth/${providerId}/authorize?mode=login`;\n\n if (isOAuthFlow(search)) {\n const authUrl = new URL(buildAuthenticatedAuthorizeUrl(search));\n const returnUrl = `${authUrl.pathname}${authUrl.search}${authUrl.hash}`;\n oauthUrl += `&return_url=${encodeURIComponent(returnUrl)}`;\n }\n\n return oauthUrl;\n };\n\n const buildPasswordLoginHref = () => {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(extractOAuthParams(search))) {\n if (value !== undefined) {\n const stringValue = String(value);\n params.set(\n key,\n key === 'account_selected'\n ? decodeURIComponent(stringValue).replaceAll('\"', '')\n : stringValue,\n );\n }\n }\n const query = params.toString();\n return query ? `/login/password?${query}` : '/login/password';\n };\n\n return (\n <AuthLayout>\n {/*\n Branding title and subtitle are rendered once, by the brand panel in\n the layout. Repeating them here would give the page two copies of the\n product name and two heading nodes with the same text.\n */}\n <AuthPageHeader\n subtitle={t('login.selectMethod.subtitle') || undefined}\n title={t('login.title')}\n />\n\n <AuthorizationContextBanner search={search} />\n\n {oauthErrorMessage && (\n <Alert icon={CircleAlertIcon} type=\"error\">\n {oauthErrorMessage}\n </Alert>\n )}\n\n {passkeyError && (\n <Alert icon={CircleAlertIcon} type=\"error\">\n {passkeyError}\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n {oauthProviders.map((provider) => (\n <AuthMethodTile\n icon={provider.icon_url}\n key={provider.id}\n label={provider.display_name}\n providerType={provider.type}\n // Leaves the SPA for the provider, so a plain anchor rather than a\n // router link. No children here — the tile supplies them, and an\n // element passed to `render` would override them.\n render={<a href={buildOAuthUrl(provider.id)} />}\n />\n ))}\n\n {isPasswordAuthEnabled && (\n <AuthMethodTile\n icon={<MailIcon aria-hidden className=\"size-tinyrack-xl\" />}\n label={t('login.method.password')}\n render={<a href={buildPasswordLoginHref()} />}\n />\n )}\n\n {isPasskeyEnabled && (\n <AuthMethodTile\n disabled={passkeyLoginMutation.isPending}\n icon={<FingerprintIcon aria-hidden className=\"size-tinyrack-xl\" />}\n isLoading={passkeyLoginMutation.isPending}\n label={t('login.method.passkey')}\n onClick={() => {\n setPasskeyError(null);\n passkeyLoginMutation.mutate();\n }}\n />\n )}\n </div>\n\n {implicitNotice && (\n <div\n className=\"prose prose-sm text-center text-tinyrack-text-muted text-tinyrack-xs! **:text-tinyrack-xs!\"\n dangerouslySetInnerHTML={{ __html: implicitNotice }}\n />\n )}\n </AuthLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login-
|
|
1
|
+
{"version":3,"file":"login-huNZDYU9.js","names":["RouteErrorFallback","SplitErrorComponent","errorComponent"],"sources":["../../../frontend/src/routes/login/index.tsx?tsr-split=errorComponent"],"sourcesContent":["import {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, redirect, useRouter } from '@tanstack/react-router';\nimport { CircleAlertIcon, FingerprintIcon, MailIcon } from 'lucide-react';\nimport { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthMethodTile } from '#frontend/components/auth/auth-method-tile.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { AuthorizationContextBanner } from '#frontend/components/auth/authorization-context-banner.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n hasAuthorizationContext,\n isOAuthFlow,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport { classifyPasskeyError } from '#frontend/libs/passkey-error.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { getAuthorizationContextQueryOptions } from '#frontend/queries/authorization-context.ts';\nimport {\n type AppConfigs,\n appConfigQueryOptions,\n} from '#frontend/queries/config.ts';\nimport { authenticateWithPasskeyMutationOptions } from '#frontend/queries/passkey.ts';\nimport {\n type AuthResponse,\n getSessionQueryOptions,\n} from '#frontend/queries/session.ts';\n\nconst SearchSchema = OAuthSearchSchema.extend({\n oauth_error: z.string().optional(),\n oauth_error_description: z.string().optional(),\n});\n\nconst OAUTH_ERROR_I18N_MAP: Record<string, string> = {\n access_denied: 'oauth.error.accessDenied',\n temporarily_unavailable: 'oauth.error.temporarilyUnavailable',\n server_error: 'oauth.error.serverError',\n registration_email_not_allowed: 'oauth.error.registrationEmailNotAllowed',\n};\n\nexport const Route = createFileRoute('/login/')({\n component: Login,\n errorComponent: RouteErrorFallback,\n validateSearch: SearchSchema,\n loaderDeps: ({ search }) => ({\n search,\n }),\n loader: async ({ context, deps }) => {\n const config = await context.queryClient.ensureQueryData(\n appConfigQueryOptions,\n );\n\n if (shouldRedirectToPassword(config, deps.search)) {\n throw redirect({\n to: '/login/password',\n search: extractOAuthParams(deps.search),\n replace: true,\n });\n }\n\n if (hasAuthorizationContext(deps.search)) {\n await context.queryClient.ensureQueryData(\n getAuthorizationContextQueryOptions(deps.search),\n );\n }\n },\n});\n\nfunction shouldRedirectToPassword(\n config: AppConfigs,\n search: z.infer<typeof SearchSchema>,\n): boolean {\n if (search.oauth_error) {\n return false;\n }\n\n return (\n config.auth.password.enabled &&\n !config.auth.passkey.enabled &&\n config.identity_providers.length === 0\n );\n}\n\nfunction Login() {\n const { t, i18n } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n\n const lang = search.lang ?? i18n.language;\n const { data: configData } = useSuspenseQuery(appConfigQueryOptions);\n\n // Get implicit notice from config for OAuth signup\n const implicitNotice =\n configData.registration.signup_notice?.[lang] ??\n configData.registration.signup_notice?.[configData.i18n.fallback_language];\n const oauthProviders = configData.identity_providers;\n\n const oauthError = search.oauth_error;\n const oauthErrorMessage = oauthError\n ? t(OAUTH_ERROR_I18N_MAP[oauthError] ?? 'oauth.error.failed')\n : undefined;\n\n const isPasswordAuthEnabled = configData.auth.password.enabled;\n const isPasskeyEnabled = configData.auth.passkey.enabled;\n const [passkeyError, setPasskeyError] = useState<string | null>(null);\n\n const handlePasskeySuccess = async (data: AuthResponse) => {\n if (data.user) {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, data);\n await tick();\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n }\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n };\n\n const passkeyLoginMutation = useMutation({\n ...authenticateWithPasskeyMutationOptions,\n onSuccess: (data) => {\n setPasskeyError(null);\n handlePasskeySuccess(data);\n },\n onError: (error) => {\n const reason = classifyPasskeyError(error);\n if (reason === 'unsupported') {\n setPasskeyError(t('login.passkey.error.unsupported'));\n return;\n }\n\n setPasskeyError(t('login.passkey.error.failed'));\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n const buildOAuthUrl = (providerId: string) => {\n let oauthUrl = `/api/oauth/${providerId}/authorize?mode=login`;\n\n if (isOAuthFlow(search)) {\n const authUrl = new URL(buildAuthenticatedAuthorizeUrl(search));\n const returnUrl = `${authUrl.pathname}${authUrl.search}${authUrl.hash}`;\n oauthUrl += `&return_url=${encodeURIComponent(returnUrl)}`;\n }\n\n return oauthUrl;\n };\n\n const buildPasswordLoginHref = () => {\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(extractOAuthParams(search))) {\n if (value !== undefined) {\n const stringValue = String(value);\n params.set(\n key,\n key === 'account_selected'\n ? decodeURIComponent(stringValue).replaceAll('\"', '')\n : stringValue,\n );\n }\n }\n const query = params.toString();\n return query ? `/login/password?${query}` : '/login/password';\n };\n\n return (\n <AuthLayout>\n {/*\n Branding title and subtitle are rendered once, by the brand panel in\n the layout. Repeating them here would give the page two copies of the\n product name and two heading nodes with the same text.\n */}\n <AuthPageHeader\n subtitle={t('login.selectMethod.subtitle') || undefined}\n title={t('login.title')}\n />\n\n <AuthorizationContextBanner search={search} />\n\n {oauthErrorMessage && (\n <Alert icon={CircleAlertIcon} type=\"error\">\n {oauthErrorMessage}\n </Alert>\n )}\n\n {passkeyError && (\n <Alert icon={CircleAlertIcon} type=\"error\">\n {passkeyError}\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n {oauthProviders.map((provider) => (\n <AuthMethodTile\n icon={provider.icon_url}\n key={provider.id}\n label={provider.display_name}\n providerType={provider.type}\n // Leaves the SPA for the provider, so a plain anchor rather than a\n // router link. No children here — the tile supplies them, and an\n // element passed to `render` would override them.\n render={<a href={buildOAuthUrl(provider.id)} />}\n />\n ))}\n\n {isPasswordAuthEnabled && (\n <AuthMethodTile\n icon={<MailIcon aria-hidden className=\"size-tinyrack-xl\" />}\n label={t('login.method.password')}\n render={<a href={buildPasswordLoginHref()} />}\n />\n )}\n\n {isPasskeyEnabled && (\n <AuthMethodTile\n disabled={passkeyLoginMutation.isPending}\n icon={<FingerprintIcon aria-hidden className=\"size-tinyrack-xl\" />}\n isLoading={passkeyLoginMutation.isPending}\n label={t('login.method.passkey')}\n onClick={() => {\n setPasskeyError(null);\n passkeyLoginMutation.mutate();\n }}\n />\n )}\n </div>\n\n {implicitNotice && (\n <div\n className=\"prose prose-sm text-center text-tinyrack-text-muted text-tinyrack-xs! **:text-tinyrack-xs!\"\n dangerouslySetInnerHTML={{ __html: implicitNotice }}\n />\n )}\n </AuthLayout>\n );\n}\n"],"mappings":"wCAcsF,IAAAC,EAA7ED"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{G as e,J as t,Z as n,c as r,q as i}from"./createLucideIcon-BPpAIXR_.js";import{_ as a,bn as o,pt as s}from"./config-DTQCUlfZ.js";import{B as c,H as l,P as u,tt as d,u as f}from"./shell-header-bar-
|
|
2
|
-
//# sourceMappingURL=modal-
|
|
1
|
+
import{G as e,J as t,Z as n,c as r,q as i}from"./createLucideIcon-BPpAIXR_.js";import{_ as a,bn as o,pt as s}from"./config-DTQCUlfZ.js";import{B as c,H as l,P as u,tt as d,u as f}from"./shell-header-bar-JcFhS7mb.js";import{a as p,c as m,d as h,f as g,i as _,l as v,n as y,o as b,r as x,s as S,u as C}from"./DialogHandle-DICBMcjK.js";var w=n(t(),1),T=w.forwardRef(function(e,t){let{render:n,className:i,style:o,forceRender:s=!1,...c}=e,l=g(),u=l.useState(`open`),d=l.useState(`nested`),f=l.useState(`mounted`);return r(`div`,e,{state:{open:u,transitionStatus:l.useState(`transitionStatus`)},ref:[l.context.backdropRef,t],stateAttributesMapping:a,props:[{role:`presentation`,hidden:!f,style:{userSelect:`none`,WebkitUserSelect:`none`}},c],enabled:s||!d})}),E=i(),D=w.forwardRef(function(e,t){let{render:n,className:i,style:a,finalFocus:o,initialFocus:u,...p}=e,m=g(),h=m.useState(`descriptionElementId`),y=m.useState(`disablePointerDismissal`),b=m.useState(`floatingRootContext`),x=m.useState(`popupProps`),S=m.useState(`modal`),C=m.useState(`mounted`),w=m.useState(`nested`),T=m.useState(`nestedOpenDialogCount`),D=m.useState(`open`),O=m.useState(`openMethod`),k=m.useState(`titleElementId`),A=m.useState(`transitionStatus`),j=m.useState(`role`),M=b.useState(`floatingId`);v(),s({open:D,ref:m.context.popupRef,onComplete(){D&&m.context.onOpenChangeComplete?.(!0)}});let N=u===void 0?l(m.context.popupRef):u,P=T>0,F=m.useStateSetter(`popupElement`),I=r(`div`,e,{state:{open:D,nested:w,transitionStatus:A,nestedDialogOpen:P},props:[x,{id:M,"aria-labelledby":k,"aria-describedby":h,role:j,...c,hidden:!C,onKeyDown(e){f.has(e.key)&&e.stopPropagation()},style:{"--nested-dialogs":T}},p],ref:[t,m.context.popupRef,F],stateAttributesMapping:_});return(0,E.jsx)(d,{context:b,openInteractionType:O,disabled:!C,closeOnFocusOut:!y,initialFocus:N,returnFocus:o,modal:S!==!1,restoreFocus:`popup`,children:I})});function O(e){return S(`dialog`,e)}var k=o(T,`tr-layer-backdrop tr-dialog-backdrop`),A=o(h,`tr-dialog-close`),j=o(C,`tr-dialog-description`);function M(){return y()}function N({className:t,placement:n=`middle`,...r}){return(0,E.jsx)(D,{...r,className:e(`tr-dialog tr-dialog-box`,t),"data-placement":n})}var P=o(m);function F(e){return(0,E.jsx)(O,{...e})}var I=o(b,`tr-dialog-title`),L=o(p,`tr-dialog-trigger`),R={Backdrop:k,Close:A,Description:j,Popup:N,Portal:P,Root:F,Viewport:o(x,`tr-layer-viewport tr-dialog-viewport`),Title:I,Trigger:L,createHandle:M},z={sm:`[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-sm)]`,md:`[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-md)]`,lg:`[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-md)]`},B={default:`bg-tinyrack-surface-muted text-tinyrack-text`,destructive:`bg-tinyrack-danger-surface text-tinyrack-danger-foreground`};function V({isOpen:e,onClose:t,title:n,description:r,children:i,size:a=`md`,preventClose:o=!1,icon:s,variant:c=`default`}){return(0,w.useEffect)(()=>{if(!e||o)return;let n=e=>{e.key===`Escape`&&t()};return document.addEventListener(`keydown`,n),()=>document.removeEventListener(`keydown`,n)},[e,o,t]),e?(0,E.jsx)(R.Root,{defaultOpen:!0,disablePointerDismissal:o,onOpenChange:e=>{!e&&!o&&t()},children:(0,E.jsx)(R.Portal,{children:(0,E.jsxs)(R.Viewport,{children:[(0,E.jsx)(R.Backdrop,{}),(0,E.jsxs)(R.Popup,{className:z[a],children:[(0,E.jsxs)(`div`,{className:`flex items-start gap-tinyrack-md`,children:[s&&(0,E.jsx)(`div`,{className:`flex size-tinyrack-2xl shrink-0 items-center justify-center rounded-tinyrack-full ${B[c]}`,children:(0,E.jsx)(s,{"aria-hidden":!0,className:`size-tinyrack-lg`})}),(0,E.jsxs)(`div`,{className:`flex min-w-0 flex-1 flex-col gap-tinyrack-3xs`,children:[(0,E.jsx)(R.Title,{children:n}),r&&(0,E.jsx)(R.Description,{className:`text-tinyrack-text-muted text-tinyrack-xs`,children:r})]}),!o&&(0,E.jsx)(R.Close,{"aria-label":`Close`,className:`shrink-0`,"data-testid":`modal-close`,children:(0,E.jsx)(u,{"aria-hidden":!0,className:`size-tinyrack-lg`})})]}),i]})]})})}):null}function H({children:e}){return(0,E.jsx)(`div`,{className:`flex justify-end gap-tinyrack-sm pt-tinyrack-lg`,children:e})}export{H as n,V as t};
|
|
2
|
+
//# sourceMappingURL=modal-tByFpy18.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"modal-CwoBUXp3.js","names":["Dialog.Backdrop","Dialog.Close","Dialog.Description","Dialog.createHandle","Dialog.Popup","Dialog.Portal","Dialog.Root","Dialog.Title","Dialog.Trigger","Dialog.Viewport"],"sources":["../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/dialog/backdrop/DialogBackdrop.mjs","../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/dialog/popup/DialogPopup.mjs","../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/dialog/root/DialogRoot.mjs","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-backdrop.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-close.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-description.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-handle.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-popup.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-portal.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-root.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-title.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-trigger.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-viewport.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.27.1_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/index.js","../../../frontend/src/components/ui/modal.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useDialogRootContext } from \"../root/DialogRootContext.mjs\";\nimport { useRenderElement } from \"../../internals/useRenderElement.mjs\";\nimport { popupTransitionStateMapping } from \"../../utils/popupStateMapping.mjs\";\n\n/**\n * An overlay displayed beneath the popup.\n * Renders a `<div>` element.\n *\n * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)\n */\nexport const DialogBackdrop = /*#__PURE__*/React.forwardRef(function DialogBackdrop(componentProps, forwardedRef) {\n const {\n render,\n className,\n style,\n forceRender = false,\n ...elementProps\n } = componentProps;\n const store = useDialogRootContext();\n const open = store.useState('open');\n const nested = store.useState('nested');\n const mounted = store.useState('mounted');\n const transitionStatus = store.useState('transitionStatus');\n const state = {\n open,\n transitionStatus\n };\n return useRenderElement('div', componentProps, {\n state,\n ref: [store.context.backdropRef, forwardedRef],\n stateAttributesMapping: popupTransitionStateMapping,\n props: [{\n role: 'presentation',\n hidden: !mounted,\n style: {\n userSelect: 'none',\n WebkitUserSelect: 'none'\n }\n }, elementProps],\n enabled: forceRender || !nested\n });\n});\nif (process.env.NODE_ENV !== \"production\") DialogBackdrop.displayName = \"DialogBackdrop\";","'use client';\n\nimport * as React from 'react';\nimport { FloatingFocusManager } from \"../../floating-ui-react/index.mjs\";\nimport { useDialogRootContext } from \"../root/DialogRootContext.mjs\";\nimport { useRenderElement } from \"../../internals/useRenderElement.mjs\";\nimport { useDialogPortalContext } from \"../portal/DialogPortalContext.mjs\";\nimport { useOpenChangeComplete } from \"../../internals/useOpenChangeComplete.mjs\";\nimport { COMPOSITE_KEYS } from \"../../internals/composite/composite.mjs\";\nimport { FOCUSABLE_POPUP_PROPS, createDefaultInitialFocus } from \"../../utils/popups/index.mjs\";\nimport { dialogStateAttributesMapping } from \"../utils/stateAttributesMapping.mjs\";\n\n/**\n * A container for the dialog contents.\n * Renders a `<div>` element.\n *\n * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)\n */\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport const DialogPopup = /*#__PURE__*/React.forwardRef(function DialogPopup(componentProps, forwardedRef) {\n const {\n render,\n className,\n style,\n finalFocus,\n initialFocus,\n ...elementProps\n } = componentProps;\n const store = useDialogRootContext();\n const descriptionElementId = store.useState('descriptionElementId');\n const disablePointerDismissal = store.useState('disablePointerDismissal');\n const floatingRootContext = store.useState('floatingRootContext');\n const rootPopupProps = store.useState('popupProps');\n const modal = store.useState('modal');\n const mounted = store.useState('mounted');\n const nested = store.useState('nested');\n const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');\n const open = store.useState('open');\n const openMethod = store.useState('openMethod');\n const titleElementId = store.useState('titleElementId');\n const transitionStatus = store.useState('transitionStatus');\n const role = store.useState('role');\n const floatingId = floatingRootContext.useState('floatingId');\n useDialogPortalContext();\n useOpenChangeComplete({\n open,\n ref: store.context.popupRef,\n onComplete() {\n if (open) {\n store.context.onOpenChangeComplete?.(true);\n }\n }\n });\n const resolvedInitialFocus = initialFocus === undefined ? createDefaultInitialFocus(store.context.popupRef) : initialFocus;\n const nestedDialogOpen = nestedOpenDialogCount > 0;\n const setPopupElement = store.useStateSetter('popupElement');\n const state = {\n open,\n nested,\n transitionStatus,\n nestedDialogOpen\n };\n const element = useRenderElement('div', componentProps, {\n state,\n props: [rootPopupProps, {\n id: floatingId,\n 'aria-labelledby': titleElementId,\n 'aria-describedby': descriptionElementId,\n role,\n ...FOCUSABLE_POPUP_PROPS,\n hidden: !mounted,\n onKeyDown(event) {\n if (COMPOSITE_KEYS.has(event.key)) {\n event.stopPropagation();\n }\n },\n style: {\n '--nested-dialogs': nestedOpenDialogCount\n }\n }, elementProps],\n ref: [forwardedRef, store.context.popupRef, setPopupElement],\n stateAttributesMapping: dialogStateAttributesMapping\n });\n return /*#__PURE__*/_jsx(FloatingFocusManager, {\n context: floatingRootContext,\n openInteractionType: openMethod,\n disabled: !mounted,\n closeOnFocusOut: !disablePointerDismissal,\n initialFocus: resolvedInitialFocus,\n returnFocus: finalFocus,\n modal: modal !== false,\n restoreFocus: \"popup\",\n children: element\n });\n});\nif (process.env.NODE_ENV !== \"production\") DialogPopup.displayName = \"DialogPopup\";","'use client';\n\nimport { useRenderDialogRoot } from \"./useRenderDialogRoot.mjs\";\n\n/**\n * Groups all parts of the dialog.\n * Doesn't render its own HTML element.\n *\n * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)\n */\nexport function DialogRoot(props) {\n return useRenderDialogRoot('dialog', props);\n}","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-backdrop.tsx\nconst TRDialogBackdrop = createComponentPart(Dialog.Backdrop, \"tr-layer-backdrop tr-dialog-backdrop\");\n//#endregion\nexport { TRDialogBackdrop };\n\n//# sourceMappingURL=dialog-backdrop.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-close.tsx\nconst TRDialogClose = createComponentPart(Dialog.Close, \"tr-dialog-close\");\n//#endregion\nexport { TRDialogClose };\n\n//# sourceMappingURL=dialog-close.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-description.tsx\nconst TRDialogDescription = createComponentPart(Dialog.Description, \"tr-dialog-description\");\n//#endregion\nexport { TRDialogDescription };\n\n//# sourceMappingURL=dialog-description.js.map","\"use client\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-handle.ts\nfunction createDialogHandle() {\n\treturn Dialog.createHandle();\n}\n//#endregion\nexport { createDialogHandle };\n\n//# sourceMappingURL=dialog-handle.js.map","\"use client\";\nimport { mergeComponentClassName } from \"../../internal/component-class-name.js\";\nimport { jsx } from \"react/jsx-runtime\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-popup.tsx\nfunction TRDialogPopup({ className, placement = \"middle\", ...props }) {\n\treturn /* @__PURE__ */ jsx(Dialog.Popup, {\n\t\t...props,\n\t\tclassName: mergeComponentClassName(\"tr-dialog tr-dialog-box\", className),\n\t\t\"data-placement\": placement\n\t});\n}\n//#endregion\nexport { TRDialogPopup };\n\n//# sourceMappingURL=dialog-popup.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-portal.tsx\nconst TRDialogPortal = createComponentPart(Dialog.Portal);\n//#endregion\nexport { TRDialogPortal };\n\n//# sourceMappingURL=dialog-portal.js.map","\"use client\";\nimport { jsx } from \"react/jsx-runtime\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-root.tsx\nfunction TRDialogRoot(props) {\n\treturn /* @__PURE__ */ jsx(Dialog.Root, { ...props });\n}\n//#endregion\nexport { TRDialogRoot };\n\n//# sourceMappingURL=dialog-root.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-title.tsx\nconst TRDialogTitle = createComponentPart(Dialog.Title, \"tr-dialog-title\");\n//#endregion\nexport { TRDialogTitle };\n\n//# sourceMappingURL=dialog-title.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-trigger.tsx\nconst TRDialogTrigger = createComponentPart(Dialog.Trigger, \"tr-dialog-trigger\");\n//#endregion\nexport { TRDialogTrigger };\n\n//# sourceMappingURL=dialog-trigger.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-viewport.tsx\nconst TRDialogViewport = createComponentPart(Dialog.Viewport, \"tr-layer-viewport tr-dialog-viewport\");\n//#endregion\nexport { TRDialogViewport };\n\n//# sourceMappingURL=dialog-viewport.js.map","import { TRDialogBackdrop } from \"./dialog-backdrop.js\";\nimport { TRDialogClose } from \"./dialog-close.js\";\nimport { TRDialogDescription } from \"./dialog-description.js\";\nimport { createDialogHandle } from \"./dialog-handle.js\";\nimport { TRDialogPopup } from \"./dialog-popup.js\";\nimport { TRDialogPortal } from \"./dialog-portal.js\";\nimport { TRDialogRoot } from \"./dialog-root.js\";\nimport { TRDialogTitle } from \"./dialog-title.js\";\nimport { TRDialogTrigger } from \"./dialog-trigger.js\";\nimport { TRDialogViewport } from \"./dialog-viewport.js\";\n//#region src/components/dialog/index.tsx\nconst TRDialog = {\n\tBackdrop: TRDialogBackdrop,\n\tClose: TRDialogClose,\n\tDescription: TRDialogDescription,\n\tPopup: TRDialogPopup,\n\tPortal: TRDialogPortal,\n\tRoot: TRDialogRoot,\n\tViewport: TRDialogViewport,\n\tTitle: TRDialogTitle,\n\tTrigger: TRDialogTrigger,\n\tcreateHandle: createDialogHandle\n};\n//#endregion\nexport { TRDialog, TRDialogBackdrop, TRDialogClose, TRDialogDescription, TRDialogPopup, TRDialogPortal, TRDialogRoot, TRDialogTitle, TRDialogTrigger, TRDialogViewport, createDialogHandle };\n\n//# sourceMappingURL=index.js.map","import { TRDialog } from '@tinyrack/ui/components/dialog';\nimport type { LucideIcon } from 'lucide-react';\nimport { XIcon } from 'lucide-react';\nimport { type ReactNode, useEffect } from 'react';\n\ntype ModalVariant = 'default' | 'destructive';\n\ninterface ModalProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n description?: string;\n children: ReactNode;\n size?: 'sm' | 'md' | 'lg';\n preventClose?: boolean;\n icon?: LucideIcon;\n variant?: ModalVariant;\n}\n\n/*\n Set through `--tr-dialog-box-max-width`, the override hook `.tr-dialog-box`\n exposes, rather than a `max-w-*` utility. The box feeds that same custom\n property into its own `width: min(…)` calculation, so a utility would move\n the max-width while leaving the width computation on the old value.\n\n This is the current design system overlay scale (20/32rem), not Tailwind's\n width scale. The local `lg` compatibility option now resolves to the largest\n supported overlay token, and `md` remains the unstyled `TRDialog` default.\n*/\nconst sizeClasses = {\n sm: '[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-sm)]',\n md: '[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-md)]',\n lg: '[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-md)]',\n} as const;\n\nconst iconVariantClasses: Record<ModalVariant, string> = {\n default: 'bg-tinyrack-surface-muted text-tinyrack-text',\n destructive: 'bg-tinyrack-danger-surface text-tinyrack-danger-foreground',\n};\n\nexport function Modal({\n isOpen,\n onClose,\n title,\n description,\n children,\n size = 'md',\n preventClose = false,\n icon: IconComponent,\n variant = 'default',\n}: ModalProps) {\n useEffect(() => {\n if (!isOpen) return;\n if (preventClose) return;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, preventClose, onClose]);\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <TRDialog.Root\n defaultOpen\n disablePointerDismissal={preventClose}\n onOpenChange={(open) => {\n if (!open && !preventClose) {\n onClose();\n }\n }}\n >\n <TRDialog.Portal>\n <TRDialog.Viewport>\n <TRDialog.Backdrop />\n {/*\n No max-height here: `.tr-dialog-box` already clamps itself against\n `100dvh` minus the viewport gap, which is more correct than the\n `90vh`/`85vh` this used to override it with.\n */}\n <TRDialog.Popup className={sizeClasses[size]}>\n <div className=\"flex items-start gap-tinyrack-md\">\n {IconComponent && (\n <div\n className={`flex size-tinyrack-2xl shrink-0 items-center justify-center rounded-tinyrack-full ${iconVariantClasses[variant]}`}\n >\n <IconComponent aria-hidden className=\"size-tinyrack-lg\" />\n </div>\n )}\n <div className=\"flex min-w-0 flex-1 flex-col gap-tinyrack-3xs\">\n <TRDialog.Title>{title}</TRDialog.Title>\n {description && (\n <TRDialog.Description className=\"text-tinyrack-text-muted text-tinyrack-xs\">\n {description}\n </TRDialog.Description>\n )}\n </div>\n {!preventClose && (\n <TRDialog.Close\n aria-label=\"Close\"\n className=\"shrink-0\"\n data-testid=\"modal-close\"\n >\n <XIcon aria-hidden className=\"size-tinyrack-lg\" />\n </TRDialog.Close>\n )}\n </div>\n {children}\n </TRDialog.Popup>\n </TRDialog.Viewport>\n </TRDialog.Portal>\n </TRDialog.Root>\n );\n}\n\ninterface ModalActionsProps {\n children: ReactNode;\n}\n\nexport function ModalActions({ children }: ModalActionsProps) {\n return (\n <div className=\"flex justify-end gap-tinyrack-sm pt-tinyrack-lg\">\n {children}\n </div>\n );\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"modal-tByFpy18.js","names":["Dialog.Backdrop","Dialog.Close","Dialog.Description","Dialog.createHandle","Dialog.Popup","Dialog.Portal","Dialog.Root","Dialog.Title","Dialog.Trigger","Dialog.Viewport"],"sources":["../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/dialog/backdrop/DialogBackdrop.mjs","../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/dialog/popup/DialogPopup.mjs","../../../../node_modules/.pnpm/@base-ui+react@1.7.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7/node_modules/@base-ui/react/dialog/root/DialogRoot.mjs","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-backdrop.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-close.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-description.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-handle.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-popup.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-portal.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-root.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-title.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-trigger.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/dialog-viewport.js","../../../../node_modules/.pnpm/@tinyrack+ui@0.29.0_@types+react@19.2.17_react-dom@19.2.7_react@19.2.7__react@19.2.7_shiki@4.4.2_tailwindcss@4.3.2/node_modules/@tinyrack/ui/dist/components/dialog/index.js","../../../frontend/src/components/ui/modal.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { useDialogRootContext } from \"../root/DialogRootContext.mjs\";\nimport { useRenderElement } from \"../../internals/useRenderElement.mjs\";\nimport { popupTransitionStateMapping } from \"../../utils/popupStateMapping.mjs\";\n\n/**\n * An overlay displayed beneath the popup.\n * Renders a `<div>` element.\n *\n * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)\n */\nexport const DialogBackdrop = /*#__PURE__*/React.forwardRef(function DialogBackdrop(componentProps, forwardedRef) {\n const {\n render,\n className,\n style,\n forceRender = false,\n ...elementProps\n } = componentProps;\n const store = useDialogRootContext();\n const open = store.useState('open');\n const nested = store.useState('nested');\n const mounted = store.useState('mounted');\n const transitionStatus = store.useState('transitionStatus');\n const state = {\n open,\n transitionStatus\n };\n return useRenderElement('div', componentProps, {\n state,\n ref: [store.context.backdropRef, forwardedRef],\n stateAttributesMapping: popupTransitionStateMapping,\n props: [{\n role: 'presentation',\n hidden: !mounted,\n style: {\n userSelect: 'none',\n WebkitUserSelect: 'none'\n }\n }, elementProps],\n enabled: forceRender || !nested\n });\n});\nif (process.env.NODE_ENV !== \"production\") DialogBackdrop.displayName = \"DialogBackdrop\";","'use client';\n\nimport * as React from 'react';\nimport { FloatingFocusManager } from \"../../floating-ui-react/index.mjs\";\nimport { useDialogRootContext } from \"../root/DialogRootContext.mjs\";\nimport { useRenderElement } from \"../../internals/useRenderElement.mjs\";\nimport { useDialogPortalContext } from \"../portal/DialogPortalContext.mjs\";\nimport { useOpenChangeComplete } from \"../../internals/useOpenChangeComplete.mjs\";\nimport { COMPOSITE_KEYS } from \"../../internals/composite/composite.mjs\";\nimport { FOCUSABLE_POPUP_PROPS, createDefaultInitialFocus } from \"../../utils/popups/index.mjs\";\nimport { dialogStateAttributesMapping } from \"../utils/stateAttributesMapping.mjs\";\n\n/**\n * A container for the dialog contents.\n * Renders a `<div>` element.\n *\n * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)\n */\nimport { jsx as _jsx } from \"react/jsx-runtime\";\nexport const DialogPopup = /*#__PURE__*/React.forwardRef(function DialogPopup(componentProps, forwardedRef) {\n const {\n render,\n className,\n style,\n finalFocus,\n initialFocus,\n ...elementProps\n } = componentProps;\n const store = useDialogRootContext();\n const descriptionElementId = store.useState('descriptionElementId');\n const disablePointerDismissal = store.useState('disablePointerDismissal');\n const floatingRootContext = store.useState('floatingRootContext');\n const rootPopupProps = store.useState('popupProps');\n const modal = store.useState('modal');\n const mounted = store.useState('mounted');\n const nested = store.useState('nested');\n const nestedOpenDialogCount = store.useState('nestedOpenDialogCount');\n const open = store.useState('open');\n const openMethod = store.useState('openMethod');\n const titleElementId = store.useState('titleElementId');\n const transitionStatus = store.useState('transitionStatus');\n const role = store.useState('role');\n const floatingId = floatingRootContext.useState('floatingId');\n useDialogPortalContext();\n useOpenChangeComplete({\n open,\n ref: store.context.popupRef,\n onComplete() {\n if (open) {\n store.context.onOpenChangeComplete?.(true);\n }\n }\n });\n const resolvedInitialFocus = initialFocus === undefined ? createDefaultInitialFocus(store.context.popupRef) : initialFocus;\n const nestedDialogOpen = nestedOpenDialogCount > 0;\n const setPopupElement = store.useStateSetter('popupElement');\n const state = {\n open,\n nested,\n transitionStatus,\n nestedDialogOpen\n };\n const element = useRenderElement('div', componentProps, {\n state,\n props: [rootPopupProps, {\n id: floatingId,\n 'aria-labelledby': titleElementId,\n 'aria-describedby': descriptionElementId,\n role,\n ...FOCUSABLE_POPUP_PROPS,\n hidden: !mounted,\n onKeyDown(event) {\n if (COMPOSITE_KEYS.has(event.key)) {\n event.stopPropagation();\n }\n },\n style: {\n '--nested-dialogs': nestedOpenDialogCount\n }\n }, elementProps],\n ref: [forwardedRef, store.context.popupRef, setPopupElement],\n stateAttributesMapping: dialogStateAttributesMapping\n });\n return /*#__PURE__*/_jsx(FloatingFocusManager, {\n context: floatingRootContext,\n openInteractionType: openMethod,\n disabled: !mounted,\n closeOnFocusOut: !disablePointerDismissal,\n initialFocus: resolvedInitialFocus,\n returnFocus: finalFocus,\n modal: modal !== false,\n restoreFocus: \"popup\",\n children: element\n });\n});\nif (process.env.NODE_ENV !== \"production\") DialogPopup.displayName = \"DialogPopup\";","'use client';\n\nimport { useRenderDialogRoot } from \"./useRenderDialogRoot.mjs\";\n\n/**\n * Groups all parts of the dialog.\n * Doesn't render its own HTML element.\n *\n * Documentation: [Base UI Dialog](https://base-ui.com/react/components/dialog)\n */\nexport function DialogRoot(props) {\n return useRenderDialogRoot('dialog', props);\n}","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-backdrop.tsx\nconst TRDialogBackdrop = createComponentPart(Dialog.Backdrop, \"tr-layer-backdrop tr-dialog-backdrop\");\n//#endregion\nexport { TRDialogBackdrop };\n\n//# sourceMappingURL=dialog-backdrop.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-close.tsx\nconst TRDialogClose = createComponentPart(Dialog.Close, \"tr-dialog-close\");\n//#endregion\nexport { TRDialogClose };\n\n//# sourceMappingURL=dialog-close.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-description.tsx\nconst TRDialogDescription = createComponentPart(Dialog.Description, \"tr-dialog-description\");\n//#endregion\nexport { TRDialogDescription };\n\n//# sourceMappingURL=dialog-description.js.map","\"use client\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-handle.ts\nfunction createDialogHandle() {\n\treturn Dialog.createHandle();\n}\n//#endregion\nexport { createDialogHandle };\n\n//# sourceMappingURL=dialog-handle.js.map","\"use client\";\nimport { mergeComponentClassName } from \"../../internal/component-class-name.js\";\nimport { jsx } from \"react/jsx-runtime\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-popup.tsx\nfunction TRDialogPopup({ className, placement = \"middle\", ...props }) {\n\treturn /* @__PURE__ */ jsx(Dialog.Popup, {\n\t\t...props,\n\t\tclassName: mergeComponentClassName(\"tr-dialog tr-dialog-box\", className),\n\t\t\"data-placement\": placement\n\t});\n}\n//#endregion\nexport { TRDialogPopup };\n\n//# sourceMappingURL=dialog-popup.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-portal.tsx\nconst TRDialogPortal = createComponentPart(Dialog.Portal);\n//#endregion\nexport { TRDialogPortal };\n\n//# sourceMappingURL=dialog-portal.js.map","\"use client\";\nimport { jsx } from \"react/jsx-runtime\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-root.tsx\nfunction TRDialogRoot(props) {\n\treturn /* @__PURE__ */ jsx(Dialog.Root, { ...props });\n}\n//#endregion\nexport { TRDialogRoot };\n\n//# sourceMappingURL=dialog-root.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-title.tsx\nconst TRDialogTitle = createComponentPart(Dialog.Title, \"tr-dialog-title\");\n//#endregion\nexport { TRDialogTitle };\n\n//# sourceMappingURL=dialog-title.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-trigger.tsx\nconst TRDialogTrigger = createComponentPart(Dialog.Trigger, \"tr-dialog-trigger\");\n//#endregion\nexport { TRDialogTrigger };\n\n//# sourceMappingURL=dialog-trigger.js.map","\"use client\";\nimport { createComponentPart } from \"../../internal/component-part.js\";\nimport { Dialog } from \"@base-ui/react/dialog\";\n//#region src/components/dialog/dialog-viewport.tsx\nconst TRDialogViewport = createComponentPart(Dialog.Viewport, \"tr-layer-viewport tr-dialog-viewport\");\n//#endregion\nexport { TRDialogViewport };\n\n//# sourceMappingURL=dialog-viewport.js.map","import { TRDialogBackdrop } from \"./dialog-backdrop.js\";\nimport { TRDialogClose } from \"./dialog-close.js\";\nimport { TRDialogDescription } from \"./dialog-description.js\";\nimport { createDialogHandle } from \"./dialog-handle.js\";\nimport { TRDialogPopup } from \"./dialog-popup.js\";\nimport { TRDialogPortal } from \"./dialog-portal.js\";\nimport { TRDialogRoot } from \"./dialog-root.js\";\nimport { TRDialogTitle } from \"./dialog-title.js\";\nimport { TRDialogTrigger } from \"./dialog-trigger.js\";\nimport { TRDialogViewport } from \"./dialog-viewport.js\";\n//#region src/components/dialog/index.tsx\nconst TRDialog = {\n\tBackdrop: TRDialogBackdrop,\n\tClose: TRDialogClose,\n\tDescription: TRDialogDescription,\n\tPopup: TRDialogPopup,\n\tPortal: TRDialogPortal,\n\tRoot: TRDialogRoot,\n\tViewport: TRDialogViewport,\n\tTitle: TRDialogTitle,\n\tTrigger: TRDialogTrigger,\n\tcreateHandle: createDialogHandle\n};\n//#endregion\nexport { TRDialog, TRDialogBackdrop, TRDialogClose, TRDialogDescription, TRDialogPopup, TRDialogPortal, TRDialogRoot, TRDialogTitle, TRDialogTrigger, TRDialogViewport, createDialogHandle };\n\n//# sourceMappingURL=index.js.map","import { TRDialog } from '@tinyrack/ui/components/dialog';\nimport type { LucideIcon } from 'lucide-react';\nimport { XIcon } from 'lucide-react';\nimport { type ReactNode, useEffect } from 'react';\n\ntype ModalVariant = 'default' | 'destructive';\n\ninterface ModalProps {\n isOpen: boolean;\n onClose: () => void;\n title: string;\n description?: string;\n children: ReactNode;\n size?: 'sm' | 'md' | 'lg';\n preventClose?: boolean;\n icon?: LucideIcon;\n variant?: ModalVariant;\n}\n\n/*\n Set through `--tr-dialog-box-max-width`, the override hook `.tr-dialog-box`\n exposes, rather than a `max-w-*` utility. The box feeds that same custom\n property into its own `width: min(…)` calculation, so a utility would move\n the max-width while leaving the width computation on the old value.\n\n This is the current design system overlay scale (20/32rem), not Tailwind's\n width scale. The local `lg` compatibility option now resolves to the largest\n supported overlay token, and `md` remains the unstyled `TRDialog` default.\n*/\nconst sizeClasses = {\n sm: '[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-sm)]',\n md: '[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-md)]',\n lg: '[--tr-dialog-box-max-width:var(--tinyrack-overlay-width-md)]',\n} as const;\n\nconst iconVariantClasses: Record<ModalVariant, string> = {\n default: 'bg-tinyrack-surface-muted text-tinyrack-text',\n destructive: 'bg-tinyrack-danger-surface text-tinyrack-danger-foreground',\n};\n\nexport function Modal({\n isOpen,\n onClose,\n title,\n description,\n children,\n size = 'md',\n preventClose = false,\n icon: IconComponent,\n variant = 'default',\n}: ModalProps) {\n useEffect(() => {\n if (!isOpen) return;\n if (preventClose) return;\n const handleKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n onClose();\n }\n };\n document.addEventListener('keydown', handleKeyDown);\n return () => document.removeEventListener('keydown', handleKeyDown);\n }, [isOpen, preventClose, onClose]);\n\n if (!isOpen) {\n return null;\n }\n\n return (\n <TRDialog.Root\n defaultOpen\n disablePointerDismissal={preventClose}\n onOpenChange={(open) => {\n if (!open && !preventClose) {\n onClose();\n }\n }}\n >\n <TRDialog.Portal>\n <TRDialog.Viewport>\n <TRDialog.Backdrop />\n {/*\n No max-height here: `.tr-dialog-box` already clamps itself against\n `100dvh` minus the viewport gap, which is more correct than the\n `90vh`/`85vh` this used to override it with.\n */}\n <TRDialog.Popup className={sizeClasses[size]}>\n <div className=\"flex items-start gap-tinyrack-md\">\n {IconComponent && (\n <div\n className={`flex size-tinyrack-2xl shrink-0 items-center justify-center rounded-tinyrack-full ${iconVariantClasses[variant]}`}\n >\n <IconComponent aria-hidden className=\"size-tinyrack-lg\" />\n </div>\n )}\n <div className=\"flex min-w-0 flex-1 flex-col gap-tinyrack-3xs\">\n <TRDialog.Title>{title}</TRDialog.Title>\n {description && (\n <TRDialog.Description className=\"text-tinyrack-text-muted text-tinyrack-xs\">\n {description}\n </TRDialog.Description>\n )}\n </div>\n {!preventClose && (\n <TRDialog.Close\n aria-label=\"Close\"\n className=\"shrink-0\"\n data-testid=\"modal-close\"\n >\n <XIcon aria-hidden className=\"size-tinyrack-lg\" />\n </TRDialog.Close>\n )}\n </div>\n {children}\n </TRDialog.Popup>\n </TRDialog.Viewport>\n </TRDialog.Portal>\n </TRDialog.Root>\n );\n}\n\ninterface ModalActionsProps {\n children: ReactNode;\n}\n\nexport function ModalActions({ children }: ModalActionsProps) {\n return (\n <div className=\"flex justify-end gap-tinyrack-sm pt-tinyrack-lg\">\n {children}\n </div>\n );\n}\n"],"x_google_ignoreList":[0,1,2,3,4,5,6,7,8,9,10,11,12,13],"mappings":"4VAaa,EAA8B,EAAM,WAAW,SAAwB,EAAgB,EAAc,CAChH,GAAM,CACJ,SACA,YACA,QACA,cAAc,GACd,GAAG,GACD,EACE,EAAQ,EAAqB,EAC7B,EAAO,EAAM,SAAS,MAAM,EAC5B,EAAS,EAAM,SAAS,QAAQ,EAChC,EAAU,EAAM,SAAS,SAAS,EAMxC,OAAO,EAAiB,MAAO,EAAgB,CAC7C,MAAA,CAJA,OACA,iBAHuB,EAAM,SAAS,kBAGvB,CAGX,EACJ,IAAK,CAAC,EAAM,QAAQ,YAAa,CAAY,EAC7C,uBAAwB,EACxB,MAAO,CAAC,CACN,KAAM,eACN,OAAQ,CAAC,EACT,MAAO,CACL,WAAY,OACZ,iBAAkB,MACpB,CACF,EAAG,CAAY,EACf,QAAS,GAAe,CAAC,CAC3B,CAAC,CACH,CAAC,QCzBY,EAA2B,EAAM,WAAW,SAAqB,EAAgB,EAAc,CAC1G,GAAM,CACJ,SACA,YACA,QACA,aACA,eACA,GAAG,GACD,EACE,EAAQ,EAAqB,EAC7B,EAAuB,EAAM,SAAS,sBAAsB,EAC5D,EAA0B,EAAM,SAAS,yBAAyB,EAClE,EAAsB,EAAM,SAAS,qBAAqB,EAC1D,EAAiB,EAAM,SAAS,YAAY,EAC5C,EAAQ,EAAM,SAAS,OAAO,EAC9B,EAAU,EAAM,SAAS,SAAS,EAClC,EAAS,EAAM,SAAS,QAAQ,EAChC,EAAwB,EAAM,SAAS,uBAAuB,EAC9D,EAAO,EAAM,SAAS,MAAM,EAC5B,EAAa,EAAM,SAAS,YAAY,EACxC,EAAiB,EAAM,SAAS,gBAAgB,EAChD,EAAmB,EAAM,SAAS,kBAAkB,EACpD,EAAO,EAAM,SAAS,MAAM,EAC5B,EAAa,EAAoB,SAAS,YAAY,EAC5D,EAAuB,EACvB,EAAsB,CACpB,OACA,IAAK,EAAM,QAAQ,SACnB,YAAa,CACP,GACF,EAAM,QAAQ,uBAAuB,EAAI,CAE7C,CACF,CAAC,EACD,IAAM,EAAuB,IAAiB,IAAA,GAAY,EAA0B,EAAM,QAAQ,QAAQ,EAAI,EACxG,EAAmB,EAAwB,EAC3C,EAAkB,EAAM,eAAe,cAAc,EAOrD,EAAU,EAAiB,MAAO,EAAgB,CACtD,MAAA,CANA,OACA,SACA,mBACA,kBAGI,EACJ,MAAO,CAAC,EAAgB,CACtB,GAAI,EACJ,kBAAmB,EACnB,mBAAoB,EACpB,OACA,GAAG,EACH,OAAQ,CAAC,EACT,UAAU,EAAO,CACX,EAAe,IAAI,EAAM,GAAG,GAC9B,EAAM,gBAAgB,CAE1B,EACA,MAAO,CACL,mBAAoB,CACtB,CACF,EAAG,CAAY,EACf,IAAK,CAAC,EAAc,EAAM,QAAQ,SAAU,CAAe,EAC3D,uBAAwB,CAC1B,CAAC,EACD,OAAoB,EAAA,EAAA,IAAA,CAAK,EAAsB,CAC7C,QAAS,EACT,oBAAqB,EACrB,SAAU,CAAC,EACX,gBAAiB,CAAC,EAClB,aAAc,EACd,YAAa,EACb,MAAO,IAAU,GACjB,aAAc,QACd,SAAU,CACZ,CAAC,CACH,CAAC,ECpFD,SAAgB,EAAW,EAAO,CAChC,OAAO,EAAoB,SAAU,CAAK,CAC5C,CCRA,IAAM,EAAmB,EAAoBA,EAAiB,sCAAsC,ECA9F,EAAgB,EAAoBC,EAAc,iBAAiB,ECAnE,EAAsB,EAAoBC,EAAoB,uBAAuB,ECD3F,SAAS,GAAqB,CAC7B,OAAOC,EAAoB,CAC5B,CCAA,SAAS,EAAc,CAAE,YAAW,YAAY,SAAU,GAAG,GAAS,CACrE,OAAuB,EAAA,EAAA,IAAA,CAAIC,EAAc,CACxC,GAAG,EACH,UAAW,EAAwB,0BAA2B,CAAS,EACvE,iBAAkB,CACnB,CAAC,CACF,CCPA,IAAM,EAAiB,EAAoBC,CAAa,ECAxD,SAAS,EAAa,EAAO,CAC5B,OAAuB,EAAA,EAAA,IAAA,CAAIC,EAAa,CAAE,GAAG,CAAM,CAAC,CACrD,CCFA,IAAM,EAAgB,EAAoBC,EAAc,iBAAiB,ECAnE,EAAkB,EAAoBC,EAAgB,mBAAmB,EEOzE,EAAW,CAChB,SAAU,EACV,MAAO,EACP,YAAa,EACb,MAAO,EACP,OAAQ,EACR,KAAM,EACN,SDdwB,EAAoBC,EAAiB,sCCcnD,EACV,MAAO,EACP,QAAS,EACT,aAAc,CACf,ECOM,EAAc,CAClB,GAAI,+DACJ,GAAI,+DACJ,GAAI,8DACN,EAEM,EAAmD,CACvD,QAAS,+CACT,YAAa,4DACf,EAEA,SAAgB,EAAM,CACpB,SACA,UACA,QACA,cACA,WACA,OAAO,KACP,eAAe,GACf,KAAM,EACN,UAAU,WACG,CAiBb,OAhBA,EAAA,EAAA,UAAA,KAAgB,CAEd,GADI,CAAC,GACD,EAAc,OAClB,IAAM,EAAiB,GAAqB,CACtC,EAAE,MAAQ,UACZ,EAAQ,CAEZ,EAEA,OADA,SAAS,iBAAiB,UAAW,CAAa,MACrC,SAAS,oBAAoB,UAAW,CAAa,CACpE,EAAG,CAAC,EAAQ,EAAc,CAAO,CAAC,EAE7B,GAKH,EAAA,EAAA,IAAA,CAAC,EAAS,KAAV,CACE,YAAA,GACA,wBAAyB,EACzB,aAAe,GAAS,CAClB,CAAC,GAAQ,CAAC,GACZ,EAAQ,CAEZ,YAEA,EAAA,EAAA,IAAA,CAAC,EAAS,OAAV,CAAA,UACE,EAAA,EAAA,KAAA,CAAC,EAAS,SAAV,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,EAAS,SAAV,CAAoB,CAAA,GAMpB,EAAA,EAAA,KAAA,CAAC,EAAS,MAAV,CAAgB,UAAW,EAAY,YAAvC,EACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,4CAAf,CACG,IACC,EAAA,EAAA,IAAA,CAAC,MAAD,CACE,UAAW,qFAAqF,EAAmB,eAEnH,EAAA,EAAA,IAAA,CAAC,EAAD,CAAe,cAAA,GAAY,UAAU,kBAAoB,CAAA,CACtD,CAAA,GAEP,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,yDAAf,EACE,EAAA,EAAA,IAAA,CAAC,EAAS,MAAV,CAAA,SAAiB,CAAsB,CAAA,EACtC,IACC,EAAA,EAAA,IAAA,CAAC,EAAS,YAAV,CAAsB,UAAU,qDAC7B,CACmB,CAAA,CAErB,IACJ,CAAC,IACA,EAAA,EAAA,IAAA,CAAC,EAAS,MAAV,CACE,aAAW,QACX,UAAU,WACV,cAAY,wBAEZ,EAAA,EAAA,IAAA,CAAC,EAAD,CAAO,cAAA,GAAY,UAAU,kBAAoB,CAAA,CACnC,CAAA,CAEf,IACJ,CACa,GACC,CAAA,CAAA,CACJ,CAAA,CACJ,CAAA,EApDR,IAsDX,CAMA,SAAgB,EAAa,CAAE,YAA+B,CAC5D,OACE,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,kDACZ,UACE,CAAA,CAET"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{J as e,Z as t,i as n,n as r,q as i,s as a,t as o}from"./createLucideIcon-BPpAIXR_.js";import{On as s,Sn as c,Sr as l,t as u}from"./config-DTQCUlfZ.js";import{t as d}from"./useMutation-BWZ0G4Td.js";import{nt as f}from"./shell-header-bar-
|
|
2
|
-
//# sourceMappingURL=passkey-
|
|
1
|
+
import{J as e,Z as t,i as n,n as r,q as i,s as a,t as o}from"./createLucideIcon-BPpAIXR_.js";import{On as s,Sn as c,Sr as l,t as u}from"./config-DTQCUlfZ.js";import{t as d}from"./useMutation-BWZ0G4Td.js";import{nt as f}from"./shell-header-bar-JcFhS7mb.js";import{t as p}from"./auth-field-CWyXGKoC.js";import{t as m}from"./fingerprint-pattern-DP62Vk8x.js";import{t as h}from"./shield-check-D_p8H0ni.js";import{c as g,o as _}from"./zod-C87S6Tec.js";import{F as v,N as y,R as b,S as x,j as S,u as C,z as w}from"./index-CKpnrw9M.js";import{t as T}from"./auth-page-header-DgGAaIiP.js";import{t as E}from"./alert-leqyvHOp.js";import{t as D}from"./auth-layout-CiNn2-Nb.js";import{t as O}from"./promise-OpBtq8tG.js";import{n as k,t as A}from"./auth-footer-Cz5ivsDc.js";import{t as j}from"./passkey-error-ZqqPQUta.js";import{i as M}from"./passkey-J1qnYp9A.js";import{r as N,t as P}from"./standard-schema-e8gtSzGE.js";var F=o(`tag`,[[`path`,{d:`M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z`,key:`vktsd0`}],[`circle`,{cx:`7.5`,cy:`7.5`,r:`.5`,fill:`currentColor`,key:`kqv944`}]]),I=t(e()),L=i();function R(){let{t:e}=n(),t=s(),i=l(),o=C.useSearch(),{data:R}=f(u),[z,B]=(0,I.useState)(o.passkey_name?`registering`:`form`),[V,H]=(0,I.useState)(``),U=(0,I.useRef)(!1),W=R.auth.password.enabled&&R.auth.password.two_factor.enrollment_required&&R.auth.password.totp.enabled,{register:G,handleSubmit:K,formState:{errors:q}}=N({defaultValues:{name:``},resolver:P((0,I.useMemo)(()=>_({name:g().max(100,e(`validation.passkey.name.max`))}),[e]))}),J=d({...M,onSuccess:async e=>{e.second_factor_setup_completed&&e.user?(i.setQueryData(x.queryKey,{user:e.user}),await O(),v(o)?window.location.href=S(o):t.navigate({to:`/profile`})):(i.invalidateQueries({queryKey:x.queryKey}),t.navigate({to:`/profile`}))},onError:t=>{switch(B(`error`),j(t)){case`unsupported`:H(e(`setupPasskey.error.unsupported`));return;case`not_allowed`:H(e(`setupPasskey.error.notAllowed`));return;case`expired`:H(e(`setupPasskey.error.expired`));return;case`user_mismatch`:case`verification_failed`:H(e(`setupPasskey.error.failed`));return}},onSettled:()=>{i.invalidateQueries({queryKey:x.queryKey})}});return(0,I.useEffect)(()=>{o.passkey_name&&!U.current&&(U.current=!0,J.mutate({name:o.passkey_name}))},[J,o.passkey_name]),z===`registering`?(0,L.jsxs)(D,{children:[(0,L.jsx)(T,{subtitle:e(`setupPasskey.subtitle`),title:e(`setupPasskey.title`)}),(0,L.jsxs)(`div`,{className:`flex flex-col items-center gap-tinyrack-lg py-tinyrack-xl`,children:[(0,L.jsx)(m,{"aria-hidden":!0,className:`size-tinyrack-3xl animate-pulse text-tinyrack-primary-foreground`}),(0,L.jsx)(r,{align:`center`,as:`p`,color:`muted`,variant:`body`,children:e(`setupPasskey.waiting`)})]})]}):z===`error`?(0,L.jsxs)(D,{children:[(0,L.jsx)(T,{subtitle:e(`setupPasskey.subtitle`),title:e(`setupPasskey.title`)}),(0,L.jsx)(E,{icon:b,type:`error`,children:V}),(0,L.jsxs)(`div`,{className:`flex flex-col gap-tinyrack-sm`,children:[W&&(0,L.jsxs)(w,{className:`w-full gap-tinyrack-sm`,intent:`primary`,render:(0,L.jsx)(c,{search:y(o),to:`/setup/totp`}),uiSize:`lg`,children:[(0,L.jsx)(h,{"aria-hidden":!0,className:`size-tinyrack-xl`}),e(`setupPasskey.useTotp`)]}),(0,L.jsx)(a,{appearance:W?`outline`:`solid`,className:`w-full`,intent:W?`neutral`:`primary`,onClick:()=>{o.passkey_name?(B(`registering`),H(``),J.mutate({name:o.passkey_name})):B(`form`)},type:`button`,uiSize:`lg`,children:e(`setupPasskey.retry`)})]}),(0,L.jsx)(A,{children:(0,L.jsx)(k,{link:(0,L.jsx)(c,{search:y(o),to:`/login`,children:e(`setupPasskey.backToLogin`)})})})]}):(0,L.jsxs)(D,{children:[(0,L.jsx)(T,{subtitle:e(`setupPasskey.subtitle`),title:e(`setupPasskey.title`)}),(0,L.jsx)(E,{icon:h,type:`info`,children:e(`setupPasskey.required`)}),(0,L.jsx)(r,{align:`center`,as:`p`,color:`muted`,variant:`bodySm`,children:e(`setupPasskey.description`)}),(0,L.jsxs)(`form`,{className:`flex flex-col gap-tinyrack-lg`,onSubmit:K(e=>{B(`registering`),H(``),J.mutate({name:e.name||void 0})}),children:[(0,L.jsx)(p,{error:q.name,hint:e(`setupPasskey.name.hint`),icon:F,id:`passkey-name`,label:e(`setupPasskey.name.label`),placeholder:e(`setupPasskey.name.placeholder`),...G(`name`),type:`text`}),(0,L.jsx)(a,{className:`w-full`,intent:`primary`,loading:J.isPending,loadingLabel:e(`setupPasskey.registering`),type:`submit`,uiSize:`lg`,children:e(`setupPasskey.continue`)})]}),(0,L.jsx)(A,{children:(0,L.jsx)(k,{link:(0,L.jsx)(c,{search:y(o),to:`/login`,children:e(`setupPasskey.backToLogin`)})})})]})}export{R as component};
|
|
2
|
+
//# sourceMappingURL=passkey-CgAHaQ1p.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passkey-V_tav53p.js","names":["standardSchemaResolver","useMutation","useQueryClient","useSuspenseQuery","Link","useRouter","TRButton","TRLinkButton","TRText","CircleAlertIcon","FingerprintIcon","ShieldCheckIcon","TagIcon","useEffect","useMemo","useRef","useState","useForm","useTranslation","z","AuthField","AuthFooter","AuthFooterLink","AuthPageHeader","Alert","AuthLayout","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","classifyPasskeyError","tick","appConfigQueryOptions","registerPasskeyMutationOptions","getSessionQueryOptions","Route","SetupStep","SetupPasskey","t","router","queryClient","search","useSearch","data","appConfig","step","setStep","passkey_name","errorMessage","setErrorMessage","autoRegisterCalledRef","canUseTotpSetup","auth","password","enabled","two_factor","enrollment_required","totp","formSchema","object","name","string","max","FormValues","infer","register","handleSubmit","formState","errors","defaultValues","resolver","registerMutation","onSuccess","second_factor_setup_completed","user","setQueryData","queryKey","window","location","href","navigate","to","invalidateQueries","onError","error","reason","onSettled","current","mutate","onSubmit","values","undefined","isPending","component"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/tag.mjs","../../../frontend/src/routes/setup/passkey/index.tsx?tsr-split=component"],"sourcesContent":["/**\n * @license lucide-react v1.25.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z\",\n key: \"vktsd0\"\n }\n ],\n [\"circle\", { cx: \"7.5\", cy: \"7.5\", r: \".5\", fill: \"currentColor\", key: \"kqv944\" }]\n];\nconst Tag = createLucideIcon(\"tag\", __iconNode);\n\nexport { __iconNode, Tag as default };\n//# sourceMappingURL=tag.mjs.map\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, Link, useRouter } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport {\n CircleAlertIcon,\n FingerprintIcon,\n ShieldCheckIcon,\n TagIcon,\n} from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport { classifyPasskeyError } from '#frontend/libs/passkey-error.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { registerPasskeyMutationOptions } from '#frontend/queries/passkey.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\nconst SearchSchema = OAuthSearchSchema.extend({\n passkey_name: z.string().optional(),\n});\n\nexport const Route = createFileRoute('/setup/passkey/')({\n component: SetupPasskey,\n errorComponent: RouteErrorFallback,\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n validateSearch: SearchSchema,\n});\n\ntype SetupStep = 'form' | 'registering' | 'error';\n\nfunction SetupPasskey() {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n\n const [step, setStep] = useState<SetupStep>(\n search.passkey_name ? 'registering' : 'form',\n );\n const [errorMessage, setErrorMessage] = useState<string>('');\n const autoRegisterCalledRef = useRef(false);\n const canUseTotpSetup =\n appConfig.auth.password.enabled &&\n appConfig.auth.password.two_factor.enrollment_required &&\n appConfig.auth.password.totp.enabled;\n\n const formSchema = useMemo(\n () =>\n z.object({\n name: z.string().max(100, t('validation.passkey.name.max')),\n }),\n [t],\n );\n\n type FormValues = z.infer<typeof formSchema>;\n\n const {\n register,\n handleSubmit,\n formState: { errors },\n } = useForm<FormValues>({\n defaultValues: { name: '' },\n resolver: standardSchemaResolver(formSchema),\n });\n\n const registerMutation = useMutation({\n ...registerPasskeyMutationOptions,\n onSuccess: async (data) => {\n if (data.second_factor_setup_completed && data.user) {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: data.user,\n });\n await tick();\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n } else {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n router.navigate({ to: '/profile' });\n }\n },\n onError: (error) => {\n setStep('error');\n const reason = classifyPasskeyError(error);\n\n switch (reason) {\n case 'unsupported':\n setErrorMessage(t('setupPasskey.error.unsupported'));\n return;\n case 'not_allowed':\n setErrorMessage(t('setupPasskey.error.notAllowed'));\n return;\n case 'expired':\n setErrorMessage(t('setupPasskey.error.expired'));\n return;\n case 'user_mismatch':\n case 'verification_failed':\n setErrorMessage(t('setupPasskey.error.failed'));\n return;\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n useEffect(() => {\n if (search.passkey_name && !autoRegisterCalledRef.current) {\n autoRegisterCalledRef.current = true;\n registerMutation.mutate({ name: search.passkey_name });\n }\n }, [registerMutation, search.passkey_name]);\n\n const onSubmit = (values: FormValues) => {\n setStep('registering');\n setErrorMessage('');\n registerMutation.mutate({ name: values.name || undefined });\n };\n\n // Registering state - waiting for WebAuthn\n if (step === 'registering') {\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('setupPasskey.subtitle')}\n title={t('setupPasskey.title')}\n />\n <div className=\"flex flex-col items-center gap-tinyrack-lg py-tinyrack-xl\">\n <FingerprintIcon\n aria-hidden\n className=\"size-tinyrack-3xl animate-pulse text-tinyrack-primary-foreground\"\n />\n <TRText align=\"center\" as=\"p\" color=\"muted\" variant=\"body\">\n {t('setupPasskey.waiting')}\n </TRText>\n </div>\n </AuthLayout>\n );\n }\n\n // Error state\n if (step === 'error') {\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('setupPasskey.subtitle')}\n title={t('setupPasskey.title')}\n />\n <Alert icon={CircleAlertIcon} type=\"error\">\n {errorMessage}\n </Alert>\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n {canUseTotpSetup && (\n <TRLinkButton\n className=\"w-full gap-tinyrack-sm\"\n intent=\"primary\"\n render={\n <Link search={extractOAuthParams(search)} to=\"/setup/totp\" />\n }\n uiSize=\"lg\"\n >\n <ShieldCheckIcon aria-hidden className=\"size-tinyrack-xl\" />\n {t('setupPasskey.useTotp')}\n </TRLinkButton>\n )}\n <TRButton\n appearance={canUseTotpSetup ? 'outline' : 'solid'}\n className=\"w-full\"\n intent={canUseTotpSetup ? 'neutral' : 'primary'}\n onClick={() => {\n if (search.passkey_name) {\n setStep('registering');\n setErrorMessage('');\n registerMutation.mutate({ name: search.passkey_name });\n } else {\n setStep('form');\n }\n }}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('setupPasskey.retry')}\n </TRButton>\n </div>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('setupPasskey.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n }\n\n // Form state\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('setupPasskey.subtitle')}\n title={t('setupPasskey.title')}\n />\n\n <Alert icon={ShieldCheckIcon} type=\"info\">\n {t('setupPasskey.required')}\n </Alert>\n\n <TRText align=\"center\" as=\"p\" color=\"muted\" variant=\"bodySm\">\n {t('setupPasskey.description')}\n </TRText>\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n error={errors.name}\n hint={t('setupPasskey.name.hint')}\n icon={TagIcon}\n id=\"passkey-name\"\n label={t('setupPasskey.name.label')}\n placeholder={t('setupPasskey.name.placeholder')}\n {...register('name')}\n type=\"text\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={registerMutation.isPending}\n loadingLabel={t('setupPasskey.registering')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('setupPasskey.continue')}\n </TRButton>\n </form>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('setupPasskey.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"passkey-CgAHaQ1p.js","names":["standardSchemaResolver","useMutation","useQueryClient","useSuspenseQuery","Link","useRouter","TRButton","TRLinkButton","TRText","CircleAlertIcon","FingerprintIcon","ShieldCheckIcon","TagIcon","useEffect","useMemo","useRef","useState","useForm","useTranslation","z","AuthField","AuthFooter","AuthFooterLink","AuthPageHeader","Alert","AuthLayout","buildAuthenticatedAuthorizeUrl","extractOAuthParams","isOAuthFlow","classifyPasskeyError","tick","appConfigQueryOptions","registerPasskeyMutationOptions","getSessionQueryOptions","Route","SetupStep","SetupPasskey","t","router","queryClient","search","useSearch","data","appConfig","step","setStep","passkey_name","errorMessage","setErrorMessage","autoRegisterCalledRef","canUseTotpSetup","auth","password","enabled","two_factor","enrollment_required","totp","formSchema","object","name","string","max","FormValues","infer","register","handleSubmit","formState","errors","defaultValues","resolver","registerMutation","onSuccess","second_factor_setup_completed","user","setQueryData","queryKey","window","location","href","navigate","to","invalidateQueries","onError","error","reason","onSettled","current","mutate","onSubmit","values","undefined","isPending","component"],"sources":["../../../../node_modules/.pnpm/lucide-react@1.25.0_react@19.2.7/node_modules/lucide-react/dist/esm/icons/tag.mjs","../../../frontend/src/routes/setup/passkey/index.tsx?tsr-split=component"],"sourcesContent":["/**\n * @license lucide-react v1.25.0 - ISC\n *\n * This source code is licensed under the ISC license.\n * See the LICENSE file in the root directory of this source tree.\n */\n\nimport createLucideIcon from '../createLucideIcon.mjs';\n\nconst __iconNode = [\n [\n \"path\",\n {\n d: \"M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z\",\n key: \"vktsd0\"\n }\n ],\n [\"circle\", { cx: \"7.5\", cy: \"7.5\", r: \".5\", fill: \"currentColor\", key: \"kqv944\" }]\n];\nconst Tag = createLucideIcon(\"tag\", __iconNode);\n\nexport { __iconNode, Tag as default };\n//# sourceMappingURL=tag.mjs.map\n","import { standardSchemaResolver } from '@hookform/resolvers/standard-schema';\nimport {\n useMutation,\n useQueryClient,\n useSuspenseQuery,\n} from '@tanstack/react-query';\nimport { createFileRoute, Link, useRouter } from '@tanstack/react-router';\nimport { TRButton } from '@tinyrack/ui/components/button';\nimport { TRLinkButton } from '@tinyrack/ui/components/link-button';\nimport { TRText } from '@tinyrack/ui/components/text';\nimport {\n CircleAlertIcon,\n FingerprintIcon,\n ShieldCheckIcon,\n TagIcon,\n} from 'lucide-react';\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport { useForm } from 'react-hook-form';\nimport { useTranslation } from 'react-i18next';\nimport { z } from 'zod';\nimport { AuthField } from '#frontend/components/auth/auth-field.tsx';\nimport {\n AuthFooter,\n AuthFooterLink,\n} from '#frontend/components/auth/auth-footer.tsx';\nimport { AuthPageHeader } from '#frontend/components/auth/auth-page-header.tsx';\nimport { Alert } from '#frontend/components/ui/alert.tsx';\nimport { RouteErrorFallback } from '#frontend/components/ui/route-error-fallback.tsx';\nimport { AuthLayout } from '#frontend/features/layout/auth-layout.tsx';\nimport {\n buildAuthenticatedAuthorizeUrl,\n extractOAuthParams,\n isOAuthFlow,\n OAuthSearchSchema,\n} from '#frontend/libs/oauth-search.ts';\nimport { classifyPasskeyError } from '#frontend/libs/passkey-error.ts';\nimport { tick } from '#frontend/libs/promise.ts';\nimport { appConfigQueryOptions } from '#frontend/queries/config.ts';\nimport { registerPasskeyMutationOptions } from '#frontend/queries/passkey.ts';\nimport { getSessionQueryOptions } from '#frontend/queries/session.ts';\n\nconst SearchSchema = OAuthSearchSchema.extend({\n passkey_name: z.string().optional(),\n});\n\nexport const Route = createFileRoute('/setup/passkey/')({\n component: SetupPasskey,\n errorComponent: RouteErrorFallback,\n loader: async ({ context }) => {\n await context.queryClient.ensureQueryData(appConfigQueryOptions);\n },\n validateSearch: SearchSchema,\n});\n\ntype SetupStep = 'form' | 'registering' | 'error';\n\nfunction SetupPasskey() {\n const { t } = useTranslation();\n const router = useRouter();\n const queryClient = useQueryClient();\n const search = Route.useSearch();\n const { data: appConfig } = useSuspenseQuery(appConfigQueryOptions);\n\n const [step, setStep] = useState<SetupStep>(\n search.passkey_name ? 'registering' : 'form',\n );\n const [errorMessage, setErrorMessage] = useState<string>('');\n const autoRegisterCalledRef = useRef(false);\n const canUseTotpSetup =\n appConfig.auth.password.enabled &&\n appConfig.auth.password.two_factor.enrollment_required &&\n appConfig.auth.password.totp.enabled;\n\n const formSchema = useMemo(\n () =>\n z.object({\n name: z.string().max(100, t('validation.passkey.name.max')),\n }),\n [t],\n );\n\n type FormValues = z.infer<typeof formSchema>;\n\n const {\n register,\n handleSubmit,\n formState: { errors },\n } = useForm<FormValues>({\n defaultValues: { name: '' },\n resolver: standardSchemaResolver(formSchema),\n });\n\n const registerMutation = useMutation({\n ...registerPasskeyMutationOptions,\n onSuccess: async (data) => {\n if (data.second_factor_setup_completed && data.user) {\n queryClient.setQueryData(getSessionQueryOptions.queryKey, {\n user: data.user,\n });\n await tick();\n\n if (isOAuthFlow(search)) {\n window.location.href = buildAuthenticatedAuthorizeUrl(search);\n } else {\n router.navigate({ to: '/profile' });\n }\n } else {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n router.navigate({ to: '/profile' });\n }\n },\n onError: (error) => {\n setStep('error');\n const reason = classifyPasskeyError(error);\n\n switch (reason) {\n case 'unsupported':\n setErrorMessage(t('setupPasskey.error.unsupported'));\n return;\n case 'not_allowed':\n setErrorMessage(t('setupPasskey.error.notAllowed'));\n return;\n case 'expired':\n setErrorMessage(t('setupPasskey.error.expired'));\n return;\n case 'user_mismatch':\n case 'verification_failed':\n setErrorMessage(t('setupPasskey.error.failed'));\n return;\n }\n },\n onSettled: () => {\n queryClient.invalidateQueries({\n queryKey: getSessionQueryOptions.queryKey,\n });\n },\n });\n\n useEffect(() => {\n if (search.passkey_name && !autoRegisterCalledRef.current) {\n autoRegisterCalledRef.current = true;\n registerMutation.mutate({ name: search.passkey_name });\n }\n }, [registerMutation, search.passkey_name]);\n\n const onSubmit = (values: FormValues) => {\n setStep('registering');\n setErrorMessage('');\n registerMutation.mutate({ name: values.name || undefined });\n };\n\n // Registering state - waiting for WebAuthn\n if (step === 'registering') {\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('setupPasskey.subtitle')}\n title={t('setupPasskey.title')}\n />\n <div className=\"flex flex-col items-center gap-tinyrack-lg py-tinyrack-xl\">\n <FingerprintIcon\n aria-hidden\n className=\"size-tinyrack-3xl animate-pulse text-tinyrack-primary-foreground\"\n />\n <TRText align=\"center\" as=\"p\" color=\"muted\" variant=\"body\">\n {t('setupPasskey.waiting')}\n </TRText>\n </div>\n </AuthLayout>\n );\n }\n\n // Error state\n if (step === 'error') {\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('setupPasskey.subtitle')}\n title={t('setupPasskey.title')}\n />\n <Alert icon={CircleAlertIcon} type=\"error\">\n {errorMessage}\n </Alert>\n\n <div className=\"flex flex-col gap-tinyrack-sm\">\n {canUseTotpSetup && (\n <TRLinkButton\n className=\"w-full gap-tinyrack-sm\"\n intent=\"primary\"\n render={\n <Link search={extractOAuthParams(search)} to=\"/setup/totp\" />\n }\n uiSize=\"lg\"\n >\n <ShieldCheckIcon aria-hidden className=\"size-tinyrack-xl\" />\n {t('setupPasskey.useTotp')}\n </TRLinkButton>\n )}\n <TRButton\n appearance={canUseTotpSetup ? 'outline' : 'solid'}\n className=\"w-full\"\n intent={canUseTotpSetup ? 'neutral' : 'primary'}\n onClick={() => {\n if (search.passkey_name) {\n setStep('registering');\n setErrorMessage('');\n registerMutation.mutate({ name: search.passkey_name });\n } else {\n setStep('form');\n }\n }}\n type=\"button\"\n uiSize=\"lg\"\n >\n {t('setupPasskey.retry')}\n </TRButton>\n </div>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('setupPasskey.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n }\n\n // Form state\n return (\n <AuthLayout>\n <AuthPageHeader\n subtitle={t('setupPasskey.subtitle')}\n title={t('setupPasskey.title')}\n />\n\n <Alert icon={ShieldCheckIcon} type=\"info\">\n {t('setupPasskey.required')}\n </Alert>\n\n <TRText align=\"center\" as=\"p\" color=\"muted\" variant=\"bodySm\">\n {t('setupPasskey.description')}\n </TRText>\n\n <form\n className=\"flex flex-col gap-tinyrack-lg\"\n onSubmit={handleSubmit(onSubmit)}\n >\n <AuthField\n error={errors.name}\n hint={t('setupPasskey.name.hint')}\n icon={TagIcon}\n id=\"passkey-name\"\n label={t('setupPasskey.name.label')}\n placeholder={t('setupPasskey.name.placeholder')}\n {...register('name')}\n type=\"text\"\n />\n\n <TRButton\n className=\"w-full\"\n intent=\"primary\"\n loading={registerMutation.isPending}\n loadingLabel={t('setupPasskey.registering')}\n type=\"submit\"\n uiSize=\"lg\"\n >\n {t('setupPasskey.continue')}\n </TRButton>\n </form>\n\n <AuthFooter>\n <AuthFooterLink\n link={\n <Link search={extractOAuthParams(search)} to=\"/login\">\n {t('setupPasskey.backToLogin')}\n </Link>\n }\n />\n </AuthFooter>\n </AuthLayout>\n );\n}\n"],"x_google_ignoreList":[0],"mappings":"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"}
|