- API: `pnpm build` bundles dist/ into apps/api/.output (esbuild), with Linux musl native packages for sharp and argon2 kept alongside - docker/api.Dockerfile copies that output; no pnpm install on the server - add dist/migrate.js (prisma migrate deploy equivalent, same _prisma_migrations table and lock) so the Prisma CLI is not needed at runtime - pnpm-workspace: supportedArchitectures for linux/x64/musl - admin: apps/admin/.env.production sets VITE_SITE_URL for the production build - drop NPM_REGISTRY / SITE_URL build args from compose and .env.example - gitignore: anchor node_modules rules so pre-built node_modules are committed - README: describe the pre-built deploy flow Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
6 lines
16 KiB
JavaScript
6 lines
16 KiB
JavaScript
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(`
|
||
`).map(e=>e.trim()).filter(Boolean),r=a(t.value.join(`
|
||
`));s(t,e=>{n(r.value).join(`
|
||
`)!==e.join(`
|
||
`)&&(r.value=e.join(`
|
||
`))});let i=()=>{t.value=n(r.value)};return(t,n)=>{let a=x(`a-textarea`);return u(),h(a,{value:r.value,"onUpdate:value":n[0]||=e=>r.value=e,placeholder:e.placeholder,"auto-size":{minRows:e.rows??3,maxRows:14},onChange:i},null,8,[`value`,`placeholder`,`auto-size`])}}}),le={class:`!hidden lg:!flex items-center gap-2`},ue=[`href`],de={class:`mobile-edit-tabs lg:!hidden flex items-center gap-1.5 overflow-x-auto pb-2.5 mb-3 sticky top-[52px] z-20 bg-white/95 backdrop-blur-md -mx-3 px-3 pt-1 border-b border-slate-100 no-scrollbar`},fe=[`onClick`],pe={class:`grid gap-6 xl:grid-cols-3`},me={class:`space-y-6 xl:col-span-2`},he={class:`grid gap-x-4 md:grid-cols-2`},ge={class:`grid gap-x-4 md:grid-cols-5`},_e={class:`mt-6 grid gap-x-4 md:grid-cols-2`},k={class:`space-y-6`},A={class:`ms-2`},j={class:`mb-1.5 flex items-center justify-between`},M={key:0,class:`text-[11px] text-orange-600 font-mono font-bold`},N={class:`flex flex-wrap gap-2.5`},P=[`src`],F=[`onClick`],I={class:`fixed bottom-0 start-0 end-0 z-30 bg-white/95 backdrop-blur-md border-t border-slate-200/80 p-3 shadow-lg flex items-center justify-between gap-3 lg:!hidden`},ve={class:`flex items-center gap-2`},ye={class:`flex items-center gap-2`},be=[`href`],L=d({__name:`ProductEditPage`,setup(d){let p=f(),h=ee(),b=S(()=>p.params.id?Number(p.params.id):null),L=()=>({categoryId:Number(p.query.categoryId)||void 0,groupId:null,code:``,slug:``,name:``,nameEn:``,desc:``,specs:[],variants:[],details:``,brand:``,origin:``,sizes:[],standards:[],features:[],applications:[],attributes:[],packaging:``,minOrder:``,seoTitle:``,seoDescription:``,imageId:null,galleryIds:[],isPublished:!0}),{model:R,validate:xe,handleError:Se,reset:z}=re(t,L()),B=a(null),V=a([]),H=a(!1),U=a([]),W=a([]),G=a(!1),K=a(!1),q=a(`general`),Ce=[{key:`general`,label:`اطلاعات اصلی`},{key:`specs`,label:`مشخصات فنی`},{key:`desc`,label:`توضیحات و کاربرد`},{key:`media`,label:`تصاویر و سفارش`},{key:`seo`,label:`سئو و انتشار`}],J=S(()=>U.value.find(e=>e.id===R.value.categoryId)),Y=S({get:()=>R.value.groupId??void 0,set:e=>R.value.groupId=e??null}),we=()=>({name:``,value:``});async function X(e){e&&(R.value.groupId=null),W.value=R.value.categoryId?(await C(`/admin/categories/${R.value.categoryId}`)).groups:[]}async function Z(){G.value=!0;try{if(U.value=await C(`/admin/categories`),b.value){let e=await C(`/admin/products/${b.value}`);z({categoryId:e.categoryId,groupId:e.groupId,code:e.code,slug:e.slug,name:e.name,nameEn:e.nameEn,desc:e.desc,specs:e.specs,variants:e.variants,details:e.details,brand:e.brand,origin:e.origin,sizes:e.sizes,standards:e.standards,features:e.features,applications:e.applications,attributes:e.attributes??[],packaging:e.packaging,minOrder:e.minOrder,seoTitle:e.seoTitle,seoDescription:e.seoDescription,imageId:e.imageId,galleryIds:e.gallery.map(e=>e.id),isPublished:e.isPublished}),B.value=e.image,V.value=e.gallery}else z(L()),B.value=null,V.value=[];await X(!1)}catch(e){w.error(y(e))}finally{G.value=!1}}function Q(e){B.value=e,R.value.imageId=e.id}function Te(e){V.value.some(t=>t.id===e.id)||(V.value=[...V.value,e])}function Ee(e){V.value=V.value.filter(t=>t.id!==e)}s(V,e=>R.value.galleryIds=e.map(e=>e.id));async function $(){let e=xe();if(e){K.value=!0;try{let t=await C(b.value?`/admin/products/${b.value}`:`/admin/products`,{method:b.value?`PUT`:`POST`,body:e});w.success(`محصول ذخیره شد`),b.value?await Z():await h.replace({name:`product-edit`,params:{id:t.id}})}catch(e){Se(e)}finally{K.value=!1}}}return s(b,Z),r(Z),(t,r)=>{let a=x(`a-button`),s=x(`a-input`),d=x(`a-textarea`),f=x(`a-select`),p=x(`a-card`),h=x(`a-switch`),y=x(`a-form`),S=x(`a-spin`);return u(),T(m,null,[_(ne,{title:b.value?`ویرایش محصول: ${o(R).name}`:`محصول جدید`,back:`/products`},{default:c(()=>[E(`div`,le,[b.value&&J.value?.isPublished&&o(R).isPublished?(u(),T(`a`,{key:0,href:`${o(D)}/products/${o(R).slug}`,target:`_blank`,rel:`noopener`},[_(a,null,{default:c(()=>[...r[28]||=[v(`مشاهده در سایت`,-1)]]),_:1})],8,ue)):g(``,!0),_(a,{type:`primary`,loading:K.value,onClick:$},{icon:c(()=>[_(e,{name:`save-2`,size:16})]),default:c(()=>[r[29]||=v(` ذخیره `,-1)]),_:1},8,[`loading`])])]),_:1},8,[`title`]),_(S,{spinning:G.value},{default:c(()=>[_(y,{layout:`vertical`,class:`pb-24 lg:pb-6`,onSubmit:te($,[`prevent`])},{default:c(()=>[E(`div`,de,[(u(),T(m,null,i(Ce,e=>E(`button`,{key:e.key,type:`button`,class:l([`px-3 py-1.5 rounded-full text-xs font-bold transition shrink-0 select-none cursor-pointer`,q.value===e.key?`bg-orange-600 text-white shadow-xs`:`bg-slate-100 text-slate-600 active:bg-slate-200`]),onClick:t=>q.value=e.key},n(e.label),11,fe)),64))]),E(`div`,pe,[E(`div`,me,[_(p,{title:`اطلاعات محصول`,class:l(q.value!==`general`&&`max-lg:hidden`)},{default:c(()=>[E(`div`,he,[_(O,{path:`name`,label:`نام محصول`,required:``},{default:c(()=>[_(s,{value:o(R).name,"onUpdate:value":r[0]||=e=>o(R).name=e},null,8,[`value`])]),_:1}),_(O,{path:`nameEn`,label:`نام انگلیسی`},{default:c(()=>[_(s,{value:o(R).nameEn,"onUpdate:value":r[1]||=e=>o(R).nameEn=e,class:`ltr-input`},null,8,[`value`])]),_:1}),_(O,{path:`code`,label:`کد محصول`,required:``,hint:`یکتا، مثلاً IP-G20`},{default:c(()=>[_(s,{value:o(R).code,"onUpdate:value":r[2]||=e=>o(R).code=e,class:`ltr-input`},null,8,[`value`])]),_:1}),_(O,{path:`slug`,label:`اسلاگ (آدرس صفحه)`,required:``,hint:`آدرس: /products/${o(R).slug||`...`}`},{default:c(()=>[_(s,{value:o(R).slug,"onUpdate:value":r[4]||=e=>o(R).slug=e,class:`ltr-input`},{suffix:c(()=>[E(`a`,{class:`text-xs`,onClick:r[3]||=e=>o(R).slug=o(se)(o(R).code||o(R).nameEn||``)},`ساخت از کد`)]),_:1},8,[`value`])]),_:1},8,[`hint`]),_(O,{path:`desc`,label:`توضیح کوتاه`,hint:`زیر عنوان صفحه محصول و روی کارت محصول نمایش داده میشود`,class:`md:col-span-2`},{default:c(()=>[_(d,{value:o(R).desc,"onUpdate:value":r[5]||=e=>o(R).desc=e,"auto-size":{minRows:2,maxRows:5}},null,8,[`value`])]),_:1}),_(O,{path:`specs`,label:`برچسبهای کوتاه`,hint:`ویژگیهای یککلمهای روی کارت؛ هر مورد را بنویسید و Enter بزنید`,class:`md:col-span-2`},{default:c(()=>[_(f,{value:o(R).specs,"onUpdate:value":r[6]||=e=>o(R).specs=e,mode:`tags`,open:!1,placeholder:`مثلاً: ضد برش`},null,8,[`value`])]),_:1})])]),_:1},8,[`class`]),_(p,{title:`توضیحات کامل (صفحه محصول)`,class:l(q.value!==`desc`&&`max-lg:hidden`)},{default:c(()=>[_(O,{path:`details`,hint:`برای شروع پاراگراف جدید یک خط خالی بگذارید. اگر خالی بماند، توضیح دسته نمایش داده میشود.`},{default:c(()=>[_(d,{value:o(R).details,"onUpdate:value":r[7]||=e=>o(R).details=e,"auto-size":{minRows:6,maxRows:20},placeholder:`معرفی محصول، جنس و ساختار، مزایا، نکات نگهداری و ...`},null,8,[`value`])]),_:1})]),_:1},8,[`class`]),_(p,{title:`ویژگیها و کاربردها`,class:l(q.value!==`desc`&&`max-lg:hidden`)},{default:c(()=>[_(O,{path:`features`,label:`ویژگیهای کلیدی`,hint:`هر ویژگی در یک خط`},{default:c(()=>[_(ce,{modelValue:o(R).features,"onUpdate:modelValue":r[8]||=e=>o(R).features=e,rows:4,placeholder:`مثلاً: زیره ضد لغزش و مقاوم در برابر روغن`},null,8,[`modelValue`])]),_:1}),_(O,{path:`applications`,label:`کاربردها / محیطهای مناسب`,hint:`هر مورد را بنویسید و Enter بزنید`},{default:c(()=>[_(f,{value:o(R).applications,"onUpdate:value":r[9]||=e=>o(R).applications=e,mode:`tags`,open:!1,placeholder:`مثلاً: پالایشگاه`},null,8,[`value`])]),_:1})]),_:1},8,[`class`]),_(p,{title:`مشخصات فنی`,"body-style":{background:`#fafafa`},class:l(q.value!==`specs`&&`max-lg:hidden`)},{default:c(()=>[r[30]||=E(`p`,{class:`mb-4 text-sm text-gray-500`},` ردیفهای جدول مشخصات صفحه محصول (مثلاً «جنس رویه» ← «چرم طبیعی»). کد، دسته، برند و موارد زیر بهصورت خودکار به جدول اضافه میشوند. `,-1),_(oe,{modelValue:o(R).attributes,"onUpdate:modelValue":r[10]||=e=>o(R).attributes=e,create:we,max:40,"add-label":`افزودن ردیف مشخصات`,"item-title":e=>e.name},{default:c(({item:e,index:t})=>[E(`div`,ge,[_(O,{path:`attributes.${t}.name`,label:`عنوان`,required:``,class:`md:col-span-2`},{default:c(()=>[_(s,{value:e.name,"onUpdate:value":t=>e.name=t,placeholder:`مثلاً: جنس رویه`},null,8,[`value`,`onUpdate:value`])]),_:2},1032,[`path`]),_(O,{path:`attributes.${t}.value`,label:`مقدار`,required:``,class:`md:col-span-3`},{default:c(()=>[_(s,{value:e.value,"onUpdate:value":t=>e.value=t,placeholder:`مثلاً: چرم طبیعی گاوی`},null,8,[`value`,`onUpdate:value`])]),_:2},1032,[`path`])])]),_:1},8,[`modelValue`,`item-title`]),E(`div`,_e,[_(O,{path:`standards`,label:`استانداردها و گواهیها`,hint:`مثلاً EN 388 — هر مورد + Enter`},{default:c(()=>[_(f,{value:o(R).standards,"onUpdate:value":r[11]||=e=>o(R).standards=e,mode:`tags`,open:!1,class:`ltr-input`},null,8,[`value`])]),_:1}),_(O,{path:`sizes`,label:`سایزها`,hint:`مثلاً 40، 41، XL — هر مورد + Enter`},{default:c(()=>[_(f,{value:o(R).sizes,"onUpdate:value":r[12]||=e=>o(R).sizes=e,mode:`tags`,open:!1},null,8,[`value`])]),_:1}),_(O,{path:`variants`,label:`مدلها / رنگها`,hint:`هر مورد + Enter`,class:`md:col-span-2`},{default:c(()=>[_(f,{value:o(R).variants,"onUpdate:value":r[13]||=e=>o(R).variants=e,mode:`tags`,open:!1,placeholder:`مثلاً: مشکی/زرد`},null,8,[`value`])]),_:1})])]),_:1},8,[`class`])]),E(`div`,k,[_(p,{title:`انتشار`,class:l(q.value!==`seo`&&`max-lg:hidden`)},{default:c(()=>[_(O,{path:`isPublished`},{default:c(()=>[_(h,{checked:o(R).isPublished,"onUpdate:checked":r[14]||=e=>o(R).isPublished=e},null,8,[`checked`]),E(`span`,A,n(o(R).isPublished?`در سایت نمایش داده میشود`:`پیشنویس (مخفی)`),1)]),_:1})]),_:1},8,[`class`]),_(p,{title:`دستهبندی`,class:l(q.value!==`general`&&`max-lg:hidden`)},{default:c(()=>[_(O,{path:`categoryId`,label:`دسته`,required:``},{default:c(()=>[_(f,{value:o(R).categoryId,"onUpdate:value":r[15]||=e=>o(R).categoryId=e,placeholder:`انتخاب دسته`,options:U.value.map(e=>({value:e.id,label:e.name})),onChange:r[16]||=e=>X(!0)},null,8,[`value`,`options`])]),_:1}),_(O,{path:`groupId`,label:`زیرگروه`},{default:c(()=>[_(f,{value:Y.value,"onUpdate:value":r[17]||=e=>Y.value=e,"allow-clear":``,disabled:!W.value.length,placeholder:W.value.length?`بدون زیرگروه`:`این دسته زیرگروه ندارد`,options:W.value.map(e=>({value:e.id,label:e.name}))},null,8,[`value`,`disabled`,`placeholder`,`options`])]),_:1})]),_:1},8,[`class`]),_(p,{title:`تصاویر محصول`,class:l(q.value!==`media`&&`max-lg:hidden`)},{default:c(()=>[E(`div`,null,[r[31]||=E(`div`,{class:`mb-2 flex items-center justify-between`},[E(`span`,{class:`text-xs font-bold text-slate-700`},`تصویر اصلی (کاور)`),E(`span`,{class:`text-[11px] text-slate-400`},`نمایش در لیستها و کارتها`)],-1),_(O,{path:`imageId`,hint:`در صورت عدم انتخاب، کاور دستهبندی استفاده میشود`},{default:c(()=>[_(ie,{url:B.value?.url,clearable:``,onPick:Q,onClear:r[18]||=e=>{B.value=null,o(R).imageId=null}},null,8,[`url`])]),_:1})]),r[35]||=E(`div`,{class:`my-5 border-t border-slate-100`},null,-1),E(`div`,null,[E(`div`,j,[r[32]||=E(`span`,{class:`text-xs font-bold text-slate-700`},`گالری تصاویر بیشتر`,-1),V.value.length?(u(),T(`span`,M,n(V.value.length)+` تصویر`,1)):g(``,!0)]),r[34]||=E(`p`,{class:`mb-3 text-xs text-slate-400`},`تصاویری که در اسلایدر جزئیات محصول نمایش داده میشوند.`,-1),E(`div`,N,[(u(!0),T(m,null,i(V.value,t=>(u(),T(`div`,{key:t.id,class:`relative size-20 overflow-hidden rounded-lg border border-slate-200 bg-slate-50`},[E(`img`,{src:t.url,alt:``,class:`size-full object-contain p-1`},null,8,P),E(`button`,{type:`button`,class:`absolute end-1 top-1 flex size-5 items-center justify-center rounded-full bg-red-500 text-white shadow-xs transition hover:bg-red-600 cursor-pointer`,title:`حذف از گالری`,onClick:e=>Ee(t.id)},[_(e,{name:`close-circle`,size:12})],8,F)]))),128)),E(`button`,{type:`button`,class:`flex size-20 flex-col items-center justify-center gap-1 rounded-lg border border-dashed border-slate-300 text-slate-400 hover:border-[#ff5a00] hover:text-[#ff5a00] hover:bg-orange-50/30 transition cursor-pointer`,onClick:r[19]||=e=>H.value=!0},[_(e,{name:`add`,size:18}),r[33]||=E(`span`,{class:`text-[10px] font-medium`},`افزودن`,-1)])])]),_(ae,{open:H.value,"onUpdate:open":r[20]||=e=>H.value=e,onSelect:Te},null,8,[`open`])]),_:1},8,[`class`]),_(p,{title:`برند و سفارش`,class:l(q.value!==`media`&&`max-lg:hidden`)},{default:c(()=>[_(O,{path:`brand`,label:`برند`},{default:c(()=>[_(s,{value:o(R).brand,"onUpdate:value":r[21]||=e=>o(R).brand=e},null,8,[`value`])]),_:1}),_(O,{path:`origin`,label:`کشور سازنده`},{default:c(()=>[_(s,{value:o(R).origin,"onUpdate:value":r[22]||=e=>o(R).origin=e,placeholder:`مثلاً: ایران`},null,8,[`value`])]),_:1}),_(O,{path:`packaging`,label:`بستهبندی`},{default:c(()=>[_(s,{value:o(R).packaging,"onUpdate:value":r[23]||=e=>o(R).packaging=e,placeholder:`مثلاً: کارتن ۱۲ جفتی`},null,8,[`value`])]),_:1}),_(O,{path:`minOrder`,label:`حداقل سفارش`},{default:c(()=>[_(s,{value:o(R).minOrder,"onUpdate:value":r[24]||=e=>o(R).minOrder=e,placeholder:`مثلاً: ۱ کارتن`},null,8,[`value`])]),_:1})]),_:1},8,[`class`]),_(p,{title:`سئو`,class:l(q.value!==`seo`&&`max-lg:hidden`)},{default:c(()=>[_(O,{path:`seoTitle`,label:`عنوان صفحه`,hint:`خالی = نام محصول`},{default:c(()=>[_(s,{value:o(R).seoTitle,"onUpdate:value":r[25]||=e=>o(R).seoTitle=e,maxlength:120,"show-count":``},null,8,[`value`])]),_:1}),_(O,{path:`seoDescription`,label:`توضیحات متا`,hint:`خالی = توضیح کوتاه`},{default:c(()=>[_(d,{value:o(R).seoDescription,"onUpdate:value":r[26]||=e=>o(R).seoDescription=e,maxlength:300,"show-count":``,"auto-size":{minRows:3}},null,8,[`value`])]),_:1})]),_:1},8,[`class`])])]),E(`div`,I,[E(`div`,ve,[_(h,{checked:o(R).isPublished,"onUpdate:checked":r[27]||=e=>o(R).isPublished=e,size:`small`},null,8,[`checked`]),E(`span`,{class:l([`text-xs`,o(R).isPublished?`text-emerald-600 font-bold`:`text-slate-400`])},n(o(R).isPublished?`منتشرشده`:`پیشنویس`),3)]),E(`div`,ye,[b.value&&J.value?.isPublished&&o(R).isPublished?(u(),T(`a`,{key:0,href:`${o(D)}/products/${o(R).slug}`,target:`_blank`,rel:`noopener`,class:`size-10 rounded-xl bg-slate-100 flex items-center justify-center text-slate-600 hover:text-orange-600 active:bg-slate-200`,title:`مشاهده در سایت`},[_(e,{name:`export`,size:18})],8,be)):g(``,!0),_(a,{type:`primary`,class:`!h-10 !px-5 !rounded-xl !font-bold !flex !items-center !gap-1.5 !shadow-sm`,loading:K.value,onClick:$},{icon:c(()=>[_(e,{name:`save-2`,size:16})]),default:c(()=>[r[36]||=v(` ذخیره محصول `,-1)]),_:1},8,[`loading`])])])]),_:1})]),_:1},8,[`spinning`])],64)}}});export{L as default}; |