Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ad88af20f | ||
|
|
cad1a8652e | ||
|
|
a8184ed59c | ||
|
|
3dc5f4ae91 | ||
|
|
f64b18aafb |
@@ -1,2 +1,2 @@
|
|||||||
# loaded by `vite build` (the pre-built dist/ the admin image serves); dev uses http://localhost:3000
|
# loaded by `vite build` (the pre-built dist/ the admin image serves); dev uses http://localhost:3000
|
||||||
VITE_SITE_URL=https://imenpishgaman.ir
|
VITE_SITE_URL=http://imenpishgaman.ir
|
||||||
|
|||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
import{b as e}from"./VuesaxIcon-B2OgulS6.js";import{Et as t,It as n,Nt as r,Ot as i,Tt as a,_t as o,dt as s,i as c,kt as l,lt as u,ot as d,r as f,yt as p}from"./index-BuGK6ptM.js";var m=Symbol(`form-errors`),h=e=>Object.fromEntries(e.map(e=>[e.path,e.message]));function g(r,i){let a=n(structuredClone(i)),o=n({});t(m,o);function s(){let t=r.safeParse(a.value);return t.success?(o.value={},t.data):(o.value=h(e(t.error)),d.error(`لطفاً خطاهای فرم را برطرف کنید`),null)}function l(e){let t=f(e).issues;t?.length&&(o.value=h(t)),d.error(c(e))}function u(e){a.value=structuredClone(e),o.value={}}return{model:a,errors:o,validate:s,handleError:l,reset:u}}var _=o({__name:`FormField`,props:{path:{},label:{},hint:{},required:{type:Boolean}},setup(e){let t=e,n=p(m,null),o=u(()=>n?.value[t.path]);return(t,n)=>{let c=l(`a-form-item`);return a(),s(c,{label:e.label,required:e.required,"validate-status":o.value?`error`:void 0,help:o.value||e.hint},{default:r(()=>[i(t.$slots,`default`)]),_:3},8,[`label`,`required`,`validate-status`,`help`])}}});export{g as n,_ as t};
|
import{b as e}from"./VuesaxIcon-UCFGkXBv.js";import{Et as t,It as n,Nt as r,Ot as i,Tt as a,_t as o,dt as s,i as c,kt as l,lt as u,ot as d,r as f,yt as p}from"./index-1U5l_fLl.js";var m=Symbol(`form-errors`),h=e=>Object.fromEntries(e.map(e=>[e.path,e.message]));function g(r,i){let a=n(structuredClone(i)),o=n({});t(m,o);function s(){let t=r.safeParse(a.value);return t.success?(o.value={},t.data):(o.value=h(e(t.error)),d.error(`لطفاً خطاهای فرم را برطرف کنید`),null)}function l(e){let t=f(e).issues;t?.length&&(o.value=h(t)),d.error(c(e))}function u(e){a.value=structuredClone(e),o.value={}}return{model:a,errors:o,validate:s,handleError:l,reset:u}}var _=o({__name:`FormField`,props:{path:{},label:{},hint:{},required:{type:Boolean}},setup(e){let t=e,n=p(m,null),o=u(()=>n?.value[t.path]);return(t,n)=>{let c=l(`a-form-item`);return a(),s(c,{label:e.label,required:e.required,"validate-status":o.value?`error`:void 0,help:o.value||e.hint},{default:r(()=>[i(t.$slots,`default`)]),_:3},8,[`label`,`required`,`validate-status`,`help`])}}});export{g as n,_ as t};
|
||||||
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
import{t as e,y as t}from"./VuesaxIcon-B2OgulS6.js";import{Bt as n,Lt as r,Nt as i,Tt as a,_t as o,bt as s,dt as c,gt as l,jt as u,kt as d,pt as f,ut as p}from"./index-BuGK6ptM.js";var m={class:`flex items-center gap-2`},h={class:`flex size-9 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-gray-50 text-[#ff5a00]`},g={key:1,class:`text-xs text-gray-400`},_={class:`flex items-center gap-2.5 py-0.5`,dir:`ltr`},v={class:`text-xs font-mono`},y=o({__name:`IconSelect`,props:s({allowClear:{type:Boolean}},{modelValue:{required:!0},modelModifiers:{}}),emits:[`update:modelValue`],setup(o){let s=u(o,`modelValue`),y=t.map(e=>({value:e,label:e}));return(t,u)=>{let b=d(`a-select`);return a(),f(`div`,m,[p(`span`,h,[s.value?(a(),c(e,{key:0,name:s.value,size:20},null,8,[`name`])):(a(),f(`span`,g,`---`))]),l(b,{value:s.value,"onUpdate:value":u[0]||=e=>s.value=e,class:`min-w-0 flex-1`,"show-search":``,"allow-clear":o.allowClear,options:r(y),placeholder:`انتخاب آیکون`},{option:i(({value:t})=>[p(`span`,_,[l(e,{name:t,size:18,class:`text-orange-600`},null,8,[`name`]),p(`span`,v,n(t),1)])]),_:1},8,[`value`,`allow-clear`,`options`])])}}});export{y as t};
|
import{t as e,y as t}from"./VuesaxIcon-UCFGkXBv.js";import{Bt as n,Lt as r,Nt as i,Tt as a,_t as o,bt as s,dt as c,gt as l,jt as u,kt as d,pt as f,ut as p}from"./index-1U5l_fLl.js";var m={class:`flex items-center gap-2`},h={class:`flex size-9 shrink-0 items-center justify-center rounded-lg border border-gray-200 bg-gray-50 text-[#ff5a00]`},g={key:1,class:`text-xs text-gray-400`},_={class:`flex items-center gap-2.5 py-0.5`,dir:`ltr`},v={class:`text-xs font-mono`},y=o({__name:`IconSelect`,props:s({allowClear:{type:Boolean}},{modelValue:{required:!0},modelModifiers:{}}),emits:[`update:modelValue`],setup(o){let s=u(o,`modelValue`),y=t.map(e=>({value:e,label:e}));return(t,u)=>{let b=d(`a-select`);return a(),f(`div`,m,[p(`span`,h,[s.value?(a(),c(e,{key:0,name:s.value,size:20},null,8,[`name`])):(a(),f(`span`,g,`---`))]),l(b,{value:s.value,"onUpdate:value":u[0]||=e=>s.value=e,class:`min-w-0 flex-1`,"show-search":``,"allow-clear":o.allowClear,options:r(y),placeholder:`انتخاب آیکون`},{option:i(({value:t})=>[p(`span`,_,[l(e,{name:t,size:18,class:`text-orange-600`},null,8,[`name`]),p(`span`,v,n(t),1)])]),_:1},8,[`value`,`allow-clear`,`options`])])}}});export{y as t};
|
||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
import{c as e,n as t,t as n}from"./VuesaxIcon-B2OgulS6.js";import{It as r,Lt as i,Nt as a,Tt as o,_t as s,a as c,gt as l,ht as u,kt as d,mt as f,o as p,pt as m,st as h,t as g,ut as _}from"./index-BuGK6ptM.js";import"./logo-BuUh917n.js";import{n as v,t as y}from"./FormField-CiVT7mqC.js";var b={class:`login-page relative flex min-h-screen overflow-hidden text-[#edede6]`},x={class:`relative z-10 mx-auto grid w-full max-w-[1120px] flex-1 items-center gap-10 px-5 py-12 md:px-8 lg:grid-cols-12 lg:gap-16`},S={class:`login-fade-3 lg:col-span-6`},C={class:`login-panel w-full max-w-md border border-[#2a3d63]/70 bg-[#1a2744]/80 p-7 backdrop-blur-md md:p-8 lg:me-auto`},w=t(s({__name:`LoginPage`,setup(t){let s=g(),w=p(),T=c(),{model:E,validate:D,handleError:O}=v(e,{phone:``,password:``}),k=r(!1);async function A(){let e=D();if(e){k.value=!0;try{await s.login(e);let t=typeof T.query.redirect==`string`?T.query.redirect:`/`;await w.replace(t.startsWith(`/`)?t:`/`)}catch(e){O(e)}finally{k.value=!1}}}return(e,t)=>{let r=d(`a-input`),s=d(`a-input-password`),c=d(`a-button`),p=d(`a-form`);return o(),m(`div`,b,[t[5]||=f(`<div class="pointer-events-none absolute inset-0 bg-[#0b1224]" data-v-2d323d25></div><div class="login-grid pointer-events-none absolute inset-0 opacity-50" data-v-2d323d25></div><div class="pointer-events-none absolute -start-24 top-1/4 size-[420px] rounded-full bg-[#ff5a00]/15 blur-[120px]" data-v-2d323d25></div><div class="pointer-events-none absolute -end-16 bottom-0 size-[360px] rounded-full bg-[#ffb800]/10 blur-[100px]" data-v-2d323d25></div><div class="login-tape pointer-events-none absolute bottom-0 end-0 z-10 h-48 w-1.5 opacity-70" data-v-2d323d25></div><div class="login-noise pointer-events-none absolute inset-0 z-[5]" data-v-2d323d25></div>`,6),_(`div`,x,[_(`div`,S,[_(`div`,C,[t[3]||=_(`div`,{class:`mb-7 text-center`},[_(`div`,{class:`font-mono text-[10px] tracking-[0.28em] text-[#8e9ab0]`},`SIGN IN`),_(`h2`,{class:`mt-2 text-xl font-bold text-[#edede6]`},`ورود به پنل`),_(`p`,{class:`mt-1.5 text-sm text-[#8e9ab0]`},`شماره موبایل و رمز عبور خود را وارد کنید`)],-1),l(p,{class:`login-form`,layout:`vertical`,onSubmit:h(A,[`prevent`])},{default:a(()=>[l(y,{path:`phone`,label:`شماره موبایل`},{default:a(()=>[l(r,{value:i(E).phone,"onUpdate:value":t[0]||=e=>i(E).phone=e,size:`large`,class:`ltr-input`,autocomplete:`tel`,autofocus:``,inputmode:`tel`,placeholder:`09121234567`},{prefix:a(()=>[l(n,{name:`mobile`,size:16,class:`text-[#8e9ab0]`})]),_:1},8,[`value`])]),_:1}),l(y,{path:`password`,label:`رمز عبور`},{default:a(()=>[l(s,{value:i(E).password,"onUpdate:value":t[1]||=e=>i(E).password=e,size:`large`,class:`ltr-input`,autocomplete:`current-password`,placeholder:`••••••••`},{prefix:a(()=>[l(n,{name:`lock`,size:16,class:`text-[#8e9ab0]`})]),_:1},8,[`value`])]),_:1}),l(c,{type:`primary`,"html-type":`submit`,size:`large`,block:``,loading:k.value,class:`!mt-2 !h-12 !rounded-full !font-bold !shadow-[0_8px_32px_rgba(255,90,0,0.35)]`},{default:a(()=>[...t[2]||=[u(` ورود به پنل `,-1)]]),_:1},8,[`loading`])]),_:1})])]),t[4]||=f(`<div class="login-fade-1 lg:col-span-6" data-v-2d323d25><div class="inline-flex items-center gap-2.5 rounded-full border border-[#ff5a00]/30 bg-[#ff5a00]/10 px-4 py-2 backdrop-blur" data-v-2d323d25><span class="size-1.5 animate-pulse rounded-full bg-[#ff5a00]" data-v-2d323d25></span><span class="text-xs font-medium text-[#ff5a00]" data-v-2d323d25>پنل مدیریت ایمن پیشگامان</span></div><div class="mt-8 flex items-center gap-4" data-v-2d323d25><span class="flex size-14 items-center justify-center rounded-xl bg-white shadow-[0_0_28px_rgba(10,30,92,0.45)]" data-v-2d323d25><img src="/logo.png" alt="ایمن پیشگامان" class="size-9 object-contain" data-v-2d323d25></span><div class="leading-tight" data-v-2d323d25><div class="text-2xl font-black tracking-tight md:text-3xl" data-v-2d323d25>ایمن پیشگامان</div><div class="mt-1 font-mono text-[10px] tracking-[0.32em] text-[#8e9ab0]" data-v-2d323d25> IMEN PISHGAMAN CO. </div></div></div><h1 class="mt-8 text-3xl font-black leading-[1.25] md:text-5xl md:leading-[1.2]" data-v-2d323d25> ورود امن <span class="mt-2 inline-block -rotate-1 rounded-lg bg-[#ffb800] px-3 py-1 text-[#0b1224] shadow-[0_10px_40px_rgba(255,184,0,0.22)]" data-v-2d323d25> به سامانه </span></h1><p class="mt-6 max-w-md text-sm leading-8 text-[#edede6]/70 md:text-base md:leading-9" data-v-2d323d25> مدیریت محصولات، محتوا و پیامهای سایت — فقط برای کاربران مجاز. </p></div>`,1)])])}}}),[[`__scopeId`,`data-v-2d323d25`]]);export{w as default};
|
import{c as e,n as t,t as n}from"./VuesaxIcon-UCFGkXBv.js";import{It as r,Lt as i,Nt as a,Tt as o,_t as s,a as c,gt as l,ht as u,kt as d,mt as f,o as p,pt as m,st as h,t as g,ut as _}from"./index-1U5l_fLl.js";import"./logo-BuUh917n.js";import{n as v,t as y}from"./FormField-DbkYgDsg.js";var b={class:`login-page relative flex min-h-screen overflow-hidden text-[#edede6]`},x={class:`relative z-10 mx-auto grid w-full max-w-[1120px] flex-1 items-center gap-10 px-5 py-12 md:px-8 lg:grid-cols-12 lg:gap-16`},S={class:`login-fade-3 lg:col-span-6`},C={class:`login-panel w-full max-w-md border border-[#2a3d63]/70 bg-[#1a2744]/80 p-7 backdrop-blur-md md:p-8 lg:me-auto`},w=t(s({__name:`LoginPage`,setup(t){let s=g(),w=p(),T=c(),{model:E,validate:D,handleError:O}=v(e,{phone:``,password:``}),k=r(!1);async function A(){let e=D();if(e){k.value=!0;try{await s.login(e);let t=typeof T.query.redirect==`string`?T.query.redirect:`/`;await w.replace(t.startsWith(`/`)?t:`/`)}catch(e){O(e)}finally{k.value=!1}}}return(e,t)=>{let r=d(`a-input`),s=d(`a-input-password`),c=d(`a-button`),p=d(`a-form`);return o(),m(`div`,b,[t[5]||=f(`<div class="pointer-events-none absolute inset-0 bg-[#0b1224]" data-v-2d323d25></div><div class="login-grid pointer-events-none absolute inset-0 opacity-50" data-v-2d323d25></div><div class="pointer-events-none absolute -start-24 top-1/4 size-[420px] rounded-full bg-[#ff5a00]/15 blur-[120px]" data-v-2d323d25></div><div class="pointer-events-none absolute -end-16 bottom-0 size-[360px] rounded-full bg-[#ffb800]/10 blur-[100px]" data-v-2d323d25></div><div class="login-tape pointer-events-none absolute bottom-0 end-0 z-10 h-48 w-1.5 opacity-70" data-v-2d323d25></div><div class="login-noise pointer-events-none absolute inset-0 z-[5]" data-v-2d323d25></div>`,6),_(`div`,x,[_(`div`,S,[_(`div`,C,[t[3]||=_(`div`,{class:`mb-7 text-center`},[_(`div`,{class:`font-mono text-[10px] tracking-[0.28em] text-[#8e9ab0]`},`SIGN IN`),_(`h2`,{class:`mt-2 text-xl font-bold text-[#edede6]`},`ورود به پنل`),_(`p`,{class:`mt-1.5 text-sm text-[#8e9ab0]`},`شماره موبایل و رمز عبور خود را وارد کنید`)],-1),l(p,{class:`login-form`,layout:`vertical`,onSubmit:h(A,[`prevent`])},{default:a(()=>[l(y,{path:`phone`,label:`شماره موبایل`},{default:a(()=>[l(r,{value:i(E).phone,"onUpdate:value":t[0]||=e=>i(E).phone=e,size:`large`,class:`ltr-input`,autocomplete:`tel`,autofocus:``,inputmode:`tel`,placeholder:`09121234567`},{prefix:a(()=>[l(n,{name:`mobile`,size:16,class:`text-[#8e9ab0]`})]),_:1},8,[`value`])]),_:1}),l(y,{path:`password`,label:`رمز عبور`},{default:a(()=>[l(s,{value:i(E).password,"onUpdate:value":t[1]||=e=>i(E).password=e,size:`large`,class:`ltr-input`,autocomplete:`current-password`,placeholder:`••••••••`},{prefix:a(()=>[l(n,{name:`lock`,size:16,class:`text-[#8e9ab0]`})]),_:1},8,[`value`])]),_:1}),l(c,{type:`primary`,"html-type":`submit`,size:`large`,block:``,loading:k.value,class:`!mt-2 !h-12 !rounded-full !font-bold !shadow-[0_8px_32px_rgba(255,90,0,0.35)]`},{default:a(()=>[...t[2]||=[u(` ورود به پنل `,-1)]]),_:1},8,[`loading`])]),_:1})])]),t[4]||=f(`<div class="login-fade-1 lg:col-span-6" data-v-2d323d25><div class="inline-flex items-center gap-2.5 rounded-full border border-[#ff5a00]/30 bg-[#ff5a00]/10 px-4 py-2 backdrop-blur" data-v-2d323d25><span class="size-1.5 animate-pulse rounded-full bg-[#ff5a00]" data-v-2d323d25></span><span class="text-xs font-medium text-[#ff5a00]" data-v-2d323d25>پنل مدیریت ایمن پیشگامان</span></div><div class="mt-8 flex items-center gap-4" data-v-2d323d25><span class="flex size-14 items-center justify-center rounded-xl bg-white shadow-[0_0_28px_rgba(10,30,92,0.45)]" data-v-2d323d25><img src="/logo.png" alt="ایمن پیشگامان" class="size-9 object-contain" data-v-2d323d25></span><div class="leading-tight" data-v-2d323d25><div class="text-2xl font-black tracking-tight md:text-3xl" data-v-2d323d25>ایمن پیشگامان</div><div class="mt-1 font-mono text-[10px] tracking-[0.32em] text-[#8e9ab0]" data-v-2d323d25> IMEN PISHGAMAN CO. </div></div></div><h1 class="mt-8 text-3xl font-black leading-[1.25] md:text-5xl md:leading-[1.2]" data-v-2d323d25> ورود امن <span class="mt-2 inline-block -rotate-1 rounded-lg bg-[#ffb800] px-3 py-1 text-[#0b1224] shadow-[0_10px_40px_rgba(255,184,0,0.22)]" data-v-2d323d25> به سامانه </span></h1><p class="mt-6 max-w-md text-sm leading-8 text-[#edede6]/70 md:text-base md:leading-9" data-v-2d323d25> مدیریت محصولات، محتوا و پیامهای سایت — فقط برای کاربران مجاز. </p></div>`,1)])])}}}),[[`__scopeId`,`data-v-2d323d25`]]);export{w as default};
|
||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
import{n as e,t}from"./VuesaxIcon-B2OgulS6.js";import{Bt as n,Nt as r,Ot as i,Tt as a,_t as o,dt as s,ft as c,gt as l,kt as u,pt as d,ut as f}from"./index-BuGK6ptM.js";var p={class:`page-header`},m={class:`page-header__main`},h={class:`min-w-0`},g={class:`page-header__title`},_={key:0,class:`page-header__subtitle`},v={key:0,class:`page-header__actions`},y=e(o({__name:`PageHeader`,props:{title:{},subtitle:{},back:{}},setup(e){return(o,y)=>{let b=u(`RouterLink`);return a(),d(`div`,p,[f(`div`,m,[e.back?(a(),s(b,{key:0,to:e.back,class:`page-header__back`,title:`بازگشت`},{default:r(()=>[l(t,{name:`arrow-right`,size:18})]),_:1},8,[`to`])):c(``,!0),f(`div`,h,[f(`h1`,g,n(e.title),1),e.subtitle?(a(),d(`p`,_,n(e.subtitle),1)):c(``,!0)])]),o.$slots.default?(a(),d(`div`,v,[i(o.$slots,`default`,{},void 0,!0)])):c(``,!0)])}}}),[[`__scopeId`,`data-v-c0b72af8`]]);export{y as t};
|
import{n as e,t}from"./VuesaxIcon-UCFGkXBv.js";import{Bt as n,Nt as r,Ot as i,Tt as a,_t as o,dt as s,ft as c,gt as l,kt as u,pt as d,ut as f}from"./index-1U5l_fLl.js";var p={class:`page-header`},m={class:`page-header__main`},h={class:`min-w-0`},g={class:`page-header__title`},_={key:0,class:`page-header__subtitle`},v={key:0,class:`page-header__actions`},y=e(o({__name:`PageHeader`,props:{title:{},subtitle:{},back:{}},setup(e){return(o,y)=>{let b=u(`RouterLink`);return a(),d(`div`,p,[f(`div`,m,[e.back?(a(),s(b,{key:0,to:e.back,class:`page-header__back`,title:`بازگشت`},{default:r(()=>[l(t,{name:`arrow-right`,size:18})]),_:1},8,[`to`])):c(``,!0),f(`div`,h,[f(`h1`,g,n(e.title),1),e.subtitle?(a(),d(`p`,_,n(e.subtitle),1)):c(``,!0)])]),o.$slots.default?(a(),d(`div`,v,[i(o.$slots,`default`,{},void 0,!0)])):c(``,!0)])}}}),[[`__scopeId`,`data-v-c0b72af8`]]);export{y as t};
|
||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
import{t as e,v as t}from"./VuesaxIcon-B2OgulS6.js";import{Bt as n,Ct as r,Dt as i,It as a,Lt as o,Mt as s,Nt as c,Rt as l,Tt as u,_t as d,a as f,bt as p,ct as m,dt as h,ft as g,gt as _,ht as v,i as y,jt as b,kt as x,lt as S,n as C,o as ee,ot as w,pt as T,st as te,ut as E}from"./index-BuGK6ptM.js";import{t as D}from"./format-B9MYDQJN.js";import{t as ne}from"./PageHeader-3Ryvg2HD.js";import{n as re,t as O}from"./FormField-CiVT7mqC.js";import{n as ie,r as ae,t as oe}from"./ListEditor-C8F91lSJ.js";import{t as se}from"./slug-Flyc3fOh.js";var ce=d({__name:`LinesInput`,props:p({placeholder:{},rows:{}},{modelValue:{required:!0},modelModifiers:{}}),emits:[`update:modelValue`],setup(e){let t=b(e,`modelValue`),n=e=>e.split(`
|
import{t as e,v as t}from"./VuesaxIcon-UCFGkXBv.js";import{Bt as n,Ct as r,Dt as i,It as a,Lt as o,Mt as s,Nt as c,Rt as l,Tt as u,_t as d,a as f,bt as p,ct as m,dt as h,ft as g,gt as _,ht as v,i as y,jt as b,kt as x,lt as S,n as C,o as ee,ot as w,pt as T,st as te,ut as E}from"./index-1U5l_fLl.js";import{t as D}from"./format-BwvWvVfG.js";import{t as ne}from"./PageHeader-C9oHYjmE.js";import{n as re,t as O}from"./FormField-DbkYgDsg.js";import{n as ie,r as ae,t as oe}from"./ListEditor-xwdh5hs-.js";import{t as se}from"./slug-Flyc3fOh.js";var ce=d({__name:`LinesInput`,props:p({placeholder:{},rows:{}},{modelValue:{required:!0},modelModifiers:{}}),emits:[`update:modelValue`],setup(e){let t=b(e,`modelValue`),n=e=>e.split(`
|
||||||
`).map(e=>e.trim()).filter(Boolean),r=a(t.value.join(`
|
`).map(e=>e.trim()).filter(Boolean),r=a(t.value.join(`
|
||||||
`));s(t,e=>{n(r.value).join(`
|
`));s(t,e=>{n(r.value).join(`
|
||||||
`)!==e.join(`
|
`)!==e.join(`
|
||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
import{o as e,s as t}from"./VuesaxIcon-B2OgulS6.js";import{Bt as n,It as r,Lt as i,Nt as a,Tt as o,_t as s,ct as c,gt as l,ht as u,kt as d,n as f,ot as p,pt as m,st as h,t as g,ut as _}from"./index-BuGK6ptM.js";import{t as v}from"./PageHeader-3Ryvg2HD.js";import{n as y,t as b}from"./FormField-CiVT7mqC.js";var x={class:`grid max-w-4xl gap-4 md:gap-6 md:grid-cols-2`},S={class:`flex items-center gap-3.5 mb-4 pb-4 border-b border-gray-100`},C={class:`flex size-14 items-center justify-center rounded-2xl bg-gradient-to-tr from-[#ff5a00] to-amber-500 text-xl font-black text-white shadow-md shadow-orange-500/20`},w={class:`text-base font-bold text-gray-900`},T={class:`mt-0.5 text-xs font-mono text-gray-500`,dir:`ltr`},E={dir:`ltr`,class:`font-mono`},D=s({__name:`ProfilePage`,setup(s){let D=g(),{model:O,validate:k,handleError:A,reset:j}=y(t,{currentPassword:``,newPassword:``}),M=r(!1);async function N(){let e=k();if(e){M.value=!0;try{await f(`/admin/auth/change-password`,{method:`POST`,body:e}),p.success(`رمز عبور تغییر کرد`),j({currentPassword:``,newPassword:``})}catch(e){A(e)}finally{M.value=!1}}}return(t,r)=>{let s=d(`a-descriptions-item`),f=d(`a-tag`),p=d(`a-descriptions`),g=d(`a-card`),y=d(`a-input-password`),k=d(`a-button`),A=d(`a-form`);return o(),m(c,null,[l(v,{title:`حساب کاربری`,subtitle:`مشخصات فردی، نقش دسترسی و تغییر رمز عبور ورود به سامانه`}),_(`div`,x,[l(g,{title:`مشخصات کاربری`},{default:a(()=>[_(`div`,S,[_(`div`,C,n(i(D).user?.name?.slice(0,1)||`U`),1),_(`div`,null,[_(`div`,w,n(i(D).user?.name),1),_(`div`,T,n(i(D).user?.phone),1)])]),l(p,{column:1,size:`small`},{default:a(()=>[l(s,{label:`نام`},{default:a(()=>[u(n(i(D).user?.name),1)]),_:1}),l(s,{label:`موبایل`},{default:a(()=>[_(`span`,E,n(i(D).user?.phone),1)]),_:1}),l(s,{label:`نقش دسترسی`},{default:a(()=>[l(f,{color:i(D).user?.role===`ADMIN`?`orange`:`blue`},{default:a(()=>[u(n(i(D).user&&i(e)[i(D).user.role]),1)]),_:1},8,[`color`])]),_:1})]),_:1})]),_:1}),l(g,{title:`تغییر رمز عبور`},{default:a(()=>[l(A,{layout:`vertical`,onSubmit:h(N,[`prevent`])},{default:a(()=>[l(b,{path:`currentPassword`,label:`رمز فعلی`,required:``},{default:a(()=>[l(y,{value:i(O).currentPassword,"onUpdate:value":r[0]||=e=>i(O).currentPassword=e,class:`ltr-input h-10`,autocomplete:`current-password`},null,8,[`value`])]),_:1}),l(b,{path:`newPassword`,label:`رمز جدید`,hint:`حداقل ۸ کاراکتر`,required:``},{default:a(()=>[l(y,{value:i(O).newPassword,"onUpdate:value":r[1]||=e=>i(O).newPassword=e,class:`ltr-input h-10`,autocomplete:`new-password`},null,8,[`value`])]),_:1}),l(k,{type:`primary`,"html-type":`submit`,loading:M.value,class:`w-full md:w-auto h-10 !font-bold`},{default:a(()=>[...r[2]||=[u(` تغییر رمز ورود `,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1})])],64)}}});export{D as default};
|
import{o as e,s as t}from"./VuesaxIcon-UCFGkXBv.js";import{Bt as n,It as r,Lt as i,Nt as a,Tt as o,_t as s,ct as c,gt as l,ht as u,kt as d,n as f,ot as p,pt as m,st as h,t as g,ut as _}from"./index-1U5l_fLl.js";import{t as v}from"./PageHeader-C9oHYjmE.js";import{n as y,t as b}from"./FormField-DbkYgDsg.js";var x={class:`grid max-w-4xl gap-4 md:gap-6 md:grid-cols-2`},S={class:`flex items-center gap-3.5 mb-4 pb-4 border-b border-gray-100`},C={class:`flex size-14 items-center justify-center rounded-2xl bg-gradient-to-tr from-[#ff5a00] to-amber-500 text-xl font-black text-white shadow-md shadow-orange-500/20`},w={class:`text-base font-bold text-gray-900`},T={class:`mt-0.5 text-xs font-mono text-gray-500`,dir:`ltr`},E={dir:`ltr`,class:`font-mono`},D=s({__name:`ProfilePage`,setup(s){let D=g(),{model:O,validate:k,handleError:A,reset:j}=y(t,{currentPassword:``,newPassword:``}),M=r(!1);async function N(){let e=k();if(e){M.value=!0;try{await f(`/admin/auth/change-password`,{method:`POST`,body:e}),p.success(`رمز عبور تغییر کرد`),j({currentPassword:``,newPassword:``})}catch(e){A(e)}finally{M.value=!1}}}return(t,r)=>{let s=d(`a-descriptions-item`),f=d(`a-tag`),p=d(`a-descriptions`),g=d(`a-card`),y=d(`a-input-password`),k=d(`a-button`),A=d(`a-form`);return o(),m(c,null,[l(v,{title:`حساب کاربری`,subtitle:`مشخصات فردی، نقش دسترسی و تغییر رمز عبور ورود به سامانه`}),_(`div`,x,[l(g,{title:`مشخصات کاربری`},{default:a(()=>[_(`div`,S,[_(`div`,C,n(i(D).user?.name?.slice(0,1)||`U`),1),_(`div`,null,[_(`div`,w,n(i(D).user?.name),1),_(`div`,T,n(i(D).user?.phone),1)])]),l(p,{column:1,size:`small`},{default:a(()=>[l(s,{label:`نام`},{default:a(()=>[u(n(i(D).user?.name),1)]),_:1}),l(s,{label:`موبایل`},{default:a(()=>[_(`span`,E,n(i(D).user?.phone),1)]),_:1}),l(s,{label:`نقش دسترسی`},{default:a(()=>[l(f,{color:i(D).user?.role===`ADMIN`?`orange`:`blue`},{default:a(()=>[u(n(i(D).user&&i(e)[i(D).user.role]),1)]),_:1},8,[`color`])]),_:1})]),_:1})]),_:1}),l(g,{title:`تغییر رمز عبور`},{default:a(()=>[l(A,{layout:`vertical`,onSubmit:h(N,[`prevent`])},{default:a(()=>[l(b,{path:`currentPassword`,label:`رمز فعلی`,required:``},{default:a(()=>[l(y,{value:i(O).currentPassword,"onUpdate:value":r[0]||=e=>i(O).currentPassword=e,class:`ltr-input h-10`,autocomplete:`current-password`},null,8,[`value`])]),_:1}),l(b,{path:`newPassword`,label:`رمز جدید`,hint:`حداقل ۸ کاراکتر`,required:``},{default:a(()=>[l(y,{value:i(O).newPassword,"onUpdate:value":r[1]||=e=>i(O).newPassword=e,class:`ltr-input h-10`,autocomplete:`new-password`},null,8,[`value`])]),_:1}),l(k,{type:`primary`,"html-type":`submit`,loading:M.value,class:`w-full md:w-auto h-10 !font-bold`},{default:a(()=>[...r[2]||=[u(` تغییر رمز ورود `,-1)]]),_:1},8,[`loading`])]),_:1})]),_:1})])],64)}}});export{D as default};
|
||||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -1 +1 @@
|
|||||||
import{d as e,f as t}from"./VuesaxIcon-B2OgulS6.js";import{Bt as n,Ct as r,Dt as i,Ft as a,It as o,Lt as s,Nt as c,Rt as l,Tt as u,_t as d,ct as f,dt as p,ft as m,gt as h,ht as g,i as _,kt as v,n as y,o as b,ot as x,pt as S,t as C,ut as w}from"./index-BuGK6ptM.js";import{a as T}from"./format-B9MYDQJN.js";import{t as E}from"./PageHeader-3Ryvg2HD.js";var D={class:`grid gap-4 sm:grid-cols-2 xl:grid-cols-3`},O={class:`flex items-center justify-between gap-2`},k=[`onClick`],A={class:`font-bold text-gray-800 group-hover:text-orange-600 transition-colors`},j={key:0,class:`flex items-center flex-shrink-0`},M=[`onClick`],N=[`onClick`],P={key:0},F=d({__name:`SectionsPage`,setup(d){let F=b(),I=C(),L=e.filter(e=>e!==`settings`||I.isAdmin),R=o(!0),z=a({hero:!0,tape:!0,stats:!0,productsIntro:!0,about:!0,services:!0,standards:!0,industries:!0,testimonials:!0,contact:!0,footer:!0}),B=a({}),V=a({});function H(e){return z[e]!==!1}function U(e){return B[e]??null}async function W(){R.value=!0;try{let e=await y(`/admin/sections`);for(let t of e)z[t.key]=t.enabled!==!1,B[t.key]=t.updatedAt}catch(e){x.error(_(e))}finally{R.value=!1}}async function G(e,n){if(e===`settings`)return;let r=z[e]!==!1;z[e]=n,V[e]=!0;try{let r=await y(`/admin/sections/${e}/visibility`,{method:`PATCH`,body:{enabled:n}});z[e]=r.enabled!==!1,B[e]=r.updatedAt,x.success(`وضعیت «${t[e].title}» به ${n?`نمایش`:`عدم نمایش`} تغییر یافت`)}catch(t){z[e]=r,x.error(_(t))}finally{V[e]=!1}}function K(e){F.push({name:`section-edit`,params:{key:e}})}return r(()=>{W()}),(e,r)=>{let a=v(`a-tag`),o=v(`a-switch`),d=v(`a-card`),_=v(`a-spin`);return u(),S(f,null,[h(E,{title:`بخشهای سایت`,subtitle:`متنها، تصاویر و فهرستهای صفحه اصلی؛ تنظیم وضعیت نمایش و ویرایش محتوا`}),h(_,{spinning:R.value},{default:c(()=>[w(`div`,D,[(u(!0),S(f,null,i(s(L),e=>(u(),S(`div`,{key:e,class:`block group`},[h(d,{hoverable:``,class:l([`h-full flex flex-col justify-between transition-all`,{"opacity-75 bg-gray-50/60":e!==`settings`&&!H(e)}])},{default:c(()=>[w(`div`,null,[w(`div`,O,[w(`div`,{class:`flex items-center gap-2 cursor-pointer flex-1`,onClick:t=>K(e)},[w(`span`,A,n(s(t)[e].title),1),e===`settings`?(u(),p(a,{key:0,color:`blue`},{default:c(()=>[...r[0]||=[g(`تنظیمات پایه`,-1)]]),_:1})):H(e)?m(``,!0):(u(),p(a,{key:1,color:`error`},{default:c(()=>[...r[1]||=[g(`مخفی`,-1)]]),_:1}))],8,k),e===`settings`?m(``,!0):(u(),S(`div`,j,[h(o,{checked:H(e),loading:V[e],"checked-children":`نمایش`,"un-checked-children":`مخفی`,onChange:t=>G(e,!!t)},null,8,[`checked`,`loading`,`onChange`])]))]),w(`p`,{class:`mt-2 text-sm leading-6 text-gray-500 cursor-pointer`,onClick:t=>K(e)},n(s(t)[e].desc),9,M)]),w(`div`,{class:`mt-4 flex items-center justify-between border-t border-gray-100 pt-3 text-xs text-gray-400 cursor-pointer`,onClick:t=>K(e)},[r[2]||=w(`span`,{class:`text-primary font-medium group-hover:underline`},`ویرایش بخش ←`,-1),U(e)?(u(),S(`span`,P,`آخرین تغییر: `+n(s(T)(U(e))),1)):m(``,!0)],8,N)]),_:2},1032,[`class`])]))),128))])]),_:1},8,[`spinning`])],64)}}});export{F as default};
|
import{d as e,f as t}from"./VuesaxIcon-UCFGkXBv.js";import{Bt as n,Ct as r,Dt as i,Ft as a,It as o,Lt as s,Nt as c,Rt as l,Tt as u,_t as d,ct as f,dt as p,ft as m,gt as h,ht as g,i as _,kt as v,n as y,o as b,ot as x,pt as S,t as C,ut as w}from"./index-1U5l_fLl.js";import{a as T}from"./format-BwvWvVfG.js";import{t as E}from"./PageHeader-C9oHYjmE.js";var D={class:`grid gap-4 sm:grid-cols-2 xl:grid-cols-3`},O={class:`flex items-center justify-between gap-2`},k=[`onClick`],A={class:`font-bold text-gray-800 group-hover:text-orange-600 transition-colors`},j={key:0,class:`flex items-center flex-shrink-0`},M=[`onClick`],N=[`onClick`],P={key:0},F=d({__name:`SectionsPage`,setup(d){let F=b(),I=C(),L=e.filter(e=>e!==`settings`||I.isAdmin),R=o(!0),z=a({hero:!0,tape:!0,stats:!0,productsIntro:!0,about:!0,services:!0,standards:!0,industries:!0,testimonials:!0,contact:!0,footer:!0}),B=a({}),V=a({});function H(e){return z[e]!==!1}function U(e){return B[e]??null}async function W(){R.value=!0;try{let e=await y(`/admin/sections`);for(let t of e)z[t.key]=t.enabled!==!1,B[t.key]=t.updatedAt}catch(e){x.error(_(e))}finally{R.value=!1}}async function G(e,n){if(e===`settings`)return;let r=z[e]!==!1;z[e]=n,V[e]=!0;try{let r=await y(`/admin/sections/${e}/visibility`,{method:`PATCH`,body:{enabled:n}});z[e]=r.enabled!==!1,B[e]=r.updatedAt,x.success(`وضعیت «${t[e].title}» به ${n?`نمایش`:`عدم نمایش`} تغییر یافت`)}catch(t){z[e]=r,x.error(_(t))}finally{V[e]=!1}}function K(e){F.push({name:`section-edit`,params:{key:e}})}return r(()=>{W()}),(e,r)=>{let a=v(`a-tag`),o=v(`a-switch`),d=v(`a-card`),_=v(`a-spin`);return u(),S(f,null,[h(E,{title:`بخشهای سایت`,subtitle:`متنها، تصاویر و فهرستهای صفحه اصلی؛ تنظیم وضعیت نمایش و ویرایش محتوا`}),h(_,{spinning:R.value},{default:c(()=>[w(`div`,D,[(u(!0),S(f,null,i(s(L),e=>(u(),S(`div`,{key:e,class:`block group`},[h(d,{hoverable:``,class:l([`h-full flex flex-col justify-between transition-all`,{"opacity-75 bg-gray-50/60":e!==`settings`&&!H(e)}])},{default:c(()=>[w(`div`,null,[w(`div`,O,[w(`div`,{class:`flex items-center gap-2 cursor-pointer flex-1`,onClick:t=>K(e)},[w(`span`,A,n(s(t)[e].title),1),e===`settings`?(u(),p(a,{key:0,color:`blue`},{default:c(()=>[...r[0]||=[g(`تنظیمات پایه`,-1)]]),_:1})):H(e)?m(``,!0):(u(),p(a,{key:1,color:`error`},{default:c(()=>[...r[1]||=[g(`مخفی`,-1)]]),_:1}))],8,k),e===`settings`?m(``,!0):(u(),S(`div`,j,[h(o,{checked:H(e),loading:V[e],"checked-children":`نمایش`,"un-checked-children":`مخفی`,onChange:t=>G(e,!!t)},null,8,[`checked`,`loading`,`onChange`])]))]),w(`p`,{class:`mt-2 text-sm leading-6 text-gray-500 cursor-pointer`,onClick:t=>K(e)},n(s(t)[e].desc),9,M)]),w(`div`,{class:`mt-4 flex items-center justify-between border-t border-gray-100 pt-3 text-xs text-gray-400 cursor-pointer`,onClick:t=>K(e)},[r[2]||=w(`span`,{class:`text-primary font-medium group-hover:underline`},`ویرایش بخش ←`,-1),U(e)?(u(),S(`span`,P,`آخرین تغییر: `+n(s(T)(U(e))),1)):m(``,!0)],8,N)]),_:2},1032,[`class`])]))),128))])]),_:1},8,[`spinning`])],64)}}});export{F as default};
|
||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
-1
@@ -1 +0,0 @@
|
|||||||
var e=new Intl.DateTimeFormat(`fa-IR-u-ca-persian`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`}),t=t=>t?e.format(new Date(t)):`—`,n=e=>e<1048576?`${(e/1024).toLocaleString(`fa-IR`,{maximumFractionDigits:0})} کیلوبایت`:`${(e/1024/1024).toLocaleString(`fa-IR`,{maximumFractionDigits:1})} مگابایت`,r=e=>e.toLocaleString(`fa-IR`),i=`https://imenpishgaman.ir`,a={create:`ایجاد`,update:`ویرایش`,delete:`حذف`,upload:`آپلود`,login:`ورود`,publish:`انتشار`,unpublish:`عدم انتشار`,duplicate:`کپی`,reorder:`مرتبسازی`,"change-password":`تغییر رمز`},o={product:`محصول`,category:`دسته`,section:`بخش سایت`,media:`رسانه`,user:`کاربر`,message:`پیام`},s=(e,t)=>`${a[e]??e} ${o[t]??t}`;export{t as a,n as i,s as n,r,i as t};
|
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
var e=new Intl.DateTimeFormat(`fa-IR-u-ca-persian`,{year:`numeric`,month:`2-digit`,day:`2-digit`,hour:`2-digit`,minute:`2-digit`}),t=t=>t?e.format(new Date(t)):`—`,n=e=>e<1048576?`${(e/1024).toLocaleString(`fa-IR`,{maximumFractionDigits:0})} کیلوبایت`:`${(e/1024/1024).toLocaleString(`fa-IR`,{maximumFractionDigits:1})} مگابایت`,r=e=>e.toLocaleString(`fa-IR`),i=`http://imenpishgaman.ir`,a={create:`ایجاد`,update:`ویرایش`,delete:`حذف`,upload:`آپلود`,login:`ورود`,publish:`انتشار`,unpublish:`عدم انتشار`,duplicate:`کپی`,reorder:`مرتبسازی`,"change-password":`تغییر رمز`},o={product:`محصول`,category:`دسته`,section:`بخش سایت`,media:`رسانه`,user:`کاربر`,message:`پیام`},s=(e,t)=>`${a[e]??e} ${o[t]??t}`;export{t as a,n as i,s as n,r,i as t};
|
||||||
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
@@ -6,7 +6,7 @@
|
|||||||
<meta name="robots" content="noindex, nofollow" />
|
<meta name="robots" content="noindex, nofollow" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
<title>پنل مدیریت ایمن پیشگامان</title>
|
<title>پنل مدیریت ایمن پیشگامان</title>
|
||||||
<script type="module" crossorigin src="/assets/index-BuGK6ptM.js"></script>
|
<script type="module" crossorigin src="/assets/index-1U5l_fLl.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-DjFpBvci.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-DjFpBvci.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import type { SectionData } from "@imen/shared";
|
||||||
|
import FormField from "@/components/FormField.vue";
|
||||||
|
|
||||||
|
/** Shared address / phone / email / hours fields from the settings section. */
|
||||||
|
const data = defineModel<SectionData<"settings">>({ required: true });
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
/** Zod path prefix when nested under another form context */
|
||||||
|
pathPrefix?: string;
|
||||||
|
}>(),
|
||||||
|
{ pathPrefix: "" },
|
||||||
|
);
|
||||||
|
|
||||||
|
function path(field: string) {
|
||||||
|
return props.pathPrefix ? `${props.pathPrefix}.${field}` : field;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="grid gap-x-4 md:grid-cols-2">
|
||||||
|
<FormField :path="path('phoneDisplay')" label="تلفن (نمایشی)" required>
|
||||||
|
<a-input v-model:value="data.phoneDisplay" />
|
||||||
|
</FormField>
|
||||||
|
<FormField :path="path('phone')" label="تلفن (برای لینک و گوگل)" hint="با کد کشور، مثلاً +98-21-91008800" required>
|
||||||
|
<a-input v-model:value="data.phone" class="ltr-input" />
|
||||||
|
</FormField>
|
||||||
|
<FormField :path="path('email')" label="ایمیل" required class="md:col-span-2">
|
||||||
|
<a-input v-model:value="data.email" class="ltr-input" />
|
||||||
|
</FormField>
|
||||||
|
<FormField :path="path('address.locality')" label="شهر" required>
|
||||||
|
<a-input v-model:value="data.address.locality" />
|
||||||
|
</FormField>
|
||||||
|
<FormField :path="path('address.country')" label="کد کشور" required>
|
||||||
|
<a-input v-model:value="data.address.country" class="ltr-input" :maxlength="2" />
|
||||||
|
</FormField>
|
||||||
|
<FormField :path="path('address.street')" label="نشانی" required class="md:col-span-2">
|
||||||
|
<a-input v-model:value="data.address.street" />
|
||||||
|
</FormField>
|
||||||
|
<FormField
|
||||||
|
:path="path('mapUrl')"
|
||||||
|
label="لینک نقشه"
|
||||||
|
hint="اختیاری — لینک گوگلمپ، نشان و …"
|
||||||
|
class="md:col-span-2"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="data.mapUrl" class="ltr-input" placeholder="https://maps.google.com/..." />
|
||||||
|
</FormField>
|
||||||
|
<FormField :path="path('workingHours')" label="ساعات کاری (متن نمایشی)" required class="md:col-span-2">
|
||||||
|
<a-input v-model:value="data.workingHours" />
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -107,9 +107,9 @@ async function submit() {
|
|||||||
|
|
||||||
<div class="mt-8 flex items-center gap-4">
|
<div class="mt-8 flex items-center gap-4">
|
||||||
<span
|
<span
|
||||||
class="flex size-14 items-center justify-center rounded-xl bg-white shadow-[0_0_28px_rgba(10,30,92,0.45)]"
|
class="flex size-20 items-center justify-center overflow-hidden rounded-2xl bg-white shadow-[0_0_28px_rgba(10,30,92,0.45)]"
|
||||||
>
|
>
|
||||||
<img src="/logo.png" alt="ایمن پیشگامان" class="size-9 object-contain" />
|
<img src="/logo.png" alt="ایمن پیشگامان" class="size-full scale-110 object-contain object-center" />
|
||||||
</span>
|
</span>
|
||||||
<div class="leading-tight">
|
<div class="leading-tight">
|
||||||
<div class="text-2xl font-black tracking-tight md:text-3xl">ایمن پیشگامان</div>
|
<div class="text-2xl font-black tracking-tight md:text-3xl">ایمن پیشگامان</div>
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { isSectionKey, SECTION_LABELS, type SectionKey, sectionSchemas } from "@imen/shared";
|
import {
|
||||||
|
isSectionKey,
|
||||||
|
SECTION_LABELS,
|
||||||
|
type SectionData,
|
||||||
|
type SectionKey,
|
||||||
|
sectionSchemas,
|
||||||
|
settingsSectionSchema,
|
||||||
|
} from "@imen/shared";
|
||||||
import { message } from "ant-design-vue";
|
import { message } from "ant-design-vue";
|
||||||
import { type Component, computed, ref } from "vue";
|
import { type Component, computed, ref } from "vue";
|
||||||
import { useRoute } from "vue-router";
|
import { useRoute } from "vue-router";
|
||||||
@@ -8,7 +15,6 @@ import VuesaxIcon from "@/components/VuesaxIcon.vue";
|
|||||||
import type { SectionResponse } from "@/api/types";
|
import type { SectionResponse } from "@/api/types";
|
||||||
import PageHeader from "@/components/PageHeader.vue";
|
import PageHeader from "@/components/PageHeader.vue";
|
||||||
import { useZodForm } from "@/composables/useZodForm";
|
import { useZodForm } from "@/composables/useZodForm";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
|
||||||
import { jalali, SITE_URL } from "@/utils/format";
|
import { jalali, SITE_URL } from "@/utils/format";
|
||||||
import AboutForm from "@/sections/AboutForm.vue";
|
import AboutForm from "@/sections/AboutForm.vue";
|
||||||
import ContactForm from "@/sections/ContactForm.vue";
|
import ContactForm from "@/sections/ContactForm.vue";
|
||||||
@@ -38,7 +44,6 @@ const FORMS: Record<SectionKey, Component> = {
|
|||||||
footer: FooterForm,
|
footer: FooterForm,
|
||||||
};
|
};
|
||||||
|
|
||||||
const auth = useAuthStore();
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const key = String(route.params.key);
|
const key = String(route.params.key);
|
||||||
const valid = isSectionKey(key);
|
const valid = isSectionKey(key);
|
||||||
@@ -49,6 +54,14 @@ const { model, validate, handleError, reset } = useZodForm(
|
|||||||
sectionSchemas[sectionKey] as (typeof sectionSchemas)["hero"],
|
sectionSchemas[sectionKey] as (typeof sectionSchemas)["hero"],
|
||||||
{} as never,
|
{} as never,
|
||||||
);
|
);
|
||||||
|
/** Contact page also edits site-wide contact info from settings */
|
||||||
|
const settingsModel = ref<SectionData<"settings"> | null>(null);
|
||||||
|
const contactModel = computed({
|
||||||
|
get: () => model.value as unknown as SectionData<"contact">,
|
||||||
|
set: (v: SectionData<"contact">) => {
|
||||||
|
model.value = v as never;
|
||||||
|
},
|
||||||
|
});
|
||||||
const loaded = ref(false);
|
const loaded = ref(false);
|
||||||
const saving = ref(false);
|
const saving = ref(false);
|
||||||
const updatedAt = ref<string | null>(null);
|
const updatedAt = ref<string | null>(null);
|
||||||
@@ -57,12 +70,20 @@ const label = computed(() => (valid ? SECTION_LABELS[sectionKey] : null));
|
|||||||
async function load() {
|
async function load() {
|
||||||
try {
|
try {
|
||||||
const res = await api<SectionResponse>(`/admin/sections/${sectionKey}`);
|
const res = await api<SectionResponse>(`/admin/sections/${sectionKey}`);
|
||||||
const data = res.data as any;
|
const data = res.data as Record<string, unknown>;
|
||||||
if (sectionKey !== "settings" && data.enabled === undefined) {
|
if (sectionKey !== "settings" && data.enabled === undefined) {
|
||||||
data.enabled = true;
|
data.enabled = true;
|
||||||
}
|
}
|
||||||
reset(data as never);
|
reset(data as never);
|
||||||
updatedAt.value = res.updatedAt;
|
updatedAt.value = res.updatedAt;
|
||||||
|
|
||||||
|
if (sectionKey === "contact") {
|
||||||
|
const settingsRes = await api<SectionResponse>("/admin/sections/settings");
|
||||||
|
settingsModel.value = settingsSectionSchema.parse(settingsRes.data);
|
||||||
|
} else {
|
||||||
|
settingsModel.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
loaded.value = true;
|
loaded.value = true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
void message.error(errorMessage(e));
|
void message.error(errorMessage(e));
|
||||||
@@ -72,8 +93,25 @@ async function load() {
|
|||||||
async function save() {
|
async function save() {
|
||||||
const data = validate();
|
const data = validate();
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
|
|
||||||
|
if (sectionKey === "contact" && settingsModel.value) {
|
||||||
|
const settingsParsed = settingsSectionSchema.safeParse(settingsModel.value);
|
||||||
|
if (!settingsParsed.success) {
|
||||||
|
void message.error("اطلاعات تماس معتبر نیست — نشانی، تلفن یا ایمیل را بررسی کنید");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
saving.value = true;
|
saving.value = true;
|
||||||
try {
|
try {
|
||||||
|
if (sectionKey === "contact" && settingsModel.value) {
|
||||||
|
const settingsRes = await api<SectionResponse>("/admin/sections/settings", {
|
||||||
|
method: "PUT",
|
||||||
|
body: settingsSectionSchema.parse(settingsModel.value),
|
||||||
|
});
|
||||||
|
settingsModel.value = settingsSectionSchema.parse(settingsRes.data);
|
||||||
|
}
|
||||||
|
|
||||||
const res = await api<SectionResponse>(`/admin/sections/${sectionKey}`, { method: "PUT", body: data });
|
const res = await api<SectionResponse>(`/admin/sections/${sectionKey}`, { method: "PUT", body: data });
|
||||||
reset(res.data as never);
|
reset(res.data as never);
|
||||||
updatedAt.value = res.updatedAt;
|
updatedAt.value = res.updatedAt;
|
||||||
@@ -87,9 +125,9 @@ async function save() {
|
|||||||
|
|
||||||
const togglingVisibility = ref(false);
|
const togglingVisibility = ref(false);
|
||||||
|
|
||||||
async function onVisibilityChange(val: any) {
|
async function onVisibilityChange(val: unknown) {
|
||||||
const nextVal = Boolean(val);
|
const nextVal = Boolean(val);
|
||||||
(model.value as any).enabled = nextVal;
|
(model.value as { enabled?: boolean }).enabled = nextVal;
|
||||||
togglingVisibility.value = true;
|
togglingVisibility.value = true;
|
||||||
try {
|
try {
|
||||||
const res = await api<{ key: string; enabled: boolean; updatedAt: string }>(
|
const res = await api<{ key: string; enabled: boolean; updatedAt: string }>(
|
||||||
@@ -99,13 +137,13 @@ async function onVisibilityChange(val: any) {
|
|||||||
body: { enabled: nextVal },
|
body: { enabled: nextVal },
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
(model.value as any).enabled = res.enabled !== false;
|
(model.value as { enabled?: boolean }).enabled = res.enabled !== false;
|
||||||
updatedAt.value = res.updatedAt;
|
updatedAt.value = res.updatedAt;
|
||||||
void message.success(
|
void message.success(
|
||||||
`وضعیت نمایش به ${nextVal ? "نمایش" : "عدم نمایش"} تغییر یافت`,
|
`وضعیت نمایش به ${nextVal ? "نمایش" : "عدم نمایش"} تغییر یافت`,
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
(model.value as any).enabled = !nextVal;
|
(model.value as { enabled?: boolean }).enabled = !nextVal;
|
||||||
void message.error(errorMessage(e));
|
void message.error(errorMessage(e));
|
||||||
} finally {
|
} finally {
|
||||||
togglingVisibility.value = false;
|
togglingVisibility.value = false;
|
||||||
@@ -126,7 +164,7 @@ if (valid) void load();
|
|||||||
<div v-if="sectionKey !== 'settings'" class="max-lg:hidden flex items-center gap-2 me-2">
|
<div v-if="sectionKey !== 'settings'" class="max-lg:hidden flex items-center gap-2 me-2">
|
||||||
<span class="text-sm font-medium text-gray-700">نمایش در سایت:</span>
|
<span class="text-sm font-medium text-gray-700">نمایش در سایت:</span>
|
||||||
<a-switch
|
<a-switch
|
||||||
:checked="(model as any)?.enabled !== false"
|
:checked="(model as { enabled?: boolean })?.enabled !== false"
|
||||||
:loading="togglingVisibility"
|
:loading="togglingVisibility"
|
||||||
checked-children="نمایش"
|
checked-children="نمایش"
|
||||||
un-checked-children="مخفی"
|
un-checked-children="مخفی"
|
||||||
@@ -138,7 +176,7 @@ if (valid) void load();
|
|||||||
type="primary"
|
type="primary"
|
||||||
class="max-lg:hidden"
|
class="max-lg:hidden"
|
||||||
:loading="saving"
|
:loading="saving"
|
||||||
:disabled="!loaded || (sectionKey === 'settings' && !auth.isAdmin)"
|
:disabled="!loaded"
|
||||||
@click="save"
|
@click="save"
|
||||||
>
|
>
|
||||||
<template #icon><VuesaxIcon name="save-2" :size="16" /></template>
|
<template #icon><VuesaxIcon name="save-2" :size="16" /></template>
|
||||||
@@ -147,21 +185,19 @@ if (valid) void load();
|
|||||||
</PageHeader>
|
</PageHeader>
|
||||||
<a-spin :spinning="!loaded">
|
<a-spin :spinning="!loaded">
|
||||||
<a-alert
|
<a-alert
|
||||||
v-if="loaded && sectionKey === 'settings' && !auth.isAdmin"
|
v-if="loaded && sectionKey !== 'settings' && (model as { enabled?: boolean }).enabled === false"
|
||||||
type="error"
|
|
||||||
show-icon
|
|
||||||
message="فقط مدیر کل میتواند تنظیمات سایت را تغییر دهد."
|
|
||||||
class="mb-4"
|
|
||||||
/>
|
|
||||||
<a-alert
|
|
||||||
v-else-if="loaded && sectionKey !== 'settings' && (model as any).enabled === false"
|
|
||||||
type="warning"
|
type="warning"
|
||||||
show-icon
|
show-icon
|
||||||
message="این بخش در حال حاضر در سایت نمایش داده نمیشود (مخفی است)."
|
message="این بخش در حال حاضر در سایت نمایش داده نمیشود (مخفی است)."
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
/>
|
/>
|
||||||
<a-form v-if="loaded" layout="vertical" class="pb-20 lg:pb-0" @submit.prevent="save">
|
<a-form v-if="loaded" layout="vertical" class="pb-20 lg:pb-0" @submit.prevent="save">
|
||||||
<component :is="FORMS[sectionKey]" v-model="model" />
|
<ContactForm
|
||||||
|
v-if="sectionKey === 'contact'"
|
||||||
|
v-model="contactModel"
|
||||||
|
v-model:settings="settingsModel"
|
||||||
|
/>
|
||||||
|
<component v-else :is="FORMS[sectionKey]" v-model="model" />
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-spin>
|
</a-spin>
|
||||||
|
|
||||||
@@ -169,13 +205,13 @@ if (valid) void load();
|
|||||||
<div class="lg:hidden fixed bottom-0 inset-x-0 z-30 border-t border-gray-200 bg-white/95 px-4 py-2.5 backdrop-blur-md shadow-lg flex items-center justify-between gap-3">
|
<div class="lg:hidden fixed bottom-0 inset-x-0 z-30 border-t border-gray-200 bg-white/95 px-4 py-2.5 backdrop-blur-md shadow-lg flex items-center justify-between gap-3">
|
||||||
<div v-if="sectionKey !== 'settings'" class="flex items-center gap-2">
|
<div v-if="sectionKey !== 'settings'" class="flex items-center gap-2">
|
||||||
<a-switch
|
<a-switch
|
||||||
:checked="(model as any)?.enabled !== false"
|
:checked="(model as { enabled?: boolean })?.enabled !== false"
|
||||||
:loading="togglingVisibility"
|
:loading="togglingVisibility"
|
||||||
size="small"
|
size="small"
|
||||||
@change="onVisibilityChange"
|
@change="onVisibilityChange"
|
||||||
/>
|
/>
|
||||||
<span class="text-xs font-medium text-gray-700">
|
<span class="text-xs font-medium text-gray-700">
|
||||||
{{ (model as any)?.enabled !== false ? 'نمایش در سایت' : 'مخفی' }}
|
{{ (model as { enabled?: boolean })?.enabled !== false ? 'نمایش در سایت' : 'مخفی' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="text-xs text-gray-500 font-medium">تنظیمات پایه</div>
|
<div v-else class="text-xs text-gray-500 font-medium">تنظیمات پایه</div>
|
||||||
@@ -191,7 +227,7 @@ if (valid) void load();
|
|||||||
size="middle"
|
size="middle"
|
||||||
class="!font-bold !px-5"
|
class="!font-bold !px-5"
|
||||||
:loading="saving"
|
:loading="saving"
|
||||||
:disabled="!loaded || (sectionKey === 'settings' && !auth.isAdmin)"
|
:disabled="!loaded"
|
||||||
@click="save"
|
@click="save"
|
||||||
>
|
>
|
||||||
<template #icon><VuesaxIcon name="save-2" :size="16" /></template>
|
<template #icon><VuesaxIcon name="save-2" :size="16" /></template>
|
||||||
|
|||||||
@@ -6,13 +6,10 @@ import { useRouter } from "vue-router";
|
|||||||
import { api, errorMessage } from "@/api/http";
|
import { api, errorMessage } from "@/api/http";
|
||||||
import type { AdminSectionSummary } from "@/api/types";
|
import type { AdminSectionSummary } from "@/api/types";
|
||||||
import PageHeader from "@/components/PageHeader.vue";
|
import PageHeader from "@/components/PageHeader.vue";
|
||||||
import { useAuthStore } from "@/stores/auth";
|
|
||||||
import { jalali } from "@/utils/format";
|
import { jalali } from "@/utils/format";
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const auth = useAuthStore();
|
const keys = SECTION_KEYS;
|
||||||
// site-wide settings are editable by admins only
|
|
||||||
const keys = SECTION_KEYS.filter((k) => k !== "settings" || auth.isAdmin);
|
|
||||||
|
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const sectionEnabled = reactive<Record<string, boolean>>({
|
const sectionEnabled = reactive<Record<string, boolean>>({
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ onMounted(load);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<PageHeader title="کاربران پنل" subtitle="مدیر کل به همه بخشها دسترسی دارد؛ ویرایشگر به جز کاربران و تنظیمات سایت">
|
<PageHeader title="کاربران پنل" subtitle="مدیر کل به همه بخشها دسترسی دارد؛ ویرایشگر به جز مدیریت کاربران و حذف دادهها">
|
||||||
<a-button type="primary" @click="openNew">
|
<a-button type="primary" @click="openNew">
|
||||||
<template #icon><VuesaxIcon name="add" :size="16" /></template>
|
<template #icon><VuesaxIcon name="add" :size="16" /></template>
|
||||||
کاربر جدید
|
کاربر جدید
|
||||||
@@ -241,7 +241,6 @@ onMounted(load);
|
|||||||
</div>
|
</div>
|
||||||
<ul class="mt-2 space-y-1 text-[11px] text-gray-600 list-disc list-inside">
|
<ul class="mt-2 space-y-1 text-[11px] text-gray-600 list-disc list-inside">
|
||||||
<li>مدیریت کاربران، نقشها و تنظیمات امنیتی</li>
|
<li>مدیریت کاربران، نقشها و تنظیمات امنیتی</li>
|
||||||
<li>تنظیمات پایه سایت، سئو، شبکهها و ساعات کاری</li>
|
|
||||||
<li>حذف دادهها (محصولات، دستهها، پیامها، رسانهها)</li>
|
<li>حذف دادهها (محصولات، دستهها، پیامها، رسانهها)</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -253,7 +252,7 @@ onMounted(load);
|
|||||||
</div>
|
</div>
|
||||||
<ul class="mt-2 space-y-1 text-[11px] text-gray-600 list-disc list-inside">
|
<ul class="mt-2 space-y-1 text-[11px] text-gray-600 list-disc list-inside">
|
||||||
<li>ایجاد، ویرایش و انتشار محصولات و دستهبندیها</li>
|
<li>ایجاد، ویرایش و انتشار محصولات و دستهبندیها</li>
|
||||||
<li>ویرایش محتوای بخشهای صفحه اصلی</li>
|
<li>ویرایش بخشهای سایت، نشانی، تماس، سئو و شبکهها</li>
|
||||||
<li>آپلود تصاویر در رسانه و مشاهده پیامهای تماس</li>
|
<li>آپلود تصاویر در رسانه و مشاهده پیامهای تماس</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@@ -269,7 +268,6 @@ onMounted(load);
|
|||||||
</div>
|
</div>
|
||||||
<ul class="mt-2.5 space-y-1.5 text-xs text-gray-600 list-disc list-inside">
|
<ul class="mt-2.5 space-y-1.5 text-xs text-gray-600 list-disc list-inside">
|
||||||
<li>مدیریت کاربران، افزودن کاربر و تغییر نقشها و وضعیت حساب</li>
|
<li>مدیریت کاربران، افزودن کاربر و تغییر نقشها و وضعیت حساب</li>
|
||||||
<li>دسترسی کامل به تنظیمات پایه سایت، ساعات کاری، شبکهها و سئو</li>
|
|
||||||
<li>مجوز حذف محصولات، دستهبندیها، رسانهها و پیامهای تماس</li>
|
<li>مجوز حذف محصولات، دستهبندیها، رسانهها و پیامهای تماس</li>
|
||||||
<li>مشاهده لاگهای امنیتی و رویدادهای کامل سامانه</li>
|
<li>مشاهده لاگهای امنیتی و رویدادهای کامل سامانه</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -282,7 +280,7 @@ onMounted(load);
|
|||||||
</div>
|
</div>
|
||||||
<ul class="mt-2.5 space-y-1.5 text-xs text-gray-600 list-disc list-inside">
|
<ul class="mt-2.5 space-y-1.5 text-xs text-gray-600 list-disc list-inside">
|
||||||
<li>ایجاد، ویرایش، کپی و انتشار محصولات و گروهها</li>
|
<li>ایجاد، ویرایش، کپی و انتشار محصولات و گروهها</li>
|
||||||
<li>ویرایش محتوای بخشهای صفحه اصلی و فعال/مخفیسازی نمایش آنها</li>
|
<li>ویرایش بخشهای سایت، نشانی، تماس، سئو، شبکهها و نمایش/مخفیسازی</li>
|
||||||
<li>آپلود و ویرایش اطلاعات رسانهها در کتابخانه رسانه</li>
|
<li>آپلود و ویرایش اطلاعات رسانهها در کتابخانه رسانه</li>
|
||||||
<li>مشاهده پیامهای تماس و بهروزرسانی وضعیت (بدون دسترسی حذف یا مدیریت کاربران)</li>
|
<li>مشاهده پیامهای تماس و بهروزرسانی وضعیت (بدون دسترسی حذف یا مدیریت کاربران)</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -319,7 +317,7 @@ onMounted(load);
|
|||||||
:options="ROLE_OPTIONS"
|
:options="ROLE_OPTIONS"
|
||||||
/>
|
/>
|
||||||
<div class="mt-1.5 text-[11px] text-gray-500">
|
<div class="mt-1.5 text-[11px] text-gray-500">
|
||||||
{{ model.role === 'ADMIN' ? 'مدیر کل: دسترسی کامل سیستمی، حذف دادهها، تنظیمات و مدیریت کاربران' : 'ویرایشگر: ویرایش محتوا، محصولات، بخشها و پیامها (بدون حذف)' }}
|
{{ model.role === 'ADMIN' ? 'مدیر کل: دسترسی کامل سیستمی، حذف دادهها و مدیریت کاربران' : 'ویرایشگر: ویرایش محتوا، محصولات، بخشهای سایت (از جمله نشانی و تماس)، پیامها (بدون حذف)' }}
|
||||||
</div>
|
</div>
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField v-if="!isNew" path="isActive">
|
<FormField v-if="!isNew" path="isActive">
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const data = defineModel<SectionData<"about">>({ required: true });
|
|||||||
<div class="grid gap-6 xl:grid-cols-3">
|
<div class="grid gap-6 xl:grid-cols-3">
|
||||||
<div class="space-y-6 xl:col-span-2">
|
<div class="space-y-6 xl:col-span-2">
|
||||||
<a-card title="عنوان و متن">
|
<a-card title="عنوان و متن">
|
||||||
<SectionHeadFields v-model="data.head" :with-desc="false" />
|
<SectionHeadFields v-model="data.head" />
|
||||||
<FormField path="body" label="متن درباره ما" required>
|
<FormField path="body" label="متن درباره ما" required>
|
||||||
<a-textarea v-model:value="data.body" :auto-size="{ minRows: 4, maxRows: 10 }" />
|
<a-textarea v-model:value="data.body" :auto-size="{ minRows: 4, maxRows: 10 }" />
|
||||||
</FormField>
|
</FormField>
|
||||||
|
|||||||
@@ -1,17 +1,31 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SectionData } from "@imen/shared";
|
import type { SectionData } from "@imen/shared";
|
||||||
|
import ContactInfoFields from "@/components/ContactInfoFields.vue";
|
||||||
import FormField from "@/components/FormField.vue";
|
import FormField from "@/components/FormField.vue";
|
||||||
import SectionHeadFields from "@/components/SectionHeadFields.vue";
|
import SectionHeadFields from "@/components/SectionHeadFields.vue";
|
||||||
|
|
||||||
const data = defineModel<SectionData<"contact">>({ required: true });
|
const data = defineModel<SectionData<"contact">>({ required: true });
|
||||||
|
/** Site-wide contact info (settings); saved together with this section. */
|
||||||
|
const settings = defineModel<SectionData<"settings"> | null>("settings", { default: null });
|
||||||
|
|
||||||
|
function onSettingsUpdate(value: SectionData<"settings">) {
|
||||||
|
settings.value = value;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<a-card title="عنوان بخش">
|
<a-card title="عنوان بخش">
|
||||||
<SectionHeadFields v-model="data.head" />
|
<SectionHeadFields v-model="data.head" />
|
||||||
<p class="text-sm text-gray-500">نشانی، تلفن، ایمیل و ساعات کاری از «تنظیمات سایت» خوانده میشوند.</p>
|
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
|
<a-card v-if="settings" title="نشانی و اطلاعات تماس">
|
||||||
|
<p class="mb-4 text-sm text-gray-500">
|
||||||
|
این مقادیر در هدر، فوتر، بخش تماس و سئوی سایت مشترکاند و با ذخیرهٔ همین صفحه بهروز میشوند.
|
||||||
|
</p>
|
||||||
|
<ContactInfoFields :model-value="settings" @update:model-value="onSettingsUpdate" />
|
||||||
|
</a-card>
|
||||||
|
|
||||||
<div class="grid gap-6 lg:grid-cols-2">
|
<div class="grid gap-6 lg:grid-cols-2">
|
||||||
<a-card title="کادر پاسخگویی اضطراری">
|
<a-card title="کادر پاسخگویی اضطراری">
|
||||||
<FormField path="emergencyTitle" label="عنوان" required><a-input v-model:value="data.emergencyTitle" /></FormField>
|
<FormField path="emergencyTitle" label="عنوان" required><a-input v-model:value="data.emergencyTitle" /></FormField>
|
||||||
@@ -24,10 +38,16 @@ const data = defineModel<SectionData<"contact">>({ required: true });
|
|||||||
<FormField path="formTitle" label="عنوان فرم" hint="برای شکستن خط Enter بزنید" required>
|
<FormField path="formTitle" label="عنوان فرم" hint="برای شکستن خط Enter بزنید" required>
|
||||||
<a-textarea v-model:value="data.formTitle" :auto-size="{ minRows: 2 }" />
|
<a-textarea v-model:value="data.formTitle" :auto-size="{ minRows: 2 }" />
|
||||||
</FormField>
|
</FormField>
|
||||||
|
<FormField path="submitLabel" label="متن دکمه ارسال" required>
|
||||||
|
<a-input v-model:value="data.submitLabel" />
|
||||||
|
</FormField>
|
||||||
<FormField path="successTitle" label="عنوان پیام موفقیت" required><a-input v-model:value="data.successTitle" /></FormField>
|
<FormField path="successTitle" label="عنوان پیام موفقیت" required><a-input v-model:value="data.successTitle" /></FormField>
|
||||||
<FormField path="successText" label="متن پیام موفقیت" required>
|
<FormField path="successText" label="متن پیام موفقیت" required>
|
||||||
<a-textarea v-model:value="data.successText" :auto-size="{ minRows: 2 }" />
|
<a-textarea v-model:value="data.successText" :auto-size="{ minRows: 2 }" />
|
||||||
</FormField>
|
</FormField>
|
||||||
|
<FormField path="resetLabel" label="متن دکمه بعد از موفقیت" required>
|
||||||
|
<a-input v-model:value="data.resetLabel" />
|
||||||
|
</FormField>
|
||||||
</a-card>
|
</a-card>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,15 +6,30 @@ const data = defineModel<SectionData<"footer">>({ required: true });
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div class="space-y-6">
|
||||||
<a-card title="فوتر">
|
<a-card title="فوتر">
|
||||||
<FormField path="about" label="متن معرفی" required>
|
<FormField path="about" label="متن معرفی" required>
|
||||||
<a-textarea v-model:value="data.about" :auto-size="{ minRows: 2, maxRows: 5 }" />
|
<a-textarea v-model:value="data.about" :auto-size="{ minRows: 2, maxRows: 5 }" />
|
||||||
</FormField>
|
</FormField>
|
||||||
|
<div class="grid gap-x-4 md:grid-cols-3">
|
||||||
|
<FormField path="linksTitle" label="عنوان ستون لینکها" required>
|
||||||
|
<a-input v-model:value="data.linksTitle" />
|
||||||
|
</FormField>
|
||||||
|
<FormField path="productsTitle" label="عنوان ستون محصولات" required>
|
||||||
|
<a-input v-model:value="data.productsTitle" />
|
||||||
|
</FormField>
|
||||||
|
<FormField path="contactTitle" label="عنوان ستون ارتباط" required>
|
||||||
|
<a-input v-model:value="data.contactTitle" />
|
||||||
|
</FormField>
|
||||||
|
</div>
|
||||||
<div class="grid gap-x-4 md:grid-cols-3">
|
<div class="grid gap-x-4 md:grid-cols-3">
|
||||||
<FormField path="copyright" label="متن کپیرایت" required><a-input v-model:value="data.copyright" /></FormField>
|
<FormField path="copyright" label="متن کپیرایت" required><a-input v-model:value="data.copyright" /></FormField>
|
||||||
<FormField path="certLine" label="خط گواهینامهها"><a-input v-model:value="data.certLine" class="ltr-input" /></FormField>
|
<FormField path="certLine" label="خط گواهینامهها"><a-input v-model:value="data.certLine" class="ltr-input" /></FormField>
|
||||||
<FormField path="emergencyNote" label="یادداشت پشتیبانی"><a-input v-model:value="data.emergencyNote" /></FormField>
|
<FormField path="emergencyNote" label="یادداشت پشتیبانی"><a-input v-model:value="data.emergencyNote" /></FormField>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-sm text-gray-500">لینک دستهها و اطلاعات تماس فوتر بهصورت خودکار از دستهبندیها و تنظیمات سایت ساخته میشوند.</p>
|
<p class="text-sm text-gray-500">
|
||||||
|
لینک دستهها و اطلاعات تماس فوتر بهصورت خودکار از دستهبندیها و تنظیمات سایت ساخته میشوند.
|
||||||
|
</p>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type MediaDto, type SectionData, SOCIAL_TYPES, type SocialType, WEEK_DAYS } from "@imen/shared";
|
import { type MediaDto, type SectionData, SOCIAL_TYPES, type SocialType, WEEK_DAYS } from "@imen/shared";
|
||||||
|
import ContactInfoFields from "@/components/ContactInfoFields.vue";
|
||||||
import FormField from "@/components/FormField.vue";
|
import FormField from "@/components/FormField.vue";
|
||||||
import ImageField from "@/components/ImageField.vue";
|
import ImageField from "@/components/ImageField.vue";
|
||||||
import ListEditor from "@/components/ListEditor.vue";
|
import ListEditor from "@/components/ListEditor.vue";
|
||||||
@@ -66,33 +67,22 @@ function pickOgImage(m: MediaDto) {
|
|||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<a-card title="اطلاعات تماس">
|
<a-card title="اطلاعات تماس">
|
||||||
|
<ContactInfoFields v-model="data" />
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<a-card title="دکمه هدر">
|
||||||
<div class="grid gap-x-4 md:grid-cols-2">
|
<div class="grid gap-x-4 md:grid-cols-2">
|
||||||
<FormField path="phoneDisplay" label="تلفن (نمایشی)" required>
|
<FormField path="headerCta.label" label="متن دکمه" required>
|
||||||
<a-input v-model:value="data.phoneDisplay" />
|
<a-input v-model:value="data.headerCta.label" />
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField path="phone" label="تلفن (برای لینک و گوگل)" hint="با کد کشور، مثلاً +98-21-91008800" required>
|
<FormField path="headerCta.href" label="لینک" required>
|
||||||
<a-input v-model:value="data.phone" class="ltr-input" />
|
<a-input v-model:value="data.headerCta.href" class="ltr-input" placeholder="/#contact" />
|
||||||
</FormField>
|
|
||||||
<FormField path="email" label="ایمیل" required class="md:col-span-2">
|
|
||||||
<a-input v-model:value="data.email" class="ltr-input" />
|
|
||||||
</FormField>
|
|
||||||
<FormField path="address.locality" label="شهر" required>
|
|
||||||
<a-input v-model:value="data.address.locality" />
|
|
||||||
</FormField>
|
|
||||||
<FormField path="address.country" label="کد کشور" required>
|
|
||||||
<a-input v-model:value="data.address.country" class="ltr-input" :maxlength="2" />
|
|
||||||
</FormField>
|
|
||||||
<FormField path="address.street" label="نشانی" required class="md:col-span-2">
|
|
||||||
<a-input v-model:value="data.address.street" />
|
|
||||||
</FormField>
|
</FormField>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</a-card>
|
||||||
|
|
||||||
<a-card title="ساعات کاری">
|
<a-card title="ساعات کاری (برای گوگل)">
|
||||||
<FormField path="workingHours" label="متن نمایشی" required>
|
<FormField path="openingHours.days" label="روزهای کاری">
|
||||||
<a-input v-model:value="data.workingHours" />
|
|
||||||
</FormField>
|
|
||||||
<FormField path="openingHours.days" label="روزهای کاری (برای گوگل)">
|
|
||||||
<a-checkbox-group
|
<a-checkbox-group
|
||||||
v-model:value="data.openingHours.days"
|
v-model:value="data.openingHours.days"
|
||||||
:options="DAY_OPTIONS"
|
:options="DAY_OPTIONS"
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ const data = defineModel<SectionData<"testimonials">>({ required: true });
|
|||||||
<template>
|
<template>
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<a-card title="عنوان بخش">
|
<a-card title="عنوان بخش">
|
||||||
<SectionHeadFields v-model="data.head" :with-highlight="false" :with-desc="false" />
|
<SectionHeadFields v-model="data.head" />
|
||||||
</a-card>
|
</a-card>
|
||||||
<a-card title="نظرات" :body-style="{ background: '#fafafa' }">
|
<a-card title="نظرات" :body-style="{ background: '#fafafa' }">
|
||||||
<ListEditor v-model="data.items" :create="() => ({ text: '', name: '', role: '' })" :max="12" add-label="افزودن نظر" :item-title="(q) => q.name">
|
<ListEditor v-model="data.items" :create="() => ({ text: '', name: '', role: '' })" :max="12" add-label="افزودن نظر" :item-title="(q) => q.name">
|
||||||
|
|||||||
+1
-4
@@ -10,7 +10,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|||||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||||
return function (target, key) { decorator(target, key, paramIndex); }
|
return function (target, key) { decorator(target, key, paramIndex); }
|
||||||
};
|
};
|
||||||
import { BadRequestException, Body, Controller, ForbiddenException, Get, Injectable, Module, NotFoundException, Param, Patch, Put, ServiceUnavailableException, } from "@nestjs/common";
|
import { BadRequestException, Body, Controller, Get, Injectable, Module, NotFoundException, Param, Patch, Put, ServiceUnavailableException, } from "@nestjs/common";
|
||||||
import { isSectionKey, SECTION_KEYS, sectionSchemas, toIssues, } from "@imen/shared";
|
import { isSectionKey, SECTION_KEYS, sectionSchemas, toIssues, } from "@imen/shared";
|
||||||
import { AdminAuth, CurrentUser } from "../common/auth.js";
|
import { AdminAuth, CurrentUser } from "../common/auth.js";
|
||||||
import { AuditService, RevalidateService } from "../common/common.module.js";
|
import { AuditService, RevalidateService } from "../common/common.module.js";
|
||||||
@@ -44,9 +44,6 @@ let SectionsService = class SectionsService {
|
|||||||
async update(key, body, user) {
|
async update(key, body, user) {
|
||||||
if (!isSectionKey(key))
|
if (!isSectionKey(key))
|
||||||
throw new NotFoundException("بخش پیدا نشد");
|
throw new NotFoundException("بخش پیدا نشد");
|
||||||
if (key === "settings" && user.role !== "ADMIN") {
|
|
||||||
throw new ForbiddenException("فقط مدیر کل میتواند تنظیمات سایت را تغییر دهد");
|
|
||||||
}
|
|
||||||
const parsed = sectionSchemas[key].safeParse(body);
|
const parsed = sectionSchemas[key].safeParse(body);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
throw new BadRequestException({
|
throw new BadRequestException({
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+7
@@ -12,6 +12,7 @@ export const SECTIONS = {
|
|||||||
locality: "تهران",
|
locality: "تهران",
|
||||||
country: "IR",
|
country: "IR",
|
||||||
},
|
},
|
||||||
|
mapUrl: "",
|
||||||
workingHours: "شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰",
|
workingHours: "شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰",
|
||||||
openingHours: {
|
openingHours: {
|
||||||
days: ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"],
|
days: ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"],
|
||||||
@@ -19,6 +20,7 @@ export const SECTIONS = {
|
|||||||
closes: "17:00",
|
closes: "17:00",
|
||||||
},
|
},
|
||||||
foundingDate: "2007",
|
foundingDate: "2007",
|
||||||
|
headerCta: { label: "درخواست مشاوره", href: "/#contact" },
|
||||||
socials: [
|
socials: [
|
||||||
{ type: "website", url: "/" },
|
{ type: "website", url: "/" },
|
||||||
{ type: "email", url: "mailto:info@imenpishgaman.ir" },
|
{ type: "email", url: "mailto:info@imenpishgaman.ir" },
|
||||||
@@ -246,6 +248,8 @@ export const SECTIONS = {
|
|||||||
formTitle: "در کمتر از ۲۴ ساعت\nبا شما تماس میگیریم",
|
formTitle: "در کمتر از ۲۴ ساعت\nبا شما تماس میگیریم",
|
||||||
successTitle: "درخواست شما ثبت شد",
|
successTitle: "درخواست شما ثبت شد",
|
||||||
successText: "کارشناسان ما تا ۲۴ ساعت آینده برای هماهنگی جلسه بازدید با شما تماس میگیرند.",
|
successText: "کارشناسان ما تا ۲۴ ساعت آینده برای هماهنگی جلسه بازدید با شما تماس میگیرند.",
|
||||||
|
submitLabel: "ارسال درخواست مشاوره",
|
||||||
|
resetLabel: "ثبت درخواست جدید",
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
@@ -253,6 +257,9 @@ export const SECTIONS = {
|
|||||||
copyright: "© ۱۴۰۴ ایمن پیشگامان — تمامی حقوق محفوظ است.",
|
copyright: "© ۱۴۰۴ ایمن پیشگامان — تمامی حقوق محفوظ است.",
|
||||||
certLine: "ISO 9001:2015 — CE CERTIFIED PPE SUPPLIER",
|
certLine: "ISO 9001:2015 — CE CERTIFIED PPE SUPPLIER",
|
||||||
emergencyNote: "پشتیبانی اضطراری: ۲۴/۷",
|
emergencyNote: "پشتیبانی اضطراری: ۲۴/۷",
|
||||||
|
linksTitle: "دسترسی",
|
||||||
|
productsTitle: "محصولات",
|
||||||
|
contactTitle: "ارتباط",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
export const CATEGORY_COVERS = {
|
export const CATEGORY_COVERS = {
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -2,7 +2,6 @@ import {
|
|||||||
BadRequestException,
|
BadRequestException,
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
ForbiddenException,
|
|
||||||
Get,
|
Get,
|
||||||
Injectable,
|
Injectable,
|
||||||
Module,
|
Module,
|
||||||
@@ -55,10 +54,6 @@ export class SectionsService {
|
|||||||
|
|
||||||
async update(key: string, body: unknown, user: AuthUser) {
|
async update(key: string, body: unknown, user: AuthUser) {
|
||||||
if (!isSectionKey(key)) throw new NotFoundException("بخش پیدا نشد");
|
if (!isSectionKey(key)) throw new NotFoundException("بخش پیدا نشد");
|
||||||
// site-wide settings (contact info, SEO defaults) are limited to admins
|
|
||||||
if (key === "settings" && user.role !== "ADMIN") {
|
|
||||||
throw new ForbiddenException("فقط مدیر کل میتواند تنظیمات سایت را تغییر دهد");
|
|
||||||
}
|
|
||||||
const parsed = sectionSchemas[key].safeParse(body);
|
const parsed = sectionSchemas[key].safeParse(body);
|
||||||
if (!parsed.success) {
|
if (!parsed.success) {
|
||||||
throw new BadRequestException({
|
throw new BadRequestException({
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const SECTIONS: SiteSections = {
|
|||||||
locality: "تهران",
|
locality: "تهران",
|
||||||
country: "IR",
|
country: "IR",
|
||||||
},
|
},
|
||||||
|
mapUrl: "",
|
||||||
workingHours: "شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰",
|
workingHours: "شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰",
|
||||||
openingHours: {
|
openingHours: {
|
||||||
days: ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"],
|
days: ["Saturday", "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday"],
|
||||||
@@ -26,6 +27,7 @@ export const SECTIONS: SiteSections = {
|
|||||||
closes: "17:00",
|
closes: "17:00",
|
||||||
},
|
},
|
||||||
foundingDate: "2007",
|
foundingDate: "2007",
|
||||||
|
headerCta: { label: "درخواست مشاوره", href: "/#contact" },
|
||||||
socials: [
|
socials: [
|
||||||
{ type: "website", url: "/" },
|
{ type: "website", url: "/" },
|
||||||
{ type: "email", url: "mailto:info@imenpishgaman.ir" },
|
{ type: "email", url: "mailto:info@imenpishgaman.ir" },
|
||||||
@@ -265,6 +267,8 @@ export const SECTIONS: SiteSections = {
|
|||||||
formTitle: "در کمتر از ۲۴ ساعت\nبا شما تماس میگیریم",
|
formTitle: "در کمتر از ۲۴ ساعت\nبا شما تماس میگیریم",
|
||||||
successTitle: "درخواست شما ثبت شد",
|
successTitle: "درخواست شما ثبت شد",
|
||||||
successText: "کارشناسان ما تا ۲۴ ساعت آینده برای هماهنگی جلسه بازدید با شما تماس میگیرند.",
|
successText: "کارشناسان ما تا ۲۴ ساعت آینده برای هماهنگی جلسه بازدید با شما تماس میگیرند.",
|
||||||
|
submitLabel: "ارسال درخواست مشاوره",
|
||||||
|
resetLabel: "ثبت درخواست جدید",
|
||||||
},
|
},
|
||||||
|
|
||||||
footer: {
|
footer: {
|
||||||
@@ -274,6 +278,9 @@ export const SECTIONS: SiteSections = {
|
|||||||
copyright: "© ۱۴۰۴ ایمن پیشگامان — تمامی حقوق محفوظ است.",
|
copyright: "© ۱۴۰۴ ایمن پیشگامان — تمامی حقوق محفوظ است.",
|
||||||
certLine: "ISO 9001:2015 — CE CERTIFIED PPE SUPPLIER",
|
certLine: "ISO 9001:2015 — CE CERTIFIED PPE SUPPLIER",
|
||||||
emergencyNote: "پشتیبانی اضطراری: ۲۴/۷",
|
emergencyNote: "پشتیبانی اضطراری: ۲۴/۷",
|
||||||
|
linksTitle: "دسترسی",
|
||||||
|
productsTitle: "محصولات",
|
||||||
|
contactTitle: "ارتباط",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"date": "2026-10-24T11:47:36.129Z",
|
"date": "2026-10-24T13:11:46.671Z",
|
||||||
"preset": "node-server",
|
"preset": "node-server",
|
||||||
"framework": {
|
"framework": {
|
||||||
"name": "nuxt",
|
"name": "nuxt",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
{"id":"3e7fac48-d5e5-406c-8e09-0f6380ec2f4f","timestamp":1792842383527}
|
{"id":"1fdc7d29-4179-4263-a065-71c6f514739a","timestamp":1792847411809}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"id":"1fdc7d29-4179-4263-a065-71c6f514739a","timestamp":1792847411809,"prerendered":[]}
|
||||||
@@ -1 +0,0 @@
|
|||||||
{"id":"3e7fac48-d5e5-406c-8e09-0f6380ec2f4f","timestamp":1792842383527,"prerendered":[]}
|
|
||||||
@@ -4066,7 +4066,7 @@ function _expandFromEnv(value) {
|
|||||||
const _inlineRuntimeConfig = {
|
const _inlineRuntimeConfig = {
|
||||||
"app": {
|
"app": {
|
||||||
"baseURL": "/",
|
"baseURL": "/",
|
||||||
"buildId": "3e7fac48-d5e5-406c-8e09-0f6380ec2f4f",
|
"buildId": "1fdc7d29-4179-4263-a065-71c6f514739a",
|
||||||
"buildAssetsDir": "/_nuxt/",
|
"buildAssetsDir": "/_nuxt/",
|
||||||
"cdnURL": ""
|
"cdnURL": ""
|
||||||
},
|
},
|
||||||
@@ -4813,6 +4813,13 @@ const assets = {
|
|||||||
"size": 337,
|
"size": 337,
|
||||||
"path": "../public/favicon.svg"
|
"path": "../public/favicon.svg"
|
||||||
},
|
},
|
||||||
|
"/images/logo.png": {
|
||||||
|
"type": "image/png",
|
||||||
|
"etag": "\"45a79-DygL+P0onWMf3/jsNWp1BqEUPE4\"",
|
||||||
|
"mtime": "2026-10-11T12:10:17.609Z",
|
||||||
|
"size": 285305,
|
||||||
|
"path": "../public/images/logo.png"
|
||||||
|
},
|
||||||
"/fonts/IRANSansWebFaNum.woff2": {
|
"/fonts/IRANSansWebFaNum.woff2": {
|
||||||
"type": "font/woff2",
|
"type": "font/woff2",
|
||||||
"etag": "\"6d28-AufINCNdNZVNkWiYA6/Wp9W2wE4\"",
|
"etag": "\"6d28-AufINCNdNZVNkWiYA6/Wp9W2wE4\"",
|
||||||
@@ -4834,13 +4841,6 @@ const assets = {
|
|||||||
"size": 27316,
|
"size": 27316,
|
||||||
"path": "../public/fonts/IRANSansWebFaNum_Bold.woff2"
|
"path": "../public/fonts/IRANSansWebFaNum_Bold.woff2"
|
||||||
},
|
},
|
||||||
"/fonts/IRANSansWebFaNum_Medium.woff2": {
|
|
||||||
"type": "font/woff2",
|
|
||||||
"etag": "\"64b8-6V1kQDm0NUXner/ubiM2u7MPIyY\"",
|
|
||||||
"mtime": "2026-07-02T15:07:42.535Z",
|
|
||||||
"size": 25784,
|
|
||||||
"path": "../public/fonts/IRANSansWebFaNum_Medium.woff2"
|
|
||||||
},
|
|
||||||
"/fonts/IRANSansWebFaNum_Light.woff2": {
|
"/fonts/IRANSansWebFaNum_Light.woff2": {
|
||||||
"type": "font/woff2",
|
"type": "font/woff2",
|
||||||
"etag": "\"6ef0-0ZuVZfSKf1HbzDmvdDZdESRzFOs\"",
|
"etag": "\"6ef0-0ZuVZfSKf1HbzDmvdDZdESRzFOs\"",
|
||||||
@@ -4848,40 +4848,12 @@ const assets = {
|
|||||||
"size": 28400,
|
"size": 28400,
|
||||||
"path": "../public/fonts/IRANSansWebFaNum_Light.woff2"
|
"path": "../public/fonts/IRANSansWebFaNum_Light.woff2"
|
||||||
},
|
},
|
||||||
"/_nuxt/BHOIQrRl.js": {
|
"/fonts/IRANSansWebFaNum_Medium.woff2": {
|
||||||
"type": "text/javascript; charset=utf-8",
|
"type": "font/woff2",
|
||||||
"etag": "\"1637-w7apJM7DBwq91AEy0Dz89/JwU4s\"",
|
"etag": "\"64b8-6V1kQDm0NUXner/ubiM2u7MPIyY\"",
|
||||||
"mtime": "2026-10-24T11:47:18.110Z",
|
"mtime": "2026-07-02T15:07:42.535Z",
|
||||||
"size": 5687,
|
"size": 25784,
|
||||||
"path": "../public/_nuxt/BHOIQrRl.js"
|
"path": "../public/fonts/IRANSansWebFaNum_Medium.woff2"
|
||||||
},
|
|
||||||
"/_nuxt/DlAPF8TW.js": {
|
|
||||||
"type": "text/javascript; charset=utf-8",
|
|
||||||
"etag": "\"1e85-YNtOmEmkO1nMcpDM/ztRmg/rRLw\"",
|
|
||||||
"mtime": "2026-10-24T11:47:18.112Z",
|
|
||||||
"size": 7813,
|
|
||||||
"path": "../public/_nuxt/DlAPF8TW.js"
|
|
||||||
},
|
|
||||||
"/_nuxt/Dd1GE-gU.js": {
|
|
||||||
"type": "text/javascript; charset=utf-8",
|
|
||||||
"etag": "\"21420-NSMMtehs6gAIMs2sZ84NfSEv/wU\"",
|
|
||||||
"mtime": "2026-10-24T11:47:18.110Z",
|
|
||||||
"size": 136224,
|
|
||||||
"path": "../public/_nuxt/Dd1GE-gU.js"
|
|
||||||
},
|
|
||||||
"/_nuxt/D0e0LUpa.js": {
|
|
||||||
"type": "text/javascript; charset=utf-8",
|
|
||||||
"etag": "\"794-cNNGOqTLOp2+IDNDEbTWshwORp0\"",
|
|
||||||
"mtime": "2026-10-24T11:47:18.110Z",
|
|
||||||
"size": 1940,
|
|
||||||
"path": "../public/_nuxt/D0e0LUpa.js"
|
|
||||||
},
|
|
||||||
"/_nuxt/DOASWvjr.js": {
|
|
||||||
"type": "text/javascript; charset=utf-8",
|
|
||||||
"etag": "\"3827-3BvUfZznriOnNWFcpi6aALVMpOE\"",
|
|
||||||
"mtime": "2026-10-24T11:47:18.110Z",
|
|
||||||
"size": 14375,
|
|
||||||
"path": "../public/_nuxt/DOASWvjr.js"
|
|
||||||
},
|
},
|
||||||
"/fonts/IRANSansWebFaNum_UltraLight.woff2": {
|
"/fonts/IRANSansWebFaNum_UltraLight.woff2": {
|
||||||
"type": "font/woff2",
|
"type": "font/woff2",
|
||||||
@@ -4890,73 +4862,101 @@ const assets = {
|
|||||||
"size": 28208,
|
"size": 28208,
|
||||||
"path": "../public/fonts/IRANSansWebFaNum_UltraLight.woff2"
|
"path": "../public/fonts/IRANSansWebFaNum_UltraLight.woff2"
|
||||||
},
|
},
|
||||||
"/_nuxt/DoONAYG5.js": {
|
"/_nuxt/D0e0LUpa.js": {
|
||||||
"type": "text/javascript; charset=utf-8",
|
"type": "text/javascript; charset=utf-8",
|
||||||
"etag": "\"3b62c-dc9OPIQbgdy0oXHYg6EoUgVy6yc\"",
|
"etag": "\"794-cNNGOqTLOp2+IDNDEbTWshwORp0\"",
|
||||||
"mtime": "2026-10-24T11:47:18.112Z",
|
"mtime": "2026-10-24T13:11:27.225Z",
|
||||||
"size": 243244,
|
"size": 1940,
|
||||||
"path": "../public/_nuxt/DoONAYG5.js"
|
"path": "../public/_nuxt/D0e0LUpa.js"
|
||||||
},
|
},
|
||||||
"/_nuxt/VuesaxIcon.4cYnpGo8.css": {
|
"/_nuxt/BHOIQrRl.js": {
|
||||||
"type": "text/css; charset=utf-8",
|
|
||||||
"etag": "\"dc-Uz1+XA31y3oabrHslWpIJp4Ls74\"",
|
|
||||||
"mtime": "2026-10-24T11:47:18.112Z",
|
|
||||||
"size": 220,
|
|
||||||
"path": "../public/_nuxt/VuesaxIcon.4cYnpGo8.css"
|
|
||||||
},
|
|
||||||
"/_nuxt/DlDWjZRl.js": {
|
|
||||||
"type": "text/javascript; charset=utf-8",
|
"type": "text/javascript; charset=utf-8",
|
||||||
"etag": "\"37855-Z6e8Pe/n6IqFkcSEj31zIJ7FhuQ\"",
|
"etag": "\"1637-w7apJM7DBwq91AEy0Dz89/JwU4s\"",
|
||||||
"mtime": "2026-10-24T11:47:18.100Z",
|
"mtime": "2026-10-24T13:11:27.225Z",
|
||||||
"size": 227413,
|
"size": 5687,
|
||||||
"path": "../public/_nuxt/DlDWjZRl.js"
|
"path": "../public/_nuxt/BHOIQrRl.js"
|
||||||
},
|
},
|
||||||
"/_nuxt/builds/latest.json": {
|
"/_nuxt/Dd1GE-gU.js": {
|
||||||
"type": "application/json",
|
|
||||||
"etag": "\"47-JKgzcgu4mBBz7nSPUtzomjs30dc\"",
|
|
||||||
"mtime": "2026-10-24T11:47:23.008Z",
|
|
||||||
"size": 71,
|
|
||||||
"path": "../public/_nuxt/builds/latest.json"
|
|
||||||
},
|
|
||||||
"/_nuxt/builds/meta/3e7fac48-d5e5-406c-8e09-0f6380ec2f4f.json": {
|
|
||||||
"type": "application/json",
|
|
||||||
"etag": "\"58-JIbrCAB+PEB8Ybvxp3RzCo2D6XU\"",
|
|
||||||
"mtime": "2026-10-24T11:47:23.008Z",
|
|
||||||
"size": 88,
|
|
||||||
"path": "../public/_nuxt/builds/meta/3e7fac48-d5e5-406c-8e09-0f6380ec2f4f.json"
|
|
||||||
},
|
|
||||||
"/_nuxt/xGM79_uD.js": {
|
|
||||||
"type": "text/javascript; charset=utf-8",
|
"type": "text/javascript; charset=utf-8",
|
||||||
"etag": "\"5023-nhybJtf5A4+tqp5rdRjhnkK+QMY\"",
|
"etag": "\"21420-NSMMtehs6gAIMs2sZ84NfSEv/wU\"",
|
||||||
"mtime": "2026-10-24T11:47:18.112Z",
|
"mtime": "2026-10-24T13:11:27.229Z",
|
||||||
"size": 20515,
|
"size": 136224,
|
||||||
"path": "../public/_nuxt/xGM79_uD.js"
|
"path": "../public/_nuxt/Dd1GE-gU.js"
|
||||||
},
|
},
|
||||||
"/_nuxt/r7-JdZhN.js": {
|
"/_nuxt/DlAPF8TW.js": {
|
||||||
"type": "text/javascript; charset=utf-8",
|
"type": "text/javascript; charset=utf-8",
|
||||||
"etag": "\"78c-4hmOEQGuRxrwCX/1v0DRIhjwpXE\"",
|
"etag": "\"1e85-YNtOmEmkO1nMcpDM/ztRmg/rRLw\"",
|
||||||
"mtime": "2026-10-24T11:47:18.112Z",
|
"mtime": "2026-10-24T13:11:27.230Z",
|
||||||
"size": 1932,
|
"size": 7813,
|
||||||
"path": "../public/_nuxt/r7-JdZhN.js"
|
"path": "../public/_nuxt/DlAPF8TW.js"
|
||||||
|
},
|
||||||
|
"/_nuxt/DOASWvjr.js": {
|
||||||
|
"type": "text/javascript; charset=utf-8",
|
||||||
|
"etag": "\"3827-3BvUfZznriOnNWFcpi6aALVMpOE\"",
|
||||||
|
"mtime": "2026-10-24T13:11:27.228Z",
|
||||||
|
"size": 14375,
|
||||||
|
"path": "../public/_nuxt/DOASWvjr.js"
|
||||||
},
|
},
|
||||||
"/_nuxt/entry.NJkjIEBJ.css": {
|
"/_nuxt/entry.NJkjIEBJ.css": {
|
||||||
"type": "text/css; charset=utf-8",
|
"type": "text/css; charset=utf-8",
|
||||||
"etag": "\"f954-gFKPvCM4WZHzb2tWqZEkEegmw5w\"",
|
"etag": "\"f954-gFKPvCM4WZHzb2tWqZEkEegmw5w\"",
|
||||||
"mtime": "2026-10-24T11:47:18.112Z",
|
"mtime": "2026-10-24T13:11:27.230Z",
|
||||||
"size": 63828,
|
"size": 63828,
|
||||||
"path": "../public/_nuxt/entry.NJkjIEBJ.css"
|
"path": "../public/_nuxt/entry.NJkjIEBJ.css"
|
||||||
},
|
},
|
||||||
"/images/logo.png": {
|
"/_nuxt/r7-JdZhN.js": {
|
||||||
"type": "image/png",
|
"type": "text/javascript; charset=utf-8",
|
||||||
"etag": "\"45a79-DygL+P0onWMf3/jsNWp1BqEUPE4\"",
|
"etag": "\"78c-4hmOEQGuRxrwCX/1v0DRIhjwpXE\"",
|
||||||
"mtime": "2026-10-11T12:10:17.609Z",
|
"mtime": "2026-10-24T13:11:27.230Z",
|
||||||
"size": 285305,
|
"size": 1932,
|
||||||
"path": "../public/images/logo.png"
|
"path": "../public/_nuxt/r7-JdZhN.js"
|
||||||
|
},
|
||||||
|
"/_nuxt/VuesaxIcon.4cYnpGo8.css": {
|
||||||
|
"type": "text/css; charset=utf-8",
|
||||||
|
"etag": "\"dc-Uz1+XA31y3oabrHslWpIJp4Ls74\"",
|
||||||
|
"mtime": "2026-10-24T13:11:27.230Z",
|
||||||
|
"size": 220,
|
||||||
|
"path": "../public/_nuxt/VuesaxIcon.4cYnpGo8.css"
|
||||||
|
},
|
||||||
|
"/_nuxt/builds/latest.json": {
|
||||||
|
"type": "application/json",
|
||||||
|
"etag": "\"47-qofqu0SWsPJasrRW/9JkfJXfC/4\"",
|
||||||
|
"mtime": "2026-10-24T13:11:32.444Z",
|
||||||
|
"size": 71,
|
||||||
|
"path": "../public/_nuxt/builds/latest.json"
|
||||||
|
},
|
||||||
|
"/_nuxt/builds/meta/1fdc7d29-4179-4263-a065-71c6f514739a.json": {
|
||||||
|
"type": "application/json",
|
||||||
|
"etag": "\"58-AOn2PtlkYd83L1490p8deI3f16g\"",
|
||||||
|
"mtime": "2026-10-24T13:11:32.444Z",
|
||||||
|
"size": 88,
|
||||||
|
"path": "../public/_nuxt/builds/meta/1fdc7d29-4179-4263-a065-71c6f514739a.json"
|
||||||
|
},
|
||||||
|
"/_nuxt/DlDWjZRl.js": {
|
||||||
|
"type": "text/javascript; charset=utf-8",
|
||||||
|
"etag": "\"37855-Z6e8Pe/n6IqFkcSEj31zIJ7FhuQ\"",
|
||||||
|
"mtime": "2026-10-24T13:11:27.225Z",
|
||||||
|
"size": 227413,
|
||||||
|
"path": "../public/_nuxt/DlDWjZRl.js"
|
||||||
|
},
|
||||||
|
"/_nuxt/xGM79_uD.js": {
|
||||||
|
"type": "text/javascript; charset=utf-8",
|
||||||
|
"etag": "\"5023-nhybJtf5A4+tqp5rdRjhnkK+QMY\"",
|
||||||
|
"mtime": "2026-10-24T13:11:27.230Z",
|
||||||
|
"size": 20515,
|
||||||
|
"path": "../public/_nuxt/xGM79_uD.js"
|
||||||
|
},
|
||||||
|
"/_nuxt/DoONAYG5.js": {
|
||||||
|
"type": "text/javascript; charset=utf-8",
|
||||||
|
"etag": "\"3b62c-dc9OPIQbgdy0oXHYg6EoUgVy6yc\"",
|
||||||
|
"mtime": "2026-10-24T13:11:27.230Z",
|
||||||
|
"size": 243244,
|
||||||
|
"path": "../public/_nuxt/DoONAYG5.js"
|
||||||
},
|
},
|
||||||
"/_nuxt/D6Ym2dQP.js": {
|
"/_nuxt/D6Ym2dQP.js": {
|
||||||
"type": "text/javascript; charset=utf-8",
|
"type": "text/javascript; charset=utf-8",
|
||||||
"etag": "\"1241f5-OG41EU7LABNWcdBigZe253LHLTw\"",
|
"etag": "\"1241f5-OG41EU7LABNWcdBigZe253LHLTw\"",
|
||||||
"mtime": "2026-10-24T11:47:18.110Z",
|
"mtime": "2026-10-24T13:11:27.228Z",
|
||||||
"size": 1196533,
|
"size": 1196533,
|
||||||
"path": "../public/_nuxt/D6Ym2dQP.js"
|
"path": "../public/_nuxt/D6Ym2dQP.js"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ const SOCIALS = computed(() =>
|
|||||||
|
|
||||||
<!-- quick links -->
|
<!-- quick links -->
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<div class="font-mono text-xs tracking-[0.25em] text-accent">// دسترسی</div>
|
<div class="font-mono text-xs tracking-[0.25em] text-accent">// {{ footer.linksTitle }}</div>
|
||||||
<ul class="mt-5 space-y-3">
|
<ul class="mt-5 space-y-3">
|
||||||
<li v-for="l in QUICK_LINKS" :key="l.href">
|
<li v-for="l in QUICK_LINKS" :key="l.href">
|
||||||
<a :href="l.href" class="text-sm text-muted transition-colors hover:text-ink">
|
<a :href="l.href" class="text-sm text-muted transition-colors hover:text-ink">
|
||||||
@@ -91,7 +91,7 @@ const SOCIALS = computed(() =>
|
|||||||
|
|
||||||
<!-- products -->
|
<!-- products -->
|
||||||
<div class="md:col-span-2">
|
<div class="md:col-span-2">
|
||||||
<div class="font-mono text-xs tracking-[0.25em] text-accent">// محصولات</div>
|
<div class="font-mono text-xs tracking-[0.25em] text-accent">// {{ footer.productsTitle }}</div>
|
||||||
<ul class="mt-5 space-y-3">
|
<ul class="mt-5 space-y-3">
|
||||||
<li v-for="p in PRODUCT_LINKS" :key="p.href">
|
<li v-for="p in PRODUCT_LINKS" :key="p.href">
|
||||||
<NuxtLink :to="p.href" class="text-sm text-muted transition-colors hover:text-ink">
|
<NuxtLink :to="p.href" class="text-sm text-muted transition-colors hover:text-ink">
|
||||||
@@ -103,10 +103,19 @@ const SOCIALS = computed(() =>
|
|||||||
|
|
||||||
<!-- contact -->
|
<!-- contact -->
|
||||||
<div class="md:col-span-3">
|
<div class="md:col-span-3">
|
||||||
<div class="font-mono text-xs tracking-[0.25em] text-accent">// ارتباط</div>
|
<div class="font-mono text-xs tracking-[0.25em] text-accent">// {{ footer.contactTitle }}</div>
|
||||||
<address class="mt-5 space-y-3 text-sm text-muted not-italic">
|
<address class="mt-5 space-y-3 text-sm text-muted not-italic">
|
||||||
<p class="leading-7">
|
<p class="leading-7">
|
||||||
{{ settings.address.locality }}، {{ settings.address.street }}
|
{{ settings.address.locality }}، {{ settings.address.street }}
|
||||||
|
<a
|
||||||
|
v-if="settings.mapUrl"
|
||||||
|
:href="settings.mapUrl"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener"
|
||||||
|
class="ms-2 text-accent transition-colors hover:text-ink"
|
||||||
|
>
|
||||||
|
نقشه
|
||||||
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p class="font-mono tracking-wider" dir="ltr">
|
<p class="font-mono tracking-wider" dir="ltr">
|
||||||
<a :href="`tel:${settings.phone}`" class="transition-colors hover:text-ink">{{ settings.phoneDisplay }}</a>
|
<a :href="`tel:${settings.phone}`" class="transition-colors hover:text-ink">{{ settings.phoneDisplay }}</a>
|
||||||
|
|||||||
@@ -72,15 +72,15 @@ onBeforeUnmount(() => {
|
|||||||
<span class="size-1.5 animate-pulse rounded-full bg-emerald-400" />
|
<span class="size-1.5 animate-pulse rounded-full bg-emerald-400" />
|
||||||
<span class="font-mono">{{ time }}</span>
|
<span class="font-mono">{{ time }}</span>
|
||||||
<span class="text-muted/50">|</span>
|
<span class="text-muted/50">|</span>
|
||||||
<span>تهران — شیفت {{ shift }}</span>
|
<span>{{ settings.address.locality }} — شیفت {{ shift }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/#contact"
|
:href="settings.headerCta.href"
|
||||||
class="hidden items-center gap-2 rounded-full bg-accent px-5 py-2.5 text-sm font-bold text-bg transition-all duration-300 hover:bg-accent-soft hover:shadow-[0_0_30px_rgba(255,90,0,0.4)] md:flex"
|
class="hidden items-center gap-2 rounded-full bg-accent px-5 py-2.5 text-sm font-bold text-bg transition-all duration-300 hover:bg-accent-soft hover:shadow-[0_0_30px_rgba(255,90,0,0.4)] md:flex"
|
||||||
>
|
>
|
||||||
<VuesaxIcon name="call" class="size-4" />
|
<VuesaxIcon name="call" class="size-4" />
|
||||||
درخواست مشاوره
|
{{ settings.headerCta.label }}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
@@ -139,7 +139,7 @@ onBeforeUnmount(() => {
|
|||||||
<VuesaxIcon name="call" class="size-4 text-accent" />
|
<VuesaxIcon name="call" class="size-4 text-accent" />
|
||||||
<a :href="`tel:${settings.phone}`" class="font-mono" dir="ltr">{{ settings.phoneDisplay }}</a>
|
<a :href="`tel:${settings.phone}`" class="font-mono" dir="ltr">{{ settings.phoneDisplay }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs text-muted">تهران — شیفت {{ shift }}، {{ time }}</div>
|
<div class="text-xs text-muted">{{ settings.address.locality }} — شیفت {{ shift }}، {{ time }}</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
|
|||||||
@@ -50,14 +50,14 @@ const step = computed(
|
|||||||
|
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<span
|
<span
|
||||||
class="flex size-12 items-center justify-center rounded-xl bg-white shadow-[0_0_28px_rgba(255,90,0,0.3)] ring-1 ring-white/10"
|
class="flex size-20 items-center justify-center overflow-hidden rounded-2xl bg-white shadow-[0_0_28px_rgba(255,90,0,0.3)] ring-1 ring-white/10"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/logo.png"
|
src="/images/logo.png"
|
||||||
alt="ایمن پیشگامان"
|
alt="ایمن پیشگامان"
|
||||||
width="32"
|
width="80"
|
||||||
height="32"
|
height="80"
|
||||||
class="size-8 object-contain"
|
class="size-full scale-110 object-contain object-center"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="font-mono text-xs tracking-[0.35em] text-muted">
|
<span class="font-mono text-xs tracking-[0.35em] text-muted">
|
||||||
|
|||||||
@@ -12,7 +12,13 @@ const content = computed(() => site.sections.contact);
|
|||||||
const INFO = computed(() => {
|
const INFO = computed(() => {
|
||||||
const s = site.settings;
|
const s = site.settings;
|
||||||
return [
|
return [
|
||||||
{ icon: "location", label: "نشانی", value: `${s.address.locality}، ${s.address.street}` },
|
{
|
||||||
|
icon: "location",
|
||||||
|
label: "نشانی",
|
||||||
|
value: `${s.address.locality}، ${s.address.street}`,
|
||||||
|
href: s.mapUrl || undefined,
|
||||||
|
external: Boolean(s.mapUrl),
|
||||||
|
},
|
||||||
{ icon: "call", label: "تلفن تماس", value: s.phoneDisplay, ltr: true, href: `tel:${s.phone}` },
|
{ icon: "call", label: "تلفن تماس", value: s.phoneDisplay, ltr: true, href: `tel:${s.phone}` },
|
||||||
{ icon: "sms", label: "ایمیل", value: s.email, ltr: true, href: `mailto:${s.email}` },
|
{ icon: "sms", label: "ایمیل", value: s.email, ltr: true, href: `mailto:${s.email}` },
|
||||||
{ icon: "clock", label: "ساعات کاری", value: s.workingHours },
|
{ icon: "clock", label: "ساعات کاری", value: s.workingHours },
|
||||||
@@ -59,6 +65,8 @@ const contact = useContactStore();
|
|||||||
<component
|
<component
|
||||||
:is="item.href ? 'a' : 'div'"
|
:is="item.href ? 'a' : 'div'"
|
||||||
:href="item.href"
|
:href="item.href"
|
||||||
|
:target="item.external ? '_blank' : undefined"
|
||||||
|
:rel="item.external ? 'noopener' : undefined"
|
||||||
:class="`mt-1 block text-sm font-medium ${item.ltr ? 'font-mono tracking-wider' : ''}`"
|
:class="`mt-1 block text-sm font-medium ${item.ltr ? 'font-mono tracking-wider' : ''}`"
|
||||||
:dir="item.ltr ? 'ltr' : 'rtl'"
|
:dir="item.ltr ? 'ltr' : 'rtl'"
|
||||||
>
|
>
|
||||||
@@ -106,7 +114,7 @@ const contact = useContactStore();
|
|||||||
class="mt-7 rounded-full border border-line px-7 py-3 text-sm transition-colors hover:border-accent hover:text-accent"
|
class="mt-7 rounded-full border border-line px-7 py-3 text-sm transition-colors hover:border-accent hover:text-accent"
|
||||||
@click="contact.reset"
|
@click="contact.reset"
|
||||||
>
|
>
|
||||||
ثبت درخواست جدید
|
{{ content.resetLabel }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -172,7 +180,7 @@ const contact = useContactStore();
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<VuesaxIcon name="send-2" class="size-5" />
|
<VuesaxIcon name="send-2" class="size-5" />
|
||||||
ارسال درخواست مشاوره
|
{{ content.submitLabel }}
|
||||||
</template>
|
</template>
|
||||||
</AButton>
|
</AButton>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,23 +1,24 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const settings = computed(() => useSiteStore().settings);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<NuxtLink to="/" class="flex items-center gap-3" aria-label="ایمن پیشگامان">
|
<NuxtLink to="/" class="flex items-center gap-3" :aria-label="settings.name">
|
||||||
<span
|
<span
|
||||||
class="flex size-10 items-center justify-center rounded-xl bg-white shadow-[0_0_28px_rgba(10,30,92,0.45)]"
|
class="flex size-11 items-center justify-center overflow-hidden rounded-xl bg-white p-0.5 shadow-[0_0_28px_rgba(10,30,92,0.45)] sm:size-12"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/images/logo.png"
|
src="/images/logo.png"
|
||||||
alt="ایمن پیشگامان"
|
:alt="settings.name"
|
||||||
width="28"
|
width="48"
|
||||||
height="28"
|
height="48"
|
||||||
class="size-7 object-contain"
|
class="size-full object-contain object-center"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span class="leading-tight">
|
<span class="leading-tight">
|
||||||
<span class="block text-lg font-black">ایمن پیشگامان</span>
|
<span class="block text-lg font-black">{{ settings.name }}</span>
|
||||||
<span class="block font-mono text-[9px] tracking-[0.32em] text-muted">
|
<span class="block font-mono text-[9px] tracking-[0.32em] text-muted">
|
||||||
IMEN PISHGAMAN CO.
|
{{ settings.nameEn }}
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
|
|||||||
+18
-3
@@ -6,6 +6,9 @@
|
|||||||
# gateway (nginx) ─┬─ site domain → web (Nuxt SSR) /api → api /media → minio
|
# gateway (nginx) ─┬─ site domain → web (Nuxt SSR) /api → api /media → minio
|
||||||
# └─ admin domain → admin (static) /api → api /media → minio
|
# └─ admin domain → admin (static) /api → api /media → minio
|
||||||
|
|
||||||
|
# services that other containers reach by name get an imen-* alias: on a shared network (Dokploy) plain
|
||||||
|
# names like web or api would also resolve to other stacks' containers
|
||||||
|
#
|
||||||
# fixed project name, so this stack never shares containers or volumes with docker-compose.dev.yml
|
# fixed project name, so this stack never shares containers or volumes with docker-compose.dev.yml
|
||||||
name: imen
|
name: imen
|
||||||
|
|
||||||
@@ -27,6 +30,9 @@ services:
|
|||||||
|
|
||||||
minio:
|
minio:
|
||||||
image: docker.arvancloud.ir/minio/minio:latest
|
image: docker.arvancloud.ir/minio/minio:latest
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases: [imen-minio]
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: server /data
|
command: server /data
|
||||||
environment:
|
environment:
|
||||||
@@ -51,6 +57,9 @@ services:
|
|||||||
|
|
||||||
api:
|
api:
|
||||||
image: imen-api
|
image: imen-api
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases: [imen-api]
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: docker/api.Dockerfile
|
dockerfile: docker/api.Dockerfile
|
||||||
@@ -67,14 +76,14 @@ services:
|
|||||||
JWT_SECRET: ${JWT_SECRET}
|
JWT_SECRET: ${JWT_SECRET}
|
||||||
COOKIE_SECURE: ${COOKIE_SECURE:-true}
|
COOKIE_SECURE: ${COOKIE_SECURE:-true}
|
||||||
TRUST_PROXY: ${TRUST_PROXY:-2}
|
TRUST_PROXY: ${TRUST_PROXY:-2}
|
||||||
S3_ENDPOINT: http://minio:9000
|
S3_ENDPOINT: http://imen-minio:9000
|
||||||
S3_BUCKET: ${S3_BUCKET:-imen-media}
|
S3_BUCKET: ${S3_BUCKET:-imen-media}
|
||||||
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
|
||||||
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
S3_SECRET_KEY: ${S3_SECRET_KEY}
|
||||||
S3_FORCE_PATH_STYLE: "true"
|
S3_FORCE_PATH_STYLE: "true"
|
||||||
S3_ENSURE_BUCKET: "true"
|
S3_ENSURE_BUCKET: "true"
|
||||||
MEDIA_PUBLIC_URL: /media
|
MEDIA_PUBLIC_URL: /media
|
||||||
WEB_REVALIDATE_URL: http://web:3000/_internal/revalidate
|
WEB_REVALIDATE_URL: http://imen-web:3000/_internal/revalidate
|
||||||
REVALIDATE_SECRET: ${REVALIDATE_SECRET}
|
REVALIDATE_SECRET: ${REVALIDATE_SECRET}
|
||||||
ADMIN_PHONE: ${ADMIN_PHONE}
|
ADMIN_PHONE: ${ADMIN_PHONE}
|
||||||
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
ADMIN_PASSWORD: ${ADMIN_PASSWORD}
|
||||||
@@ -82,6 +91,9 @@ services:
|
|||||||
|
|
||||||
web:
|
web:
|
||||||
image: imen-web
|
image: imen-web
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases: [imen-web]
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: docker/web.Dockerfile
|
dockerfile: docker/web.Dockerfile
|
||||||
@@ -89,12 +101,15 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- api
|
- api
|
||||||
environment:
|
environment:
|
||||||
NUXT_API_INTERNAL_URL: http://api:4000/api
|
NUXT_API_INTERNAL_URL: http://imen-api:4000/api
|
||||||
NUXT_REVALIDATE_SECRET: ${REVALIDATE_SECRET}
|
NUXT_REVALIDATE_SECRET: ${REVALIDATE_SECRET}
|
||||||
NUXT_PUBLIC_SITE_URL: https://${SITE_DOMAIN}
|
NUXT_PUBLIC_SITE_URL: https://${SITE_DOMAIN}
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
image: imen-admin
|
image: imen-admin
|
||||||
|
networks:
|
||||||
|
default:
|
||||||
|
aliases: [imen-admin]
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: docker/admin.Dockerfile
|
dockerfile: docker/admin.Dockerfile
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
# (only ${SITE_DOMAIN}, ${ADMIN_DOMAIN} and ${S3_BUCKET} are replaced; nginx's own $vars stay).
|
# (only ${SITE_DOMAIN}, ${ADMIN_DOMAIN} and ${S3_BUCKET} are replaced; nginx's own $vars stay).
|
||||||
# TLS is expected to be terminated in front of this container (host nginx / Traefik / CDN).
|
# TLS is expected to be terminated in front of this container (host nginx / Traefik / CDN).
|
||||||
|
|
||||||
upstream web_upstream { server web:3000; }
|
upstream web_upstream { server imen-web:3000; }
|
||||||
upstream api_upstream { server api:4000; }
|
upstream api_upstream { server imen-api:4000; }
|
||||||
upstream admin_upstream { server admin:80; }
|
upstream admin_upstream { server imen-admin:80; }
|
||||||
upstream media_upstream { server minio:9000; }
|
upstream media_upstream { server imen-minio:9000; }
|
||||||
|
|
||||||
# keep the scheme reported by the TLS proxy in front, if any
|
# keep the scheme reported by the TLS proxy in front, if any
|
||||||
map $http_x_forwarded_proto $forwarded_proto {
|
map $http_x_forwarded_proto $forwarded_proto {
|
||||||
|
|||||||
Vendored
+20
@@ -28,6 +28,7 @@ export declare const settingsSectionSchema: z.ZodObject<{
|
|||||||
locality: z.ZodString;
|
locality: z.ZodString;
|
||||||
country: z.ZodDefault<z.ZodString>;
|
country: z.ZodDefault<z.ZodString>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
|
mapUrl: z.ZodDefault<z.ZodString>;
|
||||||
workingHours: z.ZodString;
|
workingHours: z.ZodString;
|
||||||
openingHours: z.ZodObject<{
|
openingHours: z.ZodObject<{
|
||||||
days: z.ZodArray<z.ZodEnum<{
|
days: z.ZodArray<z.ZodEnum<{
|
||||||
@@ -43,6 +44,10 @@ export declare const settingsSectionSchema: z.ZodObject<{
|
|||||||
closes: z.ZodString;
|
closes: z.ZodString;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
foundingDate: z.ZodString;
|
foundingDate: z.ZodString;
|
||||||
|
headerCta: z.ZodDefault<z.ZodObject<{
|
||||||
|
label: z.ZodString;
|
||||||
|
href: z.ZodString;
|
||||||
|
}, z.core.$strip>>;
|
||||||
socials: z.ZodArray<z.ZodObject<{
|
socials: z.ZodArray<z.ZodObject<{
|
||||||
type: z.ZodEnum<{
|
type: z.ZodEnum<{
|
||||||
instagram: "instagram";
|
instagram: "instagram";
|
||||||
@@ -224,6 +229,8 @@ export declare const contactSectionSchema: z.ZodObject<{
|
|||||||
formTitle: z.ZodString;
|
formTitle: z.ZodString;
|
||||||
successTitle: z.ZodString;
|
successTitle: z.ZodString;
|
||||||
successText: z.ZodString;
|
successText: z.ZodString;
|
||||||
|
submitLabel: z.ZodDefault<z.ZodString>;
|
||||||
|
resetLabel: z.ZodDefault<z.ZodString>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
export declare const footerSectionSchema: z.ZodObject<{
|
export declare const footerSectionSchema: z.ZodObject<{
|
||||||
enabled: z.ZodDefault<z.ZodBoolean>;
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
||||||
@@ -231,6 +238,9 @@ export declare const footerSectionSchema: z.ZodObject<{
|
|||||||
copyright: z.ZodString;
|
copyright: z.ZodString;
|
||||||
certLine: z.ZodDefault<z.ZodString>;
|
certLine: z.ZodDefault<z.ZodString>;
|
||||||
emergencyNote: z.ZodDefault<z.ZodString>;
|
emergencyNote: z.ZodDefault<z.ZodString>;
|
||||||
|
linksTitle: z.ZodDefault<z.ZodString>;
|
||||||
|
productsTitle: z.ZodDefault<z.ZodString>;
|
||||||
|
contactTitle: z.ZodDefault<z.ZodString>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
export declare const sectionSchemas: {
|
export declare const sectionSchemas: {
|
||||||
readonly settings: z.ZodObject<{
|
readonly settings: z.ZodObject<{
|
||||||
@@ -246,6 +256,7 @@ export declare const sectionSchemas: {
|
|||||||
locality: z.ZodString;
|
locality: z.ZodString;
|
||||||
country: z.ZodDefault<z.ZodString>;
|
country: z.ZodDefault<z.ZodString>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
|
mapUrl: z.ZodDefault<z.ZodString>;
|
||||||
workingHours: z.ZodString;
|
workingHours: z.ZodString;
|
||||||
openingHours: z.ZodObject<{
|
openingHours: z.ZodObject<{
|
||||||
days: z.ZodArray<z.ZodEnum<{
|
days: z.ZodArray<z.ZodEnum<{
|
||||||
@@ -261,6 +272,10 @@ export declare const sectionSchemas: {
|
|||||||
closes: z.ZodString;
|
closes: z.ZodString;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
foundingDate: z.ZodString;
|
foundingDate: z.ZodString;
|
||||||
|
headerCta: z.ZodDefault<z.ZodObject<{
|
||||||
|
label: z.ZodString;
|
||||||
|
href: z.ZodString;
|
||||||
|
}, z.core.$strip>>;
|
||||||
socials: z.ZodArray<z.ZodObject<{
|
socials: z.ZodArray<z.ZodObject<{
|
||||||
type: z.ZodEnum<{
|
type: z.ZodEnum<{
|
||||||
instagram: "instagram";
|
instagram: "instagram";
|
||||||
@@ -442,6 +457,8 @@ export declare const sectionSchemas: {
|
|||||||
formTitle: z.ZodString;
|
formTitle: z.ZodString;
|
||||||
successTitle: z.ZodString;
|
successTitle: z.ZodString;
|
||||||
successText: z.ZodString;
|
successText: z.ZodString;
|
||||||
|
submitLabel: z.ZodDefault<z.ZodString>;
|
||||||
|
resetLabel: z.ZodDefault<z.ZodString>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
readonly footer: z.ZodObject<{
|
readonly footer: z.ZodObject<{
|
||||||
enabled: z.ZodDefault<z.ZodBoolean>;
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
||||||
@@ -449,6 +466,9 @@ export declare const sectionSchemas: {
|
|||||||
copyright: z.ZodString;
|
copyright: z.ZodString;
|
||||||
certLine: z.ZodDefault<z.ZodString>;
|
certLine: z.ZodDefault<z.ZodString>;
|
||||||
emergencyNote: z.ZodDefault<z.ZodString>;
|
emergencyNote: z.ZodDefault<z.ZodString>;
|
||||||
|
linksTitle: z.ZodDefault<z.ZodString>;
|
||||||
|
productsTitle: z.ZodDefault<z.ZodString>;
|
||||||
|
contactTitle: z.ZodDefault<z.ZodString>;
|
||||||
}, z.core.$strip>;
|
}, z.core.$strip>;
|
||||||
};
|
};
|
||||||
export type SectionKey = keyof typeof sectionSchemas;
|
export type SectionKey = keyof typeof sectionSchemas;
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
{"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../src/sections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;GAKG;AAEH,eAAO,MAAM,SAAS,yFAQZ,CAAC;AAEX,eAAO,MAAM,YAAY,+HAYf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAKvD,eAAO,MAAM,iBAAiB;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEhE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BhC,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;iBAG5B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;iBAY7B,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;iBAKrC,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;iBAQ7B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;iBAahC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;iBAMjC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;iBAalC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAIpC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;iBAS/B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;iBAM9B,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAajB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,cAAc,CAAC;AACrD,eAAO,MAAM,YAAY,EAAkC,UAAU,EAAE,CAAC;AACxE,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpF,MAAM,MAAM,YAAY,GAAG;KAAG,CAAC,IAAI,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC;CAAE,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;AAEnD,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,GAAG,IAAI,UAAmC,CAAC;AAEtF,yCAAyC;AACzC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,UAAU,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAa9E,CAAC"}
|
{"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../src/sections.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;GAKG;AAEH,eAAO,MAAM,SAAS,yFAQZ,CAAC;AAEX,eAAO,MAAM,YAAY,+HAYf,CAAC;AACX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAKvD,eAAO,MAAM,iBAAiB;;;;;iBAM5B,CAAC;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAEhE,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAiChC,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAa5B,CAAC;AAEH,eAAO,MAAM,iBAAiB;;;iBAG5B,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;iBAY7B,CAAC;AAEH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;iBAKrC,CAAC;AAEH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;iBAQ7B,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;iBAahC,CAAC;AAEH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;iBAMjC,CAAC;AAEH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;iBAalC,CAAC;AAEH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;iBAIpC,CAAC;AAEH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;iBAa/B,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;iBAU9B,CAAC;AAEH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAajB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,MAAM,OAAO,cAAc,CAAC;AACrD,eAAO,MAAM,YAAY,EAAkC,UAAU,EAAE,CAAC;AACxE,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,UAAU,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACpF,MAAM,MAAM,YAAY,GAAG;KAAG,CAAC,IAAI,UAAU,GAAG,WAAW,CAAC,CAAC,CAAC;CAAE,CAAC;AACjE,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,UAAU,CAAC,CAAC;AAEnD,eAAO,MAAM,YAAY,GAAI,KAAK,MAAM,KAAG,GAAG,IAAI,UAAmC,CAAC;AAEtF,yCAAyC;AACzC,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,UAAU,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAa9E,CAAC"}
|
||||||
Vendored
+14
-2
@@ -53,6 +53,8 @@ export const settingsSectionSchema = z.object({
|
|||||||
locality: req(80),
|
locality: req(80),
|
||||||
country: z.string().trim().length(2).default("IR"),
|
country: z.string().trim().length(2).default("IR"),
|
||||||
}),
|
}),
|
||||||
|
/** Google Maps / Neshan / etc. share link shown next to the address */
|
||||||
|
mapUrl: text(500),
|
||||||
/** human-readable working hours, e.g. «شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰» */
|
/** human-readable working hours, e.g. «شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰» */
|
||||||
workingHours: req(120),
|
workingHours: req(120),
|
||||||
openingHours: z.object({
|
openingHours: z.object({
|
||||||
@@ -61,6 +63,8 @@ export const settingsSectionSchema = z.object({
|
|||||||
closes: z.string().regex(/^\d{2}:\d{2}$/, "قالب ساعت باید HH:MM باشد"),
|
closes: z.string().regex(/^\d{2}:\d{2}$/, "قالب ساعت باید HH:MM باشد"),
|
||||||
}),
|
}),
|
||||||
foundingDate: z.string().regex(/^\d{4}$/, "سال میلادی چهاررقمی"),
|
foundingDate: z.string().regex(/^\d{4}$/, "سال میلادی چهاررقمی"),
|
||||||
|
/** sticky header CTA (e.g. «درخواست مشاوره») */
|
||||||
|
headerCta: linkSchema.default({ label: "درخواست مشاوره", href: "/#contact" }),
|
||||||
socials: z.array(z.object({ type: z.enum(SOCIAL_TYPES), url: req(300) })).max(12),
|
socials: z.array(z.object({ type: z.enum(SOCIAL_TYPES), url: req(300) })).max(12),
|
||||||
ogImage: imageRefSchema.extend({
|
ogImage: imageRefSchema.extend({
|
||||||
width: z.number().int().positive().nullable().default(null),
|
width: z.number().int().positive().nullable().default(null),
|
||||||
@@ -156,6 +160,10 @@ export const contactSectionSchema = z.object({
|
|||||||
formTitle: req(160),
|
formTitle: req(160),
|
||||||
successTitle: req(80),
|
successTitle: req(80),
|
||||||
successText: req(300),
|
successText: req(300),
|
||||||
|
/** submit button on the contact form */
|
||||||
|
submitLabel: req(80).default("ارسال درخواست مشاوره"),
|
||||||
|
/** button shown after a successful submit */
|
||||||
|
resetLabel: req(80).default("ثبت درخواست جدید"),
|
||||||
});
|
});
|
||||||
export const footerSectionSchema = z.object({
|
export const footerSectionSchema = z.object({
|
||||||
enabled: z.boolean().default(true),
|
enabled: z.boolean().default(true),
|
||||||
@@ -163,6 +171,10 @@ export const footerSectionSchema = z.object({
|
|||||||
copyright: req(160),
|
copyright: req(160),
|
||||||
certLine: text(160),
|
certLine: text(160),
|
||||||
emergencyNote: text(80),
|
emergencyNote: text(80),
|
||||||
|
/** column headings under the brand block */
|
||||||
|
linksTitle: req(40).default("دسترسی"),
|
||||||
|
productsTitle: req(40).default("محصولات"),
|
||||||
|
contactTitle: req(40).default("ارتباط"),
|
||||||
});
|
});
|
||||||
export const sectionSchemas = {
|
export const sectionSchemas = {
|
||||||
settings: settingsSectionSchema,
|
settings: settingsSectionSchema,
|
||||||
@@ -192,7 +204,7 @@ export const SECTION_LABELS = {
|
|||||||
standards: { title: "استانداردها", desc: "استانداردها و گواهینامهها" },
|
standards: { title: "استانداردها", desc: "استانداردها و گواهینامهها" },
|
||||||
industries: { title: "صنایع", desc: "صنایع تحت پوشش و تصویر هرکدام" },
|
industries: { title: "صنایع", desc: "صنایع تحت پوشش و تصویر هرکدام" },
|
||||||
testimonials: { title: "نظرات مشتریان", desc: "نقلقولهای مشتریان" },
|
testimonials: { title: "نظرات مشتریان", desc: "نقلقولهای مشتریان" },
|
||||||
contact: { title: "تماس با ما", desc: "عنوانها و متنهای فرم تماس" },
|
contact: { title: "تماس با ما", desc: "عنوانها، متنهای فرم، و ویرایش نشانی و اطلاعات تماس" },
|
||||||
footer: { title: "فوتر", desc: "متن معرفی، کپیرایت و توضیحات پایین صفحه" },
|
footer: { title: "فوتر", desc: "متن معرفی، عناوین ستونها، کپیرایت و توضیحات پایین صفحه" },
|
||||||
};
|
};
|
||||||
//# sourceMappingURL=sections.js.map
|
//# sourceMappingURL=sections.js.map
|
||||||
Vendored
+1
-1
File diff suppressed because one or more lines are too long
@@ -61,6 +61,8 @@ export const settingsSectionSchema = z.object({
|
|||||||
locality: req(80),
|
locality: req(80),
|
||||||
country: z.string().trim().length(2).default("IR"),
|
country: z.string().trim().length(2).default("IR"),
|
||||||
}),
|
}),
|
||||||
|
/** Google Maps / Neshan / etc. share link shown next to the address */
|
||||||
|
mapUrl: text(500),
|
||||||
/** human-readable working hours, e.g. «شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰» */
|
/** human-readable working hours, e.g. «شنبه تا پنجشنبه — ۸:۰۰ تا ۱۷:۰۰» */
|
||||||
workingHours: req(120),
|
workingHours: req(120),
|
||||||
openingHours: z.object({
|
openingHours: z.object({
|
||||||
@@ -69,6 +71,8 @@ export const settingsSectionSchema = z.object({
|
|||||||
closes: z.string().regex(/^\d{2}:\d{2}$/, "قالب ساعت باید HH:MM باشد"),
|
closes: z.string().regex(/^\d{2}:\d{2}$/, "قالب ساعت باید HH:MM باشد"),
|
||||||
}),
|
}),
|
||||||
foundingDate: z.string().regex(/^\d{4}$/, "سال میلادی چهاررقمی"),
|
foundingDate: z.string().regex(/^\d{4}$/, "سال میلادی چهاررقمی"),
|
||||||
|
/** sticky header CTA (e.g. «درخواست مشاوره») */
|
||||||
|
headerCta: linkSchema.default({ label: "درخواست مشاوره", href: "/#contact" }),
|
||||||
socials: z.array(z.object({ type: z.enum(SOCIAL_TYPES), url: req(300) })).max(12),
|
socials: z.array(z.object({ type: z.enum(SOCIAL_TYPES), url: req(300) })).max(12),
|
||||||
ogImage: imageRefSchema.extend({
|
ogImage: imageRefSchema.extend({
|
||||||
width: z.number().int().positive().nullable().default(null),
|
width: z.number().int().positive().nullable().default(null),
|
||||||
@@ -180,6 +184,10 @@ export const contactSectionSchema = z.object({
|
|||||||
formTitle: req(160),
|
formTitle: req(160),
|
||||||
successTitle: req(80),
|
successTitle: req(80),
|
||||||
successText: req(300),
|
successText: req(300),
|
||||||
|
/** submit button on the contact form */
|
||||||
|
submitLabel: req(80).default("ارسال درخواست مشاوره"),
|
||||||
|
/** button shown after a successful submit */
|
||||||
|
resetLabel: req(80).default("ثبت درخواست جدید"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const footerSectionSchema = z.object({
|
export const footerSectionSchema = z.object({
|
||||||
@@ -188,6 +196,10 @@ export const footerSectionSchema = z.object({
|
|||||||
copyright: req(160),
|
copyright: req(160),
|
||||||
certLine: text(160),
|
certLine: text(160),
|
||||||
emergencyNote: text(80),
|
emergencyNote: text(80),
|
||||||
|
/** column headings under the brand block */
|
||||||
|
linksTitle: req(40).default("دسترسی"),
|
||||||
|
productsTitle: req(40).default("محصولات"),
|
||||||
|
contactTitle: req(40).default("ارتباط"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const sectionSchemas = {
|
export const sectionSchemas = {
|
||||||
@@ -225,6 +237,6 @@ export const SECTION_LABELS: Record<SectionKey, { title: string; desc: string }>
|
|||||||
standards: { title: "استانداردها", desc: "استانداردها و گواهینامهها" },
|
standards: { title: "استانداردها", desc: "استانداردها و گواهینامهها" },
|
||||||
industries: { title: "صنایع", desc: "صنایع تحت پوشش و تصویر هرکدام" },
|
industries: { title: "صنایع", desc: "صنایع تحت پوشش و تصویر هرکدام" },
|
||||||
testimonials: { title: "نظرات مشتریان", desc: "نقلقولهای مشتریان" },
|
testimonials: { title: "نظرات مشتریان", desc: "نقلقولهای مشتریان" },
|
||||||
contact: { title: "تماس با ما", desc: "عنوانها و متنهای فرم تماس" },
|
contact: { title: "تماس با ما", desc: "عنوانها، متنهای فرم، و ویرایش نشانی و اطلاعات تماس" },
|
||||||
footer: { title: "فوتر", desc: "متن معرفی، کپیرایت و توضیحات پایین صفحه" },
|
footer: { title: "فوتر", desc: "متن معرفی، عناوین ستونها، کپیرایت و توضیحات پایین صفحه" },
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user