feat: complete application updates
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
After Width: | Height: | Size: 10 KiB |
@@ -0,0 +1 @@
|
||||
import{O as e,Z as t,at as n,g as r,l as i}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{a}from"./http-Djf2pGxE.js";import{B as o}from"./index-CznUUXv3.js";import{t as s}from"./BaseSwitch-TV5Yvtf7.js";function c(e=480){return new Promise(t=>{window.setTimeout(t,e)})}function l(e){let n=t(null),r=t(null),i=a();async function o(t,a){if(n.value)return a;let o=!a;if(!o&&e.confirmOff&&!await e.confirmOff())return a;n.value=t,r.value=null;let s=a;try{return await e.onPersist(o),i.success(e.successMessage?.(o)??(o?`وضعیت فعال شد.`:`وضعیت غیرفعال شد.`)),o}catch{return r.value=t,i.error(e.errorMessage??`بهروزرسانی وضعیت انجام نشد.`),s}finally{n.value=null}}function s(e){return n.value===e}function c(e){return r.value===e}return{toggle:o,isLoading:s,hasError:c}}var u=r({__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(),i(s,{"model-value":t.modelValue,loading:n(f)(t.id),error:n(p)(t.id),"onUpdate:modelValue":m},null,8,[`model-value`,`loading`,`error`]))}});export{u as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{O as e,Z as t,at as n,g as r,l as i}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{L as a,z as o}from"./index-CXa4EMLW.js";import{t as s}from"./BaseSwitch-TV5Yvtf7.js";function c(e=480){return new Promise(t=>{window.setTimeout(t,e)})}function l(e){let n=t(null),r=t(null),i=o();async function a(t,a){if(n.value)return a;let o=!a;if(!o&&e.confirmOff&&!await e.confirmOff())return a;n.value=t,r.value=null;let s=a;try{return await e.onPersist(o),i.success(e.successMessage?.(o)??(o?`وضعیت فعال شد.`:`وضعیت غیرفعال شد.`)),o}catch{return r.value=t,i.error(e.errorMessage??`بهروزرسانی وضعیت انجام نشد.`),s}finally{n.value=null}}function s(e){return n.value===e}function c(e){return r.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(t,{emit:r}){let o=t,u=r,{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(e){if(e===o.modelValue)return;let t=o.entityType??`مورد`;if(!await a({title:e?`فعالسازی ${t}`:`غیرفعالسازی ${t}`,message:e?`آیا از فعال کردن «${o.entityName}» مطمئن هستید؟`:`آیا از غیرفعال کردن «${o.entityName}» مطمئن هستید؟`,tone:e?`primary`:`danger`,confirmLabel:e?`فعال کردن`:`غیرفعال کردن`,cancelLabel:`انصراف`}))return;let n=await d(o.id,o.modelValue);u(`update:modelValue`,n)}return(r,a)=>(e(),i(s,{"model-value":t.modelValue,loading:n(f)(t.id),error:n(p)(t.id),"onUpdate:modelValue":m},null,8,[`model-value`,`loading`,`error`]))}});export{u as t};
|
||||
@@ -0,0 +1 @@
|
||||
import{O as e,Tt as t,d as n,g as r,s as i,t as a}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{c as o,r as s}from"./index-CznUUXv3.js";var c=[`id`,`value`,`placeholder`,`disabled`],l=a(r({__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(r,{emit:a}){let l=r,u=a,d=i(()=>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(i,a)=>(e(),n(`input`,{id:r.id,type:`text`,inputmode:`numeric`,dir:`ltr`,class:t([`admin-currency-input`,r.inputClass]),value:d.value,placeholder:r.placeholder,disabled:r.disabled,onInput:f},null,42,c))}}),[[`__scopeId`,`data-v-56435fc8`]]);export{l as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{O as e,Tt as t,d as n,g as r,s as i,t as a}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{o,t as s}from"./index-CXa4EMLW.js";var c=[`id`,`value`,`placeholder`,`disabled`],l=a(r({__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(r,{emit:a}){let l=r,u=a,d=i(()=>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(i,a)=>(e(),n(`input`,{id:r.id,type:`text`,inputmode:`numeric`,dir:`ltr`,class:t([`admin-currency-input`,r.inputClass]),value:d.value,placeholder:r.placeholder,disabled:r.disabled,onInput:f},null,42,c))}}),[[`__scopeId`,`data-v-56435fc8`]]);export{l as t};
|
||||
@@ -1 +0,0 @@
|
||||
import{C as e,Dt as t,E as n,M as r,O as i,Ot as a,R as o,Tt as s,V as c,Z as l,a as u,c as d,d as f,g as p,h as m,j as h,l as g,r as _,s as v,t as y,u as b}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{G as x,K as S,O as C,P as w,W as T}from"./index-CXa4EMLW.js";var E=[`id`],D=[`aria-label`,`aria-expanded`],O={class:`admin-select__trigger-content`},k={class:`admin-select__value`},A={class:`admin-select__chevron`,"aria-hidden":`true`},j=[`aria-label`],M={key:0,class:`admin-select__search`,role:`presentation`},N=[`placeholder`],P={key:1,class:`admin-select__empty`,role:`presentation`},F=[`aria-selected`,`onClick`],I={class:`admin-select__option-label`},L=y(p({__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(p,{emit:y}){let L=p,R=y,z=l(!1),B=l(``),V=l(null),H=l(null),U=l(null),W=l({});C(V,()=>{z.value=!1,B.value=``},{ignore:[H]});let G=v(()=>L.options.find(e=>e.value===L.modelValue)),K=v(()=>G.value?.label??L.placeholder),q=v(()=>{let e=B.value.trim().toLowerCase();return e?L.options.filter(t=>t.label.toLowerCase().includes(e)):L.options});function J(){let e=V.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));W.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 Y(e){e?(window.addEventListener(`resize`,J),window.addEventListener(`scroll`,J,!0)):(window.removeEventListener(`resize`,J),window.removeEventListener(`scroll`,J,!0))}async function X(){z.value=!z.value,z.value&&(await e(),J(),L.searchable&&(B.value=``,await e(),U.value?.focus()))}function Z(e){R(`update:modelValue`,e),z.value=!1,B.value=``}return o(z,t=>{Y(t),t&&e(()=>J())}),n(()=>{Y(!1)}),(e,n)=>(i(),f(`div`,{id:p.id,ref_key:`rootRef`,ref:V,class:s([`admin-select`,{"admin-select--open":z.value,"admin-select--block":p.block}])},[d(`button`,{type:`button`,class:`admin-select__trigger`,"aria-label":p.ariaLabel,"aria-expanded":z.value,"aria-haspopup":`listbox`,onClick:X},[d(`span`,O,[r(e.$slots,`option-leading`,{option:G.value},void 0,!0),d(`span`,k,a(K.value),1)]),d(`span`,A,[m(w,{name:`chevron-down`,size:16})])],8,D),(i(),g(u,{to:`body`},[z.value?(i(),f(`ul`,{key:0,ref_key:`menuRef`,ref:H,class:`admin-select__menu`,role:`listbox`,"aria-label":p.ariaLabel,style:t(W.value)},[p.searchable?(i(),f(`li`,M,[c(d(`input`,{ref_key:`searchInputRef`,ref:U,"onUpdate:modelValue":n[0]||=e=>B.value=e,type:`search`,class:`admin-select__search-input`,placeholder:p.searchPlaceholder,onClick:n[1]||=S(()=>{},[`stop`]),onKeydown:n[2]||=x(S(e=>z.value=!1,[`stop`]),[`esc`])},null,40,N),[[T,B.value]])])):b(``,!0),q.value.length?b(``,!0):(i(),f(`li`,P,` موردی یافت نشد `)),(i(!0),f(_,null,h(q.value,t=>(i(),f(`li`,{key:t.value,role:`presentation`},[d(`button`,{type:`button`,class:s([`admin-select__option`,{"admin-select__option--active":t.value===p.modelValue}]),role:`option`,"aria-selected":t.value===p.modelValue,onClick:e=>Z(t.value)},[d(`span`,I,[r(e.$slots,`option-leading`,{option:t},void 0,!0),d(`span`,null,a(t.label),1)]),t.value===p.modelValue?(i(),g(w,{key:0,name:`check`,size:16,class:`admin-select__check`})):b(``,!0)],10,F)]))),128))],12,j)):b(``,!0)]))],10,E))}}),[[`__scopeId`,`data-v-4623e322`]]);export{L as t};
|
||||
@@ -0,0 +1 @@
|
||||
import{C as e,Dt as t,E as n,M as r,O as i,Ot as a,R as o,Tt as s,V as c,Z as l,a as u,c as d,d as f,g as p,h as m,j as h,l as g,r as _,s as v,t as y,u as b}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{f as x,m as S,p as C}from"./http-Djf2pGxE.js";import{L as w,M as T}from"./index-CznUUXv3.js";var E=[`id`],D=[`aria-label`,`aria-expanded`],O={class:`admin-select__trigger-content`},k={class:`admin-select__value`},A={class:`admin-select__chevron`,"aria-hidden":`true`},j=[`aria-label`],M={key:0,class:`admin-select__search`,role:`presentation`},N=[`placeholder`],P={key:1,class:`admin-select__empty`,role:`presentation`},F=[`aria-selected`,`onClick`],I={class:`admin-select__option-label`},L=y(p({__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(p,{emit:y}){let L=p,R=y,z=l(!1),B=l(``),V=l(null),H=l(null),U=l(null),W=l({});T(V,()=>{z.value=!1,B.value=``},{ignore:[H]});let G=v(()=>L.options.find(e=>e.value===L.modelValue)),K=v(()=>G.value?.label??L.placeholder),q=v(()=>{let e=B.value.trim().toLowerCase();return e?L.options.filter(t=>t.label.toLowerCase().includes(e)):L.options});function J(){let e=V.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));W.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 Y(e){e?(window.addEventListener(`resize`,J),window.addEventListener(`scroll`,J,!0)):(window.removeEventListener(`resize`,J),window.removeEventListener(`scroll`,J,!0))}async function X(){z.value=!z.value,z.value&&(await e(),J(),L.searchable&&(B.value=``,await e(),U.value?.focus()))}function Z(e){R(`update:modelValue`,e),z.value=!1,B.value=``}return o(z,t=>{Y(t),t&&e(()=>J())}),n(()=>{Y(!1)}),(e,n)=>(i(),f(`div`,{id:p.id,ref_key:`rootRef`,ref:V,class:s([`admin-select`,{"admin-select--open":z.value,"admin-select--block":p.block}])},[d(`button`,{type:`button`,class:`admin-select__trigger`,"aria-label":p.ariaLabel,"aria-expanded":z.value,"aria-haspopup":`listbox`,onClick:X},[d(`span`,O,[r(e.$slots,`option-leading`,{option:G.value},void 0,!0),d(`span`,k,a(K.value),1)]),d(`span`,A,[m(w,{name:`chevron-down`,size:16})])],8,D),(i(),g(u,{to:`body`},[z.value?(i(),f(`ul`,{key:0,ref_key:`menuRef`,ref:H,class:`admin-select__menu`,role:`listbox`,"aria-label":p.ariaLabel,style:t(W.value)},[p.searchable?(i(),f(`li`,M,[c(d(`input`,{ref_key:`searchInputRef`,ref:U,"onUpdate:modelValue":n[0]||=e=>B.value=e,type:`search`,class:`admin-select__search-input`,placeholder:p.searchPlaceholder,onClick:n[1]||=S(()=>{},[`stop`]),onKeydown:n[2]||=C(S(e=>z.value=!1,[`stop`]),[`esc`])},null,40,N),[[x,B.value]])])):b(``,!0),q.value.length?b(``,!0):(i(),f(`li`,P,` موردی یافت نشد `)),(i(!0),f(_,null,h(q.value,t=>(i(),f(`li`,{key:t.value,role:`presentation`},[d(`button`,{type:`button`,class:s([`admin-select__option`,{"admin-select__option--active":t.value===p.modelValue}]),role:`option`,"aria-selected":t.value===p.modelValue,onClick:e=>Z(t.value)},[d(`span`,I,[r(e.$slots,`option-leading`,{option:t},void 0,!0),d(`span`,null,a(t.label),1)]),t.value===p.modelValue?(i(),g(w,{key:0,name:`check`,size:16,class:`admin-select__check`})):b(``,!0)],10,F)]))),128))],12,j)):b(``,!0)]))],10,E))}}),[[`__scopeId`,`data-v-4623e322`]]);export{L as t};
|
||||
@@ -1 +1 @@
|
||||
import{M as e,O as t,Ot as n,Tt as r,c as i,d as a,g as o,h as s,t as c,u as l}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{P as u}from"./index-CXa4EMLW.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=c(o({__name:`BaseEmptyState`,props:{icon:{default:`file`},title:{},description:{},compact:{type:Boolean}},emits:[`action`,`secondary`],setup(o){return(c,h)=>(t(),a(`div`,{class:r([`base-empty`,{"base-empty--compact":o.compact}])},[i(`div`,d,[s(u,{name:o.icon,size:o.compact?28:36},null,8,[`name`,`size`])]),i(`h3`,f,n(o.title),1),o.description?(t(),a(`p`,p,n(o.description),1)):l(``,!0),c.$slots.default||c.$slots.secondary?(t(),a(`div`,m,[e(c.$slots,`default`,{},void 0,!0),e(c.$slots,`secondary`,{},void 0,!0)])):l(``,!0)],2))}}),[[`__scopeId`,`data-v-47ea81e4`]]);export{h as t};
|
||||
import{M as e,O as t,Ot as n,Tt as r,c as i,d as a,g as o,h as s,t as c,u as l}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{L as u}from"./index-CznUUXv3.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=c(o({__name:`BaseEmptyState`,props:{icon:{default:`file`},title:{},description:{},compact:{type:Boolean}},emits:[`action`,`secondary`],setup(o){return(c,h)=>(t(),a(`div`,{class:r([`base-empty`,{"base-empty--compact":o.compact}])},[i(`div`,d,[s(u,{name:o.icon,size:o.compact?28:36},null,8,[`name`,`size`])]),i(`h3`,f,n(o.title),1),o.description?(t(),a(`p`,p,n(o.description),1)):l(``,!0),c.$slots.default||c.$slots.secondary?(t(),a(`div`,m,[e(c.$slots,`default`,{},void 0,!0),e(c.$slots,`secondary`,{},void 0,!0)])):l(``,!0)],2))}}),[[`__scopeId`,`data-v-47ea81e4`]]);export{h as t};
|
||||
@@ -0,0 +1 @@
|
||||
import{B as e,O as t,Ot as n,T as r,V as i,Y as a,Z as o,c as s,d as c,g as l,h as u,j as d,l as f,m as p,r as m,s as h,t as g}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{a as _,f as v,t as y}from"./http-Djf2pGxE.js";import{V as b,p as x}from"./index-CznUUXv3.js";import{t as S}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as C}from"./BaseSwitch-TV5Yvtf7.js";import{t as w}from"./BaseEmptyState-D6sk-aI_.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=g(l({__name:`BrandsView`,setup(l){let g=o([]),P=o(!0),F=o(!1),I=o(null),L=_(),R=a({name:``,slug:``,description:``,displayOrder:0,isActive:!0}),z=h(()=>I.value?`ویرایش برند`:`افزودن برند`);async function B(){P.value=!0;try{g.value=await x.list()}catch(e){L.error(e instanceof y?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 x.update(I.value,e):await x.create(e),F.value=!1,L.success(`ذخیره شد`),await B()}catch(e){L.error(e instanceof y?e.message:`ذخیره ناموفق بود`)}}return r(()=>{B()}),(r,a)=>(t(),c(`div`,T,[s(`header`,{class:`admin-page__head`},[a[6]||=s(`div`,null,[s(`h1`,null,`برندها`)],-1),s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`افزودن برند`)]),P.value?(t(),f(S,{key:0,variant:`table`})):g.value.length?(t(),c(`table`,E,[a[7]||=s(`thead`,null,[s(`tr`,null,[s(`th`,null,`نام`),s(`th`,null,`شناسه لاتین`),s(`th`,null,`مشتریان`),s(`th`,null,`فعال`),s(`th`)])],-1),s(`tbody`,null,[(t(!0),c(m,null,d(g.value,e=>(t(),c(`tr`,{key:e.id},[s(`td`,null,n(e.name),1),s(`td`,D,n(e.slug||`—`),1),s(`td`,null,n(e.customersCount??0),1),s(`td`,null,n(e.isActive?`بله`:`خیر`),1),s(`td`,null,[s(`button`,{type:`button`,class:`link`,onClick:t=>H(e)},`ویرایش`,8,O)])]))),128))])])):(t(),f(w,{key:1,title:`برندی ثبت نشده`})),u(b,{open:F.value,title:z.value,onClose:a[5]||=e=>F.value=!1},{footer:e(()=>[s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!R.name.trim(),onClick:U},`ذخیره`,8,N)]),default:e(()=>[s(`label`,k,[a[8]||=p(`نام`,-1),i(s(`input`,{"onUpdate:modelValue":a[0]||=e=>R.name=e,class:`admin-input`},null,512),[[v,R.name]])]),s(`label`,A,[a[9]||=p(`شناسه لاتین`,-1),i(s(`input`,{"onUpdate:modelValue":a[1]||=e=>R.slug=e,class:`admin-input`,dir:`ltr`,placeholder:`مثلاً nademi-silk`},null,512),[[v,R.slug]])]),s(`label`,j,[a[10]||=p(`توضیح`,-1),i(s(`textarea`,{"onUpdate:modelValue":a[2]||=e=>R.description=e,class:`admin-input`,rows:`3`},null,512),[[v,R.description]])]),s(`label`,M,[a[11]||=p(`ترتیب`,-1),i(s(`input`,{"onUpdate:modelValue":a[3]||=e=>R.displayOrder=e,type:`number`,class:`admin-input`},null,512),[[v,R.displayOrder,void 0,{number:!0}]])]),u(C,{modelValue:R.isActive,"onUpdate:modelValue":a[4]||=e=>R.isActive=e,label:`فعال`},null,8,[`modelValue`])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-88043806`]]);export{P as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{B as e,O as t,Ot as n,T as r,V as i,Y as a,Z as o,c as s,d as c,g as l,h as u,j as d,l as f,m as p,r as m,s as h,t as g}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{N as _,R as v,W as y,u as b,z as x}from"./index-CXa4EMLW.js";import{t as S}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as C}from"./BaseSwitch-TV5Yvtf7.js";import{t as w}from"./BaseEmptyState-BmO9aDcK.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=g(l({__name:`BrandsView`,setup(l){let g=o([]),P=o(!0),F=o(!1),I=o(null),L=x(),R=a({name:``,slug:``,description:``,displayOrder:0,isActive:!0}),z=h(()=>I.value?`ویرایش برند`:`افزودن برند`);async function B(){P.value=!0;try{g.value=await b.list()}catch(e){L.error(e instanceof _?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 b.update(I.value,e):await b.create(e),F.value=!1,L.success(`ذخیره شد`),await B()}catch(e){L.error(e instanceof _?e.message:`ذخیره ناموفق بود`)}}return r(()=>{B()}),(r,a)=>(t(),c(`div`,T,[s(`header`,{class:`admin-page__head`},[a[6]||=s(`div`,null,[s(`h1`,null,`برندها`)],-1),s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`افزودن برند`)]),P.value?(t(),f(S,{key:0,variant:`table`})):g.value.length?(t(),c(`table`,E,[a[7]||=s(`thead`,null,[s(`tr`,null,[s(`th`,null,`نام`),s(`th`,null,`شناسه لاتین`),s(`th`,null,`مشتریان`),s(`th`,null,`فعال`),s(`th`)])],-1),s(`tbody`,null,[(t(!0),c(m,null,d(g.value,e=>(t(),c(`tr`,{key:e.id},[s(`td`,null,n(e.name),1),s(`td`,D,n(e.slug||`—`),1),s(`td`,null,n(e.customersCount??0),1),s(`td`,null,n(e.isActive?`بله`:`خیر`),1),s(`td`,null,[s(`button`,{type:`button`,class:`link`,onClick:t=>H(e)},`ویرایش`,8,O)])]))),128))])])):(t(),f(w,{key:1,title:`برندی ثبت نشده`})),u(v,{open:F.value,title:z.value,onClose:a[5]||=e=>F.value=!1},{footer:e(()=>[s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!R.name.trim(),onClick:U},`ذخیره`,8,N)]),default:e(()=>[s(`label`,k,[a[8]||=p(`نام`,-1),i(s(`input`,{"onUpdate:modelValue":a[0]||=e=>R.name=e,class:`admin-input`},null,512),[[y,R.name]])]),s(`label`,A,[a[9]||=p(`شناسه لاتین`,-1),i(s(`input`,{"onUpdate:modelValue":a[1]||=e=>R.slug=e,class:`admin-input`,dir:`ltr`,placeholder:`مثلاً nademi-silk`},null,512),[[y,R.slug]])]),s(`label`,j,[a[10]||=p(`توضیح`,-1),i(s(`textarea`,{"onUpdate:modelValue":a[2]||=e=>R.description=e,class:`admin-input`,rows:`3`},null,512),[[y,R.description]])]),s(`label`,M,[a[11]||=p(`ترتیب`,-1),i(s(`input`,{"onUpdate:modelValue":a[3]||=e=>R.displayOrder=e,type:`number`,class:`admin-input`},null,512),[[y,R.displayOrder,void 0,{number:!0}]])]),u(C,{modelValue:R.isActive,"onUpdate:modelValue":a[4]||=e=>R.isActive=e,label:`فعال`},null,8,[`modelValue`])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-88043806`]]);export{P as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{O as e,Ot as t,T as n,Z as r,at as i,c as a,d as o,g as s,h as c,j as l,l as u,m as d,r as f,t as p,u as m}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{a as h,t as g}from"./http-Djf2pGxE.js";import{h as _,o as v}from"./index-CznUUXv3.js";import{t as y}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as b}from"./BaseEmptyState-D6sk-aI_.js";import{t as x}from"./AdminSelect-DK-_otkw.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=p(s({__name:`CatalogRequestsView`,setup(s){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=r([]),I=r(``),L=r(!0),R=r(null),z=h();async function B(){L.value=!0;try{F.value=await _.list(I.value||void 0)}catch(e){z.error(e instanceof g?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 g?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 g?e.message:`بهروزرسانی محل ناموفق بود`)}finally{R.value=null}}}function U(e){return P[e]??e}return n(()=>{B()}),(n,r)=>(e(),o(`div`,S,[a(`header`,C,[r[1]||=a(`div`,null,[a(`h1`,null,`درخواست کالیته فیزیکی`),a(`p`,null,`بررسی، گردش وضعیت و محل نگهداری اقلام`)],-1),c(x,{modelValue:I.value,"onUpdate:modelValue":[r[0]||=e=>I.value=e,B],options:p},null,8,[`modelValue`])]),L.value?(e(),u(y,{key:0,variant:`cards`})):F.value.length?(e(!0),o(f,{key:2},l(F.value,n=>(e(),o(`div`,{key:n.id,class:`request-card`},[a(`div`,w,[a(`div`,null,[a(`strong`,null,t(n.customerName),1),a(`span`,T,t(n.customerPhone),1),a(`span`,E,t(i(N)[n.status]??n.status),1)]),c(x,{"model-value":n.status,options:p.filter(e=>e.value),"onUpdate:modelValue":e=>V(n.id,String(e))},null,8,[`model-value`,`options`,`onUpdate:modelValue`])]),n.note?(e(),o(`p`,D,t(n.note),1)):m(``,!0),a(`table`,O,[r[2]||=a(`thead`,null,[a(`tr`,null,[a(`th`,null,`محصول`),a(`th`,null,`کد`),a(`th`,null,`تعداد`),a(`th`,null,`محل`)])],-1),a(`tbody`,null,[(e(!0),o(f,null,l(n.items,n=>(e(),o(`tr`,{key:n.id},[a(`td`,null,t(n.productName),1),a(`td`,k,t(n.productCode),1),a(`td`,null,t(n.quantity),1),a(`td`,null,[c(x,{"model-value":n.holderType,options:M,"onUpdate:modelValue":e=>H(n.id,String(e))},null,8,[`model-value`,`onUpdate:modelValue`]),n.movements?.length?(e(),o(`ul`,A,[(e(!0),o(f,null,l(n.movements,n=>(e(),o(`li`,{key:n.id},[d(t(U(n.fromHolder))+` → `+t(U(n.toHolder))+` `,1),a(`span`,j,t(i(v)(n.movedAt)),1)]))),128))])):m(``,!0)])]))),128))])])]))),128)):(e(),u(b,{key:1,title:`درخواستی نیست`}))]))}}),[[`__scopeId`,`data-v-747d8f88`]]);export{M as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{O as e,Ot as t,T as n,Z as r,at as i,c as a,d as o,g as s,h as c,j as l,l as u,m as d,r as f,t as p,u as m}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{N as h,f as g,i as _,z as v}from"./index-CXa4EMLW.js";import{t as y}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as b}from"./BaseEmptyState-BmO9aDcK.js";import{t as x}from"./AdminSelect-9UfEJQzN.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=p(s({__name:`CatalogRequestsView`,setup(s){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=r([]),I=r(``),L=r(!0),R=r(null),z=v();async function B(){L.value=!0;try{F.value=await g.list(I.value||void 0)}catch(e){z.error(e instanceof h?e.message:`بارگذاری انجام نشد`)}finally{L.value=!1}}async function V(e,t){try{await g.updateStatus(e,t),z.success(`وضعیت بهروز شد`),await B()}catch(e){z.error(e instanceof h?e.message:`بهروزرسانی ناموفق بود`)}}async function H(e,t){if(!R.value){R.value=e;try{await g.updateHolder(e,{holderType:t}),z.success(`محل نگهداری بهروز شد`),await B()}catch(e){z.error(e instanceof h?e.message:`بهروزرسانی محل ناموفق بود`)}finally{R.value=null}}}function U(e){return P[e]??e}return n(()=>{B()}),(n,r)=>(e(),o(`div`,S,[a(`header`,C,[r[1]||=a(`div`,null,[a(`h1`,null,`درخواست کالیته فیزیکی`),a(`p`,null,`بررسی، گردش وضعیت و محل نگهداری اقلام`)],-1),c(x,{modelValue:I.value,"onUpdate:modelValue":[r[0]||=e=>I.value=e,B],options:p},null,8,[`modelValue`])]),L.value?(e(),u(y,{key:0,variant:`cards`})):F.value.length?(e(!0),o(f,{key:2},l(F.value,n=>(e(),o(`div`,{key:n.id,class:`request-card`},[a(`div`,w,[a(`div`,null,[a(`strong`,null,t(n.customerName),1),a(`span`,T,t(n.customerPhone),1),a(`span`,E,t(i(N)[n.status]??n.status),1)]),c(x,{"model-value":n.status,options:p.filter(e=>e.value),"onUpdate:modelValue":e=>V(n.id,String(e))},null,8,[`model-value`,`options`,`onUpdate:modelValue`])]),n.note?(e(),o(`p`,D,t(n.note),1)):m(``,!0),a(`table`,O,[r[2]||=a(`thead`,null,[a(`tr`,null,[a(`th`,null,`محصول`),a(`th`,null,`کد`),a(`th`,null,`تعداد`),a(`th`,null,`محل`)])],-1),a(`tbody`,null,[(e(!0),o(f,null,l(n.items,n=>(e(),o(`tr`,{key:n.id},[a(`td`,null,t(n.productName),1),a(`td`,k,t(n.productCode),1),a(`td`,null,t(n.quantity),1),a(`td`,null,[c(x,{"model-value":n.holderType,options:M,"onUpdate:modelValue":e=>H(n.id,String(e))},null,8,[`model-value`,`onUpdate:modelValue`]),n.movements?.length?(e(),o(`ul`,A,[(e(!0),o(f,null,l(n.movements,n=>(e(),o(`li`,{key:n.id},[d(t(U(n.fromHolder))+` → `+t(U(n.toHolder))+` `,1),a(`span`,j,t(i(_)(n.movedAt)),1)]))),128))])):m(``,!0)])]))),128))])])]))),128)):(e(),u(b,{key:1,title:`درخواستی نیست`}))]))}}),[[`__scopeId`,`data-v-747d8f88`]]);export{M as default};
|
||||
@@ -1 +1 @@
|
||||
.sales-chart__header[data-v-85c70c78]{margin-bottom:12px}.sales-chart__title[data-v-85c70c78]{margin:0;font-size:.9375rem;font-weight:700}.sales-chart__subtitle[data-v-85c70c78]{color:var(--color-text-muted);margin:4px 0 0;font-size:.8125rem}.sales-chart__body[data-v-85c70c78]{position:relative}.sales-chart__svg[data-v-85c70c78]{width:100%;height:100%;display:block}.sales-chart__grid[data-v-85c70c78]{stroke:var(--color-border);stroke-width:1px;stroke-dasharray:4 4}.sales-chart__line[data-v-85c70c78]{fill:none;stroke-width:2.25px;stroke-linecap:round;stroke-linejoin:round}.sales-chart__cursor[data-v-85c70c78]{stroke:var(--color-border);stroke-width:1px;stroke-dasharray:3 3}.sales-chart__dot[data-v-85c70c78]{stroke:#fff;stroke-width:2px}.sales-chart__tooltip[data-v-85c70c78]{border-radius:var(--radius-sm);border:1px solid var(--color-border);background:var(--color-surface);min-width:120px;box-shadow:var(--shadow-sm);pointer-events:none;text-align:center;padding:8px 10px;position:absolute;top:8px;transform:translate(-50%)}.sales-chart__tooltip-label[data-v-85c70c78]{color:var(--color-text-muted);margin-bottom:2px;font-size:.6875rem;display:block}.sales-chart__tooltip strong[data-v-85c70c78]{font-size:.8125rem}.sales-chart__axis[data-v-85c70c78]{color:var(--color-text-muted);justify-content:space-between;margin-top:8px;font-size:.6875rem;display:flex}.dashboard__toolbar[data-v-bf2ebdae]{justify-content:flex-end;margin-bottom:16px;display:flex}.dashboard__stats[data-v-bf2ebdae]{grid-template-columns:repeat(5,1fr);gap:16px;margin-bottom:20px;display:grid}.dashboard__sales-header[data-v-bf2ebdae]{justify-content:space-between;align-items:flex-start;display:flex}.dashboard__sales-value[data-v-bf2ebdae]{font-size:1.25rem!important}.dashboard__sales-growth[data-v-bf2ebdae]{color:var(--color-success);font-size:.75rem;font-weight:600}.dashboard__sales-icon[data-v-bf2ebdae]{color:var(--color-text-muted)}.dashboard__sparkline[data-v-bf2ebdae]{width:100%;height:32px;margin-top:8px}.dashboard__grid[data-v-bf2ebdae]{grid-template-columns:1fr 1.4fr .9fr;gap:16px;display:grid}.dashboard__chart-card[data-v-bf2ebdae],.dashboard__orders-card[data-v-bf2ebdae],.dashboard__checks-card[data-v-bf2ebdae]{padding:20px}.dashboard__chart-card[data-v-bf2ebdae] .sales-chart__header{margin-bottom:0}.dashboard__card-header[data-v-bf2ebdae]{margin-bottom:16px}.dashboard__card-title[data-v-bf2ebdae]{margin:0;font-size:.9375rem;font-weight:700}.dashboard__card-value[data-v-bf2ebdae]{margin:4px 0 0;font-size:1.125rem;font-weight:700}.dashboard__chart[data-v-bf2ebdae]{width:100%;height:140px}.dashboard__chart-axis[data-v-bf2ebdae]{color:var(--color-text-muted);justify-content:space-between;margin-top:8px;font-size:.6875rem;display:flex}.dashboard__card-footer[data-v-bf2ebdae]{border-top:1px solid var(--color-border-light);justify-content:flex-start;margin-top:12px;padding-top:12px;display:flex}.dashboard__card-footer .admin-btn[data-v-bf2ebdae]{text-decoration:none}.dashboard__deadline-list[data-v-bf2ebdae]{flex-direction:column;gap:12px;margin:0;padding:0;list-style:none;display:flex}.dashboard__deadline-item[data-v-bf2ebdae]{grid-template-columns:auto 1fr auto auto;align-items:center;gap:10px;font-size:.8125rem;display:grid}.dashboard__deadline-count[data-v-bf2ebdae]{color:var(--color-text-muted)}.dashboard__deadline-amount[data-v-bf2ebdae]{font-size:.75rem;font-weight:600}
|
||||
.sales-chart__header[data-v-36d15f13]{margin-bottom:12px}.sales-chart__title[data-v-36d15f13]{margin:0;font-size:.9375rem;font-weight:700}.sales-chart__subtitle[data-v-36d15f13]{color:var(--color-text-muted);margin:4px 0 0;font-size:.8125rem}.sales-chart__body[data-v-36d15f13]{position:relative}.sales-chart__svg[data-v-36d15f13]{width:100%;height:100%;display:block}.sales-chart__grid[data-v-36d15f13]{stroke:var(--color-border);stroke-width:1px;stroke-dasharray:4 4}.sales-chart__line[data-v-36d15f13]{fill:none;stroke-width:2.25px;stroke-linecap:round;stroke-linejoin:round}.sales-chart__cursor[data-v-36d15f13]{stroke:var(--color-border);stroke-width:1px;stroke-dasharray:3 3}.sales-chart__dot[data-v-36d15f13]{stroke:#fff;stroke-width:2px}.sales-chart__tooltip[data-v-36d15f13]{border-radius:var(--radius-sm);border:1px solid var(--color-border);background:var(--color-surface);min-width:120px;box-shadow:var(--shadow-sm);pointer-events:none;text-align:center;padding:8px 10px;position:absolute;top:8px;transform:translate(-50%)}.sales-chart__tooltip-label[data-v-36d15f13]{color:var(--color-text-muted);margin-bottom:2px;font-size:.6875rem;display:block}.sales-chart__tooltip strong[data-v-36d15f13]{font-size:.8125rem}.sales-chart__axis[data-v-36d15f13]{color:var(--color-text-muted);justify-content:space-between;margin-top:8px;font-size:.6875rem;display:flex}.dashboard__toolbar[data-v-bf2ebdae]{justify-content:flex-end;margin-bottom:16px;display:flex}.dashboard__stats[data-v-bf2ebdae]{grid-template-columns:repeat(5,1fr);gap:16px;margin-bottom:20px;display:grid}.dashboard__sales-header[data-v-bf2ebdae]{justify-content:space-between;align-items:flex-start;display:flex}.dashboard__sales-value[data-v-bf2ebdae]{font-size:1.25rem!important}.dashboard__sales-growth[data-v-bf2ebdae]{color:var(--color-success);font-size:.75rem;font-weight:600}.dashboard__sales-icon[data-v-bf2ebdae]{color:var(--color-text-muted)}.dashboard__sparkline[data-v-bf2ebdae]{width:100%;height:32px;margin-top:8px}.dashboard__grid[data-v-bf2ebdae]{grid-template-columns:1fr 1.4fr .9fr;gap:16px;display:grid}.dashboard__chart-card[data-v-bf2ebdae],.dashboard__orders-card[data-v-bf2ebdae],.dashboard__checks-card[data-v-bf2ebdae]{padding:20px}.dashboard__chart-card[data-v-bf2ebdae] .sales-chart__header{margin-bottom:0}.dashboard__card-header[data-v-bf2ebdae]{margin-bottom:16px}.dashboard__card-title[data-v-bf2ebdae]{margin:0;font-size:.9375rem;font-weight:700}.dashboard__card-value[data-v-bf2ebdae]{margin:4px 0 0;font-size:1.125rem;font-weight:700}.dashboard__chart[data-v-bf2ebdae]{width:100%;height:140px}.dashboard__chart-axis[data-v-bf2ebdae]{color:var(--color-text-muted);justify-content:space-between;margin-top:8px;font-size:.6875rem;display:flex}.dashboard__card-footer[data-v-bf2ebdae]{border-top:1px solid var(--color-border-light);justify-content:flex-start;margin-top:12px;padding-top:12px;display:flex}.dashboard__card-footer .admin-btn[data-v-bf2ebdae]{text-decoration:none}.dashboard__deadline-list[data-v-bf2ebdae]{flex-direction:column;gap:12px;margin:0;padding:0;list-style:none;display:flex}.dashboard__deadline-item[data-v-bf2ebdae]{grid-template-columns:auto 1fr auto auto;align-items:center;gap:10px;font-size:.8125rem;display:grid}.dashboard__deadline-count[data-v-bf2ebdae]{color:var(--color-text-muted)}.dashboard__deadline-amount[data-v-bf2ebdae]{font-size:.75rem;font-weight:600}
|
||||
@@ -1,3 +1,3 @@
|
||||
import{B as e,O as t,Ot as n,T as r,V as i,Y as a,Z as o,c as s,d as c,g as l,h as u,j as d,l as f,m as p,r as m,s as h,t as g}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{N as _,R as v,W as y,v as b,z as x}from"./index-CXa4EMLW.js";import{t as S}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as C}from"./BaseEmptyState-BmO9aDcK.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=g(l({__name:`LevelsView`,setup(l){let g=o([]),N=o(!0),P=o(!1),F=o(null),I=x(),L=a({code:``,label:``,sortOrder:0,benefitsText:``}),R=h(()=>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 b.list()}catch(e){I.error(e instanceof _?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 b.update(F.value,e):await b.create(e),P.value=!1,I.success(`ذخیره شد`),await B()}catch(e){I.error(e instanceof _?e.message:`ذخیره ناموفق بود`)}}return r(()=>{B()}),(r,a)=>(t(),c(`div`,w,[s(`header`,{class:`admin-page__head`},[a[5]||=s(`div`,null,[s(`h1`,null,`سطوح و مزایای مشتری`),s(`p`,null,`تعریف Level داخلی CRM و مزایای هر سطح (نمایش فقط در ادمین)`)],-1),s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`سطح جدید`)]),N.value?(t(),f(S,{key:0,variant:`table`})):g.value.length?(t(),c(`table`,T,[a[6]||=s(`thead`,null,[s(`tr`,null,[s(`th`,null,`کد`),s(`th`,null,`عنوان`),s(`th`,null,`مزایا`),s(`th`,null,`مشتریان`),s(`th`)])],-1),s(`tbody`,null,[(t(!0),c(m,null,d(g.value,e=>(t(),c(`tr`,{key:e.id},[s(`td`,E,n(e.code),1),s(`td`,null,n(e.label),1),s(`td`,null,n(e.benefits.map(e=>e.title).join(`، `)||`—`),1),s(`td`,null,n(e.customersCount),1),s(`td`,null,[s(`button`,{type:`button`,class:`link`,onClick:t=>H(e)},`ویرایش`,8,D)])]))),128))])])):(t(),f(C,{key:1,title:`سطحی تعریف نشده`})),u(v,{open:P.value,title:R.value,onClose:a[4]||=e=>P.value=!1},{footer:e(()=>[s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!L.code.trim()||!L.label.trim(),onClick:U},` ذخیره `,8,M)]),default:e(()=>[s(`label`,O,[a[7]||=p(`کد`,-1),i(s(`input`,{"onUpdate:modelValue":a[0]||=e=>L.code=e,class:`admin-input`,dir:`ltr`},null,512),[[y,L.code]])]),s(`label`,k,[a[8]||=p(`عنوان`,-1),i(s(`input`,{"onUpdate:modelValue":a[1]||=e=>L.label=e,class:`admin-input`},null,512),[[y,L.label]])]),s(`label`,A,[a[9]||=p(`ترتیب`,-1),i(s(`input`,{"onUpdate:modelValue":a[2]||=e=>L.sortOrder=e,type:`number`,class:`admin-input`},null,512),[[y,L.sortOrder,void 0,{number:!0}]])]),s(`label`,j,[a[10]||=p(` مزایا (هر خط یک مورد) `,-1),i(s(`textarea`,{"onUpdate:modelValue":a[3]||=e=>L.benefitsText=e,class:`admin-input`,rows:`4`},null,512),[[y,L.benefitsText]])])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-44c7f808`]]);export{N as default};
|
||||
import{B as e,O as t,Ot as n,T as r,V as i,Y as a,Z as o,c as s,d as c,g as l,h as u,j as d,l as f,m as p,r as m,s as h,t as g}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{a as _,f as v,t as y}from"./http-Djf2pGxE.js";import{V as b,x}from"./index-CznUUXv3.js";import{t as S}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as C}from"./BaseEmptyState-D6sk-aI_.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=g(l({__name:`LevelsView`,setup(l){let g=o([]),N=o(!0),P=o(!1),F=o(null),I=_(),L=a({code:``,label:``,sortOrder:0,benefitsText:``}),R=h(()=>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 x.list()}catch(e){I.error(e instanceof y?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 x.update(F.value,e):await x.create(e),P.value=!1,I.success(`ذخیره شد`),await B()}catch(e){I.error(e instanceof y?e.message:`ذخیره ناموفق بود`)}}return r(()=>{B()}),(r,a)=>(t(),c(`div`,w,[s(`header`,{class:`admin-page__head`},[a[5]||=s(`div`,null,[s(`h1`,null,`سطوح و مزایای مشتری`),s(`p`,null,`تعریف Level داخلی CRM و مزایای هر سطح (نمایش فقط در ادمین)`)],-1),s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:V},`سطح جدید`)]),N.value?(t(),f(S,{key:0,variant:`table`})):g.value.length?(t(),c(`table`,T,[a[6]||=s(`thead`,null,[s(`tr`,null,[s(`th`,null,`کد`),s(`th`,null,`عنوان`),s(`th`,null,`مزایا`),s(`th`,null,`مشتریان`),s(`th`)])],-1),s(`tbody`,null,[(t(!0),c(m,null,d(g.value,e=>(t(),c(`tr`,{key:e.id},[s(`td`,E,n(e.code),1),s(`td`,null,n(e.label),1),s(`td`,null,n(e.benefits.map(e=>e.title).join(`، `)||`—`),1),s(`td`,null,n(e.customersCount),1),s(`td`,null,[s(`button`,{type:`button`,class:`link`,onClick:t=>H(e)},`ویرایش`,8,D)])]))),128))])])):(t(),f(C,{key:1,title:`سطحی تعریف نشده`})),u(b,{open:P.value,title:R.value,onClose:a[4]||=e=>P.value=!1},{footer:e(()=>[s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!L.code.trim()||!L.label.trim(),onClick:U},` ذخیره `,8,M)]),default:e(()=>[s(`label`,O,[a[7]||=p(`کد`,-1),i(s(`input`,{"onUpdate:modelValue":a[0]||=e=>L.code=e,class:`admin-input`,dir:`ltr`},null,512),[[v,L.code]])]),s(`label`,k,[a[8]||=p(`عنوان`,-1),i(s(`input`,{"onUpdate:modelValue":a[1]||=e=>L.label=e,class:`admin-input`},null,512),[[v,L.label]])]),s(`label`,A,[a[9]||=p(`ترتیب`,-1),i(s(`input`,{"onUpdate:modelValue":a[2]||=e=>L.sortOrder=e,type:`number`,class:`admin-input`},null,512),[[v,L.sortOrder,void 0,{number:!0}]])]),s(`label`,j,[a[10]||=p(` مزایا (هر خط یک مورد) `,-1),i(s(`textarea`,{"onUpdate:modelValue":a[3]||=e=>L.benefitsText=e,class:`admin-input`,rows:`4`},null,512),[[v,L.benefitsText]])])]),_:1},8,[`open`,`title`])]))}}),[[`__scopeId`,`data-v-44c7f808`]]);export{N as default};
|
||||
@@ -0,0 +1 @@
|
||||
.login-page[data-v-df8d196a]{background:var(--color-bg);grid-template-columns:1.05fr 1fr;min-height:100dvh;display:grid}.login-page__brand[data-v-df8d196a]{background:linear-gradient(160deg, var(--color-primary-dark) 0%, var(--color-primary) 55%, var(--color-sidebar-active) 100%);color:#fff;justify-content:center;align-items:center;padding:48px 40px;display:flex}.login-page__brand-inner[data-v-df8d196a]{max-width:420px}.login-page__brand-logo[data-v-df8d196a]{object-fit:contain;width:min(220px,70%);height:150px;margin-bottom:28px;display:block}.login-page__brand-badge[data-v-df8d196a]{background:#ffffff1f;border-radius:999px;margin:0 0 16px;padding:6px 12px;font-size:.8125rem;font-weight:700;display:inline-flex}.login-page__brand-title[data-v-df8d196a]{margin:0 0 12px;font-size:clamp(1.5rem,2.4vw,2rem);line-height:1.35}.login-page__brand-text[data-v-df8d196a]{color:#ffffffc7;margin:0;font-size:.9375rem;line-height:1.8}.login-page__form-wrap[data-v-df8d196a]{justify-content:center;align-items:center;padding:32px 24px;display:flex}.login-page__card[data-v-df8d196a]{width:min(100%,420px);padding:28px 24px}.login-page__header[data-v-df8d196a]{margin-bottom:20px}.login-page__title[data-v-df8d196a]{margin:0 0 6px;font-size:1.25rem;font-weight:700}.login-page__subtitle[data-v-df8d196a]{color:var(--color-text-muted);margin:0;font-size:.875rem;line-height:1.6}.login-page__mock-hint[data-v-df8d196a]{background:color-mix(in srgb, var(--color-primary) 12%, transparent);color:var(--color-primary);border-radius:8px;margin:10px 0 0;padding:8px 10px;font-size:.8125rem;line-height:1.5}.login-page__input-wrap[data-v-df8d196a]{color:var(--color-text-muted);position:relative}.login-page__field-icon[data-v-df8d196a]{pointer-events:none;z-index:1;display:inline-flex;position:absolute;top:50%;right:12px;transform:translateY(-50%)}.login-page__input[data-v-df8d196a]{padding-right:42px}.login-page__input--with-toggle[data-v-df8d196a]{padding-left:44px}.login-page__toggle-password[data-v-df8d196a]{z-index:1;width:32px;height:32px;color:var(--color-text-muted);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;padding:0;transition:color .15s,background-color .15s;display:inline-flex;position:absolute;top:50%;left:6px;transform:translateY(-50%)}.login-page__toggle-password[data-v-df8d196a]:hover{color:var(--color-primary);background:var(--color-bg-muted,#0000000a)}.login-page__remember[data-v-df8d196a]{color:var(--color-text-muted);align-items:center;gap:8px;font-size:.8125rem;display:flex}.login-page__remember input[data-v-df8d196a]{width:16px;height:16px;accent-color:var(--color-primary)}.login-page__submit[data-v-df8d196a]{width:100%;margin-top:4px}@media (width<=900px){.login-page[data-v-df8d196a]{grid-template-columns:1fr}.login-page__form-wrap[data-v-df8d196a]{order:2}.login-page__brand[data-v-df8d196a]{order:1;min-height:auto;padding:28px 20px}.login-page__brand-text[data-v-df8d196a]{font-size:.875rem}}
|
||||
@@ -0,0 +1 @@
|
||||
import{O as e,Ot as t,V as n,Z as r,at as i,c as a,d as o,g as s,h as c,m as l,s as u,t as d,u as f}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{a as p,f as m,l as h,m as g,u as _}from"./http-Djf2pGxE.js";import{F as v,I as y,L as b,P as x,R as S,z as C}from"./index-CznUUXv3.js";var w={class:`login-page`},T={class:`login-page__form-wrap`},E={class:`login-page__card admin-card`},D={class:`login-page__header`},O={key:0,class:`login-page__mock-hint`},k={dir:`ltr`},A={dir:`ltr`},j={key:0,class:`admin-form__error`,role:`alert`},M={class:`admin-form__field`},N={class:`login-page__input-wrap`},P={class:`login-page__field-icon`,"aria-hidden":`true`},F={class:`admin-form__field`},I={class:`login-page__input-wrap login-page__input-wrap--password`},L={class:`login-page__field-icon`,"aria-hidden":`true`},R=[`type`],z=[`aria-label`],B={class:`login-page__remember`},V=[`disabled`],H={key:0,class:`admin-btn__spinner`,"aria-hidden":`true`},U=d(s({__name:`LoginView`,setup(s){let d=C(),U=S(),W=x(),G=p(),K=r(y()?v.phone:``),q=r(y()?v.password:``),J=r(!0),Y=r(!1),X=y(),Z=u(()=>K.value.trim().length>0&&q.value.length>0);function Q(e){return e.replace(/[۰-۹]/g,e=>String(`۰۱۲۳۴۵۶۷۸۹`.indexOf(e))).replace(/[٠-٩]/g,e=>String(`٠١٢٣٤٥٦٧٨٩`.indexOf(e)))}async function $(){if(!Z.value){W.error=`لطفاً شماره موبایل و رمز عبور را وارد کنید.`;return}if(!await W.login(Q(K.value.trim()),q.value,J.value))return;G.success(`خوش آمدید، ${W.fullName}`);let e=typeof U.query.redirect==`string`?U.query.redirect:`/`,t=`/admin/`.replace(/\/$/,``),n=e.startsWith(t)?e.slice(t.length)||`/`:e;await d.replace(n)}return(r,s)=>(e(),o(`div`,w,[a(`div`,T,[a(`div`,E,[a(`div`,D,[s[8]||=a(`h2`,{class:`login-page__title`},`ورود به پنل مدیریت`,-1),s[9]||=a(`p`,{class:`login-page__subtitle`},`برای ادامه، اطلاعات حساب کاربری خود را وارد کنید.`,-1),i(X)?(e(),o(`p`,O,[s[6]||=l(` حالت موک فعال است — `,-1),a(`span`,k,t(i(v).phone),1),s[7]||=l(` / `,-1),a(`span`,A,t(i(v).password),1)])):f(``,!0)]),a(`form`,{class:`admin-form login-page__form`,onSubmit:g($,[`prevent`])},[i(W).error?(e(),o(`p`,j,t(i(W).error),1)):f(``,!0),a(`div`,M,[s[10]||=a(`label`,{for:`login-phone`},`شماره موبایل`,-1),a(`div`,N,[a(`span`,P,[c(b,{name:`phone`,size:18})]),n(a(`input`,{id:`login-phone`,"onUpdate:modelValue":s[0]||=e=>K.value=e,type:`tel`,class:`admin-input login-page__input`,placeholder:`۰۹۱۲۱۱۱۱۲۲۲۲`,autocomplete:`username`,inputmode:`tel`,dir:`ltr`,onInput:s[1]||=e=>i(W).clearError()},null,544),[[m,K.value]])])]),a(`div`,F,[s[11]||=a(`label`,{for:`login-password`},`رمز عبور`,-1),a(`div`,I,[a(`span`,L,[c(b,{name:`lock`,size:18})]),n(a(`input`,{id:`login-password`,"onUpdate:modelValue":s[2]||=e=>q.value=e,type:Y.value?`text`:`password`,class:`admin-input login-page__input login-page__input--with-toggle`,placeholder:`رمز عبور`,autocomplete:`current-password`,onInput:s[3]||=e=>i(W).clearError()},null,40,R),[[_,q.value]]),a(`button`,{type:`button`,class:`login-page__toggle-password`,"aria-label":Y.value?`پنهان کردن رمز`:`نمایش رمز`,onClick:s[4]||=e=>Y.value=!Y.value},[c(b,{name:Y.value?`eye-off`:`eye`,size:18},null,8,[`name`])],8,z)])]),a(`label`,B,[n(a(`input`,{"onUpdate:modelValue":s[5]||=e=>J.value=e,type:`checkbox`},null,512),[[h,J.value]]),s[12]||=a(`span`,null,`مرا به خاطر بسپار`,-1)]),a(`button`,{type:`submit`,class:`admin-btn admin-btn--primary login-page__submit`,disabled:!Z.value||i(W).loading},[i(W).loading?(e(),o(`span`,H)):f(``,!0),s[13]||=l(` ورود به پنل `,-1)],8,V)],32)])]),a(`div`,{class:`login-page__brand`},[a(`div`,{class:`login-page__brand-inner`},[a(`img`,{src:`/admin/brand-logo.png`,alt:`نادمی تکس`,class:`login-page__brand-logo`}),s[14]||=a(`p`,{class:`login-page__brand-badge`},`پارچه`,-1),s[15]||=a(`h1`,{class:`login-page__brand-title`},`پنل مدیریت سفارشهای عمده`,-1),s[16]||=a(`p`,{class:`login-page__brand-text`},` مدیریت سفارشها، مشتریان، قیمتگذاری، چکها و گزارشهای فروش در یک محیط یکپارچه. `,-1)])])]))}}),[[`__scopeId`,`data-v-df8d196a`]]);export{U as default};
|
||||
@@ -1 +0,0 @@
|
||||
.login-page[data-v-9aef901d]{background:var(--color-bg);grid-template-columns:1.05fr 1fr;min-height:100dvh;display:grid}.login-page__brand[data-v-9aef901d]{color:#fff;background:linear-gradient(160deg,#062428 0%,#0a353c 55%,#0e454e 100%);justify-content:center;align-items:center;padding:48px 40px;display:flex}.login-page__brand-inner[data-v-9aef901d]{max-width:420px}.login-page__brand-badge[data-v-9aef901d]{background:#ffffff1f;border-radius:999px;margin:0 0 16px;padding:6px 12px;font-size:.8125rem;font-weight:700;display:inline-flex}.login-page__brand-title[data-v-9aef901d]{margin:0 0 12px;font-size:clamp(1.5rem,2.4vw,2rem);line-height:1.35}.login-page__brand-text[data-v-9aef901d]{color:#ffffffc7;margin:0;font-size:.9375rem;line-height:1.8}.login-page__form-wrap[data-v-9aef901d]{justify-content:center;align-items:center;padding:32px 24px;display:flex}.login-page__card[data-v-9aef901d]{width:min(100%,420px);padding:28px 24px}.login-page__header[data-v-9aef901d]{margin-bottom:20px}.login-page__title[data-v-9aef901d]{margin:0 0 6px;font-size:1.25rem;font-weight:700}.login-page__subtitle[data-v-9aef901d]{color:var(--color-text-muted);margin:0;font-size:.875rem;line-height:1.6}.login-page__mock-hint[data-v-9aef901d]{background:color-mix(in srgb, var(--color-primary) 12%, transparent);color:var(--color-primary);border-radius:8px;margin:10px 0 0;padding:8px 10px;font-size:.8125rem;line-height:1.5}.login-page__input-wrap[data-v-9aef901d]{color:var(--color-text-muted);position:relative}.login-page__field-icon[data-v-9aef901d]{pointer-events:none;z-index:1;display:inline-flex;position:absolute;top:50%;right:12px;transform:translateY(-50%)}.login-page__input[data-v-9aef901d]{padding-right:42px}.login-page__input--with-toggle[data-v-9aef901d]{padding-left:44px}.login-page__toggle-password[data-v-9aef901d]{z-index:1;width:32px;height:32px;color:var(--color-text-muted);cursor:pointer;background:0 0;border:none;border-radius:8px;justify-content:center;align-items:center;padding:0;transition:color .15s,background-color .15s;display:inline-flex;position:absolute;top:50%;left:6px;transform:translateY(-50%)}.login-page__toggle-password[data-v-9aef901d]:hover{color:var(--color-primary);background:var(--color-bg-muted,#0000000a)}.login-page__remember[data-v-9aef901d]{color:var(--color-text-muted);align-items:center;gap:8px;font-size:.8125rem;display:flex}.login-page__remember input[data-v-9aef901d]{width:16px;height:16px;accent-color:var(--color-primary)}.login-page__submit[data-v-9aef901d]{width:100%;margin-top:4px}@media (width<=900px){.login-page[data-v-9aef901d]{grid-template-columns:1fr}.login-page__form-wrap[data-v-9aef901d]{order:2}.login-page__brand[data-v-9aef901d]{order:1;min-height:auto;padding:28px 20px}.login-page__brand-text[data-v-9aef901d]{font-size:.875rem}}
|
||||
@@ -1 +0,0 @@
|
||||
import{O as e,Ot as t,V as n,Z as r,at as i,c as a,d as o,g as s,h as c,m as l,p as u,s as d,t as f,u as p}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{A as m,F as h,H as g,I as _,K as v,M as y,P as b,V as x,W as S,j as C,z as w}from"./index-CXa4EMLW.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=f(s({__name:`LoginView`,setup(s){let f=_(),W=h(),G=m(),K=w(),q=r(y()?C.phone:``),J=r(y()?C.password:``),Y=r(!0),X=r(!1),Z=y(),Q=d(()=>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:`/`,t=`/admin/`.replace(/\/$/,``),n=e.startsWith(t)?e.slice(t.length)||`/`:e;await f.replace(n)}return(r,s)=>(e(),o(`div`,T,[a(`div`,E,[a(`div`,D,[a(`div`,O,[s[8]||=a(`h2`,{class:`login-page__title`},`ورود به پنل مدیریت`,-1),s[9]||=a(`p`,{class:`login-page__subtitle`},`برای ادامه، اطلاعات حساب کاربری خود را وارد کنید.`,-1),i(Z)?(e(),o(`p`,k,[s[6]||=l(` حالت موک فعال است — `,-1),a(`span`,A,t(i(C).phone),1),s[7]||=l(` / `,-1),a(`span`,j,t(i(C).password),1)])):p(``,!0)]),a(`form`,{class:`admin-form login-page__form`,onSubmit:v(ee,[`prevent`])},[i(G).error?(e(),o(`p`,M,t(i(G).error),1)):p(``,!0),a(`div`,N,[s[10]||=a(`label`,{for:`login-phone`},`شماره موبایل`,-1),a(`div`,P,[a(`span`,F,[c(b,{name:`phone`,size:18})]),n(a(`input`,{id:`login-phone`,"onUpdate:modelValue":s[0]||=e=>q.value=e,type:`tel`,class:`admin-input login-page__input`,placeholder:`۰۹۱۲۱۱۱۱۲۲۲۲`,autocomplete:`username`,inputmode:`tel`,dir:`ltr`,onInput:s[1]||=e=>i(G).clearError()},null,544),[[S,q.value]])])]),a(`div`,I,[s[11]||=a(`label`,{for:`login-password`},`رمز عبور`,-1),a(`div`,L,[a(`span`,R,[c(b,{name:`lock`,size:18})]),n(a(`input`,{id:`login-password`,"onUpdate:modelValue":s[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:s[3]||=e=>i(G).clearError()},null,40,z),[[g,J.value]]),a(`button`,{type:`button`,class:`login-page__toggle-password`,"aria-label":X.value?`پنهان کردن رمز`:`نمایش رمز`,onClick:s[4]||=e=>X.value=!X.value},[c(b,{name:X.value?`eye-off`:`eye`,size:18},null,8,[`name`])],8,B)])]),a(`label`,V,[n(a(`input`,{"onUpdate:modelValue":s[5]||=e=>Y.value=e,type:`checkbox`},null,512),[[x,Y.value]]),s[12]||=a(`span`,null,`مرا به خاطر بسپار`,-1)]),a(`button`,{type:`submit`,class:`admin-btn admin-btn--primary login-page__submit`,disabled:!Q.value||i(G).loading},[i(G).loading?(e(),o(`span`,U)):p(``,!0),s[13]||=l(` ورود به پنل `,-1)],8,H)],32)])]),s[14]||=u(`<div class="login-page__brand" data-v-9aef901d><div class="login-page__brand-inner" data-v-9aef901d><p class="login-page__brand-badge" data-v-9aef901d>پارچه</p><h1 class="login-page__brand-title" data-v-9aef901d>پنل مدیریت سفارشهای عمده</h1><p class="login-page__brand-text" data-v-9aef901d> مدیریت سفارشها، مشتریان، قیمتگذاری، چکها و گزارشهای فروش در یک محیط یکپارچه. </p></div></div>`,1)]))}}),[[`__scopeId`,`data-v-9aef901d`]]);export{W as default};
|
||||
@@ -0,0 +1 @@
|
||||
.sms-stats[data-v-e4cab5f3]{grid-template-columns:repeat(4,minmax(0,1fr));gap:12px;margin-bottom:16px;display:grid}.sms-stat[data-v-e4cab5f3]{border-radius:16px;min-height:112px;padding:16px 18px}.sms-stat h2[data-v-e4cab5f3]{margin:0 0 10px;font-size:.95rem}.sms-stat p[data-v-e4cab5f3]{color:var(--color-text-muted,#64748b);margin:0;font-size:.8125rem}.sms-stat p span[data-v-e4cab5f3],.sms-stat strong[data-v-e4cab5f3]{font-weight:800}.sms-stat--balance[data-v-e4cab5f3]{color:#6d28d9;background:#f3e8ff}.sms-stat--today[data-v-e4cab5f3]{color:#c2410c;background:#fff7ed}.sms-stat--price[data-v-e4cab5f3]{color:#1d4ed8;background:#eff6ff}.sms-stat--price strong[data-v-e4cab5f3],.sms-stat--wallet strong[data-v-e4cab5f3]{margin-top:18px;font-size:1.35rem;display:block}.sms-stat--wallet[data-v-e4cab5f3]{color:#b45309;background:#fffbeb}.sms-manage[data-v-e4cab5f3],.sms-deposit[data-v-e4cab5f3],.sms-thresholds[data-v-e4cab5f3]{margin-bottom:16px}.sms-manage h2[data-v-e4cab5f3],.sms-deposit h2[data-v-e4cab5f3],.sms-thresholds h2[data-v-e4cab5f3]{margin:0 0 14px;font-size:1rem}.sms-manage__row[data-v-e4cab5f3],.sms-thresholds__row[data-v-e4cab5f3]{flex-wrap:wrap;align-items:end;gap:12px;display:flex}.sms-manage__tariff[data-v-e4cab5f3],.sms-manage__price[data-v-e4cab5f3],.sms-manage__charge[data-v-e4cab5f3]{align-items:center;gap:8px;display:flex}.sms-manage__price[data-v-e4cab5f3] input,.sms-manage__charge[data-v-e4cab5f3] input,.sms-manage__charge .admin-input[data-v-e4cab5f3],.sms-thresholds__row[data-v-e4cab5f3] input{width:140px}.sms-manage__tariff strong[data-v-e4cab5f3]{text-align:center;min-width:72px;font-size:1.25rem}.sms-manage__charge[data-v-e4cab5f3]{margin-right:auto}.sms-manage__hint[data-v-e4cab5f3],.sms-deposit__hint[data-v-e4cab5f3]{color:var(--color-text-muted,#64748b);margin:12px 0 0;font-size:.8125rem}.sms-block[data-v-e4cab5f3]{background:#ecfdf3;border-radius:12px;justify-content:space-between;align-items:center;gap:12px;margin-top:14px;padding:12px 14px;display:flex}.sms-block p[data-v-e4cab5f3]{color:#067647;margin:4px 0 0;font-size:.75rem}.sms-block__switch[data-v-e4cab5f3]{color:#067647;align-items:center;gap:8px;font-weight:700;display:flex}.sms-deposit__save[data-v-e4cab5f3]{width:100%;margin-top:8px}.sms-thresholds__row .admin-btn[data-v-e4cab5f3]{margin-bottom:2px}.admin-form__field>span[data-v-e4cab5f3]{font-size:.8125rem;font-weight:600}@media (width<=960px){.sms-stats[data-v-e4cab5f3]{grid-template-columns:1fr 1fr}.sms-manage__charge[data-v-e4cab5f3]{margin-right:0}}
|
||||
@@ -1 +0,0 @@
|
||||
import{O as e,Ot as t,T as n,Z as r,c as i,d as a,g as o,j as s,l as c,m as l,r as u,t as d}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{E as f,N as p,z as m}from"./index-CXa4EMLW.js";import{t as h}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as g}from"./BaseEmptyState-BmO9aDcK.js";var _={class:`admin-page`},v={key:2,class:`admin-table`},y={class:`muted`,dir:`ltr`},b={class:`muted`,dir:`ltr`},x=[`onClick`],S=d(o({__name:`StockAlertsView`,setup(o){let d=r([]),S=r(!0),C=m();async function w(){S.value=!0;try{d.value=await f.list()}catch(e){C.error(e instanceof p?e.message:`بارگذاری ناموفق بود`)}finally{S.value=!1}}async function T(e){try{await f.notify(e),C.success(`اعلان موجودی ارسال شد`),await w()}catch(e){C.error(e instanceof p?e.message:`ارسال ناموفق بود`)}}return n(()=>{w()}),(n,r)=>(e(),a(`div`,_,[r[1]||=i(`header`,{class:`admin-page__head`},[i(`div`,null,[i(`h1`,null,`درخواست خبر موجودی`),i(`p`,null,`مشتریانی که برای محصول ناموجود درخواست اطلاع دادهاند`)])],-1),S.value?(e(),c(h,{key:0,variant:`table`})):d.value.length?(e(),a(`table`,v,[r[0]||=i(`thead`,null,[i(`tr`,null,[i(`th`,null,`مشتری`),i(`th`,null,`محصول`),i(`th`,null,`موجودی فعلی`),i(`th`)])],-1),i(`tbody`,null,[(e(!0),a(u,null,s(d.value,n=>(e(),a(`tr`,{key:n.id},[i(`td`,null,[l(t(n.customerName)+` `,1),i(`span`,y,t(n.customerPhone),1)]),i(`td`,null,[l(t(n.productName)+` `,1),i(`span`,b,`(`+t(n.productCode)+`)`,1)]),i(`td`,null,t(n.stock??`—`),1),i(`td`,null,[i(`button`,{type:`button`,class:`admin-btn admin-btn--primary admin-btn--sm`,onClick:e=>T(n.id)},`ارسال اعلان`,8,x)])]))),128))])])):(e(),c(g,{key:1,title:`درخواستی فعال نیست`}))]))}}),[[`__scopeId`,`data-v-61131544`]]);export{S as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{O as e,Ot as t,T as n,Z as r,c as i,d as a,g as o,j as s,l as c,m as l,r as u,t as d}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{a as f,t as p}from"./http-Djf2pGxE.js";import{A as m}from"./index-CznUUXv3.js";import{t as h}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as g}from"./BaseEmptyState-D6sk-aI_.js";var _={class:`admin-page`},v={key:2,class:`admin-table`},y={class:`muted`,dir:`ltr`},b={class:`muted`,dir:`ltr`},x=[`onClick`],S=d(o({__name:`StockAlertsView`,setup(o){let d=r([]),S=r(!0),C=f();async function w(){S.value=!0;try{d.value=await m.list()}catch(e){C.error(e instanceof p?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 p?e.message:`ارسال ناموفق بود`)}}return n(()=>{w()}),(n,r)=>(e(),a(`div`,_,[r[1]||=i(`header`,{class:`admin-page__head`},[i(`div`,null,[i(`h1`,null,`درخواست خبر موجودی`),i(`p`,null,`مشتریانی که برای محصول ناموجود درخواست اطلاع دادهاند`)])],-1),S.value?(e(),c(h,{key:0,variant:`table`})):d.value.length?(e(),a(`table`,v,[r[0]||=i(`thead`,null,[i(`tr`,null,[i(`th`,null,`مشتری`),i(`th`,null,`محصول`),i(`th`,null,`موجودی فعلی`),i(`th`)])],-1),i(`tbody`,null,[(e(!0),a(u,null,s(d.value,n=>(e(),a(`tr`,{key:n.id},[i(`td`,null,[l(t(n.customerName)+` `,1),i(`span`,y,t(n.customerPhone),1)]),i(`td`,null,[l(t(n.productName)+` `,1),i(`span`,b,`(`+t(n.productCode)+`)`,1)]),i(`td`,null,t(n.stock??`—`),1),i(`td`,null,[i(`button`,{type:`button`,class:`admin-btn admin-btn--primary admin-btn--sm`,onClick:e=>T(n.id)},`ارسال اعلان`,8,x)])]))),128))])])):(e(),c(g,{key:1,title:`درخواستی فعال نیست`}))]))}}),[[`__scopeId`,`data-v-61131544`]]);export{S as default};
|
||||
@@ -0,0 +1 @@
|
||||
import{B as e,O as t,Ot as n,T as r,V as i,Y as a,Z as o,c as s,d as c,g as l,h as u,j as d,l as f,m as p,r as m,s as h,t as g}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{a as _,f as v,t as y}from"./http-Djf2pGxE.js";import{T as b,V as x,j as S,y as C}from"./index-CznUUXv3.js";import{t as w}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as T}from"./BaseEmptyState-D6sk-aI_.js";import{t as E}from"./AdminSelect-DK-_otkw.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=g(l({__name:`SuggestionsView`,setup(l){let g=o([]),I=o(!0),L=o([]),R=o([]),z=o(!1),B=_(),V=a({customerId:``,productId:``,note:``}),H=h(()=>L.value.map(e=>({value:e.id,label:e.name?.trim()||e.phone}))),U=h(()=>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([S.list(),C.list(),b.list()]);g.value=e,L.value=t,R.value=n}catch(e){B.error(e instanceof y?e.message:`بارگذاری ناموفق بود`)}finally{I.value=!1}}async function G(){try{await S.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 y?e.message:`ثبت ناموفق بود`)}}async function K(e){try{await S.setActive(e.id,!e.isActive),await W()}catch(e){B.error(e instanceof y?e.message:`بهروزرسانی ناموفق بود`)}}return r(()=>{W()}),(r,a)=>(t(),c(`div`,D,[s(`header`,O,[a[5]||=s(`div`,null,[s(`h1`,null,`پیشنهاد محصول`),s(`p`,null,`پیشنهاد دستی محصول به مشتری`)],-1),s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:a[0]||=e=>z.value=!0},`پیشنهاد جدید`)]),I.value?(t(),f(w,{key:0,variant:`table`})):g.value.length?(t(),c(`table`,k,[a[6]||=s(`thead`,null,[s(`tr`,null,[s(`th`,null,`مشتری`),s(`th`,null,`محصول`),s(`th`,null,`یادداشت`),s(`th`,null,`وضعیت`),s(`th`)])],-1),s(`tbody`,null,[(t(!0),c(m,null,d(g.value,e=>(t(),c(`tr`,{key:e.id},[s(`td`,null,n(e.customerName),1),s(`td`,null,[p(n(e.productName)+` `,1),s(`span`,A,`(`+n(e.productCode)+`)`,1)]),s(`td`,null,n(e.note||`—`),1),s(`td`,null,n(e.isActive?`فعال`:`غیرفعال`),1),s(`td`,null,[s(`button`,{type:`button`,class:`link`,onClick:t=>K(e)},n(e.isActive?`غیرفعال`:`فعال`),9,j)])]))),128))])])):(t(),f(T,{key:1,title:`پیشنهادی ثبت نشده`})),u(x,{open:z.value,title:`پیشنهاد محصول`,onClose:a[4]||=e=>z.value=!1},{footer:e(()=>[s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!V.customerId||!V.productId,onClick:G},` ثبت `,8,F)]),default:e(()=>[s(`label`,M,[a[7]||=p(`مشتری `,-1),u(E,{modelValue:V.customerId,"onUpdate:modelValue":a[1]||=e=>V.customerId=e,options:H.value},null,8,[`modelValue`,`options`])]),s(`label`,N,[a[8]||=p(`محصول `,-1),u(E,{modelValue:V.productId,"onUpdate:modelValue":a[2]||=e=>V.productId=e,options:U.value},null,8,[`modelValue`,`options`])]),s(`label`,P,[a[9]||=p(`یادداشت`,-1),i(s(`textarea`,{"onUpdate:modelValue":a[3]||=e=>V.note=e,class:`admin-input`,rows:`3`},null,512),[[v,V.note]])])]),_:1},8,[`open`])]))}}),[[`__scopeId`,`data-v-56e3e728`]]);export{I as default};
|
||||
@@ -1 +0,0 @@
|
||||
import{B as e,O as t,Ot as n,T as r,V as i,Y as a,Z as o,c as s,d as c,g as l,h as u,j as d,l as f,m as p,r as m,s as h,t as g}from"./_plugin-vue_export-helper-OaxA6A2f.js";import{D as _,N as v,R as y,S as b,W as x,g as S,z as C}from"./index-CXa4EMLW.js";import{t as w}from"./AdminPageSkeleton-CTgUqaAu.js";import{t as T}from"./BaseEmptyState-BmO9aDcK.js";import{t as E}from"./AdminSelect-9UfEJQzN.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=g(l({__name:`SuggestionsView`,setup(l){let g=o([]),I=o(!0),L=o([]),R=o([]),z=o(!1),B=C(),V=a({customerId:``,productId:``,note:``}),H=h(()=>L.value.map(e=>({value:e.id,label:e.name?.trim()||e.phone}))),U=h(()=>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([_.list(),S.list(),b.list()]);g.value=e,L.value=t,R.value=n}catch(e){B.error(e instanceof v?e.message:`بارگذاری ناموفق بود`)}finally{I.value=!1}}async function G(){try{await _.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 v?e.message:`ثبت ناموفق بود`)}}async function K(e){try{await _.setActive(e.id,!e.isActive),await W()}catch(e){B.error(e instanceof v?e.message:`بهروزرسانی ناموفق بود`)}}return r(()=>{W()}),(r,a)=>(t(),c(`div`,D,[s(`header`,O,[a[5]||=s(`div`,null,[s(`h1`,null,`پیشنهاد محصول`),s(`p`,null,`پیشنهاد دستی محصول به مشتری`)],-1),s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,onClick:a[0]||=e=>z.value=!0},`پیشنهاد جدید`)]),I.value?(t(),f(w,{key:0,variant:`table`})):g.value.length?(t(),c(`table`,k,[a[6]||=s(`thead`,null,[s(`tr`,null,[s(`th`,null,`مشتری`),s(`th`,null,`محصول`),s(`th`,null,`یادداشت`),s(`th`,null,`وضعیت`),s(`th`)])],-1),s(`tbody`,null,[(t(!0),c(m,null,d(g.value,e=>(t(),c(`tr`,{key:e.id},[s(`td`,null,n(e.customerName),1),s(`td`,null,[p(n(e.productName)+` `,1),s(`span`,A,`(`+n(e.productCode)+`)`,1)]),s(`td`,null,n(e.note||`—`),1),s(`td`,null,n(e.isActive?`فعال`:`غیرفعال`),1),s(`td`,null,[s(`button`,{type:`button`,class:`link`,onClick:t=>K(e)},n(e.isActive?`غیرفعال`:`فعال`),9,j)])]))),128))])])):(t(),f(T,{key:1,title:`پیشنهادی ثبت نشده`})),u(y,{open:z.value,title:`پیشنهاد محصول`,onClose:a[4]||=e=>z.value=!1},{footer:e(()=>[s(`button`,{type:`button`,class:`admin-btn admin-btn--primary`,disabled:!V.customerId||!V.productId,onClick:G},` ثبت `,8,F)]),default:e(()=>[s(`label`,M,[a[7]||=p(`مشتری `,-1),u(E,{modelValue:V.customerId,"onUpdate:modelValue":a[1]||=e=>V.customerId=e,options:H.value},null,8,[`modelValue`,`options`])]),s(`label`,N,[a[8]||=p(`محصول `,-1),u(E,{modelValue:V.productId,"onUpdate:modelValue":a[2]||=e=>V.productId=e,options:U.value},null,8,[`modelValue`,`options`])]),s(`label`,P,[a[9]||=p(`یادداشت`,-1),i(s(`textarea`,{"onUpdate:modelValue":a[3]||=e=>V.note=e,class:`admin-input`,rows:`3`},null,512),[[x,V.note]])])]),_:1},8,[`open`])]))}}),[[`__scopeId`,`data-v-56e3e728`]]);export{I as default};
|
||||
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -2,12 +2,19 @@
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="/admin/favicon.ico">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.ico?v=2">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="./icon-192.png?v=2">
|
||||
<link rel="apple-touch-icon" href="./apple-touch-icon.png?v=2">
|
||||
<link rel="manifest" href="./manifest.webmanifest?v=2">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#081d39">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>پنل مدیریت | پارچه</title>
|
||||
<script type="module" crossorigin src="/admin/assets/index-CXa4EMLW.js"></script>
|
||||
<script type="module" crossorigin src="/admin/assets/index-CznUUXv3.js"></script>
|
||||
<link rel="modulepreload" crossorigin href="/admin/assets/_plugin-vue_export-helper-OaxA6A2f.js">
|
||||
<link rel="stylesheet" crossorigin href="/admin/assets/index-C3X1YgAk.css">
|
||||
<link rel="modulepreload" crossorigin href="/admin/assets/http-Djf2pGxE.js">
|
||||
<link rel="stylesheet" crossorigin href="/admin/assets/index-BZ7aOp8K.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "نادمی تکس | پنل مدیریت",
|
||||
"short_name": "پنل نادمی تکس",
|
||||
"description": "پنل مدیریت سفارشهای عمده",
|
||||
"lang": "fa",
|
||||
"dir": "rtl",
|
||||
"start_url": "./",
|
||||
"display": "standalone",
|
||||
"background_color": "#081d39",
|
||||
"theme_color": "#081d39",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
const CACHE_NAME = 'nademitex-admin-v1'
|
||||
|
||||
self.addEventListener('install', () => {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
if (event.request.method !== 'GET') return
|
||||
|
||||
const url = new URL(event.request.url)
|
||||
if (url.origin !== self.location.origin || url.pathname.includes('/server/api')) return
|
||||
|
||||
event.respondWith(
|
||||
fetch(event.request)
|
||||
.then((response) => {
|
||||
const copy = response.clone()
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, copy))
|
||||
return response
|
||||
})
|
||||
.catch(() => caches.match(event.request)),
|
||||
)
|
||||
})
|
||||
@@ -2,8 +2,14 @@
|
||||
<html lang="fa" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<link rel="icon" href="%BASE_URL%favicon.ico">
|
||||
<link rel="icon" type="image/x-icon" href="./favicon.ico?v=2">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="./icon-192.png?v=2">
|
||||
<link rel="apple-touch-icon" href="./apple-touch-icon.png?v=2">
|
||||
<link rel="manifest" href="./manifest.webmanifest?v=2">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="theme-color" content="#081d39">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>پنل مدیریت | پارچه</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"name": "نادمی تکس | پنل مدیریت",
|
||||
"short_name": "پنل نادمی تکس",
|
||||
"description": "پنل مدیریت سفارشهای عمده",
|
||||
"lang": "fa",
|
||||
"dir": "rtl",
|
||||
"start_url": "./",
|
||||
"display": "standalone",
|
||||
"background_color": "#081d39",
|
||||
"theme_color": "#081d39",
|
||||
"icons": [
|
||||
{
|
||||
"src": "icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
},
|
||||
{
|
||||
"src": "icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "any maskable"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
const CACHE_NAME = 'nademitex-admin-v1'
|
||||
|
||||
self.addEventListener('install', () => {
|
||||
self.skipWaiting()
|
||||
})
|
||||
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(self.clients.claim())
|
||||
})
|
||||
|
||||
self.addEventListener('fetch', (event) => {
|
||||
if (event.request.method !== 'GET') return
|
||||
|
||||
const url = new URL(event.request.url)
|
||||
if (url.origin !== self.location.origin || url.pathname.includes('/server/api')) return
|
||||
|
||||
event.respondWith(
|
||||
fetch(event.request)
|
||||
.then((response) => {
|
||||
const copy = response.clone()
|
||||
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, copy))
|
||||
return response
|
||||
})
|
||||
.catch(() => caches.match(event.request)),
|
||||
)
|
||||
})
|
||||
@@ -116,7 +116,7 @@
|
||||
|
||||
.admin-btn--primary {
|
||||
background: var(--btn-primary-bg);
|
||||
color: #fff;
|
||||
color: var(--color-surface);
|
||||
border-color: var(--btn-primary-bg);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
@import './fonts.css';
|
||||
|
||||
:root {
|
||||
/* Brand — همتراز اپ کاربر (mockups / main.css) */
|
||||
--color-primary: #0a353c;
|
||||
--color-primary-hover: #072a2f;
|
||||
--color-primary-soft: #e8f4f1;
|
||||
--color-primary-dark: #062428;
|
||||
--color-primary-light: #e8f4f1;
|
||||
--color-primary-rgb: 10, 53, 60;
|
||||
--color-accent-navy: #1b223c;
|
||||
--color-sidebar-bg: #0a353c;
|
||||
--color-sidebar-active: #0e454e;
|
||||
--color-sidebar-hover: #0e454e;
|
||||
/* Brand — sampled from the Nademitex logo */
|
||||
--color-primary: #091d36;
|
||||
--color-primary-hover: #061426;
|
||||
--color-primary-soft: #e9edf2;
|
||||
--color-primary-dark: #04101f;
|
||||
--color-primary-light: #e9edf2;
|
||||
--color-primary-rgb: 9, 29, 54;
|
||||
--color-accent-navy: #091d36;
|
||||
--color-sidebar-bg: #091d36;
|
||||
--color-sidebar-active: #162b46;
|
||||
--color-sidebar-hover: #162b46;
|
||||
--color-success: #1b8a5a;
|
||||
--color-success-light: #eaf8f1;
|
||||
--color-success-dark: #0d7a4d;
|
||||
@@ -25,12 +25,12 @@
|
||||
--color-purple-light: #ede9fe;
|
||||
--color-info: #3b6fa0;
|
||||
--color-info-light: #eef3f7;
|
||||
--color-bg: #f6f8f7;
|
||||
--color-bg: #f3f5f7;
|
||||
--color-surface: #ffffff;
|
||||
--color-elevated: #ffffff;
|
||||
--color-border: #e5e9e8;
|
||||
--color-border-light: #eef1f0;
|
||||
--color-text: #121212;
|
||||
--color-border: #dfe4ea;
|
||||
--color-border-light: #e9edf2;
|
||||
--color-text: #121821;
|
||||
--color-text-muted: #6b7280;
|
||||
--color-text-secondary: #6b7280;
|
||||
--admin-primary: var(--color-primary);
|
||||
@@ -46,8 +46,8 @@
|
||||
--sidebar-width: 240px;
|
||||
--header-height: 72px;
|
||||
--font-family: 'IRANSans', 'Tahoma', sans-serif;
|
||||
--btn-primary-bg: #0a353c;
|
||||
--btn-primary-hover: #072a2f;
|
||||
--btn-primary-bg: var(--color-primary);
|
||||
--btn-primary-hover: var(--color-primary-hover);
|
||||
--admin-desktop-min: 1280px;
|
||||
--control-height: 40px;
|
||||
--control-height-sm: 32px;
|
||||
@@ -59,20 +59,20 @@
|
||||
--space-6: 24px;
|
||||
--transition-fast: 0.15s ease;
|
||||
--transition-base: 0.18s ease;
|
||||
--focus-ring: 0 0 0 3px rgba(10, 53, 60, 0.18);
|
||||
--focus-ring: 0 0 0 3px rgba(9, 29, 54, 0.2);
|
||||
}
|
||||
|
||||
html.dark {
|
||||
--color-primary: #5eb8ad;
|
||||
--color-primary-hover: #7ad0c5;
|
||||
--color-primary-soft: #14353a;
|
||||
--color-primary-dark: #7ad0c5;
|
||||
--color-primary-light: #14353a;
|
||||
--color-primary-rgb: 94, 184, 173;
|
||||
--color-accent-navy: #9bb0d0;
|
||||
--color-primary: #7d8695;
|
||||
--color-primary-hover: #aeb6c2;
|
||||
--color-primary-soft: #27303d;
|
||||
--color-primary-dark: #aeb6c2;
|
||||
--color-primary-light: #27303d;
|
||||
--color-primary-rgb: 125, 134, 149;
|
||||
--color-accent-navy: #7d8695;
|
||||
--color-sidebar-bg: #0b0f14;
|
||||
--color-sidebar-active: #14353a;
|
||||
--color-sidebar-hover: #121821;
|
||||
--color-sidebar-active: #27303d;
|
||||
--color-sidebar-hover: #1a2230;
|
||||
--color-success: #3dd68c;
|
||||
--color-success-light: #123528;
|
||||
--color-success-dark: #6ee7b7;
|
||||
@@ -91,14 +91,14 @@ html.dark {
|
||||
--color-elevated: #1a2230;
|
||||
--color-border: #2a3444;
|
||||
--color-border-light: #1a2230;
|
||||
--color-text: #eef2f8;
|
||||
--color-text: #ffffff;
|
||||
--color-text-muted: #9aa8bc;
|
||||
--color-text-secondary: #9aa8bc;
|
||||
--shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
||||
--shadow-card: 0 1px 3px rgba(0, 0, 0, 0.4), 0 4px 16px rgba(0, 0, 0, 0.35);
|
||||
--btn-primary-bg: #5eb8ad;
|
||||
--btn-primary-hover: #7ad0c5;
|
||||
--focus-ring: 0 0 0 3px rgba(94, 184, 173, 0.28);
|
||||
--btn-primary-bg: var(--color-primary);
|
||||
--btn-primary-hover: var(--color-primary-hover);
|
||||
--focus-ring: 0 0 0 3px rgba(125, 134, 149, 0.32);
|
||||
}
|
||||
|
||||
*,
|
||||
|
||||
@@ -14,7 +14,7 @@ const props = withDefaults(
|
||||
}>(),
|
||||
{
|
||||
height: 160,
|
||||
color: '#0a353c',
|
||||
color: '#091d36',
|
||||
unit: 'میلیارد تومان',
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { onClickOutside, useEventListener } from '@vueuse/core'
|
||||
import AdminIcon from '@/components/icons/AdminIcon.vue'
|
||||
import BasicIcon from '@/components/BasicPacks/BasicIcon.vue'
|
||||
import BaseModal from '@/components/ui/BaseModal.vue'
|
||||
import { useSmsWalletSummary } from '@/composables/useSmsWalletSummary'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { dashboardApi, notificationsApi } from '@/services/api'
|
||||
import { formatDateTime } from '@/utils/format'
|
||||
import { formatDateTime, formatNumber, toPersianDigits } from '@/utils/format'
|
||||
import { resolveAdminInboxLink } from '@/utils/inboxLinks'
|
||||
import { formatCardNumber, formatSheba } from '@/utils/smsFormat'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { useThemeStore, type ThemePreference } from '@/stores/theme'
|
||||
import type { AdminNotification, InternalMessage } from '@/types'
|
||||
@@ -20,6 +24,9 @@ type OpenPanel = 'messages' | 'notifications' | 'theme' | null
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const theme = useThemeStore()
|
||||
const toast = useToast()
|
||||
const { summary: smsSummary, refresh: refreshSmsSummary } = useSmsWalletSummary()
|
||||
const chargeOpen = ref(false)
|
||||
const openPanel = ref<OpenPanel>(null)
|
||||
const actionsRef = ref<HTMLElement | null>(null)
|
||||
|
||||
@@ -135,6 +142,30 @@ function selectTheme(value: ThemePreference) {
|
||||
theme.setPreference(value)
|
||||
closePanel()
|
||||
}
|
||||
|
||||
const smsCreditTone = computed(() => {
|
||||
const row = smsSummary.value
|
||||
if (!row) return 'ok'
|
||||
if (row.blocked || row.balanceToman <= row.redThreshold) return 'danger'
|
||||
if (row.balanceToman <= row.yellowThreshold) return 'warn'
|
||||
return 'ok'
|
||||
})
|
||||
|
||||
function refreshSmsCredit() {
|
||||
void refreshSmsSummary().catch(() => {})
|
||||
}
|
||||
|
||||
onMounted(refreshSmsCredit)
|
||||
useEventListener(window, 'focus', refreshSmsCredit)
|
||||
|
||||
async function copyDeposit(value: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(value.replace(/\s/g, ''))
|
||||
toast.success('کپی شد.')
|
||||
} catch {
|
||||
toast.error('کپی انجام نشد.')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -151,6 +182,21 @@ function selectTheme(value: ThemePreference) {
|
||||
</div>
|
||||
|
||||
<div ref="actionsRef" class="admin-header__left">
|
||||
<div
|
||||
class="sms-credit"
|
||||
:class="smsSummary ? `sms-credit--${smsCreditTone}` : ''"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
class="sms-credit__charge"
|
||||
:disabled="!smsSummary"
|
||||
@click="chargeOpen = true"
|
||||
>
|
||||
شارژ
|
||||
</button>
|
||||
<span>اعتبار پیامک: {{ smsSummary ? formatNumber(smsSummary.balanceRial) : '—' }} ریال</span>
|
||||
<span v-if="smsSummary?.blocked">مسدود</span>
|
||||
</div>
|
||||
<div class="admin-header__dropdown">
|
||||
<button
|
||||
type="button"
|
||||
@@ -336,6 +382,36 @@ function selectTheme(value: ThemePreference) {
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<BaseModal :open="chargeOpen" title="شارژ اعتبار پیامک" @close="chargeOpen = false">
|
||||
<div v-if="smsSummary" class="sms-charge">
|
||||
<p class="sms-charge__note">
|
||||
لطفا مبلغ مورد نظر خود جهت شارژ سامانه پیامکی را به شماره کارت یا حساب زیر واریز نمایید و تصویر فیش پرداختی را برای پشتیبانی بفرستید.
|
||||
</p>
|
||||
<div class="sms-charge__row">
|
||||
<button type="button" class="sms-charge__copy" @click="copyDeposit(smsSummary.deposit.cardNumber)">
|
||||
کپی
|
||||
</button>
|
||||
<div>
|
||||
<span>شماره کارت</span>
|
||||
<strong dir="ltr">{{ toPersianDigits(formatCardNumber(smsSummary.deposit.cardNumber)) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sms-charge__row">
|
||||
<button type="button" class="sms-charge__copy sms-charge__copy--sheba" @click="copyDeposit(smsSummary.deposit.sheba)">
|
||||
کپی
|
||||
</button>
|
||||
<div>
|
||||
<span>شماره شبا</span>
|
||||
<strong dir="ltr">{{ toPersianDigits(formatSheba(smsSummary.deposit.sheba)) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
<p class="sms-charge__name">
|
||||
به نام
|
||||
<strong>{{ smsSummary.deposit.accountName }}</strong>
|
||||
</p>
|
||||
</div>
|
||||
</BaseModal>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -725,5 +801,118 @@ function selectTheme(value: ThemePreference) {
|
||||
padding-right: 0;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.sms-credit {
|
||||
max-width: 46vw;
|
||||
}
|
||||
|
||||
.sms-credit span:nth-child(2) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.sms-credit {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
max-width: 100%;
|
||||
padding: 4px 12px 4px 4px;
|
||||
border-radius: 999px;
|
||||
background: #e8f8ef;
|
||||
color: #067647;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sms-credit--warn {
|
||||
background: #fff7ed;
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.sms-credit--danger {
|
||||
background: #fef2f2;
|
||||
color: #b91c1c;
|
||||
}
|
||||
|
||||
.sms-credit__charge {
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 4px 12px;
|
||||
background: #12b76a;
|
||||
color: #fff;
|
||||
font: inherit;
|
||||
font-weight: 800;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sms-credit--warn .sms-credit__charge {
|
||||
background: #f79009;
|
||||
}
|
||||
|
||||
.sms-credit--danger .sms-credit__charge {
|
||||
background: #f04438;
|
||||
}
|
||||
|
||||
.sms-charge__note {
|
||||
margin: 0 0 14px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 14px;
|
||||
background: #eef6ff;
|
||||
color: #334155;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.sms-charge__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.sms-charge__row span {
|
||||
display: block;
|
||||
margin-bottom: 4px;
|
||||
color: var(--color-text-muted, #64748b);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.sms-charge__row strong {
|
||||
font-size: 1.05rem;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.sms-charge__copy {
|
||||
border: 1px solid #c4b5fd;
|
||||
border-radius: 999px;
|
||||
padding: 4px 10px;
|
||||
background: #f5f3ff;
|
||||
color: #6d28d9;
|
||||
font: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sms-charge__copy--sheba {
|
||||
border-color: #a7f3d0;
|
||||
background: #ecfdf3;
|
||||
color: #067647;
|
||||
}
|
||||
|
||||
.sms-charge__name {
|
||||
margin: 4px 0 0;
|
||||
text-align: left;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.sms-charge__name strong {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ADMIN_NAV_ITEMS, isNavActive } from '@/utils/navigation'
|
||||
import { computed } from 'vue'
|
||||
import { isNavActive, navItemsForPhone } from '@/utils/navigation'
|
||||
import AdminIcon from '@/components/icons/AdminIcon.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
@@ -15,6 +16,8 @@ const emit = defineEmits<{
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const navItems = computed(() => navItemsForPhone(auth.session?.phone))
|
||||
const brandLogoUrl = `${import.meta.env.BASE_URL}brand-logo.png`
|
||||
|
||||
function navigate(path: string) {
|
||||
router.push(path)
|
||||
@@ -42,6 +45,7 @@ function logout() {
|
||||
:aria-hidden="!open"
|
||||
>
|
||||
<div class="admin-drawer__brand">
|
||||
<img :src="brandLogoUrl" alt="نادمی تکس" class="admin-drawer__logo" />
|
||||
<strong>پارچه</strong>
|
||||
<span>پنل مدیریت</span>
|
||||
<button type="button" class="admin-drawer__close" aria-label="بستن منو" @click="emit('close')">
|
||||
@@ -51,7 +55,7 @@ function logout() {
|
||||
|
||||
<nav class="admin-drawer__nav">
|
||||
<button
|
||||
v-for="item in ADMIN_NAV_ITEMS"
|
||||
v-for="item in navItems"
|
||||
:key="item.name"
|
||||
type="button"
|
||||
class="admin-drawer__item"
|
||||
@@ -78,6 +82,14 @@ function logout() {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.admin-drawer__logo {
|
||||
display: block;
|
||||
width: 92px;
|
||||
height: 64px;
|
||||
object-fit: contain;
|
||||
margin: 0 auto 8px;
|
||||
}
|
||||
|
||||
.admin-drawer__brand strong {
|
||||
display: block;
|
||||
font-size: 1.0625rem;
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
<script setup lang="ts">
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ADMIN_NAV_ITEMS, isNavActive } from '@/utils/navigation'
|
||||
import { computed } from 'vue'
|
||||
import { isNavActive, navItemsForPhone } from '@/utils/navigation'
|
||||
import AdminIcon from '@/components/icons/AdminIcon.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
const navItems = computed(() => navItemsForPhone(auth.session?.phone))
|
||||
const brandLogoUrl = `${import.meta.env.BASE_URL}brand-logo.png`
|
||||
|
||||
function navigate(path: string) {
|
||||
router.push(path)
|
||||
@@ -21,13 +24,14 @@ function logout() {
|
||||
<template>
|
||||
<aside class="admin-sidebar" aria-label="ناوبری پنل مدیریت">
|
||||
<div class="admin-sidebar__brand">
|
||||
<img :src="brandLogoUrl" alt="نادمی تکس" class="admin-sidebar__logo" />
|
||||
<strong class="admin-sidebar__title">پارچه</strong>
|
||||
<span class="admin-sidebar__subtitle">پنل مدیریت</span>
|
||||
</div>
|
||||
|
||||
<nav class="admin-sidebar__nav">
|
||||
<button
|
||||
v-for="item in ADMIN_NAV_ITEMS"
|
||||
v-for="item in navItems"
|
||||
:key="item.name"
|
||||
type="button"
|
||||
class="admin-sidebar__item"
|
||||
@@ -64,6 +68,14 @@ function logout() {
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.admin-sidebar__logo {
|
||||
display: block;
|
||||
width: 92px;
|
||||
height: 64px;
|
||||
object-fit: contain;
|
||||
margin: 0 auto 8px;
|
||||
}
|
||||
|
||||
.admin-sidebar__title {
|
||||
display: block;
|
||||
font-size: 1.125rem;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ref } from 'vue'
|
||||
import { smsWalletApi, type SmsWalletSummary } from '@/services/api'
|
||||
|
||||
const summary = ref<SmsWalletSummary | null>(null)
|
||||
|
||||
export function useSmsWalletSummary() {
|
||||
async function refresh() {
|
||||
summary.value = await smsWalletApi.summary()
|
||||
}
|
||||
|
||||
return { summary, refresh }
|
||||
}
|
||||
@@ -21,3 +21,11 @@ useThemeStore(pinia)
|
||||
useAuthStore(pinia).hydrate()
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
if (import.meta.env.PROD && 'serviceWorker' in navigator) {
|
||||
window.addEventListener('load', () => {
|
||||
void navigator.serviceWorker.register(`${import.meta.env.BASE_URL}sw.js`, {
|
||||
scope: import.meta.env.BASE_URL,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import AdminShell from '@/layouts/AdminShell.vue'
|
||||
import { useAuthStore } from '@/stores/auth'
|
||||
import { isSmsWalletOwner } from '@/utils/navigation'
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
@@ -151,6 +152,12 @@ const router = createRouter({
|
||||
component: () => import('@/views/settings/SettingsView.vue'),
|
||||
meta: { title: 'تنظیمات' },
|
||||
},
|
||||
{
|
||||
path: 'sms-tariff',
|
||||
name: 'sms-tariff',
|
||||
component: () => import('@/views/sms/SmsTariffView.vue'),
|
||||
meta: { title: 'تعرفه پیامکها', smsWalletOwner: true },
|
||||
},
|
||||
{
|
||||
path: 'users',
|
||||
name: 'users',
|
||||
@@ -185,6 +192,10 @@ router.beforeEach(async (to) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (to.meta.smsWalletOwner && !isSmsWalletOwner(auth.session?.phone)) {
|
||||
return { name: 'dashboard' }
|
||||
}
|
||||
|
||||
return true
|
||||
})
|
||||
|
||||
|
||||
@@ -667,3 +667,46 @@ export const stockAlertsApi = {
|
||||
notify: (id: string) =>
|
||||
apiRequest<StockAlert>(`/admin/stock-alerts/${id}/notify`, { method: 'PATCH' }),
|
||||
}
|
||||
|
||||
/* ── SMS wallet ───────────────────────────────────────────── */
|
||||
|
||||
export type SmsUsageStats = {
|
||||
messages: number
|
||||
parts: number
|
||||
costToman: number
|
||||
}
|
||||
|
||||
export type SmsWalletSummary = {
|
||||
balanceToman: number
|
||||
balanceRial: number
|
||||
blocked: boolean
|
||||
yellowThreshold: number
|
||||
redThreshold: number
|
||||
deposit: {
|
||||
cardNumber: string
|
||||
sheba: string
|
||||
accountName: string
|
||||
}
|
||||
}
|
||||
|
||||
export type SmsWalletManage = {
|
||||
balanceToman: number
|
||||
pricePerPart: number
|
||||
blocked: boolean
|
||||
cardNumber: string
|
||||
sheba: string
|
||||
accountName: string
|
||||
yellowThreshold: number
|
||||
redThreshold: number
|
||||
total: SmsUsageStats
|
||||
today: SmsUsageStats
|
||||
}
|
||||
|
||||
export const smsWalletApi = {
|
||||
summary: () => apiRequest<SmsWalletSummary>('/admin/sms-wallet'),
|
||||
manage: () => apiRequest<SmsWalletManage>('/admin/sms-wallet/manage'),
|
||||
update: (body: Partial<Omit<SmsWalletManage, 'balanceToman' | 'total' | 'today'>>) =>
|
||||
apiRequest<SmsWalletManage>('/admin/sms-wallet', { method: 'PATCH', body }),
|
||||
charge: (body: { amountToman: number; note?: string }) =>
|
||||
apiRequest<SmsWalletManage>('/admin/sms-wallet/charge', { body }),
|
||||
}
|
||||
|
||||
@@ -24,9 +24,25 @@ export const ADMIN_NAV_ITEMS: NavItem[] = [
|
||||
{ name: 'shipping', label: 'ارسال و باربری', path: '/shipping', icon: 'shipping' },
|
||||
{ name: 'reports', label: 'گزارشها', path: '/reports', icon: 'reports' },
|
||||
{ name: 'settings', label: 'تنظیمات', path: '/settings', icon: 'settings' },
|
||||
{ name: 'sms-tariff', label: 'تعرفه پیامکها', path: '/sms-tariff', icon: 'sms' },
|
||||
{ name: 'users', label: 'کاربران و دسترسیها', path: '/users', icon: 'users' },
|
||||
]
|
||||
|
||||
export const SMS_WALLET_OWNER_PHONE = '09128081392'
|
||||
|
||||
export function isSmsWalletOwner(phone?: string | null): boolean {
|
||||
if (!phone) return false
|
||||
let digits = phone.replace(/\D/g, '')
|
||||
if (digits.startsWith('98') && digits.length === 12) digits = `0${digits.slice(2)}`
|
||||
if (digits.length === 10 && digits.startsWith('9')) digits = `0${digits}`
|
||||
return digits === SMS_WALLET_OWNER_PHONE
|
||||
}
|
||||
|
||||
export function navItemsForPhone(phone?: string | null): NavItem[] {
|
||||
if (isSmsWalletOwner(phone)) return ADMIN_NAV_ITEMS
|
||||
return ADMIN_NAV_ITEMS.filter((item) => item.name !== 'sms-tariff')
|
||||
}
|
||||
|
||||
export function isNavActive(path: string, currentPath: string): boolean {
|
||||
if (path === '/') return currentPath === '/'
|
||||
// چکهای یک سفارش زیر منوی «چکها» فعال میشود، نه «سفارشها»
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
export function formatCardNumber(raw: string): string {
|
||||
const digits = raw.replace(/\D/g, '')
|
||||
return digits.replace(/(\d{4})(?=\d)/g, '$1 ').trim()
|
||||
}
|
||||
|
||||
export function formatSheba(raw: string): string {
|
||||
const compact = raw.replace(/[\s-]/g, '').toUpperCase()
|
||||
const body = compact.startsWith('IR') ? compact.slice(2) : compact
|
||||
if (body.length < 2) return compact
|
||||
const groups = body.slice(2).replace(/(.{4})/g, '$1 ').trim()
|
||||
return `IR${body.slice(0, 2)} ${groups}`.trim()
|
||||
}
|
||||
@@ -16,6 +16,7 @@ const password = ref(isMockAuthEnabled() ? MOCK_ADMIN.password : '')
|
||||
const remember = ref(true)
|
||||
const showPassword = ref(false)
|
||||
const mockAuth = isMockAuthEnabled()
|
||||
const brandLogoUrl = `${import.meta.env.BASE_URL}brand-logo.png`
|
||||
|
||||
const isFormValid = computed(() => phone.value.trim().length > 0 && password.value.length > 0)
|
||||
|
||||
@@ -123,6 +124,7 @@ async function submit() {
|
||||
|
||||
<div class="login-page__brand">
|
||||
<div class="login-page__brand-inner">
|
||||
<img :src="brandLogoUrl" alt="نادمی تکس" class="login-page__brand-logo" />
|
||||
<p class="login-page__brand-badge">پارچه</p>
|
||||
<h1 class="login-page__brand-title">پنل مدیریت سفارشهای عمده</h1>
|
||||
<p class="login-page__brand-text">
|
||||
@@ -146,7 +148,12 @@ async function submit() {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 48px 40px;
|
||||
background: linear-gradient(160deg, #062428 0%, #0a353c 55%, #0e454e 100%);
|
||||
background: linear-gradient(
|
||||
160deg,
|
||||
var(--color-primary-dark) 0%,
|
||||
var(--color-primary) 55%,
|
||||
var(--color-sidebar-active) 100%
|
||||
);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
@@ -154,6 +161,14 @@ async function submit() {
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.login-page__brand-logo {
|
||||
display: block;
|
||||
width: min(220px, 70%);
|
||||
height: 150px;
|
||||
object-fit: contain;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.login-page__brand-badge {
|
||||
display: inline-flex;
|
||||
margin: 0 0 16px;
|
||||
|
||||
@@ -0,0 +1,445 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import AdminCurrencyInput from '@/components/ui/AdminCurrencyInput.vue'
|
||||
import AdminPageSkeleton from '@/components/ui/AdminPageSkeleton.vue'
|
||||
import BaseSwitch from '@/components/ui/BaseSwitch.vue'
|
||||
import { useSmsWalletSummary } from '@/composables/useSmsWalletSummary'
|
||||
import { useToast } from '@/composables/useToast'
|
||||
import { smsWalletApi, type SmsUsageStats, type SmsWalletManage } from '@/services/api'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import { formatCardNumber, formatSheba } from '@/utils/smsFormat'
|
||||
|
||||
const toast = useToast()
|
||||
const { refresh: refreshSummary } = useSmsWalletSummary()
|
||||
|
||||
const loading = ref(true)
|
||||
const savingPrice = ref(false)
|
||||
const charging = ref(false)
|
||||
const savingDeposit = ref(false)
|
||||
const savingThresholds = ref(false)
|
||||
const toggling = ref(false)
|
||||
|
||||
const wallet = ref<SmsWalletManage | null>(null)
|
||||
const pricePerPart = ref<number | null>(250)
|
||||
const chargeAmount = ref<number | null>(null)
|
||||
const chargeNote = ref('')
|
||||
const cardNumber = ref('')
|
||||
const sheba = ref('')
|
||||
const accountName = ref('')
|
||||
const yellowThreshold = ref<number | null>(10000)
|
||||
const redThreshold = ref<number | null>(1000)
|
||||
|
||||
function apply(data: SmsWalletManage) {
|
||||
wallet.value = data
|
||||
pricePerPart.value = data.pricePerPart
|
||||
cardNumber.value = formatCardNumber(data.cardNumber)
|
||||
sheba.value = formatSheba(data.sheba)
|
||||
accountName.value = data.accountName
|
||||
yellowThreshold.value = data.yellowThreshold
|
||||
redThreshold.value = data.redThreshold
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
apply(await smsWalletApi.manage())
|
||||
await refreshSummary().catch(() => {})
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : 'دریافت تعرفه پیامک ناموفق بود.')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
async function savePrice() {
|
||||
if (!pricePerPart.value || pricePerPart.value < 1 || savingPrice.value) return
|
||||
savingPrice.value = true
|
||||
try {
|
||||
apply(await smsWalletApi.update({ pricePerPart: pricePerPart.value }))
|
||||
toast.success('تعرفه پیامک ذخیره شد.')
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : 'ذخیره تعرفه ناموفق بود.')
|
||||
} finally {
|
||||
savingPrice.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function charge() {
|
||||
if (!chargeAmount.value || chargeAmount.value < 1 || charging.value) return
|
||||
charging.value = true
|
||||
try {
|
||||
apply(
|
||||
await smsWalletApi.charge({
|
||||
amountToman: chargeAmount.value,
|
||||
note: chargeNote.value.trim() || undefined,
|
||||
}),
|
||||
)
|
||||
chargeAmount.value = null
|
||||
chargeNote.value = ''
|
||||
await refreshSummary()
|
||||
toast.success('کیف پول پیامک شارژ شد.')
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : 'شارژ کیف پول ناموفق بود.')
|
||||
} finally {
|
||||
charging.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function setBlocked(next: boolean) {
|
||||
if (!wallet.value || toggling.value) return
|
||||
const previous = wallet.value.blocked
|
||||
wallet.value.blocked = next
|
||||
toggling.value = true
|
||||
try {
|
||||
apply(await smsWalletApi.update({ blocked: next }))
|
||||
await refreshSummary()
|
||||
toast.success(next ? 'ارسال پیامک مسدود شد.' : 'ارسال پیامک فعال شد.')
|
||||
} catch (e) {
|
||||
wallet.value.blocked = previous
|
||||
toast.error(e instanceof Error ? e.message : 'تغییر وضعیت مسدودسازی ناموفق بود.')
|
||||
} finally {
|
||||
toggling.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function saveDeposit() {
|
||||
if (savingDeposit.value) return
|
||||
savingDeposit.value = true
|
||||
try {
|
||||
apply(
|
||||
await smsWalletApi.update({
|
||||
cardNumber: cardNumber.value,
|
||||
sheba: sheba.value,
|
||||
accountName: accountName.value,
|
||||
}),
|
||||
)
|
||||
await refreshSummary()
|
||||
toast.success('اطلاعات واریز ذخیره شد.')
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : 'ذخیره اطلاعات واریز ناموفق بود.')
|
||||
} finally {
|
||||
savingDeposit.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function saveThresholds() {
|
||||
if (yellowThreshold.value == null || redThreshold.value == null || savingThresholds.value) return
|
||||
savingThresholds.value = true
|
||||
try {
|
||||
apply(
|
||||
await smsWalletApi.update({
|
||||
yellowThreshold: yellowThreshold.value,
|
||||
redThreshold: redThreshold.value,
|
||||
}),
|
||||
)
|
||||
await refreshSummary()
|
||||
toast.success('آستانهها ذخیره شد.')
|
||||
} catch (e) {
|
||||
toast.error(e instanceof Error ? e.message : 'ذخیره آستانهها ناموفق بود.')
|
||||
} finally {
|
||||
savingThresholds.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function stat(row: SmsUsageStats | undefined) {
|
||||
return row ?? { messages: 0, parts: 0, costToman: 0 }
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="sms-page">
|
||||
<div class="admin-page-header">
|
||||
<h1 class="admin-page-header__title">تعرفه پیامکها</h1>
|
||||
</div>
|
||||
|
||||
<AdminPageSkeleton v-if="loading" variant="form" :rows="8" />
|
||||
|
||||
<template v-else-if="wallet">
|
||||
<section class="sms-stats">
|
||||
<article class="sms-stat sms-stat--balance">
|
||||
<h2>مانده جاری</h2>
|
||||
<p><span>{{ formatNumber(stat(wallet.total).messages) }}</span> پیامک</p>
|
||||
<p><span>{{ formatNumber(stat(wallet.total).parts) }}</span> پارت</p>
|
||||
<p><span>{{ formatNumber(stat(wallet.total).costToman) }}</span> تومان</p>
|
||||
</article>
|
||||
<article class="sms-stat sms-stat--today">
|
||||
<h2>امروز</h2>
|
||||
<p><span>{{ formatNumber(stat(wallet.today).messages) }}</span> پیامک</p>
|
||||
<p><span>{{ formatNumber(stat(wallet.today).parts) }}</span> پارت</p>
|
||||
<p><span>{{ formatNumber(stat(wallet.today).costToman) }}</span> تومان</p>
|
||||
</article>
|
||||
<article class="sms-stat sms-stat--price">
|
||||
<h2>قیمت هر پارت</h2>
|
||||
<strong>{{ formatNumber(wallet.pricePerPart) }} تومان</strong>
|
||||
</article>
|
||||
<article class="sms-stat sms-stat--wallet">
|
||||
<h2>موجودی کیف پول</h2>
|
||||
<strong>{{ formatNumber(wallet.balanceToman) }} تومان</strong>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section class="admin-card sms-manage">
|
||||
<h2>مدیریت کیف پول</h2>
|
||||
<div class="sms-manage__row">
|
||||
<div class="sms-manage__tariff">
|
||||
<span>تعرفه پیامک</span>
|
||||
<strong>{{ formatNumber(wallet.pricePerPart) }}</strong>
|
||||
</div>
|
||||
<div class="sms-manage__price">
|
||||
<AdminCurrencyInput v-model="pricePerPart" :allow-empty="false" />
|
||||
<button
|
||||
type="button"
|
||||
class="admin-btn admin-btn--success"
|
||||
:disabled="savingPrice || !pricePerPart"
|
||||
@click="savePrice"
|
||||
>
|
||||
{{ savingPrice ? 'در حال ذخیره…' : 'ذخیره قیمت' }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="sms-manage__charge">
|
||||
<AdminCurrencyInput v-model="chargeAmount" placeholder="مبلغ (تومان)" />
|
||||
<input
|
||||
v-model="chargeNote"
|
||||
class="admin-input"
|
||||
type="text"
|
||||
placeholder="توضیح (اختیاری)"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
class="admin-btn admin-btn--success"
|
||||
:disabled="charging || !chargeAmount"
|
||||
@click="charge"
|
||||
>
|
||||
{{ charging ? 'در حال شارژ…' : 'شارژ کیف پول' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<p class="sms-manage__hint">هر پیامک ۶۷ کاراکتر است. پیام ۱۴۰ کاراکتری ۳ پارت محاسبه میشود.</p>
|
||||
<div class="sms-block">
|
||||
<div>
|
||||
<strong>مسدودسازی ارسال پیامک</strong>
|
||||
<p>در حالت مسدود، ارسال پیامک متوقف میشود.</p>
|
||||
</div>
|
||||
<div class="sms-block__switch">
|
||||
<span>{{ wallet.blocked ? 'فعال' : 'خاموش' }}</span>
|
||||
<BaseSwitch
|
||||
:model-value="wallet.blocked"
|
||||
:loading="toggling"
|
||||
active-text="فعال"
|
||||
inactive-text="خاموش"
|
||||
@update:model-value="setBlocked"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="admin-card sms-deposit">
|
||||
<h2>اطلاعات واریز شارژ</h2>
|
||||
<p class="sms-deposit__hint">
|
||||
لطفا مبلغ مورد نظر خود جهت شارژ سامانه پیامکی را به شماره کارت یا حساب زیر واریز نمایید و تصویر فیش پرداختی را برای پشتیبانی ارسال فرمایید.
|
||||
</p>
|
||||
<label class="admin-form__field">
|
||||
<span>شماره کارت</span>
|
||||
<input v-model="cardNumber" class="admin-input" dir="ltr" inputmode="numeric" />
|
||||
</label>
|
||||
<label class="admin-form__field">
|
||||
<span>شماره شبا</span>
|
||||
<input v-model="sheba" class="admin-input" dir="ltr" />
|
||||
</label>
|
||||
<label class="admin-form__field">
|
||||
<span>به نام</span>
|
||||
<input v-model="accountName" class="admin-input" />
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
class="admin-btn admin-btn--success sms-deposit__save"
|
||||
:disabled="savingDeposit"
|
||||
@click="saveDeposit"
|
||||
>
|
||||
{{ savingDeposit ? 'در حال ذخیره…' : 'ذخیره اطلاعات واریز' }}
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<section class="admin-card sms-thresholds">
|
||||
<h2>آستانههای رنگ موجودی</h2>
|
||||
<div class="sms-thresholds__row">
|
||||
<label class="admin-form__field">
|
||||
<span>آستانه زرد</span>
|
||||
<AdminCurrencyInput v-model="yellowThreshold" :allow-empty="false" />
|
||||
</label>
|
||||
<label class="admin-form__field">
|
||||
<span>آستانه قرمز</span>
|
||||
<AdminCurrencyInput v-model="redThreshold" :allow-empty="false" />
|
||||
</label>
|
||||
<button
|
||||
type="button"
|
||||
class="admin-btn admin-btn--success"
|
||||
:disabled="savingThresholds"
|
||||
@click="saveThresholds"
|
||||
>
|
||||
{{ savingThresholds ? 'در حال ذخیره…' : 'ذخیره آستانهها' }}
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.sms-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.sms-stat {
|
||||
border-radius: 16px;
|
||||
padding: 16px 18px;
|
||||
min-height: 112px;
|
||||
}
|
||||
|
||||
.sms-stat h2 {
|
||||
margin: 0 0 10px;
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.sms-stat p {
|
||||
margin: 0;
|
||||
color: var(--color-text-muted, #64748b);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.sms-stat p span,
|
||||
.sms-stat strong {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.sms-stat--balance {
|
||||
background: #f3e8ff;
|
||||
color: #6d28d9;
|
||||
}
|
||||
|
||||
.sms-stat--today {
|
||||
background: #fff7ed;
|
||||
color: #c2410c;
|
||||
}
|
||||
|
||||
.sms-stat--price {
|
||||
background: #eff6ff;
|
||||
color: #1d4ed8;
|
||||
}
|
||||
|
||||
.sms-stat--price strong,
|
||||
.sms-stat--wallet strong {
|
||||
display: block;
|
||||
margin-top: 18px;
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.sms-stat--wallet {
|
||||
background: #fffbeb;
|
||||
color: #b45309;
|
||||
}
|
||||
|
||||
.sms-manage,
|
||||
.sms-deposit,
|
||||
.sms-thresholds {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.sms-manage h2,
|
||||
.sms-deposit h2,
|
||||
.sms-thresholds h2 {
|
||||
margin: 0 0 14px;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.sms-manage__row,
|
||||
.sms-thresholds__row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.sms-manage__tariff,
|
||||
.sms-manage__price,
|
||||
.sms-manage__charge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sms-manage__price :deep(input),
|
||||
.sms-manage__charge :deep(input),
|
||||
.sms-manage__charge .admin-input,
|
||||
.sms-thresholds__row :deep(input) {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.sms-manage__tariff strong {
|
||||
min-width: 72px;
|
||||
text-align: center;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.sms-manage__charge {
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.sms-manage__hint,
|
||||
.sms-deposit__hint {
|
||||
margin: 12px 0 0;
|
||||
color: var(--color-text-muted, #64748b);
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.sms-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin-top: 14px;
|
||||
padding: 12px 14px;
|
||||
border-radius: 12px;
|
||||
background: #ecfdf3;
|
||||
}
|
||||
|
||||
.sms-block p {
|
||||
margin: 4px 0 0;
|
||||
color: #067647;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.sms-block__switch {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: #067647;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.sms-deposit__save {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.sms-thresholds__row .admin-btn {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.admin-form__field > span {
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.sms-stats {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.sms-manage__charge {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||