Polish customer app UI and streamline CRM overlays.

Replace standalone customer create and meeting report pages with overlays, add TimeInput, remove opaque input backgrounds app-wide, improve desktop layouts/skeletons, and refresh shopping, customer, and reminder screens.
This commit is contained in:
2026-09-15 21:41:48 +03:30
parent d2c11e5d62
commit bbb3a15d1c
101 changed files with 2910 additions and 896 deletions
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{A as e,Et as t,L as n,M as r,O as i,P as a,R as o,St as s,Y as c,Z as l,_ as ee,j as u,mt as te,n as d,rt as f,t as p,tt as m,z as h}from"./BasicIcon-C6boaehV.js";import{i as ne,n as re}from"./format-BHJptHdF.js";import{t as ie}from"./catalog-s7nVUdlj.js";import{d as ae,n as g}from"./index-B6MlTGUp.js";import{t as oe}from"./PageHeader-CDQMUtCp.js";import{t as se}from"./useRequirePricedOrder-zO2gkEXW.js";var _=h({__name:`IconSuccessCircle`,props:{size:{default:24},strokeWidth:{default:1.5},color:{default:null}},setup(e){return(t,n)=>(c(),r(p,{icon:`vuesax-linear:tick-circle`,size:e.size,"stroke-width":e.strokeWidth,color:e.color},null,8,[`size`,`stroke-width`,`color`]))}}),ce={class:`page cash-mobile-page mobile-only`},le={class:`cash-page__scroll page__scroll`},ue={class:`cash-page__success cash-page__success--done`},v={class:`cash-page__success-title`},y={class:`cash-page__success-subtitle`},b={class:`accounts-panel`},x={class:`accounts-panel__title`},S={class:`bank-card__head`},C={class:`bank-card__name`},w={class:`bank-card__icon`,"aria-hidden":`true`},T={class:`bank-card__field`},E={class:`bank-card__value`,dir:`ltr`},D={class:`bank-card__field`},O={class:`bank-card__value`,dir:`ltr`},k={class:`cash-page__footer`},A={class:`desktop-checkout-shell cash-desktop-page desktop-only`},j={class:`desktop-page__head`},M={class:`desktop-page__subtitle`},N={class:`cash-page__success cash-page__success--done`},de={class:`cash-page__success-title`},P={class:`accounts-panel`},F={class:`accounts-panel__title`},I={class:`desktop-grid-2`},L={class:`bank-card__head`},R={class:`bank-card__name`},z={class:`bank-card__icon`,"aria-hidden":`true`},B={class:`bank-card__field`},V={class:`bank-card__value`,dir:`ltr`},H={class:`bank-card__field`},fe={class:`bank-card__value`,dir:`ltr`},pe={class:`cash-desktop-page__actions`},U=d(h({__name:`CashSettlementView`,setup(r){let d=ee(),p=ae(),h=ie(),U=te([]),{orderId:W,order:G}=se(),K=e(()=>G.value?.companyName??``);m(W,async e=>{e&&!p.getOrderById(e)&&await p.fetchOrder(e)},{immediate:!0}),m(()=>G.value?.id,async e=>{if(!e||!G.value){U.value=[];return}h.companies.length||await h.loadCompanies(),U.value=await p.getBankAccountsForOrder(G.value)},{immediate:!0});let q=e(()=>G.value?.finalAmount?re(G.value.finalAmount):`—`),J=e(()=>{let e=G.value?.cashInvoiceExpiresAt;return e?`مهلت پرداخت این فاکتور تا ${ne(e)} است.`:`مبلغ فاکتور را به یکی از حساب‌های زیر واریز و رسید را ثبت کنید.`}),Y=e(()=>{let e=K.value.replace(/^شرکت\s+/u,``);return e?`شماره حساب‌های واریز — ${e}`:`شماره حساب‌های واریز`});function X(e){return e.replace(/\D/g,``).replace(/(\d{4})(?=\d)/g,`$1 `).trim()}function Z(e){return e.replace(/\s/g,``).toUpperCase().replace(/(.{4})/g,`$1 `).trim()}function Q(){d.replace(`/orders/${W.value}?track=1`)}function $(){d.replace(`/orders/${W.value}?track=1&receipt=1`)}return(e,r)=>(c(),a(i,null,[u(`div`,ce,[o(oe,{title:`تسویه نقدی`,"show-back":``,centered:``,onBack:r[0]||=e=>s(d).push(`/orders/${s(W)}`)}),u(`div`,le,[u(`div`,ue,[o(_,{size:56}),u(`p`,v,`مبلغ قابل پرداخت `+t(q.value),1),u(`p`,y,t(J.value),1)]),u(`section`,b,[u(`h2`,x,t(Y.value),1),(c(!0),a(i,null,l(U.value,e=>(c(),a(`article`,{key:e.id,class:`bank-card`},[u(`div`,S,[u(`span`,C,t(e.bankName),1),u(`span`,w,t(e.bankName.charAt(0)),1)]),u(`div`,T,[r[1]||=u(`span`,{class:`bank-card__label`},`شماره کارت`,-1),u(`strong`,E,t(X(e.cardNumber)),1)]),u(`div`,D,[r[2]||=u(`span`,{class:`bank-card__label`},`شماره شبا`,-1),u(`strong`,O,t(Z(e.iban)),1)])]))),128))])]),u(`div`,k,[o(g,{variant:`outline`,block:``,onClick:Q},{default:f(()=>[...r[3]||=[n(`مشاهده سفارش`,-1)]]),_:1}),o(g,{block:``,class:`cash-page__submit`,onClick:$},{default:f(()=>[...r[4]||=[n(` ثبت رسید پرداخت `,-1)]]),_:1})])]),u(`div`,A,[u(`header`,j,[r[5]||=u(`h1`,{class:`desktop-page__title`},`تسویه نقدی`,-1),u(`p`,M,t(J.value),1)]),u(`div`,N,[o(_,{size:56}),u(`p`,de,`مبلغ قابل پرداخت `+t(q.value),1)]),u(`section`,P,[u(`h2`,F,t(Y.value),1),u(`div`,I,[(c(!0),a(i,null,l(U.value,e=>(c(),a(`article`,{key:e.id,class:`bank-card`},[u(`div`,L,[u(`span`,R,t(e.bankName),1),u(`span`,z,t(e.bankName.charAt(0)),1)]),u(`div`,B,[r[6]||=u(`span`,{class:`bank-card__label`},`شماره کارت`,-1),u(`strong`,V,t(X(e.cardNumber)),1)]),u(`div`,H,[r[7]||=u(`span`,{class:`bank-card__label`},`شماره شبا`,-1),u(`strong`,fe,t(Z(e.iban)),1)])]))),128))])]),u(`div`,pe,[o(g,{variant:`outline`,class:`desktop-btn-compact`,onClick:Q},{default:f(()=>[...r[8]||=[n(` مشاهده سفارش `,-1)]]),_:1}),o(g,{class:`desktop-btn-compact`,onClick:$},{default:f(()=>[...r[9]||=[n(` ثبت رسید پرداخت `,-1)]]),_:1})])])],64))}}),[[`__scopeId`,`data-v-c299caf6`]]);export{U as default};
import{A as e,Et as t,L as n,M as r,O as i,P as a,R as o,St as s,Y as c,Z as l,_ as ee,j as u,mt as te,n as d,rt as f,t as p,tt as m,z as h}from"./BasicIcon-C6boaehV.js";import{i as ne,n as re}from"./format-BHJptHdF.js";import{t as ie}from"./catalog-s7nVUdlj.js";import{d as ae,n as g}from"./index-DzzUgmYT.js";import{t as oe}from"./PageHeader-BFnKSFNh.js";import{t as se}from"./useRequirePricedOrder-CxEozNXk.js";var _=h({__name:`IconSuccessCircle`,props:{size:{default:24},strokeWidth:{default:1.5},color:{default:null}},setup(e){return(t,n)=>(c(),r(p,{icon:`vuesax-linear:tick-circle`,size:e.size,"stroke-width":e.strokeWidth,color:e.color},null,8,[`size`,`stroke-width`,`color`]))}}),ce={class:`page cash-mobile-page mobile-only`},le={class:`cash-page__scroll page__scroll`},ue={class:`cash-page__success cash-page__success--done`},v={class:`cash-page__success-title`},y={class:`cash-page__success-subtitle`},b={class:`accounts-panel`},x={class:`accounts-panel__title`},S={class:`bank-card__head`},C={class:`bank-card__name`},w={class:`bank-card__icon`,"aria-hidden":`true`},T={class:`bank-card__field`},E={class:`bank-card__value`,dir:`ltr`},D={class:`bank-card__field`},O={class:`bank-card__value`,dir:`ltr`},k={class:`cash-page__footer`},A={class:`desktop-checkout-shell cash-desktop-page desktop-only`},j={class:`desktop-page__head`},M={class:`desktop-page__subtitle`},N={class:`cash-page__success cash-page__success--done`},de={class:`cash-page__success-title`},P={class:`accounts-panel`},F={class:`accounts-panel__title`},I={class:`desktop-grid-2`},L={class:`bank-card__head`},R={class:`bank-card__name`},z={class:`bank-card__icon`,"aria-hidden":`true`},B={class:`bank-card__field`},V={class:`bank-card__value`,dir:`ltr`},H={class:`bank-card__field`},fe={class:`bank-card__value`,dir:`ltr`},pe={class:`cash-desktop-page__actions`},U=d(h({__name:`CashSettlementView`,setup(r){let d=ee(),p=ae(),h=ie(),U=te([]),{orderId:W,order:G}=se(),K=e(()=>G.value?.companyName??``);m(W,async e=>{e&&!p.getOrderById(e)&&await p.fetchOrder(e)},{immediate:!0}),m(()=>G.value?.id,async e=>{if(!e||!G.value){U.value=[];return}h.companies.length||await h.loadCompanies(),U.value=await p.getBankAccountsForOrder(G.value)},{immediate:!0});let q=e(()=>G.value?.finalAmount?re(G.value.finalAmount):`—`),J=e(()=>{let e=G.value?.cashInvoiceExpiresAt;return e?`مهلت پرداخت این فاکتور تا ${ne(e)} است.`:`مبلغ فاکتور را به یکی از حساب‌های زیر واریز و رسید را ثبت کنید.`}),Y=e(()=>{let e=K.value.replace(/^شرکت\s+/u,``);return e?`شماره حساب‌های واریز — ${e}`:`شماره حساب‌های واریز`});function X(e){return e.replace(/\D/g,``).replace(/(\d{4})(?=\d)/g,`$1 `).trim()}function Z(e){return e.replace(/\s/g,``).toUpperCase().replace(/(.{4})/g,`$1 `).trim()}function Q(){d.replace(`/orders/${W.value}?track=1`)}function $(){d.replace(`/orders/${W.value}?track=1&receipt=1`)}return(e,r)=>(c(),a(i,null,[u(`div`,ce,[o(oe,{title:`تسویه نقدی`,"show-back":``,centered:``,onBack:r[0]||=e=>s(d).push(`/orders/${s(W)}`)}),u(`div`,le,[u(`div`,ue,[o(_,{size:56}),u(`p`,v,`مبلغ قابل پرداخت `+t(q.value),1),u(`p`,y,t(J.value),1)]),u(`section`,b,[u(`h2`,x,t(Y.value),1),(c(!0),a(i,null,l(U.value,e=>(c(),a(`article`,{key:e.id,class:`bank-card`},[u(`div`,S,[u(`span`,C,t(e.bankName),1),u(`span`,w,t(e.bankName.charAt(0)),1)]),u(`div`,T,[r[1]||=u(`span`,{class:`bank-card__label`},`شماره کارت`,-1),u(`strong`,E,t(X(e.cardNumber)),1)]),u(`div`,D,[r[2]||=u(`span`,{class:`bank-card__label`},`شماره شبا`,-1),u(`strong`,O,t(Z(e.iban)),1)])]))),128))])]),u(`div`,k,[o(g,{variant:`outline`,block:``,onClick:Q},{default:f(()=>[...r[3]||=[n(`مشاهده سفارش`,-1)]]),_:1}),o(g,{block:``,class:`cash-page__submit`,onClick:$},{default:f(()=>[...r[4]||=[n(` ثبت رسید پرداخت `,-1)]]),_:1})])]),u(`div`,A,[u(`header`,j,[r[5]||=u(`h1`,{class:`desktop-page__title`},`تسویه نقدی`,-1),u(`p`,M,t(J.value),1)]),u(`div`,N,[o(_,{size:56}),u(`p`,de,`مبلغ قابل پرداخت `+t(q.value),1)]),u(`section`,P,[u(`h2`,F,t(Y.value),1),u(`div`,I,[(c(!0),a(i,null,l(U.value,e=>(c(),a(`article`,{key:e.id,class:`bank-card`},[u(`div`,L,[u(`span`,R,t(e.bankName),1),u(`span`,z,t(e.bankName.charAt(0)),1)]),u(`div`,B,[r[6]||=u(`span`,{class:`bank-card__label`},`شماره کارت`,-1),u(`strong`,V,t(X(e.cardNumber)),1)]),u(`div`,H,[r[7]||=u(`span`,{class:`bank-card__label`},`شماره شبا`,-1),u(`strong`,fe,t(Z(e.iban)),1)])]))),128))])]),u(`div`,pe,[o(g,{variant:`outline`,class:`desktop-btn-compact`,onClick:Q},{default:f(()=>[...r[8]||=[n(` مشاهده سفارش `,-1)]]),_:1}),o(g,{class:`desktop-btn-compact`,onClick:$},{default:f(()=>[...r[9]||=[n(` ثبت رسید پرداخت `,-1)]]),_:1})])])],64))}}),[[`__scopeId`,`data-v-c299caf6`]]);export{U as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{A as e,Et as t,M as n,O as r,P as i,R as a,St as o,Y as s,Z as c,_ as l,g as u,j as d,mt as f,n as p,q as m,tt as h,z as g}from"./BasicIcon-C6boaehV.js";import{t as _}from"./catalog-s7nVUdlj.js";import{b as v}from"./index-B6MlTGUp.js";import{t as y}from"./EmptyState-C_QQeA_h.js";import{t as b}from"./PageSkeleton-uO-jhbNm.js";import{t as x}from"./PageHeader-CDQMUtCp.js";import{t as S}from"./SearchField-Dx2PcshV.js";var C={class:`page catalog-list-page`},w={class:`catalog-list-page__desk-head desktop-only`},T={class:`page__scroll page__scroll--with-nav catalog-list-page__scroll`},E={key:1,class:`catalog-list`},D=[`onClick`],O={class:`catalog-card__icon`},k={class:`catalog-card__info`},A={class:`catalog-card__count`},j=p(g({__name:`CatalogListView`,setup(p){let g=u(),j=l(),M=_(),N=f(!0),P=e(()=>g.params.companyId),F=e(()=>M.getCompany(P.value)),I=f(``),L=e(()=>M.fetchCatalogs(P.value,I.value));async function R(){N.value=!0,M.getCompany(P.value)||await M.loadCompanies(),await M.loadCatalogs(P.value),N.value=!1}m(R),h(P,R);function z(e){j.push({name:`catalog-detail`,params:{catalogId:e}})}function B(e){let t=M.getCatalog(e);return t?.productCount==null?M.fetchProducts(e).length:t.productCount}return(e,l)=>(s(),i(`div`,C,[a(x,{class:`mobile-only`,title:F.value?.name??`کالیته‌ها`,subtitle:`کالیته مورد نظر را انتخاب کنید`,"show-back":``,centered:``,onBack:l[0]||=e=>o(j).push(`/companies`)},null,8,[`title`]),d(`header`,w,[d(`h1`,null,t(F.value?.name??`کالیته‌ها`),1),l[2]||=d(`p`,null,`کالیته مورد نظر را انتخاب کنید`,-1)]),a(S,{modelValue:I.value,"onUpdate:modelValue":l[1]||=e=>I.value=e,placeholder:`جستجو بر اساس کد یا نام کالیته...`,class:`catalog-list-page__search`},null,8,[`modelValue`]),d(`div`,T,[N.value?(s(),n(b,{key:0,variant:`cards`,rows:5})):L.value.length>0?(s(),i(`div`,E,[(s(!0),i(r,null,c(L.value,e=>(s(),i(`button`,{key:e.id,type:`button`,class:`catalog-card`,onClick:t=>z(e.id)},[d(`span`,O,[a(v,{size:22})]),d(`div`,k,[d(`h3`,null,`کالیته کد `+t(e.code),1),d(`p`,null,t(e.name),1)]),d(`span`,A,t(B(e.id))+` محصول`,1)],8,D))),128))])):(s(),n(y,{key:2,variant:I.value?`search`:`catalogs`,message:I.value?`با این جستجو کالیته‌ای پیدا نشد`:`برای این شرکت هنوز کالیته‌ای ثبت نشده است`},null,8,[`variant`,`message`]))])]))}}),[[`__scopeId`,`data-v-53c183e1`]]);export{j as default};
import{A as e,Et as t,M as n,O as r,P as i,R as a,St as o,Y as s,Z as c,_ as l,g as u,j as d,mt as f,n as p,q as m,tt as h,z as g}from"./BasicIcon-C6boaehV.js";import{t as _}from"./catalog-s7nVUdlj.js";import{b as v}from"./index-DzzUgmYT.js";import{t as y}from"./EmptyState-C_QQeA_h.js";import{t as b}from"./PageSkeleton-uO-jhbNm.js";import{t as x}from"./PageHeader-BFnKSFNh.js";import{t as S}from"./SearchField-Dx2PcshV.js";var C={class:`page catalog-list-page`},w={class:`catalog-list-page__desk-head desktop-only`},T={class:`page__scroll page__scroll--with-nav catalog-list-page__scroll`},E={key:1,class:`catalog-list`},D=[`onClick`],O={class:`catalog-card__icon`},k={class:`catalog-card__info`},A={class:`catalog-card__count`},j=p(g({__name:`CatalogListView`,setup(p){let g=u(),j=l(),M=_(),N=f(!0),P=e(()=>g.params.companyId),F=e(()=>M.getCompany(P.value)),I=f(``),L=e(()=>M.fetchCatalogs(P.value,I.value));async function R(){N.value=!0,M.getCompany(P.value)||await M.loadCompanies(),await M.loadCatalogs(P.value),N.value=!1}m(R),h(P,R);function z(e){j.push({name:`catalog-detail`,params:{catalogId:e}})}function B(e){let t=M.getCatalog(e);return t?.productCount==null?M.fetchProducts(e).length:t.productCount}return(e,l)=>(s(),i(`div`,C,[a(x,{class:`mobile-only`,title:F.value?.name??`کالیته‌ها`,subtitle:`کالیته مورد نظر را انتخاب کنید`,"show-back":``,centered:``,onBack:l[0]||=e=>o(j).push(`/companies`)},null,8,[`title`]),d(`header`,w,[d(`h1`,null,t(F.value?.name??`کالیته‌ها`),1),l[2]||=d(`p`,null,`کالیته مورد نظر را انتخاب کنید`,-1)]),a(S,{modelValue:I.value,"onUpdate:modelValue":l[1]||=e=>I.value=e,placeholder:`جستجو بر اساس کد یا نام کالیته...`,class:`catalog-list-page__search`},null,8,[`modelValue`]),d(`div`,T,[N.value?(s(),n(b,{key:0,variant:`cards`,rows:5})):L.value.length>0?(s(),i(`div`,E,[(s(!0),i(r,null,c(L.value,e=>(s(),i(`button`,{key:e.id,type:`button`,class:`catalog-card`,onClick:t=>z(e.id)},[d(`span`,O,[a(v,{size:22})]),d(`div`,k,[d(`h3`,null,`کالیته کد `+t(e.code),1),d(`p`,null,t(e.name),1)]),d(`span`,A,t(B(e.id))+` محصول`,1)],8,D))),128))])):(s(),n(y,{key:2,variant:I.value?`search`:`catalogs`,message:I.value?`با این جستجو کالیته‌ای پیدا نشد`:`برای این شرکت هنوز کالیته‌ای ثبت نشده است`},null,8,[`variant`,`message`]))])]))}}),[[`__scopeId`,`data-v-53c183e1`]]);export{j as default};
@@ -1 +1 @@
import{A as e,Ct as t,Et as n,L as r,M as i,O as a,P as o,R as s,St as c,Y as l,Z as u,_ as d,g as f,it as p,j as m,mt as h,n as g,q as _,w as v,z as y}from"./BasicIcon-C6boaehV.js";import{o as b}from"./format-BHJptHdF.js";import{t as x}from"./PageSkeleton-uO-jhbNm.js";import{t as S}from"./crm-CtglbPsN.js";import{t as C}from"./PageHeader-CDQMUtCp.js";import{t as w}from"./catalogRequests-D1kBtGqL.js";var T={class:`page`},E={class:`page__scroll page__scroll--with-nav body`},D={class:`hint`},O={key:1,class:`error`},k=[`value`,`onInput`],A={class:`note`},j={class:`summary`},M=[`disabled`],N=g(y({__name:`CatalogRequestView`,setup(g){let y=f(),N=d(),P=S(),F=w(),I=h(!0),L=h(!1),R=h(null),z=h(``),B=h([]),V=h({}),H=e(()=>String(y.query.productId??``)),U=e(()=>String(y.query.catalogId??``)),W=e(()=>B.value.filter(e=>(V.value[e.id]??0)>0).map(e=>({productId:e.id,productName:e.name,productCode:e.code,quantity:V.value[e.id]??0})));_(async()=>{I.value=!0,R.value=null;try{let e=await P.loadBrowseProducts(U.value?{catalogId:U.value}:void 0),t=H.value;B.value=t?[...e].sort((e,n)=>Number(n.id===t)-Number(e.id===t)):e,t&&(V.value={...V.value,[t]:1})}catch(e){R.value=e instanceof Error?e.message:`بارگذاری محصولات ناموفق بود.`}finally{I.value=!1}});function G(e,t){V.value={...V.value,[e]:Math.max(0,Math.floor(t)||0)}}async function K(){L.value=!0,R.value=null;try{let e=await F.create({items:W.value,note:z.value});if(!e.success){R.value=e.message;return}N.push(`/catalog-requests`)}finally{L.value=!1}}return(e,d)=>(l(),o(`div`,T,[s(C,{title:`درخواست کالیته`,"show-back":``,centered:``,onBack:d[0]||=e=>c(N).back()}),m(`div`,E,[m(`p`,D,n(H.value?`این محصول برای درخواست کالیته انتخاب شده است. در صورت نیاز اقلام دیگر را هم اضافه کنید.`:`محصولات و تعداد اقلام موردنظر را مشخص کنید.`),1),I.value?(l(),i(x,{key:0,variant:`form`})):R.value?(l(),o(`p`,O,n(R.value),1)):(l(),o(a,{key:2},[(l(!0),o(a,null,u(B.value,e=>(l(),o(`article`,{key:e.id,class:t([`row`,{"row--selected":H.value===e.id}])},[m(`div`,null,[m(`strong`,null,n(e.name),1),m(`span`,null,`کد `+n(e.code),1)]),m(`input`,{class:`qty`,type:`number`,min:`0`,value:V.value[e.id]??0,onInput:t=>G(e.id,Number(t.target.value))},null,40,k)],2))),128)),m(`label`,A,[d[3]||=r(` توضیحات `,-1),p(m(`textarea`,{"onUpdate:modelValue":d[1]||=e=>z.value=e,rows:`3`,placeholder:`آدرس یا توضیحات اختیاری`},null,512),[[v,z.value]])]),m(`p`,j,`اقلام انتخاب‌شده: `+n(c(b)(W.value.length)),1),m(`button`,{type:`button`,class:`submit`,disabled:L.value,onClick:K},n(L.value?`در حال ثبت...`:`ثبت درخواست کالیته`),9,M),m(`button`,{type:`button`,class:`linkish`,onClick:d[2]||=e=>c(N).push(`/catalog-requests`)},` وضعیت درخواست‌های قبلی `)],64))])]))}}),[[`__scopeId`,`data-v-6bde0457`]]);export{N as default};
import{A as e,Ct as t,Et as n,L as r,M as i,O as a,P as o,R as s,St as c,Y as l,Z as u,_ as d,g as f,it as p,j as m,mt as h,n as g,q as _,w as v,z as y}from"./BasicIcon-C6boaehV.js";import{o as b}from"./format-BHJptHdF.js";import{t as x}from"./PageSkeleton-uO-jhbNm.js";import{t as S}from"./crm-CtglbPsN.js";import{t as C}from"./PageHeader-BFnKSFNh.js";import{t as w}from"./catalogRequests-D1kBtGqL.js";var T={class:`page`},E={class:`page__scroll page__scroll--with-nav body`},D={class:`hint`},O={key:1,class:`error`},k=[`value`,`onInput`],A={class:`note`},j={class:`summary`},M=[`disabled`],N=g(y({__name:`CatalogRequestView`,setup(g){let y=f(),N=d(),P=S(),F=w(),I=h(!0),L=h(!1),R=h(null),z=h(``),B=h([]),V=h({}),H=e(()=>String(y.query.productId??``)),U=e(()=>String(y.query.catalogId??``)),W=e(()=>B.value.filter(e=>(V.value[e.id]??0)>0).map(e=>({productId:e.id,productName:e.name,productCode:e.code,quantity:V.value[e.id]??0})));_(async()=>{I.value=!0,R.value=null;try{let e=await P.loadBrowseProducts(U.value?{catalogId:U.value}:void 0),t=H.value;B.value=t?[...e].sort((e,n)=>Number(n.id===t)-Number(e.id===t)):e,t&&(V.value={...V.value,[t]:1})}catch(e){R.value=e instanceof Error?e.message:`بارگذاری محصولات ناموفق بود.`}finally{I.value=!1}});function G(e,t){V.value={...V.value,[e]:Math.max(0,Math.floor(t)||0)}}async function K(){L.value=!0,R.value=null;try{let e=await F.create({items:W.value,note:z.value});if(!e.success){R.value=e.message;return}N.push(`/catalog-requests`)}finally{L.value=!1}}return(e,d)=>(l(),o(`div`,T,[s(C,{title:`درخواست کالیته`,"show-back":``,centered:``,onBack:d[0]||=e=>c(N).back()}),m(`div`,E,[m(`p`,D,n(H.value?`این محصول برای درخواست کالیته انتخاب شده است. در صورت نیاز اقلام دیگر را هم اضافه کنید.`:`محصولات و تعداد اقلام موردنظر را مشخص کنید.`),1),I.value?(l(),i(x,{key:0,variant:`form`})):R.value?(l(),o(`p`,O,n(R.value),1)):(l(),o(a,{key:2},[(l(!0),o(a,null,u(B.value,e=>(l(),o(`article`,{key:e.id,class:t([`row`,{"row--selected":H.value===e.id}])},[m(`div`,null,[m(`strong`,null,n(e.name),1),m(`span`,null,`کد `+n(e.code),1)]),m(`input`,{class:`qty`,type:`number`,min:`0`,value:V.value[e.id]??0,onInput:t=>G(e.id,Number(t.target.value))},null,40,k)],2))),128)),m(`label`,A,[d[3]||=r(` توضیحات `,-1),p(m(`textarea`,{"onUpdate:modelValue":d[1]||=e=>z.value=e,rows:`3`,placeholder:`آدرس یا توضیحات اختیاری`},null,512),[[v,z.value]])]),m(`p`,j,`اقلام انتخاب‌شده: `+n(c(b)(W.value.length)),1),m(`button`,{type:`button`,class:`submit`,disabled:L.value,onClick:K},n(L.value?`در حال ثبت...`:`ثبت درخواست کالیته`),9,M),m(`button`,{type:`button`,class:`linkish`,onClick:d[2]||=e=>c(N).push(`/catalog-requests`)},` وضعیت درخواست‌های قبلی `)],64))])]))}}),[[`__scopeId`,`data-v-6bde0457`]]);export{N as default};
@@ -1 +1 @@
import{Et as e,N as t,O as n,P as r,R as i,St as a,Y as o,Z as s,_ as c,j as l,n as u,q as d,z as f}from"./BasicIcon-C6boaehV.js";import{o as p}from"./format-BHJptHdF.js";import{t as m}from"./PageHeader-CDQMUtCp.js";import{t as h}from"./catalogRequests-D1kBtGqL.js";var g={class:`page`},_={class:`page__scroll page__scroll--with-nav body`},v={key:0,class:`empty`},y={class:`card__top`},b={class:`badge`},x={key:0,class:`note`},S=u(f({__name:`CatalogRequestsView`,setup(u){let f=c(),S=h();return d(()=>S.hydrate()),(c,u)=>(o(),r(`div`,g,[i(m,{title:`وضعیت کالیته‌ها`,"show-back":``,centered:``,onBack:u[0]||=e=>a(f).push(`/catalogs`)}),l(`div`,_,[l(`button`,{type:`button`,class:`new-btn`,onClick:u[1]||=e=>a(f).push(`/catalog-request`)},` درخواست کالیته جدید `),a(S).sorted.length?t(``,!0):(o(),r(`p`,v,`هنوز درخواستی ثبت نکرده‌اید.`)),(o(!0),r(n,null,s(a(S).sorted,i=>(o(),r(`article`,{key:i.id,class:`card`},[l(`div`,y,[l(`strong`,null,`درخواست `+e(i.id.slice(-6)),1),l(`span`,b,e(a(S).statusLabel(i.status)),1)]),l(`p`,null,e(i.createdAt.slice(0,10)),1),l(`p`,null,e(a(p)(i.items.length))+` قلم`,1),l(`ul`,null,[(o(!0),r(n,null,s(i.items,t=>(o(),r(`li`,{key:t.productId},e(t.productName)+` × `+e(a(p)(t.quantity)),1))),128))]),i.note?(o(),r(`p`,x,e(i.note),1)):t(``,!0)]))),128))])]))}}),[[`__scopeId`,`data-v-fc04181d`]]);export{S as default};
import{Et as e,N as t,O as n,P as r,R as i,St as a,Y as o,Z as s,_ as c,j as l,n as u,q as d,z as f}from"./BasicIcon-C6boaehV.js";import{o as p}from"./format-BHJptHdF.js";import{t as m}from"./PageHeader-BFnKSFNh.js";import{t as h}from"./catalogRequests-D1kBtGqL.js";var g={class:`page`},_={class:`page__scroll page__scroll--with-nav body`},v={key:0,class:`empty`},y={class:`card__top`},b={class:`badge`},x={key:0,class:`note`},S=u(f({__name:`CatalogRequestsView`,setup(u){let f=c(),S=h();return d(()=>S.hydrate()),(c,u)=>(o(),r(`div`,g,[i(m,{title:`وضعیت کالیته‌ها`,"show-back":``,centered:``,onBack:u[0]||=e=>a(f).push(`/catalogs`)}),l(`div`,_,[l(`button`,{type:`button`,class:`new-btn`,onClick:u[1]||=e=>a(f).push(`/catalog-request`)},` درخواست کالیته جدید `),a(S).sorted.length?t(``,!0):(o(),r(`p`,v,`هنوز درخواستی ثبت نکرده‌اید.`)),(o(!0),r(n,null,s(a(S).sorted,i=>(o(),r(`article`,{key:i.id,class:`card`},[l(`div`,y,[l(`strong`,null,`درخواست `+e(i.id.slice(-6)),1),l(`span`,b,e(a(S).statusLabel(i.status)),1)]),l(`p`,null,e(i.createdAt.slice(0,10)),1),l(`p`,null,e(a(p)(i.items.length))+` قلم`,1),l(`ul`,null,[(o(!0),r(n,null,s(i.items,t=>(o(),r(`li`,{key:t.productId},e(t.productName)+` × `+e(a(p)(t.quantity)),1))),128))]),i.note?(o(),r(`p`,x,e(i.note),1)):t(``,!0)]))),128))])]))}}),[[`__scopeId`,`data-v-fc04181d`]]);export{S as default};
@@ -1 +1 @@
import{A as e,Ct as t,Et as n,M as r,N as i,O as a,P as o,R as s,St as c,Tt as l,Y as u,Z as d,_ as f,g as p,j as m,mt as h,n as g,q as _,z as v}from"./BasicIcon-C6boaehV.js";import{o as y}from"./format-BHJptHdF.js";import{t as b}from"./catalog-s7nVUdlj.js";import{i as x}from"./index-B6MlTGUp.js";import{t as S}from"./IconChevronBack-DORV2H0q.js";import{t as C}from"./PageSkeleton-uO-jhbNm.js";import{t as ee}from"./crm-CtglbPsN.js";import{t as te}from"./useGoBack-CgNp0uP9.js";import{a as w,n as T}from"./catalogGallery-DMNB35I1.js";import{t as E}from"./SearchField-Dx2PcshV.js";import{t as D}from"./IconSearch-DOnq8ybc.js";var O={class:`page calite-hub`},k={class:`calite-hub__nav`},A={class:`calite-hub__nav-end`},j=[`aria-label`,`aria-pressed`],M={class:`page__scroll page__scroll--with-nav calite-hub__scroll`},N={class:`calite-hub__chips`,role:`tablist`,"aria-label":`مرتب‌سازی کالیته‌ها`},P=[`onClick`],F={key:2,class:`calite-hub__empty`},I={key:3,class:`calite-hub__list`},L=[`onClick`],R={key:0,class:`calite-card__badge`},z={class:`calite-card__copy`,dir:`rtl`},ne={class:`mr-4 text-white`},B={class:`text-lg font-bold`},V={class:`text-md`},H={class:`text-xs mt-4`},U=g(v({__name:`CatalogsHubView`,setup(g){let v=p(),U=f(),{goBack:W}=te(`/`),G=b(),K=ee(),q=h([]),J=h([]),Y=h(!0),X=h(!1),Z=h(``),Q=h(`all`),re=[{id:`all`,label:`همه`},{id:`newest`,label:`جدیدترین`},{id:`popular`,label:`محبوب‌ترین`},{id:`season`,label:`بر اساس فصل`}],ie=e(()=>T(q.value,J.value)),$=e(()=>w(ie.value,Q.value,Z.value));_(async()=>{try{let e=await G.loadCompanies(),[t,n]=await Promise.all([Promise.all(e.map(e=>G.loadCatalogs(e.id))),K.loadBrowseProducts().catch(()=>[])]);q.value=t.flat(),J.value=n;let r=String(v.query.id??``);r&&q.value.some(e=>e.id===r)&&U.replace({name:`catalog-detail`,params:{catalogId:r}})}catch{q.value=[]}finally{Y.value=!1}});function ae(e){U.push({name:`catalog-detail`,params:{catalogId:e}})}function oe(){X.value=!X.value,X.value||(Z.value=``)}function se(e){let t=e.season===`winter`?`مناسب زمستان`:e.season===`summer`?`مناسب تابستان`:`مناسب چهار فصل`;return`${y(e.patternCount)} طرح • ${t}`}return(e,f)=>(u(),o(`div`,O,[m(`header`,k,[m(`button`,{type:`button`,class:`icon-btn mobile-only`,"aria-label":`بازگشت`,onClick:f[0]||=(...e)=>c(W)&&c(W)(...e)},[s(S,{size:20})]),f[2]||=m(`h1`,null,`کالیته‌ها`,-1),m(`div`,A,[s(x,{variant:`inline`,class:`mobile-only`}),m(`button`,{type:`button`,class:`icon-btn`,"aria-label":X.value?`بستن جستجو`:`جستجو`,"aria-pressed":X.value,onClick:oe},[s(D,{size:20})],8,j)])]),m(`div`,M,[f[4]||=m(`p`,{class:`calite-hub__lead`},`مجموعه‌ای از بهترین پارچه‌ها، در یک نگاه`,-1),X.value?(u(),r(E,{key:0,"model-value":Z.value,placeholder:`جستجوی نام کالیته...`,"onUpdate:modelValue":f[1]||=e=>Z.value=e},null,8,[`model-value`])):i(``,!0),m(`div`,N,[(u(),o(a,null,d(re,e=>m(`button`,{key:e.id,type:`button`,class:t([`calite-chip`,{"is-on":Q.value===e.id}]),onClick:t=>Q.value=e.id},n(e.label),11,P)),64))]),Y.value?(u(),r(C,{key:1,variant:`cards`,rows:4})):$.value.length?(u(),o(`div`,I,[(u(!0),o(a,null,d($.value,e=>(u(),o(`button`,{key:e.id,type:`button`,class:`calite-card`,dir:`ltr`,onClick:t=>ae(e.id)},[m(`span`,{class:`calite-card__photo`,style:l(e.imageUrl?{backgroundImage:`url(${e.imageUrl})`}:void 0)},null,4),e.badge?(u(),o(`span`,R,n(e.badge===`new`?`جدید`:`پرفروش`),1)):i(``,!0),m(`div`,z,[f[3]||=m(`svg`,{class:`calite-card__arrow`,width:`12`,height:`16`,viewBox:`0 0 8 12`,fill:`none`,"aria-hidden":`true`},[m(`path`,{d:`M1.5 1.5 6 6l-4.5 4.5`,stroke:`#fff`,"stroke-width":`1.4`,"stroke-linecap":`round`,"stroke-linejoin":`round`})],-1),m(`div`,ne,[m(`div`,B,n(e.title),1),m(`div`,V,n(e.subtitle),1),m(`div`,H,n(se(e)),1)])])],8,L))),128))])):(u(),o(`p`,F,`کالیته‌ای یافت نشد.`))])]))}}),[[`__scopeId`,`data-v-a6e04708`]]);export{U as default};
import{A as e,Ct as t,Et as n,M as r,N as i,O as a,P as o,R as s,St as c,Tt as l,Y as u,Z as d,_ as f,g as p,j as m,mt as h,n as g,q as _,z as v}from"./BasicIcon-C6boaehV.js";import{o as y}from"./format-BHJptHdF.js";import{t as b}from"./catalog-s7nVUdlj.js";import{i as x}from"./index-DzzUgmYT.js";import{t as S}from"./IconChevronBack-DORV2H0q.js";import{t as C}from"./PageSkeleton-uO-jhbNm.js";import{t as ee}from"./crm-CtglbPsN.js";import{t as te}from"./useGoBack-CgNp0uP9.js";import{a as w,n as T}from"./catalogGallery-DMNB35I1.js";import{t as E}from"./SearchField-Dx2PcshV.js";import{t as D}from"./IconSearch-DOnq8ybc.js";var O={class:`page calite-hub`},k={class:`calite-hub__nav`},A={class:`calite-hub__nav-end`},j=[`aria-label`,`aria-pressed`],M={class:`page__scroll page__scroll--with-nav calite-hub__scroll`},N={class:`calite-hub__chips`,role:`tablist`,"aria-label":`مرتب‌سازی کالیته‌ها`},P=[`onClick`],F={key:2,class:`calite-hub__empty`},I={key:3,class:`calite-hub__list`},L=[`onClick`],R={key:0,class:`calite-card__badge`},z={class:`calite-card__copy`,dir:`rtl`},ne={class:`mr-4 text-white`},B={class:`text-lg font-bold`},V={class:`text-md`},H={class:`text-xs mt-4`},U=g(v({__name:`CatalogsHubView`,setup(g){let v=p(),U=f(),{goBack:W}=te(`/`),G=b(),K=ee(),q=h([]),J=h([]),Y=h(!0),X=h(!1),Z=h(``),Q=h(`all`),re=[{id:`all`,label:`همه`},{id:`newest`,label:`جدیدترین`},{id:`popular`,label:`محبوب‌ترین`},{id:`season`,label:`بر اساس فصل`}],ie=e(()=>T(q.value,J.value)),$=e(()=>w(ie.value,Q.value,Z.value));_(async()=>{try{let e=await G.loadCompanies(),[t,n]=await Promise.all([Promise.all(e.map(e=>G.loadCatalogs(e.id))),K.loadBrowseProducts().catch(()=>[])]);q.value=t.flat(),J.value=n;let r=String(v.query.id??``);r&&q.value.some(e=>e.id===r)&&U.replace({name:`catalog-detail`,params:{catalogId:r}})}catch{q.value=[]}finally{Y.value=!1}});function ae(e){U.push({name:`catalog-detail`,params:{catalogId:e}})}function oe(){X.value=!X.value,X.value||(Z.value=``)}function se(e){let t=e.season===`winter`?`مناسب زمستان`:e.season===`summer`?`مناسب تابستان`:`مناسب چهار فصل`;return`${y(e.patternCount)} طرح • ${t}`}return(e,f)=>(u(),o(`div`,O,[m(`header`,k,[m(`button`,{type:`button`,class:`icon-btn mobile-only`,"aria-label":`بازگشت`,onClick:f[0]||=(...e)=>c(W)&&c(W)(...e)},[s(S,{size:20})]),f[2]||=m(`h1`,null,`کالیته‌ها`,-1),m(`div`,A,[s(x,{variant:`inline`,class:`mobile-only`}),m(`button`,{type:`button`,class:`icon-btn`,"aria-label":X.value?`بستن جستجو`:`جستجو`,"aria-pressed":X.value,onClick:oe},[s(D,{size:20})],8,j)])]),m(`div`,M,[f[4]||=m(`p`,{class:`calite-hub__lead`},`مجموعه‌ای از بهترین پارچه‌ها، در یک نگاه`,-1),X.value?(u(),r(E,{key:0,"model-value":Z.value,placeholder:`جستجوی نام کالیته...`,"onUpdate:modelValue":f[1]||=e=>Z.value=e},null,8,[`model-value`])):i(``,!0),m(`div`,N,[(u(),o(a,null,d(re,e=>m(`button`,{key:e.id,type:`button`,class:t([`calite-chip`,{"is-on":Q.value===e.id}]),onClick:t=>Q.value=e.id},n(e.label),11,P)),64))]),Y.value?(u(),r(C,{key:1,variant:`cards`,rows:4})):$.value.length?(u(),o(`div`,I,[(u(!0),o(a,null,d($.value,e=>(u(),o(`button`,{key:e.id,type:`button`,class:`calite-card`,dir:`ltr`,onClick:t=>ae(e.id)},[m(`span`,{class:`calite-card__photo`,style:l(e.imageUrl?{backgroundImage:`url(${e.imageUrl})`}:void 0)},null,4),e.badge?(u(),o(`span`,R,n(e.badge===`new`?`جدید`:`پرفروش`),1)):i(``,!0),m(`div`,z,[f[3]||=m(`svg`,{class:`calite-card__arrow`,width:`12`,height:`16`,viewBox:`0 0 8 12`,fill:`none`,"aria-hidden":`true`},[m(`path`,{d:`M1.5 1.5 6 6l-4.5 4.5`,stroke:`#fff`,"stroke-width":`1.4`,"stroke-linecap":`round`,"stroke-linejoin":`round`})],-1),m(`div`,ne,[m(`div`,B,n(e.title),1),m(`div`,V,n(e.subtitle),1),m(`div`,H,n(se(e)),1)])])],8,L))),128))])):(u(),o(`p`,F,`کالیته‌ای یافت نشد.`))])]))}}),[[`__scopeId`,`data-v-a6e04708`]]);export{U as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{A as e,Et as t,P as n,R as r,St as i,Y as a,_ as o,j as s,n as c,tt as l,z as u}from"./BasicIcon-C6boaehV.js";import{n as d}from"./format-BHJptHdF.js";import{d as f}from"./index-B6MlTGUp.js";import{t as p}from"./IconEditPen-DLV3_yq2.js";import{t as m}from"./PageHeader-CDQMUtCp.js";import{t as h}from"./useRequirePricedOrder-zO2gkEXW.js";import{t as g}from"./IconCheckBook-DwIUojJc.js";var _={class:`page check-mode-page`},v={class:`page__scroll check-mode-page__scroll`},y={class:`check-mode-invoice`},b={class:`check-mode-invoice__value`},x={class:`mode-options`},S={class:`mode-card__icon`},C={class:`mode-card__icon`},w=c(u({__name:`CheckModeSelectView`,setup(c){let u=o(),w=f(),{orderId:T,order:E}=h(),D=e(()=>E.value?.finalAmount?d(E.value.finalAmount):`—`);l(T,async e=>{e&&(w.getOrderById(e)||await w.fetchOrder(e),w.draftOrderId!==e&&w.startCheckDraft(e))},{immediate:!0});function O(e){w.setCheckMode(e),u.push(`/orders/${T.value}/check-form`)}return(e,o)=>(a(),n(`div`,_,[r(m,{title:`تسویه چکی`,subtitle:`بر اساس مبلغ فاکتور، نحوه ثبت چک را انتخاب کنید`,"show-back":``,centered:``,onBack:o[0]||=e=>i(u).push(`/orders/${i(T)}`)}),s(`div`,v,[s(`div`,y,[o[3]||=s(`span`,{class:`check-mode-invoice__label`},`مبلغ فاکتور`,-1),s(`strong`,b,t(D.value),1)]),s(`div`,x,[s(`button`,{type:`button`,class:`mode-card`,onClick:o[1]||=e=>O(`full`)},[s(`span`,S,[r(g,{size:36})]),o[4]||=s(`h3`,{class:`mode-card__title`},`ثبت کامل (پیشنهادی)`,-1),o[5]||=s(`p`,{class:`mode-card__desc`},`وارد کردن همه اطلاعات چک`,-1)]),s(`button`,{type:`button`,class:`mode-card`,onClick:o[2]||=e=>O(`brief`)},[s(`span`,C,[r(p,{size:36})]),o[6]||=s(`h3`,{class:`mode-card__title`},`ثبت مختصر`,-1),o[7]||=s(`p`,{class:`mode-card__desc`},`وارد کردن حداقل اطلاعات`,-1)])])])]))}}),[[`__scopeId`,`data-v-680065b0`]]);export{w as default};
import{A as e,Et as t,P as n,R as r,St as i,Y as a,_ as o,j as s,n as c,tt as l,z as u}from"./BasicIcon-C6boaehV.js";import{n as d}from"./format-BHJptHdF.js";import{d as f}from"./index-DzzUgmYT.js";import{t as p}from"./IconEditPen-DLV3_yq2.js";import{t as m}from"./PageHeader-BFnKSFNh.js";import{t as h}from"./useRequirePricedOrder-CxEozNXk.js";import{t as g}from"./IconCheckBook-DwIUojJc.js";var _={class:`page check-mode-page`},v={class:`page__scroll check-mode-page__scroll`},y={class:`check-mode-invoice`},b={class:`check-mode-invoice__value`},x={class:`mode-options`},S={class:`mode-card__icon`},C={class:`mode-card__icon`},w=c(u({__name:`CheckModeSelectView`,setup(c){let u=o(),w=f(),{orderId:T,order:E}=h(),D=e(()=>E.value?.finalAmount?d(E.value.finalAmount):`—`);l(T,async e=>{e&&(w.getOrderById(e)||await w.fetchOrder(e),w.draftOrderId!==e&&w.startCheckDraft(e))},{immediate:!0});function O(e){w.setCheckMode(e),u.push(`/orders/${T.value}/check-form`)}return(e,o)=>(a(),n(`div`,_,[r(m,{title:`تسویه چکی`,subtitle:`بر اساس مبلغ فاکتور، نحوه ثبت چک را انتخاب کنید`,"show-back":``,centered:``,onBack:o[0]||=e=>i(u).push(`/orders/${i(T)}`)}),s(`div`,v,[s(`div`,y,[o[3]||=s(`span`,{class:`check-mode-invoice__label`},`مبلغ فاکتور`,-1),s(`strong`,b,t(D.value),1)]),s(`div`,x,[s(`button`,{type:`button`,class:`mode-card`,onClick:o[1]||=e=>O(`full`)},[s(`span`,S,[r(g,{size:36})]),o[4]||=s(`h3`,{class:`mode-card__title`},`ثبت کامل (پیشنهادی)`,-1),o[5]||=s(`p`,{class:`mode-card__desc`},`وارد کردن همه اطلاعات چک`,-1)]),s(`button`,{type:`button`,class:`mode-card`,onClick:o[2]||=e=>O(`brief`)},[s(`span`,C,[r(p,{size:36})]),o[6]||=s(`h3`,{class:`mode-card__title`},`ثبت مختصر`,-1),o[7]||=s(`p`,{class:`mode-card__desc`},`وارد کردن حداقل اطلاعات`,-1)])])])]))}}),[[`__scopeId`,`data-v-680065b0`]]);export{w as default};
@@ -1 +1 @@
import{A as e,Ct as t,Et as n,L as r,M as i,O as a,P as o,R as s,St as c,Y as l,Z as u,_ as d,et as f,j as p,mt as m,n as h,q as g,z as _}from"./BasicIcon-C6boaehV.js";import{t as v}from"./catalog-s7nVUdlj.js";import{T as y,o as b}from"./index-B6MlTGUp.js";import{t as x}from"./IconChevronBack-DORV2H0q.js";import{n as S,t as C}from"./IconFamBaftLogo-BaP40Dff.js";import{t as w}from"./PageSkeleton-uO-jhbNm.js";import{t as T}from"./useGoBack-CgNp0uP9.js";var E={class:`company-select-page`},D={class:`company-select-page__header mobile-only`},O={class:`company-select-page__nav`},k={class:`company-select-page__list`},A=[`onClick`],j={class:`company-brand-card__logo`},M={class:`company-brand-card__name`},N={class:`company-brand-card__desc`},P={class:`company-brand-card__cta desktop-only`},F=h(_({__name:`MobileCompanySelect`,props:{companies:{},cardVariant:{type:Function},companyDescription:{type:Function},companyLogo:{type:Function}},emits:[`select`,`back`],setup(e){return(c,d)=>(l(),o(`div`,E,[p(`header`,D,[p(`div`,O,[p(`button`,{type:`button`,class:`nav-btn`,"aria-label":`بازگشت`,onClick:d[0]||=e=>c.$emit(`back`)},[s(x,{size:20})]),d[1]||=p(`h1`,{class:`company-select-page__title`},`انتخاب شرکت`,-1),d[2]||=p(`span`,{class:`nav-btn nav-btn--spacer`,"aria-hidden":`true`},null,-1)]),d[3]||=p(`p`,{class:`company-select-page__subtitle`},`شرکت مورد نظر خود را انتخاب کنید`,-1)]),d[5]||=p(`header`,{class:`company-select-page__desk-head desktop-only`},[p(`h1`,null,`انتخاب شرکت`),p(`p`,null,`شرکت موردنظر را انتخاب کنید تا کاتالوگ و سفارش‌گذاری شروع شود`)],-1),p(`div`,k,[(l(!0),o(a,null,u(e.companies,a=>(l(),o(`button`,{key:a.id,type:`button`,class:t([`company-brand-card`,`company-brand-card--${e.cardVariant(a.slug)}`]),onClick:e=>c.$emit(`select`,a.id)},[p(`span`,j,[(l(),i(f(e.companyLogo(a.slug)),{size:72}))]),p(`h2`,M,n(a.name),1),p(`p`,N,n(e.companyDescription(a)),1),p(`span`,P,[d[4]||=r(` ورود به کاتالوگ‌ها `,-1),s(b,{size:16})])],10,A))),128))])]))}}),[[`__scopeId`,`data-v-336d01ae`]]),I={key:0,class:`page page__scroll`},L=_({__name:`CompanySelectView`,setup(t){let n=d(),{goBack:r}=T(`/`),a=v(),u=y(),f=m(!0),p=e(()=>a.getActiveCompanies()),h={par:{description:`تولید و عرضه انواع پارچه`,logo:S},"fam-baft":{description:`تولید و عرضه انواع پارچه`,logo:C}};g(async()=>{await a.loadCompanies(),f.value=!1});function _(e){let t=a.getCompany(e);t&&(u.selectCompany(t),n.push(`/companies/${e}/catalogs`))}function b(e){return e===`par`?`par`:`fam-baft`}function x(e){return h[e.slug]?.description??e.description}function E(e){return h[e]?.logo??S}return(e,t)=>f.value?(l(),o(`div`,I,[s(w,{variant:`cards`})])):(l(),i(F,{key:1,companies:p.value,"card-variant":b,"company-description":x,"company-logo":E,onSelect:_,onBack:c(r)},null,8,[`companies`,`onBack`]))}});export{L as default};
import{A as e,Ct as t,Et as n,L as r,M as i,O as a,P as o,R as s,St as c,Y as l,Z as u,_ as d,et as f,j as p,mt as m,n as h,q as g,z as _}from"./BasicIcon-C6boaehV.js";import{t as v}from"./catalog-s7nVUdlj.js";import{T as y,o as b}from"./index-DzzUgmYT.js";import{t as x}from"./IconChevronBack-DORV2H0q.js";import{n as S,t as C}from"./IconFamBaftLogo-BaP40Dff.js";import{t as w}from"./PageSkeleton-uO-jhbNm.js";import{t as T}from"./useGoBack-CgNp0uP9.js";var E={class:`company-select-page`},D={class:`company-select-page__header mobile-only`},O={class:`company-select-page__nav`},k={class:`company-select-page__list`},A=[`onClick`],j={class:`company-brand-card__logo`},M={class:`company-brand-card__name`},N={class:`company-brand-card__desc`},P={class:`company-brand-card__cta desktop-only`},F=h(_({__name:`MobileCompanySelect`,props:{companies:{},cardVariant:{type:Function},companyDescription:{type:Function},companyLogo:{type:Function}},emits:[`select`,`back`],setup(e){return(c,d)=>(l(),o(`div`,E,[p(`header`,D,[p(`div`,O,[p(`button`,{type:`button`,class:`nav-btn`,"aria-label":`بازگشت`,onClick:d[0]||=e=>c.$emit(`back`)},[s(x,{size:20})]),d[1]||=p(`h1`,{class:`company-select-page__title`},`انتخاب شرکت`,-1),d[2]||=p(`span`,{class:`nav-btn nav-btn--spacer`,"aria-hidden":`true`},null,-1)]),d[3]||=p(`p`,{class:`company-select-page__subtitle`},`شرکت مورد نظر خود را انتخاب کنید`,-1)]),d[5]||=p(`header`,{class:`company-select-page__desk-head desktop-only`},[p(`h1`,null,`انتخاب شرکت`),p(`p`,null,`شرکت موردنظر را انتخاب کنید تا کاتالوگ و سفارش‌گذاری شروع شود`)],-1),p(`div`,k,[(l(!0),o(a,null,u(e.companies,a=>(l(),o(`button`,{key:a.id,type:`button`,class:t([`company-brand-card`,`company-brand-card--${e.cardVariant(a.slug)}`]),onClick:e=>c.$emit(`select`,a.id)},[p(`span`,j,[(l(),i(f(e.companyLogo(a.slug)),{size:72}))]),p(`h2`,M,n(a.name),1),p(`p`,N,n(e.companyDescription(a)),1),p(`span`,P,[d[4]||=r(` ورود به کاتالوگ‌ها `,-1),s(b,{size:16})])],10,A))),128))])]))}}),[[`__scopeId`,`data-v-336d01ae`]]),I={key:0,class:`page page__scroll`},L=_({__name:`CompanySelectView`,setup(t){let n=d(),{goBack:r}=T(`/`),a=v(),u=y(),f=m(!0),p=e(()=>a.getActiveCompanies()),h={par:{description:`تولید و عرضه انواع پارچه`,logo:S},"fam-baft":{description:`تولید و عرضه انواع پارچه`,logo:C}};g(async()=>{await a.loadCompanies(),f.value=!1});function _(e){let t=a.getCompany(e);t&&(u.selectCompany(t),n.push(`/companies/${e}/catalogs`))}function b(e){return e===`par`?`par`:`fam-baft`}function x(e){return h[e.slug]?.description??e.description}function E(e){return h[e]?.logo??S}return(e,t)=>f.value?(l(),o(`div`,I,[s(w,{variant:`cards`})])):(l(),i(F,{key:1,companies:p.value,"card-variant":b,"company-description":x,"company-logo":E,onSelect:_,onBack:c(r)},null,8,[`companies`,`onBack`]))}});export{L as default};
@@ -1 +1 @@
import{L as e,P as t,R as n,St as r,Y as i,_ as a,j as o,mt as s,n as c,rt as l,z as u}from"./BasicIcon-C6boaehV.js";import{t as d}from"./http-BC1hNfZ4.js";import{c as f}from"./format-BHJptHdF.js";import{a as p}from"./validation-BD7oi-2I.js";import{c as m,n as h}from"./index-B6MlTGUp.js";import{t as g}from"./FormErrorBanner-CjdBfIQT.js";import{n as _,r as v,t as y}from"./formErrors-DglFjYkZ.js";import{t as b}from"./crm-CtglbPsN.js";import{t as x}from"./useGoBack-CgNp0uP9.js";import{t as S}from"./PageHeader-CDQMUtCp.js";import{t as C}from"./BaseInput-qoEXOHCQ.js";var w={class:`page customer-create-page`},T={class:`page__scroll customer-create-scroll`},E={class:`form-card`},D={class:`footer`},O=c(u({__name:`CustomerCreateView`,setup(c){let u=a(),O=m(),k=b(),{goBack:A}=x(`/customers`),j=s(``),M=s(``),N=s(``),P=s(``),F=s({}),I=s(``),L=s(!1);function R(e){F.value=_(F.value,e),v(F.value)||(I.value=``)}async function z(){if(F.value={},I.value=``,!O.isAgent){I.value=`فقط واسطه می‌تواند مشتری به لیست خود اضافه کند.`;return}let e=f(N.value),t={};if(j.value.trim()||(t.firstName=`نام الزامی است.`),M.value.trim()||(t.lastName=`نام خانوادگی الزامی است.`),p(e)||(t.phone=`شماره موبایل معتبر نیست.`),v(t)){F.value=t,I.value=y;return}L.value=!0;try{let t=await k.registerCustomer({firstName:j.value.trim(),lastName:M.value.trim(),phone:e,businessName:P.value.trim()||void 0});if(t?.id){await u.replace({path:`/customer`,query:{id:t.id}});return}await u.replace(`/customers`)}catch(e){I.value=e instanceof d||e instanceof Error?e.message:`ثبت مشتری ناموفق بود.`}finally{L.value=!1}}return(a,s)=>(i(),t(`div`,w,[n(S,{title:`افزودن مشتری`,"show-back":``,centered:``,onBack:r(A)},null,8,[`onBack`]),o(`div`,T,[s[7]||=o(`p`,{class:`intro`},` مشتری به لیست شما اضافه می‌شود و بعد از ورود با همین شماره، سفارش‌هایش به حساب شما وصل می‌ماند. `,-1),n(g,{message:I.value},null,8,[`message`]),o(`div`,E,[n(C,{modelValue:j.value,"onUpdate:modelValue":[s[0]||=e=>j.value=e,s[1]||=e=>R(`firstName`)],label:`نام`,placeholder:`نام مشتری`,error:F.value.firstName},null,8,[`modelValue`,`error`]),n(C,{modelValue:M.value,"onUpdate:modelValue":[s[2]||=e=>M.value=e,s[3]||=e=>R(`lastName`)],label:`نام خانوادگی`,placeholder:`نام خانوادگی`,error:F.value.lastName},null,8,[`modelValue`,`error`]),n(C,{modelValue:N.value,"onUpdate:modelValue":[s[4]||=e=>N.value=e,s[5]||=e=>R(`phone`)],label:`شماره موبایل`,type:`tel`,inputmode:`tel`,dir:`ltr`,placeholder:`۰۹۱۲۳۴۵۶۷۸۹`,error:F.value.phone},null,8,[`modelValue`,`error`]),n(C,{modelValue:P.value,"onUpdate:modelValue":s[6]||=e=>P.value=e,label:`نام فروشگاه یا برند (اختیاری)`,placeholder:`مثلاً بوتیک رز`},null,8,[`modelValue`])])]),o(`footer`,D,[n(h,{block:``,loading:L.value,disabled:L.value,onClick:z},{default:l(()=>[...s[8]||=[e(` ثبت مشتری `,-1)]]),_:1},8,[`loading`,`disabled`])])]))}}),[[`__scopeId`,`data-v-f29dd241`]]);export{O as default};
import{L as e,P as t,R as n,St as r,Y as i,_ as a,j as o,mt as s,n as c,rt as l,z as u}from"./BasicIcon-C6boaehV.js";import{t as d}from"./http-BC1hNfZ4.js";import{c as f}from"./format-BHJptHdF.js";import{a as p}from"./validation-BD7oi-2I.js";import{c as m,n as h}from"./index-DzzUgmYT.js";import{t as g}from"./FormErrorBanner-CjdBfIQT.js";import{n as _,r as v,t as y}from"./formErrors-DglFjYkZ.js";import{t as b}from"./crm-CtglbPsN.js";import{t as x}from"./useGoBack-CgNp0uP9.js";import{t as S}from"./PageHeader-BFnKSFNh.js";import{t as C}from"./BaseInput-qoEXOHCQ.js";var w={class:`page customer-create-page`},T={class:`page__scroll customer-create-scroll`},E={class:`form-card`},D={class:`footer`},O=c(u({__name:`CustomerCreateView`,setup(c){let u=a(),O=m(),k=b(),{goBack:A}=x(`/customers`),j=s(``),M=s(``),N=s(``),P=s(``),F=s({}),I=s(``),L=s(!1);function R(e){F.value=_(F.value,e),v(F.value)||(I.value=``)}async function z(){if(F.value={},I.value=``,!O.isAgent){I.value=`فقط واسطه می‌تواند مشتری به لیست خود اضافه کند.`;return}let e=f(N.value),t={};if(j.value.trim()||(t.firstName=`نام الزامی است.`),M.value.trim()||(t.lastName=`نام خانوادگی الزامی است.`),p(e)||(t.phone=`شماره موبایل معتبر نیست.`),v(t)){F.value=t,I.value=y;return}L.value=!0;try{let t=await k.registerCustomer({firstName:j.value.trim(),lastName:M.value.trim(),phone:e,businessName:P.value.trim()||void 0});if(t?.id){await u.replace({path:`/customer`,query:{id:t.id}});return}await u.replace(`/customers`)}catch(e){I.value=e instanceof d||e instanceof Error?e.message:`ثبت مشتری ناموفق بود.`}finally{L.value=!1}}return(a,s)=>(i(),t(`div`,w,[n(S,{title:`افزودن مشتری`,"show-back":``,centered:``,onBack:r(A)},null,8,[`onBack`]),o(`div`,T,[s[7]||=o(`p`,{class:`intro`},` مشتری به لیست شما اضافه می‌شود و بعد از ورود با همین شماره، سفارش‌هایش به حساب شما وصل می‌ماند. `,-1),n(g,{message:I.value},null,8,[`message`]),o(`div`,E,[n(C,{modelValue:j.value,"onUpdate:modelValue":[s[0]||=e=>j.value=e,s[1]||=e=>R(`firstName`)],label:`نام`,placeholder:`نام مشتری`,error:F.value.firstName},null,8,[`modelValue`,`error`]),n(C,{modelValue:M.value,"onUpdate:modelValue":[s[2]||=e=>M.value=e,s[3]||=e=>R(`lastName`)],label:`نام خانوادگی`,placeholder:`نام خانوادگی`,error:F.value.lastName},null,8,[`modelValue`,`error`]),n(C,{modelValue:N.value,"onUpdate:modelValue":[s[4]||=e=>N.value=e,s[5]||=e=>R(`phone`)],label:`شماره موبایل`,type:`tel`,inputmode:`tel`,dir:`ltr`,placeholder:`۰۹۱۲۳۴۵۶۷۸۹`,error:F.value.phone},null,8,[`modelValue`,`error`]),n(C,{modelValue:P.value,"onUpdate:modelValue":s[6]||=e=>P.value=e,label:`نام فروشگاه یا برند (اختیاری)`,placeholder:`مثلاً بوتیک رز`},null,8,[`modelValue`])])]),o(`footer`,D,[n(h,{block:``,loading:L.value,disabled:L.value,onClick:z},{default:l(()=>[...s[8]||=[e(` ثبت مشتری `,-1)]]),_:1},8,[`loading`,`disabled`])])]))}}),[[`__scopeId`,`data-v-f29dd241`]]);export{O as default};
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{A as e,Ct as t,Et as n,M as r,N as i,O as a,P as o,R as s,St as c,Tt as l,Y as u,Z as d,_ as f,g as p,j as m,mt as h,n as g,q as _,rt as v,tt as y,z as b}from"./BasicIcon-C6boaehV.js";import{o as x}from"./format-BHJptHdF.js";import{o as ee,x as S,y as C}from"./index-B6MlTGUp.js";import{t as w}from"./IconFilter-D9NdLZ7w.js";import{t as T}from"./PageSkeleton-uO-jhbNm.js";import{t as E}from"./crm-CtglbPsN.js";import{t as D}from"./PageHeader-CDQMUtCp.js";import{t as O}from"./SearchField-Dx2PcshV.js";var k={class:`page customers-page`},A={class:`page__scroll page__scroll--with-nav customers-scroll`},j={class:`toolbar`},M={class:`summary-card`},N={class:`summary-card__icon`,"aria-hidden":`true`},P={class:`summary-card__cols`},F={class:`tabs`,role:`tablist`},I=[`aria-selected`,`onClick`],L={class:`tab__text`},te={key:1},R=[`onClick`],z={class:`customer-card__body`},B={class:`customer-card__top`},V=g(b({__name:`CustomersView`,setup(g){let b=p(),V=f(),H=E(),U=h(String(b.query.q??``)),W=e(()=>String(b.query.status??`all`)),G=h([]),K=h(!0),q=h(null),J=e(()=>G.value),Y=e(()=>G.value.filter(e=>e.status===`active`).length),X=e(()=>G.value.reduce((e,t)=>e+t.totalPurchaseToman,0)),Z=[{id:`all`,label:`همه`},{id:`active`,label:`فعال`},{id:`inactive`,label:`غیرفعال`},{id:`new`,label:`جدید`}];async function Q(){K.value=!0,q.value=null;try{G.value=await H.loadCustomers({q:U.value||void 0,status:W.value===`all`?void 0:W.value})}catch(e){q.value=e instanceof Error?e.message:`بارگذاری مشتریان ناموفق بود.`,G.value=[]}finally{K.value=!1}}_(Q),y([W,U],Q);function $(e){V.replace({path:`/customers`,query:{...e===`all`?{}:{status:e},...U.value?{q:U.value}:{}}})}function ne(e){U.value=e,V.replace({path:`/customers`,query:{...W.value===`all`?{}:{status:W.value},...e?{q:e}:{}}})}function re(e){return e===`active`?`فعال`:e===`inactive`?`غیرفعال`:`جدید`}function ie(){V.push(`/customers/new`)}function ae(){let e=[`all`,`active`,`inactive`,`new`],t=e.indexOf(W.value);$(e[(t<0?0:t+1)%e.length]??`all`)}return(e,f)=>(u(),o(`div`,k,[s(D,{title:`مشتریان من`,"show-back":``,centered:``,onBack:f[0]||=e=>c(V).back()},{trailing:v(()=>[m(`button`,{type:`button`,class:`nav-add-btn`,"aria-label":`ثبت مشتری`,onClick:ie},[s(C,{size:22})])]),_:1}),m(`div`,A,[m(`div`,j,[s(O,{"model-value":U.value,placeholder:`جستجو بر اساس نام یا موبایل...`,"onUpdate:modelValue":ne},null,8,[`model-value`]),m(`button`,{type:`button`,class:`filter-btn`,"aria-label":`فیلتر وضعیت`,onClick:ae},[s(w,{size:18})])]),m(`section`,M,[m(`div`,N,[s(S,{size:22,color:`#fff`})]),m(`div`,P,[m(`div`,null,[f[1]||=m(`span`,null,`تعداد مشتری فعال`,-1),m(`strong`,null,n(c(x)(Y.value)),1)]),f[4]||=m(`span`,{class:`summary-card__divider`,"aria-hidden":`true`},null,-1),m(`div`,null,[f[2]||=m(`span`,null,`مجموع خرید (ریال)`,-1),m(`strong`,null,n(c(x)(X.value)),1),f[3]||=m(`em`,null,`تومان`,-1)])])]),m(`div`,F,[(u(),o(a,null,d(Z,e=>m(`button`,{key:e.id,type:`button`,role:`tab`,class:t([`tab`,{"tab--active":W.value===e.id}]),"aria-selected":W.value===e.id,onClick:t=>$(e.id)},[m(`span`,L,n(e.label),1)],10,I)),64))]),K.value?(u(),r(T,{key:0,variant:`list`})):q.value?(u(),o(`p`,te,n(q.value),1)):i(``,!0),(u(!0),o(a,null,d(J.value,e=>(u(),o(`button`,{key:e.id,type:`button`,class:`customer-card`,onClick:t=>c(V).push({path:`/customer`,query:{id:e.id}})},[m(`div`,{class:`customer-card__avatar`,style:l({background:e.avatarColor})},n(e.avatarLetter),5),m(`div`,z,[m(`div`,B,[m(`strong`,null,n(e.name),1)]),m(`p`,null,n(e.phone),1),m(`p`,null,`مجموع خرید: `+n(c(x)(e.totalPurchaseToman))+` تومان`,1)]),m(`span`,{class:t([`status`,{"status--ok":e.status===`active`,"status--off":e.status===`inactive`,"status--new":e.status===`new`}])},n(re(e.status)),3),s(ee,{size:18,class:`chevron`})],8,R))),128)),f[5]||=m(`section`,{class:`promo`},[m(`div`,null,[m(`strong`,null,`افزایش مشتریان = افزایش سطح شما`),m(`p`,null,`با جذب مشتریان بیشتر به سطح VIP برسید`)])],-1)])]))}}),[[`__scopeId`,`data-v-823e3f40`]]);export{V as default};
import{A as e,Ct as t,Et as n,M as r,N as i,O as a,P as o,R as s,St as c,Tt as l,Y as u,Z as d,_ as f,g as p,j as m,mt as h,n as g,q as _,rt as v,tt as y,z as b}from"./BasicIcon-C6boaehV.js";import{o as x}from"./format-BHJptHdF.js";import{o as ee,x as S,y as C}from"./index-DzzUgmYT.js";import{t as w}from"./IconFilter-D9NdLZ7w.js";import{t as T}from"./PageSkeleton-uO-jhbNm.js";import{t as E}from"./crm-CtglbPsN.js";import{t as D}from"./PageHeader-BFnKSFNh.js";import{t as O}from"./SearchField-Dx2PcshV.js";var k={class:`page customers-page`},A={class:`page__scroll page__scroll--with-nav customers-scroll`},j={class:`toolbar`},M={class:`summary-card`},N={class:`summary-card__icon`,"aria-hidden":`true`},P={class:`summary-card__cols`},F={class:`tabs`,role:`tablist`},I=[`aria-selected`,`onClick`],L={class:`tab__text`},te={key:1},R=[`onClick`],z={class:`customer-card__body`},B={class:`customer-card__top`},V=g(b({__name:`CustomersView`,setup(g){let b=p(),V=f(),H=E(),U=h(String(b.query.q??``)),W=e(()=>String(b.query.status??`all`)),G=h([]),K=h(!0),q=h(null),J=e(()=>G.value),Y=e(()=>G.value.filter(e=>e.status===`active`).length),X=e(()=>G.value.reduce((e,t)=>e+t.totalPurchaseToman,0)),Z=[{id:`all`,label:`همه`},{id:`active`,label:`فعال`},{id:`inactive`,label:`غیرفعال`},{id:`new`,label:`جدید`}];async function Q(){K.value=!0,q.value=null;try{G.value=await H.loadCustomers({q:U.value||void 0,status:W.value===`all`?void 0:W.value})}catch(e){q.value=e instanceof Error?e.message:`بارگذاری مشتریان ناموفق بود.`,G.value=[]}finally{K.value=!1}}_(Q),y([W,U],Q);function $(e){V.replace({path:`/customers`,query:{...e===`all`?{}:{status:e},...U.value?{q:U.value}:{}}})}function ne(e){U.value=e,V.replace({path:`/customers`,query:{...W.value===`all`?{}:{status:W.value},...e?{q:e}:{}}})}function re(e){return e===`active`?`فعال`:e===`inactive`?`غیرفعال`:`جدید`}function ie(){V.push(`/customers/new`)}function ae(){let e=[`all`,`active`,`inactive`,`new`],t=e.indexOf(W.value);$(e[(t<0?0:t+1)%e.length]??`all`)}return(e,f)=>(u(),o(`div`,k,[s(D,{title:`مشتریان من`,"show-back":``,centered:``,onBack:f[0]||=e=>c(V).back()},{trailing:v(()=>[m(`button`,{type:`button`,class:`nav-add-btn`,"aria-label":`ثبت مشتری`,onClick:ie},[s(C,{size:22})])]),_:1}),m(`div`,A,[m(`div`,j,[s(O,{"model-value":U.value,placeholder:`جستجو بر اساس نام یا موبایل...`,"onUpdate:modelValue":ne},null,8,[`model-value`]),m(`button`,{type:`button`,class:`filter-btn`,"aria-label":`فیلتر وضعیت`,onClick:ae},[s(w,{size:18})])]),m(`section`,M,[m(`div`,N,[s(S,{size:22,color:`#fff`})]),m(`div`,P,[m(`div`,null,[f[1]||=m(`span`,null,`تعداد مشتری فعال`,-1),m(`strong`,null,n(c(x)(Y.value)),1)]),f[4]||=m(`span`,{class:`summary-card__divider`,"aria-hidden":`true`},null,-1),m(`div`,null,[f[2]||=m(`span`,null,`مجموع خرید (ریال)`,-1),m(`strong`,null,n(c(x)(X.value)),1),f[3]||=m(`em`,null,`تومان`,-1)])])]),m(`div`,F,[(u(),o(a,null,d(Z,e=>m(`button`,{key:e.id,type:`button`,role:`tab`,class:t([`tab`,{"tab--active":W.value===e.id}]),"aria-selected":W.value===e.id,onClick:t=>$(e.id)},[m(`span`,L,n(e.label),1)],10,I)),64))]),K.value?(u(),r(T,{key:0,variant:`list`})):q.value?(u(),o(`p`,te,n(q.value),1)):i(``,!0),(u(!0),o(a,null,d(J.value,e=>(u(),o(`button`,{key:e.id,type:`button`,class:`customer-card`,onClick:t=>c(V).push({path:`/customer`,query:{id:e.id}})},[m(`div`,{class:`customer-card__avatar`,style:l({background:e.avatarColor})},n(e.avatarLetter),5),m(`div`,z,[m(`div`,B,[m(`strong`,null,n(e.name),1)]),m(`p`,null,n(e.phone),1),m(`p`,null,`مجموع خرید: `+n(c(x)(e.totalPurchaseToman))+` تومان`,1)]),m(`span`,{class:t([`status`,{"status--ok":e.status===`active`,"status--off":e.status===`inactive`,"status--new":e.status===`new`}])},n(re(e.status)),3),s(ee,{size:18,class:`chevron`})],8,R))),128)),f[5]||=m(`section`,{class:`promo`},[m(`div`,null,[m(`strong`,null,`افزایش مشتریان = افزایش سطح شما`),m(`p`,null,`با جذب مشتریان بیشتر به سطح VIP برسید`)])],-1)])]))}}),[[`__scopeId`,`data-v-823e3f40`]]);export{V as default};
@@ -1 +1 @@
import{D as e,Et as t,M as n,N as r,O as i,P as a,R as o,St as s,Y as c,Z as l,_ as u,j as d,mt as f,n as p,q as m,z as h}from"./BasicIcon-C6boaehV.js";import{l as g,o as _}from"./index-B6MlTGUp.js";import{t as v}from"./PageSkeleton-uO-jhbNm.js";import{t as y}from"./crm-CtglbPsN.js";import{t as b}from"./PageHeader-CDQMUtCp.js";import{t as x}from"./IconHeart-CZMe29Nz.js";var S={class:`page`},C={class:`page__scroll page__scroll--with-nav body`},w={key:1},T={key:2,class:`empty`},E=[`onClick`],D={class:`row__body`},O=[`onClick`],k=p(h({__name:`FavoritesView`,setup(p){let h=u(),k=g(),A=y(),j=f([]),M=f(!0),N=f(null);m(async()=>{M.value=!0;try{await k.hydrate();let e=await A.loadBrowseProducts(),t=new Set(k.ids);j.value=e.filter(e=>t.has(e.id))}catch(e){N.value=e instanceof Error?e.message:`بارگذاری علاقه‌مندی‌ها ناموفق بود.`}finally{M.value=!1}});async function P(e){await k.toggle(e),j.value=j.value.filter(t=>t.id!==e)}return(u,f)=>(c(),a(`div`,S,[o(b,{title:`علاقه‌مندی‌ها`,"show-back":``,centered:``,onBack:f[0]||=e=>s(h).back()}),d(`div`,C,[M.value?(c(),n(v,{key:0,variant:`grid`})):N.value?(c(),a(`p`,w,t(N.value),1)):j.value.length?r(``,!0):(c(),a(`p`,T,`هنوز محصولی ذخیره نکرده‌اید.`)),(c(!0),a(i,null,l(j.value,n=>(c(),a(`article`,{key:n.id,class:`row`,onClick:e=>s(h).push({path:`/product`,query:{id:n.id}})},[d(`div`,D,[d(`strong`,null,t(n.name),1),d(`span`,null,`کد `+t(n.code),1)]),d(`button`,{type:`button`,class:`bookmark`,"aria-label":`حذف از نشان‌شده‌ها`,onClick:e(e=>P(n.id),[`stop`])},[o(x,{size:18,filled:``,color:`#0a353c`})],8,O),o(_,{size:16,color:`#9aa3a0`})],8,E))),128))])]))}}),[[`__scopeId`,`data-v-8e70f9c9`]]);export{k as default};
import{D as e,Et as t,M as n,N as r,O as i,P as a,R as o,St as s,Y as c,Z as l,_ as u,j as d,mt as f,n as p,q as m,z as h}from"./BasicIcon-C6boaehV.js";import{l as g,o as _}from"./index-DzzUgmYT.js";import{t as v}from"./PageSkeleton-uO-jhbNm.js";import{t as y}from"./crm-CtglbPsN.js";import{t as b}from"./PageHeader-BFnKSFNh.js";import{t as x}from"./IconHeart-CZMe29Nz.js";var S={class:`page`},C={class:`page__scroll page__scroll--with-nav body`},w={key:1},T={key:2,class:`empty`},E=[`onClick`],D={class:`row__body`},O=[`onClick`],k=p(h({__name:`FavoritesView`,setup(p){let h=u(),k=g(),A=y(),j=f([]),M=f(!0),N=f(null);m(async()=>{M.value=!0;try{await k.hydrate();let e=await A.loadBrowseProducts(),t=new Set(k.ids);j.value=e.filter(e=>t.has(e.id))}catch(e){N.value=e instanceof Error?e.message:`بارگذاری علاقه‌مندی‌ها ناموفق بود.`}finally{M.value=!1}});async function P(e){await k.toggle(e),j.value=j.value.filter(t=>t.id!==e)}return(u,f)=>(c(),a(`div`,S,[o(b,{title:`علاقه‌مندی‌ها`,"show-back":``,centered:``,onBack:f[0]||=e=>s(h).back()}),d(`div`,C,[M.value?(c(),n(v,{key:0,variant:`grid`})):N.value?(c(),a(`p`,w,t(N.value),1)):j.value.length?r(``,!0):(c(),a(`p`,T,`هنوز محصولی ذخیره نکرده‌اید.`)),(c(!0),a(i,null,l(j.value,n=>(c(),a(`article`,{key:n.id,class:`row`,onClick:e=>s(h).push({path:`/product`,query:{id:n.id}})},[d(`div`,D,[d(`strong`,null,t(n.name),1),d(`span`,null,`کد `+t(n.code),1)]),d(`button`,{type:`button`,class:`bookmark`,"aria-label":`حذف از نشان‌شده‌ها`,onClick:e(e=>P(n.id),[`stop`])},[o(x,{size:18,filled:``,color:`#0a353c`})],8,O),o(_,{size:16,color:`#9aa3a0`})],8,E))),128))])]))}}),[[`__scopeId`,`data-v-8e70f9c9`]]);export{k as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.meeting-page[data-v-e8f8b993]{background:var(--color-bg)}.meeting-scroll[data-v-e8f8b993]{padding:8px var(--page-padding) 100px;flex-direction:column;gap:16px;display:flex}.customer-card[data-v-e8f8b993]{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);align-items:center;gap:12px;padding:12px;display:flex}.avatar[data-v-e8f8b993]{border-radius:999px;place-items:center;width:52px;height:52px;font-weight:700;display:grid}.customer-card p[data-v-e8f8b993]{color:var(--color-text-muted);margin:2px 0 0;font-size:.8rem}h3[data-v-e8f8b993]{margin:0 0 8px;font-size:.9rem}.type-grid[data-v-e8f8b993]{grid-template-columns:repeat(4,1fr);gap:8px;display:grid}.submit-icon-btn[data-v-e8f8b993]{width:40px;height:40px;color:var(--color-primary);cursor:pointer;background:0 0;border:none;place-items:center;padding:0;display:grid}.submit-icon-btn[data-v-e8f8b993]:disabled{opacity:.5;cursor:not-allowed}.type-btn[data-v-e8f8b993]{border:1.5px solid var(--color-border);background:var(--color-surface);cursor:pointer;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;gap:6px;min-height:72px;padding:8px 6px;font-size:.72rem;display:flex}.type-btn--active[data-v-e8f8b993]{border-color:var(--color-primary);color:var(--color-primary);background:var(--color-primary-soft);border-width:2px;font-weight:700}.two-cols[data-v-e8f8b993]{grid-template-columns:1fr 1fr;gap:8px;display:grid}.field[data-v-e8f8b993]{flex-direction:column;gap:6px;font-size:.85rem;display:flex}.field input[data-v-e8f8b993],.field textarea[data-v-e8f8b993]{border:1px solid var(--color-border);font:inherit;resize:vertical;background:#f5f5f5;border-radius:12px;padding:12px}.field em[data-v-e8f8b993]{color:var(--color-text-muted);align-self:flex-end;font-size:.75rem;font-style:normal}.files[data-v-e8f8b993]{gap:8px;display:flex}.file-thumb[data-v-e8f8b993],.file-add[data-v-e8f8b993]{border-radius:12px;width:64px;height:64px}.file-thumb[data-v-e8f8b993]{background:linear-gradient(145deg,#4b5563,#1f2937)}.file-add[data-v-e8f8b993]{border:1.5px dashed var(--color-border);color:var(--color-text-muted);cursor:pointer;background:0 0;font-size:1.4rem}.reminder-box[data-v-e8f8b993]{background:var(--color-surface);border-radius:var(--radius-md);flex-direction:column;gap:12px;padding:14px;display:flex}.reminder-box__head[data-v-e8f8b993]{justify-content:space-between;align-items:center;display:flex}.toggle[data-v-e8f8b993]{cursor:pointer;background:#d1d5db;border:none;border-radius:999px;width:46px;height:28px;padding:3px}.toggle span[data-v-e8f8b993]{background:var(--color-surface);border-radius:999px;width:22px;height:22px;transition:transform .15s;display:block}.toggle--on[data-v-e8f8b993]{background:var(--color-primary)}.toggle--on span[data-v-e8f8b993]{transform:translate(-18px)}.footer[data-v-e8f8b993]{padding:12px var(--page-padding) calc(12px + env(safe-area-inset-bottom,0));background:var(--color-surface);border-top:1px solid var(--color-border);position:sticky;bottom:0}.submit[data-v-e8f8b993]{background:var(--color-primary);color:#fff;cursor:pointer;border:none;border-radius:14px;width:100%;min-height:52px;font-weight:700}
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
.meeting-page[data-v-2708a25b]{background:var(--color-bg)}.meeting-scroll[data-v-2708a25b]{padding:8px var(--page-padding) 100px;flex-direction:column;gap:16px;display:flex}.customer-card[data-v-2708a25b]{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);align-items:center;gap:12px;padding:12px;display:flex}.avatar[data-v-2708a25b]{border-radius:999px;place-items:center;width:52px;height:52px;font-weight:700;display:grid}.customer-card p[data-v-2708a25b]{color:var(--color-text-muted);margin:2px 0 0;font-size:.8rem}h3[data-v-2708a25b]{margin:0 0 8px;font-size:.9rem}.type-grid[data-v-2708a25b]{grid-template-columns:repeat(4,1fr);gap:8px;display:grid}.submit-icon-btn[data-v-2708a25b]{width:40px;height:40px;color:var(--color-primary);cursor:pointer;background:0 0;border:none;place-items:center;padding:0;display:grid}.submit-icon-btn[data-v-2708a25b]:disabled{opacity:.5;cursor:not-allowed}.type-btn[data-v-2708a25b]{border:1.5px solid var(--color-border);background:var(--color-surface);cursor:pointer;border-radius:12px;flex-direction:column;justify-content:center;align-items:center;gap:6px;min-height:72px;padding:8px 6px;font-size:.72rem;display:flex}.type-btn--active[data-v-2708a25b]{border-color:var(--color-primary);color:var(--color-primary);background:var(--color-primary-soft);border-width:2px;font-weight:700}.two-cols[data-v-2708a25b]{grid-template-columns:1fr 1fr;gap:8px;display:grid}.field[data-v-2708a25b]{flex-direction:column;gap:6px;font-size:.85rem;display:flex}.field input[data-v-2708a25b],.field textarea[data-v-2708a25b]{border:1px solid var(--color-border);font:inherit;resize:vertical;background:#f5f5f5;border-radius:12px;padding:12px}.field em[data-v-2708a25b]{color:var(--color-text-muted);align-self:flex-end;font-size:.75rem;font-style:normal}.files[data-v-2708a25b]{gap:8px;display:flex}.file-thumb[data-v-2708a25b],.file-add[data-v-2708a25b]{border-radius:12px;width:64px;height:64px}.file-thumb[data-v-2708a25b]{background:linear-gradient(145deg,#4b5563,#1f2937)}.file-add[data-v-2708a25b]{border:1.5px dashed var(--color-border);color:var(--color-text-muted);cursor:pointer;background:0 0;font-size:1.4rem}.reminder-box[data-v-2708a25b]{background:var(--color-surface);border-radius:var(--radius-md);flex-direction:column;gap:12px;padding:14px;display:flex}.reminder-box__head[data-v-2708a25b]{justify-content:space-between;align-items:center;display:flex}.toggle[data-v-2708a25b]{cursor:pointer;background:#d1d5db;border:none;border-radius:999px;width:46px;height:28px;padding:3px}.toggle span[data-v-2708a25b]{background:#fff;border-radius:999px;width:22px;height:22px;transition:transform .15s;display:block}.toggle--on[data-v-2708a25b]{background:var(--color-primary)}.toggle--on span[data-v-2708a25b]{transform:translate(-18px)}.footer[data-v-2708a25b]{padding:12px var(--page-padding) calc(12px + env(safe-area-inset-bottom,0));background:var(--color-surface);border-top:1px solid var(--color-border);position:sticky;bottom:0}.submit[data-v-2708a25b]{background:var(--color-primary);color:#fff;cursor:pointer;border:none;border-radius:14px;width:100%;min-height:52px;font-weight:700}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{A as e,Et as t,L as n,N as r,P as i,R as a,St as o,Y as s,_ as c,g as l,j as u,n as d,rt as f,z as p}from"./BasicIcon-C6boaehV.js";import{a as m}from"./format-BHJptHdF.js";import{d as h,n as g}from"./index-B6MlTGUp.js";import{t as _}from"./useGoBack-CgNp0uP9.js";import{t as v}from"./PageHeader-CDQMUtCp.js";var y={class:`page success-page`},b={class:`page__scroll success-page__scroll`},x={key:0,class:`card mb-4`},S={class:`info-row`},C={class:`info-row`},w={class:`info-row`},T=d(p({__name:`OrderSuccessView`,setup(d){let p=l(),T=c(),{goBack:E}=_(`/orders`),D=h(),O=e(()=>D.getOrderById(p.params.id));return(e,c)=>(s(),i(`div`,y,[a(v,{title:`ثبت موفق`,"show-back":``,centered:``,onBack:o(E)},null,8,[`onBack`]),u(`div`,b,[c[8]||=u(`div`,{class:`success-icon`},`✓`,-1),c[9]||=u(`h1`,{class:`page-title text-center`},`سفارش ثبت شد`,-1),c[10]||=u(`p`,{class:`text-center text-muted mb-4`},` سفارش شما با موفقیت ثبت شد. کارشناسان ما سفارش را بررسی و قیمت‌گذاری می‌کنند و به‌محض صدور فاکتور، از طریق پیامک و پنل کاربری به شما اطلاع می‌دهیم تا روش تسویه را انتخاب کنید. `,-1),O.value?(s(),i(`div`,x,[u(`div`,S,[c[2]||=u(`span`,null,`شماره سفارش:`,-1),u(`strong`,null,t(O.value.orderNumber),1)]),u(`div`,C,[c[3]||=u(`span`,null,`شرکت:`,-1),u(`span`,null,t(O.value.companyName),1)]),u(`div`,w,[c[4]||=u(`span`,null,`متراژ:`,-1),u(`span`,null,t(o(m)(O.value.totalMeterage)),1)]),c[5]||=u(`div`,{class:`info-row`},[u(`span`,null,`وضعیت:`),u(`span`,null,`در انتظار قیمت‌گذاری`)],-1)])):r(``,!0),a(g,{block:``,onClick:c[0]||=e=>o(T).replace(`/orders/${O.value?.id}`)},{default:f(()=>[...c[6]||=[n(` مشاهده جزئیات سفارش `,-1)]]),_:1}),a(g,{block:``,variant:`outline`,class:`mt-2`,onClick:c[1]||=e=>o(T).replace(`/`)},{default:f(()=>[...c[7]||=[n(` بازگشت به خانه `,-1)]]),_:1})])]))}}),[[`__scopeId`,`data-v-3d172af9`]]);export{T as default};
import{A as e,Et as t,L as n,N as r,P as i,R as a,St as o,Y as s,_ as c,g as l,j as u,n as d,rt as f,z as p}from"./BasicIcon-C6boaehV.js";import{a as m}from"./format-BHJptHdF.js";import{d as h,n as g}from"./index-DzzUgmYT.js";import{t as _}from"./useGoBack-CgNp0uP9.js";import{t as v}from"./PageHeader-BFnKSFNh.js";var y={class:`page success-page`},b={class:`page__scroll success-page__scroll`},x={key:0,class:`card mb-4`},S={class:`info-row`},C={class:`info-row`},w={class:`info-row`},T=d(p({__name:`OrderSuccessView`,setup(d){let p=l(),T=c(),{goBack:E}=_(`/orders`),D=h(),O=e(()=>D.getOrderById(p.params.id));return(e,c)=>(s(),i(`div`,y,[a(v,{title:`ثبت موفق`,"show-back":``,centered:``,onBack:o(E)},null,8,[`onBack`]),u(`div`,b,[c[8]||=u(`div`,{class:`success-icon`},`✓`,-1),c[9]||=u(`h1`,{class:`page-title text-center`},`سفارش ثبت شد`,-1),c[10]||=u(`p`,{class:`text-center text-muted mb-4`},` سفارش شما با موفقیت ثبت شد. کارشناسان ما سفارش را بررسی و قیمت‌گذاری می‌کنند و به‌محض صدور فاکتور، از طریق پیامک و پنل کاربری به شما اطلاع می‌دهیم تا روش تسویه را انتخاب کنید. `,-1),O.value?(s(),i(`div`,x,[u(`div`,S,[c[2]||=u(`span`,null,`شماره سفارش:`,-1),u(`strong`,null,t(O.value.orderNumber),1)]),u(`div`,C,[c[3]||=u(`span`,null,`شرکت:`,-1),u(`span`,null,t(O.value.companyName),1)]),u(`div`,w,[c[4]||=u(`span`,null,`متراژ:`,-1),u(`span`,null,t(o(m)(O.value.totalMeterage)),1)]),c[5]||=u(`div`,{class:`info-row`},[u(`span`,null,`وضعیت:`),u(`span`,null,`در انتظار قیمت‌گذاری`)],-1)])):r(``,!0),a(g,{block:``,onClick:c[0]||=e=>o(T).replace(`/orders/${O.value?.id}`)},{default:f(()=>[...c[6]||=[n(` مشاهده جزئیات سفارش `,-1)]]),_:1}),a(g,{block:``,variant:`outline`,class:`mt-2`,onClick:c[1]||=e=>o(T).replace(`/`)},{default:f(()=>[...c[7]||=[n(` بازگشت به خانه `,-1)]]),_:1})])]))}}),[[`__scopeId`,`data-v-3d172af9`]]);export{T as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{Et as e,N as t,O as n,P as r,Q as i,R as a,Y as o,j as s,n as c,z as l}from"./BasicIcon-C6boaehV.js";import{i as u}from"./index-B6MlTGUp.js";import{t as d}from"./IconChevronBack-DORV2H0q.js";var f={class:`page-nav`},p={key:1,class:`nav-btn`},m={class:`page-nav__title`},h={class:`page-nav__trailing`},g={key:0,class:`page-nav__subtitle`},_={key:1,class:`page-header`},v={class:`header-text`},y={class:`page-title`},b={key:0,class:`page-subtitle`},x={class:`page-header__trailing`},S=c(l({__name:`PageHeader`,props:{title:{},subtitle:{},showBack:{type:Boolean},centered:{type:Boolean}},emits:[`back`],setup(c){return(l,S)=>c.centered?(o(),r(n,{key:0},[s(`header`,f,[c.showBack?(o(),r(`button`,{key:0,type:`button`,class:`nav-btn`,"aria-label":`بازگشت`,onClick:S[0]||=e=>l.$emit(`back`)},[a(d,{size:20})])):(o(),r(`span`,p)),s(`h1`,m,e(c.title),1),s(`div`,h,[i(l.$slots,`trailing`,{},void 0,!0),a(u,{variant:`inline`,class:`mobile-only`})])]),c.subtitle?(o(),r(`p`,g,e(c.subtitle),1)):t(``,!0)],64)):(o(),r(`header`,_,[c.showBack?(o(),r(`button`,{key:0,type:`button`,class:`back-btn`,"aria-label":`بازگشت`,onClick:S[1]||=e=>l.$emit(`back`)},[a(d,{size:20})])):t(``,!0),s(`div`,v,[s(`h1`,y,e(c.title),1),c.subtitle?(o(),r(`p`,b,e(c.subtitle),1)):t(``,!0)]),s(`div`,x,[i(l.$slots,`trailing`,{},void 0,!0),a(u,{variant:`inline`,class:`mobile-only`})])]))}}),[[`__scopeId`,`data-v-dd86426f`]]);export{S as t};
import{Et as e,N as t,O as n,P as r,Q as i,R as a,Y as o,j as s,n as c,z as l}from"./BasicIcon-C6boaehV.js";import{i as u}from"./index-DzzUgmYT.js";import{t as d}from"./IconChevronBack-DORV2H0q.js";var f={class:`page-nav`},p={key:1,class:`nav-btn`},m={class:`page-nav__title`},h={class:`page-nav__trailing`},g={key:0,class:`page-nav__subtitle`},_={key:1,class:`page-header`},v={class:`header-text`},y={class:`page-title`},b={key:0,class:`page-subtitle`},x={class:`page-header__trailing`},S=c(l({__name:`PageHeader`,props:{title:{},subtitle:{},showBack:{type:Boolean},centered:{type:Boolean}},emits:[`back`],setup(c){return(l,S)=>c.centered?(o(),r(n,{key:0},[s(`header`,f,[c.showBack?(o(),r(`button`,{key:0,type:`button`,class:`nav-btn`,"aria-label":`بازگشت`,onClick:S[0]||=e=>l.$emit(`back`)},[a(d,{size:20})])):(o(),r(`span`,p)),s(`h1`,m,e(c.title),1),s(`div`,h,[i(l.$slots,`trailing`,{},void 0,!0),a(u,{variant:`inline`,class:`mobile-only`})])]),c.subtitle?(o(),r(`p`,g,e(c.subtitle),1)):t(``,!0)],64)):(o(),r(`header`,_,[c.showBack?(o(),r(`button`,{key:0,type:`button`,class:`back-btn`,"aria-label":`بازگشت`,onClick:S[1]||=e=>l.$emit(`back`)},[a(d,{size:20})])):t(``,!0),s(`div`,v,[s(`h1`,y,e(c.title),1),c.subtitle?(o(),r(`p`,b,e(c.subtitle),1)):t(``,!0)]),s(`div`,x,[i(l.$slots,`trailing`,{},void 0,!0),a(u,{variant:`inline`,class:`mobile-only`})])]))}}),[[`__scopeId`,`data-v-dd86426f`]]);export{S as t};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
.products-page[data-v-ea9e9c36]{background:var(--color-bg);position:relative}.nav-filter[data-v-ea9e9c36]{cursor:pointer;width:40px;height:40px;color:var(--color-text);background:0 0;border:none;place-items:center;display:grid}.products-scroll[data-v-ea9e9c36]{padding:8px var(--page-padding) 24px;flex-direction:column;gap:12px;display:flex}.cat-banner[data-v-ea9e9c36]{border-radius:var(--radius-lg);background:linear-gradient(100deg, rgba(var(--color-primary-rgb), .75), rgba(var(--color-primary-rgb), .35)), #2f3430;color:#fff;align-items:center;gap:12px;min-height:96px;padding:16px;display:flex}.cat-banner__icon[data-v-ea9e9c36]{background:#fff3;border-radius:999px;flex-shrink:0;width:48px;height:48px}.cat-banner h2[data-v-ea9e9c36]{margin:0 0 4px;font-size:1rem}.cat-banner p[data-v-ea9e9c36]{opacity:.9;margin:0;font-size:.8rem}.chips[data-v-ea9e9c36]{gap:8px;display:flex;overflow-x:auto}.chip[data-v-ea9e9c36]{color:var(--color-text);white-space:nowrap;cursor:pointer;background:#ecefee;border:none;border-radius:999px;padding:8px 14px;font-size:.8rem}.chip--active[data-v-ea9e9c36]{background:var(--color-primary);color:#fff}.list-meta[data-v-ea9e9c36]{color:var(--color-text-muted);justify-content:space-between;align-items:center;font-size:.8rem;display:flex}.list-meta__sort[data-v-ea9e9c36]{color:var(--color-primary);font:inherit;cursor:pointer;background:0 0;border:none;padding:0;font-weight:700}.product-item[data-v-ea9e9c36]{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);cursor:pointer;grid-template-columns:112px 1fr;align-items:stretch;gap:12px;padding:12px;display:grid}.product-item__image[data-v-ea9e9c36]{background:linear-gradient(145deg,#2a3544,#111827) 50%/cover;border-radius:12px;min-height:112px}.product-item__body[data-v-ea9e9c36]{flex-direction:column;gap:6px;min-width:0;display:flex}.product-item__head[data-v-ea9e9c36]{justify-content:space-between;align-items:flex-start;gap:8px;display:flex}.product-item__title[data-v-ea9e9c36]{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;display:flex}.product-item__title strong[data-v-ea9e9c36]{color:var(--color-text);font-size:.9375rem;font-weight:700;line-height:1.4}.new-tag[data-v-ea9e9c36]{background:var(--color-primary);color:#fff;white-space:nowrap;border-radius:999px;padding:2px 8px;font-size:.65rem;font-weight:700}.bookmark[data-v-ea9e9c36]{cursor:pointer;background:0 0;border:none;flex-shrink:0;place-items:center;width:28px;height:28px;margin-inline-start:auto;padding:0;display:grid}.product-item__specs[data-v-ea9e9c36]{color:var(--color-text-muted);text-align:start;flex-direction:column;gap:2px;margin:0;padding:0;font-size:.75rem;line-height:1.55;list-style:none;display:flex}.swatches[data-v-ea9e9c36]{flex-wrap:wrap;gap:6px;margin-top:2px;display:flex}.swatch[data-v-ea9e9c36]{background-position:50%;background-size:cover;border:1px solid #00000014;border-radius:999px;width:14px;height:14px}.details-link[data-v-ea9e9c36]{color:var(--color-info);cursor:pointer;background:0 0;border:none;align-self:flex-end;align-items:center;gap:2px;margin-top:auto;padding:0;font-size:.75rem;display:inline-flex}.empty-list[data-v-ea9e9c36]{text-align:center;color:var(--color-text-muted);margin:12px 0 0}
+1
View File
@@ -0,0 +1 @@
.products-page[data-v-67beb24b]{background:var(--color-bg);position:relative}.nav-filter[data-v-67beb24b]{cursor:pointer;width:40px;height:40px;color:var(--color-text);background:0 0;border:none;place-items:center;display:grid}.products-scroll[data-v-67beb24b]{padding:8px var(--page-padding) 24px;flex-direction:column;gap:12px;display:flex}.cat-banner[data-v-67beb24b]{border-radius:var(--radius-lg);background:linear-gradient(100deg, rgba(var(--color-primary-rgb), .75), rgba(var(--color-primary-rgb), .35)), #2f3430;color:#fff;align-items:center;gap:12px;min-height:96px;padding:16px;display:flex}.cat-banner__icon[data-v-67beb24b]{background:#fff3;border-radius:999px;flex-shrink:0;width:48px;height:48px}.cat-banner h2[data-v-67beb24b]{margin:0 0 4px;font-size:1rem}.cat-banner p[data-v-67beb24b]{opacity:.9;margin:0;font-size:.8rem}.chips[data-v-67beb24b]{gap:8px;display:flex;overflow-x:auto}.chip[data-v-67beb24b]{background:var(--color-bg);color:var(--color-text);white-space:nowrap;cursor:pointer;border:none;border-radius:999px;padding:8px 14px;font-size:.8rem}.chip--active[data-v-67beb24b]{background:var(--color-primary);color:#fff}.list-meta[data-v-67beb24b]{color:var(--color-text-muted);justify-content:space-between;align-items:center;font-size:.8rem;display:flex}.list-meta__sort[data-v-67beb24b]{color:var(--color-primary);font:inherit;cursor:pointer;background:0 0;border:none;padding:0;font-weight:700}.product-item[data-v-67beb24b]{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);cursor:pointer;grid-template-columns:112px 1fr;align-items:stretch;gap:12px;padding:12px;display:grid}.product-item__image[data-v-67beb24b]{background:linear-gradient(145deg,#2a3544,#111827) 50%/cover;border-radius:12px;min-height:112px}.product-item__body[data-v-67beb24b]{flex-direction:column;gap:6px;min-width:0;display:flex}.product-item__head[data-v-67beb24b]{justify-content:space-between;align-items:flex-start;gap:8px;display:flex}.product-item__title[data-v-67beb24b]{flex-wrap:wrap;align-items:center;gap:6px;min-width:0;display:flex}.product-item__title strong[data-v-67beb24b]{color:var(--color-text);font-size:.9375rem;font-weight:700;line-height:1.4}.new-tag[data-v-67beb24b]{background:var(--color-primary);color:#fff;white-space:nowrap;border-radius:999px;padding:2px 8px;font-size:.65rem;font-weight:700}.bookmark[data-v-67beb24b]{cursor:pointer;background:0 0;border:none;flex-shrink:0;place-items:center;width:28px;height:28px;margin-inline-start:auto;padding:0;display:grid}.product-item__specs[data-v-67beb24b]{color:var(--color-text-muted);text-align:start;flex-direction:column;gap:2px;margin:0;padding:0;font-size:.75rem;line-height:1.55;list-style:none;display:flex}.swatches[data-v-67beb24b]{flex-wrap:wrap;gap:6px;margin-top:2px;display:flex}.swatch[data-v-67beb24b]{background-position:50%;background-size:cover;border:1px solid #00000014;border-radius:999px;width:14px;height:14px}.details-link[data-v-67beb24b]{color:var(--color-info);cursor:pointer;background:0 0;border:none;align-self:flex-end;align-items:center;gap:2px;margin-top:auto;padding:0;font-size:.75rem;display:inline-flex}.empty-list[data-v-67beb24b]{text-align:center;color:var(--color-text-muted);margin:12px 0 0}
@@ -1 +1 @@
import{t as e}from"./CheckListView-Bq44NHdA.js";import{M as t,Y as n,z as r}from"./BasicIcon-C6boaehV.js";var i=r({__name:`ProfileChecksView`,setup(r){return(r,i)=>(n(),t(e))}});export{i as default};
import{t as e}from"./CheckListView-DSyd4n4b.js";import{M as t,Y as n,z as r}from"./BasicIcon-C6boaehV.js";var i=r({__name:`ProfileChecksView`,setup(r){return(r,i)=>(n(),t(e))}});export{i as default};
@@ -1 +1 @@
import{A as e,Ct as t,Et as n,L as r,M as i,N as a,O as o,P as s,R as c,St as l,Y as u,Z as d,_ as f,j as p,mt as m,n as h,rt as g,t as _,z as v}from"./BasicIcon-C6boaehV.js";import{u as y}from"./dist-CUUvbY4x.js";import{a as b,c as x,d as S,n as C}from"./index-B6MlTGUp.js";import{t as w}from"./ProfileSubpageLayout-PN2YWH-s.js";var T={class:`settings-page`},E={class:`settings-links`,"aria-label":`حساب کاربری`},D={class:`settings-link__icon`,"aria-hidden":`true`},O={class:`settings-link__icon`,"aria-hidden":`true`},k={class:`settings-link__label`},A={key:0},j={class:`display-fieldset`},M={class:`display-grid`,role:`radiogroup`,"aria-label":`حالت نمایش`},N=[`aria-checked`,`onClick`],P={class:`theme-card__icon`,"aria-hidden":`true`},F={class:`theme-card__body`},I={class:`theme-card__label`},L={class:`theme-card__desc`},R={class:`theme-card__check`,"aria-hidden":`true`},z={key:0,class:`display-saved`},B=h(v({__name:`ProfileDisplayView`,setup(h){let v=f(),B=x(),V=S(),H=b(),U=m(!1),{start:W}=y(()=>{U.value=!1},2e3),G=[{value:`light`,label:`روشن`,description:`پس‌زمینه روشن و کنتراست بالا`,icon:`vuesax-linear:sun-1`},{value:`dark`,label:`تاریک`,description:`مناسب استفاده در شب`,icon:`vuesax-linear:moon`},{value:`system`,label:`سیستم`,description:`هم‌تراز با تنظیمات دستگاه`,icon:`vuesax-linear:mobile`}],K=e(()=>V.draftChecks.length);function q(){U.value=!0,W()}function J(){B.logout(),v.replace(`/welcome`)}return(e,f)=>(u(),i(w,{title:`تنظیمات`,subtitle:`نمایش، حساب کاربری و خروج`},{default:g(()=>[p(`div`,T,[p(`nav`,E,[p(`button`,{type:`button`,class:`settings-link`,onClick:f[0]||=e=>l(v).push(`/profile/edit`)},[p(`span`,D,[c(_,{icon:`vuesax-linear:profile-circle`,size:20,"stroke-width":1.5})]),f[2]||=p(`span`,{class:`settings-link__label`},`اطلاعات کاربری`,-1),c(_,{class:`settings-link__chevron`,icon:`vuesax-linear:arrow-left-1`,size:16,"stroke-width":1.5})]),p(`button`,{type:`button`,class:`settings-link`,onClick:f[1]||=e=>l(v).push(`/profile/checks`)},[p(`span`,O,[c(_,{icon:`vuesax-linear:card`,size:20,"stroke-width":1.5})]),p(`span`,k,[f[3]||=r(` لیست چک‌ها `,-1),K.value?(u(),s(`em`,A,`(`+n(K.value)+`)`,1)):a(``,!0)]),c(_,{class:`settings-link__chevron`,icon:`vuesax-linear:arrow-left-1`,size:16,"stroke-width":1.5})])]),p(`fieldset`,j,[f[4]||=p(`legend`,{class:`display-fieldset__legend`},`حالت نمایش`,-1),p(`div`,M,[(u(),s(o,null,d(G,e=>p(`button`,{key:e.value,type:`button`,class:t([`theme-card`,{"theme-card--active":l(H).preference===e.value}]),role:`radio`,"aria-checked":l(H).preference===e.value,onClick:t=>l(H).preference=e.value},[p(`span`,P,[c(_,{icon:e.icon,size:26,"stroke-width":1.5},null,8,[`icon`])]),p(`span`,F,[p(`strong`,I,n(e.label),1),p(`span`,L,n(e.description),1)]),p(`span`,R,[l(H).preference===e.value?(u(),i(_,{key:0,icon:`vuesax-linear:tick-circle`,size:20,"stroke-width":1.75})):a(``,!0)])],10,N)),64))])]),c(C,{block:``,onClick:q},{default:g(()=>[...f[5]||=[r(`ذخیره تنظیمات`,-1)]]),_:1}),U.value?(u(),s(`p`,z,`تنظیمات نمایش ذخیره شد ✓`)):a(``,!0),p(`button`,{type:`button`,class:`settings-logout`,onClick:J},`خروج`)])]),_:1}))}}),[[`__scopeId`,`data-v-f16dc950`]]);export{B as default};
import{A as e,Ct as t,Et as n,L as r,M as i,N as a,O as o,P as s,R as c,St as l,Y as u,Z as d,_ as f,j as p,mt as m,n as h,rt as g,t as _,z as v}from"./BasicIcon-C6boaehV.js";import{u as y}from"./dist-CUUvbY4x.js";import{a as b,c as x,d as S,n as C}from"./index-DzzUgmYT.js";import{t as w}from"./ProfileSubpageLayout-PN2YWH-s.js";var T={class:`settings-page`},E={class:`settings-links`,"aria-label":`حساب کاربری`},D={class:`settings-link__icon`,"aria-hidden":`true`},O={class:`settings-link__icon`,"aria-hidden":`true`},k={class:`settings-link__label`},A={key:0},j={class:`display-fieldset`},M={class:`display-grid`,role:`radiogroup`,"aria-label":`حالت نمایش`},N=[`aria-checked`,`onClick`],P={class:`theme-card__icon`,"aria-hidden":`true`},F={class:`theme-card__body`},I={class:`theme-card__label`},L={class:`theme-card__desc`},R={class:`theme-card__check`,"aria-hidden":`true`},z={key:0,class:`display-saved`},B=h(v({__name:`ProfileDisplayView`,setup(h){let v=f(),B=x(),V=S(),H=b(),U=m(!1),{start:W}=y(()=>{U.value=!1},2e3),G=[{value:`light`,label:`روشن`,description:`پس‌زمینه روشن و کنتراست بالا`,icon:`vuesax-linear:sun-1`},{value:`dark`,label:`تاریک`,description:`مناسب استفاده در شب`,icon:`vuesax-linear:moon`},{value:`system`,label:`سیستم`,description:`هم‌تراز با تنظیمات دستگاه`,icon:`vuesax-linear:mobile`}],K=e(()=>V.draftChecks.length);function q(){U.value=!0,W()}function J(){B.logout(),v.replace(`/welcome`)}return(e,f)=>(u(),i(w,{title:`تنظیمات`,subtitle:`نمایش، حساب کاربری و خروج`},{default:g(()=>[p(`div`,T,[p(`nav`,E,[p(`button`,{type:`button`,class:`settings-link`,onClick:f[0]||=e=>l(v).push(`/profile/edit`)},[p(`span`,D,[c(_,{icon:`vuesax-linear:profile-circle`,size:20,"stroke-width":1.5})]),f[2]||=p(`span`,{class:`settings-link__label`},`اطلاعات کاربری`,-1),c(_,{class:`settings-link__chevron`,icon:`vuesax-linear:arrow-left-1`,size:16,"stroke-width":1.5})]),p(`button`,{type:`button`,class:`settings-link`,onClick:f[1]||=e=>l(v).push(`/profile/checks`)},[p(`span`,O,[c(_,{icon:`vuesax-linear:card`,size:20,"stroke-width":1.5})]),p(`span`,k,[f[3]||=r(` لیست چک‌ها `,-1),K.value?(u(),s(`em`,A,`(`+n(K.value)+`)`,1)):a(``,!0)]),c(_,{class:`settings-link__chevron`,icon:`vuesax-linear:arrow-left-1`,size:16,"stroke-width":1.5})])]),p(`fieldset`,j,[f[4]||=p(`legend`,{class:`display-fieldset__legend`},`حالت نمایش`,-1),p(`div`,M,[(u(),s(o,null,d(G,e=>p(`button`,{key:e.value,type:`button`,class:t([`theme-card`,{"theme-card--active":l(H).preference===e.value}]),role:`radio`,"aria-checked":l(H).preference===e.value,onClick:t=>l(H).preference=e.value},[p(`span`,P,[c(_,{icon:e.icon,size:26,"stroke-width":1.5},null,8,[`icon`])]),p(`span`,F,[p(`strong`,I,n(e.label),1),p(`span`,L,n(e.description),1)]),p(`span`,R,[l(H).preference===e.value?(u(),i(_,{key:0,icon:`vuesax-linear:tick-circle`,size:20,"stroke-width":1.75})):a(``,!0)])],10,N)),64))])]),c(C,{block:``,onClick:q},{default:g(()=>[...f[5]||=[r(`ذخیره تنظیمات`,-1)]]),_:1}),U.value?(u(),s(`p`,z,`تنظیمات نمایش ذخیره شد ✓`)):a(``,!0),p(`button`,{type:`button`,class:`settings-logout`,onClick:J},`خروج`)])]),_:1}))}}),[[`__scopeId`,`data-v-f16dc950`]]);export{B as default};
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.notif-box[data-v-7e884b50]{flex-direction:column;gap:10px;display:flex}.notif-box__head[data-v-7e884b50]{justify-content:space-between;align-items:center;gap:12px;padding-bottom:4px;display:flex}.notif-box__count[data-v-7e884b50]{color:var(--color-text-muted);font-size:.8125rem}.notif-box__mark[data-v-7e884b50]{width:auto;padding-inline:14px}.notif-row[data-v-7e884b50]{text-align:right;border:1px solid var(--color-border);background:var(--color-surface);width:100%;color:var(--color-text);cursor:pointer;border-radius:14px;grid-template-columns:40px minmax(0,1fr);align-items:flex-start;gap:12px;padding:14px 16px;font-family:inherit;transition:border-color .15s;display:grid}.notif-row[data-v-7e884b50]:hover{border-color:var(--color-primary)}.notif-row--unread[data-v-7e884b50]{background:var(--color-tab-inactive-bg)}.notif-row__icon[data-v-7e884b50]{background:var(--color-info-light);width:40px;height:40px;color:var(--color-primary);border-radius:12px;flex-shrink:0;justify-content:center;align-items:center;display:flex}.notif-row--success .notif-row__icon[data-v-7e884b50]{background:var(--color-success-light);color:var(--color-success)}.notif-row--warning .notif-row__icon[data-v-7e884b50]{background:var(--color-warning-light);color:var(--color-warning)}.notif-row--danger .notif-row__icon[data-v-7e884b50]{background:var(--color-danger-light);color:var(--color-danger)}.notif-row__body[data-v-7e884b50]{flex-direction:column;gap:4px;min-width:0;display:flex}.notif-row__title-row[data-v-7e884b50]{align-items:center;gap:8px;display:flex}.notif-row__title[data-v-7e884b50]{font-size:.875rem;font-weight:700}.notif-row__text[data-v-7e884b50]{color:var(--color-text-muted);font-size:.8125rem;line-height:1.6}.notif-row__time[data-v-7e884b50]{color:var(--color-text-muted);font-size:.6875rem}.notif-row__dot[data-v-7e884b50]{background:var(--color-danger);border-radius:50%;flex-shrink:0;width:8px;height:8px}@media (width>=768px){.notif-box[data-v-7e884b50]{gap:12px}.notif-row[data-v-7e884b50]{grid-template-columns:44px minmax(0,1fr);gap:16px;padding:16px 18px}.notif-row__icon[data-v-7e884b50]{width:44px;height:44px}.notif-row__title[data-v-7e884b50]{font-size:.9375rem}.notif-row__text[data-v-7e884b50]{font-size:.875rem}.notif-row__time[data-v-7e884b50]{margin-top:2px}}
+1
View File
@@ -0,0 +1 @@
import{A as e,Ct as t,Et as n,L as r,M as i,N as a,O as o,P as s,R as c,St as l,Y as u,Z as d,_ as f,j as p,n as m,q as h,rt as g,t as _,z as v}from"./BasicIcon-C6boaehV.js";import{i as y}from"./format-BHJptHdF.js";import{n as b,u as x}from"./index-DzzUgmYT.js";import{t as S}from"./EmptyState-C_QQeA_h.js";import{t as C}from"./ProfileSubpageLayout-PN2YWH-s.js";var w={key:0,class:`notif-box`},T={class:`notif-box__head`},E={class:`notif-box__count`},D=[`onClick`],O={class:`notif-row__icon`,"aria-hidden":`true`},k={class:`notif-row__body`},A={class:`notif-row__title-row`},j={class:`notif-row__title`},M={key:0,class:`notif-row__dot`,"aria-label":`خوانده‌نشده`},N={class:`notif-row__text`},P={class:`notif-row__time`},F=m(v({__name:`ProfileNotificationsView`,setup(m){let v=f(),F=x(),I=e(()=>F.items),L=e(()=>F.hasUnread),R={info:`vuesax-linear:information`,success:`vuesax-linear:tick-circle`,warning:`vuesax-linear:timer`,danger:`vuesax-linear:close-circle`};h(()=>{F.hydrate()});async function z(e){await F.markRead(e.id),e.link&&v.push(e.link)}return(e,f)=>(u(),i(C,{title:`اعلان‌ها`,subtitle:`سفارش‌ها، پیشنهاد محصول و سایر اعلان‌ها`},{default:g(()=>[I.value.length?(u(),s(`div`,w,[p(`div`,T,[p(`span`,E,n(L.value?`${l(F).unreadCount} اعلان خوانده‌نشده`:`همه خوانده شده`),1),L.value?(u(),i(b,{key:0,variant:`outline`,class:`notif-box__mark`,onClick:f[0]||=e=>l(F).markAllRead()},{default:g(()=>[...f[2]||=[r(` خواندن همه `,-1)]]),_:1})):a(``,!0)]),(u(!0),s(o,null,d(I.value,e=>(u(),s(`button`,{key:e.id,type:`button`,class:t([`notif-row`,[`notif-row--${e.tone}`,{"notif-row--unread":!e.isRead}]]),onClick:t=>z(e)},[p(`span`,O,[c(_,{icon:R[e.tone],size:22,"stroke-width":1.5},null,8,[`icon`])]),p(`span`,k,[p(`span`,A,[p(`strong`,j,n(e.title),1),e.isRead?a(``,!0):(u(),s(`span`,M))]),p(`span`,N,n(e.body),1),p(`span`,P,n(l(y)(e.sentAt)),1)])],10,D))),128))])):(u(),i(S,{key:1,icon:`vuesax-linear:notification-bing`,title:`اعلانی ندارید`,message:`وقتی فاکتور سفارشتان صادر شود، پیشنهاد محصولی برایتان ثبت شود یا وضعیت پرداخت تغییر کند، همین‌جا خبردار می‌شوید.`},{default:g(()=>[c(b,{variant:`outline`,onClick:f[1]||=e=>l(v).push(`/orders`)},{default:g(()=>[...f[3]||=[r(`سفارش‌های من`,-1)]]),_:1})]),_:1}))]),_:1}))}}),[[`__scopeId`,`data-v-7e884b50`]]);export{F as default};
-1
View File
@@ -1 +0,0 @@
.notif-box[data-v-57190756]{flex-direction:column;gap:10px;display:flex}.notif-box__head[data-v-57190756]{justify-content:space-between;align-items:center;gap:12px;padding-bottom:4px;display:flex}.notif-box__count[data-v-57190756]{color:var(--color-text-muted);font-size:.8125rem}.notif-box__mark[data-v-57190756]{width:auto;padding-inline:14px}.notif-row[data-v-57190756]{text-align:right;border:1px solid var(--color-border);background:var(--color-surface);width:100%;color:var(--color-text);cursor:pointer;border-radius:14px;grid-template-columns:40px minmax(0,1fr);align-items:flex-start;gap:12px;padding:14px 16px;font-family:inherit;transition:border-color .15s;display:grid}.notif-row[data-v-57190756]:hover{border-color:var(--color-primary)}.notif-row--unread[data-v-57190756]{background:var(--color-elevated)}.notif-row__icon[data-v-57190756]{background:var(--color-info-light);width:40px;height:40px;color:var(--color-primary);border-radius:12px;flex-shrink:0;justify-content:center;align-items:center;display:flex}.notif-row--success .notif-row__icon[data-v-57190756]{background:var(--color-success-light);color:var(--color-success)}.notif-row--warning .notif-row__icon[data-v-57190756]{background:var(--color-warning-light);color:var(--color-warning)}.notif-row--danger .notif-row__icon[data-v-57190756]{background:var(--color-danger-light);color:var(--color-danger)}.notif-row__body[data-v-57190756]{flex-direction:column;gap:4px;min-width:0;display:flex}.notif-row__title-row[data-v-57190756]{align-items:center;gap:8px;display:flex}.notif-row__title[data-v-57190756]{font-size:.875rem;font-weight:700}.notif-row__text[data-v-57190756]{color:var(--color-text-muted);font-size:.8125rem;line-height:1.6}.notif-row__time[data-v-57190756]{color:var(--color-text-muted);font-size:.6875rem}.notif-row__dot[data-v-57190756]{background:var(--color-danger);border-radius:50%;flex-shrink:0;width:8px;height:8px}@media (width>=768px){.notif-box[data-v-57190756]{gap:12px}.notif-row[data-v-57190756]{grid-template-columns:44px minmax(0,1fr);gap:16px;padding:16px 18px}.notif-row__icon[data-v-57190756]{width:44px;height:44px}.notif-row__title[data-v-57190756]{font-size:.9375rem}.notif-row__text[data-v-57190756]{font-size:.875rem}.notif-row__time[data-v-57190756]{margin-top:2px}}
-1
View File
@@ -1 +0,0 @@
import{A as e,Ct as t,Et as n,L as r,M as i,N as a,O as o,P as s,R as c,St as l,Y as u,Z as d,_ as f,j as p,n as m,q as h,rt as g,t as _,z as v}from"./BasicIcon-C6boaehV.js";import{i as y}from"./format-BHJptHdF.js";import{n as b,u as x}from"./index-B6MlTGUp.js";import{t as S}from"./EmptyState-C_QQeA_h.js";import{t as C}from"./ProfileSubpageLayout-PN2YWH-s.js";var w={key:0,class:`notif-box`},T={class:`notif-box__head`},E={class:`notif-box__count`},D=[`onClick`],O={class:`notif-row__icon`,"aria-hidden":`true`},k={class:`notif-row__body`},A={class:`notif-row__title-row`},j={class:`notif-row__title`},M={key:0,class:`notif-row__dot`,"aria-label":`خوانده‌نشده`},N={class:`notif-row__text`},P={class:`notif-row__time`},F=m(v({__name:`ProfileNotificationsView`,setup(m){let v=f(),F=x(),I=e(()=>F.items),L=e(()=>F.hasUnread),R={info:`vuesax-linear:information`,success:`vuesax-linear:tick-circle`,warning:`vuesax-linear:timer`,danger:`vuesax-linear:close-circle`};h(()=>{F.hydrate()});async function z(e){await F.markRead(e.id),e.link&&v.push(e.link)}return(e,f)=>(u(),i(C,{title:`اعلان‌ها`,subtitle:`آخرین خبرهای سفارش‌های شما`},{default:g(()=>[I.value.length?(u(),s(`div`,w,[p(`div`,T,[p(`span`,E,n(L.value?`${l(F).unreadCount} اعلان خوانده‌نشده`:`همه خوانده شده`),1),L.value?(u(),i(b,{key:0,variant:`outline`,class:`notif-box__mark`,onClick:f[0]||=e=>l(F).markAllRead()},{default:g(()=>[...f[2]||=[r(` خواندن همه `,-1)]]),_:1})):a(``,!0)]),(u(!0),s(o,null,d(I.value,e=>(u(),s(`button`,{key:e.id,type:`button`,class:t([`notif-row`,[`notif-row--${e.tone}`,{"notif-row--unread":!e.isRead}]]),onClick:t=>z(e)},[p(`span`,O,[c(_,{icon:R[e.tone],size:22,"stroke-width":1.5},null,8,[`icon`])]),p(`span`,k,[p(`span`,A,[p(`strong`,j,n(e.title),1),e.isRead?a(``,!0):(u(),s(`span`,M))]),p(`span`,N,n(e.body),1),p(`span`,P,n(l(y)(e.sentAt)),1)])],10,D))),128))])):(u(),i(S,{key:1,icon:`vuesax-linear:notification-bing`,title:`اعلانی ندارید`,message:`وقتی فاکتور سفارشتان صادر شود یا وضعیت چک و پرداخت تغییر کند، همین‌جا خبردار می‌شوید.`},{default:g(()=>[c(b,{variant:`outline`,onClick:f[1]||=e=>l(v).push(`/orders`)},{default:g(()=>[...f[3]||=[r(`سفارش‌های من`,-1)]]),_:1})]),_:1}))]),_:1}))}}),[[`__scopeId`,`data-v-57190756`]]);export{F as default};
@@ -1 +1 @@
import{L as e,M as t,N as n,P as r,R as i,Y as a,j as o,mt as s,n as c,rt as l,z as u}from"./BasicIcon-C6boaehV.js";import{n as d}from"./index-B6MlTGUp.js";import{t as f}from"./FormErrorBanner-CjdBfIQT.js";import{n as p,r as m,t as h}from"./formErrors-DglFjYkZ.js";import{t as g}from"./BaseInput-qoEXOHCQ.js";import{t as _}from"./ProfileSubpageLayout-PN2YWH-s.js";var v={class:`settings-form`},y={key:0,class:`settings-form__saved`},b=c(u({__name:`ProfileSecurityView`,setup(c){let u=s(``),b=s(``),x=s(``),S=s({}),C=s(``),w=s(!1);function T(e){S.value=p(S.value,e),m(S.value)||(C.value=``)}function E(){S.value={},C.value=``,w.value=!1;let e={};if(u.value||(e.currentPassword=`رمز فعلی الزامی است.`),b.value.length<6&&(e.newPassword=`رمز جدید باید حداقل ۶ کاراکتر باشد.`),x.value!==b.value&&(e.confirmPassword=`تکرار رمز با رمز جدید یکسان نیست.`),m(e)){S.value=e,C.value=h;return}u.value=``,b.value=``,x.value=``,w.value=!0,setTimeout(()=>{w.value=!1},2e3)}return(s,c)=>(a(),t(_,{title:`امنیت حساب`,subtitle:`تغییر رمز عبور و تنظیمات ورود`},{default:l(()=>[i(f,{message:C.value},null,8,[`message`]),o(`div`,v,[i(g,{modelValue:u.value,"onUpdate:modelValue":[c[0]||=e=>u.value=e,c[1]||=e=>T(`currentPassword`)],type:`password`,label:`رمز عبور فعلی`,error:S.value.currentPassword},null,8,[`modelValue`,`error`]),i(g,{modelValue:b.value,"onUpdate:modelValue":[c[2]||=e=>b.value=e,c[3]||=e=>T(`newPassword`)],type:`password`,label:`رمز عبور جدید`,error:S.value.newPassword},null,8,[`modelValue`,`error`]),i(g,{modelValue:x.value,"onUpdate:modelValue":[c[4]||=e=>x.value=e,c[5]||=e=>T(`confirmPassword`)],type:`password`,label:`تکرار رمز عبور جدید`,error:S.value.confirmPassword},null,8,[`modelValue`,`error`]),i(d,{block:``,onClick:E},{default:l(()=>[...c[6]||=[e(`ذخیره رمز جدید`,-1)]]),_:1}),w.value?(a(),r(`p`,y,`رمز عبور با موفقیت تغییر کرد ✓`)):n(``,!0)])]),_:1}))}}),[[`__scopeId`,`data-v-074827a2`]]);export{b as default};
import{L as e,M as t,N as n,P as r,R as i,Y as a,j as o,mt as s,n as c,rt as l,z as u}from"./BasicIcon-C6boaehV.js";import{n as d}from"./index-DzzUgmYT.js";import{t as f}from"./FormErrorBanner-CjdBfIQT.js";import{n as p,r as m,t as h}from"./formErrors-DglFjYkZ.js";import{t as g}from"./BaseInput-qoEXOHCQ.js";import{t as _}from"./ProfileSubpageLayout-PN2YWH-s.js";var v={class:`settings-form`},y={key:0,class:`settings-form__saved`},b=c(u({__name:`ProfileSecurityView`,setup(c){let u=s(``),b=s(``),x=s(``),S=s({}),C=s(``),w=s(!1);function T(e){S.value=p(S.value,e),m(S.value)||(C.value=``)}function E(){S.value={},C.value=``,w.value=!1;let e={};if(u.value||(e.currentPassword=`رمز فعلی الزامی است.`),b.value.length<6&&(e.newPassword=`رمز جدید باید حداقل ۶ کاراکتر باشد.`),x.value!==b.value&&(e.confirmPassword=`تکرار رمز با رمز جدید یکسان نیست.`),m(e)){S.value=e,C.value=h;return}u.value=``,b.value=``,x.value=``,w.value=!0,setTimeout(()=>{w.value=!1},2e3)}return(s,c)=>(a(),t(_,{title:`امنیت حساب`,subtitle:`تغییر رمز عبور و تنظیمات ورود`},{default:l(()=>[i(f,{message:C.value},null,8,[`message`]),o(`div`,v,[i(g,{modelValue:u.value,"onUpdate:modelValue":[c[0]||=e=>u.value=e,c[1]||=e=>T(`currentPassword`)],type:`password`,label:`رمز عبور فعلی`,error:S.value.currentPassword},null,8,[`modelValue`,`error`]),i(g,{modelValue:b.value,"onUpdate:modelValue":[c[2]||=e=>b.value=e,c[3]||=e=>T(`newPassword`)],type:`password`,label:`رمز عبور جدید`,error:S.value.newPassword},null,8,[`modelValue`,`error`]),i(g,{modelValue:x.value,"onUpdate:modelValue":[c[4]||=e=>x.value=e,c[5]||=e=>T(`confirmPassword`)],type:`password`,label:`تکرار رمز عبور جدید`,error:S.value.confirmPassword},null,8,[`modelValue`,`error`]),i(d,{block:``,onClick:E},{default:l(()=>[...c[6]||=[e(`ذخیره رمز جدید`,-1)]]),_:1}),w.value?(a(),r(`p`,y,`رمز عبور با موفقیت تغییر کرد ✓`)):n(``,!0)])]),_:1}))}}),[[`__scopeId`,`data-v-074827a2`]]);export{b as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.reminder-detail[data-v-59c58cf4]{background:var(--color-bg)}.detail-scroll[data-v-59c58cf4]{padding:12px var(--page-padding) 24px}.form-error[data-v-59c58cf4]{color:var(--color-danger,#b91c1c);margin:0 0 12px;font-size:.85rem}.layout[data-v-59c58cf4]{flex-direction:column;gap:12px;display:flex}@media (width>=1024px){.layout[data-v-59c58cf4]{grid-template-columns:1.1fr 1fr 1fr;align-items:start;display:grid}}.panel[data-v-59c58cf4]{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px}.panel h2[data-v-59c58cf4]{margin:0 0 14px;font-size:1rem}.field[data-v-59c58cf4]{margin-bottom:14px}.field>span[data-v-59c58cf4]{color:var(--color-text-muted);margin-bottom:4px;font-size:.78rem;display:block}.customer-line[data-v-59c58cf4]{align-items:center;gap:10px;display:flex}.avatar[data-v-59c58cf4]{border-radius:999px;place-items:center;width:40px;height:40px;font-weight:700;display:grid}.customer-line p[data-v-59c58cf4]{color:var(--color-text-muted);margin:2px 0 0;font-size:.8rem}.actions[data-v-59c58cf4]{grid-template-columns:1fr 1fr;gap:8px;margin-top:8px;display:grid}.btn-outline[data-v-59c58cf4],.btn-primary[data-v-59c58cf4]{cursor:pointer;border-radius:12px;min-height:44px;font-weight:700}.btn-outline[data-v-59c58cf4]{border:1px solid var(--color-border);background:var(--color-surface)}.btn-primary[data-v-59c58cf4]{background:var(--color-primary);color:#fff;border:none}.btn-primary[data-v-59c58cf4]:disabled{opacity:.7;cursor:not-allowed}.history-row[data-v-59c58cf4]{border-bottom:1px solid var(--color-border);justify-content:space-between;gap:10px;padding:10px 0;display:flex}.history-row p[data-v-59c58cf4]{color:var(--color-text-muted);margin:2px 0 0;font-size:.78rem}.see-all[data-v-59c58cf4]{cursor:pointer;background:#eef1ef;border:none;border-radius:12px;width:100%;min-height:44px;margin-top:12px;font-weight:600}.chart-panel__head[data-v-59c58cf4]{justify-content:space-between;align-items:center;display:flex}.chart-panel__head span[data-v-59c58cf4]{color:var(--color-text-muted);font-size:.8rem}.bars[data-v-59c58cf4]{grid-template-columns:repeat(6,1fr);align-items:end;gap:8px;height:140px;margin:16px 0;display:grid}.bar[data-v-59c58cf4]{flex-direction:column-reverse;gap:2px;height:100%;display:flex}.seg[data-v-59c58cf4]{border-radius:4px 4px 0 0;width:100%;display:block}.seg--a[data-v-59c58cf4]{background:var(--color-primary)}.seg--b[data-v-59c58cf4]{background:#3b82f6}.seg--c[data-v-59c58cf4]{background:#a78bfa}.stats-grid[data-v-59c58cf4]{grid-template-columns:1fr 1fr;gap:8px;display:grid}.stats-grid div[data-v-59c58cf4]{background:var(--color-bg);border-radius:12px;padding:10px}.stats-grid span[data-v-59c58cf4]{color:var(--color-text-muted);font-size:.72rem;display:block}.stats-grid strong[data-v-59c58cf4]{margin-top:4px;display:block}.empty[data-v-59c58cf4]{text-align:center;padding:40px}
-1
View File
@@ -1 +0,0 @@
.reminder-detail[data-v-9fc8e0f1]{background:var(--color-bg)}.detail-scroll[data-v-9fc8e0f1]{padding:12px var(--page-padding) 24px}.form-error[data-v-9fc8e0f1]{color:var(--color-danger,#b91c1c);margin:0 0 12px;font-size:.85rem}.layout[data-v-9fc8e0f1]{flex-direction:column;gap:12px;display:flex}@media (width>=1024px){.layout[data-v-9fc8e0f1]{grid-template-columns:1.1fr 1fr 1fr;align-items:start;display:grid}}.panel[data-v-9fc8e0f1]{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:16px}.panel h2[data-v-9fc8e0f1]{margin:0 0 14px;font-size:1rem}.field[data-v-9fc8e0f1]{margin-bottom:14px}.field>span[data-v-9fc8e0f1]{color:var(--color-text-muted);margin-bottom:4px;font-size:.78rem;display:block}.customer-line[data-v-9fc8e0f1]{align-items:center;gap:10px;display:flex}.avatar[data-v-9fc8e0f1]{border-radius:999px;place-items:center;width:40px;height:40px;font-weight:700;display:grid}.customer-line p[data-v-9fc8e0f1]{color:var(--color-text-muted);margin:2px 0 0;font-size:.8rem}.actions[data-v-9fc8e0f1]{grid-template-columns:1fr 1fr;gap:8px;margin-top:8px;display:grid}.btn-outline[data-v-9fc8e0f1],.btn-primary[data-v-9fc8e0f1]{cursor:pointer;border-radius:12px;min-height:44px;font-weight:700}.btn-outline[data-v-9fc8e0f1]{border:1px solid var(--color-border);background:#fff}.btn-primary[data-v-9fc8e0f1]{background:var(--color-primary);color:#fff;border:none}.btn-primary[data-v-9fc8e0f1]:disabled{opacity:.7;cursor:not-allowed}.history-row[data-v-9fc8e0f1]{border-bottom:1px solid var(--color-border);justify-content:space-between;gap:10px;padding:10px 0;display:flex}.history-row p[data-v-9fc8e0f1]{color:var(--color-text-muted);margin:2px 0 0;font-size:.78rem}.see-all[data-v-9fc8e0f1]{cursor:pointer;background:#eef1ef;border:none;border-radius:12px;width:100%;min-height:44px;margin-top:12px;font-weight:600}.chart-panel__head[data-v-9fc8e0f1]{justify-content:space-between;align-items:center;display:flex}.chart-panel__head span[data-v-9fc8e0f1]{color:var(--color-text-muted);font-size:.8rem}.bars[data-v-9fc8e0f1]{grid-template-columns:repeat(6,1fr);align-items:end;gap:8px;height:140px;margin:16px 0;display:grid}.bar[data-v-9fc8e0f1]{flex-direction:column-reverse;gap:2px;height:100%;display:flex}.seg[data-v-9fc8e0f1]{border-radius:4px 4px 0 0;width:100%;display:block}.seg--a[data-v-9fc8e0f1]{background:var(--color-primary)}.seg--b[data-v-9fc8e0f1]{background:#3b82f6}.seg--c[data-v-9fc8e0f1]{background:#a78bfa}.stats-grid[data-v-9fc8e0f1]{grid-template-columns:1fr 1fr;gap:8px;display:grid}.stats-grid div[data-v-9fc8e0f1]{background:var(--color-bg);border-radius:12px;padding:10px}.stats-grid span[data-v-9fc8e0f1]{color:var(--color-text-muted);font-size:.72rem;display:block}.stats-grid strong[data-v-9fc8e0f1]{margin-top:4px;display:block}.empty[data-v-9fc8e0f1]{text-align:center;padding:40px}
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.reminders-page[data-v-5f336dcc]{background:var(--color-bg)}.top[data-v-5f336dcc]{padding:10px var(--page-padding);background:var(--color-surface);color:var(--color-text);grid-template-columns:40px 1fr auto;align-items:center;display:grid}.top h1[data-v-5f336dcc]{text-align:center;margin:0;font-size:1rem}.top__actions[data-v-5f336dcc]{align-items:center;display:flex}.icon-btn[data-v-5f336dcc]{cursor:pointer;width:40px;height:40px;color:var(--color-text);background:0 0;border:none;place-items:center;font-size:1.4rem;display:grid}.reminders-scroll[data-v-5f336dcc]{padding:12px var(--page-padding) 24px;flex-direction:column;gap:12px;display:flex}.filters[data-v-5f336dcc]{gap:12px;display:flex;overflow-x:auto}.filter[data-v-5f336dcc]{color:var(--color-text-muted);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:8px 2px;position:relative}.filter--active[data-v-5f336dcc]{color:var(--color-primary);font-weight:700}.filter--active[data-v-5f336dcc]:after{content:"";inset-inline:0;background:var(--color-primary);border-radius:999px;height:3px;position:absolute;bottom:0}.count-badge[data-v-5f336dcc]{background:var(--color-danger);color:#fff;border-radius:999px;place-items:center;min-width:18px;height:18px;margin-inline-start:4px;font-size:.65rem;display:inline-grid}.summary[data-v-5f336dcc]{background:var(--color-primary);color:#fff;border-radius:var(--radius-lg);flex-direction:column;gap:6px;padding:16px;display:flex}.summary__label[data-v-5f336dcc]{opacity:.92;font-size:.85rem}.summary__count[data-v-5f336dcc]{align-items:baseline;gap:6px;display:flex}.summary__count strong[data-v-5f336dcc]{font-size:2rem;line-height:1}.summary__count span[data-v-5f336dcc]{font-size:.95rem;font-weight:600}.summary__sub[data-v-5f336dcc]{opacity:.88;margin:4px 0 0;font-size:.78rem}.reminder-card[data-v-5f336dcc]{background:var(--color-surface);border-radius:var(--radius-md);box-shadow:var(--shadow-sm);cursor:pointer;grid-template-rows:auto 1fr;grid-template-columns:24px 1fr 40px;gap:4px 10px;padding:12px;display:grid}.time[data-v-5f336dcc]{color:var(--color-text-muted);grid-area:1/1/auto/3;font-size:.8rem;font-weight:700}.check[data-v-5f336dcc]{cursor:pointer;background:0 0;border:1.5px solid #cbd5e1;border-radius:999px;grid-area:2/1;align-self:start;width:22px;height:22px;margin-top:4px}.reminder-card__body[data-v-5f336dcc]{grid-area:2/2}.avatar[data-v-5f336dcc]{border-radius:999px;grid-area:1/3/3;place-self:start end;place-items:center;width:40px;height:40px;font-size:.85rem;font-weight:700;display:grid}.reminder-card__top[data-v-5f336dcc]{justify-content:space-between;gap:8px;display:flex}.customer[data-v-5f336dcc]{margin:6px 0 0;font-size:.85rem;font-weight:600}.contact[data-v-5f336dcc],.desc[data-v-5f336dcc]{color:var(--color-text-muted);margin:2px 0 0;font-size:.75rem}.tag[data-v-5f336dcc]{border-radius:999px;margin-top:6px;padding:2px 8px;font-size:.65rem;display:inline-block}.tag--green[data-v-5f336dcc]{background:var(--color-primary-soft);color:var(--color-primary)}.tag--blue[data-v-5f336dcc]{color:#0369a1;background:#e0f2fe}.tag--purple[data-v-5f336dcc]{color:#6d28d9;background:#ede9fe}.completed-bar[data-v-5f336dcc]{border-radius:var(--radius-md);cursor:pointer;background:#ecefee;border:none;justify-content:space-between;padding:14px;font-weight:600;display:flex}.new-btn[data-v-5f336dcc]{background:var(--color-primary);color:#fff;cursor:pointer;border:none;border-radius:14px;justify-content:center;align-items:center;gap:8px;width:100%;min-height:52px;font-weight:700;display:flex}.create-panel[data-v-5f336dcc]{background:var(--color-surface);border-radius:var(--radius-md);box-shadow:var(--shadow-sm);flex-direction:column;gap:10px;padding:14px;display:flex}.create-panel h3[data-v-5f336dcc]{margin:0;font-size:.95rem}.create-panel label[data-v-5f336dcc]{color:var(--color-text-muted);flex-direction:column;gap:4px;font-size:.8rem;display:flex}.create-panel input[data-v-5f336dcc],.create-panel select[data-v-5f336dcc],.create-panel textarea[data-v-5f336dcc]{border:1px solid var(--color-border);font:inherit;color:var(--color-text);background:var(--color-surface);border-radius:10px;padding:10px 12px}.create-row[data-v-5f336dcc]{grid-template-columns:1fr 1fr;gap:8px;display:grid}.create-actions[data-v-5f336dcc]{grid-template-columns:1fr 1.2fr;gap:8px;display:grid}.btn-cancel[data-v-5f336dcc],.btn-save[data-v-5f336dcc]{cursor:pointer;border-radius:12px;min-height:44px;font-weight:700}.btn-cancel[data-v-5f336dcc]{border:1px solid var(--color-border);background:var(--color-surface)}.btn-save[data-v-5f336dcc]{background:var(--color-primary);color:#fff;border:none}.btn-save[data-v-5f336dcc]:disabled{opacity:.7}.create-error[data-v-5f336dcc]{color:#b42318;margin:0;font-size:.8rem}
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
.reminders-page[data-v-89857cb4]{background:var(--color-bg)}.top[data-v-89857cb4]{padding:10px var(--page-padding);background:var(--color-surface);color:var(--color-text);grid-template-columns:40px 1fr auto;align-items:center;display:grid}.top h1[data-v-89857cb4]{text-align:center;margin:0;font-size:1rem}.top__actions[data-v-89857cb4]{align-items:center;display:flex}.icon-btn[data-v-89857cb4]{cursor:pointer;width:40px;height:40px;color:var(--color-text);background:0 0;border:none;place-items:center;font-size:1.4rem;display:grid}.reminders-scroll[data-v-89857cb4]{padding:12px var(--page-padding) 24px;flex-direction:column;gap:12px;display:flex}.filters[data-v-89857cb4]{gap:12px;display:flex;overflow-x:auto}.filter[data-v-89857cb4]{color:var(--color-text-muted);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:8px 2px;position:relative}.filter--active[data-v-89857cb4]{color:var(--color-primary);font-weight:700}.filter--active[data-v-89857cb4]:after{content:"";inset-inline:0;background:var(--color-primary);border-radius:999px;height:3px;position:absolute;bottom:0}.count-badge[data-v-89857cb4]{background:var(--color-danger);color:#fff;border-radius:999px;place-items:center;min-width:18px;height:18px;margin-inline-start:4px;font-size:.65rem;display:inline-grid}.summary[data-v-89857cb4]{background:var(--color-primary);color:#fff;border-radius:var(--radius-lg);flex-direction:column;gap:6px;padding:16px;display:flex}.summary__label[data-v-89857cb4]{opacity:.92;font-size:.85rem}.summary__count[data-v-89857cb4]{align-items:baseline;gap:6px;display:flex}.summary__count strong[data-v-89857cb4]{font-size:2rem;line-height:1}.summary__count span[data-v-89857cb4]{font-size:.95rem;font-weight:600}.summary__sub[data-v-89857cb4]{opacity:.88;margin:4px 0 0;font-size:.78rem}.reminder-card[data-v-89857cb4]{background:var(--color-surface);border-radius:var(--radius-md);box-shadow:var(--shadow-sm);cursor:pointer;grid-template-rows:auto 1fr;grid-template-columns:24px 1fr 40px;gap:4px 10px;padding:12px;display:grid}.time[data-v-89857cb4]{color:var(--color-text-muted);grid-area:1/1/auto/3;font-size:.8rem;font-weight:700}.check[data-v-89857cb4]{cursor:pointer;background:0 0;border:1.5px solid #cbd5e1;border-radius:999px;grid-area:2/1;align-self:start;width:22px;height:22px;margin-top:4px}.reminder-card__body[data-v-89857cb4]{grid-area:2/2}.avatar[data-v-89857cb4]{border-radius:999px;grid-area:1/3/3;place-self:start end;place-items:center;width:40px;height:40px;font-size:.85rem;font-weight:700;display:grid}.reminder-card__top[data-v-89857cb4]{justify-content:space-between;gap:8px;display:flex}.customer[data-v-89857cb4]{margin:6px 0 0;font-size:.85rem;font-weight:600}.contact[data-v-89857cb4],.desc[data-v-89857cb4]{color:var(--color-text-muted);margin:2px 0 0;font-size:.75rem}.tag[data-v-89857cb4]{border-radius:999px;margin-top:6px;padding:2px 8px;font-size:.65rem;display:inline-block}.tag--green[data-v-89857cb4]{background:var(--color-primary-soft);color:var(--color-primary)}.tag--blue[data-v-89857cb4]{color:#0369a1;background:#e0f2fe}.tag--purple[data-v-89857cb4]{color:#6d28d9;background:#ede9fe}.completed-bar[data-v-89857cb4]{border-radius:var(--radius-md);cursor:pointer;background:#ecefee;border:none;justify-content:space-between;padding:14px;font-weight:600;display:flex}.new-btn[data-v-89857cb4]{background:var(--color-primary);color:#fff;cursor:pointer;border:none;border-radius:14px;justify-content:center;align-items:center;gap:8px;width:100%;min-height:52px;font-weight:700;display:flex}.create-panel[data-v-89857cb4]{background:var(--color-surface);border-radius:var(--radius-md);box-shadow:var(--shadow-sm);flex-direction:column;gap:10px;padding:14px;display:flex}.create-panel h3[data-v-89857cb4]{margin:0;font-size:.95rem}.create-panel label[data-v-89857cb4]{color:var(--color-text-muted);flex-direction:column;gap:4px;font-size:.8rem;display:flex}.create-panel input[data-v-89857cb4],.create-panel select[data-v-89857cb4],.create-panel textarea[data-v-89857cb4]{border:1px solid var(--color-border);font:inherit;color:var(--color-text);background:#fff;border-radius:10px;padding:10px 12px}.create-row[data-v-89857cb4]{grid-template-columns:1fr 1fr;gap:8px;display:grid}.create-actions[data-v-89857cb4]{grid-template-columns:1fr 1.2fr;gap:8px;display:grid}.btn-cancel[data-v-89857cb4],.btn-save[data-v-89857cb4]{cursor:pointer;border-radius:12px;min-height:44px;font-weight:700}.btn-cancel[data-v-89857cb4]{border:1px solid var(--color-border);background:#fff}.btn-save[data-v-89857cb4]{background:var(--color-primary);color:#fff;border:none}.btn-save[data-v-89857cb4]:disabled{opacity:.7}.create-error[data-v-89857cb4]{color:#b42318;margin:0;font-size:.8rem}
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{Et as e,M as t,O as n,P as r,R as i,St as a,Y as o,Z as s,_ as c,j as l,n as u,t as d,z as f}from"./BasicIcon-C6boaehV.js";import{_ as p,o as m}from"./index-B6MlTGUp.js";import{t as h}from"./login-fabric-CzrIXTnD.js";var g={class:`page welcome-page`},_=[`src`],v={class:`welcome-page__scroll`},y={class:`welcome-page__stage`},b={class:`welcome-brand`},x={class:`welcome-features`},S={class:`welcome-feature__icon`,"aria-hidden":`true`},C={key:0,class:`welcome-feature__star`,viewBox:`0 0 24 24`,fill:`none`},w={class:`welcome-cta__chevron`,"aria-hidden":`true`},T=u(f({__name:`WelcomeView`,setup(u){let f=c(),T=[{icon:`star`,title:`همراه همیشگی کسب‌وکارتان`,subtitle:`با خدمات ویژه مشتریان`},{icon:`vuesax-linear:truck-time`,title:`سفارش عمده`,subtitle:`مستقیم از تولیدکننده`},{icon:`vuesax-linear:element-3`,title:`تنوع بی‌نظیر محصولات`,subtitle:`از کلاسیک تا مدرن`}];function E(){let e=typeof f.currentRoute.value.query.redirect==`string`?f.currentRoute.value.query.redirect:void 0;f.push({name:`login`,query:e?{redirect:e}:void 0})}return(c,u)=>(o(),r(`div`,g,[l(`img`,{class:`welcome-page__bg`,src:a(h),alt:``},null,8,_),u[7]||=l(`div`,{class:`welcome-page__veil`,"aria-hidden":`true`},null,-1),l(`div`,v,[l(`div`,y,[l(`header`,{class:`welcome-top`},[l(`button`,{type:`button`,class:`welcome-skip`,onClick:E},`رد شدن`)]),l(`div`,b,[i(p,{size:88}),u[0]||=l(`h1`,{class:`welcome-brand__fa`},`نادمی تکس`,-1),u[1]||=l(`p`,{class:`welcome-brand__tag`},`انتخاب حرفه‌ای، شروع یک همکاری ماندگار`,-1),u[2]||=l(`span`,{class:`welcome-brand__rule`,"aria-hidden":`true`},null,-1)]),l(`ul`,x,[(o(),r(n,null,s(T,n=>l(`li`,{key:n.title,class:`welcome-feature`},[l(`span`,S,[n.icon===`star`?(o(),r(`svg`,C,[...u[3]||=[l(`path`,{d:`M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z`,stroke:`#fff`,"stroke-width":`1.5`,"stroke-linejoin":`round`},null,-1)]])):(o(),t(d,{key:1,icon:n.icon,size:22,color:`#ffffff`},null,8,[`icon`]))]),l(`strong`,null,e(n.title),1),l(`span`,null,e(n.subtitle),1)])),64))]),u[5]||=l(`div`,{class:`welcome-spacer`,"aria-hidden":`true`},null,-1),u[6]||=l(`div`,{class:`welcome-dots`,"aria-hidden":`true`},[l(`i`,{class:`is-on`}),l(`i`),l(`i`),l(`i`)],-1),l(`button`,{type:`button`,class:`welcome-cta`,onClick:E},[u[4]||=l(`span`,null,`ورود / ثبت‌نام`,-1),l(`span`,w,[i(m,{size:18,color:`#ffffff`})])])])])]))}}),[[`__scopeId`,`data-v-66f13882`]]);export{T as default};
import{Et as e,M as t,O as n,P as r,R as i,St as a,Y as o,Z as s,_ as c,j as l,n as u,t as d,z as f}from"./BasicIcon-C6boaehV.js";import{_ as p,o as m}from"./index-DzzUgmYT.js";import{t as h}from"./login-fabric-CzrIXTnD.js";var g={class:`page welcome-page`},_=[`src`],v={class:`welcome-page__scroll`},y={class:`welcome-page__stage`},b={class:`welcome-brand`},x={class:`welcome-features`},S={class:`welcome-feature__icon`,"aria-hidden":`true`},C={key:0,class:`welcome-feature__star`,viewBox:`0 0 24 24`,fill:`none`},w={class:`welcome-cta__chevron`,"aria-hidden":`true`},T=u(f({__name:`WelcomeView`,setup(u){let f=c(),T=[{icon:`star`,title:`همراه همیشگی کسب‌وکارتان`,subtitle:`با خدمات ویژه مشتریان`},{icon:`vuesax-linear:truck-time`,title:`سفارش عمده`,subtitle:`مستقیم از تولیدکننده`},{icon:`vuesax-linear:element-3`,title:`تنوع بی‌نظیر محصولات`,subtitle:`از کلاسیک تا مدرن`}];function E(){let e=typeof f.currentRoute.value.query.redirect==`string`?f.currentRoute.value.query.redirect:void 0;f.push({name:`login`,query:e?{redirect:e}:void 0})}return(c,u)=>(o(),r(`div`,g,[l(`img`,{class:`welcome-page__bg`,src:a(h),alt:``},null,8,_),u[7]||=l(`div`,{class:`welcome-page__veil`,"aria-hidden":`true`},null,-1),l(`div`,v,[l(`div`,y,[l(`header`,{class:`welcome-top`},[l(`button`,{type:`button`,class:`welcome-skip`,onClick:E},`رد شدن`)]),l(`div`,b,[i(p,{size:88}),u[0]||=l(`h1`,{class:`welcome-brand__fa`},`نادمی تکس`,-1),u[1]||=l(`p`,{class:`welcome-brand__tag`},`انتخاب حرفه‌ای، شروع یک همکاری ماندگار`,-1),u[2]||=l(`span`,{class:`welcome-brand__rule`,"aria-hidden":`true`},null,-1)]),l(`ul`,x,[(o(),r(n,null,s(T,n=>l(`li`,{key:n.title,class:`welcome-feature`},[l(`span`,S,[n.icon===`star`?(o(),r(`svg`,C,[...u[3]||=[l(`path`,{d:`M11.525 2.295a.53.53 0 0 1 .95 0l2.31 4.679a2.123 2.123 0 0 0 1.595 1.16l5.166.756a.53.53 0 0 1 .294.904l-3.736 3.638a2.123 2.123 0 0 0-.611 1.878l.882 5.14a.53.53 0 0 1-.771.56l-4.618-2.428a2.122 2.122 0 0 0-1.973 0L6.396 21.01a.53.53 0 0 1-.77-.56l.881-5.139a2.122 2.122 0 0 0-.611-1.879L2.16 9.795a.53.53 0 0 1 .294-.906l5.165-.755a2.122 2.122 0 0 0 1.597-1.16z`,stroke:`#fff`,"stroke-width":`1.5`,"stroke-linejoin":`round`},null,-1)]])):(o(),t(d,{key:1,icon:n.icon,size:22,color:`#ffffff`},null,8,[`icon`]))]),l(`strong`,null,e(n.title),1),l(`span`,null,e(n.subtitle),1)])),64))]),u[5]||=l(`div`,{class:`welcome-spacer`,"aria-hidden":`true`},null,-1),u[6]||=l(`div`,{class:`welcome-dots`,"aria-hidden":`true`},[l(`i`,{class:`is-on`}),l(`i`),l(`i`),l(`i`)],-1),l(`button`,{type:`button`,class:`welcome-cta`,onClick:E},[u[4]||=l(`span`,null,`ورود / ثبت‌نام`,-1),l(`span`,w,[i(m,{size:18,color:`#ffffff`})])])])])]))}}),[[`__scopeId`,`data-v-66f13882`]]);export{T as default};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1 +1 @@
import{A as e,_ as t,g as n,tt as r}from"./BasicIcon-C6boaehV.js";import{d as i}from"./index-B6MlTGUp.js";import{n as a}from"./orderInvoice-B0L1Aeuk.js";function o(){let o=n(),s=t(),c=i(),l=e(()=>o.params.id),u=e(()=>c.getOrderById(l.value)),d=e(()=>a(u.value));function f(e){e&&(a(e)||s.replace(`/orders/${e.id}`))}return r(l,async e=>{e&&f(c.getOrderById(e)??await c.fetchOrder(e))},{immediate:!0}),r(u,e=>{f(e)}),{orderId:l,order:u,invoiceReady:d}}export{o as t};
import{A as e,_ as t,g as n,tt as r}from"./BasicIcon-C6boaehV.js";import{d as i}from"./index-DzzUgmYT.js";import{n as a}from"./orderInvoice-B0L1Aeuk.js";function o(){let o=n(),s=t(),c=i(),l=e(()=>o.params.id),u=e(()=>c.getOrderById(l.value)),d=e(()=>a(u.value));function f(e){e&&(a(e)||s.replace(`/orders/${e.id}`))}return r(l,async e=>{e&&f(c.getOrderById(e)??await c.fetchOrder(e))},{immediate:!0}),r(u,e=>{f(e)}),{orderId:l,order:u,invoiceReady:d}}export{o as t};
+2 -2
View File
@@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#17379f">
<title>سامانه سفارش عمده پارچه</title>
<script type="module" crossorigin src="/assets/index-B6MlTGUp.js"></script>
<script type="module" crossorigin src="/assets/index-DzzUgmYT.js"></script>
<link rel="modulepreload" crossorigin href="/assets/BasicIcon-C6boaehV.js">
<link rel="modulepreload" crossorigin href="/assets/http-BC1hNfZ4.js">
<link rel="modulepreload" crossorigin href="/assets/dist-CUUvbY4x.js">
@@ -15,7 +15,7 @@
<link rel="modulepreload" crossorigin href="/assets/validation-BD7oi-2I.js">
<link rel="modulepreload" crossorigin href="/assets/catalog-s7nVUdlj.js">
<link rel="stylesheet" crossorigin href="/assets/BasicIcon-QF_b9Svq.css">
<link rel="stylesheet" crossorigin href="/assets/index-CVG_qU1h.css">
<link rel="stylesheet" crossorigin href="/assets/index-8S-fr-dB.css">
</head>
<body>
<div id="app"></div>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 218 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

+27 -15
View File
@@ -19,19 +19,19 @@
--color-purple-light: #ede9fe;
--color-info: #3b6fa0;
--color-info-light: #eef3f7;
--color-bg: #f6f8f7;
--color-page-background: #f6f8f7;
--color-bg: #eef1f0;
--color-page-background: #eef1f0;
--color-shell-bg: #eef1f0;
--color-surface: #ffffff;
--color-border: #e5e9e8;
--color-surface: #eef1f0;
--color-border: #d8dedc;
--color-text: #121212;
--color-text-primary: #121212;
--color-text-muted: #6b7280;
--color-text-secondary: #6b7280;
--color-tab-inactive-bg: #f1f5f9;
--color-tab-inactive-bg: #e4e8e6;
--color-tab-inactive-text: #64748b;
--color-summary-bg: #f1f5f9;
--color-elevated: #ffffff;
--color-summary-bg: #e4e8e6;
--color-elevated: #eef1f0;
--color-brand-warm: #f3ece3;
--color-brand-warm-text: #1a202c;
--color-brand-warm-muted: #718096;
@@ -40,9 +40,9 @@
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--shadow-sm: 0 1px 2px rgba(16, 24, 40, 0.04);
--shadow-md: 0 4px 16px rgba(16, 24, 40, 0.06);
--shadow-card: 0 1px 2px rgba(16, 24, 40, 0.04), 0 4px 16px rgba(16, 24, 40, 0.04);
--shadow-sm: none;
--shadow-md: none;
--shadow-card: none;
--shadow-frame: 0 8px 32px rgba(15, 23, 42, 0.1);
--nav-height: 64px;
--page-padding: 16px;
@@ -84,9 +84,9 @@ html.dark {
--color-info-light: #1b2740;
--color-bg: #0b0f14;
--color-page-background: #0b0f14;
--color-shell-bg: #080b10;
--color-surface: #121821;
--color-elevated: #1a2230;
--color-shell-bg: #0b0f14;
--color-surface: #0b0f14;
--color-elevated: #0b0f14;
--color-border: #2a3444;
--color-text: #eef2f8;
--color-text-primary: #eef2f8;
@@ -158,6 +158,19 @@ textarea {
font-family: inherit;
}
input:not([type='checkbox']):not([type='radio']):not([type='submit']):not([type='button']):not([type='reset']):not([type='file']):not([type='hidden']):not([type='image']):not([type='range']):not([type='color']),
textarea,
select {
background-color: transparent;
}
.persian-date-input__trigger,
.time-input__trigger,
.search-field__input,
.field-select__trigger {
background-color: transparent;
}
input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button {
-webkit-appearance: none;
@@ -212,7 +225,7 @@ input[type='number'] {
border-radius: var(--radius-md);
border: 1px solid var(--color-border);
padding: 16px;
box-shadow: var(--shadow-sm);
box-shadow: none;
color: var(--color-text);
}
@@ -294,7 +307,6 @@ input[type='number'] {
.auth-page .otp-box {
border: 1px solid #eeeef1;
border-radius: 12px;
background: var(--color-surface);
}
.auth-page .btn {
@@ -0,0 +1,149 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useAuthStore } from '@/stores/auth'
import { useCrmStore } from '@/stores/crm'
import BaseInput from '@/components/ui/BaseInput.vue'
import FormErrorBanner from '@/components/ui/FormErrorBanner.vue'
import BaseButton from '@/components/ui/BaseButton.vue'
import { normalizePhone } from '@/utils/format'
import { validatePhone } from '@/utils/validation'
import {
FORM_VALIDATION_SUMMARY,
clearFieldError,
hasFieldErrors,
type FieldErrors,
} from '@/utils/formErrors'
import { ApiError } from '@/services/http'
const emit = defineEmits<{
success: [id?: string]
}>()
const auth = useAuthStore()
const crm = useCrmStore()
const firstName = ref('')
const lastName = ref('')
const phone = ref('')
const businessName = ref('')
const fieldErrors = ref<FieldErrors>({})
const formError = ref('')
const saving = ref(false)
function clearField(key: string) {
fieldErrors.value = clearFieldError(fieldErrors.value, key)
if (!hasFieldErrors(fieldErrors.value)) formError.value = ''
}
async function submit() {
fieldErrors.value = {}
formError.value = ''
if (!auth.isAgent) {
formError.value = 'فقط واسطه می‌تواند مشتری به لیست خود اضافه کند.'
return
}
const normalized = normalizePhone(phone.value)
const errors: FieldErrors = {}
if (!firstName.value.trim()) errors.firstName = 'نام الزامی است.'
if (!lastName.value.trim()) errors.lastName = 'نام خانوادگی الزامی است.'
if (!validatePhone(normalized)) errors.phone = 'شماره موبایل معتبر نیست.'
if (hasFieldErrors(errors)) {
fieldErrors.value = errors
formError.value = FORM_VALIDATION_SUMMARY
return
}
saving.value = true
try {
const created = (await crm.registerCustomer({
firstName: firstName.value.trim(),
lastName: lastName.value.trim(),
phone: normalized,
businessName: businessName.value.trim() || undefined,
})) as { id?: string }
emit('success', created?.id)
} catch (e) {
formError.value =
e instanceof ApiError || e instanceof Error ? e.message : 'ثبت مشتری ناموفق بود.'
} finally {
saving.value = false
}
}
defineExpose({ submit, saving })
</script>
<template>
<div class="customer-create-form">
<p class="customer-create-form__intro">
مشتری به لیست شما اضافه می‌شود و بعد از ورود با همین شماره، سفارش‌هایش به حساب شما وصل می‌ماند.
</p>
<FormErrorBanner :message="formError" />
<div class="customer-create-form__fields">
<BaseInput
v-model="firstName"
label="نام"
placeholder="نام مشتری"
:error="fieldErrors.firstName"
@update:model-value="clearField('firstName')"
/>
<BaseInput
v-model="lastName"
label="نام خانوادگی"
placeholder="نام خانوادگی"
:error="fieldErrors.lastName"
@update:model-value="clearField('lastName')"
/>
<BaseInput
v-model="phone"
label="شماره موبایل"
type="tel"
inputmode="tel"
dir="ltr"
placeholder="۰۹۱۲۳۴۵۶۷۸۹"
:error="fieldErrors.phone"
@update:model-value="clearField('phone')"
/>
<BaseInput
v-model="businessName"
label="نام فروشگاه یا برند (اختیاری)"
placeholder="مثلاً بوتیک رز"
/>
</div>
<BaseButton block :loading="saving" :disabled="saving" @click="submit">
ثبت مشتری
</BaseButton>
</div>
</template>
<style scoped>
.customer-create-form {
display: flex;
flex-direction: column;
gap: 12px;
}
.customer-create-form__intro {
margin: 0;
color: var(--color-text-muted);
font-size: 0.875rem;
line-height: 1.7;
}
.customer-create-form__fields {
display: flex;
flex-direction: column;
gap: 12px;
background: var(--color-surface);
border-radius: var(--radius-md);
padding: 16px;
box-shadow: var(--shadow-sm);
}
</style>
@@ -0,0 +1,183 @@
<script setup lang="ts">
import { onBeforeUnmount, watch } from 'vue'
import CustomerCreateForm from '@/components/customers/CustomerCreateForm.vue'
const props = defineProps<{
open: boolean
}>()
const emit = defineEmits<{
close: []
success: [id?: string]
}>()
function close() {
emit('close')
}
function onSuccess(id?: string) {
emit('success', id)
}
function onKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && props.open) close()
}
watch(
() => props.open,
(isOpen) => {
document.body.style.overflow = isOpen ? 'hidden' : ''
if (isOpen) {
window.addEventListener('keydown', onKeydown)
} else {
window.removeEventListener('keydown', onKeydown)
}
},
{ immediate: true },
)
onBeforeUnmount(() => {
document.body.style.overflow = ''
window.removeEventListener('keydown', onKeydown)
})
</script>
<template>
<Teleport to="body">
<Transition name="customer-create-sheet">
<div
v-if="open"
class="customer-create-overlay"
role="dialog"
aria-modal="true"
aria-labelledby="customer-create-title"
@click.self="close"
>
<div class="customer-create-sheet">
<div class="customer-create-sheet__handle" aria-hidden="true" />
<header class="customer-create-sheet__head">
<h2 id="customer-create-title">افزودن مشتری</h2>
<button
type="button"
class="customer-create-sheet__close"
aria-label="بستن"
@click="close"
>
×
</button>
</header>
<CustomerCreateForm @success="onSuccess" />
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped>
.customer-create-overlay {
position: fixed;
inset: 0;
z-index: 200;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: flex-end;
justify-content: center;
padding: 0;
}
.customer-create-sheet {
width: 100%;
max-width: 520px;
max-height: min(92dvh, 680px);
overflow-y: auto;
background: var(--color-bg);
color: var(--color-text);
border-radius: 20px 20px 0 0;
padding: 10px 16px calc(16px + env(safe-area-inset-bottom, 0px));
box-shadow: var(--shadow-md);
display: flex;
flex-direction: column;
gap: 16px;
}
.customer-create-sheet__handle {
width: 40px;
height: 4px;
border-radius: 999px;
background: var(--color-border);
margin: 4px auto 0;
}
.customer-create-sheet__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.customer-create-sheet__head h2 {
margin: 0;
font-size: 1.0625rem;
font-weight: 700;
}
.customer-create-sheet__close {
width: 36px;
height: 36px;
border: none;
border-radius: 999px;
background: var(--color-surface);
color: var(--color-text-muted);
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
padding: 0;
}
.customer-create-sheet-enter-active,
.customer-create-sheet-leave-active {
transition: opacity 0.2s ease;
}
.customer-create-sheet-enter-active .customer-create-sheet,
.customer-create-sheet-leave-active .customer-create-sheet {
transition: transform 0.22s ease;
}
.customer-create-sheet-enter-from,
.customer-create-sheet-leave-to {
opacity: 0;
}
.customer-create-sheet-enter-from .customer-create-sheet,
.customer-create-sheet-leave-to .customer-create-sheet {
transform: translateY(100%);
}
@media (min-width: 768px) {
.customer-create-overlay {
align-items: center;
padding: 24px;
}
.customer-create-sheet {
border-radius: 16px;
max-width: 440px;
max-height: min(85dvh, 620px);
padding: 20px 20px 24px;
background: var(--color-surface);
}
.customer-create-sheet__handle {
display: none;
}
.customer-create-sheet-enter-from .customer-create-sheet,
.customer-create-sheet-leave-to .customer-create-sheet {
transform: translateY(16px) scale(0.98);
}
}
</style>
@@ -1,20 +1,24 @@
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { onMounted, ref, watch } from 'vue'
import { useCrmStore } from '@/stores/crm'
import type { CrmCustomer, MeetingInteractionType } from '@/types/crm'
import PageHeader from '@/components/layout/PageHeader.vue'
import PageSkeleton from '@/components/ui/PageSkeleton.vue'
import IconCheckCircleOutline from '@/components/icons/IconCheckCircleOutline.vue'
import PersianDateInput from '@/components/ui/PersianDateInput.vue'
import TimeInput from '@/components/ui/TimeInput.vue'
import IconClipboardEmpty from '@/components/icons/IconClipboardEmpty.vue'
import IconPhone from '@/components/icons/IconPhone.vue'
import IconMail from '@/components/icons/IconMail.vue'
import IconEditPen from '@/components/icons/IconEditPen.vue'
const route = useRoute()
const router = useRouter()
const props = defineProps<{
customerId: string
}>()
const emit = defineEmits<{
success: []
}>()
const crm = useCrmStore()
const customerId = computed(() => String(route.query.customerId ?? ''))
const customer = ref<CrmCustomer | null>(null)
const loading = ref(true)
const saving = ref(false)
@@ -38,32 +42,41 @@ const types: Array<{ id: MeetingInteractionType; label: string; icon: 'clipboard
{ id: 'note', label: 'یادداشت', icon: 'pen' },
]
onMounted(async () => {
if (!customerId.value) {
async function loadCustomer() {
if (!props.customerId) {
loading.value = false
error.value = 'مشتری مشخص نشده است.'
customer.value = null
return
}
loading.value = true
error.value = null
try {
const data = await crm.loadCustomerDetail(customerId.value)
const data = await crm.loadCustomerDetail(props.customerId)
customer.value = data.customer
} catch (e) {
customer.value = null
error.value = e instanceof Error ? e.message : 'بارگذاری مشتری ناموفق بود.'
} finally {
loading.value = false
}
})
}
onMounted(loadCustomer)
watch(() => props.customerId, loadCustomer)
async function submit() {
if (!customerId.value || !subject.value.trim() || !date.value.trim()) {
if (!props.customerId || !subject.value.trim() || !date.value.trim()) {
error.value = 'موضوع و تاریخ الزامی است.'
return
}
saving.value = true
error.value = null
try {
await crm.submitMeeting({
customerId: customerId.value,
customerId: props.customerId,
type: interactionType.value,
title: subject.value.trim(),
subject: subject.value.trim(),
@@ -81,14 +94,9 @@ async function submit() {
reminderTime: reminderOn.value
? reminderTime.value.trim() || time.value.trim() || '10:00'
: undefined,
reminderNote: reminderOn.value
? reminderNote.value.trim() || undefined
: undefined,
})
router.push({
path: '/customer',
query: { id: customerId.value, tab: 'reports' },
reminderNote: reminderOn.value ? reminderNote.value.trim() || undefined : undefined,
})
emit('success')
} catch (e) {
error.value = e instanceof Error ? e.message : 'ثبت گزارش ناموفق بود.'
} finally {
@@ -98,25 +106,12 @@ async function submit() {
</script>
<template>
<div class="page meeting-page">
<PageHeader title="ثبت گزارش ملاقات" show-back centered @back="router.back()">
<template #trailing>
<button
type="button"
class="submit-icon-btn"
aria-label="ثبت گزارش"
:disabled="saving"
@click="submit"
>
<IconCheckCircleOutline :size="22" />
</button>
</template>
</PageHeader>
<div class="meeting-report-form">
<PageSkeleton v-if="loading" variant="form" />
<p v-else-if="error" class="form-error">{{ error }}</p>
<div class="page__scroll meeting-scroll">
<PageSkeleton v-if="loading" variant="form" />
<p v-else-if="error" class="form-error">{{ error }}</p>
<section v-if="customer" class="customer-card">
<template v-if="customer">
<section class="customer-card">
<div class="avatar" :style="{ background: customer.avatarColor }">
{{ customer.avatarLetter }}
</div>
@@ -149,15 +144,9 @@ async function submit() {
<section>
<h3>تاریخ و زمان</h3>
<div class="two-cols">
<label class="field">
<span>تاریخ</span>
<input v-model="date" type="text" />
</label>
<label class="field">
<span>ساعت</span>
<input v-model="time" type="text" />
</label>
<div class="two-cols two-cols--date">
<PersianDateInput v-model="date" label="تاریخ" class="meeting-date" />
<TimeInput v-model="time" label="ساعت" placeholder="مثلاً ۱۰:۳۰" class="meeting-time" />
</div>
</section>
@@ -199,15 +188,14 @@ async function submit() {
<span>عنوان یادآور</span>
<input v-model="reminderTitle" type="text" />
</label>
<div class="two-cols">
<label class="field">
<span>تاریخ یادآور</span>
<input v-model="reminderDate" type="text" />
</label>
<label class="field">
<span>ساعت یادآور</span>
<input v-model="reminderTime" type="text" />
</label>
<div class="two-cols two-cols--date">
<PersianDateInput v-model="reminderDate" label="تاریخ یادآور" class="meeting-date" />
<TimeInput
v-model="reminderTime"
label="ساعت یادآور"
placeholder="مثلاً ۱۰:۳۰"
class="meeting-time"
/>
</div>
<label class="field">
<span>یادداشت یادآور (اختیاری)</span>
@@ -215,28 +203,27 @@ async function submit() {
</label>
</template>
</section>
</div>
<footer class="footer">
<button type="button" class="submit" :disabled="saving" @click="submit">
{{ saving ? 'در حال ثبت...' : 'ثبت گزارش و یادآور' }}
</button>
</footer>
</template>
</div>
</template>
<style scoped>
.meeting-page {
background: var(--color-bg);
}
.meeting-scroll {
padding: 8px var(--page-padding) 100px;
.meeting-report-form {
display: flex;
flex-direction: column;
gap: 16px;
}
.form-error {
margin: 0;
color: var(--color-danger);
font-size: 0.875rem;
}
.customer-card {
display: flex;
gap: 12px;
@@ -273,23 +260,6 @@ h3 {
gap: 8px;
}
.submit-icon-btn {
width: 40px;
height: 40px;
border: none;
background: transparent;
color: var(--color-primary);
cursor: pointer;
display: grid;
place-items: center;
padding: 0;
}
.submit-icon-btn:disabled {
opacity: 0.5;
cursor: not-allowed;
}
.type-btn {
border: 1.5px solid var(--color-border);
background: var(--color-surface);
@@ -319,6 +289,62 @@ h3 {
gap: 8px;
}
.two-cols--date {
align-items: end;
}
.meeting-report-form :deep(.meeting-time.form-group),
.meeting-report-form :deep(.meeting-date.form-group) {
margin-bottom: 0;
}
.meeting-report-form :deep(.meeting-time .form-label),
.meeting-report-form :deep(.meeting-date .form-label) {
font-size: 0.85rem;
font-weight: 400;
margin-bottom: 6px;
}
.meeting-report-form :deep(.meeting-date .persian-date-input__trigger) {
border-radius: 12px;
padding-inline: 10px;
font-size: 0.8125rem;
gap: 6px;
}
.meeting-report-form :deep(.meeting-date .persian-date-input__trigger > span:first-child) {
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.meeting-report-form :deep(.meeting-date .persian-date-input__icon) {
flex-shrink: 0;
}
.meeting-report-form :deep(.meeting-date .persian-date-input__icon svg) {
width: 16px;
height: 16px;
}
.meeting-report-form :deep(.meeting-time .time-input__trigger) {
border-radius: 12px;
padding-inline: 10px;
font-size: 0.8125rem;
gap: 6px;
}
.meeting-report-form :deep(.meeting-time .time-input__icon svg) {
width: 16px;
height: 16px;
}
.reminder-box :deep(.meeting-time .time-input__panel) {
top: auto;
bottom: calc(100% + 6px);
}
.field {
display: flex;
flex-direction: column;
@@ -330,7 +356,6 @@ h3 {
.field textarea {
border: 1px solid var(--color-border);
border-radius: 12px;
background: #f5f5f5;
padding: 12px;
font: inherit;
resize: vertical;
@@ -397,7 +422,7 @@ h3 {
width: 22px;
height: 22px;
border-radius: 999px;
background: #fff;
background: var(--color-surface);
transition: transform 0.15s;
}
@@ -409,14 +434,6 @@ h3 {
transform: translateX(-18px);
}
.footer {
position: sticky;
bottom: 0;
padding: 12px var(--page-padding) calc(12px + env(safe-area-inset-bottom, 0));
background: var(--color-surface);
border-top: 1px solid var(--color-border);
}
.submit {
width: 100%;
min-height: 52px;
@@ -427,4 +444,9 @@ h3 {
font-weight: 700;
cursor: pointer;
}
.submit:disabled {
opacity: 0.6;
cursor: not-allowed;
}
</style>
@@ -0,0 +1,189 @@
<script setup lang="ts">
import { onBeforeUnmount, watch } from 'vue'
import MeetingReportForm from '@/components/customers/MeetingReportForm.vue'
const props = defineProps<{
open: boolean
customerId: string
}>()
const emit = defineEmits<{
close: []
success: []
}>()
function close() {
emit('close')
}
function onSuccess() {
emit('success')
}
function onKeydown(event: KeyboardEvent) {
if (event.key === 'Escape' && props.open) close()
}
watch(
() => props.open,
(isOpen) => {
document.body.style.overflow = isOpen ? 'hidden' : ''
if (isOpen) {
window.addEventListener('keydown', onKeydown)
} else {
window.removeEventListener('keydown', onKeydown)
}
},
{ immediate: true },
)
onBeforeUnmount(() => {
document.body.style.overflow = ''
window.removeEventListener('keydown', onKeydown)
})
</script>
<template>
<Teleport to="body">
<Transition name="meeting-report-sheet">
<div
v-if="open"
class="meeting-report-overlay"
role="dialog"
aria-modal="true"
aria-labelledby="meeting-report-title"
@click.self="close"
>
<div class="meeting-report-sheet">
<div class="meeting-report-sheet__handle" aria-hidden="true" />
<header class="meeting-report-sheet__head">
<h2 id="meeting-report-title">ثبت گزارش ملاقات</h2>
<button
type="button"
class="meeting-report-sheet__close"
aria-label="بستن"
@click="close"
>
×
</button>
</header>
<MeetingReportForm
v-if="customerId"
:key="customerId"
:customer-id="customerId"
@success="onSuccess"
/>
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped>
.meeting-report-overlay {
position: fixed;
inset: 0;
z-index: 200;
background: rgba(0, 0, 0, 0.45);
display: flex;
align-items: flex-end;
justify-content: center;
padding: 0;
}
.meeting-report-sheet {
width: 100%;
max-width: 560px;
max-height: min(94dvh, 760px);
overflow-y: auto;
background: var(--color-bg);
color: var(--color-text);
border-radius: 20px 20px 0 0;
padding: 10px 16px calc(16px + env(safe-area-inset-bottom, 0px));
box-shadow: var(--shadow-md);
display: flex;
flex-direction: column;
gap: 16px;
}
.meeting-report-sheet__handle {
width: 40px;
height: 4px;
border-radius: 999px;
background: var(--color-border);
margin: 4px auto 0;
}
.meeting-report-sheet__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.meeting-report-sheet__head h2 {
margin: 0;
font-size: 1.0625rem;
font-weight: 700;
}
.meeting-report-sheet__close {
width: 36px;
height: 36px;
border: none;
border-radius: 999px;
background: var(--color-surface);
color: var(--color-text-muted);
font-size: 1.5rem;
line-height: 1;
cursor: pointer;
display: grid;
place-items: center;
padding: 0;
}
.meeting-report-sheet-enter-active,
.meeting-report-sheet-leave-active {
transition: opacity 0.2s ease;
}
.meeting-report-sheet-enter-active .meeting-report-sheet,
.meeting-report-sheet-leave-active .meeting-report-sheet {
transition: transform 0.22s ease;
}
.meeting-report-sheet-enter-from,
.meeting-report-sheet-leave-to {
opacity: 0;
}
.meeting-report-sheet-enter-from .meeting-report-sheet,
.meeting-report-sheet-leave-to .meeting-report-sheet {
transform: translateY(100%);
}
@media (min-width: 768px) {
.meeting-report-overlay {
align-items: center;
padding: 24px;
}
.meeting-report-sheet {
border-radius: 16px;
max-width: 520px;
max-height: min(88dvh, 720px);
padding: 20px 20px 24px;
background: var(--color-surface);
}
.meeting-report-sheet__handle {
display: none;
}
.meeting-report-sheet-enter-from .meeting-report-sheet,
.meeting-report-sheet-leave-to .meeting-report-sheet {
transform: translateY(16px) scale(0.98);
}
}
</style>
+35
View File
@@ -0,0 +1,35 @@
<script setup lang="ts">
withDefaults(
defineProps<{
size?: number
color?: string
strokeWidth?: number
}>(),
{ size: 24, color: 'currentColor', strokeWidth: 1.5 },
)
</script>
<template>
<svg
:width="size"
:height="size"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M8.25 5.25 12 7.75l3.75-2.5 2.25 2.75L16 9.5V18.5a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V9.5L6 8l2.25-2.75Z"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12 7.75V18.5"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
/>
</svg>
</template>
+48
View File
@@ -0,0 +1,48 @@
<script setup lang="ts">
withDefaults(
defineProps<{
size?: number
color?: string
strokeWidth?: number
}>(),
{ size: 24, color: 'currentColor', strokeWidth: 1.5 },
)
</script>
<template>
<svg
:width="size"
:height="size"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M7.5 5.5h9l2 3v10.5a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1V8.5l2-3Z"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12 5.5v13"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
/>
<path
d="M7.5 9 12 11.75 16.5 9"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M10.25 5.5V4a1.75 1.75 0 0 1 3.5 0v1.5"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
/>
</svg>
</template>
+41
View File
@@ -0,0 +1,41 @@
<script setup lang="ts">
withDefaults(
defineProps<{
size?: number
color?: string
strokeWidth?: number
}>(),
{ size: 24, color: 'currentColor', strokeWidth: 1.5 },
)
</script>
<template>
<svg
:width="size"
:height="size"
viewBox="0 0 24 24"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M8 4.5h8v4.25l-1.75 11H13l-1-9.5-1 9.5H7.75L6 8.75V4.5Z"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12 8.75v11"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
/>
<path
d="M8 7.25h8"
:stroke="color"
:stroke-width="strokeWidth"
stroke-linecap="round"
/>
</svg>
</template>
-1
View File
@@ -73,7 +73,6 @@ const resolvedAlign = computed(() => props.align ?? (isNumericField.value ? 'end
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: 0.9375rem;
background: var(--color-surface);
color: var(--color-text);
transition: border-color 0.15s;
}
-1
View File
@@ -61,7 +61,6 @@ function onInput(event: Event) {
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: 0.9375rem;
background: var(--color-surface);
color: var(--color-text);
transition: border-color 0.15s;
text-align: right;
+158 -11
View File
@@ -15,24 +15,51 @@ withDefaults(
</script>
<template>
<div class="page-skeleton" role="status" aria-live="polite" aria-busy="true" aria-label="در حال بارگذاری">
<div
class="page-skeleton"
:class="{ 'page-skeleton--home': variant === 'home' }"
role="status"
aria-live="polite"
aria-busy="true"
aria-label="در حال بارگذاری"
>
<!-- Home -->
<template v-if="variant === 'home'">
<div class="page-skeleton__banner">
<SkeletonBlock height="148px" radius="16px" />
<div class="page-skeleton__hero">
<SkeletonBlock class="page-skeleton__hero-block" height="148px" radius="16px" />
</div>
<div class="page-skeleton__quick">
<div v-for="n in 4" :key="`q-${n}`" class="page-skeleton__quick-item">
<SkeletonBlock circle width="48px" height="48px" />
<SkeletonBlock width="64%" height="10px" />
<SkeletonBlock width="72%" height="10px" />
</div>
</div>
<SkeletonBlock width="40%" height="16px" />
<div class="page-skeleton__grid">
<div v-for="n in 4" :key="`g-${n}`" class="page-skeleton__card">
<SkeletonBlock height="110px" radius="12px" />
<SkeletonBlock width="80%" height="12px" />
<SkeletonBlock width="50%" height="10px" />
<div class="page-skeleton__level">
<div class="page-skeleton__level-top">
<SkeletonBlock circle width="48px" height="48px" />
<div class="page-skeleton__level-text">
<SkeletonBlock width="56px" height="10px" />
<SkeletonBlock width="88px" height="16px" />
<SkeletonBlock width="104px" height="10px" />
</div>
</div>
<SkeletonBlock height="8px" radius="999px" />
<SkeletonBlock width="62%" height="10px" />
</div>
<div class="page-skeleton__products">
<div class="page-skeleton__section-head">
<SkeletonBlock width="132px" height="16px" />
<SkeletonBlock width="72px" height="12px" />
</div>
<div class="page-skeleton__grid page-skeleton__product-grid">
<div v-for="n in 6" :key="`g-${n}`" class="page-skeleton__card">
<SkeletonBlock class="page-skeleton__product-image" height="110px" radius="12px" />
<SkeletonBlock width="82%" height="12px" />
<SkeletonBlock width="54%" height="10px" />
</div>
</div>
</div>
</template>
@@ -126,7 +153,7 @@ withDefaults(
width: 100%;
}
.page-skeleton__banner,
.page-skeleton__hero,
.page-skeleton__panel,
.page-skeleton__detail-body,
.page-skeleton__form {
@@ -135,6 +162,52 @@ withDefaults(
gap: 12px;
}
.page-skeleton__hero {
width: 100%;
}
.page-skeleton__hero-block {
width: 100%;
}
.page-skeleton__level {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
background: transparent;
}
.page-skeleton__level-top {
display: flex;
align-items: center;
gap: 12px;
}
.page-skeleton__level-text {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.page-skeleton__products {
display: flex;
flex-direction: column;
gap: 12px;
min-width: 0;
}
.page-skeleton__section-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
.page-skeleton__quick {
display: grid;
grid-template-columns: repeat(4, 1fr);
@@ -228,8 +301,82 @@ withDefaults(
}
@media (min-width: 768px) {
.page-skeleton {
padding: 8px 0 28px;
}
.page-skeleton--home {
display: grid;
grid-column: 1 / -1;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-areas:
'hero hero level'
'quick quick quick'
'products products products';
gap: 18px 20px;
width: 100%;
min-width: 0;
}
.page-skeleton__hero {
grid-area: hero;
aspect-ratio: 2.1 / 1;
min-height: 0;
}
.page-skeleton__hero-block {
height: 100% !important;
border-radius: 20px !important;
}
.page-skeleton__quick {
grid-area: quick;
gap: 14px;
}
.page-skeleton__quick-item {
min-height: 96px;
justify-content: center;
padding: 12px 8px;
border: 1px solid var(--color-border);
border-radius: 16px;
}
.page-skeleton__level {
grid-area: level;
align-self: stretch;
justify-content: center;
padding: 20px 22px;
}
.page-skeleton__products {
grid-area: products;
}
.page-skeleton__grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 16px;
}
.page-skeleton__product-image {
height: 160px !important;
}
.page-skeleton__profile-hero {
align-items: flex-start;
padding: 0 0 8px;
}
.page-skeleton__panel {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.page-skeleton__list {
gap: 12px;
}
.page-skeleton__row--card {
padding: 14px 16px;
}
}
</style>
-1
View File
@@ -190,7 +190,6 @@ onBeforeUnmount(() => {
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: 0.9375rem;
background: var(--color-surface);
color: var(--color-text);
transition: border-color 0.15s;
}
-1
View File
@@ -28,7 +28,6 @@ defineEmits<{
padding: 0 16px;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
background: var(--color-surface);
color: var(--color-text);
font-family: inherit;
font-size: 0.9375rem;
+371
View File
@@ -0,0 +1,371 @@
<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { toPersianDigits } from '@/utils/format'
import IconProgressClock from '@/components/icons/IconProgressClock.vue'
const props = defineProps<{
modelValue?: string
label?: string
placeholder?: string
hint?: string
error?: string
disabled?: boolean
}>()
const emit = defineEmits<{
'update:modelValue': [value: string]
}>()
const rootRef = ref<HTMLElement | null>(null)
const isOpen = ref(false)
const draftHour = ref(10)
const draftMinute = ref(0)
const minuteSteps = [0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55]
const parsed = computed(() => parseTime(props.modelValue ?? ''))
const displayValue = computed(() => {
if (!parsed.value) return ''
return formatTimeLabel(parsed.value.hour, parsed.value.minute)
})
watch(
() => props.modelValue,
(value) => {
const next = parseTime(value ?? '')
if (next) {
draftHour.value = next.hour
draftMinute.value = snapMinute(next.minute)
}
},
{ immediate: true },
)
function snapMinute(minute: number) {
const snapped = Math.round(minute / 5) * 5
return snapped === 60 ? 55 : snapped
}
function parseTime(value: string) {
const match = /^(\d{1,2}):(\d{2})$/.exec(value.trim())
if (!match) return null
const hour = Number(match[1])
const minute = Number(match[2])
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) return null
return { hour, minute }
}
function formatTimeLabel(hour: number, minute: number) {
return toPersianDigits(
`${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`,
)
}
function padTime(hour: number, minute: number) {
return `${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`
}
function togglePanel() {
if (props.disabled) return
if (!isOpen.value) {
if (parsed.value) {
draftHour.value = parsed.value.hour
draftMinute.value = snapMinute(parsed.value.minute)
} else {
const now = new Date()
draftHour.value = now.getHours()
draftMinute.value = snapMinute(now.getMinutes())
}
}
isOpen.value = !isOpen.value
}
function closePanel() {
isOpen.value = false
}
function stepHour(delta: number) {
draftHour.value = (draftHour.value + delta + 24) % 24
}
function stepMinute(delta: number) {
const index = minuteSteps.indexOf(draftMinute.value)
const base = index < 0 ? 0 : index
const next = (base + delta + minuteSteps.length) % minuteSteps.length
draftMinute.value = minuteSteps[next]
}
function confirmSelection() {
emit('update:modelValue', padTime(draftHour.value, draftMinute.value))
closePanel()
}
function selectNow() {
const now = new Date()
const hour = now.getHours()
const minute = snapMinute(now.getMinutes())
draftHour.value = hour
draftMinute.value = minute
emit('update:modelValue', padTime(hour, minute))
closePanel()
}
function onDocumentClick(event: MouseEvent) {
if (!rootRef.value?.contains(event.target as Node)) {
closePanel()
}
}
onMounted(() => {
document.addEventListener('click', onDocumentClick)
})
onBeforeUnmount(() => {
document.removeEventListener('click', onDocumentClick)
})
</script>
<template>
<div ref="rootRef" class="form-group time-input">
<label v-if="label" class="form-label">{{ label }}</label>
<button
type="button"
class="input time-input__trigger"
:class="{ 'input-error': error, 'is-open': isOpen }"
:disabled="disabled"
@click.stop="togglePanel"
>
<span :class="{ 'time-input__placeholder': !displayValue }">
{{ displayValue || placeholder || 'انتخاب ساعت' }}
</span>
<span class="time-input__icon" aria-hidden="true">
<IconProgressClock :size="18" />
</span>
</button>
<div v-if="isOpen" class="time-input__panel" @click.stop>
<div class="time-input__picker" role="group" aria-label="انتخاب ساعت">
<div class="time-input__spinner">
<span class="time-input__spinner-label">ساعت</span>
<button type="button" class="time-input__step" aria-label="یک ساعت بعد" @click="stepHour(1)">
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true">
<path d="M6 14l6-6 6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</button>
<strong class="time-input__value">{{ toPersianDigits(String(draftHour).padStart(2, '0')) }}</strong>
<button type="button" class="time-input__step" aria-label="یک ساعت قبل" @click="stepHour(-1)">
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true">
<path d="M6 10l6 6 6-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</button>
</div>
<span class="time-input__colon" aria-hidden="true">:</span>
<div class="time-input__spinner">
<span class="time-input__spinner-label">دقیقه</span>
<button type="button" class="time-input__step" aria-label="پنج دقیقه بعد" @click="stepMinute(1)">
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true">
<path d="M6 14l6-6 6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</button>
<strong class="time-input__value">
{{ toPersianDigits(String(draftMinute).padStart(2, '0')) }}
</strong>
<button type="button" class="time-input__step" aria-label="پنج دقیقه قبل" @click="stepMinute(-1)">
<svg width="14" height="14" viewBox="0 0 24 24" aria-hidden="true">
<path d="M6 10l6 6 6-6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" />
</svg>
</button>
</div>
</div>
<div class="time-input__footer">
<button type="button" class="time-input__now" @click="selectNow">اکنون</button>
<button type="button" class="time-input__confirm" @click="confirmSelection">تأیید</button>
</div>
</div>
<p v-if="error" class="input-error-text">{{ error }}</p>
<p v-else-if="hint" class="form-hint">{{ hint }}</p>
</div>
</template>
<style scoped>
.time-input {
position: relative;
}
.input {
width: 100%;
height: var(--btn-height);
padding: 0 14px;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
font-size: 0.9375rem;
color: var(--color-text);
transition: border-color 0.15s;
}
.time-input__trigger {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
cursor: pointer;
text-align: right;
}
.time-input__trigger:focus {
outline: none;
border-color: var(--color-primary);
}
.time-input__trigger.is-open,
.time-input__trigger:focus-visible {
border-color: var(--color-primary);
}
.time-input__trigger:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.time-input__placeholder {
color: var(--color-text-muted);
}
.time-input__trigger > span:first-child {
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.time-input__icon {
display: flex;
align-items: center;
flex-shrink: 0;
color: var(--color-text-muted);
}
.input-error {
border-color: var(--color-danger);
}
.input-error-text {
color: var(--color-danger);
font-size: 0.8125rem;
margin: 4px 0 0;
}
.time-input__panel {
position: absolute;
top: calc(100% + 4px);
right: 0;
left: 0;
z-index: 320;
padding: 8px;
border: 1px solid var(--color-border);
border-radius: 12px;
background: var(--color-surface);
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
}
.time-input__picker {
display: grid;
grid-template-columns: 1fr auto 1fr;
gap: 6px;
align-items: center;
}
.time-input__spinner {
display: flex;
flex-direction: column;
align-items: center;
gap: 2px;
padding: 4px 6px 6px;
border: 1px solid var(--color-border);
border-radius: 10px;
background: transparent;
}
.time-input__spinner-label {
font-size: 0.6875rem;
color: var(--color-text-muted);
line-height: 1.2;
}
.time-input__value {
display: flex;
align-items: center;
justify-content: center;
min-height: 24px;
font-size: 0.9375rem;
font-weight: 700;
letter-spacing: 0.04em;
color: var(--color-text);
}
.time-input__step {
width: 28px;
height: 24px;
border: none;
border-radius: 6px;
background: transparent;
color: var(--color-text-muted);
display: grid;
place-items: center;
padding: 0;
cursor: pointer;
}
.time-input__step:hover {
background: var(--color-primary-soft);
color: var(--color-primary);
}
.time-input__colon {
font-size: 0.9375rem;
font-weight: 700;
color: var(--color-text-muted);
line-height: 1;
padding-top: 12px;
}
.time-input__footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid var(--color-border);
}
.time-input__now {
border: none;
background: transparent;
color: var(--color-primary);
font: inherit;
font-size: 0.75rem;
font-weight: 600;
cursor: pointer;
padding: 4px 0;
}
.time-input__confirm {
min-height: 32px;
padding: 0 14px;
border: none;
border-radius: 8px;
background: var(--color-primary);
color: #fff;
font: inherit;
font-size: 0.75rem;
font-weight: 700;
cursor: pointer;
}
</style>
+12 -6
View File
@@ -96,9 +96,10 @@ const router = createRouter({
},
{
path: '/customers/new',
name: 'customer-new',
component: () => import('@/views/customers/CustomerCreateView.vue'),
meta: { requiresAuth: true, hideNav: true },
redirect: (to) => ({
path: '/customers',
query: { ...to.query, create: '1' },
}),
},
{
path: '/customer',
@@ -108,9 +109,14 @@ const router = createRouter({
},
{
path: '/meeting-report',
name: 'meeting-report',
component: () => import('@/views/customers/MeetingReportView.vue'),
meta: { requiresAuth: true, hideNav: true },
redirect: (to) => ({
path: '/customer',
query: {
id: String(to.query.customerId ?? ''),
tab: 'reports',
meetingReport: '1',
},
}),
},
{
path: '/reminders',
+19
View File
@@ -8,6 +8,9 @@ import IconIdCard from '@/components/icons/IconIdCard.vue'
import IconCatalog from '@/components/icons/IconCatalog.vue'
import IconHeadset from '@/components/icons/IconHeadset.vue'
import IconHome from '@/components/icons/IconHome.vue'
import IconShirt from '@/components/icons/IconShirt.vue'
import IconSuit from '@/components/icons/IconSuit.vue'
import IconTrousers from '@/components/icons/IconTrousers.vue'
export const CATEGORY_ICON_MAP: Record<string, Component> = {
bolt: IconBolt,
@@ -19,6 +22,22 @@ export const CATEGORY_ICON_MAP: Record<string, Component> = {
catalog: IconCatalog,
headset: IconHeadset,
home: IconHome,
shirt: IconShirt,
suit: IconSuit,
trousers: IconTrousers,
}
const USAGE_ICON_BY_ID: Record<string, string> = {
shirt: 'shirt',
suit: 'suit',
trousers: 'trousers',
}
export function usageCategoryIconKey(item: { id: string; icon?: string }) {
// آیکون‌های ذخیره‌شده در ادمین (grid/bag/id-card) را برای دسته‌های استاندارد نادیده بگیر
if (USAGE_ICON_BY_ID[item.id]) return USAGE_ICON_BY_ID[item.id]
if (item.icon && CATEGORY_ICON_MAP[item.icon]) return item.icon
return item.icon ?? 'grid'
}
export function categoryIconOf(icon?: string, fallback: Component = IconDiamond) {
+1 -2
View File
@@ -90,7 +90,7 @@ onMounted(async () => {
<template>
<div class="page login-page">
<img class="login-page__bg" :src="fabricUrl" alt="" />
<img class="login-page__fabric" :src="fabricUrl2" alt="" />
<!-- <img class="login-page__fabric" :src="fabricUrl2" alt="" />-->
<div class="login-page__scroll">
<div class="login-page__stage">
@@ -346,7 +346,6 @@ onMounted(async () => {
padding: 0 8px 0 14px;
border: 1px solid #e4e0db;
border-radius: 14px;
background: #e4ddd4;
}
.phone-field.is-invalid {
-2
View File
@@ -456,7 +456,6 @@ onMounted(() => {
padding: 0;
border: 1px solid #e4e0db;
border-radius: 16px;
background: #f7f5f1;
color: #1c1b19;
font-family: inherit;
font-size: 1.25rem;
@@ -469,7 +468,6 @@ onMounted(() => {
.otp-box:focus {
outline: none;
border-color: var(--color-primary);
background: #fff;
}
.otp-box--error {
-1
View File
@@ -385,7 +385,6 @@ async function submit() {
padding: 0 14px;
border: 1px solid #e0d9d0;
border-radius: 14px;
background: #ebe4dc;
}
.reg-field.is-invalid,
+8 -7
View File
@@ -400,8 +400,8 @@ async function addAllToInquiry() {
<style scoped>
.calite-detail {
background: #f4f0ed;
color: #2a2622;
background: var(--color-bg);
color: var(--color-text);
}
.calite-detail__nav,
@@ -563,7 +563,7 @@ async function addAllToInquiry() {
top: 16%;
bottom: 16%;
width: 1px;
background: #e4dfd8;
background: var(--color-border);
}
.attr__icon {
@@ -617,7 +617,7 @@ async function addAllToInquiry() {
padding: 0 14px;
border: none;
border-radius: 999px;
background: #ece8e2;
background: var(--color-tab-inactive-bg);
color: #6a645e;
font: inherit;
font-size: 0.8125rem;
@@ -644,11 +644,12 @@ async function addAllToInquiry() {
overflow: hidden;
border: none;
border-radius: 20px;
background: #fff;
background: var(--color-surface);
text-align: start;
cursor: pointer;
color: #1c1c1c;
box-shadow: 0 1px 3px rgba(40, 32, 24, 0.05);
box-shadow: none;
border: 1px solid var(--color-border);
}
.pattern-card__photo {
@@ -903,7 +904,7 @@ html.dark .calite-detail__footer.sticky-footer {
width: 100%;
max-width: none;
padding: 12px 28px 24px;
background: #f4f0ed;
background: var(--color-bg);
}
.inquiry-bar {
@@ -175,7 +175,6 @@ async function submit() {
border-radius: 10px;
text-align: center;
font: inherit;
background: var(--color-bg);
color: var(--color-text);
}
@@ -193,7 +192,6 @@ async function submit() {
padding: 10px 12px;
font: inherit;
resize: vertical;
background: var(--color-surface);
color: var(--color-text);
}
+50 -30
View File
@@ -10,7 +10,7 @@ import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
import IconHeadset from '@/components/icons/IconHeadset.vue'
import IconDiamond from '@/components/icons/IconDiamond.vue'
import IconCatalog from '@/components/icons/IconCatalog.vue'
import { categoryIconOf } from '@/utils/categoryIcons'
import { categoryIconOf, usageCategoryIconKey } from '@/utils/categoryIcons'
import { useGoBack } from '@/composables/useGoBack'
import AuthThemeToggle from '@/components/ui/AuthThemeToggle.vue'
import PageSkeleton from '@/components/ui/PageSkeleton.vue'
@@ -23,10 +23,6 @@ const seasonCategories = ref<SeasonCategory[]>([])
const loading = ref(true)
const error = ref<string | null>(null)
function usageIconKey(item: ProductCategory) {
return item.icon || (item.id === 'shirt' ? 'id-card' : item.id === 'suit' ? 'bag' : 'grid')
}
function seasonIconKey(item: SeasonCategory) {
return item.icon || (item.id === 'summer' ? 'bolt' : item.id === 'winter' ? 'feather' : 'diamond')
}
@@ -91,7 +87,7 @@ function openSeason(id: string) {
:style="item.imageUrl ? { backgroundImage: `url(${item.imageUrl})` } : undefined"
>
<span v-if="item.showIcon !== false" class="usage-card__icon">
<component :is="categoryIconOf(usageIconKey(item), IconCatalog)" :size="18" />
<component :is="categoryIconOf(usageCategoryIconKey(item), IconCatalog)" :size="18" />
</span>
</div>
<div class="usage-card__body">
@@ -114,7 +110,7 @@ function openSeason(id: string) {
:key="item.id"
type="button"
class="season-card"
:class="`season-card--${item.id}`"
:class="[`season-card--${item.id}`, { 'season-card--banner': !!item.imageUrl }]"
:style="item.imageUrl ? { backgroundImage: `url(${item.imageUrl})` } : undefined"
@click="openSeason(item.id)"
>
@@ -260,7 +256,8 @@ function openSeason(id: string) {
margin: 0;
line-height: normal;
font-family: inherit;
box-shadow: var(--shadow-sm);
box-shadow: none;
border: 1px solid var(--color-border);
}
.usage-card__image {
@@ -297,11 +294,12 @@ function openSeason(id: string) {
width: 28px;
height: 28px;
border-radius: 999px;
background: var(--color-surface);
background: var(--color-primary-soft);
color: var(--color-primary);
display: grid;
place-items: center;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
box-shadow: none;
border: 1px solid var(--color-border);
}
.usage-card__body {
@@ -335,9 +333,9 @@ function openSeason(id: string) {
position: relative;
isolation: isolate;
display: flex;
align-items: center;
align-items: flex-end;
justify-content: flex-start;
min-height: 124px;
min-height: 148px;
width: 100%;
appearance: none;
-webkit-appearance: none;
@@ -346,17 +344,28 @@ function openSeason(id: string) {
overflow: hidden;
text-align: start;
cursor: pointer;
padding: 16px 18px;
padding: 14px 16px;
margin: 0;
font-family: inherit;
box-shadow: var(--shadow-sm);
box-shadow: none;
border: 1px solid var(--color-border);
background-color: #d6c3a8;
background-image: linear-gradient(160deg, #d6c3a8, #8b7355);
background-size: cover;
background-position: center left;
background-position: center top;
background-repeat: no-repeat;
}
.season-card--banner {
align-items: stretch;
justify-content: flex-start;
padding: 0;
min-height: 156px;
aspect-ratio: 2.35 / 1;
background-size: cover;
background-position: center top;
}
.season-card--summer {
background-image: linear-gradient(160deg, #f3e6c8, #c4a574);
}
@@ -369,25 +378,23 @@ function openSeason(id: string) {
background-image: linear-gradient(160deg, #efe6d6, #b9a48a);
}
.season-card::after {
content: '';
position: absolute;
inset: 0;
z-index: 0;
pointer-events: none;
background: linear-gradient(
to left,
color-mix(in srgb, var(--color-surface) 90%, transparent) 0%,
color-mix(in srgb, var(--color-surface) 72%, transparent) 38%,
color-mix(in srgb, var(--color-surface) 22%, transparent) 68%,
transparent 100%
);
}
.season-card__content {
position: relative;
z-index: 1;
max-width: 58%;
padding: 14px 16px;
}
.season-card--banner .season-card__content {
display: flex;
flex-direction: column;
justify-content: flex-end;
align-self: stretch;
max-width: none;
width: 100%;
min-height: 100%;
margin-top: auto;
padding: 14px 16px;
}
.season-card__content strong {
@@ -415,6 +422,19 @@ function openSeason(id: string) {
flex-shrink: 0;
}
.season-card--banner .season-card__content strong,
.season-card--banner .season-card__content p,
.season-card--banner .season-card__link {
color: #fff;
text-shadow:
0 1px 2px rgba(0, 0, 0, 0.55),
0 2px 12px rgba(0, 0, 0, 0.35);
}
.season-card--banner .season-card__icon {
background: rgba(255, 255, 255, 0.92);
}
.season-card__content p {
margin: 0 0 10px;
color: var(--color-text-muted);
@@ -398,7 +398,7 @@ const currentStep = 1
}
.company-showcase--par .company-showcase__cta {
background: #fff;
background: var(--color-surface);
color: var(--color-primary);
}
-169
View File
@@ -1,169 +0,0 @@
<script setup lang="ts">
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import { useAuthStore } from '@/stores/auth'
import { useCrmStore } from '@/stores/crm'
import { useGoBack } from '@/composables/useGoBack'
import PageHeader from '@/components/layout/PageHeader.vue'
import BaseInput from '@/components/ui/BaseInput.vue'
import FormErrorBanner from '@/components/ui/FormErrorBanner.vue'
import BaseButton from '@/components/ui/BaseButton.vue'
import { normalizePhone } from '@/utils/format'
import { validatePhone } from '@/utils/validation'
import {
FORM_VALIDATION_SUMMARY,
clearFieldError,
hasFieldErrors,
type FieldErrors,
} from '@/utils/formErrors'
import { ApiError } from '@/services/http'
const router = useRouter()
const auth = useAuthStore()
const crm = useCrmStore()
const { goBack } = useGoBack('/customers')
const firstName = ref('')
const lastName = ref('')
const phone = ref('')
const businessName = ref('')
const fieldErrors = ref<FieldErrors>({})
const formError = ref('')
const saving = ref(false)
function clearField(key: string) {
fieldErrors.value = clearFieldError(fieldErrors.value, key)
if (!hasFieldErrors(fieldErrors.value)) formError.value = ''
}
async function submit() {
fieldErrors.value = {}
formError.value = ''
if (!auth.isAgent) {
formError.value = 'فقط واسطه می‌تواند مشتری به لیست خود اضافه کند.'
return
}
const normalized = normalizePhone(phone.value)
const errors: FieldErrors = {}
if (!firstName.value.trim()) errors.firstName = 'نام الزامی است.'
if (!lastName.value.trim()) errors.lastName = 'نام خانوادگی الزامی است.'
if (!validatePhone(normalized)) errors.phone = 'شماره موبایل معتبر نیست.'
if (hasFieldErrors(errors)) {
fieldErrors.value = errors
formError.value = FORM_VALIDATION_SUMMARY
return
}
saving.value = true
try {
const created = (await crm.registerCustomer({
firstName: firstName.value.trim(),
lastName: lastName.value.trim(),
phone: normalized,
businessName: businessName.value.trim() || undefined,
})) as { id?: string }
if (created?.id) {
await router.replace({ path: '/customer', query: { id: created.id } })
return
}
await router.replace('/customers')
} catch (e) {
formError.value =
e instanceof ApiError || e instanceof Error
? e.message
: 'ثبت مشتری ناموفق بود.'
} finally {
saving.value = false
}
}
</script>
<template>
<div class="page customer-create-page">
<PageHeader title="افزودن مشتری" show-back centered @back="goBack" />
<div class="page__scroll customer-create-scroll">
<p class="intro">
مشتری به لیست شما اضافه می‌شود و بعد از ورود با همین شماره، سفارش‌هایش به حساب شما وصل می‌ماند.
</p>
<FormErrorBanner :message="formError" />
<div class="form-card">
<BaseInput
v-model="firstName"
label="نام"
placeholder="نام مشتری"
:error="fieldErrors.firstName"
@update:model-value="clearField('firstName')"
/>
<BaseInput
v-model="lastName"
label="نام خانوادگی"
placeholder="نام خانوادگی"
:error="fieldErrors.lastName"
@update:model-value="clearField('lastName')"
/>
<BaseInput
v-model="phone"
label="شماره موبایل"
type="tel"
inputmode="tel"
dir="ltr"
placeholder="۰۹۱۲۳۴۵۶۷۸۹"
:error="fieldErrors.phone"
@update:model-value="clearField('phone')"
/>
<BaseInput
v-model="businessName"
label="نام فروشگاه یا برند (اختیاری)"
placeholder="مثلاً بوتیک رز"
/>
</div>
</div>
<footer class="footer">
<BaseButton block :loading="saving" :disabled="saving" @click="submit">
ثبت مشتری
</BaseButton>
</footer>
</div>
</template>
<style scoped>
.customer-create-page {
background: var(--color-bg);
}
.customer-create-scroll {
padding: 8px var(--page-padding) 100px;
display: flex;
flex-direction: column;
gap: 12px;
}
.intro {
margin: 0;
color: var(--color-text-muted);
font-size: 0.875rem;
line-height: 1.7;
}
.form-card {
background: var(--color-surface);
border-radius: var(--radius-md);
padding: 16px;
box-shadow: var(--shadow-sm);
}
.footer {
position: sticky;
bottom: 0;
padding: 12px var(--page-padding) calc(12px + env(safe-area-inset-bottom, 0px));
background: color-mix(in srgb, var(--color-bg) 92%, transparent);
}
</style>
+467 -229
View File
@@ -7,6 +7,7 @@ import type {
CrmCustomer,
CrmMeeting,
CrmOrderSummary,
CustomerStatus,
CustomerTab,
MeetingInteractionType,
} from '@/types/crm'
@@ -21,6 +22,7 @@ import IconClipboardEmpty from '@/components/icons/IconClipboardEmpty.vue'
import IconPhone from '@/components/icons/IconPhone.vue'
import IconMail from '@/components/icons/IconMail.vue'
import IconEditPen from '@/components/icons/IconEditPen.vue'
import MeetingReportOverlay from '@/components/customers/MeetingReportOverlay.vue'
const route = useRoute()
const router = useRouter()
@@ -28,6 +30,7 @@ const crm = useCrmStore()
const auth = useAuthStore()
const id = computed(() => String(route.query.id ?? ''))
const tab = computed(() => (String(route.query.tab ?? 'info') as CustomerTab))
const showMeetingReport = computed(() => route.query.meetingReport === '1')
const customer = ref<CrmCustomer | null>(null)
const orders = ref<CrmOrderSummary[]>([])
const meetings = ref<CrmMeeting[]>([])
@@ -69,6 +72,25 @@ function setTab(next: CustomerTab) {
router.replace({ path: '/customer', query: { id: id.value, tab: next } })
}
function openMeetingReport() {
router.push({
path: '/customer',
query: { id: id.value, tab: 'reports', meetingReport: '1' },
})
}
function closeMeetingReport() {
router.replace({
path: '/customer',
query: { id: id.value, tab: 'reports' },
})
}
async function onMeetingReportSuccess() {
closeMeetingReport()
await load()
}
function typeLabel(type: MeetingInteractionType) {
if (type === 'in_person') return 'ملاقات حضوری'
if (type === 'call') return 'تماس'
@@ -117,6 +139,20 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
if (variant === 'warning' || variant === 'danger') return 'warning'
return order.statusTone || 'info'
}
const recentOrders = computed(() => orders.value.slice(0, 3))
function customerStatusLabel(status: CustomerStatus) {
if (status === 'active') return 'فعال'
if (status === 'inactive') return 'غیرفعال'
return 'جدید'
}
function customerStatusClass(status: CustomerStatus) {
if (status === 'active') return 'status-badge--active'
if (status === 'inactive') return 'status-badge--inactive'
return 'status-badge--new'
}
</script>
<template>
@@ -133,22 +169,24 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
</PageHeader>
<div class="page__scroll page__scroll--with-nav customer-scroll">
<section v-if="tab !== 'reports'" class="profile-row">
<div class="avatar" :style="{ background: customer.avatarColor }">
<section v-if="tab !== 'reports'" class="profile-card">
<div class="profile-card__avatar" :style="{ background: customer.avatarColor }">
{{ customer.avatarLetter }}
</div>
<div>
<div class="name-row">
<div class="profile-card__info">
<div class="profile-card__name-row">
<h2>{{ customer.name }}</h2>
<span class="badge">فعال</span>
<span class="status-badge" :class="customerStatusClass(customer.status)">
{{ customerStatusLabel(customer.status) }}
</span>
</div>
<p dir="ltr">{{ customer.phone }}</p>
<p class="profile-card__phone" dir="ltr">{{ customer.phone }}</p>
</div>
</section>
<section v-else class="profile-banner">
<div class="profile-banner__top">
<div class="avatar avatar--lg" :style="{ background: customer.avatarColor }">
<div class="profile-card__avatar profile-card__avatar--lg" :style="{ background: customer.avatarColor }">
{{ customer.avatarLetter }}
</div>
<div>
@@ -156,7 +194,7 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
<p>{{ customer.contactName }}</p>
<p dir="ltr">{{ customer.phone }}</p>
</div>
<span class="badge badge--light">مشتری فعال</span>
<span class="status-badge status-badge--active status-badge--on-dark">فعال</span>
</div>
<div class="profile-banner__stats">
<div>
@@ -174,54 +212,85 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
</div>
</section>
<div class="tabs">
<div class="tabs" role="tablist">
<button
v-for="item in tabs"
:key="item.id"
type="button"
role="tab"
class="tab"
:class="{ 'tab--active': tab === item.id }"
:aria-selected="tab === item.id"
@click="setTab(item.id)"
>
{{ item.label }}
<span class="tab__text">{{ item.label }}</span>
</button>
</div>
<template v-if="tab === 'info'">
<section class="stats">
<h3>اطلاعات خرید</h3>
<div class="stats__grid">
<div>
<span>تعداد سفارش</span>
<strong>{{ formatNumber(customer.orderCount) }}</strong>
<section class="purchase-card">
<h3 class="section-title">اطلاعات خرید</h3>
<div class="purchase-stats">
<div class="purchase-stats__item">
<span class="purchase-stats__label">تعداد سفارش</span>
<strong class="purchase-stats__value">{{ formatNumber(customer.orderCount) }}</strong>
</div>
<div>
<span>مجموع خرید</span>
<strong>{{ formatNumber(customer.totalPurchaseToman) }}</strong>
<em>تومان</em>
<span class="purchase-stats__divider" aria-hidden="true" />
<div class="purchase-stats__item">
<span class="purchase-stats__label">مجموع خرید</span>
<strong class="purchase-stats__value">{{ formatNumber(customer.totalPurchaseToman) }}</strong>
<em class="purchase-stats__unit">تومان</em>
</div>
<div>
<span>میانگین هر سفارش</span>
<strong>{{ formatNumber(customer.averageOrderToman) }}</strong>
<em>تومان</em>
<span class="purchase-stats__divider" aria-hidden="true" />
<div class="purchase-stats__item">
<span class="purchase-stats__label">میانگین هر سفارش</span>
<strong class="purchase-stats__value">{{ formatNumber(customer.averageOrderToman) }}</strong>
<em class="purchase-stats__unit">تومان</em>
</div>
</div>
</section>
<button type="button" class="agent-card" @click="router.push('/profile')">
<div class="agent-card__avatar">{{ agentInitial }}</div>
<div>
<strong>{{ agentName }}</strong>
<p>کد واسطه: {{ agentCode }}</p>
</div>
<IconChevronLeft :size="18" />
</button>
<section class="panel-card">
<button type="button" class="agent-row" @click="router.push('/profile')">
<div class="agent-row__avatar">{{ agentInitial }}</div>
<div class="agent-row__info">
<strong>{{ agentName }}</strong>
<p>کد واسطه: {{ agentCode }}</p>
</div>
<IconChevronLeft :size="18" class="agent-row__chevron" aria-hidden="true" />
</button>
<section>
<div class="section-head">
<h3>آخرین سفارش‌ها</h3>
<button type="button" class="link" @click="setTab('orders')">مشاهده همه</button>
<div class="orders-block">
<div class="orders-block__head">
<h3 class="section-title">آخرین سفارش‌ها</h3>
<button type="button" class="link-btn" @click="setTab('orders')">مشاهده همه</button>
</div>
<article
v-for="order in recentOrders"
:key="order.id"
class="order-row"
role="button"
tabindex="0"
@click="openOrder(order.id)"
@keydown.enter="openOrder(order.id)"
>
<div class="order-row__title">
<strong>{{ order.title }}</strong>
<IconChevronLeft :size="14" class="order-row__chevron" aria-hidden="true" />
</div>
<span class="order-row__amount">{{ formatNumber(order.amountToman) }} تومان</span>
<span class="status-pill" :class="`status-pill--${orderStatusTone(order)}`">
{{ orderStatusLabel(order) }}
</span>
</article>
<p v-if="!recentOrders.length" class="empty-inline">سفارشی ثبت نشده است.</p>
</div>
</section>
</template>
<template v-else-if="tab === 'orders'">
<section class="panel-card panel-card--list">
<article
v-for="order in orders"
:key="order.id"
@@ -231,74 +300,58 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
@click="openOrder(order.id)"
@keydown.enter="openOrder(order.id)"
>
<div>
<div class="order-row__title">
<strong>{{ order.title }}</strong>
<p>{{ formatNumber(order.amountToman) }} تومان</p>
<IconChevronLeft :size="14" class="order-row__chevron" aria-hidden="true" />
</div>
<span class="tone" :class="`tone--${orderStatusTone(order)}`">{{ orderStatusLabel(order) }}</span>
<IconChevronLeft :size="16" class="order-row__chevron" />
<span class="order-row__amount">{{ formatNumber(order.amountToman) }} تومان</span>
<span class="status-pill" :class="`status-pill--${orderStatusTone(order)}`">
{{ orderStatusLabel(order) }}
</span>
</article>
<p v-if="!orders.length" class="empty-inline">سفارشی ثبت نشده است.</p>
</section>
</template>
<template v-else-if="tab === 'orders'">
<article
v-for="order in orders"
:key="order.id"
class="order-row"
role="button"
tabindex="0"
@click="openOrder(order.id)"
@keydown.enter="openOrder(order.id)"
>
<div>
<strong>{{ order.title }}</strong>
<p>{{ order.date }} — {{ formatNumber(order.amountToman) }} تومان</p>
</div>
<span class="tone" :class="`tone--${orderStatusTone(order)}`">{{ orderStatusLabel(order) }}</span>
<IconChevronLeft :size="16" class="order-row__chevron" />
</article>
<p v-if="!orders.length" class="empty-inline">سفارشی ثبت نشده است.</p>
</template>
<template v-else>
<h3 class="block-title">تعاملات و پیگیری‌ها</h3>
<button
type="button"
class="dashed-btn"
@click="router.push({ path: '/meeting-report', query: { customerId: customer.id } })"
>
+ ثبت گزارش ملاقات جدید
</button>
<div class="meeting-filters">
<section class="panel-card panel-card--reports">
<h3 class="section-title">تعاملات و پیگیری‌ها</h3>
<button
v-for="chip in meetingFilters"
:key="chip.id"
type="button"
class="meeting-filter"
:class="{ 'meeting-filter--active': meetingFilter === chip.id }"
@click="meetingFilter = chip.id"
class="dashed-btn"
@click="openMeetingReport"
>
{{ chip.label }}
+ ثبت گزارش ملاقات جدید
</button>
</div>
<article v-for="item in filteredMeetings" :key="item.id" class="meeting-row">
<div class="meeting-row__icon" :class="meetingIconClass(item.type)">
<IconClipboardEmpty v-if="item.type === 'in_person'" :size="16" />
<IconPhone v-else-if="item.type === 'call'" :size="16" />
<IconMail v-else-if="item.type === 'message'" :size="16" />
<IconEditPen v-else :size="16" />
<div class="meeting-filters">
<button
v-for="chip in meetingFilters"
:key="chip.id"
type="button"
class="meeting-filter"
:class="{ 'meeting-filter--active': meetingFilter === chip.id }"
@click="meetingFilter = chip.id"
>
{{ chip.label }}
</button>
</div>
<div class="meeting-row__body">
<strong>{{ typeLabel(item.type) }}</strong>
<p>{{ item.description || item.title }}</p>
</div>
<span>{{ item.date }}</span>
</article>
<p v-if="!filteredMeetings.length" class="empty-inline">
گزارشی برای این فیلتر ثبت نشده است.
</p>
<article v-for="item in filteredMeetings" :key="item.id" class="meeting-row">
<div class="meeting-row__icon" :class="meetingIconClass(item.type)">
<IconClipboardEmpty v-if="item.type === 'in_person'" :size="16" />
<IconPhone v-else-if="item.type === 'call'" :size="16" />
<IconMail v-else-if="item.type === 'message'" :size="16" />
<IconEditPen v-else :size="16" />
</div>
<div class="meeting-row__body">
<strong>{{ typeLabel(item.type) }}</strong>
<p>{{ item.description || item.title }}</p>
</div>
<span class="meeting-row__date">{{ item.date }}</span>
</article>
<p v-if="!filteredMeetings.length" class="empty-inline">
گزارشی برای این فیلتر ثبت نشده است.
</p>
</section>
</template>
</div>
@@ -317,6 +370,13 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
<div v-else class="page empty">
<p>{{ error || 'مشتری یافت نشد.' }}</p>
</div>
<MeetingReportOverlay
:open="showMeetingReport"
:customer-id="id"
@close="closeMeetingReport"
@success="onMeetingReportSuccess"
/>
</template>
<style scoped>
@@ -325,62 +385,100 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
}
.customer-scroll {
padding: 8px var(--page-padding) 100px;
padding: 0 var(--page-padding) 100px;
display: flex;
flex-direction: column;
gap: 14px;
gap: 0;
}
.profile-row {
.profile-card,
.purchase-card,
.panel-card {
background: transparent;
border-radius: 0;
box-shadow: none;
}
.profile-card {
display: flex;
gap: 12px;
align-items: center;
gap: 14px;
padding: 12px 0 16px;
border-bottom: 1px solid var(--color-border);
}
.avatar {
width: 64px;
height: 64px;
.profile-card__avatar {
width: 56px;
height: 56px;
border-radius: 999px;
display: grid;
place-items: center;
font-size: 1.4rem;
font-size: 1.25rem;
font-weight: 700;
color: #111;
flex-shrink: 0;
}
.avatar--lg {
.profile-card__avatar--lg {
width: 56px;
height: 56px;
}
.name-row {
.profile-card__info {
min-width: 0;
flex: 1;
}
.profile-card__name-row {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 8px;
}
.name-row h2,
.profile-card__name-row h2,
.profile-banner h2 {
margin: 0;
font-size: 1.1rem;
}
.profile-row p,
.profile-banner p {
margin: 2px 0 0;
color: var(--color-text-muted);
font-size: 0.85rem;
}
.badge {
background: var(--color-primary-soft);
color: var(--color-primary);
border-radius: 999px;
padding: 2px 10px;
font-size: 0.75rem;
font-size: 1.05rem;
font-weight: 700;
line-height: 1.35;
}
.badge--light {
.profile-card__phone,
.profile-banner p {
margin: 6px 0 0;
color: var(--color-text-muted);
font-size: 0.875rem;
line-height: 1.4;
}
.status-badge {
display: inline-flex;
align-items: center;
height: 24px;
padding: 0 10px;
border-radius: 6px;
font-size: 0.75rem;
font-weight: 600;
white-space: nowrap;
}
.status-badge--active {
background: var(--color-success-light);
color: var(--color-success);
}
.status-badge--inactive {
background: #f3f4f6;
color: #6b7280;
}
.status-badge--new {
background: #ede9fe;
color: #6d28d9;
}
.status-badge--on-dark {
background: rgba(255, 255, 255, 0.2);
color: #fff;
}
@@ -390,6 +488,7 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
color: #fff;
border-radius: var(--radius-lg);
padding: 16px;
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 6px 16px rgba(16, 24, 40, 0.05);
}
.profile-banner__top {
@@ -422,169 +521,269 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
.tabs {
display: flex;
gap: 12px;
width: 100%;
align-items: stretch;
border-bottom: 1px solid var(--color-border);
overflow-x: auto;
}
.tab {
border: none;
background: transparent;
padding: 10px 2px;
color: var(--color-text-muted);
white-space: nowrap;
flex: 1;
display: flex;
justify-content: center;
align-items: flex-end;
min-width: 0;
appearance: none;
-webkit-appearance: none;
border: 0;
background: none;
padding: 10px 0 0;
color: #9ca3af;
font-family: inherit;
font-size: 0.875rem;
font-weight: 400;
line-height: 1.5;
cursor: pointer;
}
.tab__text {
position: relative;
display: inline-block;
padding-bottom: 11px;
white-space: nowrap;
}
.tab--active {
color: var(--color-text);
font-weight: 700;
box-shadow: inset 0 -3px 0 var(--color-text);
}
.stats,
.agent-card,
.order-row,
.meeting-row {
background: var(--color-surface);
border-radius: var(--radius-md);
padding: 14px;
box-shadow: var(--shadow-sm);
.tab--active .tab__text::after {
content: '';
position: absolute;
inset-inline: 0;
bottom: -1px;
height: 3px;
background: var(--color-primary);
border-radius: 2px 2px 0 0;
}
.stats h3,
.section-head h3,
.block-title {
margin: 0 0 10px;
.section-title {
margin: 0;
font-size: 0.95rem;
font-weight: 700;
color: var(--color-text);
}
.stats__grid {
.purchase-card {
padding: 16px 0;
border-bottom: 1px solid var(--color-border);
}
.purchase-stats {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
text-align: center;
grid-template-columns: 1fr 1px 1fr 1px 1fr;
align-items: stretch;
gap: 0;
margin-top: 14px;
}
.stats__grid span,
.stats__grid em {
display: block;
font-size: 0.7rem;
.purchase-stats__item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
gap: 4px;
padding: 0 6px;
text-align: center;
min-width: 0;
}
.purchase-stats__divider {
width: 1px;
background: var(--color-border);
align-self: stretch;
}
.purchase-stats__label {
font-size: 0.72rem;
color: var(--color-text-muted);
line-height: 1.4;
}
.purchase-stats__value {
font-size: 1rem;
font-weight: 700;
line-height: 1.25;
color: var(--color-text);
}
.purchase-stats__unit {
font-size: 0.72rem;
color: var(--color-text-muted);
font-style: normal;
line-height: 1.3;
}
.stats__grid strong {
display: block;
margin: 4px 0;
font-size: 0.9rem;
.panel-card {
padding: 0 0 8px;
overflow: hidden;
}
.agent-card {
width: 100%;
border: none;
display: grid;
grid-template-columns: 44px 1fr 20px;
gap: 10px;
.panel-card--list,
.panel-card--reports {
padding: 16px 0;
}
.agent-row {
display: flex;
align-items: center;
gap: 12px;
width: 100%;
padding: 16px 0;
border: none;
border-bottom: 1px solid var(--color-border);
background: transparent;
text-align: start;
cursor: pointer;
font-family: inherit;
}
.agent-card__avatar {
.agent-row__avatar {
width: 44px;
height: 44px;
border-radius: 999px;
background: var(--color-primary-soft);
background: #e8ecea;
color: var(--color-primary);
display: grid;
place-items: center;
font-weight: 700;
}
.agent-card p {
margin: 2px 0 0;
color: var(--color-text-muted);
font-size: 0.78rem;
}
.section-head {
display: flex;
justify-content: space-between;
align-items: center;
}
.link {
border: none;
background: transparent;
color: var(--color-info);
cursor: pointer;
font-size: 0.8rem;
}
.order-row,
.meeting-row {
display: flex;
justify-content: space-between;
gap: 10px;
align-items: center;
margin-bottom: 8px;
}
.order-row {
cursor: pointer;
}
.order-row__chevron {
color: var(--color-text-muted);
flex-shrink: 0;
}
.meeting-row__body {
.agent-row__info {
flex: 1;
min-width: 0;
}
.order-row p,
.meeting-row p {
margin: 2px 0 0;
.agent-row__info strong {
display: block;
font-size: 0.9rem;
font-weight: 700;
}
.agent-row__info p {
margin: 4px 0 0;
color: var(--color-text-muted);
font-size: 0.78rem;
}
.agent-row__chevron {
color: #9ca3af;
flex-shrink: 0;
}
.orders-block {
padding: 14px 0 8px;
}
.orders-block__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 4px;
}
.link-btn {
border: none;
background: transparent;
color: #6b7280;
font-size: 0.8rem;
font-weight: 500;
cursor: pointer;
padding: 0;
font-family: inherit;
}
.order-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto auto;
gap: 10px;
align-items: center;
padding: 14px 0;
border-bottom: 1px solid var(--color-border);
cursor: pointer;
font-family: inherit;
}
.panel-card--list .order-row:last-of-type,
.orders-block .order-row:last-of-type {
border-bottom: none;
}
.order-row__title {
display: inline-flex;
align-items: center;
gap: 4px;
min-width: 0;
}
.order-row__title strong {
font-size: 0.875rem;
font-weight: 600;
line-height: 1.35;
}
.order-row__chevron {
color: #9ca3af;
flex-shrink: 0;
}
.order-row__amount {
font-size: 0.8125rem;
font-weight: 600;
color: var(--color-text);
white-space: nowrap;
}
.status-pill {
display: inline-flex;
align-items: center;
justify-content: center;
min-height: 26px;
padding: 0 10px;
border-radius: 6px;
font-size: 0.7rem;
font-weight: 600;
white-space: nowrap;
}
.status-pill--warning {
background: #fff4e5;
color: #c27803;
}
.status-pill--success {
background: var(--color-success-light);
color: var(--color-success);
}
.status-pill--info {
background: var(--color-info-light);
color: var(--color-info);
}
.empty-inline {
margin: 8px 0 0;
margin: 12px 0 4px;
text-align: center;
color: var(--color-text-muted);
font-size: 0.85rem;
}
.tone {
border-radius: 999px;
padding: 4px 10px;
font-size: 0.72rem;
white-space: nowrap;
}
.tone--warning {
background: var(--color-warning-light);
color: #b45309;
}
.tone--success {
background: var(--color-success-light);
color: var(--color-success);
}
.tone--info {
background: var(--color-info-light);
color: var(--color-info);
}
.edit-btn {
border: none;
background: transparent;
color: var(--color-info, #2563eb);
color: #3b82f6;
font-size: 0.875rem;
font-weight: 600;
cursor: pointer;
@@ -596,19 +795,20 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
display: flex;
gap: 8px;
overflow-x: auto;
margin-bottom: 8px;
margin: 12px 0 8px;
padding-bottom: 4px;
}
.meeting-filter {
border: 1px solid var(--color-border);
background: var(--color-surface);
border-radius: 999px;
background: var(--color-bg);
border-radius: 6px;
padding: 6px 12px;
font-size: 0.72rem;
color: var(--color-text-muted);
cursor: pointer;
white-space: nowrap;
font-family: inherit;
}
.meeting-filter--active {
@@ -627,7 +827,20 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
padding: 14px;
font-weight: 700;
cursor: pointer;
margin-bottom: 8px;
margin-top: 12px;
font-family: inherit;
}
.meeting-row {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 0;
border-bottom: 1px solid var(--color-border);
}
.meeting-row:last-of-type {
border-bottom: none;
}
.meeting-row__icon {
@@ -654,11 +867,34 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
color: #6d28d9;
}
.meeting-row__body {
flex: 1;
min-width: 0;
}
.meeting-row__body strong {
display: block;
font-size: 0.875rem;
}
.meeting-row__body p {
margin: 4px 0 0;
color: var(--color-text-muted);
font-size: 0.78rem;
line-height: 1.45;
}
.meeting-row__date {
font-size: 0.75rem;
color: var(--color-text-muted);
flex-shrink: 0;
}
.cta-bar {
position: sticky;
bottom: 0;
padding: 12px var(--page-padding) calc(12px + env(safe-area-inset-bottom, 0));
background: linear-gradient(transparent, var(--color-bg) 30%);
background: linear-gradient(180deg, rgba(238, 241, 240, 0) 0%, var(--color-bg) 32%);
}
.cta {
@@ -668,12 +904,14 @@ function orderStatusTone(order: CrmOrderSummary): 'warning' | 'success' | 'info'
border-radius: 14px;
background: var(--color-primary);
color: #fff;
font-size: 0.95rem;
font-weight: 700;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
font-family: inherit;
}
.empty {
+30 -1
View File
@@ -11,6 +11,7 @@ import IconFilter from '@/components/icons/IconFilter.vue'
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
import IconPlus from '@/components/icons/IconPlus.vue'
import IconProfile from '@/components/icons/IconProfile.vue'
import CustomerCreateOverlay from '@/components/customers/CustomerCreateOverlay.vue'
const route = useRoute()
const router = useRouter()
@@ -22,6 +23,7 @@ const loading = ref(true)
const error = ref<string | null>(null)
const filtered = computed(() => customers.value)
const showCreate = computed(() => route.query.create === '1')
const activeCount = computed(() => customers.value.filter((c) => c.status === 'active').length)
const totalPurchase = computed(() =>
@@ -81,8 +83,29 @@ function statusLabel(s: CustomerStatus) {
return 'جدید'
}
function customerListQuery(extra: Record<string, string> = {}) {
return {
...(status.value !== 'all' ? { status: status.value } : {}),
...(q.value ? { q: q.value } : {}),
...extra,
}
}
function onRegisterCustomer() {
router.push('/customers/new')
router.push({ path: '/customers', query: { ...customerListQuery(), create: '1' } })
}
function closeCreate() {
router.replace({ path: '/customers', query: customerListQuery() })
}
async function onCustomerCreated(id?: string) {
if (id) {
await router.replace({ path: '/customer', query: { id } })
return
}
closeCreate()
await load()
}
function cycleStatusFilter() {
@@ -196,6 +219,12 @@ function cycleStatusFilter() {
</div>
</section>
</div>
<CustomerCreateOverlay
:open="showCreate"
@close="closeCreate"
@success="onCustomerCreated"
/>
</div>
</template>
+91 -30
View File
@@ -468,10 +468,6 @@ async function toggleFavorite(productId: string) {
padding-bottom: 8px;
}
.home-scroll > .section:last-child {
margin-top: auto;
}
.hero-banner {
--hero-image: #8a7a65;
position: relative;
@@ -515,6 +511,11 @@ async function toggleFavorite(productId: string) {
.hero-banner__content {
position: relative;
z-index: 1;
width: auto;
max-width: 100%;
min-width: 0;
box-sizing: border-box;
text-align: start;
animation: hero-copy 0.7s cubic-bezier(0.22, 1, 0.36, 1) both;
}
@@ -592,6 +593,7 @@ async function toggleFavorite(productId: string) {
line-height: 1.7;
max-width: 16rem;
font-weight: 700;
overflow-wrap: break-word;
}
.hero-banner__title span {
@@ -644,7 +646,7 @@ async function toggleFavorite(productId: string) {
.quick-card {
border: none;
background: transparent;
background: var(--color-surface);
border-radius: var(--radius-md);
padding: 12px 6px;
display: flex;
@@ -652,7 +654,7 @@ async function toggleFavorite(productId: string) {
align-items: center;
gap: 8px;
cursor: pointer;
box-shadow: none;
box-shadow: 0 2px 10px rgba(16, 24, 40, 0.06);
}
.quick-card__icon {
@@ -661,8 +663,9 @@ async function toggleFavorite(productId: string) {
border-radius: 12px;
display: grid;
place-items: center;
background: var(--color-surface);
box-shadow: 0 2px 12px rgba(16, 24, 40, 0.06);
background: transparent;
box-shadow: none;
border: none;
}
.quick-card__image {
@@ -683,8 +686,8 @@ async function toggleFavorite(productId: string) {
background: var(--color-surface);
border-radius: 18px;
padding: 16px;
box-shadow: 0 2px 12px rgba(16, 24, 40, 0.06);
border: 1px solid rgba(230, 232, 230, 0.9);
box-shadow: none;
border: 1px solid var(--color-border);
}
.level-card__top {
@@ -819,7 +822,8 @@ async function toggleFavorite(productId: string) {
text-align: start;
cursor: pointer;
overflow: hidden;
box-shadow: 0 2px 10px rgba(16, 24, 40, 0.05);
box-shadow: none;
border: 1px solid var(--color-border);
scroll-snap-align: start;
}
@@ -912,7 +916,11 @@ async function toggleFavorite(productId: string) {
@media (min-width: 768px) {
.home-page {
padding-top: 8px;
max-width: var(--content-max-width);
margin-inline: auto;
width: 100%;
padding: 8px var(--page-padding) 0;
box-sizing: border-box;
}
.home-desk-head {
@@ -938,38 +946,87 @@ async function toggleFavorite(productId: string) {
}
.home-scroll {
gap: 22px;
padding: 8px 0 24px;
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
grid-template-areas:
'hero hero level'
'quick quick quick'
'products products products';
gap: 18px 20px;
padding: 8px 0 28px;
align-items: stretch;
min-width: 0;
}
.hero-banner {
min-height: 220px;
grid-area: hero;
width: 100%;
max-width: none;
min-width: 0;
min-height: 0;
height: auto;
aspect-ratio: 2.1 / 1;
align-self: start;
border-radius: 20px;
}
.hero-slide {
padding: 28px 32px;
}
.hero-banner__title {
font-size: 1.35rem;
max-width: 22rem;
.level-card {
grid-area: level;
padding: 20px 22px;
align-self: stretch;
display: flex;
flex-direction: column;
justify-content: center;
}
.quick-grid {
grid-area: quick;
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 14px;
}
.home-scroll > .section {
grid-area: products;
}
.hero-slide {
width: 100%;
box-sizing: border-box;
align-items: flex-end;
justify-content: flex-start;
padding-inline-start: 36px;
padding-inline-end: 28px;
padding-block: 24px;
}
.hero-slide__media {
background-size: cover;
background-position: left center;
background-color: #8a7a65;
}
.hero-banner__content {
max-width: min(52%, 22rem);
}
.hero-banner__title {
font-size: 1.15rem;
max-width: none;
}
.hero-next-enter-from,
.hero-next-leave-to,
.hero-prev-enter-from,
.hero-prev-leave-to {
transform: none;
}
.quick-card {
min-height: 96px;
padding: 18px 14px;
border-radius: 16px;
}
.level-card {
padding: 20px 22px;
box-shadow: 0 3px 12px rgba(16, 24, 40, 0.07);
}
.product-row {
@@ -993,12 +1050,16 @@ async function toggleFavorite(productId: string) {
}
@media (min-width: 1200px) {
.hero-banner {
aspect-ratio: 2.2 / 1;
}
.hero-banner__title {
font-size: 1.25rem;
}
.product-row {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.hero-banner {
min-height: 240px;
}
}
</style>
+25 -17
View File
@@ -11,7 +11,7 @@ import { formatNumber } from '@/utils/format'
import { colorImageAt, colorSwatchStyle } from '@/utils/colorSwatch'
import IconChevronBack from '@/components/icons/IconChevronBack.vue'
import IconCart from '@/components/icons/IconCart.vue'
import IconCatalog from '@/components/icons/IconCatalog.vue'
import quickCatalogRequestIcon from '@/assets/images/quick-catalog-request.webp'
import IconHeart from '@/components/icons/IconHeart.vue'
import IconShare from '@/components/icons/IconShare.vue'
import IconShoppingBag from '@/components/icons/IconShoppingBag.vue'
@@ -379,7 +379,7 @@ function shareProduct() {
<p class="min-note">حداقل سفارش: {{ formatNumber(minRolls) }} طاقه</p>
</div>
<button type="button" class="btn-secondary" @click="router.push({ path: '/catalog-request', query: { productId: product.id } })">
<IconCatalog :size="18" />
<img class="btn-secondary__icon" :src="quickCatalogRequestIcon" alt="" aria-hidden="true" />
درخواست کالیته
</button>
</footer>
@@ -400,7 +400,7 @@ function shareProduct() {
--pd-card-border: 1px solid #eeeef1;
--pd-card-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 6px 16px rgba(16, 24, 40, 0.05);
--pd-card-radius: 16px;
background: #fff;
background: var(--color-surface);
display: flex;
flex-direction: column;
min-height: 100%;
@@ -446,7 +446,7 @@ function shareProduct() {
inset-inline-start: 12px;
background: rgba(0, 0, 0, 0.55);
color: #fff;
border-radius: 999px;
border-radius: 6px;
padding: 4px 10px;
font-size: 0.75rem;
}
@@ -457,7 +457,7 @@ function shareProduct() {
margin: 12px var(--page-padding) 0;
padding: 10px;
overflow-x: auto;
background: #fff;
background: var(--color-surface);
border: var(--pd-card-border);
border-radius: var(--pd-card-radius);
box-shadow: var(--pd-card-shadow);
@@ -470,7 +470,7 @@ function shareProduct() {
height: 52px;
border-radius: 14px;
border: var(--pd-card-border);
background: #fff;
background: var(--color-surface);
box-shadow: var(--pd-card-shadow);
cursor: pointer;
flex-shrink: 0;
@@ -499,7 +499,7 @@ function shareProduct() {
display: flex;
flex-direction: column;
gap: 18px;
background: #fff;
background: var(--color-surface);
}
.title-row {
@@ -541,7 +541,7 @@ function shareProduct() {
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 6px;
padding: 14px 8px;
background: #fff;
background: var(--color-surface);
border: var(--pd-card-border);
border-radius: var(--pd-card-radius);
box-shadow: var(--pd-card-shadow);
@@ -631,7 +631,7 @@ function shareProduct() {
.pd-section--boxed {
padding: 12px 14px;
background: #fff;
background: var(--color-surface);
border: var(--pd-card-border);
border-radius: var(--pd-card-radius);
box-shadow: var(--pd-card-shadow);
@@ -704,8 +704,8 @@ button.tag {
min-height: 32px;
padding: 0 16px;
border: var(--pd-card-border);
border-radius: var(--pd-card-radius);
background: #fff;
border-radius: 6px;
background: var(--color-surface);
color: #6b7280;
font-size: 0.8125rem;
font-weight: 400;
@@ -726,7 +726,7 @@ button.tag--link {
}
button.tag--link:hover {
background: #fff;
background: var(--color-surface);
}
.roll-field {
@@ -734,11 +734,11 @@ button.tag--link:hover {
align-items: center;
width: fit-content;
max-width: 100%;
border: 1px solid #eeeef1;
border-radius: 16px;
background: #fff;
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 6px 16px rgba(16, 24, 40, 0.05);
padding: 14px 16px;
border: var(--pd-card-border);
border-radius: 6px;
background: var(--color-surface);
box-shadow: var(--pd-card-shadow);
padding: 0 16px;
color: #6b7280;
font-size: 0.9rem;
}
@@ -780,6 +780,14 @@ button.tag--link:hover {
color: var(--color-primary);
}
.btn-secondary__icon {
width: 18px;
height: 18px;
display: block;
object-fit: contain;
flex-shrink: 0;
}
.btn-primary {
border: none;
background: var(--color-accent-navy);
+211 -34
View File
@@ -9,8 +9,19 @@ import SearchField from '@/components/ui/SearchField.vue'
import IconFilter from '@/components/icons/IconFilter.vue'
import IconChevronLeft from '@/components/icons/IconChevronLeft.vue'
import IconHeart from '@/components/icons/IconHeart.vue'
import IconCatalog from '@/components/icons/IconCatalog.vue'
import IconDiamond from '@/components/icons/IconDiamond.vue'
import { useFavoritesStore } from '@/stores/favorites'
import { colorSwatchStyle } from '@/utils/colorSwatch'
import { categoryIconOf, usageCategoryIconKey } from '@/utils/categoryIcons'
const CATEGORY_TAGLINES: Record<string, string> = {
suit: 'شیک، بادوام و رسمی',
trousers: 'مناسب شلوار و مصارف روزانه',
shirt: 'سبک و مناسب پیراهن',
}
const GENERIC_CATEGORY_CTA = 'مشاهده محصولات'
const route = useRoute()
const router = useRouter()
@@ -43,15 +54,81 @@ const title = computed(() => {
return 'همه محصولات'
})
const bannerSubtitle = computed(() => {
if (catalogId.value) return 'انتخاب محصول از کالیته'
if (categoryId.value === 'suit') return 'شیک، بادوام و رسمی'
if (categoryId.value === 'trousers') return 'مناسب شلوار و مصارف روزانه'
if (categoryId.value === 'shirt') return 'سبک و مناسب پیراهن'
if (season.value) return 'فیلتر شده بر اساس فصل'
return 'مشاهده و ثبت سفارش'
function splitCategoryTitle(fullTitle: string) {
const trimmed = fullTitle.trim()
const prefix = 'پارچه‌های '
if (trimmed.startsWith(prefix)) {
return { lead: 'پارچه‌های', headline: trimmed.slice(prefix.length) }
}
return { lead: undefined, headline: trimmed }
}
function seasonIconKey(item: SeasonCategory) {
return item.icon || (item.id === 'summer' ? 'bolt' : item.id === 'winter' ? 'feather' : 'diamond')
}
const bannerCopy = computed(() => {
if (categoryId.value) {
const cat = crm.usageCategories.find((c) => c.id === categoryId.value)
const parts = splitCategoryTitle(cat?.title ?? title.value)
const tagline =
CATEGORY_TAGLINES[categoryId.value] ??
(cat?.subtitle && cat.subtitle !== GENERIC_CATEGORY_CTA ? cat.subtitle : undefined)
return { ...parts, tagline }
}
if (season.value) {
const item = seasons.value.find((s) => s.id === season.value)
return {
lead: undefined,
headline: item?.title ?? title.value,
tagline: item?.description || 'فیلتر شده بر اساس فصل',
}
}
if (catalogId.value) {
return {
lead: 'کالیته',
headline: catalogTitle.value || title.value,
tagline: 'انتخاب محصول از کالیته',
}
}
return {
lead: undefined,
headline: title.value,
tagline: q.value ? 'نتایج جستجو' : 'مشاهده و ثبت سفارش',
}
})
const bannerIcon = computed(() => {
if (categoryId.value) {
const cat = crm.usageCategories.find((c) => c.id === categoryId.value)
if (cat) return categoryIconOf(usageCategoryIconKey(cat), IconCatalog)
}
if (season.value) {
const item = seasons.value.find((s) => s.id === season.value)
if (item) return categoryIconOf(seasonIconKey(item), IconDiamond)
}
return null
})
const bannerImageUrl = computed(() => {
if (categoryId.value) {
const cat = crm.usageCategories.find((c) => c.id === categoryId.value)
if (cat?.imageUrl) return cat.imageUrl
}
if (season.value) {
const item = seasons.value.find((s) => s.id === season.value)
if (item?.imageUrl) return item.imageUrl
}
if (catalogId.value) {
return products.value.find((item) => item.imageUrl)?.imageUrl
}
return undefined
})
const bannerStyle = computed(() =>
bannerImageUrl.value ? { backgroundImage: `url(${bannerImageUrl.value})` } : undefined,
)
const showSearch = computed(() => !categoryId.value && !catalogId.value)
const sortedProducts = computed(() => {
@@ -75,6 +152,10 @@ function cycleSort() {
sortMode.value = sortMode.value === 'newest' ? 'name' : 'newest'
}
const sortAriaLabel = computed(() =>
sortMode.value === 'newest' ? 'مرتب‌سازی: جدیدترین' : 'مرتب‌سازی: نام',
)
function applySearch(value: string) {
searchDraft.value = value
router.replace({
@@ -168,11 +249,18 @@ function toggleFavorite(productId: string) {
<p v-if="error">{{ error }}</p>
<PageSkeleton v-else-if="loading" variant="grid" />
<template v-else>
<section class="cat-banner">
<div class="cat-banner__icon" aria-hidden="true" />
<div>
<h2>{{ title }}</h2>
<p>{{ bannerSubtitle }}</p>
<section
class="cat-banner"
:class="{ 'cat-banner--photo': !!bannerImageUrl }"
:style="bannerStyle"
>
<div v-if="bannerIcon" class="cat-banner__icon-ring" aria-hidden="true">
<component :is="bannerIcon" :size="26" color="#ffffff" />
</div>
<div class="cat-banner__copy">
<p v-if="bannerCopy.lead" class="cat-banner__lead">{{ bannerCopy.lead }}</p>
<h2 class="cat-banner__title">{{ bannerCopy.headline }}</h2>
<p v-if="bannerCopy.tagline" class="cat-banner__tagline">{{ bannerCopy.tagline }}</p>
</div>
</section>
@@ -199,8 +287,34 @@ function toggleFavorite(productId: string) {
<div class="list-meta">
<span>تعداد محصولات: {{ sortedProducts.length.toLocaleString('fa-IR') }}</span>
<button type="button" class="list-meta__sort" @click="cycleSort">
مرتب‌سازی: {{ sortMode === 'newest' ? 'جدیدترین' : 'نام' }}
<button
type="button"
class="list-meta__sort"
:aria-label="sortAriaLabel"
@click="cycleSort"
>
<span class="list-meta__sort-icon" aria-hidden="true">
<svg width="15" height="12" viewBox="0 0 15 12" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4.5 2.5V9.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
<path
d="M2.5 7.5L4.5 9.5L6.5 7.5"
stroke="currentColor"
stroke-width="1.3"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path d="M10.5 9.5V2.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" />
<path
d="M8.5 4.5L10.5 2.5L12.5 4.5"
stroke="currentColor"
stroke-width="1.3"
stroke-linecap="round"
stroke-linejoin="round"
/>
</svg>
</span>
<span class="list-meta__sort-badge">۱</span>
<span class="list-meta__sort-label">مرتب‌سازی</span>
</button>
</div>
@@ -288,35 +402,86 @@ function toggleFavorite(productId: string) {
}
.cat-banner {
position: relative;
isolation: isolate;
display: flex;
align-items: center;
gap: 12px;
min-height: 96px;
padding: 16px;
justify-content: space-between;
gap: 16px;
min-height: 112px;
padding: 18px 20px;
border-radius: var(--radius-lg);
overflow: hidden;
background:
linear-gradient(100deg, rgba(var(--color-primary-rgb), 0.75), rgba(var(--color-primary-rgb), 0.35)),
#2f3430;
background-size: cover;
background-position: center top;
background-repeat: no-repeat;
color: #fff;
}
.cat-banner__icon {
width: 48px;
height: 48px;
border-radius: 999px;
background: rgba(255, 255, 255, 0.2);
flex-shrink: 0;
.cat-banner--photo::before {
content: '';
position: absolute;
inset: 0;
z-index: 0;
background: linear-gradient(
90deg,
rgba(0, 0, 0, 0.58) 0%,
rgba(0, 0, 0, 0.28) 42%,
rgba(0, 0, 0, 0.08) 68%,
transparent 100%
);
pointer-events: none;
}
.cat-banner h2 {
margin: 0 0 4px;
font-size: 1rem;
.cat-banner__copy {
position: relative;
z-index: 1;
min-width: 0;
flex: 1;
text-align: start;
}
.cat-banner p {
.cat-banner__lead {
margin: 0 0 2px;
font-size: 0.82rem;
font-weight: 500;
line-height: 1.4;
opacity: 0.95;
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.35);
}
.cat-banner__title {
margin: 0 0 6px;
font-size: 1.35rem;
font-weight: 700;
line-height: 1.25;
text-shadow: 0 1px 8px rgba(0, 0, 0, 0.35);
}
.cat-banner__tagline {
margin: 0;
font-size: 0.8rem;
opacity: 0.9;
font-size: 0.78rem;
font-weight: 400;
line-height: 1.5;
opacity: 0.92;
text-shadow: 0 1px 6px rgba(0, 0, 0, 0.35);
}
.cat-banner__icon-ring {
position: relative;
z-index: 1;
width: 56px;
height: 56px;
border-radius: 999px;
border: 1.5px solid rgba(255, 255, 255, 0.88);
background: rgba(0, 0, 0, 0.16);
display: grid;
place-items: center;
flex-shrink: 0;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
}
.chips {
@@ -327,9 +492,9 @@ function toggleFavorite(productId: string) {
.chip {
border: none;
border-radius: 999px;
border-radius: var(--radius-lg);
padding: 8px 14px;
background: #ecefee;
background: var(--color-bg);
color: var(--color-text);
white-space: nowrap;
cursor: pointer;
@@ -350,15 +515,27 @@ function toggleFavorite(productId: string) {
}
.list-meta__sort {
display: inline-flex;
align-items: center;
gap: 4px;
border: none;
background: transparent;
color: var(--color-primary);
color: inherit;
font: inherit;
font-weight: 700;
cursor: pointer;
padding: 0;
}
.list-meta__sort-icon {
display: inline-flex;
flex-shrink: 0;
line-height: 0;
}
.list-meta__sort-badge {
font-variant-numeric: tabular-nums;
}
.product-item {
display: grid;
grid-template-columns: 112px 1fr;
@@ -415,7 +592,7 @@ function toggleFavorite(productId: string) {
font-size: 0.65rem;
font-weight: 700;
padding: 2px 8px;
border-radius: 999px;
border-radius: 6px;
white-space: nowrap;
}
@@ -33,7 +33,7 @@ async function open(item: AppNotification) {
</script>
<template>
<ProfileSubpageLayout title="اعلان‌ها" subtitle="آخرین خبرهای سفارش‌های شما">
<ProfileSubpageLayout title="اعلان‌ها" subtitle="سفارش‌ها، پیشنهاد محصول و سایر اعلان‌ها">
<div v-if="items.length" class="notif-box">
<div class="notif-box__head">
<span class="notif-box__count">
@@ -75,7 +75,7 @@ async function open(item: AppNotification) {
v-else
icon="vuesax-linear:notification-bing"
title="اعلانی ندارید"
message="وقتی فاکتور سفارشتان صادر شود یا وضعیت چک و پرداخت تغییر کند، همین‌جا خبردار می‌شوید."
message="وقتی فاکتور سفارشتان صادر شود، پیشنهاد محصولی برایتان ثبت شود یا وضعیت پرداخت تغییر کند، همین‌جا خبردار می‌شوید."
>
<BaseButton variant="outline" @click="router.push('/orders')">سفارش‌های من</BaseButton>
</EmptyState>
@@ -129,7 +129,7 @@ async function open(item: AppNotification) {
}
.notif-row--unread {
background: var(--color-elevated);
background: var(--color-tab-inactive-bg);
}
.notif-row__icon {
@@ -93,7 +93,7 @@ const ordersCount = computed(() => profile.value?.ordersCount ?? orderStore.orde
const salesVolume = computed(() => profile.value?.salesVolumeToman ?? 0)
const profit = computed(() => profile.value?.profitToman ?? 0)
const levelProgress = computed(
() => profile.value?.levelProgress ?? auth.user?.levelProgress ?? 72,
() => profile.value?.levelProgress ?? auth.user?.levelProgress ?? 0,
)
const unreadLabel = computed(() =>
notificationStore.unreadCount > 9 ? '9+' : String(notificationStore.unreadCount),
@@ -257,9 +257,11 @@ onMounted(async () => {
</svg>
</div>
<p class="loyalty__text">
شما در سطح {{ levelLabel }} قرار دارید. تا سطح
{{ nextLevelLabel }} فقط
{{ formatNumber(pointsToNext) }} تومان فروش دیگر نیاز دارید.
<span>شما در سطح {{ levelLabel }} قرار دارید.</span>
<span class="loyalty__text-next">
تا سطح {{ nextLevelLabel }} فقط
{{ formatNumber(pointsToNext) }} تومان فروش دیگر نیاز دارید.
</span>
</p>
</div>
<div class="loyalty__bar">
@@ -605,11 +607,16 @@ onMounted(async () => {
}
.loyalty__text {
font-weight: bold;
margin: 0;
font-size: 0.8125rem;
font-size: 0.75rem;
line-height: 1.75;
color: var(--color-text);
text-align: start;
text-align: center;
}
.loyalty__text-next {
display: block;
}
.loyalty__bar {
+1 -1
View File
@@ -288,7 +288,7 @@ const chartBars = computed(() => {
.btn-outline {
border: 1px solid var(--color-border);
background: #fff;
background: var(--color-surface);
}
.btn-primary {

Some files were not shown because too many files have changed in this diff Show More