PageSourceSearch

https://zynoxprincewebsite.vercel.app/assets/loan-calculator-BRBL2mDw.js

js zynoxprincewebsite.vercel.app collected 2026-10-03 08:06:12 UTC 11,951 bytes, 2 lines download raw bytes

1import{r as m,j as e,R as re}from"./react-BxeXOeIu.js";import{u as U,b as le,d as ie,A,e as G,f as b,M as oe,c as ce,D as de,C as me}from"./ui-DSRq28Qn.js";import{g as he}from"./registry-Brpnw2zG.js";import{c as ue,p as I,N as F,C as pe,A as xe,f as j,a as l,W as V,D as H,S as Q,m as E,t as ye}from"./format-Cjvc7Z9X.js";import{b as J,g as je,F as be}from"./finance-pRHLWJO8.js";import{t as K,a as X,f as fe,s as M}from"./dates-Dsttcemw.js";import"./icons-vendor-CNxPEA7v.js";import"./index-CpdcDKbd.js";import"./router-CaUyAvDj.js";const ve=he("calc","loan-calculator"),ke=[{label:"Mortgage",amount:"250000",rate:"5.25",years:"25"},{label:"Car loan",amount:"18000",rate:"8.9",years:"5"},{label:"Personal loan",amount:"10000",rate:"11.5",years:"3"},{label:"Student loan",amount:"45000",rate:"7.3",years:"30"}];function Te(){var W;const[c,h]=U("calc.currency","GBP"),[d,n]=U("calc.loan.freq","monthly"),[w,N]=m.useState("250000"),[L,B]=m.useState("5.25"),[D,Y]=m.useState("25"),[P,R]=m.useState(""),[z,Z]=m.useState(()=>K(X())),[_,ee]=m.useState(()=>new Set),[T,te]=m.useState(!1),g=ue(c).symbol,s=t=>E(t,c),i=t=>E(t,c,0),r=I(w),u=I(L),x=I(D),f=I(P)||0,S=fe(z)||X(),k=Number.isFinite(r)&&r>0&&Number.isFinite(u)&&u>=0&&Number.isFinite(x)&&x>0&&x<=60,y=m.useMemo(()=>k?J({principal:r,annualRate:u,years:x,frequency:d,extra:0,startDate:S}):null,[k,r,u,x,d,S]),p=m.useMemo(()=>k&&f>0?J({principal:r,annualRate:u,years:x,frequency:d,extra:f,startDate:S}):null,[k,r,u,x,d,f,S]),a=p||y,O=m.useMemo(()=>a?je(a.rows):[],[a]),v=((W=a==null?void 0:a.freq)==null?void 0:W.perYear)||12,ae=a?a.totalInterest/r*100:NaN,q=m.useMemo(()=>(a==null?void 0:a.rows.find(t=>t.principal>=t.interest))||null,[a]),se=()=>ye([["Payment #","Date","Payment","Interest","Principal","Balance","Cumulative interest"],...((a==null?void 0:a.rows)||[]).map(t=>[t.n,K(t.date),t.payment.toFixed(2),t.interest.toFixed(2),t.principal.toFixed(2),t.balance.toFixed(2),t.cumInterest.toFixed(2)])]),ne=t=>ee($=>{const o=new Set($);return o.has(t)?o.delete(t):o.add(t),o}),C=k?u/v:NaN;return e.jsx(le,{tool:ve,children:e.jsxs("div",{className:"tk-stack",children:[e.jsxs("div",{className:"tk-row tight",children:[e.jsx("span",{className:"tk-muted",style:{fontSize:"0.78rem"},children:"Try:"}),ke.map(t=>e.jsx("button",{type:"button",className:"tk-chip",onClick:()=>{N(t.amount),B(t.rate),Y(t.years)},children:t.label},t.label))]}),e.jsxs("div",{className:"tk-grid3",children:[e.jsx(F,{label:"Loan amount",value:w,onChange:N,prefix:g}),e.jsx(F,{label:"Annual interest rate",value:L,onChange:B,suffix:"%",hint:"The nominal APR your lender quotes."}),e.jsx(F,{label:"Term",value:D,onChange:Y,suffix:"yrs",hint:"Up to 60 years."})]}),e.jsxs("div",{className:"tk-grid3",children:[e.jsxs("div",{className:"tk-field",children:[e.jsx("span",{className:"tk-label",children:e.jsx("span",{children:"Repayment frequency"})}),e.jsx(ie,{value:d,onChange:n,options:be.map(t=>({value:t.id,label:t.label})),block:!0,ariaLabel:"Repayment frequency"})]}),e.jsxs("div",{className:"tk-field",children:[e.jsx("label",{className:"tk-label",htmlFor:"loan-cur",children:e.jsx("span",{children:"Currency"})}),e.jsx("select",{id:"loan-cur",className:"tk-select",value:c,onChange:t=>h(t.target.value),children:pe.map(t=>e.jsx("option",{value:t.code,children:t.label},t.code))})]}),e.jsxs("div",{className:"tk-field",children:[e.jsx("label",{className:"tk-label",htmlFor:"loan-start",children:e.jsx("span",{children:"First payment date"})}),e.jsx("input",{id:"loan-start",type:"date",className:"tk-input",value:z,onChange:t=>Z(t.target.value)}),e.jsx("span",{className:"tk-hint",children:"Used to date the schedule."})]})]}),k?a.neverPaysOff?e.jsx(A,{kind:"err",children:"At this rate the payment would never clear the balance. Increase the payment or lower the rate."}):e.jsxs(e.Fragment,{children:[e.jsx(xe,{caption:`${a.freq.label} payment`,value:s(a.actualPayment??a.payment),sub:f>0?`${s(a.payment)} contractual + ${s(f)} extra`:`${j(a.scheduledPeriods)} payments over ${l(x,2)} years`}),e.jsxs(G,{children:[e.jsx(b,{k:"Total interest",v:i(a.totalInterest),sub:`${l(ae,1)}% of the amount borrowed`,accent:!0}),e.jsx(b,{k:"Total repaid",v:i(a.totalPaid),sub:`${i(r)} borrowed`}),e.jsx(b,{k:"Payments",v:j(a.periods),sub:`${l(a.periods/v,1)} years`}),e.jsx(b,{k:"Interest per year",v:i(a.totalInterest/(a.periods/v)),sub:"average over the term"})]}),e.jsxs("div",{children:[e.jsxs("div",{className:"tk-row between",style:{marginBottom:"0.4rem"},children:[e.jsxs("span",{className:"tk-muted",style:{fontSize:"0.78rem"},children:["Principal ",l(r/a.totalPaid*100,0),"%"]}),e.jsxs("span",{className:"tk-muted",style:{fontSize:"0.78rem"},children:["Interest ",l(a.totalInterest/a.totalPaid*100,0),"%"]})]}),e.jsx(oe,{value:r/a.totalPaid*100,max:100})]}),e.jsx(V,{title:"How the payment is calculated",formula:"P = L × i ÷ (1 − (1 + i)^−n)      (the annuity / EMI formula)",steps:[{label:"L — amount borrowed",value:s(r)},{label:`i — rate per period (${l(u,3)}% ÷ ${v})`,value:`${l(C,6)}%  =  ${l(C/100,8)}`},{label:`n — number of payments (${l(x,2)} × ${v})`,value:j(a.scheduledPeriods)},u===0?`P = ${s(r)} ÷ ${j(a.scheduledPeriods)} = ${s(a.payment)}  (0% interest — straight-line, no division by zero)`:`P = ${s(r)} × ${l(C/100,8)} ÷ (1 − (1 + ${l(C/100,8)})^−${j(a.scheduledPeriods)}) = ${s(a.payment)}`,`Total repaid = ${s(a.actualPayment??a.payment)} × ${j(a.periods)} ≈ ${s(a.totalPaid)}`,`Total interest = ${s(a.totalPaid)} − ${s(r)} = ${s(a.totalInterest)}`],note:"The schedule below is simulated payment by payment from the opening balance, so the totals include the small final-payment adju
1stment rather than assuming every payment is identical."}),e.jsx(H,{}),e.jsx(Q,{children:"Overpayment"}),e.jsxs("div",{className:"tk-grid2",children:[e.jsx(F,{label:`Extra ${a.freq.label.toLowerCase()} payment`,value:P,onChange:R,prefix:g,placeholder:"0",hint:"Paid on top of every scheduled payment."}),e.jsxs("div",{className:"tk-row tight",style:{alignItems:"flex-end",paddingBottom:"0.2rem"},children:[[50,100,250,500].map(t=>e.jsxs("button",{type:"button",className:ce("tk-chip",String(t)===P&&"on"),onClick:()=>R(String(t)),children:["+",g,t]},t)),P&&e.jsx("button",{type:"button",className:"tk-chip",onClick:()=>R(""),children:"Clear"})]})]}),p&&y&&e.jsxs(e.Fragment,{children:[e.jsxs(G,{children:[e.jsx(b,{k:"Interest saved",v:i(y.totalInterest-p.totalInterest),sub:`${l((y.totalInterest-p.totalInterest)/y.totalInterest*100,1)}% less interest`,accent:!0}),e.jsx(b,{k:"Time saved",v:Ne((y.periods-p.periods)/v*12),sub:`paid off in ${l(p.periods/v,1)} years`}),e.jsx(b,{k:"New total repaid",v:i(p.totalPaid),sub:`was ${i(y.totalPaid)}`}),e.jsx(b,{k:"Extra paid in",v:i(f*p.periods),sub:"your additional outlay"})]}),e.jsx(V,{title:"Why overpaying saves so much",formula:"interest each period = outstanding balance × i",steps:[`Contractual: ${j(y.periods)} payments, ${s(y.totalInterest)} interest.`,`With ${s(f)} extra: ${j(p.periods)} payments, ${s(p.totalInterest)} interest.`,`Every extra ${g}1 removes ${g}1 from the balance that all remaining interest is charged on — so the saving compounds for the rest of the loan.`]})]}),e.jsx(H,{}),e.jsx(Q,{right:e.jsxs("div",{className:"tk-row tight",children:[e.jsx("button",{type:"button",className:"tk-btn sm",onClick:()=>te(t=>!t),children:T?"Group by year":"Show every payment"}),e.jsx(de,{data:se,filename:"amortisation-schedule.csv",mime:"text/csv;charset=utf-8",label:"CSV"})]}),children:"Amortisation schedule"}),q&&e.jsxs("p",{className:"tk-muted",style:{margin:0,fontSize:"0.84rem"},children:["Principal first exceeds interest at payment ",e.jsxs("strong",{children:["#",q.n]})," (",M(q.date),") — until then, most of each payment is the cost of borrowing rather than repayment."]}),e.jsx(ge,{groups:O,cur:c}),e.jsx("div",{className:"tk-tablewrap",children:e.jsxs("table",{className:"tk-table",children:[e.jsx("thead",{children:e.jsxs("tr",{children:[e.jsx("th",{children:T?"#":"Year"}),e.jsx("th",{children:"Date"}),e.jsx("th",{className:"num",children:"Payment"}),e.jsx("th",{className:"num",children:"Interest"}),e.jsx("th",{className:"num",children:"Principal"}),e.jsx("th",{className:"num",children:"Balance"})]})}),e.jsx("tbody",{children:T?a.rows.map(t=>e.jsxs("tr",{children:[e.jsx("td",{className:"num",children:t.n}),e.jsx("td",{className:"tk-nowrap",children:M(t.date)}),e.jsx("td",{className:"num",children:s(t.payment)}),e.jsx("td",{className:"num",children:s(t.interest)}),e.jsx("td",{className:"num",children:s(t.principal)}),e.jsx("td",{className:"num",children:s(t.balance)})]},t.n)):O.map(t=>{const $=_.has(t.year);return e.jsxs(re.Fragment,{children:[e.jsxs("tr",{children:[e.jsx("td",{children:e.jsxs("button",{type:"button",onClick:()=>ne(t.year),"aria-expanded":$,style:{background:"none",border:0,padding:0,cursor:"pointer",color:"var(--tk-accent)",font:"inherit",fontWeight:600},children:[$?"▾":"▸"," ",t.year]})}),e.jsxs("td",{className:"tk-muted tk-nowrap",children:[t.rows.length," payments"]}),e.jsx("td",{className:"num",children:i(t.payment)}),e.jsx("td",{className:"num",children:i(t.interest)}),e.jsx("td",{className:"num",children:i(t.principal)}),e.jsx("td",{className:"num",children:i(t.endBalance)})]}),$&&t.rows.map(o=>e.jsxs("tr",{style:{background:"var(--tk-surface-2)"},children:[e.jsxs("td",{className:"num tk-muted",children:["#",o.n]}),e.jsx("td",{className:"tk-nowrap tk-muted",children:M(o.date)}),e.jsx("td",{className:"num",children:s(o.payment)}),e.jsx("td",{className:"num",children:s(o.interest)}),e.jsx("td",{className:"num",children:s(o.principal)}),e.jsx("td",{className:"num",children:s(o.balance)})]},o.n))]},t.year)})})]})}),e.jsx("div",{className:"tk-row",children:e.jsx(me,{text:()=>[`${a.freq.label} payment: ${s(a.actualPayment??a.payment)}`,`Total interest: ${s(a.totalInterest)}`,`Total repaid: ${s(a.totalPaid)}`,`${j(a.periods)} payments at ${l(u,3)}% on ${s(r)}`].join(`
2`),label:"Copy summary"})}),e.jsx(A,{kind:"info",children:"Principal and interest only. Arrangement fees, mortgage insurance, property taxes and early-repayment charges are not included — add them separately when comparing real offers."})]}):e.jsx(A,{kind:"info",children:"Enter a loan amount, an interest rate (0 or more) and a term between 0 and 60 years."})]})})}function Ne(c){const h=Math.round(c);if(!Number.isFinite(h)||h<=0)return"None";const d=Math.floor(h/12),n=h%12;return d?n?`${d}y ${n}m`:`${d} years`:`${h} mo`}function ge({groups:c,cur:h}){if(!c.length)return null;const d=Math.max(...c.map(n=>n.payment))||1;return e.jsxs("div",{children:[e.jsx("div",{style:{display:"flex",alignItems:"flex-end",gap:"2px",height:"110px"},role:"img","aria-label":"Interest and principal paid each year",children:c.map(n=>{const w=n.payment/d*100,N=n.payment?n.interest/n.payment*100:0;return e.jsxs("div",{title:`${n.year}: ${E(n.interest,h,0)} interest, ${E(n.principal,h,0)} principal`,style:{flex:1,minWidth:"3px",height:`${w}%`,display:"flex",flexDirection:"column",justifyContent:"flex-end",borderRadius:"3px 3px 0 0",overflow:"hidden"},children:[e.jsx("div",{style:{height:`${N}%`,background:"var(--tk-accent-2, #22d3ee)",opacity:.85}}),e.jsx("div",{style:{height:`${100-N}%`,background:"var(--tk-accent)"}})]},n.year)})}),e.jsxs("div",{className:"tk-row tight",style:{marginTop:"0.5rem",fontSize:"0.76rem"},children:[e.jsxs("span",{className:"tk-row tight",children:[e.jsx("span",{style:{width:10,height:10,borderRadius:2,background:"var(--tk-accent-2, #22d3ee)",display:"inline-block"}}),e.jsx("span",{className:"tk-muted",children:"Interest"})]}),e.jsxs("span",{className:"tk-row tight",children:[e.jsx("span",{style:{width:10,height:10,borderRadius:2,background:"var(--tk-accent)",display:"inline-block"}}),e.jsx("span",{className:"tk-muted",children:"Principal"})]}),e.jsx("span",{className:"tk-muted",children:"· one column per calendar year"})]})]})}export{Te as default};

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.