PageSourceSearch

https://xynergy.ai/_next/static/chunks/1488-f14254ba0a9f7b8c.js

js xynergy.ai collected 2026-10-05 16:01:50 UTC 45,033 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[1488],{14560:(e,t,r)=>{r.d(t,{V:()=>C});var a=r(85047),s=r(31615),i=r(18534),n=r(21410),o=r(96968),l=r(21582),d=r(56620),c=r(42451),u=r(34961);let m={small:64,medium:128,large:256};function h({config:e,size:t="medium",className:r}){let i="number"==typeof t?t:m[t||"medium"]||m.medium,[n,o]=(0,s.useState)(null),[l,f]=(0,s.useState)(!0),[p,x]=(0,s.useState)(!1);return(0,s.useEffect)(()=>{let t=(0,c.qQ)(e,i);o(t),f(!0),x(!1);let r=new window.Image;r.onload=()=>{f(!1),x(!1)},r.onerror=()=>{f(!1),x(!0)},r.src=t},[e,i]),(0,a.jsxs)("div",{className:(0,u.cn)("flex items-center justify-center rounded-full bg-muted overflow-hidden",r),style:{width:i,height:i},children:[l&&(0,a.jsx)("div",{className:"w-full h-full bg-muted animate-pulse"}),p&&(0,a.jsx)("div",{className:"w-full h-full bg-muted flex items-center justify-center text-xs text-muted-foreground",children:"載入失敗"}),!l&&!p&&n&&(0,a.jsx)(d.default,{src:n,alt:"頭像預覽",width:i,height:i,className:"w-full h-full object-cover",unoptimized:!0})]})}let f=["short","medium","long","curly","wavy","spiky","bob","pixie","bun","ponytail","braid","bald"],p=["variant01","variant02","variant03","variant04","variant05","variant06","variant07","variant08","variant09","variant10","variant11","variant12","variant13"],x=["variant01","variant02","variant03","variant04","variant05"],v=["variant01","variant02","variant03","variant04","variant05","variant06","variant07","variant08","variant09","variant10","variant11","variant12","variant13","variant14","variant15","variant16","variant17","variant18","variant19","variant20","variant21","variant22","variant23","variant24","variant25","variant26","variant27","variant28","variant29","variant30"],b=["variant01","variant02","variant03","variant04","variant05","variant06","variant07","variant08","variant09","variant10","variant11","variant12","variant13","variant14","variant15","variant16","variant17","variant18","variant19","variant20"],y=["#FDBCB4","#F8D25C","#FFDBAC","#D08B5B","#AE5D29","#8B4513","#F4C2A1","#E6BCB5","#D4A574","#C68642"],j=["variant01","variant02","variant03","variant04","variant05","variant06","variant07","variant08","variant09","variant10","variant11"],g=["none","stubble","short","medium","long","goatee","mustache","full"];var w=r(26787),N=r(41178),k=r(51976);function C({open:e,onOpenChange:t,userId:r,initialConfig:d,onSave:c}){let{toast:u}=(0,N.dj)(),[m,S]=(0,s.useState)(d||{brows:p[0],eyes:x[0],lips:v[0],nose:b[0],hairStyle:"short",skinColor:"#FDBCB4"}),[E,T]=(0,s.useState)(!1);(0,s.useEffect)(()=>{d&&S(d)},[d]);let P=async()=>{T(!0);try{if(await (0,w.l9)(r,m))u({title:"頭像已儲存",description:"您的頭像已成功更新"}),c?.(m),t(!1);else throw Error("儲存失敗")}catch(e){u({title:"儲存失敗",description:"無法儲存頭像,請稍後再試",variant:"destructive"})}finally{T(!1)}},A=(e,t)=>{S(r=>({...r,[e]:t}))};return(0,a.jsx)(i.lG,{open:e,onOpenChange:t,children:(0,a.jsxs)(i.Cf,{className:"max-w-2xl max-h-[90vh] overflow-y-auto",children:[(0,a.jsxs)(i.c7,{children:[(0,a.jsx)(i.L3,{children:"編輯頭像"}),(0,a.jsx)(i.rr,{children:"自訂您的頭像外觀,選擇不同的特徵組合"})]}),(0,a.jsxs)("div",{className:"space-y-6 py-4",children:[(0,a.jsx)("div",{className:"flex justify-center",children:(0,a.jsx)(h,{config:m,size:"large"})}),(0,a.jsxs)("div",{className:"grid grid-cols-2 gap-4",children:[(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(o.J,{children:"眉毛(可選)"}),(0,a.jsxs)(l.l6,{value:m.brows||"random",onValueChange:e=>A("brows","random"===e?void 0:e),children:[(0,a.jsx)(l.bq,{children:(0,a.jsx)(l.yv,{placeholder:"選擇眉毛"})}),(0,a.jsxs)(l.gC,{children:[(0,a.jsx)(l.eb,{value:"random",children:"隨機"}),p.map(e=>(0,a.jsx)(l.eb,{value:e,children:I(e,"brow")},e))]})]})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(o.J,{children:"眼睛"}),(0,a.jsxs)(l.l6,{value:m.eyes,onValueChange:e=>A("eyes",e),children:[(0,a.jsx)(l.bq,{children:(0,a.jsx)(l.yv,{})}),(0,a.jsx)(l.gC,{children:x.map(e=>(0,a.jsx)(l.eb,{value:e,children:I(e,"eye")},e))})]})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(o.J,{children:"鼻子(可選)"}),(0,a.jsxs)(l.l6,{value:m.nose||"random",onValueChange:e=>A("nose","random"===e?void 0:e),children:[(0,a.jsx)(l.bq,{children:(0,a.jsx)(l.yv,{placeholder:"選擇鼻子"})}),(0,a.jsxs)(l.gC,{children:[(0,a.jsx)(l.eb,{value:"random",children:"隨機"}),b.map(e=>(0,a.jsx)(l.eb,{value:e,children:I(e,"nose")},e))]})]})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(o.J,{children:"嘴巴"}),(0,a.jsxs)(l.l6,{value:m.lips||m.mouth||"",onValueChange:e=>{A("lips",e),A("mouth",e)},children:[(0,a.jsx)(l.bq,{children:(0,a.jsx)(l.yv,{})}),(0,a.jsx)(l.gC,{children:v.map(e=>(0,a.jsx)(l.eb,{value:e,children:I(e,"lip")},e))})]})]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsx)(o.J,{children:"眼鏡(可選)"}),(0,a.jsxs)(l.l6,{value:m.glasses||"none",onValueChange:e=>A("glasses","none"===e?void 0:e),children:[(0,a.jsx)(l.bq,{children:(0,a.jsx)(l.yv,{placeholder:"選擇眼鏡"})}),(0,a.jsxs)(l.gC,{children:[(0,a.jsx)(l.eb,{value:"none",children:"無"}),j.map(e=>(0,a.jsx)(l.eb,{value:e,children:I(e,"glasses")},e))]})]})]})]}),(0,a.jsx)("div",{className:"flex justify-center",children:(0,a.jsxs)(n.$,{type:"button",variant:"outline",onClick:()=>{S({brows:p[Math.floor(Math.random()*p.length)],eyes:x[Math.floor(Math.random()*x.length)],lips:v[Math.floor(Math.random()*v.length)],nose:b[Math.floor(Math.random()*b.length)],glasses:Math.random()>.5?j[Math.floor(Math.random()*j.length)]:void 0,hairStyle:f[Math.floor(Math.random()*f.length)],skinColor:y[Math.floor(Math.random()*y.length)],beard:Math.random()>.5?g[Math.floor(Math.random()*g.length)]:void 0})},className:"gap-2",children:[(0,a.jsx)(k.A,{className:"h-4 w-4"}),"隨機生成"]})})]}),(0,a.jsxs)(i.Es,{children:[(0,a.jsx)(n.$,{type:"button",variant:"outline",onClick:()=>t(!1),disabled:E,children:"取消"}),(0,a.jsx)(n.$,{type:"button",onClick:P,disabled:E,children:E?"儲存中...":"儲存"})]})]})})}function I(e,t){if(e.startsWith("variant")){let r=e.replace("variant","");return`${({brow:"眉毛",eye:"眼睛",nose:"鼻子",lip:"嘴巴",glasses:"眼鏡"})[t||""]||"樣式"} ${r}`}let r=t?`${t}:`:"";return({})[`${r}${e}`]||e}},26787:(e,t,r)=>{r.d(t,{Sk:()=>c,l9:()=>m});var a=r(24571),s=r(76458),i=r(18654),n=r(25642),o=r(42451),l=r(65834);let d=new Map;async function c(e){if(!s.db)return(0,i.vV)("Firebase 尚未初始化"),null;let t=d.get(e);if(t&&Date.now()-t.timestamp<3e5)return(0,i.fH)("從快取讀取頭像配置"),t.config;try{let t=(0,a.doc)(s.db,"userAvatars",e),r=await (0,a.getDoc)(t);if(!r.exists())return null;let i={...r.data(),userId:e},o=n.y.parse(i);if(o.avatarConfig)return d.set(e,{config:o.avatarConfig,timestamp:Date.now()}),o.avatarConfig;return null}catch(e){return(0,i.vV)("讀取頭像配置失敗:",e),null}}async function u(e,t,r){if(!s.db)return(0,i.vV)("Firebase 尚未初始化"),!1;try{let n=(0,a.doc)(s.db,"userAvatars",e),o=function(e){if(!e)return;let t={};return void 0!==e.brows&&(t.brows=e.brows),void 0!==e.eyes&&(t.eyes=e.eyes),void 0!==e.lips&&(t.lips=e.lips),void 0!==e.mouth&&(t.mouth=e.mouth),void 0!==e.nose&&(t.nose=e.nose),void 0!==e.glasses&&(t.glasses=e.glasses),void 0!==e.hairStyle&&(t.hairStyle=e.hairStyle),void 0!==e.skinColor&&(t.skinColor=e.skinColor),void 0!==e.beard&&(t.beard=e.beard),!t.eyes&&e.eyes&&(t.eyes=e.eyes),t.lips||t.mouth||(e.lips?t.lips=e.lips:e.mouth&&(t.mouth=e.mouth)),t}(r),c={avatarUrl:t,avatarConfig:o,createdAt:(0,a.serverTimestamp)(),updatedAt:(0,a.serverTimestamp)()},u=await (0,a.getDoc)(n);return u.exists()&&(c.createdAt=u.data().createdAt),await (0,a.setDoc)(n,c,{merge:!0}),r&&d.set(e,{config:r,timestamp:Date.now()}),(0,l.P_)(t).catch(e=>{(0,i.vV)("緩存頭像圖片失敗:",e)}),(0,i.fH)("頭像 URL 已儲存"),!0}
1catch(e){return(0,i.vV)("儲存頭像 URL 失敗:",e),!1}}async function m(e,t){return u(e,(0,o.qQ)(t),t)}},28723:(e,t,r)=>{r.d(t,{Nv:()=>m,TX:()=>u,co:()=>d,tD:()=>c,xX:()=>l});var a=r(24571),s=r(76458),i=r(64685),n=r(18654);function o(e){if(null==e)return null;if(e instanceof Date)return e;if("function"==typeof e.toDate)return e.toDate();let t=e.seconds??e._seconds;if("number"==typeof t)return new Date(1e3*t);if("string"==typeof e){let t=new Date(e);return Number.isNaN(t.getTime())?null:t}return null}async function l(e){if(!s.db||!e)return null;try{let t=await (0,a.getDoc)((0,a.doc)(s.db,s.USERS,e));if(!t.exists())return{nextPromptAt:null,dismissedCount:0};let r=t.data().platformReviewPrompt;if(!r)return{nextPromptAt:null,dismissedCount:0};return{nextPromptAt:o(r.nextPromptAt),dismissedCount:"number"==typeof r.dismissedCount?r.dismissedCount:0}}catch(e){return(0,n.vV)("取得 platformReviewPrompt 狀態失敗:",e),null}}async function d(e,t=i.Cc){if(!s.db||!e)return;let r=new Date;r.setDate(r.getDate()+t);let n=await l(e),o=(n?.dismissedCount??0)+1;await (0,a.mZ)((0,a.doc)(s.db,s.USERS,e),{platformReviewPrompt:{nextPromptAt:r,dismissedCount:o},updatedAt:(0,a.serverTimestamp)()})}async function c(e){s.db&&e&&await (0,a.mZ)((0,a.doc)(s.db,s.USERS,e),{platformReviewPrompt:(0,a.CL)(),updatedAt:(0,a.serverTimestamp)()})}async function u(e){if(!s.db||!e)return null;try{let t=await (0,a.getDoc)((0,a.doc)(s.db,s.USERS,e));if(!t.exists())return null;let r=o(t.data().createdAt);if(!r)return null;let i=Date.now()-r.getTime();return Math.floor(i/864e5)}catch(e){return(0,n.vV)("取得帳號年齡失敗:",e),null}}function m(e){return!e?.nextPromptAt||Date.now()>=e.nextPromptAt.getTime()}},35346:(e,t,r)=>{r.d(t,{SD:()=>i});let a=["藍色","紅色","綠色","黃色","紫色","橙色","粉色","青色","棕色","灰色","黑色","白色","金色","銀色","彩虹"],s=["兔子","狐狸","小貓","小狗","小熊","小鳥","金魚","小鹿","野狼","老虎","獅子","獵豹","大象","小馬","小羊","小牛","小豬","老鼠","猴子","小雞","小鴨","小鵝","老鷹","飛鴿"];function i(){let e=a[Math.floor(Math.random()*a.length)],t=s[Math.floor(Math.random()*s.length)];return`${e}${t}`}},36506:(e,t,r)=>{r.d(t,{_:()=>p});var a=r(85047),s=r(31615),i=r(21410),n=r(96968),o=r(41178),l=r(18654),d=r(8947),c=r(93315),u=r(11787),m=r(48407),h=r(54296),f=r(34961);function p({userId:e,currentPhotoUrl:t,oauthPhotoUrl:r,getToken:x,onPhotoChange:v,className:b,compact:y=!1}){let{toast:j}=(0,o.dj)(),g=(0,s.useRef)(null),[w,N]=(0,s.useState)(!1),k=t?(0,c.ZF)(t):r||null,C=async r=>{if(r){N(!0);try{let{downloadURL:a}=await (0,d.ng)({file:r,userId:e,oldPhotoUrl:t,getToken:x});await v(a),j({title:"照片已更新",description:"大頭照已成功上傳"})}catch(e){(0,l.vV)("上傳使用者照片失敗",e),j({title:"上傳失敗",description:e instanceof Error?e.message:"請稍後再試",variant:"destructive"})}finally{N(!1),g.current&&(g.current.value="")}}},I=async()=>{if(r){N(!0);try{let a=await (0,d.lo)(r,e,x);t&&t!==a&&await (0,d.FJ)(t,e,x),await v(a),j({title:"已套用目前照片",description:"已將登入帳號照片儲存為大頭照"})}catch(e){(0,l.vV)("匯入 OAuth 頭像失敗",e),j({title:"套用失敗",description:e instanceof Error?e.message:"請稍後再試",variant:"destructive"})}finally{N(!1)}}},S=async()=>{if(!t)return void await v(null);N(!0);try{await (0,d.FJ)(t,e,x),await v(null),j({title:"已移除照片",description:"將改用虛擬頭像或其他預設顯示"})}catch(e){(0,l.vV)("移除使用者照片失敗",e),j({title:"移除失敗",description:e instanceof Error?e.message:"請稍後再試",variant:"destructive"})}finally{N(!1)}};return(0,a.jsxs)("div",{className:(0,f.cn)("flex flex-col gap-3",b),children:[!y&&(0,a.jsx)(n.J,{children:"真實照片"}),(0,a.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[k?(0,a.jsx)("img",{src:k,alt:"大頭照預覽",className:"h-20 w-20 rounded-full object-cover border border-border/60"}):(0,a.jsx)("div",{className:"flex h-20 w-20 items-center justify-center rounded-full border border-dashed border-border bg-muted/40 text-xs text-muted-foreground",children:"尚無照片"}),(0,a.jsxs)("div",{className:"flex flex-col gap-2",children:[(0,a.jsx)("input",{ref:g,type:"file",accept:"image/jpeg,image/png,image/webp",className:"hidden",onChange:e=>void C(e.target.files?.[0])}),(0,a.jsxs)(i.$,{type:"button",variant:"outline",size:"sm",disabled:w,onClick:()=>g.current?.click(),className:"rounded-xl",children:[w?(0,a.jsx)(u.A,{className:"h-4 w-4 animate-spin"}):(0,a.jsx)(m.A,{className:"h-4 w-4"}),(0,a.jsx)("span",{className:"ml-2",children:"上傳照片"})]}),r&&!t?(0,a.jsx)(i.$,{type:"button",variant:"secondary",size:"sm",disabled:w,onClick:()=>void I(),className:"rounded-xl",children:"使用目前登入照片"}):null,t?(0,a.jsxs)(i.$,{type:"button",variant:"ghost",size:"sm",disabled:w,onClick:()=>void S(),className:"rounded-xl text-muted-foreground",children:[(0,a.jsx)(h.A,{className:"h-4 w-4"}),(0,a.jsx)("span",{className:"ml-2",children:"移除照片"})]}):null]})]}),!y&&(0,a.jsx)("p",{className:"text-xs text-muted-foreground",children:"支援 JPG/PNG/WEBP,最大 5MB,建議至少 200\xd7200。"})]})}},39049:(e,t,r)=>{r.d(t,{I:()=>p});var a=r(85047),s=r(31615),i=r(56620),n=r(24571),o=r(10250),l=r(11787),d=r(42165),c=r(76842),u=r(85183),m=r(2
11410),h=r(41178),f=r(76458);function p({uid:e,initiallyLinked:t,onLinkedChange:r}){let{toast:x}=(0,h.dj)(),[v,b]=(0,s.useState)(t),[y,j]=(0,s.useState)(null),[g,w]=(0,s.useState)(0),[N,k]=(0,s.useState)(!1),[C,I]=(0,s.useState)(!1),S=(0,s.useRef)(null),E=(0,s.useRef)(!0),T=(0,s.useRef)(t);(0,s.useEffect)(()=>{if(!e||!f.db)return;E.current=!0,T.current=t;let a=(0,n.doc)(f.db,"users",e);return(0,n.aQ)(a,e=>{let t=e.data(),a="string"==typeof t?.lineUserId&&t.lineUserId.length>0,s=T.current;if(s===a)return;let i=E.current;i&&(E.current=!1),T.current=a,b(a),r?.(a),i||s||!a||(j(null),x({title:"✅ LINE 綁定成功",description:"之後重要提醒會推到 LINE"}))})},[e,t,r,x]),(0,s.useEffect)(()=>{if(!y?.expiresAt){S.current&&clearInterval(S.current);return}let e=()=>{let e=Math.max(0,Math.floor((new Date(y.expiresAt).getTime()-Date.now())/1e3));w(e),0===e&&j(null)};return e(),S.current=setInterval(e,1e3),()=>{S.current&&clearInterval(S.current)}},[y]);let P=(0,s.useCallback)(async()=>{if(!f.auth?.currentUser)return void x({title:"尚未登入"});k(!0);try{let e=await f.auth.currentUser.getIdToken(),t=await fetch("/api/line/binding/create",{method:"POST",headers:{Authorization:`Bearer ${e}`}}),r=await t.json();if(!t.ok||!r.ok)throw Error(r.error||`HTTP ${t.status}`);j(r)}catch(e){x({title:"取得綁定碼失敗",description:e instanceof Error?e.message:"請稍後再試"})}finally{k(!1)}},[x]),A=(0,s.useCallback)(async()=>{if(y?.code)try{await navigator.clipboard.writeText(y.code),x({title:"已複製綁定碼"})}catch{x({title:"複製失敗,請手動選取"})}},[y,x]),_=(0,s.useCallback)(async()=>{if(f.auth?.currentUser&&confirm("確定要解除 LINE 綁定?之後將不會收到 LINE 推播")){I(!0);try{let e=await f.auth.currentUser.getIdToken(),t=await fetch("/api/line/binding/delete",{method:"POST",headers:{Authorization:`Bearer ${e}`}});if(!t.ok){let e=await t.json().catch(()=>({}));throw Error(e.error||`HTTP ${t.status}`)}x({title:"已解除綁定"})}catch(e){x({title:"解除綁定失敗",description:e instanceof Error?e.message:"請稍後再試"})}finally{I(!1)}}},[x]);return v?(0,a.jsx)("div",{className:"rounded-md border p-4 space-y-3",children:(0,a.jsxs)("div",{className:"flex items-center justify-between gap-2",children:[(0,a.jsxs)("div",{className:"flex items-start gap-2",children:[(0,a.jsx)(o.A,{className:"h-5 w-5 text-green-600 mt-0.5"}),(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"font-medium",children:"LINE 已綁定"}),(0,a.jsx)("p",{className:"text-sm text-muted-foreground",children:"重要提醒會推送到您的 LINE"})]})]}),(0,a.jsx)(m.$,{type:"button",variant:"outline",size:"sm",onClick:_,disabled:C,children:C?(0,a.jsx)(l.A,{className:"h-4 w-4 animate-spin"}):"解除綁定"})]})}):y?(0,a.jsxs)("div",{className:"rounded-md border p-4 space-y-4",children:[(0,a.jsxs)("div",{className:"flex items-start justify-between",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"font-medium",children:"綁定 LINE(步驟 2/2)"}),(0,a.jsxs)("p",{className:"text-sm text-muted-foreground mt-0.5",children:["用 LINE 掃下方 QR Code 加好友,然後傳訊息「",(0,a.jsx)("span",{className:"font-mono",children:y.code}),"」給機器人。"]})]}),(0,a.jsx)("button",{type:"button",onClick:()=>j(null),className:"text-muted-foreground hover:text-foreground","aria-label":"取消",children:(0,a.jsx)(d.A,{className:"h-4 w-4"})})]}),(0,a.jsxs)("div",{className:"flex flex-col sm:flex-row gap-4 items-center sm:items-start",children:[y.qrUrl&&(0,a.jsx)("div",{className:"shrink-0",children:(0,a.jsx)(i.default,{src:y.qrUrl,alt:"LINE 加好友 QR Code",width:200,height:200,unoptimized:!0,className:"rounded-md border"})}),(0,a.jsxs)("div",{className:"flex-1 space-y-3 w-full",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"text-xs text-muted-foreground mb-1",children:"綁定碼"}),(0,a.jsxs)("div",{className:"flex items-center gap-2",children:[(0,a.jsx)("code",{className:"text-2xl font-mono font-bold tracking-wider",children:y.code}),(0,a.jsx)(m.$,{type:"button",variant:"ghost",size:"sm",onClick:A,className:"h-8 w-8 p-0","aria-label":"複製",children:(0,a.jsx)(c.A,{className:"h-4 w-4"})})]})]}),(0,a.jsxs)("div",{className:"text-xs text-muted-foreground",children:[(0,a.jsxs)("p",{children:["有效期間:",(0,a.jsxs)("span",{className:"font-mono",children:[Math.floor(g/60),":",String(g%60).padStart(2,"0")]})]}),y.oaBasicId&&(0,a.jsxs)("p",{children:["官方帳號:",(0,a.jsx)("span",{className:"font-mono",children:y.oaBasicId})]})]}),y.addFriendUrl&&(0,a.jsx)(m.$,{asChild:!0,variant:"outline",size:"sm",className:"w-full sm:w-auto",children:(0,a.jsx)("a",{href:y.addFriendUrl,target:"_blank",rel:"noreferrer",children:"在 LINE App 開啟"})})]})]}),(0,a.jsx)("p",{className:"text-xs text-muted-foreground border-t pt-2",children:"加好友後傳送綁定碼,這個畫面會自動跳到「已綁定」。"})]}):(0,a.jsxs)("div",{className:"rounded-md border p-4 space-y-3",children:[(0,a.jsxs)("div",{children:[(0,a.jsx)("p",{className:"font-medium",children:"綁定 LINE 接收通知"}),(0,a.jsx)("p",{className:"text-sm text-muted-foreground",children:"重要提醒(目標進度、付款、預約等)會推送到您的 LINE"})]}),(0,a.jsx)(m.$,{type:"button",variant:"default",size:"sm",onClick:P,disabled:N,children:N?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(l.A,{className:"h-4 w-4 mr-2 animate-spin"}),"產生綁定碼..."]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(u.A,{className:"h-4 w-4 mr-2"}),"取得綁定碼"]})})]})}},52204:(e,t,r)=>{async function a(e,t,r){return fetch(t,{...r,headers:{Authorization:`Bearer ${e}`,"Content-Type":"application/json",...r?.headers}})}async function s(e){try{let t=await e.json();if("string"==typeof t.error)return t.error}catch{}return`HTTP ${e.status}: ${e.statusText}`}async function i(e){let t=await a(e,"/api/reviews/platform/me");
1if(!t.ok)throw Error(await s(t));return t.json()}async function n(e,t){let r=await a(e,"/api/reviews/platform",{method:"POST",body:JSON.stringify(t)});if(!r.ok)throw Error(await s(r))}async function o(e,t){let r=await a(e,"/api/reviews/platform/me",{method:"PATCH",body:JSON.stringify(t)});if(!r.ok)throw Error(await s(r))}async function l(e){let t=await a(e,"/api/reviews/platform/me",{method:"POST"});if(!t.ok)throw Error(await s(t))}r.d(t,{_D:()=>i,b2:()=>l,nX:()=>o,yO:()=>n})},54938:(e,t,r)=>{r.d(t,{DW:()=>o,Fx:()=>c,Gx:()=>m,WN:()=>d,Xl:()=>l,ni:()=>n,yE:()=>u});var a=r(15418);let s=a.Yj().min(1,"請輸入電子郵件").email("電子郵件格式不正確"),i=a.Yj().min(8,"密碼至少需要 8 個字元").regex(/[A-Z]/,"密碼需包含至少一個大寫字母").regex(/[a-z]/,"密碼需包含至少一個小寫字母").regex(/[0-9]/,"密碼需包含至少一個數字").regex(/[^A-Za-z0-9]/,"密碼需包含至少一個特殊字元"),n=a.Ik({email:s,password:i,rememberEmail:a.zM()}),o=a.Ik({email:s}),l=a.Ik({email:s}),d=a.Ik({phoneNumber:a.Yj().min(1,"請輸入手機號碼"),smsCode:a.Yj().length(6,"請輸入 6 位數驗證碼"),newPassword:i,confirmPassword:a.Yj().min(1,"請再次輸入新密碼")}).refine(e=>e.newPassword===e.confirmPassword,{message:"新密碼與確認密碼不符",path:["confirmPassword"]});a.Ik({code:a.Yj().length(6,"請輸入 6 碼驗證碼")});let c=a.Ik({name:a.Yj().optional(),email:a.Yj().email("電子郵件格式不正確").or(a.eu("")).optional(),birthday:a.Yj().optional(),gender:a.k5(["","male","female"]).optional()}),u=a.Ik({password:a.Yj().min(1,"請輸入目前密碼")}),m=a.Ik({code:a.Yj().length(6,"請輸入 6 碼驗證碼")})},64685:(e,t,r)=>{r.d(t,{Cc:()=>s,Lb:()=>i,k0:()=>a});let a=7,s=10,i=500},65396:(e,t,r)=>{r.d(t,{e:()=>c});var a=r(85047),s=r(56620),i=r(21410),n=r(866),o=r(97040),l=r(75822),d=r(34961);function c({enrollment:e,className:t,startLabel:r="綁定驗證器 App"}){let{step:u,secret:m,qrDataUrl:h,loading:f,hasPassword:p,reauthForm:x,totpVerifyForm:v,totpVerifyCode:b,emailVerified:y,start:j,cancel:g,submitReauth:w,submitCode:N}=e;return y?"idle"===u?(0,a.jsx)(i.$,{type:"button",variant:"outline",className:(0,d.cn)("rounded-xl",t),onClick:()=>j(),children:r}):"reauth"===u?(0,a.jsx)(o.lV,{...x,children:(0,a.jsxs)("form",{onSubmit:x.handleSubmit(e=>void w(e)),className:(0,d.cn)("space-y-3",t),children:[p?(0,a.jsx)(o.zB,{control:x.control,name:"password",render:({field:e})=>(0,a.jsxs)(o.eI,{children:[(0,a.jsx)(o.lR,{children:"請輸入目前密碼以繼續"}),(0,a.jsx)(o.MJ,{children:(0,a.jsx)(n.p,{type:"password",placeholder:"目前密碼",autoComplete:"current-password",className:"rounded-xl",...e})}),(0,a.jsx)(o.C5,{})]})}):(0,a.jsx)("p",{className:"text-sm text-muted-foreground",children:"將開啟彈出視窗請您重新登入以驗證身份。"}),(0,a.jsxs)("div",{className:"flex gap-2",children:[(0,a.jsx)(i.$,{type:"submit",disabled:f,className:"rounded-xl",children:f?"驗證中...":"繼續"}),(0,a.jsx)(i.$,{type:"button",variant:"outline",className:"rounded-xl",onClick:g,children:"取消"})]})]})}):"qrcode"===u&&m&&h?(0,a.jsx)(o.lV,{...v,children:(0,a.jsxs)("form",{onSubmit:v.handleSubmit(e=>void N(e)),className:(0,d.cn)("space-y-4",t),children:[(0,a.jsx)("p",{className:"text-sm",children:"請使用驗證器 App 掃描下方 QR Code,或手動輸入密鑰。"}),(0,a.jsx)("div",{className:"flex justify-center",children:(0,a.jsx)(s.default,{src:h,alt:"TOTP QR Code",width:160,height:160,className:"h-40 w-40 rounded border object-contain",unoptimized:!0})}),(0,a.jsxs)("p",{className:"break-all text-xs text-muted-foreground",children:["密鑰:",m.secretKey]}),(0,a.jsx)(o.zB,{control:v.control,name:"code",render:({field:e})=>(0,a.jsxs)(o.eI,{children:[(0,a.jsx)(o.lR,{children:"輸入 App 顯示的 6 碼"}),(0,a.jsx)("div",{className:"flex justify-center",children:(0,a.jsx)(o.MJ,{children:(0,a.jsx)(l.UV,{maxLength:6,value:e.value,onChange:e.onChange,children:(0,a.jsx)(l.NV,{className:"gap-1",children:[0,1,2,3,4,5].map(e=>(0,a.jsx)(l.sF,{index:e},e))})})})}),(0,a.jsx)(o.C5,{})]})}),(0,a.jsxs)("div",{className:"flex gap-2",children:[(0,a.jsx)(i.$,{type:"submit",disabled:f||6!==b.length,className:"rounded-xl bg-gradient-to-r from-blue-500 to-purple-500 text-white",children:f?"綁定中...":"完成綁定"}),(0,a.jsx)(i.$,{type:"button",variant:"outline",className:"rounded-xl",onClick:g,children:"取消"})]})]})}):null:(0,a.jsx)("div",{className:(0,d.cn)("space-y-2 rounded-xl border border-amber-200 bg-amber-50 p-3 text-sm dark:border-amber-800 dark:bg-amber-900/20",t),children:(0,a.jsx)("p",{className:"text-amber-900 dark:text-amber-100",children:"請先完成 Email 驗證後再綁定驗證器。可稍後到設定頁操作,或略過此步驟。"})})}},75822:(e,t,r)=>{r.d(t,{NV:()=>d,UV:()=>l,sF:()=>c});var a=r(85047),s=r(31615),i=r(36587),n=r(57694),o=r(34961);let l=s.forwardRef(({className:e,containerClassName:t,...r},s)=>(0,a.jsx)(i.wE,{ref:s,containerClassName:(0,o.cn)("flex items-center gap-2 has-[:disabled]:opacity-50",t),className:(0,o.cn)("disabled:cursor-not-allowed",e),...r}));l.displayName="InputOTP";let d=s.forwardRef(({className:e,...t},r)=>(0,a.jsx)("div",{ref:r,className:(0,o.cn)("flex items-center",e),...t}));d.displayName="InputOTPGroup";
1let c=s.forwardRef(({index:e,className:t,...r},n)=>{let{char:l,hasFakeCaret:d,isActive:c}=s.useContext(i.dK).slots[e];return(0,a.jsxs)("div",{ref:n,className:(0,o.cn)("relative flex h-10 w-10 items-center justify-center border-y border-r border-input text-sm transition-all first:rounded-l-md first:border-l last:rounded-r-md",c&&"z-10 ring-2 ring-ring ring-offset-background",t),...r,children:[l,d&&(0,a.jsx)("div",{className:"pointer-events-none absolute inset-0 flex items-center justify-center",children:(0,a.jsx)("div",{className:"h-4 w-px animate-caret-blink bg-foreground duration-1000"})})]})});c.displayName="InputOTPSlot",s.forwardRef(({...e},t)=>(0,a.jsx)("div",{ref:t,role:"separator",...e,children:(0,a.jsx)(n.A,{})})).displayName="InputOTPSeparator"},78139:(e,t,r)=>{r.d(t,{m:()=>p});var a=r(31615),s=r(73521),i=r(41178),n=r(18654);function o(e){if(e)return e instanceof Error?{name:e.name,message:e.message,stack:e.stack}:{message:String(e)}}let l={debug(e,t){(0,n.MD)(e,t)},info(e,t){(0,n.fH)(e,t)},warn(e,t,r){(0,n.JE)(e,{...t??{},error:o(r)})},error(e,t,r){(0,n.vV)(e,{...t??{},error:o(r)})},fatal(e,t,r){(0,n.vV)(e,{...t??{},error:o(r),level:"fatal"})}};function d(e,t=Date.now()){if(e.revokedAt){let r=Date.parse(e.revokedAt);if(Number.isFinite(r)&&r<=t)return!1}if(e.startsAt){let r=Date.parse(e.startsAt);if(Number.isFinite(r)&&t<r)return!1}if(e.expiresAt){let r=Date.parse(e.expiresAt);if(Number.isFinite(r)&&t>r)return!1}return!0}var c=r(15418),u=r(83392);let m=c.Ik({uid:c.Yj(),email:c.Yj(),displayName:c.Yj().optional()}),h=c.Ik({id:c.Yj(),ownerId:c.Yj().optional(),recipientUid:c.Yj(),resourceKey:c.Yj(),permission:u.qi,startsAt:c.Yj().nullable().optional(),expiresAt:c.Yj().nullable().optional(),revokedAt:c.Yj().nullable().optional(),createdAt:c.Yj().nullable().optional(),recipientEmail:c.Yj().nullable().optional(),recipientLabel:c.Yj().nullable().optional(),ownerEmail:c.Yj().nullable().optional(),ownerLabel:c.Yj().nullable().optional()}),f=c.Ik({outbound:c.YO(h),inbound:c.YO(h)});function p(){let{user:e}=(0,s.A)(),{toast:t}=(0,i.dj)(),[r,n]=(0,a.useState)([]),[o,u]=(0,a.useState)([]),[h,p]=(0,a.useState)(!1),[x,v]=(0,a.useState)(null),[b,y]=(0,a.useState)(null),j=(0,a.useCallback)(async e=>({Authorization:`Bearer ${e}`}),[]),g=(0,a.useCallback)(async()=>{if(e){p(!0);try{let t=await e.getIdToken(),r=await fetch("/api/me/resource-shares",{headers:await j(t)});if(!r.ok)throw Error("載入分享設定失敗");let a=await r.json(),s=f.safeParse(a);if(!s.success)throw Error("回應格式錯誤");n(s.data.outbound),u(s.data.inbound)}catch(e){t({title:"載入失敗",description:e instanceof Error?e.message:String(e),variant:"destructive"})}finally{p(!1)}}},[e,t,j]);(0,a.useEffect)(()=>{e?.uid&&g()},[e?.uid,g]);let w=(0,a.useMemo)(()=>r.filter(d),[r]),N=(0,a.useMemo)(()=>o.filter(d),[o]),k=(0,a.useCallback)(async(t,r,a,s)=>{if(!e)return{ok:!1,error:"未登入"};let i={recipientUid:t,resourceKey:r,permission:a};s&&(i.expiresAtIso=s);let n=await e.getIdToken(),o=await fetch("/api/me/resource-shares",{method:"POST",headers:{...await j(n),"Content-Type":"application/json"},body:JSON.stringify(i)});if(o.ok)return{ok:!0};let l=await o.json().catch(()=>({}));return{ok:!1,error:"string"==typeof l.error?l.error:`HTTP ${o.status}`}},[e,j]),C=(0,a.useCallback)(async(r,a)=>{if(e){v(r);try{let s=await e.getIdToken(),i=await fetch(`/api/me/resource-shares/${encodeURIComponent(r)}`,{method:"PATCH",headers:{...await j(s),"Content-Type":"application/json"},body:JSON.stringify({permission:a})});if(!i.ok){let e=await i.json().catch(()=>({})),t="string"==typeof e.error?e.error:`HTTP ${i.status}`;throw Error(t)}t({title:"已更新權限"}),await g()}catch(a){let e=a instanceof Error?a:Error(String(a));l.error("updateSharePermission failed",{endpoint:"/api/me/resource-shares/[shareId]",shareId:r},e),t({title:"更新失敗",description:e.message,variant:"destructive"})}finally{v(null)}}},[e,t,j,g]),I=(0,a.useCallback)(async(r,a)=>{if(e){v(r);try{let s=await e.getIdToken(),i=await fetch(`/api/me/resource-shares/${encodeURIComponent(r)}`,{method:"PATCH",headers:{...await j(s),"Content-Type":"application/json"},body:JSON.stringify({expiresAtIso:a})});if(!i.ok){let e=await i.json().catch(()=>({})),t="string"==typeof e.error?e.error:`HTTP ${i.status}`;throw Error(t)}t({title:"已更新有效期限"}),await g()}catch(a){let e=a instanceof Error?a:Error(String(a));l.error("updateShareExpiry failed",{endpoint:"/api/me/resource-shares/[shareId]",shareId:r},e),t({title:"更新失敗",description:e.message,variant:"destructive"})}finally{v(null)}}},[e,t,j,g]),S=(0,a.useCallback)(async r=>{if(e){y(r);try{let a=await e.getIdToken(),s=await fetch(`/api/me/resource-shares/${encodeURIComponent(r)}`,{method:"PATCH",headers:{...await j(a),"Content-Type":"application/json"},body:JSON.stringify({recipientDismissedAtIso:new Date().toISOString()})});if(!s.ok){let e=await s.json().catch(()=>({})),t="string"==typeof e.error?e.error:`HTTP ${s.status}`;throw Error(t)}t({title:"已從清單移除"}),await g()}catch(e){t({title:"移除失敗",description:e instanceof Error?e.message:String(e),variant:"destructive"})}finally{y(null)}}},[e,t,j,g]),E=(0,a.useCallback)(async r=>{if(e)try{let a=await e.getIdToken();if(!(await fetch(`/api/me/resource-shares/${encodeURIComponent(r)}`,{method:"DELETE",headers:await j(a)})).ok)throw Error("撤銷失敗");t({title:"已撤銷分享"}),await g()}catch(e){t({title:"操作失敗",description:e instanceof Error?e.message:String(e),variant:"destructive"})}},[e,t,j,g]);return{outbound:r,inbound:o,activeOutbound:w,activeInbound:N,activeShares:w,loading:h,loadAll:g,createShare:k,revokeShare:E,dismissInboundShare:S,updateSharePermission:C,updateShareExpiry:I,permissionUpdatingShareId:x,dismissingShareId:b,lookupUserByEmail:(0,a.useCallback)(async t=>{if(!e)return{ok:!1,error:"未登入"};let r=t.trim().toLowerCase();try{let t=await e.getIdToken(),a=await fetch("/api/me/lookup-user",{method:"POST",headers:{...await j(t),"Content-Type":"application/json"},body:JSON.stringify({email:r})}),s=await a.json().catch(()=>({}));if(429===a.status)return{ok:!1,error:"查詢次數過多,請稍後再試"};
1if(!a.ok){let e="string"==typeof s.error?s.error:`查詢失敗(${a.status})`;return{ok:!1,error:e}}let i=m.safeParse(s);if(!i.success)return{ok:!1,error:"回應格式錯誤"};return{ok:!0,user:i.data}}catch(e){return l.error("lookup-user failed",{endpoint:"/api/me/lookup-user"},e instanceof Error?e:Error(String(e))),{ok:!1,error:e instanceof Error?e.message:String(e)}}},[e,j]),searchUsers:(0,a.useCallback)(async t=>{if(!e)return{ok:!1,error:"未登入",users:[]};let r=t.trim();if(r.length<2)return{ok:!0,users:[]};try{let t=await e.getIdToken(),a=await fetch("/api/me/search-users",{method:"POST",headers:{...await j(t),"Content-Type":"application/json"},body:JSON.stringify({q:r})}),s=await a.json().catch(()=>({}));if(429===a.status)return{ok:!1,error:"查詢次數過多,請稍後再試",users:[]};if(!a.ok){let e="string"==typeof s.error?s.error:`查詢失敗(${a.status})`;return{ok:!1,error:e,users:[]}}let i=c.YO(m).safeParse(s.users);if(!i.success)return{ok:!1,error:"回應格式錯誤",users:[]};return{ok:!0,users:i.data}}catch(e){return{ok:!1,error:e instanceof Error?e.message:String(e),users:[]}}},[e,j])}}},83298:(e,t,r)=>{r.d(t,{i:()=>d});var a=r(31615),s=r(87777),i=r(37014),n=r(73521),o=r(18654),l=r(46711);function d(){let{user:e}=(0,n.A)(),[t,r]=(0,a.useState)(!1);return{loading:t,createSponsorship:(0,a.useCallback)(async t=>{if(!e)throw Error("請先登入");r(!0);try{(0,l.a1)(t.type,t.amount);let e=window.location.origin,r=t.clientBackURL??"/sponsor",a=t.orderResultURL??"/payment/success?type=sponsorship",s={type:t.type,amount:t.amount,displayOnWall:t.displayOnWall,clientBackURL:r.startsWith("http")?r:`${e}${r}`,orderResultURL:a.startsWith("http")?a:`${e}${a}`};t.tierId&&(s.tierId=t.tierId),t.displayName&&(s.displayName=t.displayName),t.message&&(s.message=t.message);let n=(await (0,i.callFn)("createSponsorship",s)).data;if(n.success&&n.formHtml){let e=document.createElement("div");e.innerHTML=n.formHtml,document.body.appendChild(e);let t=e.querySelector("form");if(t)t.submit();else throw Error("無法取得付款表單")}else if(!n.success)throw Error(n.error||"建立贊助失敗")}catch(e){throw(0,o.vV)("createSponsorship failed:",e),function(e){if(e instanceof s.g){if("functions/not-found"===e.code)return Error("贊助服務尚未部署或無法使用,請稍後再試或聯絡客服。");if("functions/unauthenticated"===e.code)return Error("請先登入後再贊助。");if("functions/internal"===e.code)return Error("贊助服務暫時無法使用,請稍後再試。");if(e.message&&"internal"!==e.message)return Error(e.message)}return e instanceof Error?e:Error("建立贊助失敗,請稍後再試。")}(e)}finally{r(!1)}},[e]),cancelSponsorship:(0,a.useCallback)(async(e,t)=>{let r=await fetch("/api/sponsorship/cancel",{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t}`},body:JSON.stringify({sponsorshipId:e})}),a=await r.json().catch(()=>({}));if(!r.ok)throw Error(a.error||"取消贊助失敗")},[]),trackSponsorSuccess:l.Hn}}},83392:(e,t,r)=>{r.d(t,{k3:()=>i,kc:()=>s,l7:()=>d,pu:()=>l,qi:()=>n,tc:()=>o,wr:()=>c});var a=r(15418);let s=["share_dashboard","share_accounts","share_income","share_expenses","share_assets","share_liabilities","share_investments","share_insurance","share_retirement","share_taxes","share_legacy","share_trusts"],i=a.k5(s),n=a.k5(["view","edit"]),o={share_dashboard:{key:"share_dashboard",label:"儀表板",description:"財務指標等總覽資料;報表備註亦歸此範疇。",dashboardPath:"/dashboard",moduleId:"dashboard",sortOrder:0},share_accounts:{key:"share_accounts",label:"帳戶",description:"帳戶、月結餘額與未分配金額等。",dashboardPath:"/dashboard/accounts",moduleId:"accounts",sortOrder:0},share_income:{key:"share_income",label:"收入",description:"收入類交易與收入分配規則。",dashboardPath:"/dashboard/income",moduleId:"income",sortOrder:0},share_expenses:{key:"share_expenses",label:"支出",description:"支出類交易。",dashboardPath:"/dashboard/expense",moduleId:"expenses",sortOrder:0},share_assets:{key:"share_assets",label:"資產",description:"資產清單與項目。",dashboardPath:"/dashboard/assets",moduleId:"assets",sortOrder:0},share_liabilities:{key:"share_liabilities",label:"負債",description:"負債項目。",dashboardPath:"/dashboard/liabilities",moduleId:"liabilities",sortOrder:0},share_investments:{key:"share_investments",label:"投資",description:"投資部位與紀錄。",dashboardPath:"/dashboard/investments",moduleId:"investment",sortOrder:0},share_insurance:{key:"share_insurance",label:"保險",description:"保單與保險相關文件。",dashboardPath:"/dashboard/insurance",moduleId:"insurance",sortOrder:0},share_retirement:{key:"share_retirement",label:"退休",description:"財務目標/退休規劃相關之 goals 資料(試算文件另議)。",dashboardPath:"/dashboard/retirement",moduleId:"retirement",sortOrder:0},share_taxes:{key:"share_taxes",label:"稅務",description:"稅務提醒項目。",dashboardPath:"/dashboard/tax",moduleId:"taxes",sortOrder:0},share_legacy:{key:"share_legacy",label:"傳承",description:"預立醫療決定與監護相關文件資料。",dashboardPath:"/dashboard/estate",moduleId:"inheritance",sortOrder:0},share_trusts:{key:"share_trusts",label:"信託",description:"信託項目。",dashboardPath:"/dashboard/trust",moduleId:"trust",sortOrder:0}};function l(e){return o[e]?.label??e}[{prefix:"/dashboard/accounts",key:"share_accounts"}
1,{prefix:"/dashboard/income",key:"share_income"},{prefix:"/dashboard/expense",key:"share_expenses"},{prefix:"/dashboard/assets",key:"share_assets"},{prefix:"/dashboard/liabilities",key:"share_liabilities"},{prefix:"/dashboard/investments",key:"share_investments"},{prefix:"/dashboard/insurance",key:"share_insurance"},{prefix:"/dashboard/retirement",key:"share_retirement"},{prefix:"/dashboard/tax",key:"share_taxes"},{prefix:"/dashboard/estate",key:"share_legacy"},{prefix:"/dashboard/trust",key:"share_trusts"}].sort((e,t)=>t.prefix.length-e.prefix.length);let d=a.k5(["never","1d","3d","7d","1mo","custom"]);function c(e,t){if("custom"===e){var r;let e,a,s,i;if(!t?.date||Number.isNaN(t.date.getTime()))return;return(r=t.date,a=Number.parseInt((e=(t.time||"23:59").trim().split(":"))[0]??"",10),s=Number.parseInt(e[1]??"",10),(i=new Date(r)).setHours(Number.isFinite(a)?a:0,Number.isFinite(s)?s:0,0,0),i).toISOString()}if("never"===e||"custom"===e)return;let a=Date.now();switch(e){case"1d":return new Date(a+864e5).toISOString();case"3d":return new Date(a+2592e5).toISOString();case"7d":return new Date(a+6048e5).toISOString();case"1mo":return new Date(a+2592e6).toISOString()}}},83474:(e,t,r)=>{r.d(t,{m:()=>m,Z:()=>u});var a=r(85047),s=r(31615),i=r(34619),n=r(34961);function o({value:e,onChange:t,size:r="md"}
1){let[l,d]=(0,s.useState)(0),c=l||e,u="sm"===r?"h-6 w-6":"h-8 w-8";return(0,a.jsx)("div",{className:"flex items-center gap-1",role:"radiogroup","aria-label":"評分",children:Array.from({length:5}).map((r,s)=>{let o=s+1,l=o<=c;return(0,a.jsx)("button",{type:"button",role:"radio","aria-checked":e===o,className:"rounded p-0.5 transition-transform hover:scale-110 focus:outline-none focus-visible:ring-2 focus-visible:ring-ring",onMouseEnter:()=>d(o),onMouseLeave:()=>d(0),onClick:()=>t(o),children:(0,a.jsx)(i.A,{className:(0,n.cn)(u,l?"fill-amber-400 text-amber-400":"text-muted-foreground/40")})},o)})})}var l=r(21410),d=r(10892),c=r(64685);function u(e){return e.trim().length<=c.Lb}function m({rating:e,onRatingChange:t,comment:r,onCommentChange:s,onSubmit:i,submitting:n=!1,submitLabel:h="送出",commentId:f="platform-review-comment",starSize:p="sm",layout:x="inline",showSubmit:v=!0}){let b=u(r);return"stacked"===x?(0,a.jsxs)("div",{className:"space-y-3",children:[(0,a.jsx)("div",{className:"flex justify-center",children:(0,a.jsx)(o,{value:e,onChange:t,size:p})}),(0,a.jsx)(d.T,{id:f,rows:2,value:r,onChange:e=>s(e.target.value),placeholder:"補充心得(選填)",maxLength:c.Lb,className:"resize-none text-sm"}),v?(0,a.jsx)(l.$,{type:"button",className:"w-full",onClick:i,disabled:n||!b,children:n?"送出中…":h}):null]}):(0,a.jsxs)("div",{className:"space-y-3",children:[(0,a.jsxs)("div",{className:"flex flex-wrap items-center gap-3",children:[(0,a.jsx)(o,{value:e,onChange:t,size:p}),(0,a.jsx)(l.$,{type:"button",size:"sm",onClick:i,disabled:n||!b,children:n?"儲存中…":h})]}),(0,a.jsx)(d.T,{id:f,rows:2,value:r,onChange:e=>s(e.target.value),placeholder:"補充心得(選填)",maxLength:c.Lb,className:"resize-none text-sm"})]})}},95897:(e,t,r)=>{r.d(t,{$:()=>m,b:()=>u});var a=r(31615),s=r(56928),i=r(73289),n=r(30274),o=r(76458),l=r(41178),d=r(18654),c=r(54938);function u(e){return e?(0,n.multiFactor)(e).enrolledFactors.filter(e=>e.factorId===n.TotpMultiFactorGenerator.FACTOR_ID):[]}function m(e){let{toast:t}=(0,l.dj)(),[m,h]=(0,a.useState)("idle"),[f,p]=(0,a.useState)(null),[x,v]=(0,a.useState)(null),[b,y]=(0,a.useState)(!1),[j,g]=(0,a.useState)([]),w=(0,s.mN)({resolver:(0,i.u)(c.yE),defaultValues:{password:""}}),N=(0,s.mN)({resolver:(0,i.u)(c.Gx),defaultValues:{code:""}}),k=(0,s.FH)({control:N.control,name:"code"}),C=!!e?.providerData?.some(e=>"password"===e.providerId),I=(0,a.useCallback)(()=>{g(u(e))},[e]);(0,a.useEffect)(()=>{I()},[I]);let S=(0,a.useCallback)(()=>{h("idle"),w.reset(),p(null),v(null),N.reset()},[w,N]),E=(0,a.useCallback)(()=>e?.emailVerified?(h("reauth"),w.reset(),!0):(t({title:"尚未完成信箱驗證",description:"請先驗證電子郵件後再綁定驗證器。",variant:"destructive"}),!1),[e,t,w]),T=(0,a.useCallback)(async a=>{if(e&&o.auth){y(!0);try{if(C){if(!a.password.trim()){t({title:"請輸入目前密碼",variant:"destructive"}),y(!1);return}let r=n.EmailAuthProvider.credential(e.email,a.password);await (0,n.reauthenticateWithCredential)(e,r)}else{let r=e.providerData[0]?.providerId;if("google.com"===r)await (0,n.reauthenticateWithPopup)(e,new n.GoogleAuthProvider);else if("facebook.com"===r)await (0,n.reauthenticateWithPopup)(e,new n.FacebookAuthProvider);else if(r===o.OIDC_LINE_PROVIDER_ID||r?.startsWith("oidc"))await (0,n.reauthenticateWithPopup)(e,new n.OAuthProvider(o.OIDC_LINE_PROVIDER_ID));else{t({title:"不支援的登入方式",description:"請先綁定電子郵件/密碼後再使用雙因素認證。",variant:"destructive"}),y(!1);return}}let s=await (0,n.multiFactor)(e).getSession(),i=await n.TotpMultiFactorGenerator.generateSecret(s);p(i);let l=e.displayName||e.email||"我的帳戶",c=i.generateQrCodeUrl(l,"財務管理系統"),u=(await r.e(1806).then(r.t.bind(r,71806,19))).default,m=await u.toDataURL(c);v(m),h("qrcode"),N.reset(),(0,d.fH)("TOTP secret 已產生")}catch(r){(0,d.vV)("重新驗證或產生 TOTP 失敗:",r);let e="object"==typeof r&&null!==r&&"code"in r?String(r.code):"";if("auth/popup-closed-by-user"===e||"auth/cancelled-popup-request"===e){t({title:"已取消",description:"請再次點擊「綁定驗證器 App」以繼續。"}),h("idle");return}if("auth/wrong-password"===e||"auth/invalid-credential"===e)return void t({title:"驗證失敗",description:"密碼不正確,請重試。",variant:"destructive"});if("auth/operation-not-allowed"===e){t({title:"驗證失敗",description:"專案尚未啟用 TOTP 多重驗證。請由管理員在 Firebase Console 啟用 TOTP MFA。",variant:"destructive"}),h("idle");return}t({title:"驗證失敗",description:r instanceof Error?r.message:"請檢查密碼或重試",variant:"destructive"})}finally{y(!1)}}},[e,C,t,N]),P=(0,a.useCallback)(async r=>{if(e&&f&&6===r.code.length){y(!0);try{let a=n.TotpMultiFactorGenerator.assertionForEnrollment(f,r.code);await (0,n.multiFactor)(e).enroll(a,"我的驗證器"),t({title:"已綁定驗證器",description:"登入時將要求輸入驗證器 App 的 6 碼。"}),h("idle"),p(null),v(null),N.reset(),I()}
1catch(r){(0,d.vV)("TOTP 綁定失敗:",r);let e="object"==typeof r&&null!==r&&"code"in r?String(r.code):"";if("auth/invalid-verification-code"===e||"auth/invalid-multi-factor-session"===e)return void t({title:"綁定失敗",description:"驗證碼錯誤或已過期,請輸入 App 顯示的最新 6 碼後重試。",variant:"destructive"});t({title:"綁定失敗",description:r instanceof Error?r.message:"驗證碼錯誤或已過期,請重試",variant:"destructive"})}finally{y(!1)}}},[e,f,t,N,I]);return{step:m,secret:f,qrDataUrl:x,loading:b,enrolledFactors:j,hasPassword:!!C,reauthForm:w,totpVerifyForm:N,totpVerifyCode:k??"",emailVerified:!!e?.emailVerified,start:E,cancel:S,submitReauth:T,submitCode:P,refreshEnrolled:I,setEnrolledFactors:g}}},97040:(e,t,r)=>{r.d(t,{C5:()=>v,MJ:()=>x,eI:()=>f,lR:()=>p,lV:()=>d,zB:()=>u});var a=r(85047),s=r(31615),i=r(94397),n=r(56928),o=r(34961),l=r(96968);let d=n.Op,c=s.createContext({}),u=({...e})=>(0,a.jsx)(c.Provider,{value:{name:e.name},children:(0,a.jsx)(n.xI,{...e})}),m=()=>{let e=s.useContext(c),t=s.useContext(h),{getFieldState:r,formState:a}=(0,n.xW)(),i=r(e.name,a);if(!e)throw Error("useFormField should be used within <FormField>");let{id:o}=t;return{id:o,name:e.name,formItemId:`${o}-form-item`,formDescriptionId:`${o}-form-item-description`,formMessageId:`${o}-form-item-message`,...i}},h=s.createContext({}),f=s.forwardRef(({className:e,...t},r)=>{let i=s.useId();return(0,a.jsx)(h.Provider,{value:{id:i},children:(0,a.jsx)("div",{ref:r,className:(0,o.cn)("space-y-2",e),...t})})});f.displayName="FormItem";let p=s.forwardRef(({className:e,...t},r)=>{let{error:s,formItemId:i}=m();return(0,a.jsx)(l.J,{ref:r,className:(0,o.cn)(s&&"text-destructive",e),htmlFor:i,...t})});p.displayName="FormLabel";let x=s.forwardRef(({...e},t)=>{let{error:r,formItemId:s,formDescriptionId:n,formMessageId:o}=m();return(0,a.jsx)(i.DX,{ref:t,id:s,"aria-describedby":r?`${n} ${o}`:`${n}`,"aria-invalid":!!r,...e})});x.displayName="FormControl",s.forwardRef(({className:e,...t},r)=>{let{formDescriptionId:s}=m();return(0,a.jsx)("p",{ref:r,id:s,className:(0,o.cn)("text-sm text-muted-foreground",e),...t})}).displayName="FormDescription";let v=s.forwardRef(({className:e,children:t,...r},s)=>{let{error:i,formMessageId:n}=m(),l=i?String(i?.message):t;return l?(0,a.jsx)("p",{ref:s,id:n,className:(0,o.cn)("text-sm font-medium text-destructive",e),...r,children:l}):null});v.displayName="FormMessage"}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.