Replace the plain white "МЕНЮ" text label with a gold icon badge, item count, and divider line, matching the icon-badge language already used elsewhere in the template instead of a bare heading. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
291 lines
40 KiB
JavaScript
291 lines
40 KiB
JavaScript
(()=>{
|
||
'use strict';
|
||
if(window.SunSignatureOfferPDFV18)return;
|
||
|
||
const VERSION='18.1.0';
|
||
const RELEASE='20260916-v18-1-0-signature-offer-pdf-polish';
|
||
const W=1000,H=1414,SCALE=1.6,M=54;
|
||
const SIGNATURE_IDS=['cream-elegance','neon-menu','emerald-circles','midnight-checklist','gourmet-hero','diamond-gold','premium-dark','premium-emerald'];
|
||
const SIGNATURE_SET=new Set(SIGNATURE_IDS);
|
||
const INNER_THEME={
|
||
'cream-elegance':'light',
|
||
'neon-menu':'midnight-glass',
|
||
'emerald-circles':'midnight-glass',
|
||
'midnight-checklist':'midnight-glass',
|
||
'gourmet-hero':'black-gold',
|
||
'diamond-gold':'black-gold',
|
||
'premium-dark':'black-gold',
|
||
'premium-emerald':'midnight-glass'
|
||
};
|
||
const PALETTES={
|
||
'cream-elegance':{bg:'#faf3e6',paper:'#ffffff',ink:'#22281f',muted:'#726c5f',accent:'#8f8536',accent2:'#4f6a3f',deep:'#274a34',line:'#ecdfc4',foot:'#faf3e6'},
|
||
'neon-menu':{bg:'#070908',paper:'#0d1210',ink:'#eafff1',muted:'#8fa89a',accent:'#39e27a',accent2:'#c9a24a',deep:'#0f2318',line:'#1f4430',foot:'#070908'},
|
||
'emerald-circles':{bg:'#08181c',paper:'#0d2226',ink:'#eef6f2',muted:'#94aca6',accent:'#c9a24a',accent2:'#2f9e78',deep:'#0a2a26',line:'#1f4642',foot:'#08181c'},
|
||
'midnight-checklist':{bg:'#06170f',paper:'#0b2417',ink:'#eef6ee',muted:'#8fac97',accent:'#dfb957',accent2:'#3f8f5c',deep:'#123822',line:'#1f4a30',foot:'#06170f'},
|
||
'gourmet-hero':{bg:'#0a0a09',paper:'#141311',ink:'#f5f1e8',muted:'#a89f8e',accent:'#d7ac55',accent2:'#8a6a2c',deep:'#1c1a15',line:'#3a352a',foot:'#0a0a09'},
|
||
'diamond-gold':{bg:'#071510',paper:'#0d251c',ink:'#f0f5ee',muted:'#93ab9c',accent:'#e0b95c',accent2:'#2f9e6f',deep:'#123d29',line:'#204b34',foot:'#071510'},
|
||
'premium-dark':{bg:'#0a0d12',paper:'#12161d',ink:'#f5f6f8',muted:'#8b93a1',accent:'#e0a545',accent2:'#c9863a',deep:'#171c24',line:'#252b35',foot:'#0a0d12'},
|
||
'premium-emerald':{bg:'#0a1210',paper:'#121d19',ink:'#f2f8f5',muted:'#8fa79c',accent:'#3fcf8e',accent2:'#2a9d6c',deep:'#17241f',line:'#233830',foot:'#0a1210'}
|
||
};
|
||
|
||
// Distinctive Cyrillic-friendly type pairing per polished template (falls back to Georgia/Arial elsewhere).
|
||
const FONT_STACKS={
|
||
'cream-elegance':{display:'"Playfair Display",Georgia,serif',label:'"Montserrat",Arial,sans-serif'},
|
||
'neon-menu':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
|
||
'emerald-circles':{display:'"Playfair Display",Georgia,serif',label:'"Montserrat",Arial,sans-serif'},
|
||
'midnight-checklist':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
|
||
'gourmet-hero':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
|
||
'diamond-gold':{display:'"Playfair Display",Georgia,serif',label:'"Montserrat",Arial,sans-serif'},
|
||
'premium-dark':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'},
|
||
'premium-emerald':{display:'"Unbounded",Arial,sans-serif',label:'"Manrope",Arial,sans-serif'}
|
||
};
|
||
function fontsFor(id){return FONT_STACKS[id]||{display:'Georgia,serif',label:'Arial,sans-serif'}}
|
||
function ensureFontLink(){
|
||
if(document.getElementById('sunSignatureFontsV18'))return;
|
||
const link=document.createElement('link');link.id='sunSignatureFontsV18';link.rel='stylesheet';
|
||
link.href='https://fonts.googleapis.com/css2?family=Playfair+Display:wght@500;600;700&family=Montserrat:wght@500;600;700;800&family=Unbounded:wght@600;700;800&family=Manrope:wght@500;600;700;800&display=swap';
|
||
document.head.appendChild(link);
|
||
}
|
||
ensureFontLink();
|
||
async function ensureFontsReady(id){
|
||
const f=FONT_STACKS[id];if(!f||!document.fonts)return;
|
||
try{
|
||
await Promise.all([
|
||
document.fonts.load(`700 40px ${f.display}`),
|
||
document.fonts.load(`600 16px ${f.label}`),
|
||
document.fonts.load(`800 16px ${f.label}`)
|
||
]);
|
||
}catch(_){}
|
||
}
|
||
|
||
const money=v=>Math.round(Number(v||0)).toLocaleString('ru-RU')+' ₽';
|
||
const classicBoxCount=s=>(s?.items||[]).filter(i=>[0,5].includes(Number(i?.categoryId))).reduce((sum,i)=>sum+Math.max(0,Number(i?.qty||0)),0);
|
||
const dateText=value=>{if(!value)return'';const d=new Date(String(value)+'T12:00:00');return Number.isNaN(d.getTime())?String(value):d.toLocaleDateString('ru-RU',{day:'numeric',month:'long',year:'numeric'})};
|
||
const plural=(n,a,b,c)=>{const x=Math.abs(Number(n)||0)%100,y=x%10;return x>10&&x<20?c:y===1?a:y>=2&&y<=4?b:c};
|
||
const safeImage=src=>/^data:image\/(?:png|jpe?g|webp);base64,/i.test(String(src||''));
|
||
function loadImage(src){return new Promise(resolve=>{if(!safeImage(src))return resolve(null);const im=new Image();let done=false;const finish=v=>{if(done)return;done=true;clearTimeout(timer);resolve(v)};const timer=setTimeout(()=>finish(null),3500);im.onload=()=>finish(im);im.onerror=()=>finish(null);im.src=String(src)})}
|
||
function roundRect(ctx,x,y,w,h,r,fill,stroke,width=1){const rr=Math.min(r,w/2,h/2);ctx.beginPath();ctx.moveTo(x+rr,y);ctx.arcTo(x+w,y,x+w,y+h,rr);ctx.arcTo(x+w,y+h,x,y+h,rr);ctx.arcTo(x,y+h,x,y,rr);ctx.arcTo(x,y,x+w,y,rr);ctx.closePath();if(fill){ctx.fillStyle=fill;ctx.fill()}if(stroke){ctx.strokeStyle=stroke;ctx.lineWidth=width;ctx.stroke()}}
|
||
function wrap(ctx,text,maxWidth){const words=String(text||'').trim().split(/\s+/).filter(Boolean),lines=[];let line='';for(const word of words){const test=line?line+' '+word:word;if(ctx.measureText(test).width<=maxWidth)line=test;else{if(line)lines.push(line);line=word}}if(line)lines.push(line);return lines}
|
||
function text(ctx,value,x,y,width,lineHeight,{font='16px Arial',color='#111',align='left',maxLines=0}={}){ctx.save();ctx.font=font;ctx.fillStyle=color||'#111';ctx.textAlign=align;ctx.textBaseline='top';let lines=wrap(ctx,value,width);if(maxLines&&lines.length>maxLines){lines=lines.slice(0,maxLines);let last=lines[lines.length-1]||'';while(last&&ctx.measureText(last+'…').width>width)last=last.slice(0,-1);lines[lines.length-1]=last+'…'}lines.forEach((line,i)=>ctx.fillText(line,x,y+i*lineHeight));ctx.restore();return lines.length*lineHeight}
|
||
function line(ctx,x1,y1,x2,y2,color,width=1,dash=[]){ctx.save();ctx.strokeStyle=color;ctx.lineWidth=width;ctx.setLineDash(dash);ctx.beginPath();ctx.moveTo(x1,y1);ctx.lineTo(x2,y2);ctx.stroke();ctx.restore()}
|
||
function coverImage(ctx,img,x,y,w,h,r=24){if(!img)return;const s=Math.max(w/img.naturalWidth,h/img.naturalHeight),sw=w/s,sh=h/s,sx=(img.naturalWidth-sw)/2,sy=(img.naturalHeight-sh)/2;ctx.save();roundRect(ctx,x,y,w,h,r);ctx.clip();ctx.drawImage(img,sx,sy,sw,sh,x,y,w,h);ctx.restore()}
|
||
function circleImage(ctx,img,cx,cy,r){if(!img)return;const d=r*2,s=Math.max(d/img.naturalWidth,d/img.naturalHeight),sw=d/s,sh=d/s,sx=(img.naturalWidth-sw)/2,sy=(img.naturalHeight-sh)/2;ctx.save();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.clip();ctx.drawImage(img,sx,sy,sw,sh,cx-r,cy-r,d,d);ctx.restore()}
|
||
function sun(ctx,cx,cy,r,color,rays=18){ctx.save();ctx.strokeStyle=color;ctx.fillStyle=color;ctx.lineWidth=2;for(let i=0;i<rays;i++){const a=i*Math.PI*2/rays,ri=r+8,ro=r+24+(i%2)*8;ctx.beginPath();ctx.moveTo(cx+Math.cos(a)*ri,cy+Math.sin(a)*ri);ctx.lineTo(cx+Math.cos(a)*ro,cy+Math.sin(a)*ro);ctx.stroke()}ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.fill();ctx.restore()}
|
||
function logoOrBrand(ctx,logo,p,{x=M,y=46,dark=false,fonts}={},brandName='Солнце Кейтеринг'){if(logo){const s=Math.min(190/logo.naturalWidth,64/logo.naturalHeight),dw=logo.naturalWidth*s,dh=logo.naturalHeight*s;ctx.drawImage(logo,x,y,dw,dh);return}const df=fonts?.display||'Arial',lf=fonts?.label||'Arial';sun(ctx,x+28,y+29,15,p.accent,16);const parts=String(brandName||'Солнце Кейтеринг').trim().split(/\s+/),line1=(parts[0]||'Солнце').toUpperCase(),line2=(parts.slice(1).join(' ')||'Кейтеринг').toUpperCase();text(ctx,line1,x+60,y+11,220,28,{font:`700 23px ${df}`,color:dark?'#fff':p.ink,maxLines:1});text(ctx,line2,x+61,y+42,180,18,{font:`600 11px ${lf}`,color:p.accent,maxLines:1})}
|
||
function eventTitle(s){const event=String(s.event||'Персональное предложение').trim();return String(s.client||'').trim()?'Предложение для '+String(s.client).trim():event}
|
||
function footer(ctx,p,page,total,brandName='Солнце Кейтеринг',fonts){const lf=fonts?.label||'Arial';line(ctx,M,H-48,W-M,H-48,p.line,1);text(ctx,String(brandName||'Солнце Кейтеринг')+' · предложение клиенту',M,H-34,500,16,{font:`500 10px ${lf}`,color:p.muted,maxLines:1});text(ctx,String(page)+' / '+String(total),W-M,H-34,100,16,{font:`500 10px ${lf}`,color:p.muted,align:'right',maxLines:1})}
|
||
function iconCircle(ctx,cx,cy,r,glyph,{fill,stroke,glyphColor='#fff',size=20}={}){roundRect(ctx,cx-r,cy-r,r*2,r*2,r,fill,stroke,1.4);text(ctx,glyph,cx,cy-size/2-1,r*2,size+4,{font:`${size}px Arial`,color:glyphColor,align:'center',maxLines:1})}
|
||
// Small line-drawn icon badges (calendar/people/gauge/box) for the "premium" templates, so those stat cards use real iconography instead of emoji.
|
||
function iconBadge(ctx,x,y,size,p,draw){roundRect(ctx,x,y,size,size,size*0.28,p.deep,p.accent,1.4);ctx.save();ctx.translate(x+size/2,y+size/2);ctx.strokeStyle=p.accent;ctx.fillStyle=p.accent;ctx.lineWidth=Math.max(1.6,size*0.05);ctx.lineCap='round';ctx.lineJoin='round';draw(ctx,size*0.32);ctx.restore()}
|
||
function iconCalendar(ctx,r){roundRect(ctx,-r,-r*0.75,r*2,r*1.65,r*0.22,null,ctx.strokeStyle,ctx.lineWidth);ctx.beginPath();ctx.moveTo(-r,-r*0.3);ctx.lineTo(r,-r*0.3);ctx.stroke();ctx.beginPath();ctx.moveTo(-r*0.5,-r*1.05);ctx.lineTo(-r*0.5,-r*0.55);ctx.stroke();ctx.beginPath();ctx.moveTo(r*0.5,-r*1.05);ctx.lineTo(r*0.5,-r*0.55);ctx.stroke()}
|
||
function iconPeople(ctx,r){ctx.beginPath();ctx.arc(0,-r*0.4,r*0.4,0,Math.PI*2);ctx.stroke();ctx.beginPath();ctx.arc(0,r*0.75,r*0.82,Math.PI,0,true);ctx.stroke()}
|
||
function iconGauge(ctx,r){ctx.beginPath();ctx.arc(0,r*0.15,r*0.85,Math.PI,0);ctx.stroke();ctx.beginPath();ctx.moveTo(0,r*0.15);ctx.lineTo(r*0.48,-r*0.32);ctx.stroke();ctx.beginPath();ctx.arc(0,r*0.15,r*0.1,0,Math.PI*2);ctx.fill()}
|
||
function iconBox(ctx,r){roundRect(ctx,-r,-r*0.75,r*2,r*1.55,r*0.16,null,ctx.strokeStyle,ctx.lineWidth);ctx.beginPath();ctx.moveTo(-r,-r*0.05);ctx.lineTo(r,-r*0.05);ctx.stroke();ctx.beginPath();ctx.moveTo(0,-r*0.75);ctx.lineTo(0,r*0.8);ctx.stroke()}
|
||
function iconMenu(ctx,r){for(let i=-1;i<=1;i++){const yy=r*0.55*i;ctx.beginPath();ctx.arc(-r*0.72,yy,r*0.12,0,Math.PI*2);ctx.fill();ctx.beginPath();ctx.moveTo(-r*0.36,yy);ctx.lineTo(r,yy);ctx.stroke()}}
|
||
function statBadgeRow(ctx,p,f,x,y,w,h,cards){const gap=14,cw=(w-gap*(cards.length-1))/cards.length;const bs=Math.min(52,h*0.42);cards.forEach(([draw,value,label],i)=>{const xx=x+i*(cw+gap);roundRect(ctx,xx,y,cw,h,20,p.paper,p.line,1);iconBadge(ctx,xx+18,y+18,bs,p,draw);text(ctx,value,xx+18+bs+10,y+18+bs/2-12,cw-18-bs-10-12,24,{font:`700 16px ${f.display}`,color:'#fff',maxLines:1});text(ctx,label,xx+18,y+h-30,cw-36,16,{font:`500 12px ${f.label}`,color:p.muted,maxLines:1})})}
|
||
function bulletList(ctx,p,f,x,y,w,lineH,items,color){let yy=y;items.forEach(t=>{ctx.save();ctx.fillStyle=p.accent;ctx.beginPath();ctx.arc(x+4,yy+9,4,0,Math.PI*2);ctx.fill();ctx.restore();const used=text(ctx,t,x+20,yy,w-20,18,{font:`500 14px ${f.label}`,color:color||p.ink,maxLines:2});yy+=Math.max(lineH,used+6)});return yy-y}
|
||
function factChips(ctx,s,p,x,y,w,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';const facts=[];if(s.date)facts.push(dateText(s.date));if(s.guests)facts.push(String(s.guests)+' '+plural(s.guests,'гость','гостя','гостей'));if(s.time)facts.push('Доставка '+String(s.time));const gap=8,cw=(w-gap*Math.max(0,facts.length-1))/Math.max(1,facts.length);facts.forEach((f,i)=>{const xx=x+i*(cw+gap);roundRect(ctx,xx,y,cw,44,13,dark?'rgba(255,255,255,.06)':p.paper,dark?'rgba(255,255,255,.2)':p.line);text(ctx,f,xx+cw/2,y+14,cw-14,17,{font:`600 12px ${lf}`,color:dark?'#f4f1e6':p.ink,align:'center',maxLines:1})})}
|
||
function menuList(ctx,s,p,x,y,w,rowH,count,{dark=true,numbered=true,fonts}={}){const df=fonts?.display||'Georgia',lf=fonts?.label||'Arial';const items=(s.items||[]).slice(0,count),gap=8;items.forEach((item,i)=>{const yy=y+i*(rowH+gap);roundRect(ctx,x,yy,w,rowH,13,dark?'rgba(255,255,255,.05)':p.paper,dark?'rgba(255,255,255,.14)':p.line);const badgeX=x+14;if(numbered){roundRect(ctx,badgeX,yy+rowH/2-13,26,26,13,p.accent,null);text(ctx,String(i+1),badgeX+13,yy+rowH/2-9,26,18,{font:`700 12px ${lf}`,color:p.deep,align:'center',maxLines:1})}const textX=numbered?badgeX+38:x+16;text(ctx,item.name||'Позиция меню',textX,yy+10,w-(textX-x)-70,20,{font:`600 14px ${df}`,color:dark?'#fff':p.ink,maxLines:1});const meta=[item.weight?String(item.weight):'',].filter(Boolean).join(' · ');if(meta)text(ctx,meta,textX,yy+rowH-24,w-(textX-x)-70,16,{font:`500 11px ${lf}`,color:p.muted,maxLines:1});text(ctx,'× '+Math.round(Number(item.qty||1)),x+w-16,yy+rowH/2-9,50,18,{font:`700 12px ${lf}`,color:p.accent,align:'right',maxLines:1})});return items.length*(rowH+gap)}
|
||
function photoGridSquare(ctx,s,p,x,y,w,h,cols,rows,{numbered=true,fonts}={}){const lf=fonts?.label||'Arial';const items=(s.items||[]).slice(0,cols*rows);const gap=7,cw=(w-gap*(cols-1))/cols,ch=(h-gap*(rows-1))/rows;return (async()=>{const imgs=await Promise.all(items.map(it=>loadImage(it.photoData||'')));imgs.forEach((img,i)=>{const col=i%cols,row=Math.floor(i/cols),xx=x+col*(cw+gap),yy=y+row*(ch+gap);roundRect(ctx,xx,yy,cw,ch,10,p.deep,null);if(img)coverImage(ctx,img,xx,yy,cw,ch,10);if(numbered){roundRect(ctx,xx+6,yy+6,22,22,11,'rgba(6,10,8,.72)',null);text(ctx,String(i+1),xx+17,yy+10,22,16,{font:`700 10px ${lf}`,color:'#fff',align:'center',maxLines:1})}})})()}
|
||
function photoGridCircles(ctx,s,p,x,y,w,cols,r,{numbered=true,fonts}={}){const lf=fonts?.label||'Arial';const items=(s.items||[]).slice(0,cols*2);const gapX=(w-cols*r*2)/(cols-1||1);return (async()=>{const imgs=await Promise.all(items.map(it=>loadImage(it.photoData||'')));imgs.forEach((img,i)=>{const col=i%cols,row=Math.floor(i/cols),cx=x+r+col*(r*2+gapX),cy=y+r+row*(r*2+22);ctx.save();ctx.beginPath();ctx.arc(cx,cy,r+3,0,Math.PI*2);ctx.strokeStyle=p.accent;ctx.lineWidth=2;ctx.stroke();ctx.restore();if(img)circleImage(ctx,img,cx,cy,r);else{ctx.save();ctx.beginPath();ctx.arc(cx,cy,r,0,Math.PI*2);ctx.fillStyle=p.deep;ctx.fill();ctx.restore()}if(numbered){roundRect(ctx,cx-13,cy+r-8,26,22,11,p.accent,null);text(ctx,String(i+1),cx,cy+r-4,26,16,{font:`700 10px ${lf}`,color:p.deep,align:'center',maxLines:1})}})})()}
|
||
function statCards(ctx,s,p,x,y,w,h,cards,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';const gap=10,cw=(w-gap*(cards.length-1))/cards.length;cards.forEach((c,i)=>{const xx=x+i*(cw+gap);roundRect(ctx,xx,y,cw,h,14,dark?'rgba(255,255,255,.05)':p.paper,dark?'rgba(255,255,255,.16)':p.line);text(ctx,c[1],xx+16,y+14,cw-32,17,{font:`700 10px ${lf}`,color:p.muted,maxLines:1});text(ctx,String(c[0]),xx+16,y+38,cw-32,32,{font:`800 24px ${lf}`,color:dark?'#fff':p.deep,maxLines:1})})}
|
||
function priceBand(ctx,s,p,x,y,w,h,{dark=true,fonts}={}){
|
||
const df=fonts?.display||'Arial',lf=fonts?.label||'Arial';
|
||
const perGuest=s.guests?money((Number(s.pricing?.itemsTotal||s.pricing?.total||0))/Math.max(1,Number(s.guests))):'';
|
||
if(dark){
|
||
roundRect(ctx,x,y,w,h,16,p.accent,null);
|
||
text(ctx,'ИТОГОВАЯ СТОИМОСТЬ',x+22,y+16,w-44,16,{font:`700 11px ${lf}`,color:p.deep,maxLines:1});
|
||
text(ctx,money(s.pricing?.total),x+22,y+38,w-44,44,{font:`700 32px ${df}`,color:p.deep,maxLines:1});
|
||
if(perGuest)text(ctx,'на гостя: '+perGuest,x+22,y+h-26,w-44,18,{font:`500 12px ${lf}`,color:p.deep,maxLines:1});
|
||
}else{
|
||
roundRect(ctx,x,y,w,h,16,p.paper,p.accent,2);
|
||
text(ctx,'ИТОГОВАЯ СТОИМОСТЬ',x+22,y+18,w-44,16,{font:`700 11px ${lf}`,color:p.accent2,maxLines:1});
|
||
text(ctx,money(s.pricing?.total),x+22,y+38,w-44,46,{font:`700 34px ${df}`,color:p.deep,maxLines:1});
|
||
if(perGuest)text(ctx,'на гостя: '+perGuest,x+22,y+h-25,w-44,18,{font:`500 12px ${lf}`,color:p.muted,maxLines:1});
|
||
}
|
||
}
|
||
function checklistCard(ctx,p,x,y,w,h,title,lines,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';roundRect(ctx,x,y,w,h,16,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.14)':p.line);text(ctx,title,x+18,y+16,w-36,18,{font:`700 13px ${lf}`,color:p.accent,maxLines:1});const cols=2,rowH=22,colW=(w-36)/cols;lines.forEach((t,i)=>{const col=i%cols,row=Math.floor(i/cols),xx=x+18+col*colW,yy=y+46+row*rowH;roundRect(ctx,xx,yy+2,14,14,7,p.accent,null);text(ctx,'✓',xx+7,yy+3,10,12,{font:`700 9px ${lf}`,color:p.deep,align:'center',maxLines:1});text(ctx,t,xx+20,yy,colW-24,14,{font:`500 10px ${lf}`,color:dark?'#eef':p.ink,maxLines:1})})}
|
||
function addonRow(ctx,p,x,y,w,h,items,{dark=true,fonts}={}){const lf=fonts?.label||'Arial';roundRect(ctx,x,y,w,h,16,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.14)':p.line);text(ctx,'Рекомендуем добавить',x+18,y+16,w-36,16,{font:`700 12px ${lf}`,color:p.accent,maxLines:1});const innerX=x+18,innerW=w-36,innerY=y+48,gap=8,cw=(innerW-gap*(items.length-1))/items.length;items.forEach((t,i)=>{const xx=innerX+i*(cw+gap);roundRect(ctx,xx+cw/2-11,innerY,22,22,11,p.accent,null);text(ctx,'+',xx+cw/2,innerY+2,22,18,{font:`700 13px ${lf}`,color:p.deep,align:'center',maxLines:1});text(ctx,t,xx+cw/2,innerY+32,cw,14,{font:`600 10px ${lf}`,color:dark?'#eef':p.ink,align:'center',maxLines:2})})}
|
||
|
||
const CHECK_LINES_DEFAULT=['Доставка в фирменных боксах','Свежие продукты каждый день','Аккуратная подача и упаковка','Готовность к сервировке'];
|
||
const ADDON_LINES_DEFAULT=['Напитки','Чайная станция','Официант','Текстиль'];
|
||
|
||
async function renderCover(s,id){
|
||
await ensureFontsReady(id);
|
||
const p=PALETTES[id]||PALETTES['cream-elegance'],f=fontsFor(id),canvas=document.createElement('canvas');canvas.width=Math.round(W*SCALE);canvas.height=Math.round(H*SCALE);const ctx=canvas.getContext('2d',{alpha:false});ctx.setTransform(SCALE,0,0,SCALE,0,0);ctx.fillStyle=p.bg;ctx.fillRect(0,0,W,H);
|
||
const logo=await loadImage(s.logo||''),heroSrc=(s.items||[]).find(x=>safeImage(x.photoData||x.photo))?.photoData||(s.finalGallery||[]).find(safeImage)||'',hero=await loadImage(heroSrc),title=eventTitle(s),event=String(s.event||'Персональное предложение').trim();
|
||
// Pull the user's editable "control"/"extras" lists from Settings when present (set in preparedSnapshot), falling back to sensible defaults otherwise.
|
||
const CHECK_LINES=Array.isArray(s.controlLines)&&s.controlLines.length?s.controlLines:CHECK_LINES_DEFAULT;
|
||
const ADDON_LINES=Array.isArray(s.extraServices)&&s.extraServices.length?s.extraServices:ADDON_LINES_DEFAULT;
|
||
|
||
if(id==='cream-elegance'){
|
||
logoOrBrand(ctx,logo,p,{fonts:f},s.brandName);
|
||
text(ctx,'МЕРОПРИЯТИЕ',M,168,390,18,{font:`700 11px ${f.label}`,color:p.accent2,maxLines:1});
|
||
text(ctx,title,M,198,405,52,{font:`600 40px ${f.display}`,color:p.ink,maxLines:2});
|
||
factChips(ctx,s,p,M,372,405,{dark:false,fonts:f});
|
||
line(ctx,M,440,445,440,p.accent,1);
|
||
text(ctx,'Меню составлено индивидуально для вашего мероприятия с учётом количества гостей и пожеланий.',M,464,395,24,{font:`500 14px ${f.label}`,color:p.muted,maxLines:4});
|
||
if(hero)coverImage(ctx,hero,500,150,446,600,28);
|
||
text(ctx,'МЕНЮ',W/2,795,W,26,{font:`600 24px ${f.display}`,color:p.deep,align:'center',maxLines:1});
|
||
line(ctx,M,814,410,814,p.line,1);line(ctx,590,814,W-M,814,p.line,1);
|
||
await photoGridSquare(ctx,s,p,M,850,W-2*M,300,3,2,{numbered:false,fonts:f});
|
||
statCards(ctx,s,p,M,1170,565,100,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:false,fonts:f});
|
||
priceBand(ctx,s,p,640,1170,306,100,{dark:false,fonts:f});
|
||
}else if(id==='neon-menu'){
|
||
ctx.save();roundRect(ctx,20,20,W-40,H-40,0,null,p.accent,1.4);ctx.restore();
|
||
logoOrBrand(ctx,logo,p,{x:M,y:52,dark:true,fonts:f},s.brandName);
|
||
roundRect(ctx,M,140,150,34,17,'rgba(57,226,122,.12)',p.accent,1);
|
||
text(ctx,dateText(s.date)||'Дата уточняется',M+14,150,200,16,{font:`700 10px ${f.label}`,color:p.accent,maxLines:1});
|
||
text(ctx,title,M,192,430,60,{font:`700 34px ${f.display}`,color:'#fff',maxLines:3});
|
||
text(ctx,event,M,340,400,26,{font:`500 14px ${f.label}`,color:p.muted,maxLines:2});
|
||
if(hero)coverImage(ctx,hero,520,140,426,300,20);
|
||
if(s.guests){roundRect(ctx,830,390,116,58,16,p.deep,p.accent,1.4);text(ctx,String(s.guests),888,402,116,26,{font:`700 20px ${f.display}`,color:p.accent,align:'center',maxLines:1});text(ctx,'ГОСТЕЙ',888,432,116,14,{font:`700 9px ${f.label}`,color:p.muted,align:'center',maxLines:1})}
|
||
const qual=['Свежие ингредиенты','Авторские рецепты','Эстетичная подача','Контроль качества'];
|
||
qual.forEach((t,i)=>{const xx=M+i*228;roundRect(ctx,xx,470,212,60,14,'rgba(57,226,122,.05)',p.line);text(ctx,t,xx+106,494,188,16,{font:`600 11px ${f.label}`,color:p.accent,align:'center',maxLines:2})});
|
||
menuList(ctx,s,p,M,560,470,64,5,{dark:true,fonts:f});
|
||
await photoGridSquare(ctx,s,p,540,560,406,470,3,4,{numbered:true,fonts:f});
|
||
text(ctx,'НАШИ ЗАКУСКИ',743,536,406,16,{font:`700 13px ${f.display}`,color:p.accent,align:'center',maxLines:1});
|
||
statCards(ctx,s,p,M,1060,306,80,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});
|
||
priceBand(ctx,s,p,376,1060,570,80,{dark:true,fonts:f});
|
||
checklistCard(ctx,p,M,1160,470,150,'ВСЁ ПОД КОНТРОЛЕМ',CHECK_LINES,{dark:true,fonts:f});
|
||
addonRow(ctx,p,540,1160,406,150,ADDON_LINES,{dark:true,fonts:f});
|
||
}else if(id==='emerald-circles'){
|
||
logoOrBrand(ctx,logo,p,{x:M,y:52,dark:true,fonts:f},s.brandName);roundRect(ctx,M,140,260,34,17,'rgba(201,162,74,.12)',p.accent,1);text(ctx,'ИНДИВИДУАЛЬНОЕ ПРЕДЛОЖЕНИЕ',M+14,150,240,14,{font:`700 9px ${f.label}`,color:p.accent,maxLines:1});text(ctx,title,M,192,430,58,{font:`600 38px ${f.display}`,color:'#fff',maxLines:3});text(ctx,'Составим идеальное гастрономическое решение для вашего мероприятия.',M,350,410,22,{font:`500 13px ${f.label}`,color:p.muted,maxLines:3});
|
||
const feats=['Разнообразие блюд','Свежие ингредиенты','Премиальное качество'];feats.forEach((t,i)=>{const xx=M+i*145;roundRect(ctx,xx,430,132,48,13,'rgba(255,255,255,.05)',p.line);text(ctx,t,xx+66,446,112,14,{font:`600 10px ${f.label}`,color:p.accent2,align:'center',maxLines:2})});
|
||
menuList(ctx,s,p,M,510,470,66,5,{dark:true,fonts:f});await photoGridCircles(ctx,s,p,565,520,380,3,58,{numbered:true,fonts:f});
|
||
statCards(ctx,s,p,M,940,W-2*M,78,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ ',],[s.guests||'—','ГОСТЕЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,M,1032,W-2*M,90,{dark:true,fonts:f});
|
||
checklistCard(ctx,p,M,1140,470,170,'ВСЁ ПОД КОНТРОЛЕМ КОМАНДЫ СОЛНЦА',CHECK_LINES,{dark:true,fonts:f});addonRow(ctx,p,540,1140,406,170,ADDON_LINES,{dark:true,fonts:f});
|
||
}else if(id==='midnight-checklist'){
|
||
logoOrBrand(ctx,logo,p,{x:M,y:48,dark:true,fonts:f},s.brandName);if(s.client)text(ctx,String(s.client),W-M,58,300,20,{font:`700 15px ${f.display}`,color:'#fff',align:'right',maxLines:1});const metaLine=[dateText(s.date),s.guests?String(s.guests)+' гостей':''].filter(Boolean).join(' · ');if(metaLine)text(ctx,metaLine,W-M,84,300,16,{font:`500 11px ${f.label}`,color:p.muted,align:'right',maxLines:1});
|
||
roundRect(ctx,M,120,W-2*M,60,14,'rgba(255,255,255,.04)',p.line);text(ctx,'Меню составлено из расчёта количества гостей. Все позиции приедут готовыми к подаче на стол.',M+20,138,W-2*M-40,20,{font:`500 12px ${f.label}`,color:p.muted,maxLines:2});
|
||
iconBadge(ctx,M,192,34,p,iconMenu);text(ctx,'МЕНЮ',M+48,197,260,24,{font:`700 20px ${f.display}`,color:'#fff',maxLines:1});const menuCount=(s.items||[]).length;if(menuCount)text(ctx,String(menuCount)+' '+plural(menuCount,'позиция','позиции','позиций'),W-M,200,240,16,{font:`500 11px ${f.label}`,color:p.muted,align:'right',maxLines:1});line(ctx,M,234,W-M,234,p.line,1);
|
||
menuList(ctx,s,p,M,255,W-2*M,58,8,{dark:true,fonts:f});
|
||
await photoGridSquare(ctx,s,p,M,860,W-2*M,190,5,2,{numbered:false,fonts:f});
|
||
statCards(ctx,s,p,M,1075,470,74,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,540,1075,406,74,{dark:true,fonts:f});
|
||
checklistCard(ctx,p,M,1170,470,150,'ВСЁ ПОД КОНТРОЛЕМ КОМАНДЫ СОЛНЦА',CHECK_LINES,{dark:true,fonts:f});addonRow(ctx,p,540,1170,406,150,ADDON_LINES,{dark:true,fonts:f});
|
||
}else if(id==='gourmet-hero'){
|
||
logoOrBrand(ctx,logo,p,{x:W-M-190,y:44,dark:true,fonts:f},s.brandName);text(ctx,'КЕЙТЕРИНГ ДЛЯ',M,96,460,44,{font:`700 32px ${f.display}`,color:'#fff',maxLines:1});text(ctx,'ВАШЕГО СОБЫТИЯ',M,140,460,44,{font:`700 32px ${f.display}`,color:'#fff',maxLines:1});text(ctx,'Индивидуальное меню, безупречная подача и сервис, о котором будут говорить ваши гости.',M,235,400,24,{font:`500 14px ${f.label}`,color:p.muted,maxLines:3});if(hero)coverImage(ctx,hero,520,60,426,340,18);roundRect(ctx,520,420,240,42,12,p.paper,p.line);text(ctx,String((s.items||[]).length)+' блюд в меню',540,432,220,18,{font:`700 12px ${f.label}`,color:p.accent,maxLines:1});
|
||
const realCats=[...new Set((s.items||[]).map(it=>it.categoryName).filter(Boolean))].slice(0,5);
|
||
if(realCats.length){const gap=10,cw=(W-2*M-gap*(realCats.length-1))/realCats.length;realCats.forEach((t,i)=>{const xx=M+i*(cw+gap);roundRect(ctx,xx,494,cw,32,10,'rgba(255,255,255,.06)',p.line);text(ctx,t,xx+cw/2,503,cw-10,14,{font:`600 10px ${f.label}`,color:p.accent,align:'center',maxLines:1})})}
|
||
const featured=(s.items||[]).slice(0,3);const cardW=(W-2*M-2*14)/3;
|
||
{const imgs=await Promise.all(featured.map(it=>loadImage(it.photoData||'')));featured.forEach((it,i)=>{const xx=M+i*(cardW+14);roundRect(ctx,xx,555,cardW,230,16,p.paper,p.line);if(imgs[i])coverImage(ctx,imgs[i],xx,555,cardW,150,16);text(ctx,String(i+1).padStart(2,'0'),xx+14,715,40,16,{font:`700 12px ${f.label}`,color:p.accent,maxLines:1});text(ctx,it.name||'Позиция меню',xx+14,735,cardW-28,20,{font:`600 13px ${f.display}`,color:'#fff',maxLines:2});if(it.weight)text(ctx,String(it.weight),xx+14,765,cardW-28,14,{font:`500 10px ${f.label}`,color:p.muted,maxLines:1})})}
|
||
roundRect(ctx,M,822,W-2*M,52,14,null,p.accent,1.4);text(ctx,'СМОТРЕТЬ ПОЛНОЕ МЕНЮ →',W/2,838,W,18,{font:`700 12px ${f.label}`,color:p.accent,align:'center',maxLines:1});
|
||
statCards(ctx,s,p,M,910,306,100,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,376,910,306,100,{dark:true,fonts:f});checklistCard(ctx,p,698,910,248,100,'ДОСТАВКА',CHECK_LINES.slice(0,2),{dark:true,fonts:f});
|
||
addonRow(ctx,p,M,1045,W-2*M,150,ADDON_LINES,{dark:true,fonts:f});
|
||
}else if(id==='diamond-gold'){
|
||
logoOrBrand(ctx,logo,p,{x:M,y:50,dark:true,fonts:f},s.brandName);if(s.guests){roundRect(ctx,W-M-140,50,140,34,17,'rgba(255,255,255,.06)',p.line);text(ctx,String(s.guests)+' гостей',W-M-70,58,140,16,{font:`700 11px ${f.label}`,color:'#fff',align:'center',maxLines:1})}
|
||
text(ctx,'ИНДИВИДУАЛЬНОЕ МЕНЮ',M,150,440,34,{font:`700 28px ${f.display}`,color:'#fff',maxLines:1});text(ctx,'для вашего мероприятия',M,186,440,24,{font:`500 18px ${f.display}`,color:p.accent,maxLines:1});text(ctx,'Мы создаём гастрономические впечатления, которые запоминаются.',M,222,400,20,{font:`500 13px ${f.label}`,color:p.muted,maxLines:3});
|
||
const feats=['Индивидуальный подход','Только свежие ингредиенты','Разнообразие вкусов','Сделано с любовью'];feats.forEach((t,i)=>{const xx=M+(i%2)*220,yy=330+Math.floor(i/2)*46;roundRect(ctx,xx,yy,205,38,11,'rgba(255,255,255,.04)',p.line);text(ctx,t,xx+10,yy+12,185,14,{font:`500 10px ${f.label}`,color:p.accent2,maxLines:1})});
|
||
menuList(ctx,s,p,M,440,470,56,5,{dark:true,fonts:f});
|
||
text(ctx,'ПРИМЕРЫ ЗАКУСОК',540,428,406,16,{font:`700 12px ${f.label}`,color:p.accent,maxLines:1});await photoGridSquare(ctx,s,p,540,455,406,300,3,2,{numbered:true,fonts:f});
|
||
statCards(ctx,s,p,M,900,306,90,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});priceBand(ctx,s,p,376,900,306,90,{dark:true,fonts:f});
|
||
const dgx=698,dgy=900;roundRect(ctx,dgx,dgy,248,90,16,'rgba(255,255,255,.04)',p.line);ctx.save();ctx.translate(dgx+124,dgy+45);ctx.rotate(Math.PI/4);ctx.fillStyle=p.accent;ctx.fillRect(-16,-16,32,32);ctx.restore();text(ctx,'СКИДКА ВКЛЮЧЕНА',dgx+124,dgy+70,248,14,{font:`700 9px ${f.label}`,color:p.muted,align:'center',maxLines:1});
|
||
checklistCard(ctx,p,M,1010,470,170,'ДОСТАВКА И КОНТРОЛЬ КАЧЕСТВА',CHECK_LINES,{dark:true,fonts:f});addonRow(ctx,p,540,1010,406,170,ADDON_LINES.concat(['Посуда','Мебель']),{dark:true,fonts:f});
|
||
}else if(id==='premium-dark'||id==='premium-emerald'){
|
||
logoOrBrand(ctx,logo,p,{x:M,y:44,dark:true,fonts:f},s.brandName);
|
||
text(ctx,'ПЕРСОНАЛЬНОЕ ПРЕДЛОЖЕНИЕ',M,140,500,16,{font:`700 11px ${f.label}`,color:p.accent,maxLines:1});
|
||
text(ctx,title,M,164,W-2*M,40,{font:`700 32px ${f.display}`,color:'#fff',maxLines:2});
|
||
bulletList(ctx,p,f,M,256,W-2*M,30,CHECK_LINES,'#dfe3e8');
|
||
if(hero)coverImage(ctx,hero,M,396,W-2*M,230,26);
|
||
statBadgeRow(ctx,p,f,M,650,W-2*M,104,[[iconCalendar,dateText(s.date)||'Уточняется','Дата мероприятия'],[iconPeople,s.guests?String(s.guests):'—','Количество гостей'],[iconBox,String(Math.round(classicBoxCount(s))||0),'Коробок в заказе']]);
|
||
text(ctx,'МЕНЮ',W/2,786,W,26,{font:`700 22px ${f.display}`,color:'#fff',align:'center',maxLines:1});
|
||
line(ctx,M,812,410,812,p.line,1);line(ctx,590,812,W-M,812,p.line,1);
|
||
await photoGridSquare(ctx,s,p,M,846,W-2*M,272,3,2,{numbered:true,fonts:f});
|
||
statCards(ctx,s,p,M,1138,565,95,[[Math.round(classicBoxCount(s))||0,'КОРОБОК'],[(s.items||[]).length,'ПОЗИЦИЙ']],{dark:true,fonts:f});
|
||
priceBand(ctx,s,p,640,1138,306,95,{dark:true,fonts:f});
|
||
checklistCard(ctx,p,M,1253,470,113,'ВСЁ ПОД КОНТРОЛЕМ',CHECK_LINES.slice(0,2),{dark:true,fonts:f});
|
||
addonRow(ctx,p,540,1253,406,113,ADDON_LINES,{dark:true,fonts:f});
|
||
}
|
||
canvas.dataset.sunSignatureTemplate=id;canvas.dataset.sunSignatureCover='1';return canvas;
|
||
}
|
||
|
||
function correctPageNumbers(pages,start,total,innerId){const foot=(PALETTES[innerId]||{}).foot||'#fff';pages.forEach((canvas,i)=>{try{const scale=canvas.width/W,ctx=canvas.getContext('2d');ctx.save();ctx.setTransform(scale,0,0,scale,0,0);ctx.fillStyle=foot;ctx.fillRect(W-190,H-50,150,40);text(ctx,String(start+i)+' / '+String(total),W-M,H-34,100,16,{font:'10px Arial',color:'#888',align:'right',maxLines:1});ctx.restore()}catch(_){}})}
|
||
|
||
// ---- Fully-styled inner pages for the 2 polished templates, so the whole document reads as one design instead of a nice cover glued to the generic base pages. ----
|
||
function newPage(p,dark){const canvas=document.createElement('canvas');canvas.width=Math.round(W*SCALE);canvas.height=Math.round(H*SCALE);const ctx=canvas.getContext('2d',{alpha:false});ctx.setTransform(SCALE,0,0,SCALE,0,0);ctx.fillStyle=p.bg;ctx.fillRect(0,0,W,H);if(dark){ctx.save();roundRect(ctx,20,20,W-40,H-40,0,null,p.accent,1.4);ctx.restore()}return{canvas,ctx}}
|
||
function contentHeader(ctx,p,f,s,label,dark){text(ctx,String(s.brandName||'Солнце Кейтеринг').toUpperCase(),M,50,320,16,{font:`700 11px ${f.label}`,color:p.accent,maxLines:1});text(ctx,eventTitle(s),M,70,520,26,{font:`600 20px ${f.display}`,color:dark?'#fff':p.ink,maxLines:1});text(ctx,label,W-M,58,260,16,{font:`600 11px ${f.label}`,color:p.muted,align:'right',maxLines:1});line(ctx,M,108,W-M,108,p.line,1)}
|
||
async function itemRowsPage(ctx,p,f,items,y0,dark){
|
||
const rowH=dark?96:104,gap=dark?12:14;
|
||
const imgs=await Promise.all(items.map(it=>loadImage(it.photoData||'')));
|
||
items.forEach((item,i)=>{
|
||
const yy=y0+i*(rowH+gap),photoSize=rowH-20,px=M+10,py=yy+10,textX=px+photoSize+18,sum=Number(item.sum??(Number(item.unitPrice||0)*Number(item.qty||0)));
|
||
roundRect(ctx,M,yy,W-2*M,rowH,16,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.12)':p.line);
|
||
roundRect(ctx,px,py,photoSize,photoSize,12,p.deep,null);
|
||
if(imgs[i])coverImage(ctx,imgs[i],px,py,photoSize,photoSize,12);
|
||
text(ctx,item.name||'Позиция меню',textX,yy+14,W-2*M-(textX-M)-135,22,{font:`600 16px ${f.display}`,color:dark?'#fff':p.ink,maxLines:1});
|
||
const meta=[item.weight?String(item.weight):'',item.categoryName?String(item.categoryName):''].filter(Boolean).join(' · ');
|
||
if(meta)text(ctx,meta,textX,yy+42,W-2*M-(textX-M)-135,16,{font:`500 12px ${f.label}`,color:p.muted,maxLines:1});
|
||
text(ctx,'× '+Math.round(Number(item.qty||1)),W-M-16,yy+16,110,18,{font:`700 13px ${f.label}`,color:p.accent,align:'right',maxLines:1});
|
||
text(ctx,money(sum),W-M-16,yy+rowH-36,110,18,{font:`700 16px ${f.label}`,color:dark?'#fff':p.deep,align:'right',maxLines:1});
|
||
});
|
||
}
|
||
function pricingPage(ctx,p,f,s,dark){
|
||
text(ctx,'ИТОГОВАЯ СМЕТА',M,132,W-2*M,34,{font:`600 28px ${f.display}`,color:dark?'#fff':p.deep,maxLines:1});
|
||
const pr=s.pricing||{};let y=200;
|
||
const rows=[['Стоимость позиций',Number(pr.base||0),false]];
|
||
if(Number(pr.manual||0)>0)rows.push(['Скидка',Number(pr.manual),true]);
|
||
if(Number(pr.promo||0)>0)rows.push(['Промокод'+(s.promoCode?` «${s.promoCode}»`:''),Number(pr.promo),true]);
|
||
rows.push(['Меню после скидок',Number(pr.itemsTotal||0),false]);
|
||
rows.push(['Доставка',Number(pr.delivery||0),false]);
|
||
rows.forEach(([label,val,negative])=>{
|
||
roundRect(ctx,M,y,W-2*M,52,14,dark?'rgba(255,255,255,.04)':p.paper,dark?'rgba(255,255,255,.12)':p.line);
|
||
text(ctx,label,M+20,y+17,520,18,{font:`500 14px ${f.label}`,color:dark?'#eef2ee':p.ink,maxLines:1});
|
||
text(ctx,(negative?'− ':'')+money(val),W-M-20,y+17,260,18,{font:`700 16px ${f.label}`,color:negative?'#c0554a':(dark?'#fff':p.deep),align:'right',maxLines:1});
|
||
y+=52+10;
|
||
});
|
||
y+=8;
|
||
roundRect(ctx,M,y,W-2*M,160,20,p.accent,null);
|
||
text(ctx,'ИТОГО К ОПЛАТЕ',M+28,y+24,400,16,{font:`700 12px ${f.label}`,color:p.deep,maxLines:1});
|
||
text(ctx,money(pr.total),M+28,y+48,W-2*M-56,56,{font:`700 44px ${f.display}`,color:p.deep,maxLines:1});
|
||
if(s.guests)text(ctx,'на гостя: '+money(Number(pr.itemsTotal||pr.total||0)/Math.max(1,Number(s.guests))),M+28,y+118,400,20,{font:`600 14px ${f.label}`,color:p.deep,maxLines:1});
|
||
y+=160+36;
|
||
text(ctx,'Спасибо, что выбираете нас — будем рады сделать ваше событие особенным.',M,y,W-2*M,22,{font:`500 15px ${f.label}`,color:p.muted,maxLines:2});
|
||
}
|
||
async function renderContentPages(s,id){
|
||
await ensureFontsReady(id);
|
||
const p=PALETTES[id],f=fontsFor(id),dark=id!=='cream-elegance';
|
||
const items=s.items||[],top=120,bottom=H-90,rowH=dark?96:104,gap=dark?12:14;
|
||
const perPage=Math.max(1,Math.floor((bottom-top+gap)/(rowH+gap)));
|
||
const pageCount=Math.max(1,Math.ceil(items.length/perPage));
|
||
const pages=[];
|
||
for(let pi=0;pi<pageCount;pi++){
|
||
const {canvas,ctx}=newPage(p,dark);
|
||
contentHeader(ctx,p,f,s,`Меню · стр. ${pi+1} из ${pageCount}`,dark);
|
||
await itemRowsPage(ctx,p,f,items.slice(pi*perPage,(pi+1)*perPage),top,dark);
|
||
pages.push(canvas);
|
||
}
|
||
const priced=newPage(p,dark);
|
||
contentHeader(priced.ctx,p,f,s,'Стоимость',dark);
|
||
pricingPage(priced.ctx,p,f,s,dark);
|
||
pages.push(priced.canvas);
|
||
return pages;
|
||
}
|
||
|
||
async function renderPages(snapshot,baseRenderer){
|
||
if(typeof baseRenderer!=='function')throw new Error('Base PDF renderer is unavailable');
|
||
const id=String(snapshot?.offerTemplateId||'');
|
||
if(!SIGNATURE_SET.has(id))return baseRenderer(snapshot);
|
||
const [cover,innerPages]=await Promise.all([renderCover(snapshot,id),renderContentPages(snapshot,id)]);
|
||
const pages=[cover,...(innerPages||[])],total=pages.length,p=PALETTES[id]||PALETTES['cream-elegance'];
|
||
try{const ctx=cover.getContext('2d');ctx.save();ctx.setTransform(SCALE,0,0,SCALE,0,0);footer(ctx,p,1,total,snapshot?.brandName,fontsFor(id));ctx.restore()}catch(_){}
|
||
const f=fontsFor(id);
|
||
(innerPages||[]).forEach((canvas,i)=>{try{const scale=canvas.width/W,ctx=canvas.getContext('2d');ctx.save();ctx.setTransform(scale,0,0,scale,0,0);footer(ctx,p,2+i,total,snapshot?.brandName,f);ctx.restore()}catch(_){}});
|
||
return pages;
|
||
}
|
||
|
||
function installPreviewStyles(){if(document.getElementById('sunSignatureOfferPreviewV18'))return;const style=document.createElement('style');style.id='sunSignatureOfferPreviewV18';style.textContent=`
|
||
.sun-offer-template-mini[data-template-mini="cream-elegance"] i,.sun-offer-template-mini[data-template-mini="neon-menu"] i,.sun-offer-template-mini[data-template-mini="emerald-circles"] i,.sun-offer-template-mini[data-template-mini="midnight-checklist"] i,.sun-offer-template-mini[data-template-mini="gourmet-hero"] i,.sun-offer-template-mini[data-template-mini="diamond-gold"] i,.sun-offer-template-mini[data-template-mini="premium-dark"] i,.sun-offer-template-mini[data-template-mini="premium-emerald"] i{display:none!important}
|
||
.sun-offer-template-mini[data-template-mini="cream-elegance"]{background:radial-gradient(circle at 78% 30%,#fff 0 18%,#8f8536 19% 21%,transparent 22%),linear-gradient(#fdf7ea,#f2e3bf)!important}
|
||
.sun-offer-template-mini[data-template-mini="neon-menu"]{background:linear-gradient(90deg,rgba(57,226,122,.35),transparent 45%),linear-gradient(135deg,#0d1210,#070908)!important;border-color:#1f4430!important}
|
||
.sun-offer-template-mini[data-template-mini="emerald-circles"]{background:radial-gradient(circle at 70% 55%,#c9a24a 0 6%,transparent 7%),radial-gradient(circle at 85% 55%,#c9a24a 0 6%,transparent 7%),linear-gradient(135deg,#0d2226,#08181c)!important;border-color:#1f4642!important}
|
||
.sun-offer-template-mini[data-template-mini="midnight-checklist"]{background:repeating-linear-gradient(0deg,#0b2417 0 11%,#123822 11% 12%),#06170f!important;border-color:#1f4a30!important}
|
||
.sun-offer-template-mini[data-template-mini="gourmet-hero"]{background:linear-gradient(90deg,#141311 0 55%,#3a352a 55% 58%,#0a0a09 58%)!important;border-color:#3a352a!important}
|
||
.sun-offer-template-mini[data-template-mini="diamond-gold"]{background:radial-gradient(circle at 75% 35%,#e0b95c 0 5%,transparent 6%),linear-gradient(135deg,#123d29,#071510)!important;border-color:#204b34!important}
|
||
.sun-offer-template-mini[data-template-mini="premium-dark"]{background:radial-gradient(circle at 18% 15%,rgba(224,165,69,.35),transparent 40%),linear-gradient(160deg,#12161d,#0a0d12)!important;border-color:#252b35!important}
|
||
.sun-offer-template-mini[data-template-mini="premium-emerald"]{background:radial-gradient(circle at 18% 15%,rgba(63,207,142,.35),transparent 40%),linear-gradient(160deg,#121d19,#0a1210)!important;border-color:#233830!important}
|
||
`;document.head.appendChild(style)}
|
||
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',installPreviewStyles,{once:true});else installPreviewStyles();
|
||
|
||
window.SunSignatureOfferPDFV18={VERSION,RELEASE,SIGNATURE_IDS:[...SIGNATURE_IDS],renderPages,renderCover,checks:()=>({version:VERSION,signature:SIGNATURE_IDS.length})};
|
||
})();
|