PageSourceSearch

https://xynergy.ai/_next/static/chunks/2205.6d6ccf0972298820.js

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

1"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[2205],{12205:(e,t,r)=>{r.r(t),r.d(t,{AddInvestmentDialog:()=>j});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),p=r(9409),m=r(18654),v=r(15258),x=r(35617),y=r(47026),f=r(10055),g=r(17496);let b={type:"",market:"TW",symbol:"",name:"",quantity:"",price:"",notes:""};function j({open:e,onOpenChange:t,accounts:r,selectedAccountId:w,onSelectedAccountIdChange:S,onCreate:k,onSuccess:N,userId:T,presentation:C="dialog"}){let[P,E]=(0,n.useState)(b),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),v=(0,n.useRef)(null),x=(0,n.useRef)(new Map),y=(0,n.useRef)(new Map),f="US"===t?p.mj:p.Uu,g="US"===t?p.pY:p.$z;return(0,n.useEffect)(()=>{"TW"!==t||(async()=>{try{for(let e of["台積電","聯發科","鴻海","中華電","台塑"]){let t=`tw_${e.toLowerCase()}`;if(!x.current.has(t)){let r=await (0,p.Uu)(e);if(x.current.set(t,r),r.length>0)try{let e=await (0,p.$z)(r[0].value);y.current.set(r[0].value,{price:e.price,currency:e.currency,name:e.name,industry:e.industry,timestamp:e.timestamp})}catch{}}}}catch(e){(0,m.vV)("預載入股票搜尋結果失敗:",e)}})()},[t]),(0,n.useEffect)(()=>{if(v.current&&clearTimeout(v.current),!r.trim())return void c([]);let e=`${t}_${r.toLowerCase()}`;return x.current.has(e)?void c(x.current.get(e)):(v.current=setTimeout(async()=>{try{l(!0);let t=(await f(r)).map(e=>({label:e.label,value:e.value}));x.current.set(e,t),c(t)}catch(e){(0,m.vV)("搜尋股票失敗:",e),c([])}finally{l(!1)}},300),()=>{v.current&&clearTimeout(v.current)})},[r,t,f]),{searchTerm:r,setSearchTerm:s,options:a,isSearching:i,currentPrice:o,isSearchOpen:d,setSearchOpen:h,getQuote:(0,n.useCallback)(async e=>{if(y.current.has(e))return void u(y.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};y.current.set(e,r),u(r)}catch(e){throw(0,m.vV)("取得股票報價失敗:",e),e}},[g])}}({market:"US"===P.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),p=(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,v.Z)(e);if(h.current.set(e.toLowerCase(),t),t.length>0)try{let e=await (0,v.p)(t[0].value);p.current.set(t[0].value,{price:e.nav,currency:e.currency,navDate:e.navDate})}catch{}}}catch(e){(0,m.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,v.Z)(e)).map(e=>({label:e.label,value:e.value}));h.current.set(t,r),s(r)}catch(e){(0,m.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(p.current.has(e)){let t=p.current.get(e);return l(t),t}try{let t=await (0,v.p)(e),r={price:t.nav,currency:t.currency,navDate:t.navDate};return p.current.set(e,r),l(r),r}catch(e){throw(0,m.vV)("取得基金淨值失敗:",e),e}},[])}}(),q=(0,n.useRef)(null),D=(0,n.useRef)(null),W=(0,n.useRef)(!1),M=(0,n.useMemo)(()=>{let e=Number(P.quantity);return Number.isFinite(e)&&e>0?(0,f.Qg)(e):0},[P.quantity]),$=(0,n.useMemo)(()=>{if("stocks"===P.type){let e=R.currentPrice;if(!e)return 0;let t=(0,y.WC)(e.currency||"twd");return"twd"===t?e.price:(0,y.A7)(e.price,t)}let e=Number(P.price);return Number.isFinite(e)&&e>0?e:0},[P.type,R.currentPrice,P.price]),F=M*$,J=M>0&&$>0&&!!w;(0,n.useMemo)(()=>"stocks"===P.type&&R.currentPrice?R.currentPrice.currency||"TWD":"funds"===P.type&&O.currentPrice&&O.currentPrice.currency||"TWD",[P.type,R.currentPrice,O.currentPrice]);let L=(0,n.useCallback)(()=>{E(b),R.setSearchTerm(""),R.setSearchOpen(!1),O.setSearchTerm(""),O.setSearchOpen(!1)},[R,O]),U=(0,n.useCallback)(e=>{let t=R.options.find(t=>t.value===e);t&&(W.current=!0,E(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]),V=(0,n.useCallback)(e=>{let t=O.options.find(t=>t.value===e);t&&(W.current=!0,E(r=>({...r,symbol:e,name:t.label,type:"funds"})),O.setSearchTerm(t.label),O.getNav(e).then(e=>{if(e){let t=(0,y.WC)(e.currency||"twd"),r=Math.round(100*("twd"===t?e.price:(0,y.A7)(e.price,t)))/100;E(e=>({...e,price:r.toString()}))}}).catch(()=>{(0,h.oR)({title:"獲取基金淨值失敗",description:"無法獲取淨值,請稍後再試",variant:"destructive"})})),O.setSearchOpen(!1)},[O]),A=async e=>{if(e?.preventDefault(),!P.type||!P.name||!P.quantity)return void(0,h.oR)({title:"錯誤",description:"請填寫所有必要欄位",variant:"destructive"});if(!T)return void(0,h.oR)({title:"錯誤",description:"請先登入帳號",variant:"destructive"});let r=Math.max(Number(P.quantity)||0,0),s="stocks"===P.type?Math.floor(r):(0,f.Qg)(r);if(s<=0)return void(0,h.oR)({title:"錯誤",description:"stocks"===P.type?"股數必須為大於 0 的正整數":"數量/單位必須大於 0",variant:"destructive"});let n={type:P.type,name:P.name,quantity:s,notes:P.notes,accountId:w||void 0};if("stocks"===P.type){if(!P.symbol||!R.currentPrice)return void(0,h.oR)({title:"錯誤",description:"請選擇股票並等待現價載入",variant:"destructive"});let e=R.currentPrice;n={...n,symbol:P.symbol,price:e.price,currency:e.currency,market:P.market||"TW",industry:e.industry,purchasePrice:e.price,purchasePriceTimestamp:e.timestamp??Date.now()}}else{if(!P.price)return void(0,h.oR)({title:"錯誤",description:"請輸入成本價格",variant:"destructive"});n={...n,symbol:P.symbol||"",price:Math.max(Number(P.price)||0,0),currency:"TWD"}}try{await k(n),L(),t(!1),(0,h.oR)({title:"成功",description:"已新增投資項目"}),N?.()}catch(e){(0,h.oR)({title:"儲存失敗",description:e instanceof Error?e.message:"無法儲存投資資料,請稍後再試",variant:"destructive"})}},I=(e,t)=>{E(r=>
1({...r,type:e,market:t??r.market,symbol:"",name:"",price:""}))};return(0,n.useEffect)(()=>{"stocks"===P.type&&R.searchTerm.length>=2&&R.options.length>0&&!W.current&&R.setSearchOpen(!0)},[P.type,R.searchTerm,R.options.length,R.setSearchOpen]),(0,n.useEffect)(()=>{"funds"===P.type&&O.searchTerm.length>=2&&O.options.length>0&&!W.current&&O.setSearchOpen(!0)},[P.type,O.searchTerm,O.options.length,O.setSearchOpen]),(0,n.useEffect)(()=>{let t=e=>{let t=e.target;q.current?.contains(t)||D.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:C,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:A,className:"space-y-4",children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"投資類型"}),(0,s.jsxs)(i.l6,{value:P.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"===P.type&&(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"市場"}),(0,s.jsxs)(i.l6,{value:P.market,onValueChange:e=>{I(P.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"===P.type&&(0,s.jsxs)("div",{ref:q,className:"relative",children:[(0,s.jsx)(c.J,{children:"股票代號/名稱"}),(0,s.jsx)(a.p,{placeholder:"US"===P.market?"輸入美股代號(如 AAPL)...":"輸入股票代號或名稱...",value:R.searchTerm,onChange:e=>{W.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:()=>U(e.value),children:[(0,s.jsx)(u.A,{className:`mr-2 h-4 w-4 shrink-0 ${P.symbol===e.value?"opacity-100":"opacity-0"}`}),e.label," (",e.value,")"]},e.value))})})]}),"funds"===P.type&&(0,s.jsxs)("div",{ref:D,className:"relative",children:[(0,s.jsx)(c.J,{children:"基金代號/名稱"}),(0,s.jsx)(a.p,{placeholder:"輸入基金名稱或代號...",value:O.searchTerm,onChange:e=>{W.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:()=>V(e.value),children:[(0,s.jsx)(u.A,{className:`mr-2 h-4 w-4 shrink-0 ${P.symbol===e.value?"opacity-100":"opacity-0"}`}),e.label," (",e.value,")"]},e.value))})})]}),P.type&&"stocks"!==P.type&&"funds"!==P.type&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"名稱"}),(0,s.jsx)(a.p,{value:P.name,onChange:e=>E(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:P.symbol,onChange:e=>E(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"===P.type?"1":"0.01",step:"stocks"===P.type?"1":"0.01",value:P.quantity,onChange:e=>{let t=e.target.value;("stocks"===P.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))&&E(e=>({...e,quantity:t}))},onBlur:e=>{let t=Number(e.target.value);if(""!==e.target.value&&Number.isFinite(t)&&t>0){let e="stocks"===P.type?Math.floor(t):(0,f.Qg)(t);E(t=>({...t,quantity:e.toString()}))}},placeholder:"stocks"===P.type?"輸入股數(正整數)":"輸入數量"})]}),P.type&&"stocks"!==P.type&&(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"單價"}),(0,s.jsx)(a.p,{type:"number",value:P.price,onChange:e=>E(t=>({...t,price:e.target.value})),placeholder:"輸入單價"})]}),"funds"===P.type&&P.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"===P.type&&P.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:S,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))})]})]}),F>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,x.C)(F)})]}),(0,s.jsxs)("div",{children:[(0,s.jsx)(c.J,{children:"備註(選填)"}),(0,s.jsx)(l.T,{value:P.notes,onChange:e=>E(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:!J,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}}}}]);

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.