Files
tehran_taj/apps/user-app-v2/dist/assets/CompleteProfileView-D3k57yUp.js
T
s.esmaeiliandClaude Opus 5 5ad1083aee parity(apps+api): complete_profile in both apps [ref: henza_server Major/CustomerController::complete]
Ports "تکمیل پروفایل" end to end (D22).

API
- POST customer/me/complete-profile — three required document ids, the
  customer taken from the token, verification step set to the reference's
  LIVE value (approved outright; its `= 1` line is commented out at :83 in
  favour of `= 2` at :85). Both SMS messages byte-identical and non-blocking
  per D20.
- POST customer/me/documents — the reference let the app upload through the
  same file/create the admin used, inside its auth:api-customer group
  (routes/api.php:95); the target's copy is admin-guarded, so the page could
  not have uploaded at all. Reuses AdminUploadsService unchanged, so both
  paths share one size limit, MIME whitelist and naming scheme (UPL-01).
- PROFILE_ADMIN_MOBILES replaces the reference's four hardcoded personal
  numbers; blank means nobody is notified and the upload still succeeds.

Three reference behaviours deliberately NOT ported, all in ISSUES.md:
- SEC-14: `$customer->update($request->all())` mass-assigned the whole request
  body, letting the app write its own credit limit, making charge, gold debt,
  active flag or mobile. Replaced with a three-field whitelist.
- SEC-15: four real personal mobiles hardcoded in the source.
- APP-03: refresh()'s unterminated setInterval page reload.

Apps (both, template+script byte-identical, per-app styling only)
- CompleteProfileView + SingleImageUploader
- route /complete_profile, redirecting home once the documents are in, as the
  reference's guard did (router/index.ts:266)
- the entry point on the profile screen, shown only while they are missing
  (profile.vue:96-102)

Also (D23): JWT_CUSTOMER_SECRET now warns loudly at boot when unset. It falls
back to JWT_SECRET, which signs app and admin tokens with the same key and
leaves the `typ` check as the only barrier — and an expert's app token carries
their AdminUser id in `sub`. Warn rather than refuse to boot (D19, lowest
risk); DELIVERY.md lists it as the first pre-release step (REL-03).

Verified: build 5/5 · jest 45/45 · complete-profile 38/38 (new) ·
expert login 36/36 · apps smoke 43/43 · admin smoke 43/43 ·
B2B scan 34 ok/0 broken · typecheck clean on all four apps.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-23 01:42:07 +03:30

2 lines
5.0 KiB
JavaScript

import{Y as O,Z as T,$ as I,B as S,a0 as U,a1 as V,l,a2 as z,d as $,f as _,c as m,i as E,b as c,I as B,t as k,e as w,g as p,p as D,_ as N,s as h,a3 as R,a4 as j,v as A,x as L}from"./index-D7u32dbn.js";import{P as M}from"./PageBar-CihrRnwd.js";import{a as Y}from"./Spinner.vue_vue_type_style_index_0_lang-Ce4r_sA4.js";import{s as W}from"./index-CnhrxceM.js";import"./_commonjsHelpers-Cpj98o6Y.js";function X(){const e=S({show:!1}),o=a=>{e.show=a},s=a=>{I(e,a,{transitionAppear:!0}),o(!0)},u=()=>o(!1);return T({open:s,close:u,toggle:o}),{open:s,close:u,state:e,toggle:o}}function Z(e){const o=O(e),s=document.createElement("div");return document.body.appendChild(s),{instance:o.mount(s),unmount(){o.unmount(),document.body.removeChild(s)}}}let P,v;const q=e=>V(e)?e:{message:e};function F(){({instance:v}=Z({setup(){const{state:e,toggle:o}=X();return()=>l(W,z(e,{"onUpdate:show":o}),null)}}))}const G=()=>({type:"danger",color:void 0,message:"",onClose:void 0,onClick:void 0,onOpened:void 0,duration:3e3,position:void 0,className:"",lockScroll:!1,background:void 0});let H=G();const J=()=>{v&&v.toggle(!1)};function K(e){if(U)return v||F(),e=I({},H,q(e)),v.open(e),clearTimeout(P),e.duration>0&&(P=setTimeout(J,e.duration)),v}const Q=["disabled"],ee=["src"],se={key:1,class:"siu__icon"},te={class:"siu__text"},oe={key:2,class:"siu__badge"},ne={key:0,class:"siu__error"},ae=8*1024*1024,ie=$({__name:"SingleImageUploader",props:{uploadText:{},purpose:{}},emits:["done"],setup(e,{emit:o}){const s=e,u=o,a=p(null),i=p(!1),r=p(null),d=p(null),g=p(null);async function t(y){var b;const n=y.target,f=(b=n.files)==null?void 0:b[0];if(n.value="",!!f){if(r.value=null,f.size>ae){r.value="حجم فایل بیش از حد مجاز است (۸ مگابایت)";return}i.value=!0;try{const C=await D.uploadDocument(f,s.purpose);d.value=C.id,g.value=f.type.startsWith("image/")?URL.createObjectURL(f):null,u("done",C.id)}catch{r.value="بارگذاری انجام نشد. دوباره تلاش کنید.",d.value=null,g.value=null}finally{i.value=!1}}}return(y,n)=>(_(),m("div",{class:E(["siu",{"is-done":!!d.value,"is-error":!!r.value}])},[c("input",{ref_key:"input",ref:a,type:"file",accept:"image/jpeg,image/png,image/webp,application/pdf",class:"siu__input",onChange:t},null,544),c("button",{type:"button",class:"siu__box",disabled:i.value,onClick:n[0]||(n[0]=f=>{var b;return(b=a.value)==null?void 0:b.click()})},[g.value?(_(),m("img",{key:0,src:g.value,class:"siu__preview",alt:""},null,8,ee)):(_(),m("span",se,[l(B,{icon:i.value?"vuesax-linear:refresh":"vuesax-linear:gallery-add",size:"26"},null,8,["icon"])])),c("span",te,k(i.value?"در حال بارگذاری…":d.value?"تغییر تصویر":e.uploadText),1),d.value&&!i.value?(_(),m("span",oe,[l(B,{icon:"vuesax-linear:tick-circle",size:"16"})])):w("",!0)],8,Q),r.value?(_(),m("p",ne,k(r.value),1)):w("",!0)],2))}}),x=N(ie,[["__scopeId","data-v-61170337"]]),le={class:"cpf"},ue={key:0,class:"cpf__error"},re=["disabled"],ce=$({__name:"CompleteProfileView",setup(e){const o=L(),s=S({business_license_image_id:null,shop_image_id:null,ncode_image_id:null}),u=p(!1),a=p(!1),i=p(null);async function r(){if(i.value=null,!s.business_license_image_id||!s.shop_image_id||!s.ncode_image_id){K("لطفا تمامی عکس های خواسته شده را آپلود کنید");return}u.value=!0;try{await D.completeProfile({business_license_image_id:s.business_license_image_id,shop_image_id:s.shop_image_id,ncode_image_id:s.ncode_image_id}),a.value=!0}catch{i.value="ثبت اطلاعات انجام نشد. دوباره تلاش کنید."}finally{u.value=!1}}function d(){a.value=!1,o.push({name:"home"})}return(g,t)=>{const y=A("container");return _(),m("div",le,[l(M,{title:"تکمیل پروفایل","back-to":"userPage::hasSubRoute"}),l(Y,{spinning:u.value,tip:"در حال ثبت اطلاعات"},{default:h(()=>[l(y,{class:"cpf__body"},{default:h(()=>[t[4]||(t[4]=c("p",{class:"cpf__lead"}," برای تکمیل پروفایل، سه مدرک زیر را بارگذاری کنید. ",-1)),c("form",{class:"cpf__form",onSubmit:j(r,["prevent"])},[l(x,{"upload-text":"بارگذاری عکس جواز کسب",purpose:"business-license",onDone:t[0]||(t[0]=n=>s.business_license_image_id=n)}),l(x,{"upload-text":"بارگذاری عکس سردر مغازه",purpose:"shop-image",onDone:t[1]||(t[1]=n=>s.shop_image_id=n)}),l(x,{"upload-text":"بارگذاری عکس کارت ملی",purpose:"national-card",onDone:t[2]||(t[2]=n=>s.ncode_image_id=n)}),i.value?(_(),m("p",ue,k(i.value),1)):w("",!0),c("button",{type:"submit",class:"cpf__submit",disabled:u.value}," بارگذاری اطلاعات ",8,re)],32)]),_:1})]),_:1},8,["spinning"]),l(R,{show:a.value,"onUpdate:show":t[3]||(t[3]=n=>a.value=n),"time-out":-1,"close-on-click-overlay":!1},{title:h(()=>[...t[5]||(t[5]=[c("div",{class:"cpf__dialogText"}," تکمیل اطلاعات شما با موفقیت انجام شد. ",-1)])]),button:h(()=>[c("div",{class:"cpf__dialogBtn",onClick:d},"متوجه شدم")]),_:1},8,["show"])])}}}),ve=N(ce,[["__scopeId","data-v-c1854a08"]]);export{ve as default};