PageSourceSearch

https://xynergy.ai/_next/static/chunks/4586.67d02cfed1c975dd.js

js xynergy.ai collected 2026-10-05 16:02:15 UTC 17,366 bytes, 1 lines download raw bytes

1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2205,4586],{12205:(e,t,r)=>{r.r(t),r.d(t,{AddInvestmentDialog:()=>b});var s=r(85047),n=r(31615),a=r(866),c=r(96968),i=r(21582),l=r(10892),o=r(21410),u=r(68341),d=r(54306),h=r(82721),m=r(9409),p=r(18654),x=r(15258),v=r(35617),f=r(47026),y=r(10055),g=r(17496);let j={type:"",market:"TW",symbol:"",name:"",quantity:"",price:"",notes:""};function b({open:e,onOpenChange:t,accounts:r,selectedAccountId:w,onSelectedAccountIdChange:N,onCreate:k,onSuccess:S,userId:C,presentation:T="dialog"}){let[E,P]=(0,n.useState)(j),R=function(e={}){let{market:t="TW"}=e,[r,s]=(0,n.useState)(""),[a,c]=(0,n.useState)([]),[i,l]=(0,n.useState)(!1),[o,u]=(0,n.useState)(null),[d,h]=(0,n.useState)(!1),x=(0,n.useRef)(null),v=(0,n.useRef)(new Map),f=(0,n.useRef)(new Map),y="US"===t?m.mj:m.Uu,g="US"===t?m.pY:m.$z;return(0,n.useEffect)(()=>{"TW"!==t||(async()=>{try{for(let e of["台積電","聯發科","鴻海","中華電","台塑"]){let t=`tw_${e.toLowerCase()}`;if(!v.current.has(t)){let r=await (0,m.Uu)(e);if(v.current.set(t,r),r.length>0)try{let e=await (0,m.$z)(r[0].value);f.current.set(r[0].value,{price:e.price,currency:e.currency,name:e.name,industry:e.industry,timestamp:e.timestamp})}catch{}}}}catch(e){(0,p.vV)("預載入股票搜尋結果失敗:",e)}})()},[t]),(0,n.useEffect)(()=>{if(x.current&&clearTimeout(x.current),!r.trim())return void c([]);let e=`${t}_${r.toLowerCase()}`;return v.current.has(e)?void c(v.current.get(e)):(x.current=setTimeout(async()=>{try{l(!0);let t=(await y(r)).map(e=>({label:e.label,value:e.value}));v.current.set(e,t),c(t)}catch(e){(0,p.vV)("搜尋股票失敗:",e),c([])}finally{l(!1)}},300),()=>{x.current&&clearTimeout(x.current)})},[r,t,y]),{searchTerm:r,setSearchTerm:s,options:a,isSearching:i,currentPrice:o,isSearchOpen:d,setSearchOpen:h,getQuote:(0,n.useCallback)(async e=>{if(f.current.has(e))return void u(f.current.get(e));try{let t=await g(e),r={price:t.price,currency:t.currency,name:t.name,industry:t.industry,timestamp:t.timestamp};f.current.set(e,r),u(r)}catch(e){throw(0,p.vV)("取得股票報價失敗:",e),e}},[g])}}({market:"US"===E.market?"US":"TW"}),O=function(){let[e,t]=(0,n.useState)(""),[r,s]=(0,n.useState)([]),[a,c]=(0,n.useState)(!1),[i,l]=(0,n.use
1State)(null),[o,u]=(0,n.useState)(!1),d=(0,n.useRef)(null),h=(0,n.useRef)(new Map),m=(0,n.useRef)(new Map);return(0,n.useEffect)(()=>{(async()=>{try{for(let e of["元大台灣50","富邦台50","國泰台灣50"])if(!h.current.has(e.toLowerCase())){let t=await (0,x.Z)(e);if(h.current.set(e.toLowerCase(),t),t.length>0)try{let e=await (0,x.p)(t[0].value);m.current.set(t[0].value,{price:e.nav,currency:e.currency,navDate:e.navDate})}catch{}}}catch(e){(0,p.vV)("預載入基金搜尋結果失敗:",e)}})()},[]),(0,n.useEffect)(()=>{if(d.current&&clearTimeout(d.current),!e.trim())return void s([]);let t=e.toLowerCase();return h.current.has(t)?void s(h.current.get(t)):(d.current=setTimeout(async()=>{try{c(!0);let r=(await (0,x.Z)(e)).map(e=>({label:e.label,value:e.value}));h.current.set(t,r),s(r)}catch(e){(0,p.vV)("搜尋基金失敗:",e),s([])}finally{c(!1)}},300),()=>{d.current&&clearTimeout(d.current)})},[e]),{searchTerm:e,setSearchTerm:t,options:r,isSearching:a,currentPrice:i,isSearchOpen:o,setSearchOpen:u,getNav:(0,n.useCallback)(async e=>{if(m.current.has(e)){let t=m.current.get(e);return l(t),t}try{let t=await (0,x.p)(e),r={price:t.nav,currency:t.currency,navDate:t.navDate};return m.current.set(e,r),l(r),r}catch(e){throw(0,p.vV)("取得基金淨值失敗:",e),e}},[])}}(),q=(0,n.useRef)(null),F=(0,n.useRef)(null),$=(0,n.useRef)(!1),D=(0,n.useMemo)(()=>{let e=Number(E.quantity);return Number.isFinite(e)&&e>0?(0,y.Qg)(e):0},[E.quantity]),L=(0,n.useMemo)(()=>{if("stocks"===E.type){let e=R.currentPrice;if(!e)return 0;let t=(0,f.WC)(e.currency||"twd");return"twd"===t?e.price:(0,f.A7)(e.price,t)}let e=Number(E.price);return Number.isFinite(e)&&e>0?e:0},[E.type,R.currentPrice,E.price]),W=D*L,M=D>0&&L>0&&!!w;(0,n.useMemo)(()=>"stocks"===E.type&&R.currentPrice?R.currentPrice.currency||"TWD":"funds"===E.type&&O.currentPrice&&O.currentPrice.currency||"TWD",[E.type,R.currentPrice,O.currentPrice]);let A=(0,n.useCallback)(()=>{P(j),R.setSearchTerm(""),R.setSearchOpen(!1),O.setSearchTerm(""),O.setSearchOpen(!1)},[R,O]),J=(0,n.useCallback)(e=>{let t=R.options.find(t=>t.value===e);t&&($.current=!0,P(r=>({...r,symbol:e,name:t.label,type:"stocks"})),R.setSearchTerm(t.label),R.getQuote(e).catch(()=>{(0,h.oR)({title:"獲取股價失敗",description:"無法獲取股價,請稍後再試",variant:"destructive"})})),R.setSearchOpen(!1)},[R]),U=(0,n.useCallback)(e=>{let t=O.options.find(t=>t.value===e);t&&($.current=!0,P(r=>({...r,symbol:e,name:t.label,type:"funds"})),O.setSearchTerm(t.label),O.getNav(e).then(e=>{if(e){let t=(0,f.WC)(e.currency||"twd"),r=Math.round(100*("twd"===t?e.price:(0,f.A7)(e.price,t)))/100;P(e=>({...e,price:r.toString()}))}}).catch(()=>{(0,h.oR)({title:"獲取基金淨值失敗",description:"無法獲取淨值,請稍後再試",variant:"destructive"})})),O.setSearchOpen(!1)},[O]),V=async e=>{if(e?.preventDefault(),!E.type||!E.name||!E.quantity)return void(0,h.oR)({title:"錯誤",description:"請填寫所有必要欄位",variant:"destructive"});if(!C)return void(0,h.oR)({title:"錯誤",description:"請先登入帳號",variant:"destructive"});let r=Math.max(Number(E.quantity)||0,0),s="stocks"===E.type?Math.floor(r):(0,y.Qg)(r);if(s<=0)return void(0,h.oR)({title:"錯誤",description:"stocks"===E.type?"股數必須為大於 0 的正整數":"數量/單位必須大於 0",variant:"destructive"});let n={type:E.type,name:E.name,quantity:s,notes:E.notes,accountId:w||void 0};if("stocks"===E.type){if(!E.symbol||!R.currentPrice)return void(0,h.oR)({title:"錯誤",description:"請選擇股票並等待現價載入",variant:"destructive"});let e=R.currentPrice;n={...n,symbol:E.symbol,price:e.price,currency:e.currency,market:E.market||"TW",industry:e.industry,purchasePrice:e.price,purchasePriceTimestamp:e.timestamp??Date.now()}}else{if(!E.price)return void(0,h.oR)({title:"錯誤",description:"請輸入成本價格",variant:"destructive"});n={...n,symbol:E.symbol||"",price:Math.max(Number(E.price)||0,0),currency:"TWD"}}try{await k(n),A(),t(!1),(0,h.oR)({title:"成功",description:"已新增投資項目"}),S?.()}catch(e){(0,h.oR)({title:"儲存失敗",description:e instanceof Error?e.message:"無法儲存投資資料,請稍後再試",variant:"destructive"})}},I=(e,t)=>{P(r=>
1({...r,type:e,market:t??r.market,symbol:"",name:"",price:""}))};return(0,n.useEffect)(()=>{"stocks"===E.type&&R.searchTerm.length>=2&&R.options.length>0&&!$.current&&R.setSearchOpen(!0)},[E.type,R.searchTerm,R.options.length,R.setSearchOpen]),(0,n.useEffect)(()=>{"funds"===E.type&&O.searchTerm.length>=2&&O.options.length>0&&!$.current&&O.setSearchOpen(!0)},[E.type,O.searchTerm,O.options.length,O.setSearchOpen]),(0,n.useEffect)(()=>{let t=e=>{let t=e.target;q.current?.contains(t)||F.current?.contains(t)||(R.setSearchOpen(!1),O.setSearchOpen(!1))};if(e)return document.addEventListener("mousedown",t),()=>document.removeEventListener("mousedown",t)},[e,R.setSearchOpen,O.setSearchOpen]),(0,s.jsx)(g.T,{presentation:T,open:e,onOpenChange:t,title:"新增投資",description:"選擇投資類型並填寫相關資訊",dialogClassName:"max-w-lg max-h-[90vh] overflow-y-auto scrollbar-hide",drawerWide:!0,children:(0,s.jsxs)("form",{onSubmit:V,className:"space-y-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"投資類型"}),(0,s.jsxs)(i.l6,{value:E.type,onValueChange:e=>I(e),children:[(0,s.jsx)(i.bq,{children:(0,s.jsx)(i.yv,{placeholder:"選擇類型"})}),(0,s.jsxs)(i.gC,{children:[(0,s.jsx)(i.eb,{value:"stocks",children:"股票"}),(0,s.jsx)(i.eb,{value:"funds",children:"基金"}),(0,s.jsx)(i.eb,{value:"bonds",children:"債券"}),(0,s.jsx)(i.eb,{value:"realestate",children:"不動產"}),(0,s.jsx)(i.eb,{value:"other",children:"其他"})]})]})]}),"stocks"===E.type&&(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"市場"}),(0,s.jsxs)(i.l6,{value:E.market,onValueChange:e=>{I(E.type,e)},children:[(0,s.jsx)(i.bq,{children:(0,s.jsx)(i.yv,{placeholder:"選擇市場"})}),(0,s.jsxs)(i.gC,{children:[(0,s.jsx)(i.eb,{value:"TW",children:"台股"}),(0,s.jsx)(i.eb,{value:"US",children:"美股"})]})]})]}),"stocks"===E.type&&(0,s.jsxs)("div",{ref:q,className:"relative",children:[(0,s.jsx)(c.J,{children:"股票代號/名稱"}),(0,s.jsx)(a.p,{placeholder:"US"===E.market?"輸入美股代號(如 AAPL)...":"輸入股票代號或名稱...",value:R.searchTerm,onChange:e=>{$.current=!1,R.setSearchTerm(e.target.value)},onFocus:()=>{(R.options.length>0||R.isSearching)&&R.setSearchOpen(!0)}}),R.isSearchOpen&&(0,s.jsx)("div",{className:"absolute left-0 right-0 top-full z-[100] mt-1 max-h-[300px] overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md scrollbar-hide",children:R.isSearching?(0,s.jsx)("div",{className:"py-6 text-center text-sm text-muted-foreground",children:"搜尋中..."}):R.searchTerm.length<2?(0,s.jsx)("div",{className:"py-6 text-center text-sm text-muted-foreground",children:"請輸入至少 2 個字元"}):0===R.options.length?(0,s.jsx)("div",{className:"py-6 text-center text-sm text-muted-foreground",children:"找不到相關標的"}):(0,s.jsx)("div",{className:"flex flex-col gap-0.5 py-1",children:R.options.map(e=>(0,s.jsxs)("button",{type:"button",className:"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[selected]:bg-accent",onClick:()=>J(e.value),children:[(0,s.jsx)(u.A,{className:`mr-2 h-4 w-4 shrink-0 ${E.symbol===e.value?"opacity-100":"opacity-0"}`}),e.label," (",e.value,")"]},e.value))})})]}),"funds"===E.type&&(0,s.jsxs)("div",{ref:F,className:"relative",children:[(0,s.jsx)(c.J,{children:"基金代號/名稱"}),(0,s.jsx)(a.p,{placeholder:"輸入基金名稱或代號...",value:O.searchTerm,onChange:e=>{$.current=!1,O.setSearchTerm(e.target.value)},onFocus:()=>{(O.options.length>0||O.isSearching)&&O.setSearchOpen(!0)}}),O.isSearchOpen&&(0,s.jsx)("div",{className:"absolute left-0 right-0 top-full z-[100] mt-1 max-h-[300px] overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md scrollbar-hide",children:O.isSearching?(0,s.jsx)("div",{className:"py-6 text-center text-sm text-muted-foreground",children:"搜尋中..."}):O.searchTerm.length<2?(0,s.jsx)("div",{className:"py-6 text-center text-sm text-muted-foreground",children:"請輸入至少 2 個字元"}):0===O.options.length?(0,s.jsx)("div",{className:"py-6 text-center text-sm text-muted-foreground",children:"找不到相關基金"}):(0,s.jsx)("div",{className:"flex flex-col gap-0.5 py-1",children:O.options.map(e=>(0,s.jsxs)("button",{type:"button",className:"flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none hover:bg-accent hover:text-accent-foreground data-[selected]:bg-accent",onClick:()=>U(e.value),children:[(0,s.jsx)(u.A,{className:`mr-2 h-4 w-4 shrink-0 ${E.symbol===e.value?"opacity-100":"opacity-0"}`}),e.label," (",e.value,")"]},e.value))})})]}),E.type&&"stocks"!==E.type&&"funds"!==E.type&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"名稱"}),(0,s.jsx)(a.p,{value:E.name,onChange:e=>P(t=>({...t,name:e.target.value}
1)),placeholder:"輸入名稱"})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"代號(選填)"}),(0,s.jsx)(a.p,{value:E.symbol,onChange:e=>P(t=>({...t,symbol:e.target.value})),placeholder:"輸入代號"})]})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"數量"}),(0,s.jsx)(a.p,{type:"number",min:"stocks"===E.type?"1":"0.01",step:"stocks"===E.type?"1":"0.01",value:E.quantity,onChange:e=>{let t=e.target.value;("stocks"===E.type?/^\d*$/.test(t):function(e){if(""===e)return!0;let t=Number(e);if(!Number.isFinite(t)||t<=0)return!1;let r=e.trim().split(".");return 1===r.length||r[1].length<=2}(t))&&P(e=>({...e,quantity:t}))},onBlur:e=>{let t=Number(e.target.value);if(""!==e.target.value&&Number.isFinite(t)&&t>0){let e="stocks"===E.type?Math.floor(t):(0,y.Qg)(t);P(t=>({...t,quantity:e.toString()}))}},placeholder:"stocks"===E.type?"輸入股數(正整數)":"輸入數量"})]}),E.type&&"stocks"!==E.type&&(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"單價"}),(0,s.jsx)(a.p,{type:"number",value:E.price,onChange:e=>P(t=>({...t,price:e.target.value})),placeholder:"輸入單價"})]}),"funds"===E.type&&E.symbol&&(0,s.jsxs)("div",{className:"rounded-lg bg-muted p-3",children:[(0,s.jsx)("div",{className:"text-sm text-muted-foreground",children:"最新淨值"}),O.currentPrice?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("div",{className:"text-lg font-semibold",children:[O.currentPrice.currency," ",O.currentPrice.price.toLocaleString()]}),O.currentPrice.navDate&&(0,s.jsxs)("div",{className:"text-xs text-muted-foreground mt-1",children:["淨值日期: ",O.currentPrice.navDate]})]}):null]}),"stocks"===E.type&&E.symbol&&(0,s.jsxs)("div",{className:"rounded-lg bg-muted p-3",children:[(0,s.jsx)("div",{className:"text-sm text-muted-foreground",children:"現價"}),R.currentPrice?(0,s.jsxs)("div",{className:"text-lg font-semibold",children:[R.currentPrice.currency||"TWD"," ",R.currentPrice.price.toLocaleString()]}):(0,s.jsx)("div",{className:"text-lg font-semibold flex items-center gap-2",children:(0,s.jsx)(d.default,{size:32})})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"關聯帳戶"}),(0,s.jsxs)(i.l6,{value:w,onValueChange:N,children:[(0,s.jsx)(i.bq,{children:(0,s.jsx)(i.yv,{placeholder:"選擇帳戶"})}),(0,s.jsx)(i.gC,{children:r.map(e=>(0,s.jsxs)(i.eb,{value:e.id,children:[e.name," (",e.bank,")"]},e.id))})]})]}),W>0&&(0,s.jsxs)("div",{className:"rounded-lg p-3 bg-muted",children:[(0,s.jsx)("div",{className:"text-sm text-muted-foreground",children:"預計金額"}),(0,s.jsx)("div",{className:"text-lg font-semibold",children:(0,v.C)(W)})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"備註(選填)"}),(0,s.jsx)(l.T,{value:E.notes,onChange:e=>P(t=>({...t,notes:e.target.value})),placeholder:"輸入備註"})]}),(0,s.jsxs)("div",{className:"flex justify-end gap-2 pt-4",children:[(0,s.jsx)(o.$,{type:"button",variant:"outline",onClick:()=>t(!1),children:"取消"}),(0,s.jsx)(o.$,{type:"submit",disabled:!M,children:"新增"})]})]})})}},15258:(e,t,r)=>{r.d(t,{Z:()=>n,p:()=>a});var s=r(58678);async function n(e){if(!e)return[];try{let t=await fetch(`/api/fund/search?q=${encodeURIComponent(e)}`);
1if(!t.ok){let e=await t.json().catch(()=>({}));throw Error(e?.error?.message||e?.error||t.statusText||"基金搜尋失敗")}let r=await t.json(),n=(0,s.Xp)(s.ks).safeParse(r);if(!n.success)throw Error("Invalid fund search response");return n.data.data}catch(e){if(e instanceof TypeError&&"Failed to fetch"===e.message)throw Error("無法連接到伺服器,請檢查網路連接");throw e}}async function a(e){if(!e)throw Error("Fund code cannot be empty.");try{let t=await fetch(`/api/fund/nav?code=${encodeURIComponent(e)}`);if(!t.ok){let e=await t.json().catch(()=>({}));throw Error(e?.error?.message||e?.error||t.statusText||"取得基金淨值失敗")}let r=await t.json(),n=(0,s.Xp)(s.wh).safeParse(r);if(!n.success)throw Error("Invalid NAV data");return n.data.data}catch(e){if(e instanceof TypeError&&"Failed to fetch"===e.message)throw Error("無法連接到伺服器,請檢查網路連接");throw e}}},17496:(e,t,r)=>{r.d(t,{T:()=>l});var s=r(85047),n=r(18534),a=r(31615),c=r(42165);function i({open:e,onClose:t,title:r,description:n,children:l,footer:o,wide:u}){let[d,h]=(0,a.useState)(!1);function m(){h(!1),setTimeout(t,240)}return((0,a.useEffect)(()=>{if(!e)return;let t=requestAnimationFrame(()=>h(!0));return()=>cancelAnimationFrame(t)},[e]),(0,a.useEffect)(()=>{e||h(!1)},[e]),(0,a.useEffect)(()=>{if(!e)return;let t=e=>{"Escape"===e.key&&m()};return document.addEventListener("keydown",t),()=>document.removeEventListener("keydown",t)},[e]),e)?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("div",{className:"dbui-drawer-backdrop",onClick:m}),(0,s.jsxs)("div",{className:`dbui-drawer ${u?"dbui-drawer-wide":""} ${d?"open":""}`,role:"dialog","aria-modal":"true",children:[(0,s.jsxs)("div",{className:"flex items-start justify-between gap-3 px-8 py-5",children:[(0,s.jsxs)("div",{className:"min-w-0",children:[(0,s.jsx)("div",{className:"text-lg font-extrabold",children:r}),n&&(0,s.jsx)("p",{className:"mt-1 text-xs text-muted-foreground",children:n})]}),(0,s.jsx)("button",{onClick:m,className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground","aria-label":"關閉",children:(0,s.jsx)(c.A,{className:"h-4 w-4"})})]}),(0,s.jsx)("div",{className:"flex-1 overflow-auto px-8 pb-6",children:l}),o&&(0,s.jsx)("div",{className:"border-t border-border/40 px-8 py-4",children:o})]})]}):null}function l({presentation:e="dialog",open:t,onOpenChange:r,title:a,description:c,dialogClassName:o,drawerWide:u,children:d,footer:h}){return"drawer"===e?(0,s.jsx)(i,{open:t,onClose:()=>r(!1),title:a,description:c,wide:u,footer:h,children:d}):(0,s.jsx)(n.lG,{open:t,onOpenChange:r,children:(0,s.jsxs)(n.Cf,{className:o,children:[(0,s.jsxs)(n.c7,{children:[(0,s.jsx)(n.L3,{children:a}),c&&(0,s.jsx)(n.rr,{children:c})]}),d,h]})})}}}]);

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.