change UI & fix bugs

This commit is contained in:
2026-09-14 16:10:30 +03:30
parent 33a3828aaf
commit a349526385
78 changed files with 1214 additions and 506 deletions
+1
View File
@@ -0,0 +1 @@
import{L as e,Z as t,ct as n,rt as r,vt as i,yt as a,z as o}from"./index-BHqDDh7X.js";import{t as s}from"./BaseSwitch-DYYNQNXf.js";function c(e=480){return new Promise(t=>{window.setTimeout(t,e)})}function l(e){let t=i(null),n=i(null),r=o();async function a(i,a){if(t.value)return a;let o=!a;if(!o&&e.confirmOff&&!await e.confirmOff())return a;t.value=i,n.value=null;let s=a;try{return await e.onPersist(o),r.success(e.successMessage?.(o)??(o?`وضعیت فعال شد.`:`وضعیت غیرفعال شد.`)),o}catch{return n.value=i,r.error(e.errorMessage??`به‌روزرسانی وضعیت انجام نشد.`),s}finally{t.value=null}}function s(e){return t.value===e}function c(e){return n.value===e}return{toggle:a,isLoading:s,hasError:c}}var u=r({__name:`ActiveSwitchCell`,props:{id:{},modelValue:{type:Boolean},entityName:{},entityType:{},persist:{type:Function}},emits:[`update:modelValue`],setup(r,{emit:i}){let o=r,u=i,{toggle:d,isLoading:f,hasError:p}=l({onPersist:async e=>{o.persist?await o.persist(o.id,e):await c()},successMessage:e=>e?`${o.entityType??`مورد`} «${o.entityName}» فعال شد.`:`${o.entityType??`مورد`} «${o.entityName}» غیرفعال شد.`});async function m(t){if(t===o.modelValue)return;let n=o.entityType??`مورد`;if(!await e({title:t?`فعال‌سازی ${n}`:`غیرفعال‌سازی ${n}`,message:t?`آیا از فعال کردن «${o.entityName}» مطمئن هستید؟`:`آیا از غیرفعال کردن «${o.entityName}» مطمئن هستید؟`,tone:t?`primary`:`danger`,confirmLabel:t?`فعال کردن`:`غیرفعال کردن`,cancelLabel:`انصراف`}))return;let r=await d(o.id,o.modelValue);u(`update:modelValue`,r)}return(e,i)=>(n(),t(s,{"model-value":r.modelValue,loading:a(f)(r.id),error:a(p)(r.id),"onUpdate:modelValue":m},null,8,[`model-value`,`loading`,`error`]))}});export{u as t};
-1
View File
@@ -1 +0,0 @@
import{N as e,a as t,m as n,nt as r,ut as i,x as a}from"./http-WTIyGIuL.js";import{I as o}from"./index-BOMJTziC.js";import{t as s}from"./BaseSwitch-gjOaAbzm.js";function c(e=480){return new Promise(t=>{window.setTimeout(t,e)})}function l(e){let n=r(null),i=r(null),a=t();async function o(t,r){if(n.value)return r;let o=!r;if(!o&&e.confirmOff&&!await e.confirmOff())return r;n.value=t,i.value=null;let s=r;try{return await e.onPersist(o),a.success(e.successMessage?.(o)??(o?`وضعیت فعال شد.`:`وضعیت غیرفعال شد.`)),o}catch{return i.value=t,a.error(e.errorMessage??`به‌روزرسانی وضعیت انجام نشد.`),s}finally{n.value=null}}function s(e){return n.value===e}function c(e){return i.value===e}return{toggle:o,isLoading:s,hasError:c}}var u=a({__name:`ActiveSwitchCell`,props:{id:{},modelValue:{type:Boolean},entityName:{},entityType:{},persist:{type:Function}},emits:[`update:modelValue`],setup(t,{emit:r}){let a=t,u=r,{toggle:d,isLoading:f,hasError:p}=l({onPersist:async e=>{a.persist?await a.persist(a.id,e):await c()},successMessage:e=>e?`${a.entityType??`مورد`} «${a.entityName}» فعال شد.`:`${a.entityType??`مورد`} «${a.entityName}» غیرفعال شد.`});async function m(e){if(e===a.modelValue)return;let t=a.entityType??`مورد`;if(!await o({title:e?`فعال‌سازی ${t}`:`غیرفعال‌سازی ${t}`,message:e?`آیا از فعال کردن «${a.entityName}» مطمئن هستید؟`:`آیا از غیرفعال کردن «${a.entityName}» مطمئن هستید؟`,tone:e?`primary`:`danger`,confirmLabel:e?`فعال کردن`:`غیرفعال کردن`,cancelLabel:`انصراف`}))return;let n=await d(a.id,a.modelValue);u(`update:modelValue`,n)}return(r,a)=>(e(),n(s,{"model-value":t.modelValue,loading:i(f)(t.id),error:i(p)(t.id),"onUpdate:modelValue":m},null,8,[`model-value`,`loading`,`error`]))}});export{u as t};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,Y as n,bt as r,ct as i,o as a,rt as o,t as s}from"./index-BHqDDh7X.js";var c=[`id`,`value`,`placeholder`,`disabled`],l=t(o({__name:`AdminCurrencyInput`,props:{modelValue:{default:null},placeholder:{default:``},disabled:{type:Boolean,default:!1},id:{},inputClass:{default:`admin-input`},allowEmpty:{type:Boolean,default:!0}},emits:[`update:modelValue`],setup(t,{emit:o}){let l=t,u=o,d=n(()=>l.modelValue==null||!Number.isFinite(l.modelValue)?``:a(l.modelValue));function f(e){let t=e.target,n=s(t.value);if(!n){u(`update:modelValue`,l.allowEmpty?null:0),t.value=``;return}let r=Number(n);u(`update:modelValue`,r),t.value=a(r)}return(n,a)=>(i(),e(`input`,{id:t.id,type:`text`,inputmode:`numeric`,dir:`ltr`,class:r([`admin-currency-input`,t.inputClass]),value:d.value,placeholder:t.placeholder,disabled:t.disabled,onInput:f},null,42,c))}}),[[`__scopeId`,`data-v-56435fc8`]]);export{l as t};
-1
View File
@@ -1 +0,0 @@
import{At as e,N as t,f as n,g as r,o as i,x as a}from"./http-WTIyGIuL.js";import{o,t as s}from"./index-BOMJTziC.js";var c=[`id`,`value`,`placeholder`,`disabled`],l=i(a({__name:`AdminCurrencyInput`,props:{modelValue:{default:null},placeholder:{default:``},disabled:{type:Boolean,default:!1},id:{},inputClass:{default:`admin-input`},allowEmpty:{type:Boolean,default:!0}},emits:[`update:modelValue`],setup(i,{emit:a}){let l=i,u=a,d=n(()=>l.modelValue==null||!Number.isFinite(l.modelValue)?``:o(l.modelValue));function f(e){let t=e.target,n=s(t.value);if(!n){u(`update:modelValue`,l.allowEmpty?null:0),t.value=``;return}let r=Number(n);u(`update:modelValue`,r),t.value=o(r)}return(n,a)=>(t(),r(`input`,{id:i.id,type:`text`,inputmode:`numeric`,dir:`ltr`,class:e([`admin-currency-input`,i.inputClass]),value:d.value,placeholder:i.placeholder,disabled:i.disabled,onInput:f},null,42,c))}}),[[`__scopeId`,`data-v-56435fc8`]]);export{l as t};
-1
View File
@@ -1 +0,0 @@
import{At as e,Mt as t,N as n,Nt as r,g as i,h as a,nt as o,o as s,p as c,x as l}from"./http-WTIyGIuL.js";import{t as u}from"./catalogImage-DbusXpop.js";var d={class:`image-picker`},f={key:0,class:`image-picker__label`},p={class:`image-picker__row`},m={class:`image-picker__actions`},h=[`disabled`],g=[`disabled`],_={key:1,class:`image-picker__error`},v=s(l({__name:`AdminImagePicker`,props:{modelValue:{},label:{}},emits:[`update:modelValue`],setup(s,{emit:l}){let v=s,y=l,b=o(null),x=o(!1),S=o(``);function C(){b.value?.click()}async function w(e){let t=e.target,n=t.files?.[0];if(t.value=``,n){x.value=!0,S.value=``;try{y(`update:modelValue`,await u(n))}catch(e){S.value=e instanceof Error?e.message:`بارگذاری تصویر ناموفق بود.`}finally{x.value=!1}}}function T(){y(`update:modelValue`,null)}return(o,l)=>(n(),i(`div`,d,[s.label?(n(),i(`p`,f,r(s.label),1)):a(``,!0),c(`div`,p,[c(`div`,{class:e([`image-picker__preview`,{"image-picker__preview--empty":!v.modelValue}]),style:t(v.modelValue?{backgroundImage:`url(${v.modelValue})`}:void 0)},null,6),c(`div`,m,[c(`button`,{type:`button`,class:`admin-btn admin-btn--outline admin-btn--sm`,disabled:x.value,onClick:C},r(v.modelValue?`تغییر تصویر`:`افزودن تصویر`),9,h),v.modelValue?(n(),i(`button`,{key:0,type:`button`,class:`admin-btn admin-btn--outline admin-btn--sm`,disabled:x.value,onClick:T},` حذف `,8,g)):a(``,!0),S.value?(n(),i(`p`,_,r(S.value),1)):a(``,!0)])]),c(`input`,{ref_key:`inputRef`,ref:b,type:`file`,accept:`image/*`,class:`image-picker__file`,onChange:w},null,544)]))}}),[[`__scopeId`,`data-v-55584001`]]);export{v as t};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,Q as n,St as r,X as i,bt as a,ct as o,rt as s,vt as c,xt as l}from"./index-BHqDDh7X.js";var u=960,d=.82;async function f(e){if(!e.type.startsWith(`image/`))throw Error(`فقط فایل تصویری مجاز است.`);if(e.size>8388608)throw Error(`حجم تصویر نباید بیشتر از ۸ مگابایت باشد.`);return m(await p(e))}function p(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>t(String(r.result??``)),r.onerror=()=>n(Error(`خواندن تصویر ناموفق بود.`)),r.readAsDataURL(e)})}function m(e){return new Promise((t,n)=>{let r=new Image;r.onload=()=>{let e=Math.min(1,u/Math.max(r.width,r.height)),i=Math.max(1,Math.round(r.width*e)),a=Math.max(1,Math.round(r.height*e)),o=document.createElement(`canvas`);o.width=i,o.height=a;let s=o.getContext(`2d`);if(!s){n(Error(`پردازش تصویر ناموفق بود.`));return}s.drawImage(r,0,0,i,a),t(o.toDataURL(`image/jpeg`,d))},r.onerror=()=>n(Error(`تصویر نامعتبر است.`)),r.src=e})}var h={class:`image-picker`},g={key:0,class:`image-picker__label`},_={class:`image-picker__row`},v={class:`image-picker__actions`},y=[`disabled`],b=[`disabled`],x={key:1,class:`image-picker__error`},S=t(s({__name:`AdminImagePicker`,props:{modelValue:{},label:{}},emits:[`update:modelValue`],setup(t,{emit:s}){let u=t,d=s,p=c(null),m=c(!1),S=c(``);function C(){p.value?.click()}async function w(e){let t=e.target,n=t.files?.[0];if(t.value=``,n){m.value=!0,S.value=``;try{d(`update:modelValue`,await f(n))}catch(e){S.value=e instanceof Error?e.message:`بارگذاری تصویر ناموفق بود.`}finally{m.value=!1}}}function T(){d(`update:modelValue`,null)}return(s,c)=>(o(),e(`div`,h,[t.label?(o(),e(`p`,g,r(t.label),1)):n(``,!0),i(`div`,_,[i(`div`,{class:a([`image-picker__preview`,{"image-picker__preview--empty":!u.modelValue}]),style:l(u.modelValue?{backgroundImage:`url(${u.modelValue})`}:void 0)},null,6),i(`div`,v,[i(`button`,{type:`button`,class:`admin-btn admin-btn--outline admin-btn--sm`,disabled:m.value,onClick:C},r(u.modelValue?`تغییر تصویر`:`افزودن تصویر`),9,y),u.modelValue?(o(),e(`button`,{key:0,type:`button`,class:`admin-btn admin-btn--outline admin-btn--sm`,disabled:m.value,onClick:T},` حذف `,8,b)):n(``,!0),S.value?(o(),e(`p`,x,r(S.value),1)):n(``,!0)])]),i(`input`,{ref_key:`inputRef`,ref:p,type:`file`,accept:`image/*`,class:`image-picker__file`,onChange:w},null,544)]))}}),[[`__scopeId`,`data-v-55584001`]]);export{f as n,S as t};
-1
View File
@@ -1 +0,0 @@
import{I as e,N as t,c as n,g as r,o as i,p as a,v as o,x as s}from"./http-WTIyGIuL.js";var c={class:`admin-page-skeleton`,role:`status`,"aria-live":`polite`,"aria-busy":`true`,"aria-label":`در حال بارگذاری`},l={class:`admin-card admin-page-skeleton__table`},u={key:1,class:`admin-card admin-page-skeleton__form`},d={key:2,class:`admin-card admin-page-skeleton__detail`},f={key:3,class:`admin-page-skeleton__cards`},p=i(s({__name:`AdminPageSkeleton`,props:{variant:{default:`cards`},rows:{default:5}},setup(i){return(s,p)=>(t(),r(`div`,c,[i.variant===`table`?(t(),r(n,{key:0},[p[1]||=a(`div`,{class:`admin-page-skeleton__toolbar`},[a(`span`,{class:`admin-skeleton admin-page-skeleton__search`}),a(`span`,{class:`admin-skeleton admin-page-skeleton__btn`})],-1),a(`div`,l,[(t(!0),r(n,null,e(i.rows,e=>(t(),r(`div`,{key:e,class:`admin-page-skeleton__tr`},[...p[0]||=[o(`<span class="admin-skeleton" style="width:18%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:22%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:14%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:16%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:12%;height:12px;" data-v-8283cd1d></span>`,5)]]))),128))])],64)):i.variant===`form`?(t(),r(`div`,u,[(t(!0),r(n,null,e(i.rows,e=>(t(),r(`div`,{key:e,class:`admin-page-skeleton__field`},[...p[2]||=[a(`span`,{class:`admin-skeleton`,style:{width:`28%`,height:`10px`}},null,-1),a(`span`,{class:`admin-skeleton`,style:{width:`100%`,height:`40px`,"border-radius":`10px`}},null,-1)]]))),128))])):i.variant===`detail`?(t(),r(`div`,d,[...p[3]||=[o(`<span class="admin-skeleton" style="width:40%;height:18px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:70%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:100%;height:120px;border-radius:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:100%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:88%;height:12px;" data-v-8283cd1d></span>`,5)]])):(t(),r(`div`,f,[(t(!0),r(n,null,e(i.rows,e=>(t(),r(`div`,{key:e,class:`admin-card admin-page-skeleton__card`},[...p[4]||=[a(`span`,{class:`admin-skeleton`,style:{width:`45%`,height:`14px`}},null,-1),a(`span`,{class:`admin-skeleton`,style:{width:`70%`,height:`11px`}},null,-1),a(`span`,{class:`admin-skeleton`,style:{width:`35%`,height:`11px`}},null,-1)]]))),128))]))]))}}),[[`__scopeId`,`data-v-8283cd1d`]]);export{p as t};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,X as n,ct as r,et as i,q as a,rt as o,ut as s}from"./index-BHqDDh7X.js";var c={class:`admin-page-skeleton`,role:`status`,"aria-live":`polite`,"aria-busy":`true`,"aria-label":`در حال بارگذاری`},l={class:`admin-card admin-page-skeleton__table`},u={key:1,class:`admin-card admin-page-skeleton__form`},d={key:2,class:`admin-card admin-page-skeleton__detail`},f={key:3,class:`admin-page-skeleton__cards`},p=t(o({__name:`AdminPageSkeleton`,props:{variant:{default:`cards`},rows:{default:5}},setup(t){return(o,p)=>(r(),e(`div`,c,[t.variant===`table`?(r(),e(a,{key:0},[p[1]||=n(`div`,{class:`admin-page-skeleton__toolbar`},[n(`span`,{class:`admin-skeleton admin-page-skeleton__search`}),n(`span`,{class:`admin-skeleton admin-page-skeleton__btn`})],-1),n(`div`,l,[(r(!0),e(a,null,s(t.rows,t=>(r(),e(`div`,{key:t,class:`admin-page-skeleton__tr`},[...p[0]||=[i(`<span class="admin-skeleton" style="width:18%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:22%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:14%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:16%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:12%;height:12px;" data-v-8283cd1d></span>`,5)]]))),128))])],64)):t.variant===`form`?(r(),e(`div`,u,[(r(!0),e(a,null,s(t.rows,t=>(r(),e(`div`,{key:t,class:`admin-page-skeleton__field`},[...p[2]||=[n(`span`,{class:`admin-skeleton`,style:{width:`28%`,height:`10px`}},null,-1),n(`span`,{class:`admin-skeleton`,style:{width:`100%`,height:`40px`,"border-radius":`10px`}},null,-1)]]))),128))])):t.variant===`detail`?(r(),e(`div`,d,[...p[3]||=[i(`<span class="admin-skeleton" style="width:40%;height:18px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:70%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:100%;height:120px;border-radius:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:100%;height:12px;" data-v-8283cd1d></span><span class="admin-skeleton" style="width:88%;height:12px;" data-v-8283cd1d></span>`,5)]])):(r(),e(`div`,f,[(r(!0),e(a,null,s(t.rows,t=>(r(),e(`div`,{key:t,class:`admin-card admin-page-skeleton__card`},[...p[4]||=[n(`span`,{class:`admin-skeleton`,style:{width:`45%`,height:`14px`}},null,-1),n(`span`,{class:`admin-skeleton`,style:{width:`70%`,height:`11px`}},null,-1),n(`span`,{class:`admin-skeleton`,style:{width:`35%`,height:`11px`}},null,-1)]]))),128))]))]))}}),[[`__scopeId`,`data-v-8283cd1d`]]);export{p as t};
-1
View File
@@ -1 +0,0 @@
import{At as e,I as t,K as n,Mt as r,N as i,Nt as a,O as o,U as s,b as c,c as l,f as u,g as d,h as f,j as p,m,nt as h,o as g,p as _,u as v,x as y}from"./http-WTIyGIuL.js";import{H as b,N as x,O as S,U as C,V as w}from"./index-BOMJTziC.js";var T=[`id`],E=[`aria-label`,`aria-expanded`],D={class:`admin-select__value`},O={class:`admin-select__chevron`,"aria-hidden":`true`},k=[`aria-label`],A={key:0,class:`admin-select__search`,role:`presentation`},j=[`placeholder`],M={key:1,class:`admin-select__empty`,role:`presentation`},N=[`aria-selected`,`onClick`],P=g(y({__name:`AdminSelect`,props:{modelValue:{},options:{},placeholder:{default:`انتخاب کنید`},ariaLabel:{default:`انتخاب`},block:{type:Boolean,default:!1},searchable:{type:Boolean,default:!1},searchPlaceholder:{default:`جستجو…`},id:{default:void 0}},emits:[`update:modelValue`],setup(g,{emit:y}){let P=g,F=y,I=h(!1),L=h(``),R=h(null),z=h(null),B=h(null),V=h({});S(R,()=>{I.value=!1,L.value=``},{ignore:[z]});let H=u(()=>P.options.find(e=>e.value===P.modelValue)?.label??P.placeholder),U=u(()=>{let e=L.value.trim().toLowerCase();return e?P.options.filter(t=>t.label.toLowerCase().includes(e)):P.options});function W(){let e=R.value;if(!e)return;let t=e.getBoundingClientRect(),n=Math.min(320,window.innerHeight*.5),r=window.innerHeight-t.bottom-6,i=t.top-6,a=r<Math.min(160,n)&&i>r,o=Math.max(120,Math.min(n,a?i:r));V.value={position:`fixed`,left:`${Math.max(8,Math.min(t.left,window.innerWidth-Math.max(t.width,220)-8))}px`,width:`${Math.max(t.width,220)}px`,zIndex:`1100`,maxHeight:`${o}px`,...a?{top:`auto`,bottom:`${window.innerHeight-t.top+6}px`}:{top:`${t.bottom+6}px`,bottom:`auto`}}}function G(e){e?(window.addEventListener(`resize`,W),window.addEventListener(`scroll`,W,!0)):(window.removeEventListener(`resize`,W),window.removeEventListener(`scroll`,W,!0))}async function K(){I.value=!I.value,I.value&&(await o(),W(),P.searchable&&(L.value=``,await o(),B.value?.focus()))}function q(e){F(`update:modelValue`,e),I.value=!1,L.value=``}return s(I,e=>{G(e),e&&o(()=>W())}),p(()=>{G(!1)}),(o,s)=>(i(),d(`div`,{id:g.id,ref_key:`rootRef`,ref:R,class:e([`admin-select`,{"admin-select--open":I.value,"admin-select--block":g.block}])},[_(`button`,{type:`button`,class:`admin-select__trigger`,"aria-label":g.ariaLabel,"aria-expanded":I.value,"aria-haspopup":`listbox`,onClick:K},[_(`span`,D,a(H.value),1),_(`span`,O,[c(x,{name:`chevron-down`,size:16})])],8,E),(i(),m(v,{to:`body`},[I.value?(i(),d(`ul`,{key:0,ref_key:`menuRef`,ref:z,class:`admin-select__menu`,role:`listbox`,"aria-label":g.ariaLabel,style:r(V.value)},[g.searchable?(i(),d(`li`,A,[n(_(`input`,{ref_key:`searchInputRef`,ref:B,"onUpdate:modelValue":s[0]||=e=>L.value=e,type:`search`,class:`admin-select__search-input`,placeholder:g.searchPlaceholder,onClick:s[1]||=C(()=>{},[`stop`]),onKeydown:s[2]||=b(C(e=>I.value=!1,[`stop`]),[`esc`])},null,40,j),[[w,L.value]])])):f(``,!0),U.value.length?f(``,!0):(i(),d(`li`,M,` موردی یافت نشد `)),(i(!0),d(l,null,t(U.value,t=>(i(),d(`li`,{key:t.value,role:`presentation`},[_(`button`,{type:`button`,class:e([`admin-select__option`,{"admin-select__option--active":t.value===g.modelValue}]),role:`option`,"aria-selected":t.value===g.modelValue,onClick:e=>q(t.value)},[_(`span`,null,a(t.label),1),t.value===g.modelValue?(i(),m(x,{key:0,name:`check`,size:16,class:`admin-select__check`})):f(``,!0)],10,N)]))),128))],12,k)):f(``,!0)]))],10,T))}}),[[`__scopeId`,`data-v-7f43c8c3`]]);export{P as t};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,G as n,J as r,K as i,O as a,P as o,Q as s,St as c,W as l,X as u,Y as d,Z as f,at as p,bt as m,ct as h,ht as g,nt as _,pt as v,q as y,rt as b,st as x,ut as S,vt as C,xt as w}from"./index-BHqDDh7X.js";var T=[`id`],E=[`aria-label`,`aria-expanded`],D={class:`admin-select__value`},O={class:`admin-select__chevron`,"aria-hidden":`true`},k=[`aria-label`],A={key:0,class:`admin-select__search`,role:`presentation`},j=[`placeholder`],M={key:1,class:`admin-select__empty`,role:`presentation`},N=[`aria-selected`,`onClick`],P=t(b({__name:`AdminSelect`,props:{modelValue:{},options:{},placeholder:{default:`انتخاب کنید`},ariaLabel:{default:`انتخاب`},block:{type:Boolean,default:!1},searchable:{type:Boolean,default:!1},searchPlaceholder:{default:`جستجو…`},id:{default:void 0}},emits:[`update:modelValue`],setup(t,{emit:b}){let P=t,F=b,I=C(!1),L=C(``),R=C(null),z=C(null),B=C(null),V=C({});a(R,()=>{I.value=!1,L.value=``},{ignore:[z]});let H=d(()=>P.options.find(e=>e.value===P.modelValue)?.label??P.placeholder),U=d(()=>{let e=L.value.trim().toLowerCase();return e?P.options.filter(t=>t.label.toLowerCase().includes(e)):P.options});function W(){let e=R.value;if(!e)return;let t=e.getBoundingClientRect(),n=Math.min(320,window.innerHeight*.5),r=window.innerHeight-t.bottom-6,i=t.top-6,a=r<Math.min(160,n)&&i>r,o=Math.max(120,Math.min(n,a?i:r));V.value={position:`fixed`,left:`${Math.max(8,Math.min(t.left,window.innerWidth-Math.max(t.width,220)-8))}px`,width:`${Math.max(t.width,220)}px`,zIndex:`1100`,maxHeight:`${o}px`,...a?{top:`auto`,bottom:`${window.innerHeight-t.top+6}px`}:{top:`${t.bottom+6}px`,bottom:`auto`}}}function G(e){e?(window.addEventListener(`resize`,W),window.addEventListener(`scroll`,W,!0)):(window.removeEventListener(`resize`,W),window.removeEventListener(`scroll`,W,!0))}async function K(){I.value=!I.value,I.value&&(await p(),W(),P.searchable&&(L.value=``,await p(),B.value?.focus()))}function q(e){F(`update:modelValue`,e),I.value=!1,L.value=``}return v(I,e=>{G(e),e&&p(()=>W())}),x(()=>{G(!1)}),(a,d)=>(h(),e(`div`,{id:t.id,ref_key:`rootRef`,ref:R,class:m([`admin-select`,{"admin-select--open":I.value,"admin-select--block":t.block}])},[u(`button`,{type:`button`,class:`admin-select__trigger`,"aria-label":t.ariaLabel,"aria-expanded":I.value,"aria-haspopup":`listbox`,onClick:K},[u(`span`,D,c(H.value),1),u(`span`,O,[_(o,{name:`chevron-down`,size:16})])],8,E),(h(),f(r,{to:`body`},[I.value?(h(),e(`ul`,{key:0,ref_key:`menuRef`,ref:z,class:`admin-select__menu`,role:`listbox`,"aria-label":t.ariaLabel,style:w(V.value)},[t.searchable?(h(),e(`li`,A,[g(u(`input`,{ref_key:`searchInputRef`,ref:B,"onUpdate:modelValue":d[0]||=e=>L.value=e,type:`search`,class:`admin-select__search-input`,placeholder:t.searchPlaceholder,onClick:d[1]||=i(()=>{},[`stop`]),onKeydown:d[2]||=n(i(e=>I.value=!1,[`stop`]),[`esc`])},null,40,j),[[l,L.value]])])):s(``,!0),U.value.length?s(``,!0):(h(),e(`li`,M,` موردی یافت نشد `)),(h(!0),e(y,null,S(U.value,n=>(h(),e(`li`,{key:n.value,role:`presentation`},[u(`button`,{type:`button`,class:m([`admin-select__option`,{"admin-select__option--active":n.value===t.modelValue}]),role:`option`,"aria-selected":n.value===t.modelValue,onClick:e=>q(n.value)},[u(`span`,null,c(n.label),1),n.value===t.modelValue?(h(),f(o,{key:0,name:`check`,size:16,class:`admin-select__check`})):s(``,!0)],10,N)]))),128))],12,k)):s(``,!0)]))],10,T))}}),[[`__scopeId`,`data-v-7f43c8c3`]]);export{P 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
-1
View File
@@ -1 +0,0 @@
.admin-page-header__sub[data-v-b734755d]{color:var(--color-text-muted);margin:4px 0 0;font-size:.85rem}.banner-thumb[data-v-b734755d]{background:#e8eceb 50%/cover no-repeat;border-radius:8px;width:72px;height:44px;display:block}.banner-thumb--empty[data-v-b734755d]{background:#eef1f0}.banner-sub[data-v-b734755d]{color:var(--color-text-muted);margin:2px 0 0;font-size:.8rem}.field[data-v-b734755d]{flex-direction:column;gap:6px;margin-bottom:12px;font-size:.875rem;display:flex}
+1
View File
@@ -0,0 +1 @@
.admin-page-header__sub[data-v-1ca3d686]{color:var(--color-text-muted);margin:4px 0 0;font-size:.85rem}.banner-thumb[data-v-1ca3d686]{background:#e8eceb 50%/cover no-repeat;border-radius:8px;width:72px;height:44px;display:block}.banner-thumb--empty[data-v-1ca3d686]{background:#eef1f0}.banner-sub[data-v-1ca3d686]{color:var(--color-text-muted);margin:2px 0 0;font-size:.8rem}.field[data-v-1ca3d686]{flex-direction:column;gap:6px;margin-bottom:12px;font-size:.875rem;display:flex}.field__head[data-v-1ca3d686]{justify-content:space-between;align-items:center;gap:12px;display:flex}
File diff suppressed because one or more lines are too long
-1
View File
@@ -1 +0,0 @@
import{At as e,L as t,N as n,Nt as r,b as i,g as a,h as o,o as s,p as c,x as l}from"./http-WTIyGIuL.js";import{N as u}from"./index-BOMJTziC.js";var d={class:`base-empty__icon`,"aria-hidden":`true`},f={class:`base-empty__title`},p={key:0,class:`base-empty__description`},m={key:1,class:`base-empty__actions`},h=s(l({__name:`BaseEmptyState`,props:{icon:{default:`file`},title:{},description:{},compact:{type:Boolean}},emits:[`action`,`secondary`],setup(s){return(l,h)=>(n(),a(`div`,{class:e([`base-empty`,{"base-empty--compact":s.compact}])},[c(`div`,d,[i(u,{name:s.icon,size:s.compact?28:36},null,8,[`name`,`size`])]),c(`h3`,f,r(s.title),1),s.description?(n(),a(`p`,p,r(s.description),1)):o(``,!0),l.$slots.default||l.$slots.secondary?(n(),a(`div`,m,[t(l.$slots,`default`,{},void 0,!0),t(l.$slots,`secondary`,{},void 0,!0)])):o(``,!0)],2))}}),[[`__scopeId`,`data-v-47ea81e4`]]);export{h as t};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,P as n,Q as r,St as i,X as a,bt as o,ct as s,dt as c,nt as l,rt as u}from"./index-BHqDDh7X.js";var d={class:`base-empty__icon`,"aria-hidden":`true`},f={class:`base-empty__title`},p={key:0,class:`base-empty__description`},m={key:1,class:`base-empty__actions`},h=t(u({__name:`BaseEmptyState`,props:{icon:{default:`file`},title:{},description:{},compact:{type:Boolean}},emits:[`action`,`secondary`],setup(t){return(u,h)=>(s(),e(`div`,{class:o([`base-empty`,{"base-empty--compact":t.compact}])},[a(`div`,d,[l(n,{name:t.icon,size:t.compact?28:36},null,8,[`name`,`size`])]),a(`h3`,f,i(t.title),1),t.description?(s(),e(`p`,p,i(t.description),1)):r(``,!0),u.$slots.default||u.$slots.secondary?(s(),e(`div`,m,[c(u.$slots,`default`,{},void 0,!0),c(u.$slots,`secondary`,{},void 0,!0)])):r(``,!0)],2))}}),[[`__scopeId`,`data-v-47ea81e4`]]);export{h as t};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,Q as n,St as r,X as i,Y as a,bt as o,ct as s,rt as c}from"./index-BHqDDh7X.js";var l=[`title`],u={key:0,class:`base-switch__label`},d={class:`base-switch__control`},f=[`id`,`checked`,`disabled`,`aria-checked`,`aria-label`],p={class:`base-switch__track`,"aria-hidden":`true`},m={class:`base-switch__thumb`},h={key:0,class:`base-switch__spinner`},g=t(c({__name:`BaseSwitch`,props:{modelValue:{type:Boolean},disabled:{type:Boolean},loading:{type:Boolean},error:{type:Boolean},label:{},activeText:{default:`فعال`},inactiveText:{default:`غیرفعال`},id:{}},emits:[`update:modelValue`],setup(t,{emit:c}){let g=t,_=c,v=a(()=>g.id??`switch-${crypto.randomUUID().slice(0,8)}`),y=a(()=>g.modelValue?g.activeText:g.inactiveText);function b(e){let t=e.target,n=t.checked;t.checked=g.modelValue,!(g.disabled||g.loading)&&_(`update:modelValue`,n)}return(a,c)=>(s(),e(`label`,{class:o([`base-switch`,{"base-switch--disabled":t.disabled||t.loading,"base-switch--error":t.error}]),title:y.value},[t.label?(s(),e(`span`,u,r(t.label),1)):n(``,!0),i(`span`,d,[i(`input`,{id:v.value,class:`base-switch__input`,type:`checkbox`,role:`switch`,checked:t.modelValue,disabled:t.disabled||t.loading,"aria-checked":t.modelValue,"aria-label":t.label||y.value,onChange:b},null,40,f),i(`span`,p,[i(`span`,m,[t.loading?(s(),e(`span`,h)):n(``,!0)])])])],10,l))}}),[[`__scopeId`,`data-v-0410eed4`]]);export{g as t};
-1
View File
@@ -1 +0,0 @@
import{At as e,N as t,Nt as n,f as r,g as i,h as a,o,p as s,x as c}from"./http-WTIyGIuL.js";var l=[`title`],u={key:0,class:`base-switch__label`},d={class:`base-switch__control`},f=[`id`,`checked`,`disabled`,`aria-checked`,`aria-label`],p={class:`base-switch__track`,"aria-hidden":`true`},m={class:`base-switch__thumb`},h={key:0,class:`base-switch__spinner`},g=o(c({__name:`BaseSwitch`,props:{modelValue:{type:Boolean},disabled:{type:Boolean},loading:{type:Boolean},error:{type:Boolean},label:{},activeText:{default:`فعال`},inactiveText:{default:`غیرفعال`},id:{}},emits:[`update:modelValue`],setup(o,{emit:c}){let g=o,_=c,v=r(()=>g.id??`switch-${crypto.randomUUID().slice(0,8)}`),y=r(()=>g.modelValue?g.activeText:g.inactiveText);function b(e){let t=e.target,n=t.checked;t.checked=g.modelValue,!(g.disabled||g.loading)&&_(`update:modelValue`,n)}return(r,c)=>(t(),i(`label`,{class:e([`base-switch`,{"base-switch--disabled":o.disabled||o.loading,"base-switch--error":o.error}]),title:y.value},[o.label?(t(),i(`span`,u,n(o.label),1)):a(``,!0),s(`span`,d,[s(`input`,{id:v.value,class:`base-switch__input`,type:`checkbox`,role:`switch`,checked:o.modelValue,disabled:o.disabled||o.loading,"aria-checked":o.modelValue,"aria-label":o.label||y.value,onChange:b},null,40,f),s(`span`,p,[s(`span`,m,[o.loading?(t(),i(`span`,h)):a(``,!0)])])])],10,l))}}),[[`__scopeId`,`data-v-0410eed4`]]);export{g as t};
-1
View File
@@ -1 +0,0 @@
import{A as e,G as t,I as n,K as r,N as i,Nt as a,a as o,b as s,c,et as l,f as u,g as d,m as f,nt as p,o as m,p as h,t as g,x as _,y as v}from"./http-WTIyGIuL.js";import{L as y,V as b,u as x}from"./index-BOMJTziC.js";import{t as S}from"./AdminPageSkeleton-D-u-8bBz.js";import{t as C}from"./BaseSwitch-gjOaAbzm.js";import{t as w}from"./BaseEmptyState-Wc1O8rBP.js";var T={class:`admin-page`},E={key:2,class:`admin-table`},D={dir:`ltr`},O=[`onClick`],k={class:`field`},A={class:`field`},j={class:`field`},M={class:`field`},N=[`disabled`],P=m(_({__name:`BrandsView`,setup(m){let _=p([]),P=p(!0),F=p(!1),I=p(null),L=o(),R=l({name:``,slug:``,description:``,displayOrder:0,isActive:!0}),z=u(()=>I.value?`ویرایش برند`:`افزودن برند`);async function B(){P.value=!0;try{_.value=await x.list()}catch(e){L.error(e instanceof g?e.message:`بارگذاری برندها ناموفق بود`)}finally{P.value=!1}}function V(){I.value=null,Object.assign(R,{name:``,slug:``,description:``,displayOrder:_.value.length+1,isActive:!0}),F.value=!0}function H(e){I.value=e.id,Object.assign(R,{name:e.name,slug:e.slug??``,description:e.description??``,displayOrder:e.displayOrder,isActive:e.isActive}),F.value=!0}async function U(){try{let e={name:R.name.trim(),slug:R.slug.trim()||void 0,description:R.description.trim()||void 0,displayOrder:R.displayOrder,isActive:R.isActive};I.value?await x.update(I.value,e):await x.create(e),F.value=!1,L.success(`ذخیره شد`),await B()}catch(e){L.error(e instanceof g?e.message:`ذخیره ناموفق بود`)}}return e(()=>{B()}),(e,o)=>(i(),d(`div`,T,[h(`header`,{class:`admin-page__head`},[o[6]||=h(`div`,null,[h(`h1`,null,`برندها`)],-1),h(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`افزودن برند`)]),P.value?(i(),f(S,{key:0,variant:`table`})):_.value.length?(i(),d(`table`,E,[o[7]||=h(`thead`,null,[h(`tr`,null,[h(`th`,null,`نام`),h(`th`,null,`شناسه لاتین`),h(`th`,null,`مشتریان`),h(`th`,null,`فعال`),h(`th`)])],-1),h(`tbody`,null,[(i(!0),d(c,null,n(_.value,e=>(i(),d(`tr`,{key:e.id},[h(`td`,null,a(e.name),1),h(`td`,D,a(e.slug||`—`),1),h(`td`,null,a(e.customersCount??0),1),h(`td`,null,a(e.isActive?`بله`:`خیر`),1),h(`td`,null,[h(`button`,{type:`button`,class:`link`,onClick:t=>H(e)},`ویرایش`,8,O)])]))),128))])])):(i(),f(w,{key:1,title:`برندی ثبت نشده`})),s(y,{open:F.value,title:z.value,onClose:o[5]||=e=>F.value=!1},{footer:t(()=>[h(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!R.name.trim(),onClick:U},`ذخیره`,8,N)]),default:t(()=>[h(`label`,k,[o[8]||=v(`نام`,-1),r(h(`input`,{"onUpdate:modelValue":o[0]||=e=>R.name=e,class:`admin-input`},null,512),[[b,R.name]])]),h(`label`,A,[o[9]||=v(`شناسه لاتین`,-1),r(h(`input`,{"onUpdate:modelValue":o[1]||=e=>R.slug=e,class:`admin-input`,dir:`ltr`,placeholder:`مثلاً nademi-silk`},null,512),[[b,R.slug]])]),h(`label`,j,[o[10]||=v(`توضیح`,-1),r(h(`textarea`,{"onUpdate:modelValue":o[2]||=e=>R.description=e,class:`admin-input`,rows:`3`},null,512),[[b,R.description]])]),h(`label`,M,[o[11]||=v(`ترتیب`,-1),r(h(`input`,{"onUpdate:modelValue":o[3]||=e=>R.displayOrder=e,type:`number`,class:`admin-input`},null,512),[[b,R.displayOrder,void 0,{number:!0}]])]),s(C,{modelValue:R.isActive,"onUpdate:modelValue":o[4]||=e=>R.isActive=e,label:`فعال`},null,8,[`modelValue`])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-88043806`]]);export{P as default};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,N as n,R as r,St as i,W as a,X as o,Y as s,Z as c,_t as l,ct as u,ht as d,mt as f,nt as p,ot as m,q as h,rt as g,tt as _,u as v,ut as y,vt as b,z as x}from"./index-BHqDDh7X.js";import{t as S}from"./AdminPageSkeleton-E50Kwv4j.js";import{t as C}from"./BaseSwitch-DYYNQNXf.js";import{t as w}from"./BaseEmptyState-vNIhbGdV.js";var T={class:`admin-page`},E={key:2,class:`admin-table`},D={dir:`ltr`},O=[`onClick`],k={class:`field`},A={class:`field`},j={class:`field`},M={class:`field`},N=[`disabled`],P=t(g({__name:`BrandsView`,setup(t){let g=b([]),P=b(!0),F=b(!1),I=b(null),L=x(),R=l({name:``,slug:``,description:``,displayOrder:0,isActive:!0}),z=s(()=>I.value?`ویرایش برند`:`افزودن برند`);async function B(){P.value=!0;try{g.value=await v.list()}catch(e){L.error(e instanceof n?e.message:`بارگذاری برندها ناموفق بود`)}finally{P.value=!1}}function V(){I.value=null,Object.assign(R,{name:``,slug:``,description:``,displayOrder:g.value.length+1,isActive:!0}),F.value=!0}function H(e){I.value=e.id,Object.assign(R,{name:e.name,slug:e.slug??``,description:e.description??``,displayOrder:e.displayOrder,isActive:e.isActive}),F.value=!0}async function U(){try{let e={name:R.name.trim(),slug:R.slug.trim()||void 0,description:R.description.trim()||void 0,displayOrder:R.displayOrder,isActive:R.isActive};I.value?await v.update(I.value,e):await v.create(e),F.value=!1,L.success(`ذخیره شد`),await B()}catch(e){L.error(e instanceof n?e.message:`ذخیره ناموفق بود`)}}return m(()=>{B()}),(t,n)=>(u(),e(`div`,T,[o(`header`,{class:`admin-page__head`},[n[6]||=o(`div`,null,[o(`h1`,null,`برندها`)],-1),o(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`افزودن برند`)]),P.value?(u(),c(S,{key:0,variant:`table`})):g.value.length?(u(),e(`table`,E,[n[7]||=o(`thead`,null,[o(`tr`,null,[o(`th`,null,`نام`),o(`th`,null,`شناسه لاتین`),o(`th`,null,`مشتریان`),o(`th`,null,`فعال`),o(`th`)])],-1),o(`tbody`,null,[(u(!0),e(h,null,y(g.value,t=>(u(),e(`tr`,{key:t.id},[o(`td`,null,i(t.name),1),o(`td`,D,i(t.slug||`—`),1),o(`td`,null,i(t.customersCount??0),1),o(`td`,null,i(t.isActive?`بله`:`خیر`),1),o(`td`,null,[o(`button`,{type:`button`,class:`link`,onClick:e=>H(t)},`ویرایش`,8,O)])]))),128))])])):(u(),c(w,{key:1,title:`برندی ثبت نشده`})),p(r,{open:F.value,title:z.value,onClose:n[5]||=e=>F.value=!1},{footer:f(()=>[o(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!R.name.trim(),onClick:U},`ذخیره`,8,N)]),default:f(()=>[o(`label`,k,[n[8]||=_(`نام`,-1),d(o(`input`,{"onUpdate:modelValue":n[0]||=e=>R.name=e,class:`admin-input`},null,512),[[a,R.name]])]),o(`label`,A,[n[9]||=_(`شناسه لاتین`,-1),d(o(`input`,{"onUpdate:modelValue":n[1]||=e=>R.slug=e,class:`admin-input`,dir:`ltr`,placeholder:`مثلاً nademi-silk`},null,512),[[a,R.slug]])]),o(`label`,j,[n[10]||=_(`توضیح`,-1),d(o(`textarea`,{"onUpdate:modelValue":n[2]||=e=>R.description=e,class:`admin-input`,rows:`3`},null,512),[[a,R.description]])]),o(`label`,M,[n[11]||=_(`ترتیب`,-1),d(o(`input`,{"onUpdate:modelValue":n[3]||=e=>R.displayOrder=e,type:`number`,class:`admin-input`},null,512),[[a,R.displayOrder,void 0,{number:!0}]])]),p(C,{modelValue:R.isActive,"onUpdate:modelValue":n[4]||=e=>R.isActive=e,label:`فعال`},null,8,[`modelValue`])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-88043806`]]);export{P 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 @@
import{$ as e,B as t,N as n,Q as r,St as i,X as a,Z as o,ct as s,f as c,i as l,nt as u,ot as d,q as f,rt as p,tt as m,ut as h,vt as g,yt as _,z as v}from"./index-BHqDDh7X.js";import{t as y}from"./AdminPageSkeleton-E50Kwv4j.js";import{t as b}from"./BaseEmptyState-vNIhbGdV.js";import{t as x}from"./AdminSelect-BjB6PHdr.js";var S={class:`admin-page`},C={class:`admin-page__head`},w={class:`request-card__top`},T={class:`muted`,dir:`ltr`},E={class:`badge`},D={key:0,class:`muted`},O={class:`admin-table`},k={dir:`ltr`},A={key:0,class:`movements`},j={class:`muted`},M=t(p({__name:`CatalogRequestsView`,setup(t){let p=[{value:``,label:`همه وضعیت‌ها`},{value:`new`,label:`درخواست جدید`},{value:`approved`,label:`تأیید شده`},{value:`ready_to_ship`,label:`آماده ارسال`},{value:`delivered`,label:`تحویل به مشتری`},{value:`returned`,label:`بازگردانده شده`},{value:`cancelled`,label:`لغو شده`}],M=[{value:`warehouse`,label:`انبار`},{value:`customer`,label:`مشتری`},{value:`agent`,label:`واسطه`}],N=Object.fromEntries(p.filter(e=>e.value).map(e=>[e.value,e.label])),P=Object.fromEntries(M.map(e=>[e.value,e.label])),F=g([]),I=g(``),L=g(!0),R=g(null),z=v();async function B(){L.value=!0;try{F.value=await c.list(I.value||void 0)}catch(e){z.error(e instanceof n?e.message:`بارگذاری انجام نشد`)}finally{L.value=!1}}async function V(e,t){try{await c.updateStatus(e,t),z.success(`وضعیت به‌روز شد`),await B()}catch(e){z.error(e instanceof n?e.message:`به‌روزرسانی ناموفق بود`)}}async function H(e,t){if(!R.value){R.value=e;try{await c.updateHolder(e,{holderType:t}),z.success(`محل نگهداری به‌روز شد`),await B()}catch(e){z.error(e instanceof n?e.message:`به‌روزرسانی محل ناموفق بود`)}finally{R.value=null}}}function U(e){return P[e]??e}return d(()=>{B()}),(t,n)=>(s(),e(`div`,S,[a(`header`,C,[n[1]||=a(`div`,null,[a(`h1`,null,`درخواست کالیته فیزیکی`),a(`p`,null,`بررسی، گردش وضعیت و محل نگهداری اقلام`)],-1),u(x,{modelValue:I.value,"onUpdate:modelValue":[n[0]||=e=>I.value=e,B],options:p},null,8,[`modelValue`])]),L.value?(s(),o(y,{key:0,variant:`cards`})):F.value.length?(s(!0),e(f,{key:2},h(F.value,t=>(s(),e(`div`,{key:t.id,class:`request-card`},[a(`div`,w,[a(`div`,null,[a(`strong`,null,i(t.customerName),1),a(`span`,T,i(t.customerPhone),1),a(`span`,E,i(_(N)[t.status]??t.status),1)]),u(x,{"model-value":t.status,options:p.filter(e=>e.value),"onUpdate:modelValue":e=>V(t.id,String(e))},null,8,[`model-value`,`options`,`onUpdate:modelValue`])]),t.note?(s(),e(`p`,D,i(t.note),1)):r(``,!0),a(`table`,O,[n[2]||=a(`thead`,null,[a(`tr`,null,[a(`th`,null,`محصول`),a(`th`,null,`کد`),a(`th`,null,`تعداد`),a(`th`,null,`محل`)])],-1),a(`tbody`,null,[(s(!0),e(f,null,h(t.items,t=>(s(),e(`tr`,{key:t.id},[a(`td`,null,i(t.productName),1),a(`td`,k,i(t.productCode),1),a(`td`,null,i(t.quantity),1),a(`td`,null,[u(x,{"model-value":t.holderType,options:M,"onUpdate:modelValue":e=>H(t.id,String(e))},null,8,[`model-value`,`onUpdate:modelValue`]),t.movements?.length?(s(),e(`ul`,A,[(s(!0),e(f,null,h(t.movements,t=>(s(),e(`li`,{key:t.id},[m(i(U(t.fromHolder))+` → `+i(U(t.toHolder))+` `,1),a(`span`,j,i(_(l)(t.movedAt)),1)]))),128))])):r(``,!0)])]))),128))])])]))),128)):(s(),o(b,{key:1,title:`درخواستی نیست`}))]))}}),[[`__scopeId`,`data-v-747d8f88`]]);export{M as default};
-1
View File
@@ -1 +0,0 @@
import{A as e,I as t,N as n,Nt as r,a as i,b as a,c as o,g as s,h as c,m as l,nt as u,o as d,p as f,t as p,ut as m,x as h,y as g}from"./http-WTIyGIuL.js";import{f as _,i as v}from"./index-BOMJTziC.js";import{t as y}from"./AdminPageSkeleton-D-u-8bBz.js";import{t as b}from"./BaseEmptyState-Wc1O8rBP.js";import{t as x}from"./AdminSelect-BVy_0-3W.js";var S={class:`admin-page`},C={class:`admin-page__head`},w={class:`request-card__top`},T={class:`muted`,dir:`ltr`},E={class:`badge`},D={key:0,class:`muted`},O={class:`admin-table`},k={dir:`ltr`},A={key:0,class:`movements`},j={class:`muted`},M=d(h({__name:`CatalogRequestsView`,setup(d){let h=[{value:``,label:`همه وضعیت‌ها`},{value:`new`,label:`درخواست جدید`},{value:`approved`,label:`تأیید شده`},{value:`ready_to_ship`,label:`آماده ارسال`},{value:`delivered`,label:`تحویل به مشتری`},{value:`returned`,label:`بازگردانده شده`},{value:`cancelled`,label:`لغو شده`}],M=[{value:`warehouse`,label:`انبار`},{value:`customer`,label:`مشتری`},{value:`agent`,label:`واسطه`}],N=Object.fromEntries(h.filter(e=>e.value).map(e=>[e.value,e.label])),P=Object.fromEntries(M.map(e=>[e.value,e.label])),F=u([]),I=u(``),L=u(!0),R=u(null),z=i();async function B(){L.value=!0;try{F.value=await _.list(I.value||void 0)}catch(e){z.error(e instanceof p?e.message:`بارگذاری انجام نشد`)}finally{L.value=!1}}async function V(e,t){try{await _.updateStatus(e,t),z.success(`وضعیت به‌روز شد`),await B()}catch(e){z.error(e instanceof p?e.message:`به‌روزرسانی ناموفق بود`)}}async function H(e,t){if(!R.value){R.value=e;try{await _.updateHolder(e,{holderType:t}),z.success(`محل نگهداری به‌روز شد`),await B()}catch(e){z.error(e instanceof p?e.message:`به‌روزرسانی محل ناموفق بود`)}finally{R.value=null}}}function U(e){return P[e]??e}return e(()=>{B()}),(e,i)=>(n(),s(`div`,S,[f(`header`,C,[i[1]||=f(`div`,null,[f(`h1`,null,`درخواست کالیته فیزیکی`),f(`p`,null,`بررسی، گردش وضعیت و محل نگهداری اقلام`)],-1),a(x,{modelValue:I.value,"onUpdate:modelValue":[i[0]||=e=>I.value=e,B],options:h},null,8,[`modelValue`])]),L.value?(n(),l(y,{key:0,variant:`cards`})):F.value.length?(n(!0),s(o,{key:2},t(F.value,e=>(n(),s(`div`,{key:e.id,class:`request-card`},[f(`div`,w,[f(`div`,null,[f(`strong`,null,r(e.customerName),1),f(`span`,T,r(e.customerPhone),1),f(`span`,E,r(m(N)[e.status]??e.status),1)]),a(x,{"model-value":e.status,options:h.filter(e=>e.value),"onUpdate:modelValue":t=>V(e.id,String(t))},null,8,[`model-value`,`options`,`onUpdate:modelValue`])]),e.note?(n(),s(`p`,D,r(e.note),1)):c(``,!0),f(`table`,O,[i[2]||=f(`thead`,null,[f(`tr`,null,[f(`th`,null,`محصول`),f(`th`,null,`کد`),f(`th`,null,`تعداد`),f(`th`,null,`محل`)])],-1),f(`tbody`,null,[(n(!0),s(o,null,t(e.items,e=>(n(),s(`tr`,{key:e.id},[f(`td`,null,r(e.productName),1),f(`td`,k,r(e.productCode),1),f(`td`,null,r(e.quantity),1),f(`td`,null,[a(x,{"model-value":e.holderType,options:M,"onUpdate:modelValue":t=>H(e.id,String(t))},null,8,[`model-value`,`onUpdate:modelValue`]),e.movements?.length?(n(),s(`ul`,A,[(n(!0),s(o,null,t(e.movements,e=>(n(),s(`li`,{key:e.id},[g(r(U(e.fromHolder))+` → `+r(U(e.toHolder))+` `,1),f(`span`,j,r(m(v)(e.movedAt)),1)]))),128))])):c(``,!0)])]))),128))])])]))),128)):(n(),l(b,{key:1,title:`درخواستی نیست`}))]))}}),[[`__scopeId`,`data-v-747d8f88`]]);export{M 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 @@
.admin-page-header__sub[data-v-45420745]{color:var(--color-text-muted);margin:4px 0 0;font-size:.85rem}.categories-admin__grid[data-v-45420745]{align-items:start;gap:16px;display:grid}.categories-panel[data-v-45420745]{padding:20px}.categories-panel__head[data-v-45420745]{border-bottom:1px solid var(--color-border);justify-content:space-between;align-items:flex-start;gap:12px;margin-bottom:16px;padding-bottom:14px;display:flex}.categories-panel__head h2[data-v-45420745]{margin:0 0 4px;font-size:1.05rem;font-weight:700}.categories-panel__head p[data-v-45420745]{color:var(--color-text-muted);margin:0;font-size:.78rem}.categories-panel__add[data-v-45420745]{border:1px dashed var(--color-border);background:var(--color-bg);border-radius:12px;gap:8px;margin-bottom:16px;padding:14px;display:grid}.categories-panel__empty[data-v-45420745]{text-align:center;color:var(--color-text-muted);background:var(--color-bg);border-radius:12px;margin:0;padding:28px 12px;font-size:.875rem}.categories-panel__list[data-v-45420745]{gap:12px;display:grid}.category-row[data-v-45420745]{border:1px solid var(--color-border);background:var(--color-bg);border-radius:14px;grid-template-columns:auto minmax(0,1fr) auto;align-items:start;gap:12px;padding:12px;display:grid}.category-row__fields[data-v-45420745]{gap:8px;min-width:0;display:grid}.category-row__line[data-v-45420745]{grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:8px;display:grid}.category-row__id[data-v-45420745]{color:var(--color-text-muted);margin:0;font-family:ui-monospace,monospace;font-size:.72rem}.category-row__remove[data-v-45420745]{color:var(--color-danger,#dc3545);font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:6px 4px;font-size:.8125rem}.category-row[data-v-45420745] .image-picker__preview{width:72px;height:60px}@media (width>=900px){.categories-admin__grid[data-v-45420745]{grid-template-columns:1fr 1fr}}@media (width<=640px){.category-row[data-v-45420745]{grid-template-columns:1fr}.category-row__remove[data-v-45420745]{justify-self:start}}
-1
View File
@@ -1 +0,0 @@
import{A as e,I as t,N as n,Nt as r,a as i,b as a,c as o,et as s,g as c,m as l,nt as u,o as d,p as f,t as p,x as m}from"./http-WTIyGIuL.js";import{C as h}from"./index-BOMJTziC.js";import{t as g}from"./AdminPageSkeleton-D-u-8bBz.js";import{t as _}from"./AdminImagePicker-DcFp1rIR.js";var v={class:`categories-admin`},y={class:`admin-page-header`},b=[`disabled`],x={key:1,class:`categories-admin__grid`},S={class:`admin-card`},C={class:`categories-admin__list`},w={class:`admin-card`},T={class:`categories-admin__list`},E=d(m({__name:`CategoriesView`,setup(d){let m=[{id:`trousers`,title:`پارچه‌های شلواری`},{id:`suit`,title:`پارچه‌های کت‌شلواری`},{id:`shirt`,title:`پارچه‌های پیراهنی`}],E=[{id:`summer`,title:`تابستانه`},{id:`winter`,title:`زمستانه`},{id:`all_season`,title:`چهارفصل`}],D=s({trousers:null,suit:null,shirt:null,summer:null,winter:null,all_season:null}),O=u(!0),k=u(!1),A=i();async function j(){O.value=!0;try{let e=(await h.get()).categoryImagesJson??{};for(let t of Object.keys(D))D[t]=e[t]??null}catch(e){A.error(e instanceof p?e.message:`بارگذاری تصاویر دسته‌ها ناموفق بود.`)}finally{O.value=!1}}async function M(){k.value=!0;try{let e={};for(let[t,n]of Object.entries(D))n&&(e[t]=n);await h.update({categoryImagesJson:e}),A.success(`تصاویر دسته‌بندی ذخیره شد.`)}catch(e){A.error(e instanceof p?e.message:`ذخیره تصاویر ناموفق بود.`)}finally{k.value=!1}}return e(()=>{j()}),(e,i)=>(n(),c(`div`,v,[f(`div`,y,[i[0]||=f(`div`,null,[f(`h1`,{class:`admin-page-header__title`},`دسته‌بندی‌ها`),f(`p`,{class:`admin-page-header__sub`},`تصویر هر دسته در اپ کاربر نمایش داده می‌شود.`)],-1),f(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:k.value||O.value,onClick:M},r(k.value?`در حال ذخیره...`:`ذخیره تصاویر`),9,b)]),O.value?(n(),l(g,{key:0,variant:`cards`})):(n(),c(`div`,x,[f(`section`,S,[i[1]||=f(`h2`,null,`بر اساس کاربرد`,-1),f(`div`,C,[(n(),c(o,null,t(m,e=>a(_,{key:e.id,label:e.title,"model-value":D[e.id],"onUpdate:modelValue":t=>D[e.id]=t},null,8,[`label`,`model-value`,`onUpdate:modelValue`])),64))])]),f(`section`,w,[i[2]||=f(`h2`,null,`بر اساس فصل`,-1),f(`div`,T,[(n(),c(o,null,t(E,e=>a(_,{key:e.id,label:e.title,"model-value":D[e.id],"onUpdate:modelValue":t=>D[e.id]=t},null,8,[`label`,`model-value`,`onUpdate:modelValue`])),64))])])]))]))}}),[[`__scopeId`,`data-v-551e5d97`]]);export{E as default};
-1
View File
@@ -1 +0,0 @@
.admin-page-header__sub[data-v-551e5d97]{color:var(--color-text-muted);margin:4px 0 0;font-size:.85rem}.categories-admin__grid[data-v-551e5d97]{gap:16px;display:grid}.categories-admin__grid h2[data-v-551e5d97]{margin:0 0 14px;font-size:1rem}.categories-admin__list[data-v-551e5d97]{gap:16px;display:grid}@media (width>=900px){.categories-admin__grid[data-v-551e5d97]{grid-template-columns:1fr 1fr}}
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
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
+3
View File
@@ -0,0 +1,3 @@
import{$ as e,B as t,N as n,R as r,St as i,W as a,X as o,Y as s,Z as c,_t as l,ct as u,ht as d,mt as f,nt as p,ot as m,q as h,rt as g,tt as _,ut as v,v as y,vt as b,z as x}from"./index-BHqDDh7X.js";import{t as S}from"./AdminPageSkeleton-E50Kwv4j.js";import{t as C}from"./BaseEmptyState-vNIhbGdV.js";var w={class:`admin-page`},T={key:2,class:`admin-table`},E={dir:`ltr`},D=[`onClick`],O={class:`field`},k={class:`field`},A={class:`field`},j={class:`field`},M=[`disabled`],N=t(g({__name:`LevelsView`,setup(t){let g=b([]),N=b(!0),P=b(!1),F=b(null),I=x(),L=l({code:``,label:``,sortOrder:0,benefitsText:``}),R=s(()=>F.value?`ویرایش سطح`:`سطح جدید`);function z(e){return e.split(`
`).map(e=>e.trim()).filter(Boolean).map(e=>({title:e}))}async function B(){N.value=!0;try{g.value=await y.list()}catch(e){I.error(e instanceof n?e.message:`بارگذاری سطوح ناموفق بود`)}finally{N.value=!1}}function V(){F.value=null,Object.assign(L,{code:``,label:``,sortOrder:g.value.length+1,benefitsText:``}),P.value=!0}function H(e){F.value=e.id,Object.assign(L,{code:e.code,label:e.label,sortOrder:e.sortOrder,benefitsText:e.benefits.map(e=>e.title).join(`
`)}),P.value=!0}async function U(){try{let e={code:L.code.trim(),label:L.label.trim(),sortOrder:L.sortOrder,benefits:z(L.benefitsText)};F.value?await y.update(F.value,e):await y.create(e),P.value=!1,I.success(`ذخیره شد`),await B()}catch(e){I.error(e instanceof n?e.message:`ذخیره ناموفق بود`)}}return m(()=>{B()}),(t,n)=>(u(),e(`div`,w,[o(`header`,{class:`admin-page__head`},[n[5]||=o(`div`,null,[o(`h1`,null,`سطوح و مزایای مشتری`),o(`p`,null,`تعریف Level داخلی CRM و مزایای هر سطح (نمایش فقط در ادمین)`)],-1),o(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`سطح جدید`)]),N.value?(u(),c(S,{key:0,variant:`table`})):g.value.length?(u(),e(`table`,T,[n[6]||=o(`thead`,null,[o(`tr`,null,[o(`th`,null,`کد`),o(`th`,null,`عنوان`),o(`th`,null,`مزایا`),o(`th`,null,`مشتریان`),o(`th`)])],-1),o(`tbody`,null,[(u(!0),e(h,null,v(g.value,t=>(u(),e(`tr`,{key:t.id},[o(`td`,E,i(t.code),1),o(`td`,null,i(t.label),1),o(`td`,null,i(t.benefits.map(e=>e.title).join(`، `)||`—`),1),o(`td`,null,i(t.customersCount),1),o(`td`,null,[o(`button`,{type:`button`,class:`link`,onClick:e=>H(t)},`ویرایش`,8,D)])]))),128))])])):(u(),c(C,{key:1,title:`سطحی تعریف نشده`})),p(r,{open:P.value,title:R.value,onClose:n[4]||=e=>P.value=!1},{footer:f(()=>[o(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!L.code.trim()||!L.label.trim(),onClick:U},` ذخیره `,8,M)]),default:f(()=>[o(`label`,O,[n[7]||=_(`کد`,-1),d(o(`input`,{"onUpdate:modelValue":n[0]||=e=>L.code=e,class:`admin-input`,dir:`ltr`},null,512),[[a,L.code]])]),o(`label`,k,[n[8]||=_(`عنوان`,-1),d(o(`input`,{"onUpdate:modelValue":n[1]||=e=>L.label=e,class:`admin-input`},null,512),[[a,L.label]])]),o(`label`,A,[n[9]||=_(`ترتیب`,-1),d(o(`input`,{"onUpdate:modelValue":n[2]||=e=>L.sortOrder=e,type:`number`,class:`admin-input`},null,512),[[a,L.sortOrder,void 0,{number:!0}]])]),o(`label`,j,[n[10]||=_(` مزایا (هر خط یک مورد) `,-1),d(o(`textarea`,{"onUpdate:modelValue":n[3]||=e=>L.benefitsText=e,class:`admin-input`,rows:`4`},null,512),[[a,L.benefitsText]])])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-44c7f808`]]);export{N as default};
-3
View File
@@ -1,3 +0,0 @@
import{A as e,G as t,I as n,K as r,N as i,Nt as a,a as o,b as s,c,et as l,f as u,g as d,m as f,nt as p,o as m,p as h,t as g,x as _,y as v}from"./http-WTIyGIuL.js";import{L as y,V as b,v as x}from"./index-BOMJTziC.js";import{t as S}from"./AdminPageSkeleton-D-u-8bBz.js";import{t as C}from"./BaseEmptyState-Wc1O8rBP.js";var w={class:`admin-page`},T={key:2,class:`admin-table`},E={dir:`ltr`},D=[`onClick`],O={class:`field`},k={class:`field`},A={class:`field`},j={class:`field`},M=[`disabled`],N=m(_({__name:`LevelsView`,setup(m){let _=p([]),N=p(!0),P=p(!1),F=p(null),I=o(),L=l({code:``,label:``,sortOrder:0,benefitsText:``}),R=u(()=>F.value?`ویرایش سطح`:`سطح جدید`);function z(e){return e.split(`
`).map(e=>e.trim()).filter(Boolean).map(e=>({title:e}))}async function B(){N.value=!0;try{_.value=await x.list()}catch(e){I.error(e instanceof g?e.message:`بارگذاری سطوح ناموفق بود`)}finally{N.value=!1}}function V(){F.value=null,Object.assign(L,{code:``,label:``,sortOrder:_.value.length+1,benefitsText:``}),P.value=!0}function H(e){F.value=e.id,Object.assign(L,{code:e.code,label:e.label,sortOrder:e.sortOrder,benefitsText:e.benefits.map(e=>e.title).join(`
`)}),P.value=!0}async function U(){try{let e={code:L.code.trim(),label:L.label.trim(),sortOrder:L.sortOrder,benefits:z(L.benefitsText)};F.value?await x.update(F.value,e):await x.create(e),P.value=!1,I.success(`ذخیره شد`),await B()}catch(e){I.error(e instanceof g?e.message:`ذخیره ناموفق بود`)}}return e(()=>{B()}),(e,o)=>(i(),d(`div`,w,[h(`header`,{class:`admin-page__head`},[o[5]||=h(`div`,null,[h(`h1`,null,`سطوح و مزایای مشتری`),h(`p`,null,`تعریف Level داخلی CRM و مزایای هر سطح (نمایش فقط در ادمین)`)],-1),h(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`سطح جدید`)]),N.value?(i(),f(S,{key:0,variant:`table`})):_.value.length?(i(),d(`table`,T,[o[6]||=h(`thead`,null,[h(`tr`,null,[h(`th`,null,`کد`),h(`th`,null,`عنوان`),h(`th`,null,`مزایا`),h(`th`,null,`مشتریان`),h(`th`)])],-1),h(`tbody`,null,[(i(!0),d(c,null,n(_.value,e=>(i(),d(`tr`,{key:e.id},[h(`td`,E,a(e.code),1),h(`td`,null,a(e.label),1),h(`td`,null,a(e.benefits.map(e=>e.title).join(`، `)||`—`),1),h(`td`,null,a(e.customersCount),1),h(`td`,null,[h(`button`,{type:`button`,class:`link`,onClick:t=>H(e)},`ویرایش`,8,D)])]))),128))])])):(i(),f(C,{key:1,title:`سطحی تعریف نشده`})),s(y,{open:P.value,title:R.value,onClose:o[4]||=e=>P.value=!1},{footer:t(()=>[h(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!L.code.trim()||!L.label.trim(),onClick:U},` ذخیره `,8,M)]),default:t(()=>[h(`label`,O,[o[7]||=v(`کد`,-1),r(h(`input`,{"onUpdate:modelValue":o[0]||=e=>L.code=e,class:`admin-input`,dir:`ltr`},null,512),[[b,L.code]])]),h(`label`,k,[o[8]||=v(`عنوان`,-1),r(h(`input`,{"onUpdate:modelValue":o[1]||=e=>L.label=e,class:`admin-input`},null,512),[[b,L.label]])]),h(`label`,A,[o[9]||=v(`ترتیب`,-1),r(h(`input`,{"onUpdate:modelValue":o[2]||=e=>L.sortOrder=e,type:`number`,class:`admin-input`},null,512),[[b,L.sortOrder,void 0,{number:!0}]])]),h(`label`,j,[o[10]||=v(` مزایا (هر خط یک مورد) `,-1),r(h(`textarea`,{"onUpdate:modelValue":o[3]||=e=>L.benefitsText=e,class:`admin-input`,rows:`4`},null,512),[[b,L.benefitsText]])])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-44c7f808`]]);export{N as default};
+1
View File
@@ -0,0 +1 @@
import{$ as e,A as t,B as n,F as r,H as i,I as a,K as o,M as s,P as c,Q as l,St as u,V as d,W as f,X as p,Y as m,ct as h,et as g,ht as _,j as v,nt as y,rt as b,tt as x,vt as S,yt as C,z as w}from"./index-BHqDDh7X.js";var T={class:`login-page`},E={class:`login-page__form-wrap`},D={class:`login-page__card admin-card`},O={class:`login-page__header`},k={key:0,class:`login-page__mock-hint`},A={dir:`ltr`},j={dir:`ltr`},M={key:0,class:`admin-form__error`,role:`alert`},N={class:`admin-form__field`},P={class:`login-page__input-wrap`},F={class:`login-page__field-icon`,"aria-hidden":`true`},I={class:`admin-form__field`},L={class:`login-page__input-wrap login-page__input-wrap--password`},R={class:`login-page__field-icon`,"aria-hidden":`true`},z=[`type`],B=[`aria-label`],V={class:`login-page__remember`},H=[`disabled`],U={key:0,class:`admin-btn__spinner`,"aria-hidden":`true`},W=n(b({__name:`LoginView`,setup(n){let b=a(),W=r(),G=t(),K=w(),q=S(s()?v.phone:``),J=S(s()?v.password:``),Y=S(!0),X=S(!1),Z=s(),Q=m(()=>q.value.trim().length>0&&J.value.length>0);function $(e){return e.replace(/[۰-۹]/g,e=>String(`۰۱۲۳۴۵۶۷۸۹`.indexOf(e))).replace(/[٠-٩]/g,e=>String(`٠١٢٣٤٥٦٧٨٩`.indexOf(e)))}async function ee(){if(!Q.value){G.error=`لطفاً شماره موبایل و رمز عبور را وارد کنید.`;return}if(!await G.login($(q.value.trim()),J.value,Y.value))return;K.success(`خوش آمدید، ${G.fullName}`);let e=typeof W.query.redirect==`string`?W.query.redirect:`/`;b.replace(e)}return(t,n)=>(h(),e(`div`,T,[p(`div`,E,[p(`div`,D,[p(`div`,O,[n[8]||=p(`h2`,{class:`login-page__title`},`ورود به پنل مدیریت`,-1),n[9]||=p(`p`,{class:`login-page__subtitle`},`برای ادامه، اطلاعات حساب کاربری خود را وارد کنید.`,-1),C(Z)?(h(),e(`p`,k,[n[6]||=x(` حالت موک فعال است — `,-1),p(`span`,A,u(C(v).phone),1),n[7]||=x(` / `,-1),p(`span`,j,u(C(v).password),1)])):l(``,!0)]),p(`form`,{class:`admin-form login-page__form`,onSubmit:o(ee,[`prevent`])},[C(G).error?(h(),e(`p`,M,u(C(G).error),1)):l(``,!0),p(`div`,N,[n[10]||=p(`label`,{for:`login-phone`},`شماره موبایل`,-1),p(`div`,P,[p(`span`,F,[y(c,{name:`phone`,size:18})]),_(p(`input`,{id:`login-phone`,"onUpdate:modelValue":n[0]||=e=>q.value=e,type:`tel`,class:`admin-input login-page__input`,placeholder:`۰۹۱۲۱۱۱۱۲۲۲۲`,autocomplete:`username`,inputmode:`tel`,dir:`ltr`,onInput:n[1]||=e=>C(G).clearError()},null,544),[[f,q.value]])])]),p(`div`,I,[n[11]||=p(`label`,{for:`login-password`},`رمز عبور`,-1),p(`div`,L,[p(`span`,R,[y(c,{name:`lock`,size:18})]),_(p(`input`,{id:`login-password`,"onUpdate:modelValue":n[2]||=e=>J.value=e,type:X.value?`text`:`password`,class:`admin-input login-page__input login-page__input--with-toggle`,placeholder:`رمز عبور`,autocomplete:`current-password`,onInput:n[3]||=e=>C(G).clearError()},null,40,z),[[i,J.value]]),p(`button`,{type:`button`,class:`login-page__toggle-password`,"aria-label":X.value?`پنهان کردن رمز`:`نمایش رمز`,onClick:n[4]||=e=>X.value=!X.value},[y(c,{name:X.value?`eye-off`:`eye`,size:18},null,8,[`name`])],8,B)])]),p(`label`,V,[_(p(`input`,{"onUpdate:modelValue":n[5]||=e=>Y.value=e,type:`checkbox`},null,512),[[d,Y.value]]),n[12]||=p(`span`,null,`مرا به خاطر بسپار`,-1)]),p(`button`,{type:`submit`,class:`admin-btn admin-btn--primary login-page__submit`,disabled:!Q.value||C(G).loading},[C(G).loading?(h(),e(`span`,U)):l(``,!0),n[13]||=x(` ورود به پنل `,-1)],8,H)],32)])]),n[14]||=g(`<div class="login-page__brand" data-v-3c4fbd85><div class="login-page__brand-inner" data-v-3c4fbd85><p class="login-page__brand-badge" data-v-3c4fbd85>پارچه</p><h1 class="login-page__brand-title" data-v-3c4fbd85>پنل مدیریت سفارش‌های عمده</h1><p class="login-page__brand-text" data-v-3c4fbd85> مدیریت سفارش‌ها، مشتریان، قیمت‌گذاری، چک‌ها و گزارش‌های فروش در یک محیط یکپارچه. </p></div></div>`,1)]))}}),[[`__scopeId`,`data-v-3c4fbd85`]]);export{W as default};
-1
View File
@@ -1 +0,0 @@
import{K as e,N as t,Nt as n,a as r,b as i,f as a,g as o,h as s,nt as c,o as l,p as u,ut as d,v as f,x as p,y as m}from"./http-WTIyGIuL.js";import{A as h,F as g,M as _,N as v,P as y,R as b,U as x,V as S,j as C,z as w}from"./index-BOMJTziC.js";var T={class:`login-page`},E={class:`login-page__form-wrap`},D={class:`login-page__card admin-card`},O={class:`login-page__header`},k={key:0,class:`login-page__mock-hint`},A={dir:`ltr`},j={dir:`ltr`},M={key:0,class:`admin-form__error`,role:`alert`},N={class:`admin-form__field`},P={class:`login-page__input-wrap`},F={class:`login-page__field-icon`,"aria-hidden":`true`},I={class:`admin-form__field`},L={class:`login-page__input-wrap login-page__input-wrap--password`},R={class:`login-page__field-icon`,"aria-hidden":`true`},z=[`type`],B=[`aria-label`],V={class:`login-page__remember`},H=[`disabled`],U={key:0,class:`admin-btn__spinner`,"aria-hidden":`true`},W=l(p({__name:`LoginView`,setup(l){let p=g(),W=y(),G=h(),K=r(),q=c(_()?C.phone:``),J=c(_()?C.password:``),Y=c(!0),X=c(!1),Z=_(),Q=a(()=>q.value.trim().length>0&&J.value.length>0);function $(e){return e.replace(/[۰-۹]/g,e=>String(`۰۱۲۳۴۵۶۷۸۹`.indexOf(e))).replace(/[٠-٩]/g,e=>String(`٠١٢٣٤٥٦٧٨٩`.indexOf(e)))}async function ee(){if(!Q.value){G.error=`لطفاً شماره موبایل و رمز عبور را وارد کنید.`;return}if(!await G.login($(q.value.trim()),J.value,Y.value))return;K.success(`خوش آمدید، ${G.fullName}`);let e=typeof W.query.redirect==`string`?W.query.redirect:`/`;p.replace(e)}return(r,a)=>(t(),o(`div`,T,[u(`div`,E,[u(`div`,D,[u(`div`,O,[a[8]||=u(`h2`,{class:`login-page__title`},`ورود به پنل مدیریت`,-1),a[9]||=u(`p`,{class:`login-page__subtitle`},`برای ادامه، اطلاعات حساب کاربری خود را وارد کنید.`,-1),d(Z)?(t(),o(`p`,k,[a[6]||=m(` حالت موک فعال است — `,-1),u(`span`,A,n(d(C).phone),1),a[7]||=m(` / `,-1),u(`span`,j,n(d(C).password),1)])):s(``,!0)]),u(`form`,{class:`admin-form login-page__form`,onSubmit:x(ee,[`prevent`])},[d(G).error?(t(),o(`p`,M,n(d(G).error),1)):s(``,!0),u(`div`,N,[a[10]||=u(`label`,{for:`login-phone`},`شماره موبایل`,-1),u(`div`,P,[u(`span`,F,[i(v,{name:`phone`,size:18})]),e(u(`input`,{id:`login-phone`,"onUpdate:modelValue":a[0]||=e=>q.value=e,type:`tel`,class:`admin-input login-page__input`,placeholder:`۰۹۱۲۱۱۱۱۲۲۲۲`,autocomplete:`username`,inputmode:`tel`,dir:`ltr`,onInput:a[1]||=e=>d(G).clearError()},null,544),[[S,q.value]])])]),u(`div`,I,[a[11]||=u(`label`,{for:`login-password`},`رمز عبور`,-1),u(`div`,L,[u(`span`,R,[i(v,{name:`lock`,size:18})]),e(u(`input`,{id:`login-password`,"onUpdate:modelValue":a[2]||=e=>J.value=e,type:X.value?`text`:`password`,class:`admin-input login-page__input login-page__input--with-toggle`,placeholder:`رمز عبور`,autocomplete:`current-password`,onInput:a[3]||=e=>d(G).clearError()},null,40,z),[[w,J.value]]),u(`button`,{type:`button`,class:`login-page__toggle-password`,"aria-label":X.value?`پنهان کردن رمز`:`نمایش رمز`,onClick:a[4]||=e=>X.value=!X.value},[i(v,{name:X.value?`eye-off`:`eye`,size:18},null,8,[`name`])],8,B)])]),u(`label`,V,[e(u(`input`,{"onUpdate:modelValue":a[5]||=e=>Y.value=e,type:`checkbox`},null,512),[[b,Y.value]]),a[12]||=u(`span`,null,`مرا به خاطر بسپار`,-1)]),u(`button`,{type:`submit`,class:`admin-btn admin-btn--primary login-page__submit`,disabled:!Q.value||d(G).loading},[d(G).loading?(t(),o(`span`,U)):s(``,!0),a[13]||=m(` ورود به پنل `,-1)],8,H)],32)])]),a[14]||=f(`<div class="login-page__brand" data-v-3c4fbd85><div class="login-page__brand-inner" data-v-3c4fbd85><p class="login-page__brand-badge" data-v-3c4fbd85>پارچه</p><h1 class="login-page__brand-title" data-v-3c4fbd85>پنل مدیریت سفارش‌های عمده</h1><p class="login-page__brand-text" data-v-3c4fbd85> مدیریت سفارش‌ها، مشتریان، قیمت‌گذاری، چک‌ها و گزارش‌های فروش در یک محیط یکپارچه. </p></div></div>`,1)]))}}),[[`__scopeId`,`data-v-3c4fbd85`]]);export{W 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
-1
View File
@@ -1 +0,0 @@
.products-page__filters[data-v-89b21e26]{flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:16px;padding:12px 14px;display:flex}.products-page__search[data-v-89b21e26]{min-width:200px;color:var(--color-text-muted);flex:1;align-items:center;gap:10px;display:flex}.products-page__search-input[data-v-89b21e26]{background:0 0;border:none;outline:none;flex:1;font-size:.875rem}.products-page__catalog-filter[data-v-89b21e26]{width:min(260px,100%)}.products-page__table[data-v-89b21e26]{overflow:hidden}.product-thumb[data-v-89b21e26]{background:#e8eceb 50%/cover no-repeat;border-radius:8px;width:44px;height:36px;display:block}.product-thumb--empty[data-v-89b21e26]{background:#eef1f0}.product-images[data-v-89b21e26]{flex-wrap:wrap;gap:10px;display:flex}.product-images__item[data-v-89b21e26]{flex-direction:column;gap:6px;width:92px;display:flex}.product-images__thumb[data-v-89b21e26]{border:1px solid var(--color-border);background:#e8eceb 50%/cover no-repeat;border-radius:10px;width:92px;height:72px;display:block}.product-images__remove[data-v-89b21e26],.product-images__add[data-v-89b21e26]{border:1px dashed var(--color-border);cursor:pointer;text-align:center;background:0 0;border-radius:8px;padding:6px 8px;font-size:.75rem}.product-images__add[data-v-89b21e26]{min-width:92px;min-height:72px;color:var(--color-text-muted);place-items:center;display:grid}
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
.products-page__filters[data-v-881243f1]{flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:16px;padding:12px 14px;display:flex}.products-page__search[data-v-881243f1]{min-width:200px;color:var(--color-text-muted);flex:1;align-items:center;gap:10px;display:flex}.products-page__search-input[data-v-881243f1]{background:0 0;border:none;outline:none;flex:1;font-size:.875rem}.products-page__catalog-filter[data-v-881243f1]{width:min(260px,100%)}.products-page__table[data-v-881243f1]{overflow:hidden}.product-thumb[data-v-881243f1]{background:#e8eceb 50%/cover no-repeat;border-radius:8px;width:44px;height:36px;display:block}.product-thumb--empty[data-v-881243f1]{background:#eef1f0}.color-variants[data-v-881243f1]{flex-direction:column;gap:14px;display:flex}.color-variants__hint[data-v-881243f1]{color:var(--color-text-muted);margin:0 0 10px;font-size:.75rem;line-height:1.6}.color-variant[data-v-881243f1]{border:1px solid var(--color-border);background:var(--color-bg,#f7f8f7);border-radius:12px;flex-wrap:wrap;align-items:center;gap:12px;padding:10px;display:flex}.color-variant__fields[data-v-881243f1]{flex-direction:column;flex:1;align-items:flex-start;gap:8px;min-width:180px;display:flex}.product-images[data-v-881243f1]{flex-wrap:wrap;gap:10px;display:flex}.product-images__item[data-v-881243f1]{flex-direction:column;gap:6px;width:92px;display:flex}.product-images__thumb[data-v-881243f1]{border:1px solid var(--color-border);background:#e8eceb 50%/cover no-repeat;border-radius:10px;width:92px;height:72px;display:block}.product-images__remove[data-v-881243f1],.product-images__add[data-v-881243f1]{border:1px dashed var(--color-border);cursor:pointer;text-align:center;background:0 0;border-radius:8px;padding:6px 8px;font-size:.75rem}.product-images__add[data-v-881243f1]{min-width:92px;min-height:72px;color:var(--color-text-muted);place-items:center;display:grid}
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
View File
@@ -0,0 +1 @@
import{$ as e,B as t,St as n,bt as r,ct as i,rt as a,yt as o}from"./index-BHqDDh7X.js";import{t as s}from"./orderStatus-CYVblRJB.js";var c=t(a({__name:`StatusBadge`,props:{status:{}},setup(t){let a={active:{label:`فعال`,tone:`success`},inactive:{label:`غیرفعال`,tone:`neutral`},pending_check_review:{label:`در انتظار بررسی`,tone:`warning`},check_approved:{label:`تأیید شده`,tone:`success`},check_rejected:{label:`رد شده`,tone:`danger`}};return(c,l)=>(i(),e(`span`,{class:r([`admin-badge status-badge`,`admin-badge--${(o(s)[t.status]??a[t.status])?.tone??`neutral`}`])},n((o(s)[t.status]??a[t.status])?.label??t.status),3))}}),[[`__scopeId`,`data-v-23aa055a`]]);export{c as t};
-1
View File
@@ -1 +0,0 @@
import{At as e,N as t,Nt as n,g as r,o as i,ut as a,x as o}from"./http-WTIyGIuL.js";import{t as s}from"./orderStatus-CYVblRJB.js";var c=i(o({__name:`StatusBadge`,props:{status:{}},setup(i){let o={active:{label:`فعال`,tone:`success`},inactive:{label:`غیرفعال`,tone:`neutral`},pending_check_review:{label:`در انتظار بررسی`,tone:`warning`},check_approved:{label:`تأیید شده`,tone:`success`},check_rejected:{label:`رد شده`,tone:`danger`}};return(c,l)=>(t(),r(`span`,{class:e([`admin-badge status-badge`,`admin-badge--${(a(s)[i.status]??o[i.status])?.tone??`neutral`}`])},n((a(s)[i.status]??o[i.status])?.label??i.status),3))}}),[[`__scopeId`,`data-v-23aa055a`]]);export{c as t};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,E as n,N as r,St as i,X as a,Z as o,ct as s,ot as c,q as l,rt as u,tt as d,ut as f,vt as p,z as m}from"./index-BHqDDh7X.js";import{t as h}from"./AdminPageSkeleton-E50Kwv4j.js";import{t as g}from"./BaseEmptyState-vNIhbGdV.js";var _={class:`admin-page`},v={key:2,class:`admin-table`},y={class:`muted`,dir:`ltr`},b={class:`muted`,dir:`ltr`},x=[`onClick`],S=t(u({__name:`StockAlertsView`,setup(t){let u=p([]),S=p(!0),C=m();async function w(){S.value=!0;try{u.value=await n.list()}catch(e){C.error(e instanceof r?e.message:`بارگذاری ناموفق بود`)}finally{S.value=!1}}async function T(e){try{await n.notify(e),C.success(`اعلان موجودی ارسال شد`),await w()}catch(e){C.error(e instanceof r?e.message:`ارسال ناموفق بود`)}}return c(()=>{w()}),(t,n)=>(s(),e(`div`,_,[n[1]||=a(`header`,{class:`admin-page__head`},[a(`div`,null,[a(`h1`,null,`درخواست خبر موجودی`),a(`p`,null,`مشتریانی که برای محصول ناموجود درخواست اطلاع داده‌اند`)])],-1),S.value?(s(),o(h,{key:0,variant:`table`})):u.value.length?(s(),e(`table`,v,[n[0]||=a(`thead`,null,[a(`tr`,null,[a(`th`,null,`مشتری`),a(`th`,null,`محصول`),a(`th`,null,`موجودی فعلی`),a(`th`)])],-1),a(`tbody`,null,[(s(!0),e(l,null,f(u.value,t=>(s(),e(`tr`,{key:t.id},[a(`td`,null,[d(i(t.customerName)+` `,1),a(`span`,y,i(t.customerPhone),1)]),a(`td`,null,[d(i(t.productName)+` `,1),a(`span`,b,`(`+i(t.productCode)+`)`,1)]),a(`td`,null,i(t.stock??`—`),1),a(`td`,null,[a(`button`,{type:`button`,class:`admin-btn admin-btn--primary admin-btn--sm`,onClick:e=>T(t.id)},`ارسال اعلان`,8,x)])]))),128))])])):(s(),o(g,{key:1,title:`درخواستی فعال نیست`}))]))}}),[[`__scopeId`,`data-v-61131544`]]);export{S as default};
-1
View File
@@ -1 +0,0 @@
import{A as e,I as t,N as n,Nt as r,a as i,c as a,g as o,m as s,nt as c,o as l,p as u,t as d,x as f,y as p}from"./http-WTIyGIuL.js";import{E as m}from"./index-BOMJTziC.js";import{t as h}from"./AdminPageSkeleton-D-u-8bBz.js";import{t as g}from"./BaseEmptyState-Wc1O8rBP.js";var _={class:`admin-page`},v={key:2,class:`admin-table`},y={class:`muted`,dir:`ltr`},b={class:`muted`,dir:`ltr`},x=[`onClick`],S=l(f({__name:`StockAlertsView`,setup(l){let f=c([]),S=c(!0),C=i();async function w(){S.value=!0;try{f.value=await m.list()}catch(e){C.error(e instanceof d?e.message:`بارگذاری ناموفق بود`)}finally{S.value=!1}}async function T(e){try{await m.notify(e),C.success(`اعلان موجودی ارسال شد`),await w()}catch(e){C.error(e instanceof d?e.message:`ارسال ناموفق بود`)}}return e(()=>{w()}),(e,i)=>(n(),o(`div`,_,[i[1]||=u(`header`,{class:`admin-page__head`},[u(`div`,null,[u(`h1`,null,`درخواست خبر موجودی`),u(`p`,null,`مشتریانی که برای محصول ناموجود درخواست اطلاع داده‌اند`)])],-1),S.value?(n(),s(h,{key:0,variant:`table`})):f.value.length?(n(),o(`table`,v,[i[0]||=u(`thead`,null,[u(`tr`,null,[u(`th`,null,`مشتری`),u(`th`,null,`محصول`),u(`th`,null,`موجودی فعلی`),u(`th`)])],-1),u(`tbody`,null,[(n(!0),o(a,null,t(f.value,e=>(n(),o(`tr`,{key:e.id},[u(`td`,null,[p(r(e.customerName)+` `,1),u(`span`,y,r(e.customerPhone),1)]),u(`td`,null,[p(r(e.productName)+` `,1),u(`span`,b,`(`+r(e.productCode)+`)`,1)]),u(`td`,null,r(e.stock??`—`),1),u(`td`,null,[u(`button`,{type:`button`,class:`admin-btn admin-btn--primary admin-btn--sm`,onClick:t=>T(e.id)},`ارسال اعلان`,8,x)])]))),128))])])):(n(),s(g,{key:1,title:`درخواستی فعال نیست`}))]))}}),[[`__scopeId`,`data-v-61131544`]]);export{S as default};
+1
View File
@@ -0,0 +1 @@
import{$ as e,B as t,D as n,N as r,R as i,S as a,St as o,W as s,X as c,Y as l,Z as u,_t as d,ct as f,g as p,ht as m,mt as h,nt as g,ot as _,q as v,rt as y,tt as b,ut as x,vt as S,z as C}from"./index-BHqDDh7X.js";import{t as w}from"./AdminPageSkeleton-E50Kwv4j.js";import{t as T}from"./BaseEmptyState-vNIhbGdV.js";import{t as E}from"./AdminSelect-BjB6PHdr.js";var D={class:`admin-page`},O={class:`admin-page__head`},k={key:2,class:`admin-table`},A={class:`muted`,dir:`ltr`},j=[`onClick`],M={class:`field`},N={class:`field`},P={class:`field`},F=[`disabled`],I=t(y({__name:`SuggestionsView`,setup(t){let y=S([]),I=S(!0),L=S([]),R=S([]),z=S(!1),B=C(),V=d({customerId:``,productId:``,note:``}),H=l(()=>L.value.map(e=>({value:e.id,label:e.name?.trim()||e.phone}))),U=l(()=>R.value.map(e=>({value:e.id,label:`${e.name} (${e.productCode})`})));async function W(){I.value=!0;try{let[e,t,r]=await Promise.all([n.list(),p.list(),a.list()]);y.value=e,L.value=t,R.value=r}catch(e){B.error(e instanceof r?e.message:`بارگذاری ناموفق بود`)}finally{I.value=!1}}async function G(){try{await n.create({customerId:V.customerId,productId:V.productId,note:V.note.trim()||void 0}),z.value=!1,V.note=``,B.success(`پیشنهاد ثبت شد`),await W()}catch(e){B.error(e instanceof r?e.message:`ثبت ناموفق بود`)}}async function K(e){try{await n.setActive(e.id,!e.isActive),await W()}catch(e){B.error(e instanceof r?e.message:`به‌روزرسانی ناموفق بود`)}}return _(()=>{W()}),(t,n)=>(f(),e(`div`,D,[c(`header`,O,[n[5]||=c(`div`,null,[c(`h1`,null,`پیشنهاد محصول`),c(`p`,null,`پیشنهاد دستی محصول به مشتری`)],-1),c(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:n[0]||=e=>z.value=!0},`پیشنهاد جدید`)]),I.value?(f(),u(w,{key:0,variant:`table`})):y.value.length?(f(),e(`table`,k,[n[6]||=c(`thead`,null,[c(`tr`,null,[c(`th`,null,`مشتری`),c(`th`,null,`محصول`),c(`th`,null,`یادداشت`),c(`th`,null,`وضعیت`),c(`th`)])],-1),c(`tbody`,null,[(f(!0),e(v,null,x(y.value,t=>(f(),e(`tr`,{key:t.id},[c(`td`,null,o(t.customerName),1),c(`td`,null,[b(o(t.productName)+` `,1),c(`span`,A,`(`+o(t.productCode)+`)`,1)]),c(`td`,null,o(t.note||`—`),1),c(`td`,null,o(t.isActive?`فعال`:`غیرفعال`),1),c(`td`,null,[c(`button`,{type:`button`,class:`link`,onClick:e=>K(t)},o(t.isActive?`غیرفعال`:`فعال`),9,j)])]))),128))])])):(f(),u(T,{key:1,title:`پیشنهادی ثبت نشده`})),g(i,{open:z.value,title:`پیشنهاد محصول`,onClose:n[4]||=e=>z.value=!1},{footer:h(()=>[c(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!V.customerId||!V.productId,onClick:G},` ثبت `,8,F)]),default:h(()=>[c(`label`,M,[n[7]||=b(`مشتری `,-1),g(E,{modelValue:V.customerId,"onUpdate:modelValue":n[1]||=e=>V.customerId=e,options:H.value},null,8,[`modelValue`,`options`])]),c(`label`,N,[n[8]||=b(`محصول `,-1),g(E,{modelValue:V.productId,"onUpdate:modelValue":n[2]||=e=>V.productId=e,options:U.value},null,8,[`modelValue`,`options`])]),c(`label`,P,[n[9]||=b(`یادداشت`,-1),m(c(`textarea`,{"onUpdate:modelValue":n[3]||=e=>V.note=e,class:`admin-input`,rows:`3`},null,512),[[s,V.note]])])]),_:1},8,[`open`])]))}}),[[`__scopeId`,`data-v-56e3e728`]]);export{I as default};
-1
View File
@@ -1 +0,0 @@
import{A as e,G as t,I as n,K as r,N as i,Nt as a,a as o,b as s,c,et as l,f as u,g as d,m as f,nt as p,o as m,p as h,t as g,x as _,y as v}from"./http-WTIyGIuL.js";import{D as y,L as b,S as x,V as S,g as C}from"./index-BOMJTziC.js";import{t as w}from"./AdminPageSkeleton-D-u-8bBz.js";import{t as T}from"./BaseEmptyState-Wc1O8rBP.js";import{t as E}from"./AdminSelect-BVy_0-3W.js";var D={class:`admin-page`},O={class:`admin-page__head`},k={key:2,class:`admin-table`},A={class:`muted`,dir:`ltr`},j=[`onClick`],M={class:`field`},N={class:`field`},P={class:`field`},F=[`disabled`],I=m(_({__name:`SuggestionsView`,setup(m){let _=p([]),I=p(!0),L=p([]),R=p([]),z=p(!1),B=o(),V=l({customerId:``,productId:``,note:``}),H=u(()=>L.value.map(e=>({value:e.id,label:e.name?.trim()||e.phone}))),U=u(()=>R.value.map(e=>({value:e.id,label:`${e.name} (${e.productCode})`})));async function W(){I.value=!0;try{let[e,t,n]=await Promise.all([y.list(),C.list(),x.list()]);_.value=e,L.value=t,R.value=n}catch(e){B.error(e instanceof g?e.message:`بارگذاری ناموفق بود`)}finally{I.value=!1}}async function G(){try{await y.create({customerId:V.customerId,productId:V.productId,note:V.note.trim()||void 0}),z.value=!1,V.note=``,B.success(`پیشنهاد ثبت شد`),await W()}catch(e){B.error(e instanceof g?e.message:`ثبت ناموفق بود`)}}async function K(e){try{await y.setActive(e.id,!e.isActive),await W()}catch(e){B.error(e instanceof g?e.message:`به‌روزرسانی ناموفق بود`)}}return e(()=>{W()}),(e,o)=>(i(),d(`div`,D,[h(`header`,O,[o[5]||=h(`div`,null,[h(`h1`,null,`پیشنهاد محصول`),h(`p`,null,`پیشنهاد دستی محصول به مشتری`)],-1),h(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:o[0]||=e=>z.value=!0},`پیشنهاد جدید`)]),I.value?(i(),f(w,{key:0,variant:`table`})):_.value.length?(i(),d(`table`,k,[o[6]||=h(`thead`,null,[h(`tr`,null,[h(`th`,null,`مشتری`),h(`th`,null,`محصول`),h(`th`,null,`یادداشت`),h(`th`,null,`وضعیت`),h(`th`)])],-1),h(`tbody`,null,[(i(!0),d(c,null,n(_.value,e=>(i(),d(`tr`,{key:e.id},[h(`td`,null,a(e.customerName),1),h(`td`,null,[v(a(e.productName)+` `,1),h(`span`,A,`(`+a(e.productCode)+`)`,1)]),h(`td`,null,a(e.note||`—`),1),h(`td`,null,a(e.isActive?`فعال`:`غیرفعال`),1),h(`td`,null,[h(`button`,{type:`button`,class:`link`,onClick:t=>K(e)},a(e.isActive?`غیرفعال`:`فعال`),9,j)])]))),128))])])):(i(),f(T,{key:1,title:`پیشنهادی ثبت نشده`})),s(b,{open:z.value,title:`پیشنهاد محصول`,onClose:o[4]||=e=>z.value=!1},{footer:t(()=>[h(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!V.customerId||!V.productId,onClick:G},` ثبت `,8,F)]),default:t(()=>[h(`label`,M,[o[7]||=v(`مشتری `,-1),s(E,{modelValue:V.customerId,"onUpdate:modelValue":o[1]||=e=>V.customerId=e,options:H.value},null,8,[`modelValue`,`options`])]),h(`label`,N,[o[8]||=v(`محصول `,-1),s(E,{modelValue:V.productId,"onUpdate:modelValue":o[2]||=e=>V.productId=e,options:U.value},null,8,[`modelValue`,`options`])]),h(`label`,P,[o[9]||=v(`یادداشت`,-1),r(h(`textarea`,{"onUpdate:modelValue":o[3]||=e=>V.note=e,class:`admin-input`,rows:`3`},null,512),[[S,V.note]])])]),_:1},8,[`open`])]))}}),[[`__scopeId`,`data-v-56e3e728`]]);export{I 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
@@ -1 +0,0 @@
async function e(e){if(!e.type.startsWith(`image/`))throw Error(`فقط فایل تصویری مجاز است.`);if(e.size>8388608)throw Error(`حجم تصویر نباید بیشتر از ۸ مگابایت باشد.`);return n(await t(e))}function t(e){return new Promise((t,n)=>{let r=new FileReader;r.onload=()=>t(String(r.result??``)),r.onerror=()=>n(Error(`خواندن تصویر ناموفق بود.`)),r.readAsDataURL(e)})}function n(e){return new Promise((t,n)=>{let r=new Image;r.onload=()=>{let e=Math.min(1,960/Math.max(r.width,r.height)),i=Math.max(1,Math.round(r.width*e)),a=Math.max(1,Math.round(r.height*e)),o=document.createElement(`canvas`);o.width=i,o.height=a;let s=o.getContext(`2d`);if(!s){n(Error(`پردازش تصویر ناموفق بود.`));return}s.drawImage(r,0,0,i,a),t(o.toDataURL(`image/jpeg`,.82))},r.onerror=()=>n(Error(`تصویر نامعتبر است.`)),r.src=e})}export{e as t};
-1
View File
File diff suppressed because one or more lines are too long
+382
View File
File diff suppressed because one or more lines are too long
-382
View File
File diff suppressed because one or more lines are too long
+1 -2
View File
@@ -5,8 +5,7 @@
<link rel="icon" href="/admin/favicon.ico"> <link rel="icon" href="/admin/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>پنل مدیریت | پارچه</title> <title>پنل مدیریت | پارچه</title>
<script type="module" crossorigin src="/admin/assets/index-BOMJTziC.js"></script> <script type="module" crossorigin src="/admin/assets/index-BHqDDh7X.js"></script>
<link rel="modulepreload" crossorigin href="/admin/assets/http-WTIyGIuL.js">
<link rel="stylesheet" crossorigin href="/admin/assets/index-FKaOTPi2.css"> <link rel="stylesheet" crossorigin href="/admin/assets/index-FKaOTPi2.css">
</head> </head>
<body> <body>
+29 -1
View File
@@ -173,7 +173,7 @@ export type CreateProductPayload = {
widthCm?: number | null widthCm?: number | null
weightGrams?: number | null weightGrams?: number | null
isNew?: boolean isNew?: boolean
colorsJson?: string[] colorsJson?: Array<string | { name: string; imageUrl?: string }>
imageUrlsJson?: string[] imageUrlsJson?: string[]
tagsJson?: string[] tagsJson?: string[]
} }
@@ -439,6 +439,28 @@ export type SystemSettings = {
supportPhone: string | null supportPhone: string | null
supportEmail: string | null supportEmail: string | null
categoryImagesJson?: Record<string, string> categoryImagesJson?: Record<string, string>
categoryDefsJson?: {
usage: Array<{
id: string
title: string
subtitle: string
showTitle?: boolean
showSubtitle?: boolean
showIcon?: boolean
icon?: string
}>
seasons: Array<{
id: string
title: string
description: string
showTitle?: boolean
showDescription?: boolean
showCta?: boolean
showIcon?: boolean
ctaLabel?: string
icon?: string
}>
}
updatedAt: string updatedAt: string
} }
@@ -531,6 +553,9 @@ export type AppBanner = {
imageUrl: string imageUrl: string
displayOrder: number displayOrder: number
isActive: boolean isActive: boolean
showTitle?: boolean
showSubtitle?: boolean
showCta?: boolean
} }
export type BannerPayload = { export type BannerPayload = {
@@ -542,6 +567,9 @@ export type BannerPayload = {
imageUrl?: string | null imageUrl?: string | null
displayOrder?: number displayOrder?: number
isActive?: boolean isActive?: boolean
showTitle?: boolean
showSubtitle?: boolean
showCta?: boolean
} }
export const bannersApi = { export const bannersApi = {
+1
View File
@@ -233,6 +233,7 @@ export interface Product {
rollMeters?: number rollMeters?: number
isNew?: boolean isNew?: boolean
colors?: string[] colors?: string[]
colorImages?: Array<string | null>
imageUrls?: string[] imageUrls?: string[]
} }
+40 -3
View File
@@ -31,6 +31,9 @@ const form = reactive({
imageUrl: null as string | null, imageUrl: null as string | null,
displayOrder: 0, displayOrder: 0,
isActive: true, isActive: true,
showTitle: true,
showSubtitle: true,
showCta: true,
}) })
const modalTitle = computed(() => (editingId.value ? 'ویرایش بنر' : 'افزودن بنر')) const modalTitle = computed(() => (editingId.value ? 'ویرایش بنر' : 'افزودن بنر'))
@@ -61,6 +64,9 @@ function openAdd() {
imageUrl: null, imageUrl: null,
displayOrder: list.value.length, displayOrder: list.value.length,
isActive: true, isActive: true,
showTitle: true,
showSubtitle: true,
showCta: true,
}) })
modalOpen.value = true modalOpen.value = true
} }
@@ -76,6 +82,9 @@ function openEdit(banner: AppBanner) {
imageUrl: banner.imageUrl || null, imageUrl: banner.imageUrl || null,
displayOrder: banner.displayOrder, displayOrder: banner.displayOrder,
isActive: banner.isActive, isActive: banner.isActive,
showTitle: banner.showTitle !== false,
showSubtitle: banner.showSubtitle !== false,
showCta: banner.showCta !== false,
}) })
modalOpen.value = true modalOpen.value = true
} }
@@ -93,6 +102,9 @@ async function save() {
imageUrl: form.imageUrl, imageUrl: form.imageUrl,
displayOrder: form.displayOrder, displayOrder: form.displayOrder,
isActive: form.isActive, isActive: form.isActive,
showTitle: form.showTitle,
showSubtitle: form.showSubtitle,
showCta: form.showCta,
} }
if (editingId.value) await bannersApi.update(editingId.value, payload) if (editingId.value) await bannersApi.update(editingId.value, payload)
else await bannersApi.create(payload) else await bannersApi.create(payload)
@@ -185,9 +197,27 @@ onMounted(() => {
بخش نمایش بخش نمایش
<AdminSelect v-model="form.slot" :options="SLOT_OPTIONS" /> <AdminSelect v-model="form.slot" :options="SLOT_OPTIONS" />
</label> </label>
<label class="field">عنوان<input v-model="form.title" class="admin-input" /></label> <div class="field">
<label class="field">زیرعنوان<input v-model="form.subtitle" class="admin-input" /></label> <span class="field__head">
<label class="field">متن دکمه<input v-model="form.ctaLabel" class="admin-input" /></label> عنوان
<BaseSwitch v-model="form.showTitle" active-text="نمایش" inactive-text="مخفی" />
</span>
<input v-model="form.title" class="admin-input" />
</div>
<div class="field">
<span class="field__head">
زیرعنوان
<BaseSwitch v-model="form.showSubtitle" active-text="نمایش" inactive-text="مخفی" />
</span>
<input v-model="form.subtitle" class="admin-input" />
</div>
<div class="field">
<span class="field__head">
متن دکمه
<BaseSwitch v-model="form.showCta" active-text="نمایش" inactive-text="مخفی" />
</span>
<input v-model="form.ctaLabel" class="admin-input" />
</div>
<label class="field"> <label class="field">
لینک دکمه لینک دکمه
<input v-model="form.ctaLink" class="admin-input" dir="ltr" placeholder="/categories" /> <input v-model="form.ctaLink" class="admin-input" dir="ltr" placeholder="/categories" />
@@ -237,4 +267,11 @@ onMounted(() => {
margin-bottom: 12px; margin-bottom: 12px;
font-size: 0.875rem; font-size: 0.875rem;
} }
.field__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
</style> </style>
+592 -49
View File
@@ -1,51 +1,369 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue' import { computed, onMounted, reactive, ref } from 'vue'
import AdminPageSkeleton from '@/components/ui/AdminPageSkeleton.vue' import AdminPageSkeleton from '@/components/ui/AdminPageSkeleton.vue'
import AdminImagePicker from '@/components/ui/AdminImagePicker.vue' import AdminImagePicker from '@/components/ui/AdminImagePicker.vue'
import BaseSwitch from '@/components/ui/BaseSwitch.vue'
import { settingsApi } from '@/services/api' import { settingsApi } from '@/services/api'
import { ApiError } from '@/services/http' import { ApiError } from '@/services/http'
import { useToast } from '@/composables/useToast' import { useToast } from '@/composables/useToast'
const USAGE = [ type UsageDef = {
{ id: 'trousers', title: 'پارچه‌های شلواری' }, id: string
{ id: 'suit', title: 'پارچه‌های کت‌شلواری' }, title: string
{ id: 'shirt', title: 'پارچه‌های پیراهنی' }, subtitle: string
] icon: string
showTitle: boolean
showSubtitle: boolean
showIcon: boolean
}
type SeasonDef = {
id: string
title: string
description: string
ctaLabel: string
icon: string
showTitle: boolean
showDescription: boolean
showCta: boolean
showIcon: boolean
}
const SEASONS = [ const usage = ref<UsageDef[]>([])
{ id: 'summer', title: 'تابستانه' }, const seasons = ref<SeasonDef[]>([])
{ id: 'winter', title: 'زمستانه' }, const images = reactive<Record<string, string | null>>({})
{ id: 'all_season', title: 'چهارفصل' },
]
const images = reactive<Record<string, string | null>>({
trousers: null,
suit: null,
shirt: null,
summer: null,
winter: null,
all_season: null,
})
const loading = ref(true) const loading = ref(true)
const saving = ref(false) const saving = ref(false)
const toast = useToast() const toast = useToast()
const showAddUsage = ref(false)
const showAddSeason = ref(false)
const ICON_OPTIONS = [
{ value: 'bolt', label: 'آذرخش' },
{ value: 'feather', label: 'پر' },
{ value: 'diamond', label: 'الماس' },
{ value: 'grid', label: 'شبکه' },
{ value: 'bag', label: 'کیف' },
{ value: 'id-card', label: 'کارت' },
{ value: 'catalog', label: 'کالیته' },
{ value: 'headset', label: 'پشتیبانی' },
{ value: 'home', label: 'خانه' },
]
function defaultUsageIcon(id: string) {
if (id === 'shirt') return 'id-card'
if (id === 'suit') return 'bag'
return 'grid'
}
function defaultSeasonIcon(id: string) {
if (id === 'summer') return 'bolt'
if (id === 'winter') return 'feather'
return 'diamond'
}
const newUsage = reactive({
id: '',
title: '',
subtitle: 'مشاهده محصولات',
icon: 'grid',
showTitle: true,
showSubtitle: true,
showIcon: true,
})
const newSeason = reactive({
id: '',
title: '',
description: '',
ctaLabel: 'مشاهده محصولات',
icon: 'diamond',
showTitle: true,
showDescription: true,
showCta: true,
showIcon: true,
})
function visibleFlag(value: unknown) {
return value !== false
}
const usageIds = computed(() => usage.value.map((item) => item.id))
const seasonIds = computed(() => seasons.value.map((item) => item.id))
function slugify(raw: string) {
return raw
.trim()
.toLowerCase()
.replace(/\s+/g, '_')
.replace(/[^a-z0-9_\u0600-\u06ff-]/gi, '')
.slice(0, 48)
}
function syncImageKeys() {
const keys = [...usageIds.value, ...seasonIds.value]
for (const key of keys) {
if (!(key in images)) images[key] = null
}
for (const key of Object.keys(images)) {
if (!keys.includes(key)) delete images[key]
}
}
async function load() { async function load() {
loading.value = true loading.value = true
try { try {
const data = await settingsApi.get() const data = await settingsApi.get()
usage.value = data.categoryDefsJson?.usage?.length
? data.categoryDefsJson.usage.map((item) => ({
id: item.id,
title: item.title,
subtitle: item.subtitle || 'مشاهده محصولات',
icon: item.icon || defaultUsageIcon(item.id),
showTitle: visibleFlag(item.showTitle),
showSubtitle: visibleFlag(item.showSubtitle),
showIcon: visibleFlag(item.showIcon),
}))
: [
{
id: 'trousers',
title: 'پارچه‌های شلواری',
subtitle: 'مشاهده محصولات',
icon: 'grid',
showTitle: true,
showSubtitle: true,
showIcon: true,
},
{
id: 'suit',
title: 'پارچه‌های کت‌شلواری',
subtitle: 'مشاهده محصولات',
icon: 'bag',
showTitle: true,
showSubtitle: true,
showIcon: true,
},
{
id: 'shirt',
title: 'پارچه‌های پیراهنی',
subtitle: 'مشاهده محصولات',
icon: 'id-card',
showTitle: true,
showSubtitle: true,
showIcon: true,
},
]
seasons.value = data.categoryDefsJson?.seasons?.length
? data.categoryDefsJson.seasons.map((item) => ({
id: item.id,
title: item.title,
description: item.description || '',
ctaLabel: item.ctaLabel || 'مشاهده محصولات',
icon: item.icon || defaultSeasonIcon(item.id),
showTitle: visibleFlag(item.showTitle),
showDescription: visibleFlag(item.showDescription),
showCta: visibleFlag(item.showCta),
showIcon: visibleFlag(item.showIcon),
}))
: [
{
id: 'summer',
title: 'تابستانه',
description: 'سبک، خنک و مناسب فصل گرم',
ctaLabel: 'مشاهده محصولات',
icon: 'bolt',
showTitle: true,
showDescription: true,
showCta: true,
showIcon: true,
},
{
id: 'winter',
title: 'زمستانه',
description: 'گرم و مناسب فصل سرد',
ctaLabel: 'مشاهده محصولات',
icon: 'feather',
showTitle: true,
showDescription: true,
showCta: true,
showIcon: true,
},
{
id: 'all_season',
title: 'چهارفصل',
description: 'مناسب استفاده در بیشتر فصل‌های سال',
ctaLabel: 'مشاهده محصولات',
icon: 'diamond',
showTitle: true,
showDescription: true,
showCta: true,
showIcon: true,
},
]
syncImageKeys()
const stored = data.categoryImagesJson ?? {} const stored = data.categoryImagesJson ?? {}
for (const key of Object.keys(images)) { for (const key of Object.keys(images)) {
images[key] = stored[key] ?? null images[key] = stored[key] ?? null
} }
} catch (err) { } catch (err) {
toast.error(err instanceof ApiError ? err.message : 'بارگذاری تصاویر دسته‌ها ناموفق بود.') toast.error(err instanceof ApiError ? err.message : 'بارگذاری دسته‌ها ناموفق بود.')
if (!usage.value.length) {
usage.value = [
{
id: 'trousers',
title: 'پارچه‌های شلواری',
subtitle: 'مشاهده محصولات',
icon: 'grid',
showTitle: true,
showSubtitle: true,
showIcon: true,
},
{
id: 'suit',
title: 'پارچه‌های کت‌شلواری',
subtitle: 'مشاهده محصولات',
icon: 'bag',
showTitle: true,
showSubtitle: true,
showIcon: true,
},
{
id: 'shirt',
title: 'پارچه‌های پیراهنی',
subtitle: 'مشاهده محصولات',
icon: 'id-card',
showTitle: true,
showSubtitle: true,
showIcon: true,
},
]
}
if (!seasons.value.length) {
seasons.value = [
{
id: 'summer',
title: 'تابستانه',
description: 'سبک، خنک و مناسب فصل گرم',
ctaLabel: 'مشاهده محصولات',
icon: 'bolt',
showTitle: true,
showDescription: true,
showCta: true,
showIcon: true,
},
{
id: 'winter',
title: 'زمستانه',
description: 'گرم و مناسب فصل سرد',
ctaLabel: 'مشاهده محصولات',
icon: 'feather',
showTitle: true,
showDescription: true,
showCta: true,
showIcon: true,
},
{
id: 'all_season',
title: 'چهارفصل',
description: 'مناسب استفاده در بیشتر فصل‌های سال',
ctaLabel: 'مشاهده محصولات',
icon: 'diamond',
showTitle: true,
showDescription: true,
showCta: true,
showIcon: true,
},
]
}
syncImageKeys()
} finally { } finally {
loading.value = false loading.value = false
} }
} }
function addUsage() {
const id = slugify(newUsage.id || newUsage.title)
const title = newUsage.title.trim()
if (!id || !title) {
toast.error('شناسه و عنوان دسته کاربرد الزامی است.')
return
}
if (usageIds.value.includes(id) || seasonIds.value.includes(id)) {
toast.error('این شناسه از قبل وجود دارد.')
return
}
usage.value.push({
id,
title,
subtitle: newUsage.subtitle.trim() || 'مشاهده محصولات',
icon: newUsage.icon,
showTitle: newUsage.showTitle,
showSubtitle: newUsage.showSubtitle,
showIcon: newUsage.showIcon,
})
images[id] = null
newUsage.id = ''
newUsage.title = ''
newUsage.subtitle = 'مشاهده محصولات'
newUsage.icon = 'grid'
newUsage.showTitle = true
newUsage.showSubtitle = true
newUsage.showIcon = true
showAddUsage.value = false
}
function addSeason() {
const id = slugify(newSeason.id || newSeason.title)
const title = newSeason.title.trim()
if (!id || !title) {
toast.error('شناسه و عنوان دسته فصل الزامی است.')
return
}
if (usageIds.value.includes(id) || seasonIds.value.includes(id)) {
toast.error('این شناسه از قبل وجود دارد.')
return
}
seasons.value.push({
id,
title,
description: newSeason.description.trim(),
ctaLabel: newSeason.ctaLabel.trim() || 'مشاهده محصولات',
icon: newSeason.icon,
showTitle: newSeason.showTitle,
showDescription: newSeason.showDescription,
showCta: newSeason.showCta,
showIcon: newSeason.showIcon,
})
images[id] = null
newSeason.id = ''
newSeason.title = ''
newSeason.description = ''
newSeason.ctaLabel = 'مشاهده محصولات'
newSeason.icon = 'diamond'
newSeason.showTitle = true
newSeason.showDescription = true
newSeason.showCta = true
newSeason.showIcon = true
showAddSeason.value = false
}
function removeUsage(id: string) {
if (usage.value.length <= 1) {
toast.error('حداقل یک دسته کاربرد لازم است.')
return
}
usage.value = usage.value.filter((item) => item.id !== id)
delete images[id]
}
function removeSeason(id: string) {
if (seasons.value.length <= 1) {
toast.error('حداقل یک دسته فصل لازم است.')
return
}
seasons.value = seasons.value.filter((item) => item.id !== id)
delete images[id]
}
async function save() { async function save() {
saving.value = true saving.value = true
try { try {
@@ -53,10 +371,17 @@ async function save() {
for (const [key, value] of Object.entries(images)) { for (const [key, value] of Object.entries(images)) {
if (value) categoryImagesJson[key] = value if (value) categoryImagesJson[key] = value
} }
await settingsApi.update({ categoryImagesJson }) await settingsApi.update({
toast.success('تصاویر دسته‌بندی ذخیره شد.') categoryDefsJson: {
usage: usage.value,
seasons: seasons.value,
},
categoryImagesJson,
})
toast.success('دسته‌بندی‌ها ذخیره شد.')
await load()
} catch (err) { } catch (err) {
toast.error(err instanceof ApiError ? err.message : 'ذخیره تصاویر ناموفق بود.') toast.error(err instanceof ApiError ? err.message : 'ذخیره دسته‌بندی‌ها ناموفق بود.')
} finally { } finally {
saving.value = false saving.value = false
} }
@@ -72,37 +397,156 @@ onMounted(() => {
<div class="admin-page-header"> <div class="admin-page-header">
<div> <div>
<h1 class="admin-page-header__title">دسته‌بندی‌ها</h1> <h1 class="admin-page-header__title">دسته‌بندی‌ها</h1>
<p class="admin-page-header__sub">تصویر هر دسته در اپ کاربر نمایش داده می‌شود.</p> <p class="admin-page-header__sub">
دسته‌های کاربرد و فصل را اضافه کنید. با کلید «نمایش / مخفی» مشخص کنید عنوان و توضیح در اپ کاربر دیده شود.
</p>
</div> </div>
<button type="button" class="admin-btn admin-btn--primary" :disabled="saving || loading" @click="save"> <button type="button" class="admin-btn admin-btn--primary" :disabled="saving || loading" @click="save">
{{ saving ? 'در حال ذخیره...' : 'ذخیره تصاویر' }} {{ saving ? 'در حال ذخیره...' : 'ذخیره تغییرات' }}
</button> </button>
</div> </div>
<AdminPageSkeleton v-if="loading" variant="cards" /> <AdminPageSkeleton v-if="loading" variant="cards" />
<div v-else class="categories-admin__grid"> <div v-else class="categories-admin__grid">
<section class="admin-card"> <section class="admin-card categories-panel">
<h2>بر اساس کاربرد</h2> <div class="categories-panel__head">
<div class="categories-admin__list"> <div>
<AdminImagePicker <h2>بر اساس کاربرد</h2>
v-for="item in USAGE" <p>{{ usage.length }} دسته برای فیلتر محصول در اپ کاربر</p>
:key="item.id" </div>
:label="item.title" <button type="button" class="admin-btn admin-btn--outline" @click="showAddUsage = !showAddUsage">
:model-value="images[item.id]" {{ showAddUsage ? 'بستن فرم' : '+ افزودن دسته' }}
@update:model-value="images[item.id] = $event" </button>
</div>
<div v-if="showAddUsage" class="categories-panel__add">
<input v-model="newUsage.title" class="admin-input" type="text" placeholder="عنوان (مثلاً پارچه‌های مانتویی)" />
<input
v-model="newUsage.id"
class="admin-input"
type="text"
dir="ltr"
placeholder="شناسه انگلیسی (اختیاری، مثلاً mantle)"
/> />
<input v-model="newUsage.subtitle" class="admin-input" type="text" placeholder="زیرعنوان" />
<button type="button" class="admin-btn admin-btn--primary" @click="addUsage">ثبت در لیست</button>
</div>
<p v-if="!usage.length" class="categories-panel__empty">هنوز دسته‌ای ثبت نشده. با «افزودن دسته» شروع کنید.</p>
<div v-else class="categories-panel__list">
<article v-for="item in usage" :key="item.id" class="category-row">
<AdminImagePicker
:model-value="images[item.id]"
@update:model-value="images[item.id] = $event"
/>
<div class="category-row__fields">
<div class="category-row__line">
<input v-model="item.title" class="admin-input" type="text" placeholder="عنوان دسته" />
<BaseSwitch
v-model="item.showTitle"
active-text="نمایش"
inactive-text="مخفی"
/>
</div>
<div class="category-row__line">
<input v-model="item.subtitle" class="admin-input" type="text" placeholder="زیرعنوان" />
<BaseSwitch
v-model="item.showSubtitle"
active-text="نمایش"
inactive-text="مخفی"
/>
</div>
<div class="category-row__line">
<select v-model="item.icon" class="admin-input" aria-label="آیکون">
<option v-for="opt in ICON_OPTIONS" :key="opt.value" :value="opt.value">
آیکون: {{ opt.label }}
</option>
</select>
<BaseSwitch
v-model="item.showIcon"
active-text="نمایش"
inactive-text="مخفی"
/>
</div>
<p class="category-row__id" dir="ltr">{{ item.id }}</p>
</div>
<button type="button" class="category-row__remove" @click="removeUsage(item.id)">حذف</button>
</article>
</div> </div>
</section> </section>
<section class="admin-card">
<h2>بر اساس فصل</h2> <section class="admin-card categories-panel">
<div class="categories-admin__list"> <div class="categories-panel__head">
<AdminImagePicker <div>
v-for="item in SEASONS" <h2>بر اساس فصل</h2>
:key="item.id" <p>{{ seasons.length }} دسته فصلی برای فیلتر محصول</p>
:label="item.title" </div>
:model-value="images[item.id]" <button type="button" class="admin-btn admin-btn--outline" @click="showAddSeason = !showAddSeason">
@update:model-value="images[item.id] = $event" {{ showAddSeason ? 'بستن فرم' : '+ افزودن دسته' }}
</button>
</div>
<div v-if="showAddSeason" class="categories-panel__add">
<input v-model="newSeason.title" class="admin-input" type="text" placeholder="عنوان (مثلاً بهاره)" />
<input
v-model="newSeason.id"
class="admin-input"
type="text"
dir="ltr"
placeholder="شناسه انگلیسی (اختیاری، مثلاً spring)"
/> />
<input v-model="newSeason.description" class="admin-input" type="text" placeholder="توضیح" />
<button type="button" class="admin-btn admin-btn--primary" @click="addSeason">ثبت در لیست</button>
</div>
<p v-if="!seasons.length" class="categories-panel__empty">هنوز دسته‌ای ثبت نشده. با «افزودن دسته» شروع کنید.</p>
<div v-else class="categories-panel__list">
<article v-for="item in seasons" :key="item.id" class="category-row">
<AdminImagePicker
:model-value="images[item.id]"
@update:model-value="images[item.id] = $event"
/>
<div class="category-row__fields">
<div class="category-row__line">
<input v-model="item.title" class="admin-input" type="text" placeholder="عنوان دسته" />
<BaseSwitch
v-model="item.showTitle"
active-text="نمایش"
inactive-text="مخفی"
/>
</div>
<div class="category-row__line">
<input v-model="item.description" class="admin-input" type="text" placeholder="توضیح" />
<BaseSwitch
v-model="item.showDescription"
active-text="نمایش"
inactive-text="مخفی"
/>
</div>
<div class="category-row__line">
<input v-model="item.ctaLabel" class="admin-input" type="text" placeholder="متن لینک (مشاهده محصولات)" />
<BaseSwitch
v-model="item.showCta"
active-text="نمایش"
inactive-text="مخفی"
/>
</div>
<div class="category-row__line">
<select v-model="item.icon" class="admin-input" aria-label="آیکون">
<option v-for="opt in ICON_OPTIONS" :key="opt.value" :value="opt.value">
آیکون: {{ opt.label }}
</option>
</select>
<BaseSwitch
v-model="item.showIcon"
active-text="نمایش"
inactive-text="مخفی"
/>
</div>
<p class="category-row__id" dir="ltr">{{ item.id }}</p>
</div>
<button type="button" class="category-row__remove" @click="removeSeason(item.id)">حذف</button>
</article>
</div> </div>
</section> </section>
</div> </div>
@@ -119,16 +563,105 @@ onMounted(() => {
.categories-admin__grid { .categories-admin__grid {
display: grid; display: grid;
gap: 16px; gap: 16px;
align-items: start;
} }
.categories-admin__grid h2 { .categories-panel {
margin: 0 0 14px; padding: 20px;
font-size: 1rem;
} }
.categories-admin__list { .categories-panel__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: 12px;
margin-bottom: 16px;
padding-bottom: 14px;
border-bottom: 1px solid var(--color-border);
}
.categories-panel__head h2 {
margin: 0 0 4px;
font-size: 1.05rem;
font-weight: 700;
}
.categories-panel__head p {
margin: 0;
font-size: 0.78rem;
color: var(--color-text-muted);
}
.categories-panel__add {
display: grid; display: grid;
gap: 16px; gap: 8px;
margin-bottom: 16px;
padding: 14px;
border: 1px dashed var(--color-border);
border-radius: 12px;
background: var(--color-bg);
}
.categories-panel__empty {
margin: 0;
padding: 28px 12px;
text-align: center;
color: var(--color-text-muted);
font-size: 0.875rem;
background: var(--color-bg);
border-radius: 12px;
}
.categories-panel__list {
display: grid;
gap: 12px;
}
.category-row {
display: grid;
grid-template-columns: auto minmax(0, 1fr) auto;
gap: 12px;
align-items: start;
padding: 12px;
border: 1px solid var(--color-border);
border-radius: 14px;
background: var(--color-bg);
}
.category-row__fields {
display: grid;
gap: 8px;
min-width: 0;
}
.category-row__line {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: 8px;
align-items: center;
}
.category-row__id {
margin: 0;
color: var(--color-text-muted);
font-size: 0.72rem;
font-family: ui-monospace, monospace;
}
.category-row__remove {
border: none;
background: transparent;
color: var(--color-danger, #dc3545);
font: inherit;
font-size: 0.8125rem;
cursor: pointer;
padding: 6px 4px;
white-space: nowrap;
}
.category-row :deep(.image-picker__preview) {
width: 72px;
height: 60px;
} }
@media (min-width: 900px) { @media (min-width: 900px) {
@@ -136,4 +669,14 @@ onMounted(() => {
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
} }
} }
@media (max-width: 640px) {
.category-row {
grid-template-columns: 1fr;
}
.category-row__remove {
justify-self: start;
}
}
</style> </style>
+133 -31
View File
@@ -2,12 +2,13 @@
import { computed, onMounted, reactive, ref } from 'vue' import { computed, onMounted, reactive, ref } from 'vue'
import AdminPageSkeleton from '@/components/ui/AdminPageSkeleton.vue' import AdminPageSkeleton from '@/components/ui/AdminPageSkeleton.vue'
import BaseModal from '@/components/ui/BaseModal.vue' import BaseModal from '@/components/ui/BaseModal.vue'
import AdminImagePicker from '@/components/ui/AdminImagePicker.vue'
import BaseSwitch from '@/components/ui/BaseSwitch.vue' import BaseSwitch from '@/components/ui/BaseSwitch.vue'
import BaseEmptyState from '@/components/ui/BaseEmptyState.vue' import BaseEmptyState from '@/components/ui/BaseEmptyState.vue'
import ActiveSwitchCell from '@/components/ui/ActiveSwitchCell.vue' import ActiveSwitchCell from '@/components/ui/ActiveSwitchCell.vue'
import AdminIcon from '@/components/icons/AdminIcon.vue' import AdminIcon from '@/components/icons/AdminIcon.vue'
import AdminSelect from '@/components/ui/AdminSelect.vue' import AdminSelect from '@/components/ui/AdminSelect.vue'
import { catalogsApi, productsApi } from '@/services/api' import { catalogsApi, productsApi, settingsApi } from '@/services/api'
import { ApiError } from '@/services/http' import { ApiError } from '@/services/http'
import { useToast } from '@/composables/useToast' import { useToast } from '@/composables/useToast'
import { formatCurrency, formatNumber } from '@/utils/format' import { formatCurrency, formatNumber } from '@/utils/format'
@@ -17,6 +18,17 @@ import { fileToCatalogImage } from '@/utils/catalogImage'
const productList = ref<Product[]>([]) const productList = ref<Product[]>([])
const catalogs = ref<Catalog[]>([]) const catalogs = ref<Catalog[]>([])
const usageOptions = ref<Array<{ value: string; label: string }>>([
{ value: 'trousers', label: 'شلوار' },
{ value: 'suit', label: 'کت‌وشلوار' },
{ value: 'shirt', label: 'پیراهن' },
{ value: 'other', label: 'سایر' },
])
const seasonOptions = ref<Array<{ value: string; label: string }>>([
{ value: 'summer', label: 'تابستان' },
{ value: 'winter', label: 'زمستان' },
{ value: 'all_season', label: 'چهارفصل' },
])
const searchQuery = ref('') const searchQuery = ref('')
const catalogFilter = ref('all') const catalogFilter = ref('all')
const loading = ref(true) const loading = ref(true)
@@ -58,8 +70,8 @@ const emptyForm = () => ({
subtitle: '', subtitle: '',
rollMeters: 50, rollMeters: 50,
isNew: false, isNew: false,
colorsText: '', colorRows: [{ name: '', imageUrl: '' }] as Array<{ name: string; imageUrl: string }>,
imageUrls: [] as string[], extraImageUrls: [] as string[],
}) })
const form = reactive(emptyForm()) const form = reactive(emptyForm())
@@ -92,12 +104,28 @@ const filteredProducts = computed(() => {
async function loadData() { async function loadData() {
loading.value = true loading.value = true
try { try {
const [productsData, catalogsData] = await Promise.all([ const [productsData, catalogsData, settings] = await Promise.all([
productsApi.list(), productsApi.list(),
catalogsApi.list(), catalogsApi.list(),
settingsApi.get().catch(() => null),
]) ])
productList.value = productsData productList.value = productsData
catalogs.value = catalogsData catalogs.value = catalogsData
if (settings?.categoryDefsJson?.usage?.length) {
usageOptions.value = [
...settings.categoryDefsJson.usage.map((item) => ({
value: item.id,
label: item.title,
})),
{ value: 'other', label: 'سایر' },
]
}
if (settings?.categoryDefsJson?.seasons?.length) {
seasonOptions.value = settings.categoryDefsJson.seasons.map((item) => ({
value: item.id,
label: item.title,
}))
}
} catch (err) { } catch (err) {
toast.error(err instanceof ApiError ? err.message : 'بارگذاری محصولات انجام نشد.') toast.error(err instanceof ApiError ? err.message : 'بارگذاری محصولات انجام نشد.')
} finally { } finally {
@@ -139,13 +167,33 @@ function openEditModal(product: Product) {
form.subtitle = (product as { subtitle?: string }).subtitle ?? '' form.subtitle = (product as { subtitle?: string }).subtitle ?? ''
form.rollMeters = (product as { rollMeters?: number }).rollMeters ?? 50 form.rollMeters = (product as { rollMeters?: number }).rollMeters ?? 50
form.isNew = (product as { isNew?: boolean }).isNew ?? false form.isNew = (product as { isNew?: boolean }).isNew ?? false
form.colorsText = (product.colors ?? []).join(', ') const colors = product.colors ?? []
form.imageUrls = [...(product.imageUrls ?? [])] const colorImages = product.colorImages ?? []
const gallery = [...(product.imageUrls ?? [])]
if (colors.length) {
form.colorRows = colors.map((name, index) => ({
name,
imageUrl: colorImages[index] || gallery[index] || '',
}))
const used = new Set(form.colorRows.map((row) => row.imageUrl).filter(Boolean))
form.extraImageUrls = gallery.filter((url) => !used.has(url))
} else {
form.colorRows = [{ name: product.color || '', imageUrl: gallery[0] || '' }]
form.extraImageUrls = gallery.slice(1)
}
formError.value = '' formError.value = ''
} }
function parseListText(value: string): string[] { function addColorRow() {
return value.split(',').map((part) => part.trim()).filter(Boolean) form.colorRows.push({ name: '', imageUrl: '' })
}
function removeColorRow(index: number) {
if (form.colorRows.length <= 1) {
form.colorRows[0] = { name: '', imageUrl: '' }
return
}
form.colorRows.splice(index, 1)
} }
const imageBusy = ref(false) const imageBusy = ref(false)
@@ -159,7 +207,7 @@ async function addProductImages(event: Event) {
formError.value = '' formError.value = ''
try { try {
const next = await Promise.all(files.map((file) => fileToCatalogImage(file))) const next = await Promise.all(files.map((file) => fileToCatalogImage(file)))
form.imageUrls = [...form.imageUrls, ...next] form.extraImageUrls = [...form.extraImageUrls, ...next]
} catch (e) { } catch (e) {
formError.value = e instanceof Error ? e.message : 'بارگذاری تصویر ناموفق بود.' formError.value = e instanceof Error ? e.message : 'بارگذاری تصویر ناموفق بود.'
} finally { } finally {
@@ -168,7 +216,7 @@ async function addProductImages(event: Event) {
} }
function removeProductImage(index: number) { function removeProductImage(index: number) {
form.imageUrls.splice(index, 1) form.extraImageUrls.splice(index, 1)
} }
function closeModal() { function closeModal() {
@@ -189,6 +237,12 @@ async function submitProduct() {
return return
} }
const colorEntries = form.colorRows
.map((row) => ({
name: row.name.trim(),
imageUrl: row.imageUrl.trim() || undefined,
}))
.filter((row) => row.name)
const catalogPayload = { const catalogPayload = {
usageCategory: form.usageCategory, usageCategory: form.usageCategory,
season: form.season, season: form.season,
@@ -197,8 +251,8 @@ async function submitProduct() {
subtitle: form.subtitle.trim() || null, subtitle: form.subtitle.trim() || null,
rollMeters: form.rollMeters, rollMeters: form.rollMeters,
isNew: form.isNew, isNew: form.isNew,
colorsJson: parseListText(form.colorsText), colorsJson: colorEntries,
imageUrlsJson: form.imageUrls, imageUrlsJson: form.extraImageUrls,
} }
const productCode = form.productCode.trim() || `${cat.code}-NEW` const productCode = form.productCode.trim() || `${cat.code}-NEW`
saving.value = true saving.value = true
@@ -207,7 +261,7 @@ async function submitProduct() {
const updated = await productsApi.update(editingId.value, { const updated = await productsApi.update(editingId.value, {
catalogId: form.catalogId, catalogId: form.catalogId,
name: form.name.trim(), name: form.name.trim(),
color: form.color.trim() || '—', color: form.color.trim() || colorEntries[0]?.name || '—',
code: productCode, code: productCode,
productCode, productCode,
minMeterage: form.minMeterage, minMeterage: form.minMeterage,
@@ -225,7 +279,7 @@ async function submitProduct() {
companyId: cat.companyId, companyId: cat.companyId,
catalogId: form.catalogId, catalogId: form.catalogId,
name: form.name.trim(), name: form.name.trim(),
color: form.color.trim() || '—', color: form.color.trim() || colorEntries[0]?.name || '—',
code: productCode, code: productCode,
productCode, productCode,
minMeterage: form.minMeterage, minMeterage: form.minMeterage,
@@ -445,18 +499,17 @@ function clearFilters() {
<div class="admin-form__field"> <div class="admin-form__field">
<label for="product-usage">کاربرد</label> <label for="product-usage">کاربرد</label>
<select id="product-usage" v-model="form.usageCategory" class="admin-input"> <select id="product-usage" v-model="form.usageCategory" class="admin-input">
<option value="trousers">شلوار</option> <option v-for="opt in usageOptions" :key="opt.value" :value="opt.value">
<option value="suit">کت‌وشلوار</option> {{ opt.label }}
<option value="shirt">پیراهن</option> </option>
<option value="other">سایر</option>
</select> </select>
</div> </div>
<div class="admin-form__field"> <div class="admin-form__field">
<label for="product-season">فصل</label> <label for="product-season">فصل</label>
<select id="product-season" v-model="form.season" class="admin-input"> <select id="product-season" v-model="form.season" class="admin-input">
<option value="summer">تابستان</option> <option v-for="opt in seasonOptions" :key="opt.value" :value="opt.value">
<option value="winter">زمستان</option> {{ opt.label }}
<option value="all_season">چهارفصل</option> </option>
</select> </select>
</div> </div>
</div> </div>
@@ -484,21 +537,37 @@ function clearFilters() {
</div> </div>
<div class="admin-form__field"> <div class="admin-form__field">
<label for="product-colors">رنگ‌ها (با ویرگول)</label> <label>رنگ‌ها و تصویر هر رنگ</label>
<input <p class="color-variants__hint">برای هر رنگ یک نام و یک عکس جدا بگذارید؛ در فروشگاه با انتخاب همان رنگ، همان عکس نمایش داده می‌شود.</p>
id="product-colors" <div class="color-variants">
v-model="form.colorsText" <div v-for="(row, index) in form.colorRows" :key="index" class="color-variant">
type="text" <AdminImagePicker
class="admin-input" :model-value="row.imageUrl || null"
placeholder="مثلاً: سرمه‌ای، خاکستری، بژ" @update:model-value="row.imageUrl = $event ?? ''"
/> />
<div class="color-variant__fields">
<input
v-model="row.name"
type="text"
class="admin-input"
:placeholder="`نام رنگ ${index + 1}`"
/>
<button type="button" class="admin-btn admin-btn--ghost admin-btn--xs" @click="removeColorRow(index)">
حذف این رنگ
</button>
</div>
</div>
<button type="button" class="admin-btn admin-btn--outline" @click="addColorRow">
+ افزودن رنگ
</button>
</div>
</div> </div>
<div class="admin-form__field"> <div class="admin-form__field">
<label>تصاویر محصول</label> <label>تصاویر عمومی محصول (اختیاری)</label>
<div class="product-images"> <div class="product-images">
<div <div
v-for="(src, index) in form.imageUrls" v-for="(src, index) in form.extraImageUrls"
:key="`${index}-${src.slice(0, 24)}`" :key="`${index}-${src.slice(0, 24)}`"
class="product-images__item" class="product-images__item"
> >
@@ -580,6 +649,39 @@ function clearFilters() {
background: #eef1f0; background: #eef1f0;
} }
.color-variants {
display: flex;
flex-direction: column;
gap: 14px;
}
.color-variants__hint {
margin: 0 0 10px;
color: var(--color-text-muted);
font-size: 0.75rem;
line-height: 1.6;
}
.color-variant {
display: flex;
flex-wrap: wrap;
gap: 12px;
align-items: center;
padding: 10px;
border: 1px solid var(--color-border);
border-radius: 12px;
background: var(--color-bg, #f7f8f7);
}
.color-variant__fields {
flex: 1;
min-width: 180px;
display: flex;
flex-direction: column;
gap: 8px;
align-items: flex-start;
}
.product-images { .product-images {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;