@customafk/lunas-ui 0.2.59 → 0.2.61
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/dist/cms-layout-BeU_CkJl.mjs +2 -0
- package/dist/cms-layout-BeU_CkJl.mjs.map +1 -0
- package/dist/cms-layout-hLiQbkXR.cjs +2 -0
- package/dist/cms-layout-hLiQbkXR.cjs.map +1 -0
- package/dist/dialogs/detail-dialog/index.cjs +1 -1
- package/dist/dialogs/detail-dialog/index.cjs.map +1 -1
- package/dist/dialogs/detail-dialog/index.mjs +1 -1
- package/dist/dialogs/detail-dialog/index.mjs.map +1 -1
- package/dist/features/logo/index.cjs +2 -0
- package/dist/features/logo/index.cjs.map +1 -0
- package/dist/features/logo/index.d.cts +22 -0
- package/dist/features/logo/index.d.mts +22 -0
- package/dist/features/logo/index.mjs +2 -0
- package/dist/features/logo/index.mjs.map +1 -0
- package/dist/features/tables/index.cjs +1 -1
- package/dist/features/tables/index.d.cts +1 -1
- package/dist/features/tables/index.d.mts +1 -1
- package/dist/features/tables/index.mjs +1 -1
- package/dist/{index-nsk470g_.d.cts → index-CGpGIM1H.d.cts} +8 -4
- package/dist/{index-Cbhv1_Xe.d.mts → index-DueBB0rP.d.mts} +8 -4
- package/dist/{index-DqlCqAcc.d.cts → index-teY5hw2R.d.cts} +24 -3
- package/dist/{index-DqlCqAcc.d.mts → index-teY5hw2R.d.mts} +24 -3
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.mts +3 -3
- package/dist/index.mjs +1 -1
- package/dist/layouts/cms-layout/index.cjs +1 -1
- package/dist/layouts/cms-layout/index.d.cts +1 -1
- package/dist/layouts/cms-layout/index.d.mts +1 -1
- package/dist/layouts/cms-layout/index.mjs +1 -1
- package/dist/layouts/payment-layout/index.cjs +1 -1
- package/dist/layouts/payment-layout/index.mjs +1 -1
- package/dist/pages/FeatureDeveloping.cjs +1 -1
- package/dist/pages/FeatureDeveloping.cjs.map +1 -1
- package/dist/pages/FeatureDeveloping.mjs +1 -1
- package/dist/pages/FeatureDeveloping.mjs.map +1 -1
- package/dist/pages/FeatureFixing.cjs +1 -1
- package/dist/pages/FeatureFixing.cjs.map +1 -1
- package/dist/pages/FeatureFixing.mjs +1 -1
- package/dist/pages/FeatureFixing.mjs.map +1 -1
- package/dist/pages/LoginPage.cjs +1 -1
- package/dist/pages/LoginPage.cjs.map +1 -1
- package/dist/pages/LoginPage.mjs +1 -1
- package/dist/pages/LoginPage.mjs.map +1 -1
- package/dist/pages/NotAuthorized.cjs +1 -1
- package/dist/pages/NotAuthorized.cjs.map +1 -1
- package/dist/pages/NotAuthorized.mjs +1 -1
- package/dist/pages/NotAuthorized.mjs.map +1 -1
- package/dist/pages/NotFound.cjs +1 -1
- package/dist/pages/NotFound.cjs.map +1 -1
- package/dist/pages/NotFound.mjs +1 -1
- package/dist/pages/NotFound.mjs.map +1 -1
- package/dist/pages/RegisterPage.cjs +1 -1
- package/dist/pages/RegisterPage.cjs.map +1 -1
- package/dist/pages/RegisterPage.mjs +1 -1
- package/dist/pages/RegisterPage.mjs.map +1 -1
- package/dist/pages/VerifyEmailPage.cjs +1 -1
- package/dist/pages/VerifyEmailPage.cjs.map +1 -1
- package/dist/pages/VerifyEmailPage.mjs +1 -1
- package/dist/pages/VerifyEmailPage.mjs.map +1 -1
- package/dist/payment-layout-BLrXT-oc.cjs +2 -0
- package/dist/payment-layout-BLrXT-oc.cjs.map +1 -0
- package/dist/payment-layout-Dsz6wYT1.mjs +2 -0
- package/dist/{payment-layout-CG_l7Es-.mjs.map → payment-layout-Dsz6wYT1.mjs.map} +1 -1
- package/dist/{resizable-CVpc0g5U.d.cts → resizable-CFCYn9e_.d.cts} +2 -2
- package/dist/{resizable-CVpc0g5U.d.mts → resizable-CFCYn9e_.d.mts} +2 -2
- package/dist/tables-B0Jl8Osc.cjs +4 -0
- package/dist/{tables-CNJ10l7L.cjs.map → tables-B0Jl8Osc.cjs.map} +1 -1
- package/dist/tables-Cylalxey.mjs +4 -0
- package/dist/{tables-UaA4qIaD.mjs.map → tables-Cylalxey.mjs.map} +1 -1
- package/dist/tanstack-form-BMVn8ai_.cjs.map +1 -1
- package/dist/tanstack-form-DTCgWGCA.mjs.map +1 -1
- package/dist/ui/resizable.d.cts +1 -1
- package/dist/ui/resizable.d.mts +1 -1
- package/package.json +5 -1
- package/dist/cms-layout-Bsw2mKXo.cjs +0 -2
- package/dist/cms-layout-Bsw2mKXo.cjs.map +0 -1
- package/dist/cms-layout-IwpD0LvR.mjs +0 -2
- package/dist/cms-layout-IwpD0LvR.mjs.map +0 -1
- package/dist/payment-layout-CG_l7Es-.mjs +0 -2
- package/dist/payment-layout-Dlvc1P7b.cjs +0 -2
- package/dist/payment-layout-Dlvc1P7b.cjs.map +0 -1
- package/dist/tables-CNJ10l7L.cjs +0 -4
- package/dist/tables-UaA4qIaD.mjs +0 -4
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Button as e}from"../ui/button.mjs";import{
|
|
1
|
+
"use client";import{Button as e}from"../ui/button.mjs";import{LunasLogo as t}from"../features/logo/index.mjs";import{Construction as n}from"lucide-react";import{cn as r}from"@customafk/react-toolkit/utils";import{jsx as i,jsxs as a}from"react/jsx-runtime";const o=({title:o=`Tính năng đang phát triển`,subtitle:s=`Chúng tôi đang phát triển tính năng này. Vui lòng quay lại sau.`,buttonText:c=`Quay lại`,onButtonClick:l=()=>window.history.back(),className:u,iconClassName:d})=>a(`div`,{className:r(`flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full`,u),children:[i(t,{variant:`stacked`,size:`sm`}),a(`div`,{className:`flex flex-col items-center gap-2`,children:[i(n,{className:r(`size-16 sm:size-24 text-yellow-500`,d)}),i(`div`,{className:`text-3xl sm:text-5xl font-bold text-yellow-500`})]}),i(`h1`,{className:`text-xl sm:text-2xl font-semibold mt-2`,children:o}),i(`p`,{className:`text-muted-foreground max-w-md text-sm sm:text-base`,children:s}),i(e,{onClick:l,variant:`default`,className:`mt-2`,size:`sm`,children:c})]});export{o as FeatureDeveloping,o as default};
|
|
2
2
|
//# sourceMappingURL=FeatureDeveloping.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeatureDeveloping.mjs","names":[],"sources":["../../packages/components/pages/FeatureDeveloping.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"FeatureDeveloping.mjs","names":[],"sources":["../../packages/components/pages/FeatureDeveloping.tsx"],"sourcesContent":["'use client';\n\nimport { Construction } from 'lucide-react';\n\nimport { cn } from '@customafk/react-toolkit/utils';\n\nimport { Button } from '../ui/button';\nimport { LunasLogo } from '../features/logo';\n\nexport interface FeatureDevelopingProps {\n /** Main heading shown beneath the construction icon. Defaults to `'Tính năng đang phát triển'`. */\n title?: string;\n /** Supporting text shown below the title. Defaults to a Vietnamese message asking the user to check back later. */\n subtitle?: string;\n /** Label for the action button. Defaults to `'Quay lại'`. */\n buttonText?: string;\n /** Handler called when the action button is clicked. Defaults to `window.history.back()`. */\n onButtonClick?: () => void;\n /** Additional class names applied to the root container element. */\n className?: string;\n /** Additional class names applied to the `Construction` icon. */\n iconClassName?: string;\n}\n\n/**\n * Full-page placeholder displayed when a feature is still under active development.\n *\n * @example\n * ```tsx\n * import { FeatureDeveloping } from '@customafk/lunas-ui/pages/FeatureDeveloping';\n *\n * <FeatureDeveloping\n * title=\"Coming Soon\"\n * subtitle=\"This feature is not ready yet.\"\n * buttonText=\"Go Back\"\n * onButtonClick={() => router.back()}\n * />\n * ```\n */\nexport const FeatureDeveloping = ({\n title = 'Tính năng đang phát triển',\n subtitle = 'Chúng tôi đang phát triển tính năng này. Vui lòng quay lại sau.',\n buttonText = 'Quay lại',\n onButtonClick = () => window.history.back(),\n className,\n iconClassName,\n}: FeatureDevelopingProps) => {\n return (\n <div className={cn('flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full', className)}>\n <LunasLogo variant=\"stacked\" size=\"sm\" />\n <div className=\"flex flex-col items-center gap-2\">\n <Construction className={cn('size-16 sm:size-24 text-yellow-500', iconClassName)} />\n <div className=\"text-3xl sm:text-5xl font-bold text-yellow-500\">{/* This text shows the feature is in development */}</div>\n </div>\n\n <h1 className=\"text-xl sm:text-2xl font-semibold mt-2\">{title}</h1>\n\n <p className=\"text-muted-foreground max-w-md text-sm sm:text-base\">{subtitle}</p>\n\n <Button onClick={onButtonClick} variant=\"default\" className=\"mt-2\" size=\"sm\">\n {buttonText}\n </Button>\n </div>\n );\n};\n\nexport default FeatureDeveloping;\n"],"mappings":"gQAuCA,MAAa,GAAqB,CAChC,QAAQ,4BACR,WAAW,kEACX,aAAa,WACb,oBAAsB,OAAO,QAAQ,KAAK,EAC1C,YACA,mBAGE,EAAC,MAAD,CAAK,UAAW,EAAG,8GAA+G,CAAS,WAA3I,CACE,EAAC,EAAD,CAAW,QAAQ,UAAU,KAAK,IAAM,CAAA,EACxC,EAAC,MAAD,CAAK,UAAU,4CAAf,CACE,EAAC,EAAD,CAAc,UAAW,EAAG,qCAAsC,CAAa,CAAI,CAAA,EACnF,EAAC,MAAD,CAAK,UAAU,gDAA2G,CAAA,CACvH,IAEL,EAAC,KAAD,CAAI,UAAU,kDAA0C,CAAU,CAAA,EAElE,EAAC,IAAD,CAAG,UAAU,+DAAuD,CAAY,CAAA,EAEhF,EAAC,EAAD,CAAQ,QAAS,EAAe,QAAQ,UAAU,UAAU,OAAO,KAAK,cACrE,CACK,CAAA,CACL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../ui/button.cjs");let
|
|
1
|
+
"use client";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../ui/button.cjs"),t=require("../features/logo/index.cjs");let n=require("lucide-react"),r=require("@customafk/react-toolkit/utils"),i=require("react/jsx-runtime");const a=({title:a=`Tính năng đang bảo trì`,subtitle:o=`Chúng tôi đang khắc phục vấn đề với tính năng này. Vui lòng quay lại sau.`,buttonText:s=`Quay lại`,onButtonClick:c=()=>window.history.back(),className:l,iconClassName:u})=>(0,i.jsxs)(`div`,{className:(0,r.cn)(`flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full`,l),children:[(0,i.jsx)(t.LunasLogo,{variant:`stacked`,size:`sm`}),(0,i.jsxs)(`div`,{className:`flex flex-col items-center gap-2`,children:[(0,i.jsx)(n.Wrench,{className:(0,r.cn)(`size-16 sm:size-24 text-orange-500`,u)}),(0,i.jsx)(`div`,{className:`text-3xl sm:text-5xl font-bold text-orange-500`})]}),(0,i.jsx)(`h1`,{className:`text-xl sm:text-2xl font-semibold mt-2`,children:a}),(0,i.jsx)(`p`,{className:`text-muted-foreground max-w-md text-sm sm:text-base`,children:o}),(0,i.jsx)(e.Button,{onClick:c,variant:`default`,className:`mt-2`,size:`sm`,children:s})]});exports.FeatureFixing=a,exports.default=a;
|
|
2
2
|
//# sourceMappingURL=FeatureFixing.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeatureFixing.cjs","names":["Wrench","Button"],"sources":["../../packages/components/pages/FeatureFixing.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"FeatureFixing.cjs","names":["LunasLogo","Wrench","Button"],"sources":["../../packages/components/pages/FeatureFixing.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '../ui/button';\nimport { cn } from '@customafk/react-toolkit/utils';\nimport { Wrench } from 'lucide-react';\nimport { LunasLogo } from '../features/logo';\n\nexport interface FeatureFixingProps {\n /** Main heading shown beneath the wrench icon. Defaults to `'Tính năng đang bảo trì'`. */\n title?: string;\n /** Supporting text shown below the title. Defaults to a Vietnamese maintenance message. */\n subtitle?: string;\n /** Label for the action button. Defaults to `'Quay lại'`. */\n buttonText?: string;\n /** Handler called when the action button is clicked. Defaults to `window.history.back()`. */\n onButtonClick?: () => void;\n /** Additional class names applied to the root container element. */\n className?: string;\n /** Additional class names applied to the `Wrench` icon. */\n iconClassName?: string;\n}\n\n/**\n * Full-page placeholder displayed when a feature is temporarily unavailable due to maintenance or bug fixes.\n *\n * @example\n * ```tsx\n * import { FeatureFixing } from '@customafk/lunas-ui/pages/FeatureFixing';\n *\n * <FeatureFixing\n * title=\"Under Maintenance\"\n * subtitle=\"We are fixing an issue. Please check back later.\"\n * buttonText=\"Go Back\"\n * onButtonClick={() => router.back()}\n * />\n * ```\n */\nexport const FeatureFixing = ({\n title = 'Tính năng đang bảo trì',\n subtitle = 'Chúng tôi đang khắc phục vấn đề với tính năng này. Vui lòng quay lại sau.',\n buttonText = 'Quay lại',\n onButtonClick = () => window.history.back(),\n className,\n iconClassName,\n}: FeatureFixingProps) => {\n return (\n <div className={cn('flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full', className)}>\n <LunasLogo variant=\"stacked\" size=\"sm\" />\n <div className=\"flex flex-col items-center gap-2\">\n <Wrench className={cn('size-16 sm:size-24 text-orange-500', iconClassName)} />\n <div className=\"text-3xl sm:text-5xl font-bold text-orange-500\">{/* This text indicates the feature is being fixed */}</div>\n </div>\n\n <h1 className=\"text-xl sm:text-2xl font-semibold mt-2\">{title}</h1>\n\n <p className=\"text-muted-foreground max-w-md text-sm sm:text-base\">{subtitle}</p>\n\n <Button onClick={onButtonClick} variant=\"default\" className=\"mt-2\" size=\"sm\">\n {buttonText}\n </Button>\n </div>\n );\n};\n\nexport default FeatureFixing;\n"],"mappings":"iSAqCA,MAAa,GAAiB,CAC5B,QAAQ,yBACR,WAAW,4EACX,aAAa,WACb,oBAAsB,OAAO,QAAQ,KAAK,EAC1C,YACA,oBAGE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,WAAA,EAAA,EAAA,GAAA,CAAc,8GAA+G,CAAS,WAA3I,EACE,EAAA,EAAA,IAAA,CAACA,EAAAA,UAAD,CAAW,QAAQ,UAAU,KAAK,IAAM,CAAA,GACxC,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,4CAAf,EACE,EAAA,EAAA,IAAA,CAACC,EAAAA,OAAD,CAAQ,WAAA,EAAA,EAAA,GAAA,CAAc,qCAAsC,CAAa,CAAI,CAAA,GAC7E,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,gDAA4G,CAAA,CACxH,KAEL,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,kDAA0C,CAAU,CAAA,GAElE,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAU,+DAAuD,CAAY,CAAA,GAEhF,EAAA,EAAA,IAAA,CAACC,EAAAA,OAAD,CAAQ,QAAS,EAAe,QAAQ,UAAU,UAAU,OAAO,KAAK,cACrE,CACK,CAAA,CACL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Button as e}from"../ui/button.mjs";import{
|
|
1
|
+
"use client";import{Button as e}from"../ui/button.mjs";import{LunasLogo as t}from"../features/logo/index.mjs";import{Wrench as n}from"lucide-react";import{cn as r}from"@customafk/react-toolkit/utils";import{jsx as i,jsxs as a}from"react/jsx-runtime";const o=({title:o=`Tính năng đang bảo trì`,subtitle:s=`Chúng tôi đang khắc phục vấn đề với tính năng này. Vui lòng quay lại sau.`,buttonText:c=`Quay lại`,onButtonClick:l=()=>window.history.back(),className:u,iconClassName:d})=>a(`div`,{className:r(`flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full`,u),children:[i(t,{variant:`stacked`,size:`sm`}),a(`div`,{className:`flex flex-col items-center gap-2`,children:[i(n,{className:r(`size-16 sm:size-24 text-orange-500`,d)}),i(`div`,{className:`text-3xl sm:text-5xl font-bold text-orange-500`})]}),i(`h1`,{className:`text-xl sm:text-2xl font-semibold mt-2`,children:o}),i(`p`,{className:`text-muted-foreground max-w-md text-sm sm:text-base`,children:s}),i(e,{onClick:l,variant:`default`,className:`mt-2`,size:`sm`,children:c})]});export{o as FeatureFixing,o as default};
|
|
2
2
|
//# sourceMappingURL=FeatureFixing.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FeatureFixing.mjs","names":[],"sources":["../../packages/components/pages/FeatureFixing.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"FeatureFixing.mjs","names":[],"sources":["../../packages/components/pages/FeatureFixing.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '../ui/button';\nimport { cn } from '@customafk/react-toolkit/utils';\nimport { Wrench } from 'lucide-react';\nimport { LunasLogo } from '../features/logo';\n\nexport interface FeatureFixingProps {\n /** Main heading shown beneath the wrench icon. Defaults to `'Tính năng đang bảo trì'`. */\n title?: string;\n /** Supporting text shown below the title. Defaults to a Vietnamese maintenance message. */\n subtitle?: string;\n /** Label for the action button. Defaults to `'Quay lại'`. */\n buttonText?: string;\n /** Handler called when the action button is clicked. Defaults to `window.history.back()`. */\n onButtonClick?: () => void;\n /** Additional class names applied to the root container element. */\n className?: string;\n /** Additional class names applied to the `Wrench` icon. */\n iconClassName?: string;\n}\n\n/**\n * Full-page placeholder displayed when a feature is temporarily unavailable due to maintenance or bug fixes.\n *\n * @example\n * ```tsx\n * import { FeatureFixing } from '@customafk/lunas-ui/pages/FeatureFixing';\n *\n * <FeatureFixing\n * title=\"Under Maintenance\"\n * subtitle=\"We are fixing an issue. Please check back later.\"\n * buttonText=\"Go Back\"\n * onButtonClick={() => router.back()}\n * />\n * ```\n */\nexport const FeatureFixing = ({\n title = 'Tính năng đang bảo trì',\n subtitle = 'Chúng tôi đang khắc phục vấn đề với tính năng này. Vui lòng quay lại sau.',\n buttonText = 'Quay lại',\n onButtonClick = () => window.history.back(),\n className,\n iconClassName,\n}: FeatureFixingProps) => {\n return (\n <div className={cn('flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full', className)}>\n <LunasLogo variant=\"stacked\" size=\"sm\" />\n <div className=\"flex flex-col items-center gap-2\">\n <Wrench className={cn('size-16 sm:size-24 text-orange-500', iconClassName)} />\n <div className=\"text-3xl sm:text-5xl font-bold text-orange-500\">{/* This text indicates the feature is being fixed */}</div>\n </div>\n\n <h1 className=\"text-xl sm:text-2xl font-semibold mt-2\">{title}</h1>\n\n <p className=\"text-muted-foreground max-w-md text-sm sm:text-base\">{subtitle}</p>\n\n <Button onClick={onButtonClick} variant=\"default\" className=\"mt-2\" size=\"sm\">\n {buttonText}\n </Button>\n </div>\n );\n};\n\nexport default FeatureFixing;\n"],"mappings":"0PAqCA,MAAa,GAAiB,CAC5B,QAAQ,yBACR,WAAW,4EACX,aAAa,WACb,oBAAsB,OAAO,QAAQ,KAAK,EAC1C,YACA,mBAGE,EAAC,MAAD,CAAK,UAAW,EAAG,8GAA+G,CAAS,WAA3I,CACE,EAAC,EAAD,CAAW,QAAQ,UAAU,KAAK,IAAM,CAAA,EACxC,EAAC,MAAD,CAAK,UAAU,4CAAf,CACE,EAAC,EAAD,CAAQ,UAAW,EAAG,qCAAsC,CAAa,CAAI,CAAA,EAC7E,EAAC,MAAD,CAAK,UAAU,gDAA4G,CAAA,CACxH,IAEL,EAAC,KAAD,CAAI,UAAU,kDAA0C,CAAU,CAAA,EAElE,EAAC,IAAD,CAAG,UAAU,+DAAuD,CAAY,CAAA,EAEhF,EAAC,EAAD,CAAQ,QAAS,EAAe,QAAQ,UAAU,UAAU,OAAO,KAAK,cACrE,CACK,CAAA,CACL"}
|
package/dist/pages/LoginPage.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../ui/button.cjs"),t=require("../ui/dialog.cjs"),
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../ui/button.cjs"),t=require("../features/logo/index.cjs"),n=require("../ui/dialog.cjs"),r=require("../ui/input.cjs"),i=require("../ui/label.cjs"),a=require("../alert-D31c6DLq.cjs");let o=require("lucide-react"),s=require("react"),c=require("react/jsx-runtime"),l=require("zod");const u=l.z.object({email:l.z.string().min(1,`Vui lòng nhập email`).email(`Email không hợp lệ`),password:l.z.string().min(1,`Vui lòng nhập mật khẩu`)}),d=({open:l,onOpenChange:d,onLogin:f,onForgotPassword:p,onRegister:m,isLoading:h=!1,errorMessage:g,title:_=`Đăng nhập`,subtitle:v=`Nhập thông tin để tiếp tục`})=>{let y=(0,s.useId)(),b=(0,s.useId)(),[x,S]=(0,s.useState)(``),[C,w]=(0,s.useState)(``),[T,E]=(0,s.useState)(!1),[D,O]=(0,s.useState)({});return(0,c.jsx)(n.Dialog,{open:l,onOpenChange:d,children:(0,c.jsxs)(n.DialogContent,{className:`max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm`,children:[(0,c.jsxs)(n.DialogHeader,{children:[(0,c.jsx)(t.LunasLogo,{variant:`horizontal`,size:`sm`,className:`mx-auto`}),(0,c.jsxs)(`div`,{className:`flex flex-col gap-0.5`,children:[(0,c.jsx)(n.DialogTitle,{children:_}),(0,c.jsx)(n.DialogDescription,{children:v})]})]}),(0,c.jsxs)(`form`,{onSubmit:async e=>{e.preventDefault();let t=u.safeParse({email:x,password:C});if(!t.success){let e=t.error.flatten().fieldErrors;O({email:e.email?.[0],password:e.password?.[0]});return}O({}),await f(x,C)},noValidate:!0,className:`flex flex-col gap-4`,children:[g&&(0,c.jsx)(a.t,{variant:`destructive`,children:(0,c.jsx)(a.n,{children:g})}),(0,c.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,c.jsx)(i.Label,{htmlFor:y,children:`Email`}),(0,c.jsx)(r.Input,{id:y,type:`email`,autoComplete:`email`,placeholder:`you@example.com`,value:x,onChange:e=>S(e.target.value),"aria-invalid":!!D.email,disabled:h}),D.email&&(0,c.jsx)(`p`,{className:`text-destructive text-xs`,children:D.email})]}),(0,c.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,c.jsxs)(`div`,{className:`flex items-center justify-between`,children:[(0,c.jsx)(i.Label,{htmlFor:b,children:`Mật khẩu`}),p&&(0,c.jsx)(e.Button,{type:`button`,variant:`link`,size:`sm`,className:`h-auto p-0 text-xs`,onClick:p,children:`Quên mật khẩu?`})]}),(0,c.jsxs)(`div`,{className:`relative`,children:[(0,c.jsx)(r.Input,{id:b,type:T?`text`:`password`,autoComplete:`current-password`,placeholder:`••••••••`,value:C,onChange:e=>w(e.target.value),"aria-invalid":!!D.password,disabled:h,className:`pr-10`}),(0,c.jsx)(`button`,{type:`button`,className:`absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground`,onMouseDown:e=>{e.preventDefault(),E(e=>!e)},"aria-label":T?`Ẩn mật khẩu`:`Hiện mật khẩu`,children:T?(0,c.jsx)(o.EyeOff,{size:16}):(0,c.jsx)(o.Eye,{size:16})})]}),D.password&&(0,c.jsx)(`p`,{className:`text-destructive text-xs`,children:D.password})]}),(0,c.jsx)(e.Button,{type:`submit`,isLoading:h,className:`w-full`,children:`Đăng nhập`}),(0,c.jsx)(e.Button,{type:`button`,variant:`outline`,className:`w-full`,disabled:h,onClick:()=>d(!1),children:`Đóng hộp thoại`})]}),m&&(0,c.jsxs)(`div`,{className:`flex justify-center gap-1 text-muted-foreground text-sm`,children:[(0,c.jsx)(`span`,{children:`Chưa có tài khoản?`}),(0,c.jsx)(e.Button,{variant:`link`,size:`sm`,className:`h-auto p-0`,onClick:m,children:`Đăng ký`})]})]})})};exports.LoginPage=d;
|
|
2
2
|
//# sourceMappingURL=LoginPage.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginPage.cjs","names":["z","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","Alert","AlertDescription","Label","Input","Button","EyeOff","Eye"],"sources":["../../packages/components/pages/LoginPage.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\n\nimport { Eye, EyeOff } from 'lucide-react';\nimport { z } from 'zod';\n\nimport { Alert, AlertDescription } from '../ui/alert';\nimport { Button } from '../ui/button';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog';\nimport { Input } from '../ui/input';\nimport { Label } from '../ui/label';\n\nexport interface LoginPageProps {\n /** Controls whether the dialog is open */\n open: boolean;\n /** Called when the dialog open state changes */\n onOpenChange: (open: boolean) => void;\n /** Called when the user submits login credentials */\n onLogin: (email: string, password: string) => Promise<void> | void;\n /** Called when the \"Quên mật khẩu?\" link is clicked */\n onForgotPassword?: () => void;\n /** Called when the \"Đăng ký\" link is clicked */\n onRegister?: () => void;\n /** Disables the form and shows a spinner on the submit button */\n isLoading?: boolean;\n /** Server-side error message rendered in a destructive Alert */\n errorMessage?: string;\n /** Dialog heading. Default: \"Đăng nhập\" */\n title?: string;\n /** Dialog subheading. Default: \"Nhập thông tin để tiếp tục\" */\n subtitle?: string;\n}\n\nconst loginSchema = z.object({\n email: z.string().min(1, 'Vui lòng nhập email').email('Email không hợp lệ'),\n password: z.string().min(1, 'Vui lòng nhập mật khẩu'),\n});\n\n/**\n * Modal login dialog for the client authentication flow.\n *\n * @example\n * ```tsx\n * import { LoginPage } from '@customafk/lunas-ui/pages/LoginPage';\n *\n * <LoginPage\n * open={open}\n * onOpenChange={setOpen}\n * onLogin={async (email, password) => {\n * await authService.login(email, password);\n * setOpen(false);\n * }}\n * onRegister={() => { setOpen(false); setRegisterOpen(true); }}\n * />\n * ```\n */\nexport const LoginPage = ({\n open,\n onOpenChange,\n onLogin,\n onForgotPassword,\n onRegister,\n isLoading = false,\n errorMessage,\n title = 'Đăng nhập',\n subtitle = 'Nhập thông tin để tiếp tục',\n}: LoginPageProps) => {\n const emailId = useId();\n const passwordId = useId();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ email?: string; password?: string }>({});\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const result = loginSchema.safeParse({ email, password });\n if (!result.success) {\n const errors = result.error.flatten().fieldErrors;\n setFieldErrors({ email: errors.email?.[0], password: errors.password?.[0] });\n return;\n }\n setFieldErrors({});\n await onLogin(email, password);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm\">\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{subtitle}</DialogDescription>\n </DialogHeader>\n\n <form onSubmit={handleSubmit} noValidate className=\"flex flex-col gap-4\">\n {errorMessage && (\n <Alert variant=\"destructive\">\n <AlertDescription>{errorMessage}</AlertDescription>\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={emailId}>Email</Label>\n <Input\n id={emailId}\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n value={email}\n onChange={e => setEmail(e.target.value)}\n aria-invalid={!!fieldErrors.email}\n disabled={isLoading}\n />\n {fieldErrors.email && <p className=\"text-destructive text-xs\">{fieldErrors.email}</p>}\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center justify-between\">\n <Label htmlFor={passwordId}>Mật khẩu</Label>\n {onForgotPassword && (\n <Button type=\"button\" variant=\"link\" size=\"sm\" className=\"h-auto p-0 text-xs\" onClick={onForgotPassword}>\n Quên mật khẩu?\n </Button>\n )}\n </div>\n <div className=\"relative\">\n <Input\n id={passwordId}\n type={showPassword ? 'text' : 'password'}\n autoComplete=\"current-password\"\n placeholder=\"••••••••\"\n value={password}\n onChange={e => setPassword(e.target.value)}\n aria-invalid={!!fieldErrors.password}\n disabled={isLoading}\n className=\"pr-10\"\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground\"\n onMouseDown={e => {\n e.preventDefault();\n setShowPassword(v => !v);\n }}\n aria-label={showPassword ? 'Ẩn mật khẩu' : 'Hiện mật khẩu'}\n >\n {showPassword ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n {fieldErrors.password && <p className=\"text-destructive text-xs\">{fieldErrors.password}</p>}\n </div>\n\n <Button type=\"submit\" isLoading={isLoading} className=\"w-full\">\n Đăng nhập\n </Button>\n <Button type=\"button\" variant=\"outline\" className=\"w-full\" disabled={isLoading} onClick={() => onOpenChange(false)}>\n Đóng hộp thoại\n </Button>\n </form>\n\n {onRegister && (\n <div className=\"flex justify-center gap-1 text-muted-foreground text-sm\">\n <span>Chưa có tài khoản?</span>\n <Button variant=\"link\" size=\"sm\" className=\"h-auto p-0\" onClick={onRegister}>\n Đăng ký\n </Button>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LoginPage.cjs","names":["z","Dialog","DialogContent","DialogHeader","LunasLogo","DialogTitle","DialogDescription","Alert","AlertDescription","Label","Input","Button","EyeOff","Eye"],"sources":["../../packages/components/pages/LoginPage.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\n\nimport { Eye, EyeOff } from 'lucide-react';\nimport { z } from 'zod';\n\nimport { Alert, AlertDescription } from '../ui/alert';\nimport { Button } from '../ui/button';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog';\nimport { LunasLogo } from '../features/logo';\nimport { Input } from '../ui/input';\nimport { Label } from '../ui/label';\n\nexport interface LoginPageProps {\n /** Controls whether the dialog is open */\n open: boolean;\n /** Called when the dialog open state changes */\n onOpenChange: (open: boolean) => void;\n /** Called when the user submits login credentials */\n onLogin: (email: string, password: string) => Promise<void> | void;\n /** Called when the \"Quên mật khẩu?\" link is clicked */\n onForgotPassword?: () => void;\n /** Called when the \"Đăng ký\" link is clicked */\n onRegister?: () => void;\n /** Disables the form and shows a spinner on the submit button */\n isLoading?: boolean;\n /** Server-side error message rendered in a destructive Alert */\n errorMessage?: string;\n /** Dialog heading. Default: \"Đăng nhập\" */\n title?: string;\n /** Dialog subheading. Default: \"Nhập thông tin để tiếp tục\" */\n subtitle?: string;\n}\n\nconst loginSchema = z.object({\n email: z.string().min(1, 'Vui lòng nhập email').email('Email không hợp lệ'),\n password: z.string().min(1, 'Vui lòng nhập mật khẩu'),\n});\n\n/**\n * Modal login dialog for the client authentication flow.\n *\n * @example\n * ```tsx\n * import { LoginPage } from '@customafk/lunas-ui/pages/LoginPage';\n *\n * <LoginPage\n * open={open}\n * onOpenChange={setOpen}\n * onLogin={async (email, password) => {\n * await authService.login(email, password);\n * setOpen(false);\n * }}\n * onRegister={() => { setOpen(false); setRegisterOpen(true); }}\n * />\n * ```\n */\nexport const LoginPage = ({\n open,\n onOpenChange,\n onLogin,\n onForgotPassword,\n onRegister,\n isLoading = false,\n errorMessage,\n title = 'Đăng nhập',\n subtitle = 'Nhập thông tin để tiếp tục',\n}: LoginPageProps) => {\n const emailId = useId();\n const passwordId = useId();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ email?: string; password?: string }>({});\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const result = loginSchema.safeParse({ email, password });\n if (!result.success) {\n const errors = result.error.flatten().fieldErrors;\n setFieldErrors({ email: errors.email?.[0], password: errors.password?.[0] });\n return;\n }\n setFieldErrors({});\n await onLogin(email, password);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm\">\n <DialogHeader>\n <LunasLogo variant=\"horizontal\" size=\"sm\" className=\"mx-auto\" />\n <div className=\"flex flex-col gap-0.5\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{subtitle}</DialogDescription>\n </div>\n </DialogHeader>\n\n <form onSubmit={handleSubmit} noValidate className=\"flex flex-col gap-4\">\n {errorMessage && (\n <Alert variant=\"destructive\">\n <AlertDescription>{errorMessage}</AlertDescription>\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={emailId}>Email</Label>\n <Input\n id={emailId}\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n value={email}\n onChange={e => setEmail(e.target.value)}\n aria-invalid={!!fieldErrors.email}\n disabled={isLoading}\n />\n {fieldErrors.email && <p className=\"text-destructive text-xs\">{fieldErrors.email}</p>}\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center justify-between\">\n <Label htmlFor={passwordId}>Mật khẩu</Label>\n {onForgotPassword && (\n <Button type=\"button\" variant=\"link\" size=\"sm\" className=\"h-auto p-0 text-xs\" onClick={onForgotPassword}>\n Quên mật khẩu?\n </Button>\n )}\n </div>\n <div className=\"relative\">\n <Input\n id={passwordId}\n type={showPassword ? 'text' : 'password'}\n autoComplete=\"current-password\"\n placeholder=\"••••••••\"\n value={password}\n onChange={e => setPassword(e.target.value)}\n aria-invalid={!!fieldErrors.password}\n disabled={isLoading}\n className=\"pr-10\"\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground\"\n onMouseDown={e => {\n e.preventDefault();\n setShowPassword(v => !v);\n }}\n aria-label={showPassword ? 'Ẩn mật khẩu' : 'Hiện mật khẩu'}\n >\n {showPassword ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n {fieldErrors.password && <p className=\"text-destructive text-xs\">{fieldErrors.password}</p>}\n </div>\n\n <Button type=\"submit\" isLoading={isLoading} className=\"w-full\">\n Đăng nhập\n </Button>\n <Button type=\"button\" variant=\"outline\" className=\"w-full\" disabled={isLoading} onClick={() => onOpenChange(false)}>\n Đóng hộp thoại\n </Button>\n </form>\n\n {onRegister && (\n <div className=\"flex justify-center gap-1 text-muted-foreground text-sm\">\n <span>Chưa có tài khoản?</span>\n <Button variant=\"link\" size=\"sm\" className=\"h-auto p-0\" onClick={onRegister}>\n Đăng ký\n </Button>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
|
package/dist/pages/LoginPage.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{Button as e}from"../ui/button.mjs";import{Dialog as
|
|
1
|
+
"use client";import{Button as e}from"../ui/button.mjs";import{LunasLogo as t}from"../features/logo/index.mjs";import{Dialog as n,DialogContent as r,DialogDescription as i,DialogHeader as a,DialogTitle as o}from"../ui/dialog.mjs";import{Input as s}from"../ui/input.mjs";import{Label as c}from"../ui/label.mjs";import{n as l,t as u}from"../alert-OD1KJTEV.mjs";import{Eye as d,EyeOff as f}from"lucide-react";import{useId as p,useState as m}from"react";import{jsx as h,jsxs as g}from"react/jsx-runtime";import{z as _}from"zod";const v=_.object({email:_.string().min(1,`Vui lòng nhập email`).email(`Email không hợp lệ`),password:_.string().min(1,`Vui lòng nhập mật khẩu`)}),y=({open:_,onOpenChange:y,onLogin:b,onForgotPassword:x,onRegister:S,isLoading:C=!1,errorMessage:w,title:T=`Đăng nhập`,subtitle:E=`Nhập thông tin để tiếp tục`})=>{let D=p(),O=p(),[k,A]=m(``),[j,M]=m(``),[N,P]=m(!1),[F,I]=m({});return h(n,{open:_,onOpenChange:y,children:g(r,{className:`max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm`,children:[g(a,{children:[h(t,{variant:`horizontal`,size:`sm`,className:`mx-auto`}),g(`div`,{className:`flex flex-col gap-0.5`,children:[h(o,{children:T}),h(i,{children:E})]})]}),g(`form`,{onSubmit:async e=>{e.preventDefault();let t=v.safeParse({email:k,password:j});if(!t.success){let e=t.error.flatten().fieldErrors;I({email:e.email?.[0],password:e.password?.[0]});return}I({}),await b(k,j)},noValidate:!0,className:`flex flex-col gap-4`,children:[w&&h(u,{variant:`destructive`,children:h(l,{children:w})}),g(`div`,{className:`flex flex-col gap-1.5`,children:[h(c,{htmlFor:D,children:`Email`}),h(s,{id:D,type:`email`,autoComplete:`email`,placeholder:`you@example.com`,value:k,onChange:e=>A(e.target.value),"aria-invalid":!!F.email,disabled:C}),F.email&&h(`p`,{className:`text-destructive text-xs`,children:F.email})]}),g(`div`,{className:`flex flex-col gap-1.5`,children:[g(`div`,{className:`flex items-center justify-between`,children:[h(c,{htmlFor:O,children:`Mật khẩu`}),x&&h(e,{type:`button`,variant:`link`,size:`sm`,className:`h-auto p-0 text-xs`,onClick:x,children:`Quên mật khẩu?`})]}),g(`div`,{className:`relative`,children:[h(s,{id:O,type:N?`text`:`password`,autoComplete:`current-password`,placeholder:`••••••••`,value:j,onChange:e=>M(e.target.value),"aria-invalid":!!F.password,disabled:C,className:`pr-10`}),h(`button`,{type:`button`,className:`absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground`,onMouseDown:e=>{e.preventDefault(),P(e=>!e)},"aria-label":N?`Ẩn mật khẩu`:`Hiện mật khẩu`,children:h(N?f:d,{size:16})})]}),F.password&&h(`p`,{className:`text-destructive text-xs`,children:F.password})]}),h(e,{type:`submit`,isLoading:C,className:`w-full`,children:`Đăng nhập`}),h(e,{type:`button`,variant:`outline`,className:`w-full`,disabled:C,onClick:()=>y(!1),children:`Đóng hộp thoại`})]}),S&&g(`div`,{className:`flex justify-center gap-1 text-muted-foreground text-sm`,children:[h(`span`,{children:`Chưa có tài khoản?`}),h(e,{variant:`link`,size:`sm`,className:`h-auto p-0`,onClick:S,children:`Đăng ký`})]})]})})};export{y as LoginPage};
|
|
2
2
|
//# sourceMappingURL=LoginPage.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LoginPage.mjs","names":[],"sources":["../../packages/components/pages/LoginPage.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\n\nimport { Eye, EyeOff } from 'lucide-react';\nimport { z } from 'zod';\n\nimport { Alert, AlertDescription } from '../ui/alert';\nimport { Button } from '../ui/button';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog';\nimport { Input } from '../ui/input';\nimport { Label } from '../ui/label';\n\nexport interface LoginPageProps {\n /** Controls whether the dialog is open */\n open: boolean;\n /** Called when the dialog open state changes */\n onOpenChange: (open: boolean) => void;\n /** Called when the user submits login credentials */\n onLogin: (email: string, password: string) => Promise<void> | void;\n /** Called when the \"Quên mật khẩu?\" link is clicked */\n onForgotPassword?: () => void;\n /** Called when the \"Đăng ký\" link is clicked */\n onRegister?: () => void;\n /** Disables the form and shows a spinner on the submit button */\n isLoading?: boolean;\n /** Server-side error message rendered in a destructive Alert */\n errorMessage?: string;\n /** Dialog heading. Default: \"Đăng nhập\" */\n title?: string;\n /** Dialog subheading. Default: \"Nhập thông tin để tiếp tục\" */\n subtitle?: string;\n}\n\nconst loginSchema = z.object({\n email: z.string().min(1, 'Vui lòng nhập email').email('Email không hợp lệ'),\n password: z.string().min(1, 'Vui lòng nhập mật khẩu'),\n});\n\n/**\n * Modal login dialog for the client authentication flow.\n *\n * @example\n * ```tsx\n * import { LoginPage } from '@customafk/lunas-ui/pages/LoginPage';\n *\n * <LoginPage\n * open={open}\n * onOpenChange={setOpen}\n * onLogin={async (email, password) => {\n * await authService.login(email, password);\n * setOpen(false);\n * }}\n * onRegister={() => { setOpen(false); setRegisterOpen(true); }}\n * />\n * ```\n */\nexport const LoginPage = ({\n open,\n onOpenChange,\n onLogin,\n onForgotPassword,\n onRegister,\n isLoading = false,\n errorMessage,\n title = 'Đăng nhập',\n subtitle = 'Nhập thông tin để tiếp tục',\n}: LoginPageProps) => {\n const emailId = useId();\n const passwordId = useId();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ email?: string; password?: string }>({});\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const result = loginSchema.safeParse({ email, password });\n if (!result.success) {\n const errors = result.error.flatten().fieldErrors;\n setFieldErrors({ email: errors.email?.[0], password: errors.password?.[0] });\n return;\n }\n setFieldErrors({});\n await onLogin(email, password);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm\">\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n
|
|
1
|
+
{"version":3,"file":"LoginPage.mjs","names":[],"sources":["../../packages/components/pages/LoginPage.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\n\nimport { Eye, EyeOff } from 'lucide-react';\nimport { z } from 'zod';\n\nimport { Alert, AlertDescription } from '../ui/alert';\nimport { Button } from '../ui/button';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog';\nimport { LunasLogo } from '../features/logo';\nimport { Input } from '../ui/input';\nimport { Label } from '../ui/label';\n\nexport interface LoginPageProps {\n /** Controls whether the dialog is open */\n open: boolean;\n /** Called when the dialog open state changes */\n onOpenChange: (open: boolean) => void;\n /** Called when the user submits login credentials */\n onLogin: (email: string, password: string) => Promise<void> | void;\n /** Called when the \"Quên mật khẩu?\" link is clicked */\n onForgotPassword?: () => void;\n /** Called when the \"Đăng ký\" link is clicked */\n onRegister?: () => void;\n /** Disables the form and shows a spinner on the submit button */\n isLoading?: boolean;\n /** Server-side error message rendered in a destructive Alert */\n errorMessage?: string;\n /** Dialog heading. Default: \"Đăng nhập\" */\n title?: string;\n /** Dialog subheading. Default: \"Nhập thông tin để tiếp tục\" */\n subtitle?: string;\n}\n\nconst loginSchema = z.object({\n email: z.string().min(1, 'Vui lòng nhập email').email('Email không hợp lệ'),\n password: z.string().min(1, 'Vui lòng nhập mật khẩu'),\n});\n\n/**\n * Modal login dialog for the client authentication flow.\n *\n * @example\n * ```tsx\n * import { LoginPage } from '@customafk/lunas-ui/pages/LoginPage';\n *\n * <LoginPage\n * open={open}\n * onOpenChange={setOpen}\n * onLogin={async (email, password) => {\n * await authService.login(email, password);\n * setOpen(false);\n * }}\n * onRegister={() => { setOpen(false); setRegisterOpen(true); }}\n * />\n * ```\n */\nexport const LoginPage = ({\n open,\n onOpenChange,\n onLogin,\n onForgotPassword,\n onRegister,\n isLoading = false,\n errorMessage,\n title = 'Đăng nhập',\n subtitle = 'Nhập thông tin để tiếp tục',\n}: LoginPageProps) => {\n const emailId = useId();\n const passwordId = useId();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{ email?: string; password?: string }>({});\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const result = loginSchema.safeParse({ email, password });\n if (!result.success) {\n const errors = result.error.flatten().fieldErrors;\n setFieldErrors({ email: errors.email?.[0], password: errors.password?.[0] });\n return;\n }\n setFieldErrors({});\n await onLogin(email, password);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm\">\n <DialogHeader>\n <LunasLogo variant=\"horizontal\" size=\"sm\" className=\"mx-auto\" />\n <div className=\"flex flex-col gap-0.5\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{subtitle}</DialogDescription>\n </div>\n </DialogHeader>\n\n <form onSubmit={handleSubmit} noValidate className=\"flex flex-col gap-4\">\n {errorMessage && (\n <Alert variant=\"destructive\">\n <AlertDescription>{errorMessage}</AlertDescription>\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={emailId}>Email</Label>\n <Input\n id={emailId}\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n value={email}\n onChange={e => setEmail(e.target.value)}\n aria-invalid={!!fieldErrors.email}\n disabled={isLoading}\n />\n {fieldErrors.email && <p className=\"text-destructive text-xs\">{fieldErrors.email}</p>}\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center justify-between\">\n <Label htmlFor={passwordId}>Mật khẩu</Label>\n {onForgotPassword && (\n <Button type=\"button\" variant=\"link\" size=\"sm\" className=\"h-auto p-0 text-xs\" onClick={onForgotPassword}>\n Quên mật khẩu?\n </Button>\n )}\n </div>\n <div className=\"relative\">\n <Input\n id={passwordId}\n type={showPassword ? 'text' : 'password'}\n autoComplete=\"current-password\"\n placeholder=\"••••••••\"\n value={password}\n onChange={e => setPassword(e.target.value)}\n aria-invalid={!!fieldErrors.password}\n disabled={isLoading}\n className=\"pr-10\"\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground\"\n onMouseDown={e => {\n e.preventDefault();\n setShowPassword(v => !v);\n }}\n aria-label={showPassword ? 'Ẩn mật khẩu' : 'Hiện mật khẩu'}\n >\n {showPassword ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n {fieldErrors.password && <p className=\"text-destructive text-xs\">{fieldErrors.password}</p>}\n </div>\n\n <Button type=\"submit\" isLoading={isLoading} className=\"w-full\">\n Đăng nhập\n </Button>\n <Button type=\"button\" variant=\"outline\" className=\"w-full\" disabled={isLoading} onClick={() => onOpenChange(false)}>\n Đóng hộp thoại\n </Button>\n </form>\n\n {onRegister && (\n <div className=\"flex justify-center gap-1 text-muted-foreground text-sm\">\n <span>Chưa có tài khoản?</span>\n <Button variant=\"link\" size=\"sm\" className=\"h-auto p-0\" onClick={onRegister}>\n Đăng ký\n </Button>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../ui/button.cjs");let
|
|
1
|
+
"use client";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../ui/button.cjs"),t=require("../features/logo/index.cjs");let n=require("lucide-react"),r=require("@customafk/react-toolkit/utils"),i=require("react/jsx-runtime");const a=({title:a=`Quyền truy cập bị từ chối`,subtitle:o=`Xin lỗi, bạn không có quyền truy cập vào trang này.`,buttonText:s=`Quay lại`,onButtonClick:c=()=>window.history.back(),className:l,iconClassName:u})=>(0,i.jsxs)(`div`,{className:(0,r.cn)(`flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full`,l),children:[(0,i.jsx)(t.LunasLogo,{variant:`stacked`,size:`sm`}),(0,i.jsxs)(`div`,{className:`flex flex-col items-center gap-2`,children:[(0,i.jsx)(n.ShieldAlert,{className:(0,r.cn)(`size-16 sm:size-24 text-primary`,u)}),(0,i.jsx)(`div`,{className:`text-4xl sm:text-6xl font-bold text-primary`,children:`403`})]}),(0,i.jsx)(`h1`,{className:`text-xl sm:text-2xl font-semibold mt-2`,children:a}),(0,i.jsx)(`p`,{className:`text-muted-foreground max-w-md text-sm sm:text-base`,children:o}),(0,i.jsx)(e.Button,{onClick:c,variant:`default`,className:`mt-2`,size:`sm`,children:s})]});exports.NotAuthorized=a,exports.default=a;
|
|
2
2
|
//# sourceMappingURL=NotAuthorized.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotAuthorized.cjs","names":["ShieldAlert","Button"],"sources":["../../packages/components/pages/NotAuthorized.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"NotAuthorized.cjs","names":["LunasLogo","ShieldAlert","Button"],"sources":["../../packages/components/pages/NotAuthorized.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '../ui/button';\nimport { cn } from '@customafk/react-toolkit/utils';\nimport { ShieldAlert } from 'lucide-react';\nimport { LunasLogo } from '../features/logo';\n\nexport interface NotAuthorizedProps {\n /** Main heading shown beneath the shield icon. Defaults to `'Quyền truy cập bị từ chối'`. */\n title?: string;\n /** Supporting text shown below the title. Defaults to a Vietnamese access-denied message. */\n subtitle?: string;\n /** Label for the action button. Defaults to `'Quay lại'`. */\n buttonText?: string;\n /** Handler called when the action button is clicked. Defaults to `window.history.back()`. */\n onButtonClick?: () => void;\n /** Additional class names applied to the root container element. */\n className?: string;\n /** Additional class names applied to the `ShieldAlert` icon. */\n iconClassName?: string;\n}\n\n/**\n * Full-page 403 error screen shown when the current user lacks permission to access a resource.\n *\n * @example\n * ```tsx\n * import { NotAuthorized } from '@customafk/lunas-ui/pages/NotAuthorized';\n *\n * <NotAuthorized\n * title=\"Access Denied\"\n * subtitle=\"You do not have permission to view this page.\"\n * buttonText=\"Go Back\"\n * onButtonClick={() => router.push('/')}\n * />\n * ```\n */\nexport const NotAuthorized = ({\n title = 'Quyền truy cập bị từ chối',\n subtitle = 'Xin lỗi, bạn không có quyền truy cập vào trang này.',\n buttonText = 'Quay lại',\n onButtonClick = () => window.history.back(),\n className,\n iconClassName,\n}: NotAuthorizedProps) => {\n return (\n <div className={cn('flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full', className)}>\n <LunasLogo variant=\"stacked\" size=\"sm\" />\n <div className=\"flex flex-col items-center gap-2\">\n <ShieldAlert className={cn('size-16 sm:size-24 text-primary', iconClassName)} />\n <div className=\"text-4xl sm:text-6xl font-bold text-primary\">403</div>\n </div>\n\n <h1 className=\"text-xl sm:text-2xl font-semibold mt-2\">{title}</h1>\n\n <p className=\"text-muted-foreground max-w-md text-sm sm:text-base\">{subtitle}</p>\n\n <Button onClick={onButtonClick} variant=\"default\" className=\"mt-2\" size=\"sm\">\n {buttonText}\n </Button>\n </div>\n );\n};\n\nexport default NotAuthorized;\n"],"mappings":"iSAqCA,MAAa,GAAiB,CAC5B,QAAQ,4BACR,WAAW,sDACX,aAAa,WACb,oBAAsB,OAAO,QAAQ,KAAK,EAC1C,YACA,oBAGE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,WAAA,EAAA,EAAA,GAAA,CAAc,8GAA+G,CAAS,WAA3I,EACE,EAAA,EAAA,IAAA,CAACA,EAAAA,UAAD,CAAW,QAAQ,UAAU,KAAK,IAAM,CAAA,GACxC,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,4CAAf,EACE,EAAA,EAAA,IAAA,CAACC,EAAAA,YAAD,CAAa,WAAA,EAAA,EAAA,GAAA,CAAc,kCAAmC,CAAa,CAAI,CAAA,GAC/E,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,uDAA8C,KAAQ,CAAA,CAClE,KAEL,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,kDAA0C,CAAU,CAAA,GAElE,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAU,+DAAuD,CAAY,CAAA,GAEhF,EAAA,EAAA,IAAA,CAACC,EAAAA,OAAD,CAAQ,QAAS,EAAe,QAAQ,UAAU,UAAU,OAAO,KAAK,cACrE,CACK,CAAA,CACL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Button as e}from"../ui/button.mjs";import{
|
|
1
|
+
"use client";import{Button as e}from"../ui/button.mjs";import{LunasLogo as t}from"../features/logo/index.mjs";import{ShieldAlert as n}from"lucide-react";import{cn as r}from"@customafk/react-toolkit/utils";import{jsx as i,jsxs as a}from"react/jsx-runtime";const o=({title:o=`Quyền truy cập bị từ chối`,subtitle:s=`Xin lỗi, bạn không có quyền truy cập vào trang này.`,buttonText:c=`Quay lại`,onButtonClick:l=()=>window.history.back(),className:u,iconClassName:d})=>a(`div`,{className:r(`flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full`,u),children:[i(t,{variant:`stacked`,size:`sm`}),a(`div`,{className:`flex flex-col items-center gap-2`,children:[i(n,{className:r(`size-16 sm:size-24 text-primary`,d)}),i(`div`,{className:`text-4xl sm:text-6xl font-bold text-primary`,children:`403`})]}),i(`h1`,{className:`text-xl sm:text-2xl font-semibold mt-2`,children:o}),i(`p`,{className:`text-muted-foreground max-w-md text-sm sm:text-base`,children:s}),i(e,{onClick:l,variant:`default`,className:`mt-2`,size:`sm`,children:c})]});export{o as NotAuthorized,o as default};
|
|
2
2
|
//# sourceMappingURL=NotAuthorized.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotAuthorized.mjs","names":[],"sources":["../../packages/components/pages/NotAuthorized.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"NotAuthorized.mjs","names":[],"sources":["../../packages/components/pages/NotAuthorized.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '../ui/button';\nimport { cn } from '@customafk/react-toolkit/utils';\nimport { ShieldAlert } from 'lucide-react';\nimport { LunasLogo } from '../features/logo';\n\nexport interface NotAuthorizedProps {\n /** Main heading shown beneath the shield icon. Defaults to `'Quyền truy cập bị từ chối'`. */\n title?: string;\n /** Supporting text shown below the title. Defaults to a Vietnamese access-denied message. */\n subtitle?: string;\n /** Label for the action button. Defaults to `'Quay lại'`. */\n buttonText?: string;\n /** Handler called when the action button is clicked. Defaults to `window.history.back()`. */\n onButtonClick?: () => void;\n /** Additional class names applied to the root container element. */\n className?: string;\n /** Additional class names applied to the `ShieldAlert` icon. */\n iconClassName?: string;\n}\n\n/**\n * Full-page 403 error screen shown when the current user lacks permission to access a resource.\n *\n * @example\n * ```tsx\n * import { NotAuthorized } from '@customafk/lunas-ui/pages/NotAuthorized';\n *\n * <NotAuthorized\n * title=\"Access Denied\"\n * subtitle=\"You do not have permission to view this page.\"\n * buttonText=\"Go Back\"\n * onButtonClick={() => router.push('/')}\n * />\n * ```\n */\nexport const NotAuthorized = ({\n title = 'Quyền truy cập bị từ chối',\n subtitle = 'Xin lỗi, bạn không có quyền truy cập vào trang này.',\n buttonText = 'Quay lại',\n onButtonClick = () => window.history.back(),\n className,\n iconClassName,\n}: NotAuthorizedProps) => {\n return (\n <div className={cn('flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full', className)}>\n <LunasLogo variant=\"stacked\" size=\"sm\" />\n <div className=\"flex flex-col items-center gap-2\">\n <ShieldAlert className={cn('size-16 sm:size-24 text-primary', iconClassName)} />\n <div className=\"text-4xl sm:text-6xl font-bold text-primary\">403</div>\n </div>\n\n <h1 className=\"text-xl sm:text-2xl font-semibold mt-2\">{title}</h1>\n\n <p className=\"text-muted-foreground max-w-md text-sm sm:text-base\">{subtitle}</p>\n\n <Button onClick={onButtonClick} variant=\"default\" className=\"mt-2\" size=\"sm\">\n {buttonText}\n </Button>\n </div>\n );\n};\n\nexport default NotAuthorized;\n"],"mappings":"+PAqCA,MAAa,GAAiB,CAC5B,QAAQ,4BACR,WAAW,sDACX,aAAa,WACb,oBAAsB,OAAO,QAAQ,KAAK,EAC1C,YACA,mBAGE,EAAC,MAAD,CAAK,UAAW,EAAG,8GAA+G,CAAS,WAA3I,CACE,EAAC,EAAD,CAAW,QAAQ,UAAU,KAAK,IAAM,CAAA,EACxC,EAAC,MAAD,CAAK,UAAU,4CAAf,CACE,EAAC,EAAD,CAAa,UAAW,EAAG,kCAAmC,CAAa,CAAI,CAAA,EAC/E,EAAC,MAAD,CAAK,UAAU,uDAA8C,KAAQ,CAAA,CAClE,IAEL,EAAC,KAAD,CAAI,UAAU,kDAA0C,CAAU,CAAA,EAElE,EAAC,IAAD,CAAG,UAAU,+DAAuD,CAAY,CAAA,EAEhF,EAAC,EAAD,CAAQ,QAAS,EAAe,QAAQ,UAAU,UAAU,OAAO,KAAK,cACrE,CACK,CAAA,CACL"}
|
package/dist/pages/NotFound.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../ui/button.cjs");let
|
|
1
|
+
"use client";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("../ui/button.cjs"),t=require("../features/logo/index.cjs");let n=require("lucide-react"),r=require("@customafk/react-toolkit/utils"),i=require("react/jsx-runtime");const a=({title:a=`Không tìm thấy trang`,subtitle:o=`Xin lỗi, trang bạn đang tìm kiếm không tồn tại hoặc đã bị di chuyển.`,buttonText:s=`Trở về trang chủ`,onButtonClick:c=()=>window.history.back(),className:l,iconClassName:u})=>(0,i.jsxs)(`div`,{className:(0,r.cn)(`flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full`,l),children:[(0,i.jsx)(t.LunasLogo,{variant:`stacked`,size:`sm`}),(0,i.jsxs)(`div`,{className:`flex flex-col items-center gap-2`,children:[(0,i.jsx)(n.FileQuestion,{className:(0,r.cn)(`size-16 sm:size-24 text-primary`,u)}),(0,i.jsx)(`div`,{className:`text-4xl sm:text-6xl font-bold text-primary`,children:`404`})]}),(0,i.jsx)(`h1`,{className:`text-xl sm:text-2xl font-semibold mt-2`,children:a}),(0,i.jsx)(`p`,{className:`text-muted-foreground max-w-md text-sm sm:text-base`,children:o}),(0,i.jsx)(e.Button,{onClick:c,variant:`default`,className:`mt-2`,size:`sm`,children:s})]});exports.NotFound=a,exports.default=a;
|
|
2
2
|
//# sourceMappingURL=NotFound.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotFound.cjs","names":["FileQuestion","Button"],"sources":["../../packages/components/pages/NotFound.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"NotFound.cjs","names":["LunasLogo","FileQuestion","Button"],"sources":["../../packages/components/pages/NotFound.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '../ui/button';\nimport { cn } from '@customafk/react-toolkit/utils';\nimport { FileQuestion } from 'lucide-react';\nimport { LunasLogo } from '../features/logo';\n\nexport interface NotFoundProps {\n /** Main heading shown beneath the file-question icon. Defaults to `'Không tìm thấy trang'`. */\n title?: string;\n /** Supporting text shown below the title. Defaults to a Vietnamese 404 message. */\n subtitle?: string;\n /** Label for the action button. Defaults to `'Trở về trang chủ'`. */\n buttonText?: string;\n /** Handler called when the action button is clicked. Defaults to `window.history.back()`. */\n onButtonClick?: () => void;\n /** Additional class names applied to the root container element. */\n className?: string;\n /** Additional class names applied to the `FileQuestion` icon. */\n iconClassName?: string;\n}\n\n/**\n * Full-page 404 error screen displayed when a requested route or resource does not exist.\n *\n * @example\n * ```tsx\n * import { NotFound } from '@customafk/lunas-ui/pages/NotFound';\n *\n * <NotFound\n * title=\"Page Not Found\"\n * subtitle=\"The page you are looking for does not exist.\"\n * buttonText=\"Go Home\"\n * onButtonClick={() => router.push('/')}\n * />\n * ```\n */\nexport const NotFound = ({\n title = 'Không tìm thấy trang',\n subtitle = 'Xin lỗi, trang bạn đang tìm kiếm không tồn tại hoặc đã bị di chuyển.',\n buttonText = 'Trở về trang chủ',\n onButtonClick = () => window.history.back(),\n className,\n iconClassName,\n}: NotFoundProps) => {\n return (\n <div className={cn('flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full', className)}>\n <LunasLogo variant=\"stacked\" size=\"sm\" />\n <div className=\"flex flex-col items-center gap-2\">\n <FileQuestion className={cn('size-16 sm:size-24 text-primary', iconClassName)} />\n <div className=\"text-4xl sm:text-6xl font-bold text-primary\">404</div>\n </div>\n\n <h1 className=\"text-xl sm:text-2xl font-semibold mt-2\">{title}</h1>\n\n <p className=\"text-muted-foreground max-w-md text-sm sm:text-base\">{subtitle}</p>\n\n <Button onClick={onButtonClick} variant=\"default\" className=\"mt-2\" size=\"sm\">\n {buttonText}\n </Button>\n </div>\n );\n};\n\nexport default NotFound;\n"],"mappings":"iSAqCA,MAAa,GAAY,CACvB,QAAQ,uBACR,WAAW,uEACX,aAAa,mBACb,oBAAsB,OAAO,QAAQ,KAAK,EAC1C,YACA,oBAGE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,WAAA,EAAA,EAAA,GAAA,CAAc,8GAA+G,CAAS,WAA3I,EACE,EAAA,EAAA,IAAA,CAACA,EAAAA,UAAD,CAAW,QAAQ,UAAU,KAAK,IAAM,CAAA,GACxC,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAU,4CAAf,EACE,EAAA,EAAA,IAAA,CAACC,EAAAA,aAAD,CAAc,WAAA,EAAA,EAAA,GAAA,CAAc,kCAAmC,CAAa,CAAI,CAAA,GAChF,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAU,uDAA8C,KAAQ,CAAA,CAClE,KAEL,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAU,kDAA0C,CAAU,CAAA,GAElE,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAU,+DAAuD,CAAY,CAAA,GAEhF,EAAA,EAAA,IAAA,CAACC,EAAAA,OAAD,CAAQ,QAAS,EAAe,QAAQ,UAAU,UAAU,OAAO,KAAK,cACrE,CACK,CAAA,CACL"}
|
package/dist/pages/NotFound.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{Button as e}from"../ui/button.mjs";import{
|
|
1
|
+
"use client";import{Button as e}from"../ui/button.mjs";import{LunasLogo as t}from"../features/logo/index.mjs";import{FileQuestion as n}from"lucide-react";import{cn as r}from"@customafk/react-toolkit/utils";import{jsx as i,jsxs as a}from"react/jsx-runtime";const o=({title:o=`Không tìm thấy trang`,subtitle:s=`Xin lỗi, trang bạn đang tìm kiếm không tồn tại hoặc đã bị di chuyển.`,buttonText:c=`Trở về trang chủ`,onButtonClick:l=()=>window.history.back(),className:u,iconClassName:d})=>a(`div`,{className:r(`flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full`,u),children:[i(t,{variant:`stacked`,size:`sm`}),a(`div`,{className:`flex flex-col items-center gap-2`,children:[i(n,{className:r(`size-16 sm:size-24 text-primary`,d)}),i(`div`,{className:`text-4xl sm:text-6xl font-bold text-primary`,children:`404`})]}),i(`h1`,{className:`text-xl sm:text-2xl font-semibold mt-2`,children:o}),i(`p`,{className:`text-muted-foreground max-w-md text-sm sm:text-base`,children:s}),i(e,{onClick:l,variant:`default`,className:`mt-2`,size:`sm`,children:c})]});export{o as NotFound,o as default};
|
|
2
2
|
//# sourceMappingURL=NotFound.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotFound.mjs","names":[],"sources":["../../packages/components/pages/NotFound.tsx"],"sourcesContent":["
|
|
1
|
+
{"version":3,"file":"NotFound.mjs","names":[],"sources":["../../packages/components/pages/NotFound.tsx"],"sourcesContent":["'use client';\n\nimport { Button } from '../ui/button';\nimport { cn } from '@customafk/react-toolkit/utils';\nimport { FileQuestion } from 'lucide-react';\nimport { LunasLogo } from '../features/logo';\n\nexport interface NotFoundProps {\n /** Main heading shown beneath the file-question icon. Defaults to `'Không tìm thấy trang'`. */\n title?: string;\n /** Supporting text shown below the title. Defaults to a Vietnamese 404 message. */\n subtitle?: string;\n /** Label for the action button. Defaults to `'Trở về trang chủ'`. */\n buttonText?: string;\n /** Handler called when the action button is clicked. Defaults to `window.history.back()`. */\n onButtonClick?: () => void;\n /** Additional class names applied to the root container element. */\n className?: string;\n /** Additional class names applied to the `FileQuestion` icon. */\n iconClassName?: string;\n}\n\n/**\n * Full-page 404 error screen displayed when a requested route or resource does not exist.\n *\n * @example\n * ```tsx\n * import { NotFound } from '@customafk/lunas-ui/pages/NotFound';\n *\n * <NotFound\n * title=\"Page Not Found\"\n * subtitle=\"The page you are looking for does not exist.\"\n * buttonText=\"Go Home\"\n * onButtonClick={() => router.push('/')}\n * />\n * ```\n */\nexport const NotFound = ({\n title = 'Không tìm thấy trang',\n subtitle = 'Xin lỗi, trang bạn đang tìm kiếm không tồn tại hoặc đã bị di chuyển.',\n buttonText = 'Trở về trang chủ',\n onButtonClick = () => window.history.back(),\n className,\n iconClassName,\n}: NotFoundProps) => {\n return (\n <div className={cn('flex flex-col items-center justify-center min-h-[50vh] gap-4 sm:gap-6 py-8 sm:py-10 px-4 text-center w-full', className)}>\n <LunasLogo variant=\"stacked\" size=\"sm\" />\n <div className=\"flex flex-col items-center gap-2\">\n <FileQuestion className={cn('size-16 sm:size-24 text-primary', iconClassName)} />\n <div className=\"text-4xl sm:text-6xl font-bold text-primary\">404</div>\n </div>\n\n <h1 className=\"text-xl sm:text-2xl font-semibold mt-2\">{title}</h1>\n\n <p className=\"text-muted-foreground max-w-md text-sm sm:text-base\">{subtitle}</p>\n\n <Button onClick={onButtonClick} variant=\"default\" className=\"mt-2\" size=\"sm\">\n {buttonText}\n </Button>\n </div>\n );\n};\n\nexport default NotFound;\n"],"mappings":"gQAqCA,MAAa,GAAY,CACvB,QAAQ,uBACR,WAAW,uEACX,aAAa,mBACb,oBAAsB,OAAO,QAAQ,KAAK,EAC1C,YACA,mBAGE,EAAC,MAAD,CAAK,UAAW,EAAG,8GAA+G,CAAS,WAA3I,CACE,EAAC,EAAD,CAAW,QAAQ,UAAU,KAAK,IAAM,CAAA,EACxC,EAAC,MAAD,CAAK,UAAU,4CAAf,CACE,EAAC,EAAD,CAAc,UAAW,EAAG,kCAAmC,CAAa,CAAI,CAAA,EAChF,EAAC,MAAD,CAAK,UAAU,uDAA8C,KAAQ,CAAA,CAClE,IAEL,EAAC,KAAD,CAAI,UAAU,kDAA0C,CAAU,CAAA,EAElE,EAAC,IAAD,CAAG,UAAU,+DAAuD,CAAY,CAAA,EAEhF,EAAC,EAAD,CAAQ,QAAS,EAAe,QAAQ,UAAU,UAAU,OAAO,KAAK,cACrE,CACK,CAAA,CACL"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../ui/button.cjs"),t=require("../ui/dialog.cjs"),
|
|
1
|
+
"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("../ui/button.cjs"),t=require("../features/logo/index.cjs"),n=require("../ui/dialog.cjs"),r=require("../ui/input.cjs"),i=require("../ui/label.cjs"),a=require("../alert-D31c6DLq.cjs");let o=require("lucide-react"),s=require("react"),c=require("react/jsx-runtime"),l=require("zod");const u=l.z.object({email:l.z.string().min(1,`Vui lòng nhập email`).email(`Email không hợp lệ`),password:l.z.string().min(8,`Mật khẩu phải có ít nhất 8 ký tự`).max(50,`Mật khẩu không được vượt quá 50 ký tự`).regex(/[A-Z]/,`Phải có ít nhất một chữ hoa`).regex(/[a-z]/,`Phải có ít nhất một chữ thường`).regex(/[0-9]/,`Phải có ít nhất một chữ số`).regex(/[^A-Za-z0-9]/,`Phải có ít nhất một ký tự đặc biệt`),confirmPassword:l.z.string().min(1,`Vui lòng xác nhận mật khẩu`)}).refine(e=>e.password===e.confirmPassword,{message:`Mật khẩu xác nhận không khớp`,path:[`confirmPassword`]}),d=({open:l,onOpenChange:d,onRegister:f,onLogin:p,isLoading:m=!1,errorMessage:h,title:g=`Đăng ký`,subtitle:_=`Tạo tài khoản mới để bắt đầu`})=>{let v=(0,s.useId)(),y=(0,s.useId)(),b=(0,s.useId)(),[x,S]=(0,s.useState)(``),[C,w]=(0,s.useState)(``),[T,E]=(0,s.useState)(``),[D,O]=(0,s.useState)(!1),[k,A]=(0,s.useState)(!1),[j,M]=(0,s.useState)({});return(0,c.jsx)(n.Dialog,{open:l,onOpenChange:d,children:(0,c.jsxs)(n.DialogContent,{className:`max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm`,children:[(0,c.jsxs)(n.DialogHeader,{children:[(0,c.jsx)(t.LunasLogo,{variant:`horizontal`,size:`sm`,className:`mx-auto`}),(0,c.jsxs)(`div`,{className:`flex flex-col gap-0.5`,children:[(0,c.jsx)(n.DialogTitle,{children:g}),(0,c.jsx)(n.DialogDescription,{children:_})]})]}),(0,c.jsxs)(`form`,{onSubmit:async e=>{e.preventDefault();let t=u.safeParse({email:x,password:C,confirmPassword:T});if(!t.success){let e=t.error.flatten().fieldErrors;M({email:e.email?.[0],password:e.password?.[0],confirmPassword:e.confirmPassword?.[0]});return}M({}),await f(x,C)},noValidate:!0,className:`flex flex-col gap-4`,children:[h&&(0,c.jsx)(a.t,{variant:`destructive`,children:(0,c.jsx)(a.n,{children:h})}),(0,c.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,c.jsx)(i.Label,{htmlFor:v,children:`Email`}),(0,c.jsx)(r.Input,{id:v,type:`email`,autoComplete:`email`,placeholder:`you@example.com`,value:x,onChange:e=>S(e.target.value),"aria-invalid":!!j.email,disabled:m}),j.email&&(0,c.jsx)(`p`,{className:`text-destructive text-xs`,children:j.email})]}),(0,c.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,c.jsx)(i.Label,{htmlFor:y,children:`Mật khẩu`}),(0,c.jsxs)(`div`,{className:`relative`,children:[(0,c.jsx)(r.Input,{id:y,type:D?`text`:`password`,autoComplete:`new-password`,placeholder:`••••••••`,value:C,onChange:e=>w(e.target.value),"aria-invalid":!!j.password,disabled:m,className:`pr-10`}),(0,c.jsx)(`button`,{type:`button`,className:`absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground`,onMouseDown:e=>{e.preventDefault(),O(e=>!e)},"aria-label":D?`Ẩn mật khẩu`:`Hiện mật khẩu`,children:D?(0,c.jsx)(o.EyeOff,{size:16}):(0,c.jsx)(o.Eye,{size:16})})]}),j.password?(0,c.jsx)(`p`,{className:`text-destructive text-xs`,children:j.password}):(0,c.jsx)(`p`,{className:`text-muted-foreground text-xs`,children:`Tối thiểu 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt.`})]}),(0,c.jsxs)(`div`,{className:`flex flex-col gap-1.5`,children:[(0,c.jsx)(i.Label,{htmlFor:b,children:`Xác nhận mật khẩu`}),(0,c.jsxs)(`div`,{className:`relative`,children:[(0,c.jsx)(r.Input,{id:b,type:k?`text`:`password`,autoComplete:`new-password`,placeholder:`••••••••`,value:T,onChange:e=>E(e.target.value),"aria-invalid":!!j.confirmPassword,disabled:m,className:`pr-10`}),(0,c.jsx)(`button`,{type:`button`,className:`absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground`,onMouseDown:e=>{e.preventDefault(),A(e=>!e)},"aria-label":k?`Ẩn mật khẩu`:`Hiện mật khẩu`,children:k?(0,c.jsx)(o.EyeOff,{size:16}):(0,c.jsx)(o.Eye,{size:16})})]}),j.confirmPassword&&(0,c.jsx)(`p`,{className:`text-destructive text-xs`,children:j.confirmPassword})]}),(0,c.jsx)(e.Button,{type:`submit`,isLoading:m,className:`w-full`,children:`Đăng ký`}),(0,c.jsx)(e.Button,{type:`button`,variant:`outline`,className:`w-full`,disabled:m,onClick:()=>d(!1),children:`Đóng hộp thoại`})]}),p&&(0,c.jsxs)(`div`,{className:`flex justify-center gap-1 text-muted-foreground text-sm`,children:[(0,c.jsx)(`span`,{children:`Đã có tài khoản?`}),(0,c.jsx)(e.Button,{variant:`link`,size:`sm`,className:`h-auto p-0`,onClick:p,children:`Đăng nhập`})]})]})})};exports.RegisterPage=d;
|
|
2
2
|
//# sourceMappingURL=RegisterPage.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegisterPage.cjs","names":["z","Dialog","DialogContent","DialogHeader","DialogTitle","DialogDescription","Alert","AlertDescription","Label","Input","EyeOff","Eye","Button"],"sources":["../../packages/components/pages/RegisterPage.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\n\nimport { Eye, EyeOff } from 'lucide-react';\nimport { z } from 'zod';\n\nimport { Alert, AlertDescription } from '../ui/alert';\nimport { Button } from '../ui/button';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog';\nimport { Input } from '../ui/input';\nimport { Label } from '../ui/label';\n\nexport interface RegisterPageProps {\n /** Controls whether the dialog is open */\n open: boolean;\n /** Called when the dialog open state changes */\n onOpenChange: (open: boolean) => void;\n /** Called when the user submits registration credentials */\n onRegister: (email: string, password: string) => Promise<void> | void;\n /** Called when the \"Đăng nhập\" link is clicked */\n onLogin?: () => void;\n /** Disables the form and shows a spinner on the submit button */\n isLoading?: boolean;\n /** Server-side error message rendered in a destructive Alert */\n errorMessage?: string;\n /** Dialog heading. Default: \"Đăng ký\" */\n title?: string;\n /** Dialog subheading. Default: \"Tạo tài khoản mới để bắt đầu\" */\n subtitle?: string;\n}\n\nconst registerSchema = z\n .object({\n email: z.string().min(1, 'Vui lòng nhập email').email('Email không hợp lệ'),\n password: z\n .string()\n .min(8, 'Mật khẩu phải có ít nhất 8 ký tự')\n .max(50, 'Mật khẩu không được vượt quá 50 ký tự')\n .regex(/[A-Z]/, 'Phải có ít nhất một chữ hoa')\n .regex(/[a-z]/, 'Phải có ít nhất một chữ thường')\n .regex(/[0-9]/, 'Phải có ít nhất một chữ số')\n .regex(/[^A-Za-z0-9]/, 'Phải có ít nhất một ký tự đặc biệt'),\n confirmPassword: z.string().min(1, 'Vui lòng xác nhận mật khẩu'),\n })\n .refine(data => data.password === data.confirmPassword, {\n message: 'Mật khẩu xác nhận không khớp',\n path: ['confirmPassword'],\n });\n\n/**\n * Modal registration dialog for the client authentication flow.\n *\n * @example\n * ```tsx\n * import { RegisterPage } from '@customafk/lunas-ui/pages/RegisterPage';\n *\n * <RegisterPage\n * open={open}\n * onOpenChange={setOpen}\n * onRegister={async (email, password) => {\n * await authService.register(email, password);\n * setOpen(false);\n * }}\n * onLogin={() => { setOpen(false); setLoginOpen(true); }}\n * />\n * ```\n */\nexport const RegisterPage = ({\n open,\n onOpenChange,\n onRegister,\n onLogin,\n isLoading = false,\n errorMessage,\n title = 'Đăng ký',\n subtitle = 'Tạo tài khoản mới để bắt đầu',\n}: RegisterPageProps) => {\n const emailId = useId();\n const passwordId = useId();\n const confirmPasswordId = useId();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [showConfirmPassword, setShowConfirmPassword] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{\n email?: string;\n password?: string;\n confirmPassword?: string;\n }>({});\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const result = registerSchema.safeParse({ email, password, confirmPassword });\n if (!result.success) {\n const errors = result.error.flatten().fieldErrors;\n setFieldErrors({\n email: errors.email?.[0],\n password: errors.password?.[0],\n confirmPassword: errors.confirmPassword?.[0],\n });\n return;\n }\n setFieldErrors({});\n await onRegister(email, password);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm\">\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{subtitle}</DialogDescription>\n </DialogHeader>\n\n <form onSubmit={handleSubmit} noValidate className=\"flex flex-col gap-4\">\n {errorMessage && (\n <Alert variant=\"destructive\">\n <AlertDescription>{errorMessage}</AlertDescription>\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={emailId}>Email</Label>\n <Input\n id={emailId}\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n value={email}\n onChange={e => setEmail(e.target.value)}\n aria-invalid={!!fieldErrors.email}\n disabled={isLoading}\n />\n {fieldErrors.email && <p className=\"text-destructive text-xs\">{fieldErrors.email}</p>}\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={passwordId}>Mật khẩu</Label>\n <div className=\"relative\">\n <Input\n id={passwordId}\n type={showPassword ? 'text' : 'password'}\n autoComplete=\"new-password\"\n placeholder=\"••••••••\"\n value={password}\n onChange={e => setPassword(e.target.value)}\n aria-invalid={!!fieldErrors.password}\n disabled={isLoading}\n className=\"pr-10\"\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground\"\n onMouseDown={e => {\n e.preventDefault();\n setShowPassword(v => !v);\n }}\n aria-label={showPassword ? 'Ẩn mật khẩu' : 'Hiện mật khẩu'}\n >\n {showPassword ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n {fieldErrors.password ? (\n <p className=\"text-destructive text-xs\">{fieldErrors.password}</p>\n ) : (\n <p className=\"text-muted-foreground text-xs\">Tối thiểu 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt.</p>\n )}\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={confirmPasswordId}>Xác nhận mật khẩu</Label>\n <div className=\"relative\">\n <Input\n id={confirmPasswordId}\n type={showConfirmPassword ? 'text' : 'password'}\n autoComplete=\"new-password\"\n placeholder=\"••••••••\"\n value={confirmPassword}\n onChange={e => setConfirmPassword(e.target.value)}\n aria-invalid={!!fieldErrors.confirmPassword}\n disabled={isLoading}\n className=\"pr-10\"\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground\"\n onMouseDown={e => {\n e.preventDefault();\n setShowConfirmPassword(v => !v);\n }}\n aria-label={showConfirmPassword ? 'Ẩn mật khẩu' : 'Hiện mật khẩu'}\n >\n {showConfirmPassword ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n {fieldErrors.confirmPassword && <p className=\"text-destructive text-xs\">{fieldErrors.confirmPassword}</p>}\n </div>\n\n <Button type=\"submit\" isLoading={isLoading} className=\"w-full\">\n Đăng ký\n </Button>\n <Button type=\"button\" variant=\"outline\" className=\"w-full\" disabled={isLoading} onClick={() => onOpenChange(false)}>\n Đóng hộp thoại\n </Button>\n </form>\n\n {onLogin && (\n <div className=\"flex justify-center gap-1 text-muted-foreground text-sm\">\n <span>Đã có tài khoản?</span>\n <Button variant=\"link\" size=\"sm\" className=\"h-auto p-0\" onClick={onLogin}>\n Đăng nhập\n </Button>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"RegisterPage.cjs","names":["z","Dialog","DialogContent","DialogHeader","LunasLogo","DialogTitle","DialogDescription","Alert","AlertDescription","Label","Input","EyeOff","Eye","Button"],"sources":["../../packages/components/pages/RegisterPage.tsx"],"sourcesContent":["'use client';\n\nimport { useId, useState } from 'react';\n\nimport { Eye, EyeOff } from 'lucide-react';\nimport { z } from 'zod';\n\nimport { Alert, AlertDescription } from '../ui/alert';\nimport { Button } from '../ui/button';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/dialog';\nimport { LunasLogo } from '../features/logo';\nimport { Input } from '../ui/input';\nimport { Label } from '../ui/label';\n\nexport interface RegisterPageProps {\n /** Controls whether the dialog is open */\n open: boolean;\n /** Called when the dialog open state changes */\n onOpenChange: (open: boolean) => void;\n /** Called when the user submits registration credentials */\n onRegister: (email: string, password: string) => Promise<void> | void;\n /** Called when the \"Đăng nhập\" link is clicked */\n onLogin?: () => void;\n /** Disables the form and shows a spinner on the submit button */\n isLoading?: boolean;\n /** Server-side error message rendered in a destructive Alert */\n errorMessage?: string;\n /** Dialog heading. Default: \"Đăng ký\" */\n title?: string;\n /** Dialog subheading. Default: \"Tạo tài khoản mới để bắt đầu\" */\n subtitle?: string;\n}\n\nconst registerSchema = z\n .object({\n email: z.string().min(1, 'Vui lòng nhập email').email('Email không hợp lệ'),\n password: z\n .string()\n .min(8, 'Mật khẩu phải có ít nhất 8 ký tự')\n .max(50, 'Mật khẩu không được vượt quá 50 ký tự')\n .regex(/[A-Z]/, 'Phải có ít nhất một chữ hoa')\n .regex(/[a-z]/, 'Phải có ít nhất một chữ thường')\n .regex(/[0-9]/, 'Phải có ít nhất một chữ số')\n .regex(/[^A-Za-z0-9]/, 'Phải có ít nhất một ký tự đặc biệt'),\n confirmPassword: z.string().min(1, 'Vui lòng xác nhận mật khẩu'),\n })\n .refine(data => data.password === data.confirmPassword, {\n message: 'Mật khẩu xác nhận không khớp',\n path: ['confirmPassword'],\n });\n\n/**\n * Modal registration dialog for the client authentication flow.\n *\n * @example\n * ```tsx\n * import { RegisterPage } from '@customafk/lunas-ui/pages/RegisterPage';\n *\n * <RegisterPage\n * open={open}\n * onOpenChange={setOpen}\n * onRegister={async (email, password) => {\n * await authService.register(email, password);\n * setOpen(false);\n * }}\n * onLogin={() => { setOpen(false); setLoginOpen(true); }}\n * />\n * ```\n */\nexport const RegisterPage = ({\n open,\n onOpenChange,\n onRegister,\n onLogin,\n isLoading = false,\n errorMessage,\n title = 'Đăng ký',\n subtitle = 'Tạo tài khoản mới để bắt đầu',\n}: RegisterPageProps) => {\n const emailId = useId();\n const passwordId = useId();\n const confirmPasswordId = useId();\n const [email, setEmail] = useState('');\n const [password, setPassword] = useState('');\n const [confirmPassword, setConfirmPassword] = useState('');\n const [showPassword, setShowPassword] = useState(false);\n const [showConfirmPassword, setShowConfirmPassword] = useState(false);\n const [fieldErrors, setFieldErrors] = useState<{\n email?: string;\n password?: string;\n confirmPassword?: string;\n }>({});\n\n const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {\n e.preventDefault();\n const result = registerSchema.safeParse({ email, password, confirmPassword });\n if (!result.success) {\n const errors = result.error.flatten().fieldErrors;\n setFieldErrors({\n email: errors.email?.[0],\n password: errors.password?.[0],\n confirmPassword: errors.confirmPassword?.[0],\n });\n return;\n }\n setFieldErrors({});\n await onRegister(email, password);\n };\n\n return (\n <Dialog open={open} onOpenChange={onOpenChange}>\n <DialogContent className=\"max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm\">\n <DialogHeader>\n <LunasLogo variant=\"horizontal\" size=\"sm\" className=\"mx-auto\" />\n <div className=\"flex flex-col gap-0.5\">\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{subtitle}</DialogDescription>\n </div>\n </DialogHeader>\n\n <form onSubmit={handleSubmit} noValidate className=\"flex flex-col gap-4\">\n {errorMessage && (\n <Alert variant=\"destructive\">\n <AlertDescription>{errorMessage}</AlertDescription>\n </Alert>\n )}\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={emailId}>Email</Label>\n <Input\n id={emailId}\n type=\"email\"\n autoComplete=\"email\"\n placeholder=\"you@example.com\"\n value={email}\n onChange={e => setEmail(e.target.value)}\n aria-invalid={!!fieldErrors.email}\n disabled={isLoading}\n />\n {fieldErrors.email && <p className=\"text-destructive text-xs\">{fieldErrors.email}</p>}\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={passwordId}>Mật khẩu</Label>\n <div className=\"relative\">\n <Input\n id={passwordId}\n type={showPassword ? 'text' : 'password'}\n autoComplete=\"new-password\"\n placeholder=\"••••••••\"\n value={password}\n onChange={e => setPassword(e.target.value)}\n aria-invalid={!!fieldErrors.password}\n disabled={isLoading}\n className=\"pr-10\"\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground\"\n onMouseDown={e => {\n e.preventDefault();\n setShowPassword(v => !v);\n }}\n aria-label={showPassword ? 'Ẩn mật khẩu' : 'Hiện mật khẩu'}\n >\n {showPassword ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n {fieldErrors.password ? (\n <p className=\"text-destructive text-xs\">{fieldErrors.password}</p>\n ) : (\n <p className=\"text-muted-foreground text-xs\">Tối thiểu 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt.</p>\n )}\n </div>\n\n <div className=\"flex flex-col gap-1.5\">\n <Label htmlFor={confirmPasswordId}>Xác nhận mật khẩu</Label>\n <div className=\"relative\">\n <Input\n id={confirmPasswordId}\n type={showConfirmPassword ? 'text' : 'password'}\n autoComplete=\"new-password\"\n placeholder=\"••••••••\"\n value={confirmPassword}\n onChange={e => setConfirmPassword(e.target.value)}\n aria-invalid={!!fieldErrors.confirmPassword}\n disabled={isLoading}\n className=\"pr-10\"\n />\n <button\n type=\"button\"\n className=\"absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground\"\n onMouseDown={e => {\n e.preventDefault();\n setShowConfirmPassword(v => !v);\n }}\n aria-label={showConfirmPassword ? 'Ẩn mật khẩu' : 'Hiện mật khẩu'}\n >\n {showConfirmPassword ? <EyeOff size={16} /> : <Eye size={16} />}\n </button>\n </div>\n {fieldErrors.confirmPassword && <p className=\"text-destructive text-xs\">{fieldErrors.confirmPassword}</p>}\n </div>\n\n <Button type=\"submit\" isLoading={isLoading} className=\"w-full\">\n Đăng ký\n </Button>\n <Button type=\"button\" variant=\"outline\" className=\"w-full\" disabled={isLoading} onClick={() => onOpenChange(false)}>\n Đóng hộp thoại\n </Button>\n </form>\n\n {onLogin && (\n <div className=\"flex justify-center gap-1 text-muted-foreground text-sm\">\n <span>Đã có tài khoản?</span>\n <Button variant=\"link\" size=\"sm\" className=\"h-auto p-0\" onClick={onLogin}>\n Đăng nhập\n </Button>\n </div>\n )}\n </DialogContent>\n </Dialog>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use client";import{Button as e}from"../ui/button.mjs";import{Dialog as
|
|
1
|
+
"use client";import{Button as e}from"../ui/button.mjs";import{LunasLogo as t}from"../features/logo/index.mjs";import{Dialog as n,DialogContent as r,DialogDescription as i,DialogHeader as a,DialogTitle as o}from"../ui/dialog.mjs";import{Input as s}from"../ui/input.mjs";import{Label as c}from"../ui/label.mjs";import{n as l,t as u}from"../alert-OD1KJTEV.mjs";import{Eye as d,EyeOff as f}from"lucide-react";import{useId as p,useState as m}from"react";import{jsx as h,jsxs as g}from"react/jsx-runtime";import{z as _}from"zod";const v=_.object({email:_.string().min(1,`Vui lòng nhập email`).email(`Email không hợp lệ`),password:_.string().min(8,`Mật khẩu phải có ít nhất 8 ký tự`).max(50,`Mật khẩu không được vượt quá 50 ký tự`).regex(/[A-Z]/,`Phải có ít nhất một chữ hoa`).regex(/[a-z]/,`Phải có ít nhất một chữ thường`).regex(/[0-9]/,`Phải có ít nhất một chữ số`).regex(/[^A-Za-z0-9]/,`Phải có ít nhất một ký tự đặc biệt`),confirmPassword:_.string().min(1,`Vui lòng xác nhận mật khẩu`)}).refine(e=>e.password===e.confirmPassword,{message:`Mật khẩu xác nhận không khớp`,path:[`confirmPassword`]}),y=({open:_,onOpenChange:y,onRegister:b,onLogin:x,isLoading:S=!1,errorMessage:C,title:w=`Đăng ký`,subtitle:T=`Tạo tài khoản mới để bắt đầu`})=>{let E=p(),D=p(),O=p(),[k,A]=m(``),[j,M]=m(``),[N,P]=m(``),[F,I]=m(!1),[L,R]=m(!1),[z,B]=m({});return h(n,{open:_,onOpenChange:y,children:g(r,{className:`max-sm:data-[state=open]:slide-in-from-bottom max-sm:data-[state=open]:zoom-in-100 max-sm:data-[state=closed]:slide-out-to-bottom max-sm:data-[state=closed]:zoom-out-100 max-sm:top-auto max-sm:right-0 max-sm:bottom-0 max-sm:left-0 max-sm:max-w-full max-sm:translate-x-0 max-sm:translate-y-0 max-sm:rounded-b-none sm:max-w-sm`,children:[g(a,{children:[h(t,{variant:`horizontal`,size:`sm`,className:`mx-auto`}),g(`div`,{className:`flex flex-col gap-0.5`,children:[h(o,{children:w}),h(i,{children:T})]})]}),g(`form`,{onSubmit:async e=>{e.preventDefault();let t=v.safeParse({email:k,password:j,confirmPassword:N});if(!t.success){let e=t.error.flatten().fieldErrors;B({email:e.email?.[0],password:e.password?.[0],confirmPassword:e.confirmPassword?.[0]});return}B({}),await b(k,j)},noValidate:!0,className:`flex flex-col gap-4`,children:[C&&h(u,{variant:`destructive`,children:h(l,{children:C})}),g(`div`,{className:`flex flex-col gap-1.5`,children:[h(c,{htmlFor:E,children:`Email`}),h(s,{id:E,type:`email`,autoComplete:`email`,placeholder:`you@example.com`,value:k,onChange:e=>A(e.target.value),"aria-invalid":!!z.email,disabled:S}),z.email&&h(`p`,{className:`text-destructive text-xs`,children:z.email})]}),g(`div`,{className:`flex flex-col gap-1.5`,children:[h(c,{htmlFor:D,children:`Mật khẩu`}),g(`div`,{className:`relative`,children:[h(s,{id:D,type:F?`text`:`password`,autoComplete:`new-password`,placeholder:`••••••••`,value:j,onChange:e=>M(e.target.value),"aria-invalid":!!z.password,disabled:S,className:`pr-10`}),h(`button`,{type:`button`,className:`absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground`,onMouseDown:e=>{e.preventDefault(),I(e=>!e)},"aria-label":F?`Ẩn mật khẩu`:`Hiện mật khẩu`,children:h(F?f:d,{size:16})})]}),z.password?h(`p`,{className:`text-destructive text-xs`,children:z.password}):h(`p`,{className:`text-muted-foreground text-xs`,children:`Tối thiểu 8 ký tự, bao gồm chữ hoa, chữ thường, số và ký tự đặc biệt.`})]}),g(`div`,{className:`flex flex-col gap-1.5`,children:[h(c,{htmlFor:O,children:`Xác nhận mật khẩu`}),g(`div`,{className:`relative`,children:[h(s,{id:O,type:L?`text`:`password`,autoComplete:`new-password`,placeholder:`••••••••`,value:N,onChange:e=>P(e.target.value),"aria-invalid":!!z.confirmPassword,disabled:S,className:`pr-10`}),h(`button`,{type:`button`,className:`absolute inset-y-0 right-0 flex items-center px-3 text-muted-foreground hover:text-foreground`,onMouseDown:e=>{e.preventDefault(),R(e=>!e)},"aria-label":L?`Ẩn mật khẩu`:`Hiện mật khẩu`,children:h(L?f:d,{size:16})})]}),z.confirmPassword&&h(`p`,{className:`text-destructive text-xs`,children:z.confirmPassword})]}),h(e,{type:`submit`,isLoading:S,className:`w-full`,children:`Đăng ký`}),h(e,{type:`button`,variant:`outline`,className:`w-full`,disabled:S,onClick:()=>y(!1),children:`Đóng hộp thoại`})]}),x&&g(`div`,{className:`flex justify-center gap-1 text-muted-foreground text-sm`,children:[h(`span`,{children:`Đã có tài khoản?`}),h(e,{variant:`link`,size:`sm`,className:`h-auto p-0`,onClick:x,children:`Đăng nhập`})]})]})})};export{y as RegisterPage};
|
|
2
2
|
//# sourceMappingURL=RegisterPage.mjs.map
|