Ports "گزارش" end to end (D24), the last missing app page.
In the reference this feature computes nothing: Customer\TahesabController
proxies an external accounting system (hesab.henzagold.com and a bare IP at
31.7.68.147:3030) with TLS verification disabled, a bearer token hardcoded in
the source and a URL-embedded secret, keyed on Customer.mcode. The target has
no such system and no mcode, so per the owner's option الف this is built on our
own ledger with no external connection.
API — customer/me/report{,/detail,/balances,/:id}
- one row per CONFIRMED ledger entry with the running gold and rial balance
after it, newest first as the reference displayed it
- only confirmed entries count, the same rule the cached
Customer.goldDebtGrams/fiatDebtRial follow, so the statement's closing
balance agrees with the figure shown everywhere else in the app
- fields the target has no source for (foreign currency, نام کارساخته,
جنس فلز, the external system's internal ids) are null/zero and the UI OMITS
the row rather than rendering a fabricated value (REP-01)
SEC-16, NOT ported: the reference's detail($id) took the factor code from the
URL and performed no ownership check at all — it never consults auth() — so any
signed-in customer could read any other customer's document by guessing a code.
Here the id is only a filter on top of a customerId scope from the token.
Tested in both directions.
Apps (both, template+script byte-identical, per-app styling only)
- ReportView with the reference's two tabs, its card rows and its «جزئیات»
modal; ReportDocument for one document
- routes /report and /report/detail, the latter opening the second tab
- dashboard entry (v1 tile, v2 row), shown to every signed-in customer since
the reference's hasMCode() gate has no target equivalent
Verified: build 5/5 · jest 45/45 · report 40/40 (new) ·
complete-profile 38/38 · apps smoke 43/43 · typecheck clean on all four apps.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2 lines
11 KiB
JavaScript
2 lines
11 KiB
JavaScript
import{d as T,g as $,H as P,D as Q,i as y,c as k,w as j,J as ee,b as c,e as N,f as E,F as L,r as te,x as A,y as q,K as se,G as ae,v as K,k as o,s as v,I as H,t as V,u as s,M as ne,E as W,C as B,q as U,N as oe,h as le,_ as z}from"./index-BrtROTwT.js";import{P as G}from"./PageBar-Dh4JEEwN.js";import{_ as ue}from"./NotifyVant.vue_vue_type_script_setup_true_lang-cAF7h1vK.js";import{a as J,F as C,I as D,T as ie}from"./index-aFFAclMF.js";import{a as re}from"./Spinner.vue_vue_type_style_index_0_lang-DTTCd0cJ.js";import{_ as ce}from"./DialogVant.vue_vue_type_style_index_0_lang-Cbw1QjpS.js";import"./index-DypKz_AO.js";import"./FormItemContext-CekdmH-R.js";import"./classCallCheck-DE78qYAT.js";import"./_commonjsHelpers-Cpj98o6Y.js";const de={class:"otp-single-input"},pe=["type","placeholder","disabled"],me={key:0},ve=["innerHTML"],fe=T({__name:"SingleOtpInput",props:{inputType:{default:"tel"},value:{},separator:{},focus:{type:Boolean},inputClasses:{},shouldAutoFocus:{type:Boolean},isLastChild:{type:Boolean},placeholder:{},disabled:{type:Boolean}},emits:["on-change","on-keydown","on-paste","on-focus","on-blur"],setup(l,{emit:g}){const r=l,f=g,a=$(r.value||""),n=$(null),u=()=>(a.value=a.value.toString(),a.value.length>1&&(a.value=a.value.slice(0,1)),f("on-change",a.value)),h=d=>d>=48&&d<=57||d>=96&&d<=105,p=d=>{r.disabled&&d.preventDefault();const m=d||window.event,O=m.which?m.which:m.keyCode;h(O)||[8,9,13,37,39,46,86].includes(O)?f("on-keydown",d):m.preventDefault()},e=d=>f("on-paste",d),b=()=>(n.value.select(),f("on-focus")),i=()=>f("on-blur");return P(()=>r.value,(d,m)=>{d!==m&&(a.value=d??"")}),P(()=>r.focus,(d,m)=>{m!==d&&n.value&&r.focus&&(n.value.focus(),n.value.select())}),Q(()=>{n.value&&r.focus&&r.shouldAutoFocus&&(n.value.focus(),n.value.select())}),(d,m)=>(y(),k("div",de,[j(c("input",{type:l.inputType,placeholder:l.placeholder,disabled:l.disabled,ref_key:"input",ref:n,min:"0",max:"9",maxlength:"1",pattern:"[0-9]","onUpdate:modelValue":m[0]||(m[0]=O=>a.value=O),class:N(l.inputClasses),onInput:u,onKeydown:p,onPaste:e,onFocus:b,onBlur:i},null,42,pe),[[ee,a.value]]),!l.isLastChild&&l.separator?(y(),k("span",me,[c("span",{innerHTML:l.separator},null,8,ve)])):E("",!0)]))}}),he={class:"flex ltr justify-around w-full"},_e=8,ye=37,ge=39,be=46,we=T({__name:"OtpInput",props:{defaultFocus:{type:Boolean},numInputs:{default:4},separator:{},inputClasses:{},inputType:{},shouldAutoFocus:{type:Boolean,default:!1},placeholder:{},disabled:{type:Boolean,default:!1}},emits:["on-change","on-complete"],setup(l,{emit:g}){const r=l,f=g,a=$(0),n=$([]),u=$([]),h=t=>{r.defaultFocus&&t<r.numInputs&&!n.value[t]?a.value=n.value.length:a.value=t},p=()=>{a.value=-1},e=()=>n.value.join("").length===r.numInputs?f("on-complete",n.value.join("")):"Wait until the user enters the required number of characters",b=t=>{a.value=Math.max(Math.min(r.numInputs-1,t),0)},i=()=>b(a.value+1),d=()=>b(a.value-1),m=t=>{u.value=Object.assign([],n.value),typeof t=="string"&&t===""?n.value.splice(a.value,1):n.value[a.value]=t,u.value.join("")!==n.value.join("")&&(f("on-change",n.value.join("")),e())},O=t=>{var M;t.preventDefault();const w=(((M=t.clipboardData)==null?void 0:M.getData("text/plain"))??"").slice(0,r.numInputs-a.value).split("");if(r.inputType==="number"&&!w.join("").match(/^\d+$/))return"Invalid pasted data";const R=n.value.slice(0,a.value).concat(w);return R.slice(0,r.numInputs).forEach((Y,Z)=>{n.value[Z]=Y}),b(R.slice(0,r.numInputs).length),e()},S=t=>{m(t),i()},_=t=>{switch(t.keyCode){case _e:t.preventDefault(),m(""),d();break;case be:t.preventDefault(),m("");break;case ye:t.preventDefault(),d();break;case ge:t.preventDefault(),i();break}};return(t,x)=>(y(),k("div",null,[c("div",he,[(y(!0),k(L,null,te(l.numInputs,(w,I)=>(y(),A(fe,{key:I,focus:a.value===I,value:n.value[I],separator:l.separator,"input-type":l.inputType,"input-classes":l.inputClasses,"is-last-child":I===l.numInputs-1,"should-auto-focus":l.shouldAutoFocus,placeholder:l.placeholder,disabled:l.disabled,onOnChange:S,onOnKeydown:_,onOnPaste:O,onOnFocus:R=>h(I),onOnBlur:p},null,8,["focus","value","separator","input-type","input-classes","is-last-child","should-auto-focus","placeholder","disabled","onOnFocus"]))),128))])]))}}),ke=l=>` مقدار ${l} نا معتبر است `,X=l=>({pattern:/^(\+98|0098|98|0)?9\d{9}$/g,message:ke(l)}),F=l=>({required:!0,message:` لطفا ${l} را وارد کنید `}),xe={class:"auth"},Oe={class:"auth__intro"},Ie={class:"auth__mark","aria-hidden":"true"},Ce={class:"auth__sub"},Fe={class:"auth__card"},$e={key:0,class:"auth__timer"},De={key:1,class:"auth__timer"},Be=["disabled"],Te={class:"auth__switch"},Ee=T({__name:"LoginView",emits:["register"],setup(l,{emit:g}){const r=g,f=q(),a=se(),n=ae(),u=B({username:void 0}),h=B({show:!1,text:"اطلاعات کاربری در سیستم وجود ندارد."}),p=B({step:1,loading:!1,defaultFocus:!1}),e=$(0),b=()=>{e.value>0&&setTimeout(()=>{e.value>0&&(e.value-=1,b())},1e3)},i=async()=>{try{p.loading=!0,await U.requestOtp(String(u.username??"")),p.step=2,e.value=120,b(),await oe(()=>{p.defaultFocus=!0})}catch(_){const t=_ instanceof Error&&/\(429\)/.test(_.message)?m(_):null;h.text=t??"خطا در ارسال کد. لطفاً دوباره تلاش کنید.",h.show=!0}finally{p.loading=!1}},d=async _=>{try{p.loading=!0;const{token:t,customer:x}=await U.verifyOtp(String(u.username??""),_);n.login(t,x.id,x.mobile);try{const I=await U.getExpertIdentity();n.setSalesExpert(I.isSalesExpert)}catch{n.setSalesExpert(!1)}const w=typeof a.query.redirect=="string"?a.query.redirect:void 0;await f.push(w??{name:"home"}),h.text="با موفقیت وارد شدید",h.show=!0}catch(t){h.text=t instanceof Error&&/\(429\)/.test(t.message)?m(t)??"تعداد تلاشها بیش از حد مجاز است.":"کد وارد شده صحیح نیست",h.show=!0}finally{p.loading=!1}};function m(_){const t=_.message.match(/\{.*\}/);if(!t)return null;try{const x=JSON.parse(t[0]);return typeof x.message=="string"?x.message:null}catch{return null}}const O=_=>_<10?"0"+_:_,S=le(()=>p.step===1?"دریافت رمز عبور":"دریافت مجدد رمز عبور");return(_,t)=>{const x=K("container");return y(),k("div",xe,[o(ue,{timeOut:2e3,modelValue:h.show,"onUpdate:modelValue":t[0]||(t[0]=w=>h.show=w),text:h.text,color:"background-color: #DC2626"},null,8,["modelValue","text"]),o(G,{title:"ورود","back-to":"home"}),o(x,{class:"auth__body"},{default:v(()=>[c("section",Oe,[c("div",Ie,[o(H,{icon:"vuesax-linear:security-user",size:"28"})]),t[3]||(t[3]=c("h2",{class:"auth__title"},"ورود به منتزر",-1)),c("p",Ce,V(p.step===1?"شماره موبایل خود را وارد کنید":"رمز یکبارمصرف را وارد کنید"),1)]),c("section",Fe,[p.step===2?(y(),k(L,{key:0},[o(we,{class:"auth__otp","input-classes":"otp-input",inputType:"tel","num-inputs":4,"should-auto-focus":!0,defaultFocus:p.defaultFocus,onOnComplete:d},null,8,["defaultFocus"]),e.value>0?(y(),k("p",$e," امکان دریافت مجدد رمز در "+V(O(e.value))+" ثانیه دیگر ",1)):(y(),k("p",De,"برای دریافت رمز عبور مجدد تلاش کنید"))],64)):E("",!0),o(s(J),{model:u,layout:"vertical",onFinish:i,class:"auth__form"},{default:v(()=>[j(o(s(C),{name:"username",label:"تلفن همراه",rules:[s(F)("تلفن همراه"),s(X)("تلفن همراه")]},{default:v(()=>[o(s(D),{type:"number",value:u.username,"onUpdate:value":t[1]||(t[1]=w=>u.username=w)},null,8,["value"])]),_:1},8,["rules"]),[[ne,p.step===1]]),c("button",{type:"submit",class:N(["auth__cta",{"is-loading":p.loading}]),disabled:p.loading||e.value>0&&p.step===2},V(S.value),11,Be)]),_:1},8,["model"])]),c("p",Te,[t[4]||(t[4]=W(" اگر ثبت نام نکردهاید ",-1)),c("button",{type:"button",onClick:t[2]||(t[2]=w=>r("register",!0))},"ثبت نام کنید")])]),_:1})])}}}),Se=z(Ee,[["__scopeId","data-v-5c10e11c"]]),Re="/assets/registerDelay-BdXluE_Y.svg",Ve={class:"auth"},Ue={class:"auth__intro"},Ae={class:"auth__mark","aria-hidden":"true"},Le={class:"auth__card"},Me={class:"auth__switch"},Pe=["src"],je=T({__name:"RegisterView",emits:["register"],setup(l,{emit:g}){const r=g,f=q(),a=B({loading:!1}),n=$(!1),u=B({fname:void 0,lname:void 0,mobile:void 0,store_name:void 0,phone:void 0,address:void 0,city:void 0}),h=async()=>{try{a.loading=!0,n.value=!0}finally{a.loading=!1}};return(p,e)=>{const b=K("container");return y(),k("div",Ve,[o(G,{title:"ثبت نام","back-to":"home"}),o(re,{spinning:a.loading,tip:"در حال ثبت اطلاعات"},{default:v(()=>[o(b,{class:"auth__body"},{default:v(()=>[c("section",Ue,[c("div",Ae,[o(H,{icon:"vuesax-linear:user-add",size:"28"})]),e[10]||(e[10]=c("h2",{class:"auth__title"},"ساخت حساب کاربری",-1)),e[11]||(e[11]=c("p",{class:"auth__sub"},"اطلاعات فروشگاه خود را وارد کنید",-1))]),c("section",Le,[o(s(J),{model:u,layout:"vertical",onFinish:h,class:"auth__form"},{default:v(()=>[o(s(C),{name:"fname",label:"نام",rules:s(F)("نام")},{default:v(()=>[o(s(D),{value:u.fname,"onUpdate:value":e[0]||(e[0]=i=>u.fname=i)},null,8,["value"])]),_:1},8,["rules"]),o(s(C),{name:"lname",label:"نام خانوادگی",rules:s(F)("نام خانوادگی")},{default:v(()=>[o(s(D),{value:u.lname,"onUpdate:value":e[1]||(e[1]=i=>u.lname=i)},null,8,["value"])]),_:1},8,["rules"]),o(s(C),{name:"mobile",label:"شماره موبایل",rules:[s(X)("شماره موبایل"),s(F)("شماره موبایل")]},{default:v(()=>[o(s(D),{value:u.mobile,"onUpdate:value":e[2]||(e[2]=i=>u.mobile=i),type:"number"},null,8,["value"])]),_:1},8,["rules"]),o(s(C),{name:"store_name",label:"نام فروشگاه",rules:s(F)("نام فروشگاه")},{default:v(()=>[o(s(D),{value:u.store_name,"onUpdate:value":e[3]||(e[3]=i=>u.store_name=i)},null,8,["value"])]),_:1},8,["rules"]),o(s(C),{name:"phone",label:"تلفن فروشگاه",rules:[s(F)("تلفن فروشگاه")]},{default:v(()=>[o(s(D),{value:u.phone,"onUpdate:value":e[4]||(e[4]=i=>u.phone=i),type:"number"},null,8,["value"])]),_:1},8,["rules"]),o(s(C),{name:"address",label:"آدرس پستی",rules:s(F)("آدرس")},{default:v(()=>[o(s(ie),{value:u.address,"onUpdate:value":e[5]||(e[5]=i=>u.address=i),rows:3},null,8,["value"])]),_:1},8,["rules"]),o(s(C),{name:"city",label:"شهر",rules:s(F)("شهر")},{default:v(()=>[o(s(D),{value:u.city,"onUpdate:value":e[6]||(e[6]=i=>u.city=i)},null,8,["value"])]),_:1},8,["rules"]),e[12]||(e[12]=c("button",{type:"submit",class:"auth__cta"},"ثبت اطلاعات",-1))]),_:1},8,["model"])]),c("p",Me,[e[13]||(e[13]=W(" اگر حساب کاربری دارید ",-1)),c("button",{type:"button",onClick:e[7]||(e[7]=i=>r("register",!1))},"وارد شوید")])]),_:1})]),_:1},8,["spinning"]),o(ce,{timeOut:-1,show:n.value,"onUpdate:show":e[9]||(e[9]=i=>n.value=i),closeOnClickOverlay:!1},{img:v(()=>[c("img",{src:s(Re),alt:"",class:"opacity-80"},null,8,Pe)]),title:v(()=>[...e[14]||(e[14]=[c("div",{class:"p-5"}," ثبت نام شما با موفقیت انجام شد. منتظر تایید اطلاعات ثبت نامی شما توسط کارشناسان بمانید. ",-1)])]),button:v(()=>[c("div",{class:"text-center",onClick:e[8]||(e[8]=i=>s(f).push({name:"home"}))},"متوجه شدم")]),_:1},8,["show"])])}}}),Ne=z(je,[["__scopeId","data-v-23baea3e"]]),Qe=T({__name:"LoginRegisterView",setup(l){const g=$(!1),r=f=>g.value=f;return(f,a)=>(y(),k(L,null,[g.value?E("",!0):(y(),A(Se,{key:0,onRegister:r})),g.value?(y(),A(Ne,{key:1,onRegister:r})):E("",!0)],64))}});export{Qe as default};
|