PageSourceSearch

https://vault22.com/_next/static/chunks/pages/app/hafiq/plan/purification-eda5241866e01bd7.js

js vault22.com collected 2026-10-02 00:12:07 UTC 59,135 bytes, 1 lines download raw bytes

1(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[76577],{6120:(e,t,a)=>{"use strict";a.r(t),a.d(t,{default:()=>M});var s=a(37876),n=a(77328),i=a.n(n),r=a(57307),o=a(69972),l=a(14232),d=a(91452),c=a(84714),u=a(35820),m=a(60363),p=a(77944),x=a(83554),h=a(38967),f=a(93387),g=a(44446),b=a(45922),y=a(33420);let v=["last12Months","calendarYear"],N={provisional:"bg-[#EEF2F5] text-[#4E5159]",review:"bg-amber-50 text-amber-800",confirmed:"bg-[#e8faf5] text-[#006f53]",settled:"bg-[#e8faf5] text-[#006f53]",reversed:"bg-slate-100 text-slate-600 line-through",unknown:"bg-slate-100 text-slate-700"};function j(e,t){return"".concat(e," ").concat(t.toLocaleString("en-GB",{minimumFractionDigits:2,maximumFractionDigits:2}))}function C(e){if(!e)return"";let t=new Date(/^\d{4}-\d{2}-\d{2}$/.test(e)?"".concat(e,"T12:00:00"):e);return Number.isNaN(t.getTime())?"":t.toLocaleDateString("en-GB",{day:"numeric",month:"short",year:"numeric"})}function E(e){if(!e||!/^\d{4}-\d{2}$/.test(e))return null!=e?e:"";let t=new Date("".concat(e,"-01T12:00:00"));return Number.isNaN(t.getTime())?e:t.toLocaleDateString("en-GB",{month:"short",year:"numeric"})}let w="mt-1 block w-full rounded-lg border border-[#D9E2EC] px-3 py-2 text-[#22252B]";function k(e){let{tone:t,children:a,testId:n}=e;return(0,s.jsx)("span",{"data-testid":n,className:"rounded px-1.5 py-0.5 text-xs ".concat(N[t]),children:a})}function I(e){let{testId:t,label:a,value:n,note:i,meta:r}=e;return(0,s.jsxs)("div",{"data-testid":t,className:"rounded-xl border border-[#D9E2EC] bg-white p-4",children:[(0,s.jsx)("p",{className:"text-sm font-medium text-[#4E5159]",children:a}),(0,s.jsx)("p",{className:"mt-1 text-2xl font-semibold text-[#22252B]",children:n}),(0,s.jsx)("p",{className:"mt-1 text-xs text-[#6C6F7C]",children:r}),i?(0,s.jsx)("p",{className:"mt-2 text-xs leading-5 text-[#4E5159]",children:i}):null]})}function R(e){var t;let{capability:a,now:n}=e,{customerId:i}=(0,d.n)(),{selectedCurrency:r,supportedCurrencies:o}=(0,u.H)(),{balanceVisibility:c}=(0,m.K)(),[p]=(0,l.useState)(()=>null!=n?n:new Date),x=(0,f.Sh)(p),[N,I]=(0,l.useState)("last12Months"),[R,D]=(0,l.useState)({status:"loading"}),[T,F]=(0,l.useState)(0),O=(0,l.useRef)(0),P="SUSPENDED"===a.state,_=a.can("sync"),L=a.can("record_disposal"),B=a.can("allocate"),U=_.allowed,[V,M]=(0,l.useState)(()=>(0,y.sZ)((r||"").toUpperCase(),x)),Y=(0,l.useRef)((0,f.NO)()),[q,G]=(0,l.useState)(!1),[K,z]=(0,l.useState)(null),[Z,H]=(0,l.useState)(null),[W,$]=(0,l.useState)(!1),[X,Q]=(0,l.useState)(null),[J,ee]=(0,l.useState)(!1),[et,ea]=(0,l.useState)(null),[es,en]=(0,l.useState)(null),[ei,er]=(0,l.useState)(""),[eo,el]=(0,l.useState)(""),[ed,ec]=(0,l.useState)(""),eu=(0,l.useRef)((0,f.NO)()),em=(0,l.useCallback)(async e=>{let t=++O.current;if(!i)return void D({status:"failed"});D(e=>"ok"===e.status?e:{status:"loading"});let a=(0,y.S)(N,p),s=e?"synced":null;if(e)try{await g.jA.sync(i,a)}catch(e){(0,f.Gt)(e).errorCode!==f.sz&&(s="failed")}try{let e=await g.jA.getLedger(i,a);if(t!==O.current)return;D(t=>({status:"ok",ledger:e,sync:null!=s?s:"ok"===t.status?t.sync:"skipped",refreshFailed:!1}))}catch(e){if(t!==O.current)return;D(e=>"ok"===e.status?{...e,refreshFailed:!0}:{status:"failed"})}},[i,N,p]);(0,l.useEffect)(()=>(D({status:"loading"}),em(U),()=>{O.current+=1}),[em,U,T]);let ep=(e,t)=>null!==t&&e?c?j(e,t):"*****":"Not available",ex="ok"===R.status?R.ledger:null,eh=(0,l.useMemo)(()=>ex?(0,y.m3)(ex,r):[],[ex,r]),ef=(0,l.useMemo)(()=>{var e;return Array.from(new Set([V.currencyCode,(r||"").toUpperCase(),...null!=(e=null==ex?void 0:ex.currencyTotals.map(e=>e.currencyCode))?e:[],...(null!=o?o:[]).map(e=>e.code.toUpperCase())].filter(Boolean)))},[V.currencyCode,r,ex,o]),eg=async()=>{if(q||!i||!L.allowed)return;H(null);let e=(0,y.nC)(V,x);if(!e.ok)return void z(e.error);G(!0),z(null);try{await g.jA.recordDisposal(i,{idempotencyKey:Y.current,...e.request}),Y.current=(0,f.NO)(),M((0,y.sZ)(e.request.currencyCode,x)),H("Saved ".concat(j(e.request.currencyCode,e.request.amount),'. It shows as "Recorded by you" because Vault22 has not verified it.')),await em(!1)}catch(a){let t=(0,f.Gt)(a);t.errorCode===f.sz?(Y.current=(0,f.NO)(),M((0,y.sZ)(e.request.currencyCode,x)),H('Saved. It shows as "Recorded by you" because Vault22 has not verified it.'),await em(!1)):("IDEMPOTENCY_KEY_REUSED"===t.errorCode&&(Y.current=(0,f.NO)(),await em(!1)),z((0,b.U)(t,"purification")))}finally{G(!1)}},eb=e=>{Q(e),ea(null),en(null),er(""),el(""),ec(""),eu.current=(0,f.NO)()},ey=()=>{Q(null),ea(null)},ev=async(e,t)=>{if(!J){ee(!0),ea(null);try{await e(),Q(null),en(t),await em(!1)}catch(t){let e=(0,f.Gt)(t);
1("VERSION_CONFLICT"===e.errorCode||"INVALID_TRANSITION"===e.errorCode)&&await em(!1),ea((0,b.U)(e,"purification"))}finally{ee(!1)}}},eN=ex?ex.disposals.filter(e=>"ACTIVE"===e.status):[],ej=ex?ex.disposals.filter(e=>"ACTIVE"!==e.status):[],eC=e=>{var t,a,n;if(!ex)return null;let r=(0,y.cZ)(ex,e),o="ACTIVE"===e.status,l=y.TY[e.kind];return(0,s.jsxs)("li",{className:"py-3 text-sm","data-testid":"purification-server-disposal","data-disposal-id":e.id,children:[(0,s.jsxs)("div",{className:"flex flex-wrap items-baseline justify-between gap-2",children:[(0,s.jsxs)("span",{className:"min-w-0",children:[(0,s.jsxs)("span",{className:"block text-[#22252B]",children:[l.label," \xb7 ",C(e.disposedOn)||"Date not recorded",e.recipient?" \xb7 ".concat(e.recipient):""]}),(0,s.jsxs)("span",{className:"mt-0.5 flex flex-wrap gap-1",children:[(0,s.jsx)(k,{tone:"provisional",testId:"purification-disposal-verification",children:"VERIFIED"===e.verificationState?"Verified":"RECONCILED"===e.verificationState?"Matched to your bank":"Recorded by you"}),!o&&(0,s.jsx)(k,{tone:"reversed",testId:"purification-disposal-reversed",children:"Reversed"})]}),o&&(0,s.jsx)("span",{className:"mt-1 block text-xs text-[#6C6F7C]","data-testid":"purification-disposal-allocation",children:(null!=(t=e.unallocatedAmount)?t:0)>0?"".concat(ep(e.currencyCode,e.unallocatedAmount)," not allocated to an item").concat((null!=(a=e.allocatedAmount)?a:0)>0?" \xb7 ".concat(ep(e.currencyCode,e.allocatedAmount)," allocated"):""):"Fully allocated"})]}),(0,s.jsx)("strong",{className:"text-[#22252B]",children:ep(e.currencyCode,e.amount)})]}),e.allocations.length>0&&(0,s.jsx)("ul",{className:"mt-2 space-y-1 rounded-lg bg-[#F7FAFC] p-2 text-xs text-[#4E5159]",children:e.allocations.map((t,a)=>{var n,r,o;let l=(null==X?void 0:X.type)==="reverseAllocation"&&X.allocationId===t.id;return(0,s.jsxs)("li",{"data-testid":"purification-allocation",children:[(0,s.jsxs)("span",{children:[ep(null!=(n=t.currencyCode)?n:e.currencyCode,t.amount)," allocated","REVERSED"===t.status?" \xb7 reversed":"PART_REVERSED"===t.status?" \xb7 ".concat(ep(null!=(r=t.currencyCode)?r:e.currencyCode,t.reversedAmount)," reversed"):""]}),B.allowed&&t.id&&"REVERSED"!==t.status&&!l&&(0,s.jsx)("button",{type:"button","data-testid":"purification-allocation-reverse",onClick:()=>eb({type:"reverseAllocation",allocationId:t.id}),className:"ml-2 font-medium text-[#006f53] underline",children:"Reverse"}),l&&(0,s.jsxs)("div",{className:"mt-2 space-y-2","data-testid":"purification-allocation-reverse-form",children:[(0,s.jsxs)("label",{className:"block",children:["Amount to reverse (leave empty for all)",(0,s.jsx)("input",{"data-testid":"purification-allocation-reverse-amount",inputMode:"decimal",value:eo,onChange:e=>el(e.target.value),className:w})]}),(0,s.jsxs)("label",{className:"block",children:["Reason, for example a refund",(0,s.jsx)("input",{"data-testid":"purification-action-reason",maxLength:200,value:ei,onChange:e=>er(e.target.value),className:w})]}),(0,s.jsxs)("span",{className:"flex gap-2",children:[(0,s.jsx)("button",{type:"button","data-testid":"purification-allocation-reverse-confirm",disabled:J,onClick:()=>(e=>{var t,a;let s=ei.trim();if(!s)return void ea("Say why you are reversing this allocation, for example a refund.");if(s.length>200)return void ea("Keep the reason to 200 characters or fewer.");let n=eo.trim()?(0,y.pg)(eo):null;if(eo.trim()&&null===n)return void ea("Enter an amount greater than zero, with at most two decimal places, or leave it empty to reverse all of it.");let r=Math.max(0,(null!=(t=e.amount)?t:0)-(null!=(a=e.reversedAmount)?a:0));if(null!==n&&n>r)return void ea("That is more than is still allocated.");if(!i||!e.id)return;let o=e.id;ev(()=>g.jA.reverseAllocation(i,o,{...null!==n?{amount:n}:{},reason:s}),"The allocation was reversed and the item reopened by that amount.")})(t),className:"rounded-lg border border-[#006f53] px-3 py-1 font-semibold text-[#006f53] disabled:opacity-60",children:J?"Reversing…":"Reverse allocation"}),(0,s.jsx)("button",{type:"button",onClick:ey,disabled:J,className:"px-2 py-1 text-[#4E5159]",children:"Cancel"})]})]})]},null!=(o=t.id)?o:"".concat(e.id,"-").concat(a))})}),o&&(0,s.jsxs)("div",{className:"mt-2 flex flex-wrap gap-x-4 gap-y-1",children:[B.allowed&&r.length>0&&(null!=(n=e.unallocatedAmount)?n:0)>0&&((null==X?void 0:X.type)!=="allocate"||X.disposalId!==e.id)&&(0,s.jsx)("button",{type:"button","data-testid":"purification-disposal-allocate",onClick:()=>eb({type:"allocate",disposalId:e.id}),className:"font-medium text-[#006f53] underline",children:"Allocate to an item"}),L.allowed&&((null==X?void 0:X.type)!=="reverseDisposal"||X.disposalId!==e.id)&&(0,s.jsx)("button",{type:"button","data-testid":"purification-disposal-reverse",onClick:()=>eb({type:"reverseDisposal",disposalId:e.id}),className:"font-medium text-[#4E5159] underline",children:"Rever
1se"})]}),(null==X?void 0:X.type)==="allocate"&&X.disposalId===e.id&&(0,s.jsxs)("div",{className:"mt-3 grid gap-2 rounded-xl border border-[#D9E2EC] bg-[#F7FAFC] p-3 sm:grid-cols-2","data-testid":"purification-allocate-form",children:[(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Item",(0,s.jsxs)("select",{"data-testid":"purification-allocate-target",value:ed,onChange:e=>ec(e.target.value),className:w,children:[(0,s.jsx)("option",{value:"",children:"Choose an item"}),r.map(e=>(0,s.jsxs)("option",{value:e.id,children:[E(e.periodYearMonth)," \xb7 ",ep(e.currencyCode,e.remainingAmount)," left"]},e.id))]})]}),(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Amount (",e.currencyCode,")",(0,s.jsx)("input",{"data-testid":"purification-allocate-amount",inputMode:"decimal",value:eo,onChange:e=>el(e.target.value),className:w})]}),(0,s.jsxs)("span",{className:"flex gap-2 sm:col-span-2",children:[(0,s.jsx)("button",{type:"button","data-testid":"purification-allocate-confirm",disabled:J,onClick:()=>((e,t)=>{var a,s;let n=t.find(e=>e.id===ed);if(!n)return void ea("Choose the item to allocate to.");let r=(0,y.pg)(eo);if(null===r)return void ea("Enter an amount greater than zero, with at most two decimal places.");if(r>(null!=(a=e.unallocatedAmount)?a:0)||r>(null!=(s=n.remainingAmount)?s:0))return void ea("That is more than is left on the item or on the disposal.");if(!i)return;let o=eu.current;ev(async()=>{await g.jA.allocate(i,e.id,{eventId:o,allocations:[{obligationId:n.id,amount:r}]}),eu.current=(0,f.NO)()},"The disposal was allocated to the item.")})(e,r),className:"rounded-lg bg-[#01C38D] px-3 py-1.5 text-sm font-semibold text-[#042B47] disabled:opacity-60",children:J?"Allocating…":"Allocate"}),(0,s.jsx)("button",{type:"button",onClick:ey,disabled:J,className:"px-2 py-1 text-sm text-[#4E5159]",children:"Cancel"})]})]}),(null==X?void 0:X.type)==="reverseDisposal"&&X.disposalId===e.id&&(0,s.jsxs)("div",{className:"mt-3 space-y-2 rounded-xl border border-[#D9E2EC] bg-[#F7FAFC] p-3","data-testid":"purification-disposal-reverse-form",children:[(0,s.jsx)("p",{className:"text-sm text-[#4E5159]",children:"Reversing keeps this disposal in your history and reopens any amount matched to an item."}),(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Reason",(0,s.jsx)("input",{"data-testid":"purification-action-reason",maxLength:200,value:ei,onChange:e=>er(e.target.value),className:w})]}),(0,s.jsxs)("span",{className:"flex gap-2",children:[(0,s.jsx)("button",{type:"button","data-testid":"purification-disposal-reverse-confirm",disabled:J,onClick:()=>(e=>{let t=ei.trim();return t?t.length>200?void ea("Keep the reason to 200 characters or fewer."):void(i&&ev(()=>g.jA.reverseDisposal(i,e.id,{expectedRevision:e.revision,reason:t}),"The disposal was reversed. It stays in your history, and any amount matched to an item is reopened.")):void ea("Say why you are reversing this disposal.")})(e),className:"rounded-lg border border-red-300 px-3 py-1 text-sm font-semibold text-red-700 disabled:opacity-60",children:J?"Reversing…":"Reverse disposal"}),(0,s.jsx)("button",{type:"button",onClick:ey,disabled:J,className:"px-2 py-1 text-sm text-[#4E5159]",children:"Keep it"})]})]})]},e.id)};return(0,s.jsxs)("main",{className:"mx-auto max-w-5xl px-4 py-6 sm:px-6","data-testid":"hafiq-purification-page","data-mode":"server",children:[(0,s.jsx)("h1",{className:"text-3xl font-semibold text-[#22252B]",children:"Purification (Tathhir)"}),(0,s.jsx)("p",{className:"mt-2 text-sm leading-6 text-[#4E5159]",children:"The review and disposal of identified non-compliant income; separate from Zakat and voluntary giving."}),(0,s.jsx)("div",{role:"radiogroup","aria-label":"Period",className:"mt-5 inline-flex rounded-lg border border-[#D9E2EC] bg-white p-1","data-testid":"purification-period",children:v.map(e=>(0,s.jsx)("button",{type:"button",role:"radio","aria-checked":N===e,"data-testid":"purification-period-".concat(e),onClick:()=>I(e),className:"rounded-md px-3 py-1.5 text-sm font-medium ".concat(N===e?"bg-[#e8faf5] text-[#006f53]":"text-[#4E5159] hover:bg-slate-50"),children:h.OL[e]},e))}),P&&(0,s.jsx)("p",{role:"status",className:"mt-4 rounded-lg bg-amber-50 p-3 text-sm text-amber-900","data-testid":"purification-suspended",children:null!=(t=a.publicReason)?t:"Purification is paused right now. Your records stay visible, and new records can be added once it resumes."}),"loading"===R.status?(0,s.jsx)("p",{className:"mt-5 text-sm text-[#4E5159]",role:"status","data-testid":"purification-server-loading",children:"Checking your purification ledger…"}):"failed"===R.status?(0,s.jsxs)("div",{className:"mt-5 flex flex-wrap items-center gap-3","data-testid":"purification-server-failed",children:[(0,s.jsx)("p",{className:"text-sm text-[#4E5159]",role:"status",children:"Your purification ledger could not be loaded right now. Nothing has been counted or changed."}),(0,s.jsx)("button",{type:"button","data-testid":"purification-server-retry",onClick:()=>F(e=>e+1),className:"text-sm font-medium text-[#006f53] underline underline-offset-2",children:"Try again"})]}):(0,s.jsx)(S,{ledger:R.ledger,sync:R.sync,refreshFailed:R.refreshFailed,currencyRows:eh,selectedCurrency:r,period:N,meta:e=>"".concat(e," \xb7 ").concat(h.OL[N]," \xb7 as of ").concat(C(null==ex?void 0:ex.asOf)||C(x)),moneyIn:ep}),ex&&(0,s.jsxs)("section",{className:"mt-6 rounded-2xl border border-[#D9E2EC] bg-white p-4 sm:p-5","data-testid":"purification-server-disposals","aria-label":"Disposals",children:[(0,s.jsx)("h2",{className:"text-lg font-semibold text-[#22252B]",children:"Disposals"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-[#6C6F7C]",children:"Disposals you recorded. Vault22 has not verified them, so they show as recorded by you."}),es&&(0,s.jsx)("p",{role:"status",className:"mt-3 text-sm text-[#006f53]","data-testid":"purification-action-success",children:es}),0===ex.disposals.length?(0,s.jsxs)("p",{className:"mt-3 text-sm text-[#4E5159]","data-testid":"purification-server-disposals-empty",children:["No dis
1posals recorded for ",h.OL[N].toLowerCase(),"."]}):(0,s.jsxs)(s.Fragment,{children:[eN.length>0?(0,s.jsx)("ul",{className:"mt-3 divide-y divide-[#EEF2F5]","data-testid":"purification-server-disposals-active",children:eN.map(eC)}):(0,s.jsxs)("p",{className:"mt-3 text-sm text-[#4E5159]","data-testid":"purification-server-disposals-none-active",children:["No active disposals for ",h.OL[N].toLowerCase(),"."]}),ej.length>0&&(0,s.jsxs)("div",{className:"mt-3 border-t border-[#EEF2F5] pt-3","data-testid":"purification-server-disposals-reversed",children:[(0,s.jsxs)("button",{type:"button","data-testid":"purification-reversed-toggle","aria-expanded":W,"aria-controls":"purification-reversed-disposals",onClick:()=>$(e=>!e),className:"text-sm font-medium text-[#4E5159] underline underline-offset-2",children:[W?"Hide":"Show"," ",ej.length," reversed disposal",1===ej.length?"":"s"]}),W&&(0,s.jsx)("ul",{id:"purification-reversed-disposals",className:"mt-2 divide-y divide-[#EEF2F5]","data-testid":"purification-server-disposals-reversed-list",children:ej.map(eC)})]})]}),et&&(0,s.jsx)("p",{role:"alert",className:"mt-2 text-sm text-red-700","data-testid":"purification-action-error",children:et}),!(0,y.n3)(ex)&&ex.disposals.some(e=>{var t;return"ACTIVE"===e.status&&(null!=(t=e.unallocatedAmount)?t:0)>0})&&(0,s.jsx)("p",{className:"mt-3 text-xs text-[#6C6F7C]","data-testid":"purification-allocation-unavailable",children:"Nothing is confirmed yet, so disposals stay unallocated. They are kept, and can be matched to an item once it is confirmed."}),(0,s.jsx)(A,{check:L,denialText:e=>{var t;return"CAPABILITY_SUSPENDED"===e?null!=(t=a.publicReason)?t:"Purification is paused right now. Your records stay visible.":"RULE_NOT_APPROVED"===e?"This needs an approved purification method, which is not published yet.":"This is not available for your account right now."},draft:V,today:x,currencyOptions:ef,recording:q,error:K,notice:Z,onChange:e=>{M(t=>({...t,...e})),z(null)},onSubmit:()=>void eg()})]}),ex&&ex.legacyLogEntries.length>0&&(0,s.jsxs)("section",{className:"mt-6 rounded-2xl border border-[#D9E2EC] bg-white p-4 sm:p-5","data-testid":"purification-legacy-entries","aria-label":"Earlier records",children:[(0,s.jsx)("h2",{className:"text-lg font-semibold text-[#22252B]",children:"Earlier records"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-[#6C6F7C]",children:"Disposals recorded before this ledger. They are kept as they were, read-only, and are not matched to items."}),(0,s.jsx)("ul",{className:"mt-3 divide-y divide-[#EEF2F5]",children:ex.legacyLogEntries.map(e=>(0,s.jsxs)("li",{className:"flex flex-wrap items-baseline justify-between gap-2 py-2 text-sm","data-testid":"purification-legacy-entry",children:[(0,s.jsxs)("span",{className:"text-[#22252B]",children:[C(e.donatedAt)||"Date not recorded"," \xb7 ",(0,s.jsx)("span",{className:"text-xs text-[#6C6F7C]",children:"Recorded by you"})]}),(0,s.jsx)("strong",{className:"text-[#22252B]",children:ep(e.currencyCode,e.amount)})]},e.id))})]})]})}function S(e){let{ledger:t,sync:a,refreshFailed:n,currencyRows:i,selectedCurrency:r,period:o,meta:l,moneyIn:d}=e,c="APPROVED"===t.ruleStatus?y.Sd.APPROVED:"RULE_NOT_APPROVED"===t.ruleStatus?y.Sd.RULE_NOT_APPROVED:y.Sd.UNKNOWN,u=(0,y.xV)(t),{coverage:m}=t,p=e=>t.reviewItems.filter(t=>t.currencyCode===e).length,x=t.obligations.filter(e=>"REVERSED"!==e.status);return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)("p",{className:"mt-5 rounded-lg p-3 text-sm ".concat("APPROVED"===t.ruleStatus?"bg-[#e8faf5] text-[#006f53]":"bg-[#F7FAFC] text-[#4E5159]"),"data-testid":"purification-rule-status","data-rule-status":t.ruleStatus,children:c}),"failed"===a&&(0,s.jsx)("p",{role:"status",className:"mt-2 text-sm text-amber-800","data-testid":"purification-sync-failed",children:"We could not check your latest transactions just now, so this shows your ledger as it was last updated."}),n&&(0,s.jsx)("p",{role:"status",className:"mt-2 text-sm text-amber-800","data-testid":"purification-refresh-failed",children:"Your change was saved, but the ledger could not be refreshed just now. The figures below are from before it; reload the page to see it."}),u?(0,s.jsx)("p",{className:"mt-5 text-sm text-[#4E5159]","data-testid":"purification-server-not-built",children:"Your ledger has not been built from your transactions yet. Nothing has been counted, so no totals are shown."}):0===i.length?"synced"===a?(0,s.jsxs)("p",{className:"mt-5 text-sm text-[#4E5159]","data-testid":"purification-server-none",children:["No interest received was found in your transactions for ",h.OL[o].toLowerCase(),"."]}):(0,s.jsxs)("p",{className:"mt-5 text-sm text-[#4E5159]","data-testid":"purification-server-unchecked",children:["Nothing is in your ledger for ",h.OL[o].toLowerCase(),", but your latest transactions have not been checked","failed"===a?" just now":" yet",". This does not mean nothing is due."]}):(0,s.jsx)("div",{className:"mt-5 space-y-4","data-testid":"purification-server-totals",children:i.map(e=>(0,s.jsxs)("section",{"aria-label":"Totals in ".concat(e.currencyCode),"data-testid":"purification-currency-totals","data-currency":e.currencyCode,children:[i.length>1&&(0,s.jsxs)("h2",{className:"mb-2 text-sm font-semibold text-[#22252B]",children:["In ",e.currencyCode,e.currencyCode===r.toUpperCase()?"":" (not converted or added to other currencies)"]}),(0,s.jsxs)("div",{className:"grid gap-3 sm:grid-cols-2 lg:grid-cols-4",children:[(0,s.jsx)(I,{testId:"purification-server-provisional",label:"Provisional",value:d(e.currencyCode,e.provisional),meta:l(e.currencyCode),note:"Interest received, counted in full while the method is not confirmed. Not a confirmed amount to dispose."}),(0,s.jsx)(I,{testId:"purification-server-review",label:"Needs review",value:d(e.currencyCode,e.reviewRequired),meta:l(e.currencyCode),note:"Kept apart until checked".concat(p(e.currencyCode)>0?"; ".concat(p(e.currencyCode)," investment income item").concat(1===p(e.currencyCode)?" is":"s are"," listed below and not counted"):"",".")}),(0,s.jsx)(I,{testId:"purification-server-outstanding",label:"Confirmed, still to dispose",value:d(e.currencyCode,e.outstandingConfirmed),meta:l(e.currencyCode),note:"Confirmed ".concat(d(e.currencyCode,e.confirmed)," \xb7 allocated ").concat(d(e.currencyCode,e.allocated),".")}),(0,s.jsx)(I,{testId:"purification-server-unallocated",label:"Disposals not allocated",value:d(e.currencyCode,e.unallocatedDisposals),meta:l(e.currencyCode),note:"Recorded by you and n
1ot yet matched to a confirmed item."})]})]},e.currencyCode))}),(0,s.jsx)("p",{className:"mt-3 text-sm text-[#4E5159]","data-testid":"purification-interest-charged-note",children:"Interest you pay, for example on a card or loan, is never counted here and never reduces these amounts."}),!u&&(0,s.jsxs)("p",{className:"mt-2 text-xs text-[#6C6F7C]","data-testid":"purification-server-coverage",children:[null!==m.transactionsScanned?"".concat(m.transactionsScanned," transaction").concat(1===m.transactionsScanned?"":"s"," checked"):"Transactions checked: not reported",m.pendingExcluded?" \xb7 ".concat(m.pendingExcluded," pending left out until they clear"):"",m.investmentIncomeNotSupported?" \xb7 ".concat(m.investmentIncomeNotSupported," investment income item").concat(1===m.investmentIncomeNotSupported?"":"s"," not supported yet"):""]}),x.length>0&&(0,s.jsxs)("section",{className:"mt-6 rounded-2xl border border-[#D9E2EC] bg-white p-4 sm:p-5","data-testid":"purification-obligations","aria-label":"Items",children:[(0,s.jsx)("h2",{className:"text-lg font-semibold text-[#22252B]",children:"Items from your transactions"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-[#6C6F7C]",children:"Each interest credit received, its status and what is left to dispose."}),(0,s.jsx)("ul",{className:"mt-3 divide-y divide-[#EEF2F5]",children:x.map(e=>{let t=y.qi[e.status];return(0,s.jsxs)("li",{className:"flex flex-wrap items-start justify-between gap-2 py-3 text-sm","data-testid":"purification-obligation","data-obligation-id":e.id,"data-status":e.status,children:[(0,s.jsxs)("span",{className:"min-w-0",children:[(0,s.jsxs)("span",{className:"block text-[#22252B]",children:["Interest received \xb7 ",E(e.periodYearMonth)||"Month not recorded"]}),(0,s.jsx)("span",{className:"mt-0.5 flex flex-wrap gap-1",children:(0,s.jsx)(k,{tone:t.tone,testId:"purification-obligation-status",children:t.label})}),e.statusReason&&(0,s.jsx)("span",{className:"mt-1 block text-xs text-[#6C6F7C]","data-testid":"purification-obligation-reason",children:y.kQ[e.statusReason]})]}),(0,s.jsxs)("span",{className:"text-right",children:[(0,s.jsx)("strong",{className:"block text-[#22252B]",children:d(e.currencyCode,e.amount)}),(0,s.jsxs)("span",{className:"block text-xs text-[#6C6F7C]","data-testid":"purification-obligation-remaining",children:[d(e.currencyCode,e.remainingAmount)," left"]})]})]},e.id)})})]}),t.reviewItems.length>0&&(0,s.jsxs)("section",{className:"mt-6 rounded-2xl border border-[#D9E2EC] bg-white p-4 sm:p-5","data-testid":"purification-review-items","aria-label":"Not supported yet",children:[(0,s.jsx)("h2",{className:"text-lg font-semibold text-[#22252B]",children:"Investment income: not supported yet"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-[#6C6F7C]",children:"A purification method for investment income is not supported yet. The
1se are not counted in any total, which does not mean nothing is due."}),(0,s.jsx)("ul",{className:"mt-3 divide-y divide-[#EEF2F5]",children:t.reviewItems.map(e=>(0,s.jsxs)("li",{className:"flex flex-wrap items-baseline justify-between gap-2 py-2 text-sm","data-testid":"purification-review-item",children:[(0,s.jsxs)("span",{className:"text-[#22252B]",children:["Investment income ",(0,s.jsx)("span",{className:"text-xs text-amber-800",children:"\xb7 coverage warning"})]}),(0,s.jsx)("strong",{className:"text-[#22252B]",children:d(e.currencyCode,e.amount)})]},e.transactionId))})]})]})}function A(e){let{check:t,denialText:a,draft:n,today:i,currencyOptions:r,recording:o,error:l,notice:d,onChange:c,onSubmit:u}=e;return t.allowed?(0,s.jsxs)("div",{className:"mt-4 rounded-xl border border-[#D9E2EC] bg-[#F7FAFC] p-4","data-testid":"purification-server-record-form",children:[(0,s.jsx)("h3",{className:"text-base font-semibold text-[#22252B]",children:"Record a disposal"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-[#4E5159]",children:"Record interest you gave away or returned. This keeps a record only; it does not send money."}),(0,s.jsxs)("fieldset",{className:"mt-3","data-testid":"purification-record-kind",children:[(0,s.jsx)("legend",{className:"text-sm font-medium text-[#4E5159]",children:"What you did"}),(0,s.jsx)("div",{className:"mt-2 grid gap-2 sm:grid-cols-2",children:g.mh.map(e=>(0,s.jsxs)("label",{className:"cursor-pointer rounded-xl border p-3 text-sm ".concat(n.kind===e?"border-[#006f53] bg-[#e8faf5]":"border-[#D9E2EC] bg-white"),children:[(0,s.jsxs)("span",{className:"flex items-center gap-2 font-semibold text-[#22252B]",children:[(0,s.jsx)("input",{type:"radio",name:"purification-disposal-kind",value:e,checked:n.kind===e,onChange:()=>c({kind:e}),"data-testid":"purification-record-kind-".concat(e.toLowerCase()),className:"accent-[#006f53]"}),y.TY[e].label]}),(0,s.jsx)("span",{className:"mt-1 block text-xs leading-5 text-[#4E5159]",children:y.TY[e].explanation})]},e))})]}),(0,s.jsxs)("div",{className:"mt-3 grid gap-3 sm:grid-cols-2",children:[(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Amount",(0,s.jsx)("input",{"data-testid":"purification-server-record-amount",inputMode:"decimal",value:n.amount,onChange:e=>c({amount:e.target.value}),className:w})]}),(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Currency",(0,s.jsxs)("select",{"data-testid":"purification-server-record-currency",value:n.currencyCode,onChange:e=>c({currencyCode:e.target.value}),className:w,children:[n.currencyCode?null:(0,s.jsx)("option",{value:"",children:"Choose"}),r.map(e=>(0,s.jsx)("option",{value:e,children:e},e))]})]}),(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Date",(0,s.jsx)("input",{"data-testid":"purification-server-record-date",type:"date",max:i,value:n.disposedOn,onChange:e=>c({disposedOn:e.target.value}),className:w})]}),(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["RETURN_TO_SOURCE"===n.kind?"Returned to":"Charity or cause",(0,s.jsx)("input",{"data-testid":"purification-server-record-recipient",maxLength:120,value:n.recipient,onChange:e=>c({recipient:e.target.value}),className:w})]}),"CHARITABLE_DISPOSAL"===n.kind&&(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Category (optional)",(0,s.jsx)("input",{"data-testid":"purification-server-record-category",maxLength:60,value:n.charityCategory,onChange:e=>c({charityCategory:e.target.value}),placeholder:"For example, education",className:w})]}),(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159]",children:["Reference (optional)",(0,s.jsx)("input",{"data-testid":"purification-server-record-reference",maxLength:80,value:n.reference,onChange:e=>c({reference:e.target.value}),placeholder:"A receipt or transfer reference",className:w})]}),(0,s.jsxs)("label",{className:"block text-sm font-medium text-[#4E5159] sm:col-span-2",children:["Note (optional)",(0,s.jsx)("input",{"data-testid":"purification-server-record-notes",maxLength:500,value:n.notes,onChange:e=>c({notes:e.target.value}),className:w})]})]}),(0,s.jsx)("button",{type:"button","data-testid":"purification-server-record-submit",onClick:u,disabled:o,className:"mt-3 rounded-lg bg-[#01C38D] px-4 py-2.5 text-sm font-semibold text-[#042B47] disabled:opacity-60",children:o?"Recording…":"Record disposal"}),l?(0,s.jsx)("p",{role:"alert",className:"mt-2 text-sm text-red-700","data-testid":"purification-server-record-error",children:l}):null,d?(0,s.jsx)("p",{role:"status",className:"mt-2 text-sm text-[#006f53]","data-testid":"purification-server-record-success",children:d}):null]}):(0,s.jsxs)("p",{className:"mt-4 text-sm text-[#4E5159]","data-testid":"purification-server-record-unavailable",children:["Recording a disposal: ",a(t.denial)]})}function D(e){let{evidence:t,now:a}=e,n=(0,c.H2)("purification");return"AVAILABLE"===n.state||"SUSPENDED"===n.state?(0,s.jsx)(R,{capability:n,now:a}):"LOADING"===n.state?(0,s.jsx)("main",{className:"mx-auto max-w-5xl px-4 py-6 sm:px-6","data-testid":"hafiq-purification-page",children:(0,s.jsx)("p",{className:"text-sm text-[#4E5159]",role:"status",children:"Checking your purification ledger…"})}):(0,s.jsx)(B,{evidence:t,now:a})}let T=new Set(["critical","realtime","background"]),F=["last12Months","calendarYear"],O={last12Months:"the last 12 months",calendarYear:"this calendar year"};function P(e){let t=e instanceof Date?e:new Date(e);return Number.isNaN(t.getTime())?"":t.toLocaleDateString("en-GB",{day:"numeric",month:"short",year:"numeric"})}function _(e){let{testId:t,label:a,value:n,note:i,meta:r}=e;return(0,s.jsxs)("div",{"data-testid":t,className:"rounded-xl border border-[#D9E2EC] bg-white p-4",children:[(0,s.jsx)("p",{className:"text-sm font-medium text-[#4E5159]",children:a}),(0,s.jsx)("p",{className:"mt-1 text-2xl font-semibold text-[#22252B]",children:n}),(0,s.jsx)("p",{className:"mt-1 text-xs text-[#6C6F7C]",children:r}),i?(0,s.jsx)("p",{className:"mt-2 text-xs leading-5 text-[#4E5159]",children:i}):null]})}function L(e){let{label:t,children:a,className:n=""}=e;return(0,s.jsxs)("div",{className:"min-w-0 ".concat(n),children:[(0,s.jsx)("span",{className:"block text-xs text-[#6C6F7C] md:hidden",children:t}),(0,s.jsx)("span",{className:"block text-sm text-[#22252B]",children:a})]})}function B(e){let{evidence:t,now:a}=e,{customerId:n,transactions:i,accounts:r,exchangeRates:o,loadingPhase:c,aggregate:f,error:g}=(0,d.n)(),{selectedCurrency:b}=(0,u.H)(),{balanceVisibility:y}=(0,m.K)(),v="available"!==t.outcome,[N]=(0,l.useState)(()=>null!=a?a:new Date),[j,C]=(0,l.useState)("last12Months"),[E,w]=(0,l.useState)(()=>new Set),[k,I]=(0,l.useState)(null),[R,S]=(0,l.useState)(""),[A,D]=(0,l.useState)([]),[L,B]=(0,l.useState)("loading"),[V,M]=(0,l.useState)(0),[Y,q]=(0,l.useState)(""),[G,K]=(0,l.useState)(!1),[z,Z]=(0,l.useState)(null),[H,W]=(0,l.useState)(null);(0,l.useEffect)(()=>{let e=!1;return n?(B("loading"),p.b.listPurificationEntries(n).then(t=>{e||(D(t),B("loaded"))}).catch(()=>{e||(D([]),B("failed"))}),()=>{e=!0}):(D([]),B("loaded"),()=>{e=!0})},[n,V]);let $=(0,l.useMemo)(()=>(0,x.y)(null!=i?i:[],b,null!=o?o:[],{now:N}),[i,b,o,N]),X=Number(R),Q=(0,l.useMemo)(()=>(0,h.ND)({signals:$,currency:b,period:j,now:N,transactions:i,accounts:r,exchangeRates:o,excludedIds:E,disposals:A,userNonPermissiblePct:Number.isFinite(X)&&X>0&&X<=100?X:null,approvedInvestmentFactorPct:null}),[$,b,j,N,i,r,o,E,A,X]),J=0===(null!=i?i:[]).length,ee=J&&!f&&!!g,et=J&&!ee&&(!f||T.has(null!=c?c:"")),ea=et?"Checking…":ee?"Not available":null,es=!ea&&null===Q.coverage.transactionsFrom,en=(e,t)=>
1y?"".concat(e," ").concat(t.toLocaleString("en-GB",{minimumFractionDigits:2,maximumFractionDigits:2})):"*****",ei=e=>en(Q.currency,e),er="".concat(Q.currency," \xb7 ").concat(h.OL[j]," \xb7 as of ").concat(P(N)),{totals:eo}=Q,el=Q.rows.some(e=>"investmentIncome"===e.sourceType),ed=(0,h.Eu)(Q,"bankInterest"),ec=e=>"loading"===L?"Checking…":"failed"===L?"Not available":e(),eu=!v&&!ea&&"loaded"===L&&ed>0,em=""!==R.trim()&&!(Number.isFinite(X)&&X>=0&&X<=100),ep=async()=>{let e=Math.round(100*Number(Y))/100;if(Z(null),W(null),eu){if(!(e>0))return void Z("Enter the amount you disposed of.");if(e>ed)return void Z("A disposal cannot be more than the outstanding bank interest (".concat(ei(ed),")."));if(!n)return void Z("Your profile is still loading. Please try again.");K(!0);try{var t;let a=await p.b.recordPurificationEntry(n,{type:"Riba",amount:e,currencyCode:Q.currency,periodYearMonth:(t=new Date,"".concat(t.getFullYear(),"-").concat(String(t.getMonth()+1).padStart(2,"0"))),sourceDescription:"Bank interest disposal recorded by you"});D(t=>[{...a,amount:a.amount>0?a.amount:e,currencyCode:a.currencyCode||Q.currency,donatedAt:a.donatedAt||new Date().toISOString()},...t]),W(a.id||"recorded"),q("")}catch(e){Z("Your disposal could not be recorded. Nothing was saved; please try again.")}finally{K(!1)}}},ex=null!=ea?ea:es?"No transactions yet":eo.identifiedCount>0?ei(eo.identified):Q.coverage.unconvertedCount>0?"Not available":"None identified",eh=null!=ea?ea:es?"No transactions yet":eo.identifiedCount>0?ec(()=>ei(eo.outstanding)):"Nothing identified",ef=null!=ea?ea:es?"No transactions yet":eo.needsReviewCount>0?"".concat(eo.needsReviewCount," item").concat(1===eo.needsReviewCount?"":"s"):"Nothing to review",eg=[eo.needsReviewInvestmentCount>0?"Investment income of ".concat(ei(eo.needsReviewBase),". The amount to purify is not known until an approved purification factor is available.").concat(null!==eo.needsReviewUserEstimate?" Estimate by you: ".concat(ei(eo.needsReviewUserEstimate)," (not counted)."):""):"",eo.needsReviewOtherCount>0?"".concat(eo.needsReviewOtherCount," other credit").concat(1===eo.needsReviewOtherCount?"":"s"," of ").concat(ei(eo.needsReviewOtherBase)," ").concat(1===eo.needsReviewOtherCount?"is":"are"," not counted until checked; the ledger says why."):""].filter(Boolean).join(" "),{coverage:eb}=Q,ey="loading"===L?"Checking…":"failed"===L?"Not available":eo.disposedCount>0?ei(eo.disposedRecorded):"None recorded";return(0,s.jsxs)("main",{className:"mx-auto max-w-5xl px-4 py-6 sm:px-6","data-testid":"hafiq-purification-page",children:[(0,s.jsx)("h1",{className:"text-3xl font-semibold text-[#22252B]",children:"Purification (Tathhir)"}),(0,s.jsx)("p",{className:"mt-2 text-sm leading-6 text-[#4E5159]",children:"The review and disposal of identified non-compliant income; separate from Zakat and voluntary giving."}),(0,s.jsx)("div",{role:"radiogroup","aria-label":"Period",className:"mt-5 inline-flex rounded-lg border border-[#D9E2EC] bg-white p-1","data-testid":"purification-period",children:F.map(e=>(0,s.jsx)("button",{type:"button",role:"radio","aria-checked":j===e,"data-testid":"purification-period-".concat(e),onClick:()=>C(e),className:"rounded-md px-3 py-1.5 text-sm font-medium ".concat(j===e?"bg-[#e8faf5] text-[#006f53]":"text-[#4E5159] hover:bg-slate-50"),children:h.OL[e]},e))}),(0,s.jsxs)("section",{className:"mt-5 grid gap-3 sm:grid-cols-2 lg:grid-cols-4","data-testid":"purification-totals","aria-label":"Purification totals",children:[(0,s.jsx)(_,{testId:"purification-total-identified",label:"Identified amount",value:ex,meta:er,note:v?"Estimate: bank interest received, counted in full.":"Bank interest received, counted in full."}),(0,s.jsx)(_,{testId:"purification-total-review",label:"Needs review",meta:er,value:ef,note:!ea&&eo.needsReviewCount>0?eg:void 0}),(0,s.jsx)(_,{testId:"purification-total-outstanding",label:"Outstanding to dispose",value:eh,meta:er,note:"failed"===L?"Your recorded disposals could not be loaded, so the amount left is not shown.":"".concat(v?"Estimate: identified":"Identified"," amount less the disposals you recorded. Items needing review are not included.")}),(0,s.jsx)(_,{testId:"purification-total-disposed",label:"Disposed / recorded",value:ey,meta:er,note:eo.unconvertedDisposalCount>0?"".concat(eo.unconvertedDisposalCount," record").concat(1===eo.unconvertedDisposalCount?"":"s"," in another currency could not be converted and ").concat(1===eo.unconvertedDisposalCount?"is":"are"," not included."):"Recorded by you. Kept separate from Zakat paid and voluntary giving."})]}),(0,s.jsx)("p",{className:"mt-3 text-sm text-[#4E5159]","data-testid":"purification-interest-charged-note",children:"Interest you pay, for example on a card or loan, is never counted here and never reduces these amounts."}),!ea&&eb.startsAfterPeriodStart&&eb.transactionsFrom?(0,s.jsxs)("p",{className:"mt-2 text-sm text-[#4E5159]","data-testid":"purification-coverage-start",children:["The transactions available here start on ",P(eb.transactionsFrom),", so anything received before then in this period is not included."]}):null,!ea&&eb.unconvertedCount>0?(0,s.jsxs)("p",{className:"mt-2 text-sm text-[#4E5159]","data-testid":"purification-coverage-unconverted",children:[eb.unconvertedCount," credit",1===eb.unconvertedCount?"":"s"," in another currency could not be converted into ",Q.currency," and ",1===eb.unconvertedCount?"is":"are"," not shown."]}):null,(0,s.jsxs)("section",{className:"mt-6 rounded-2xl border border-[#D9E2EC] bg-white p-4 sm:p-5","data-testid":"purification-ledger","aria-label":"Source ledger",children:[(0,s.jsx)("h2",{className:"text-lg font-semibold text-[#22252B]",children:"Source ledger"}),(0,s.jsxs)("p",{className:"mt-1 text-sm text-[#6C6F7C]",children:["Bank interest received and investment income from your transactions in this period. Amou
1nts are in ",Q.currency,"."]}),el?(0,s.jsxs)("label",{className:"mt-4 block max-w-sm text-sm font-medium text-[#4E5159]",children:[(0,s.jsx)("span",{children:"Your own estimate of non-permissible revenue (%) — optional"}),(0,s.jsx)("input",{"data-testid":"purification-user-pct",inputMode:"decimal",type:"number",min:"0",max:"100",step:"any",value:R,"aria-invalid":em,onChange:e=>S(e.target.value),className:"mt-1 block w-full rounded-lg border border-[#D9E2EC] px-3 py-2 text-[#22252B]"}),em?(0,s.jsx)("span",{role:"alert",className:"mt-1 block text-xs font-normal text-red-700","data-testid":"purification-user-pct-error",children:"Enter a percentage from 0 to 100."}):null,(0,s.jsx)("span",{className:"mt-1 block text-xs font-normal leading-5 text-[#6C6F7C]",children:'Shows an "estimate by you" on investment income. It is not an approved factor and is not added to any total.'})]}):null,et?(0,s.jsx)("p",{className:"mt-4 text-sm text-[#4E5159]",role:"status",children:"Checking your transactions…"}):ee?(0,s.jsx)("p",{className:"mt-4 text-sm text-[#4E5159]",role:"status","data-testid":"purification-ledger-failed",children:"Your transactions could not be loaded right now, so nothing can be shown here. Nothing has been counted."}):es?(0,s.jsx)("p",{className:"mt-4 text-sm text-[#4E5159]","data-testid":"purification-ledger-no-history",children:"There are no transactions here to check yet. Once your accounts have transactions, bank interest and investment income will show here."}):0===Q.rows.length?(0,s.jsx)("p",{className:"mt-4 text-sm text-[#4E5159]","data-testid":"purification-ledger-empty",children:eb.unconvertedCount>0?"Nothing can be shown in ".concat(Q.currency," for ").concat(O[j],"; the credits found could not be converted."):"No bank interest or investment income found in your transactions for ".concat(O[j],".")}):(0,s.jsxs)("div",{className:"mt-4",children:[(0,s.jsxs)("div",{className:"hidden grid-cols-[6.5rem_minmax(0,1.6fr)_minmax(0,1fr)_7rem_minmax(0,1.3fr)_8rem_7rem] gap-3 border-b border-[#EEF2F5] pb-2 text-xs font-medium text-[#6C6F7C] md:grid",children:[(0,s.jsx)("span",{children:"Date"}),(0,s.jsx)("span",{children:"Source description"}),(0,s.jsx)("span",{children:"Account"}),(0,s.jsx)("span",{children:"Base amount"}),(0,s.jsx)("span",{children:"Method / source"}),(0,s.jsx)("span",{children:"Requiring action"}),(0,s.jsx)("span",{children:"Remaining"})]}),(0,s.jsx)("ul",{className:"divide-y divide-[#EEF2F5]",children:Q.rows.map(e=>(0,s.jsx)(U,{row:e,money:ei,moneyIn:en,settlementValue:ec,whyOpen:k===e.id,onWhy:()=>I(t=>t===e.id?null:e.id),onToggle:()=>{let t;return t=e.id,w(e=>{let a=new Set(e);return a.has(t)?a.delete(t):a.add(t),a})}},e.id))}),E.size>0?(0,s.jsx)("p",{className:"mt-3 text-xs text-[#6C6F7C]","data-testid":"purification-excluded-note",children:"Items you untick are left out of the totals for this visit only. Nothing is saved and your transactions are not changed."}):null]})]}),(0,s.jsxs)("section",{className:"mt-6 rounded-2xl border border-[#D9E2EC] bg-white p-4 sm:p-5","data-testid":"purification-disposals","aria-label":"Disposals",children:[(0,s.jsx)("h2",{className:"text-lg font-semibold text-[#22252B]",children:"Disposals"}),(0,s.jsx)("p",{className:"mt-1 text-sm text-[#6C6F7C]",children:"Disposals you recorded in this period, allocated against the oldest outstanding item of the same kind."}),"loading"===L?(0,s.jsx)("p",{className:"mt-3 text-sm text-[#4E5159]",role:"status",children:"Checking your records…"}):"failed"===L?(0,s.jsxs)("div",{className:"mt-3 flex flex-wrap items-center gap-3",children:[(0,s.jsx)("p",{className:"text-sm text-[#4E5159]",role:"status","data-testid":"purification-disposals-failed",children:"Your recorded disposals could not be loaded right now."}),(0,s.jsx)("button",{type:"button","data-testid":"purification-disposals-retry",onClick:()=>M(e=>e+1),className:"text-sm font-medium text-[#006f53] underline underline-offset-2",children:"Try again"})]}):0===Q.disposals.length?(0,s.jsxs)("p",{className:"mt-3 text-sm text-[#4E5159]","data-testid":"purification-disposals-empty",children:["No dis
1posals recorded for ",O[j],"."]}):(0,s.jsx)("ul",{className:"mt-3 divide-y divide-[#EEF2F5]",children:Q.disposals.map(e=>(0,s.jsxs)("li",{className:"flex flex-wrap items-baseline justify-between gap-2 py-2 text-sm","data-testid":"purification-disposal",children:[(0,s.jsxs)("span",{className:"min-w-0",children:[(0,s.jsxs)("span",{className:"block text-[#22252B]",children:[h.a3[e.sourceType]," \xb7 ",P(e.donatedAt)||e.periodYearMonth]}),(0,s.jsxs)("span",{className:"block text-xs text-[#6C6F7C]",children:["Recorded by you",null===e.amountInCurrency?" \xb7 not converted into ".concat(Q.currency,", so not allocated"):0===e.allocated&&"investmentIncome"===e.sourceType?" \xb7 not matched: the investment income amount is not known yet":e.unallocated>0?" \xb7 ".concat(ei(e.allocated)," allocated, ").concat(ei(e.unallocated)," not matched to an item"):" \xb7 fully allocated"]})]}),(0,s.jsx)("strong",{className:"text-[#22252B]",children:en(e.currencyCode,e.amount)})]},e.id||"".concat(e.periodYearMonth,"-").concat(e.amount)))}),v?(0,s.jsx)("p",{className:"mt-4 text-sm text-[#4E5159]","data-testid":"purification-record-unavailable",children:"Recording disposals opens when the purification method is published."}):eu||H?eu?(0,s.jsxs)("div",{className:"mt-4 rounded-xl border border-[#D9E2EC] bg-[#F7FAFC] p-4","data-testid":"purification-record-form",children:[(0,s.jsxs)("label",{className:"block max-w-xs text-sm font-medium text-[#4E5159]",children:[(0,s.jsxs)("span",{children:["Bank interest you disposed of (",Q.currency,")"]}),(0,s.jsx)("input",{"data-testid":"purification-record-amount",inputMode:"decimal",type:"number",min:"0",step:"any",value:Y,onChange:e=>q(e.target.value),className:"mt-1 block w-full rounded-lg border border-[#D9E2EC] px-3 py-2 text-[#22252B]"})]}),(0,s.jsx)("button",{type:"button","data-testid":"purification-record-submit",onClick:()=>void ep(),disabled:G,className:"mt-3 rounded-lg bg-[#01C38D] px-4 py-2.5 text-sm font-semibold text-[#042B47] disabled:opacity-60",children:G?"Recording…":"Record disposal"}),z?(0,s.jsx)("p",{role:"alert",className:"mt-2 text-sm text-red-700","data-testid":"purification-record-error",children:z}):null,H?(0,s.jsx)("p",{role:"status",className:"mt-2 text-sm text-[#006f53]","data-testid":"purification-record-success",children:'Saved. It shows as "Recorded by you" because Vault22 has not verified it.'}):null]}):(0,s.jsx)("p",{role:"status",className:"mt-4 text-sm text-[#006f53]","data-testid":"purification-record-success",children:'Saved. It shows as "Recorded by you" because Vault22 has not verified it.'}):et||"loading"===L?null:"failed"===L?(0,s.jsx)("p",{className:"mt-4 text-sm text-[#4E5159]","data-testid":"purification-record-waiting",children:"You can record a disposal once your earlier records have loaded, so nothing is counted twice."}):(0,s.jsx)("p",{className:"mt-4 text-sm text-[#4E5159]","data-testid":"purification-record-none",children:"There is no outstanding bank interest in this period to record a disposal against."})]})]})}function U(e){let{row:t,money:a,moneyIn:n,settlementValue:i,whyOpen:r,onWhy:o,onToggle:l}=e,d="profitUnverified"===t.reviewReason?"Islamic profit, not interest? Untick":"bankInterest"===t.sourceType?"Not interest? Untick":"Not investment income? Untick",c=null!==t.amountRequiringAction?a(t.amountRequiringAction):null!==t.userEstimate?"Not known \xb7 estimate by you ".concat(a(t.userEstimate)):"factorUnavailable"===t.reviewReason?"Not known yet":"Not counted until checked",u=null!==t.remaining?i(()=>{var e;return a(null!=(e=t.remaining)?e:0)}):"—";return(0,s.jsxs)("li",{className:"py-3 ".concat(t.excluded?"opacity-60":""),"data-testid":"purification-row","data-row-id":t.id,"data-source-type":t.sourceType,"data-excluded":t.excluded?"true":"false",children:[(0,s.jsxs)("div",{className:"grid grid-cols-2 gap-3 md:grid-cols-[6.5rem_minmax(0,1.6fr)_minmax(0,1fr)_7rem_minmax(0,1.3fr)_8rem_7rem]",children:[(0,s.jsx)(L,{label:"Date",children:P(t.date)}),(0,s.jsxs)(L,{label:"Source description",className:"col-span-2 md:col-span-1",children:[(0,s.jsx)("span",{className:"block break-words",children:t.description}),(0,s.jsxs)("span",{className:"mt-0.5 flex flex-wrap gap-1",children:[(0,s.jsx)("span",{className:"rounded bg-[#EEF2F5] px-1.5 py-0.5 text-xs text-[#4E5159]",children:t.label}),(0,s.jsx)("span",{className:"rounded px-1.5 py-0.5 text-xs ".concat("REVIEW_REQUIRED"===t.status?"bg-amber-50 text-amber-800":"bg-[#e8faf5] text-[#006f53]"),children:h.Nh[t.status]}),"none"!==t.settlement?(0,s.jsx)("span",{className:"rounded bg-[#EEF2F5] px-1.5 py-0.5 text-xs text-[#4E5159]",children:"full"===t.settlement?"Disposal recorded by you":"Part recorded by you"}):null]})]}),(0,s.jsx)(L,{label:"Account",children:t.accountName||"Account not named"}),(0,s.jsxs)(L,{label:"Base amount",children:[a(t.baseAmount),t.originalCurrency&&null!==t.originalAmount?(0,s.jsxs)("span",{className:"block text-xs text-[#6C6F7C]",children:["from ",n(t.originalCurrency,t.originalAmount)]}):null]}),(0,s.jsx)(L,{label:"Method / source",children:t.method}),(0,s.jsx)(L,{label:"Requiring action",children:(0,s.jsx)("span",{"data-testid":"purification-row-requiring",children:c})}),(0,s.jsx)(L,{label:"Remaining",children:(0,s.jsx)("span",{"data-testid":"purification-row-remaining",children:u})})]}),(0,s.jsxs)("div",{className:"mt-2 flex flex-wrap items-center gap-x-4 gap-y-1 text-sm",children:[(0,s.jsx)("button",{type:"button","data-testid":"purification-row-why","aria-expanded":r,onClick:o,className:"font-medium text-[#006f53] underline underline-offset-2",children:"Why is this here?"}),(0,s.jsxs)("label",{className:"inline-flex cursor-pointer items-center gap-2 text-[#4E5159]",children:[(0,s.jsx)("input",{type:"checkbox","data-testid":"purification-row-include",checked:!t.excluded,onChange:l,className:"h-4 w-4 accent-[#006f53]"}),(0,s.jsx)("span",{children:t.excluded?"Left out for this visit — tick to count it again":d})]})]}
1),r?(0,s.jsx)("p",{className:"mt-2 rounded-lg bg-[#F7FAFC] p-3 text-sm leading-6 text-[#4E5159]","data-testid":"purification-row-why-text",children:t.why}):null]})}var V=a(85249);function M(){return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(i(),{children:(0,s.jsx)("title",{children:"Purification | Islamic finance hub | Vault22"})}),(0,s.jsx)(r.Ay,{children:()=>(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(V.Ay,{active:"purification"}),(0,s.jsx)(o.hf,{domain:"purification",methodologyKey:o.ko,title:"Purification (Tathhir)",children:e=>(0,s.jsx)(D,{evidence:e})})]})})]})}},20028:(e,t,a)=>{(window.__NEXT_P=window.__NEXT_P||[]).push(["/app/hafiq/plan/purification",function(){return a(6120)}])},45922:(e,t,a)=>{"use strict";a.d(t,{U:()=>i});var s=a(93387);let n={purification:"Purification",zakat:"Zakat assessments",giving:"Giving records"};function i(e,t){let a=(0,s.Gt)(e);switch(a.errorCode){case"CAPABILITY_SUSPENDED":return"".concat(n[t]," are paused right now. Your records stay visible; nothing was changed.");case"RULE_NOT_APPROVED":return"This step needs rules approved by a Shariah scholar, which are not published yet. Nothing was changed.";case"PROVIDER_UNAVAILABLE":return"This is not available because no provider is set up for it. Nothing was changed.";case"IDEMPOTENCY_KEY_REUSED":return"An earlier attempt with different details was already saved. Check your records below before trying again.";case"VERSION_CONFLICT":case"INVALID_TRANSITION":return"This record changed since you opened it, so it has been reloaded. Check it and try again.";case"OBLIGATION_NOT_CONFIRMED":return"Only confirmed items can have a disposal allocated to them. Nothing was changed.";case"CURRENCY_MISMATCH":return"The disposal and the item are in different currencies, so they cannot be matched. Nothing was changed.";case"ALLOCATION_EXCEEDS_REMAINING":return"That is more than is left on the item or on the disposal. Nothing was changed.";case"DUPLICATE_SOURCE":return"The same account appears twice in this assessment. Remove the duplicate and try again.";case"DUPLICATE_SOURCE_TRANSACTION":return"This bank payment is already in your giving records, so it was not added again.";case"USE_PURIFICATION_LEDGER":return"Purification disposals are recorded in Purification, not here. Nothing was changed."}return 404===a.status?"".concat(n[t]," are not available for your account right now. Nothing was changed."):412===a.status&&a.message?"".concat(a.message.replace(/\.?$/,".")," Nothing was changed."):null===a.status||a.errorCode===s.sz||a.status>=500&&a.status<600?"We could not reach Vault22 just now. Your details are kept here; please try again.":"Something went wrong and nothing was changed. Please try again."}},69972:(e,t,a)=>{"use strict";a.d(t,{c0:()=>d,hf:()=>m,ko:()=>c});var s=a(37876),n=a(48230),i=a.n(n),r=a(14232),o=a(80487),l=a(91452);let d="v22-uae-zakat-v1",c="v22-uae-purification-v1";function u(e){if(!e)return"Not published";let t=new Date(e);return Number.isNaN(t.getTime())?"Not published":t.toLocaleDateString()}function m(e){let{domain:t,methodologyKey:a,title:n,children:i}=e,{customerId:d}=(0,l.n)(),[c,m]=(0,r.useState)(null),[x,h]=(0,r.useState)(!0);if((0,r.useEffect)(()=>{let e=!1;return(m(null),d)?(h(!0),o.TS.lookup(d,{domain:t,methodologyKey:a}).then(t=>{e||m(t)}).catch(()=>{e||m(null)}).finally(()=>{e||h(!1)}),()=>{e=!0}):(h(!1),()=>{e=!0})},[d,t,a]),x)return(0,s.jsx)(p,{title:"Checking ".concat(n," methodology"),body:"Vault22 is checking the current published UAE evidence.",testId:"hafiq-plan-evidence-loading"});if(!function(e,t,a){let s=arguments.length>3&&void 0!==arguments[3]?arguments[3]:new Date,n=null==e?void 0:e.methodology;if((null==e?void 0:e.outcome)!=="available"||!n||"UAE"!==n.market||n.domain!==t||n.methodologyKey!==a)return!1;let i=new Date(n.asOf),r=n.expiresAt?new Date(n.expiresAt):null;return!Number.isNaN(i.getTime())&&i<=s&&(!r||!Number.isNaN(r.getTime())&&r>=s)}(c,t,a)){var f;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("section",{className:"mx-auto mt-6 max-w-3xl rounded-xl border border-amber-200 bg-amber-50 p-4","data-testid":"hafiq-plan-preview-banner",children:[(0,s.jsxs)("p",{className:"text-sm font-semibold text-amber-900",children:["Preview \xb7 ",n]}),(0,s.jsx)("p",{className:"mt-1 text-sm leading-6 text-amber-800",children:"This tool uses an illustrative, commonly used method until our scholar-reviewed UAE methodology is published in the Trust Centre. Treat results as an estimate, not a religious ruling."})]}),i((f=t,{outcome:"review_pending",methodology:{id:"preview",domain:f,market:"UAE",ver
1sion:"preview",methodologyKey:a,publicSummary:"Illustrative method: 2.5% of net zakatable wealth when it is at or above the gold nisab of 85 grams.",asOf:new Date().toISOString(),expiresAt:null,approvalReference:null,sources:[],requiredFinancialInputs:["goldPricePerGram"],eosbAccruedValueTreatment:"exclude"},productEligibility:null,limitations:["Preview only. Not scholar reviewed."]}))]})}let g=c.methodology;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)("section",{className:"mb-5 rounded-xl border border-[#D9E2EC] bg-[#f7fbfa] p-4","data-testid":"hafiq-plan-methodology",children:[(0,s.jsx)("p",{className:"text-sm font-medium text-[#006f53]",children:"Published UAE methodology"}),(0,s.jsxs)("p",{className:"mt-1 text-sm text-[#4E5159]",children:["Version ",g.version," \xb7 reviewed ",u(g.asOf),g.expiresAt?" \xb7 review due ".concat(u(g.expiresAt)):""]}),(0,s.jsx)("p",{className:"mt-2 text-sm leading-6 text-[#4E5159]",children:g.publicSummary}),g.sources.length>0&&(0,s.jsx)("ul",{className:"mt-3 space-y-1 text-sm",children:g.sources.map(e=>(0,s.jsxs)("li",{children:[(0,s.jsx)("a",{href:e.url,target:"_blank",rel:"noreferrer",className:"font-medium text-[#006f53] underline underline-offset-2",children:e.title}),e.sourceVersion?" \xb7 ".concat(e.sourceVersion):""]},"".concat(e.title,"-").concat(e.url)))})]}),i(c)]})}function p(e){let{title:t,body:a,testId:n}=e;return(0,s.jsxs)("main",{className:"mx-auto max-w-3xl px-4 py-8","data-testid":n,children:[(0,s.jsx)(i(),{href:"/app/hafiq",className:"text-sm font-medium text-[#006f53]",children:"← Islamic finance hub"}),(0,s.jsxs)("section",{className:"mt-5 rounded-2xl border border-amber-200 bg-amber-50 p-5",children:[(0,s.jsx)("h1",{className:"text-xl font-semibold text-amber-900",children:t}),(0,s.jsx)("p",{className:"mt-2 text-sm leading-6 text-amber-800",children:a}),(0,s.jsx)(i(),{href:"/app/hafiq/trust-centre",className:"mt-4 inline-block text-sm font-semibold text-[#006f53]",children:"View Trust Centre →"})]})]})}},77944:(e,t,a)=>{"use strict";a.d(t,{b:()=>n});var s=a(60865);let n={async calculateZakat(e,t){let{data:a}=await s.Ay.post("/customer/".concat(encodeURIComponent(e),"/hafiq/zakat/calculate/"),t);return a},async calculatePurification(e,t){let{data:a}=await s.Ay.post("/customer/".concat(encodeURIComponent(e),"/hafiq/purification/calculate/"),t);return a},async listPurificationEntries(e){let{data:t}=await s.Ay.get("/customer/".concat(encodeURIComponent(e),"/hafiq/purification/log/"));return Array.isArray(t)?t.map(e=>{var t,a,s,n,i;return{id:String(null!=(t=null==e?void 0:e.id)?t:""),type:1===Number(null==e?void 0:e.type)||(null==e?void 0:e.type)==="Portfolio"?"Portfolio":"Riba",amount:Number(null!=(a=null==e?void 0:e.amount)?a:0),currencyCode:String(null!=(s=null==e?void 0:e.currencyCode)?s:""),periodYearMonth:String(null!=(n=null==e?void 0:e.periodYearMonth)?n:""),donatedAt:String(null!=(i=null==e?void 0:e.donatedAt)?i:"")}}):[]},async recordPurificationEntry(e,t){var a,n,i,r,o;let{data:l}=await s.Ay.post("/customer/".concat(encodeURIComponent(e),"/hafiq/purification/log/"),{...t,type:+("Portfolio"===t.type)});return{id:String(null!=(a=null==l?void 0:l.id)?a:""),type:1===Number(null==l?void 0:l.type)||(null==l?void 0:l.type)==="Portfolio"?"Portfolio":"Riba",amount:Number(null!=(n=null==l?void 0:l.amount)?n:0),currencyCode:String(null!=(i=null==l?void 0:l.currencyCode)?i:""),periodYearMonth:String(null!=(r=null==l?void 0:l.periodYearMonth)?r:t.periodYearMonth),donatedAt:String(null!=(o=null==l?void 0:l.donatedAt)?o:"")}}}},80487:(e,t,a)=>{"use strict";a.d(t,{$h:()=>i,I3:()=>n,TS:()=>r});var s=a(60865);let n="investments",i="v22-uae-investments-v1",r={async lookup(e,t){let{data:a}=await s.Ay.get("/customer/".concat(encodeURIComponent(e),"/hafiq/evidence/lookup"),{params:{domain:t.domain,productId:t.productId,methodologyKey:t.methodologyKey}});return a}}},83554:(e,t,a)=>{"use strict";a.d(t,{T:()=>c,y:()=>u});var s=a(28800);let n=/\binterest\b|\bprofit (?:paid|credit)\b/i,i=/interest paid|interest charge|finance charge|loan|mortgage|bond|credit card/i,r=/\bdividends?\b/i,o=/donation|charit|gifts? \(out\)/i,l=/\bdonat|\bcharit|\bsadaq|\bmosque\b|\bred crescent\b|\bwaqf\b|\binfaq\b/i,d=/\bzaka[th]\b|\bzakat al[- ]fitr\b/i;function c(e){var t,a,s;if(e.isDeleted||e.isParentTransaction)return null;
1let c=[e.description,e.originalDescription,e.merchantName,e.categoryName].filter(Boolean).join(" "),u=null!=(t=e.categoryName)?t:"";return(null==(s=e.amount)||null==(a=s.debitOrCredit)?void 0:a.toLowerCase())==="credit"?/^dividends?$/i.test(u)||r.test(c)?"dividends":n.test(c)&&!i.test(c)?"interestIncome":null:d.test(c)?"zakatPaid":o.test(u)||l.test(c)?"donations":null}function u(e,t,a){let{months:n=12,now:i=new Date}=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},r=new Date(i);r.setMonth(r.getMonth()-n);let o=e=>({kind:e,total:0,transactions:[]}),l={interestIncome:o("interestIncome"),dividends:o("dividends"),donations:o("donations"),zakatPaid:o("zakatPaid")};for(let n of null!=e?e:[]){var d;let e=c(n);if(!e)continue;let o=function(e){let t=e.transactionDate,a="number"==typeof t||t?new Date(t):null;return a&&!Number.isNaN(a.getTime())?a:null}(n);if(!o||o<r||o>i)continue;let u=(0,s.BB)(n,t,a);u>0&&(l[e].transactions.push({id:n.id,date:o.toISOString(),description:n.description||n.categoryName||"Transaction",accountName:null!=(d=n.accountName)?d:"",amount:u}),l[e].total+=u)}for(let e of Object.values(l))e.transactions.sort((e,t)=>t.date.localeCompare(e.date)),e.total=Math.round(100*e.total)/100;return l}},99010:(e,t,a)=>{"use strict";a.d(t,{D4:()=>i,FG:()=>r,X:()=>o,q7:()=>n,sM:()=>s});let s=["GREGORIAN","HIJRI"],n=["RULE_NOT_APPROVED","APPROVED"],i=["GOLD","SILVER"],r=["RECORDED_BY_YOU","RECONCILED","VERIFIED"],o=["ACTIVE","REVERSED"]}},e=>{e.O(0,[28800,85249,87765,90636,46593,38792],()=>e(e.s=20028)),_N_E=e.O()}]);

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.