// Variant 4 — Soft Sky / Cloud Light
const { useState, useMemo, useEffect } = React;
function getFILMS() { return window.ALLIANCE_DATA.FILMS; }
function t(path, lang) { return window.ALLIANCE_DATA.t(path, lang); }
let FILMS = window.ALLIANCE_DATA.FILMS;
function stockLbl(s, lang) {
if (s === 'high') return t('catalog.stockHigh', lang);
if (s === 'medium') return t('catalog.stockMed', lang);
return t('catalog.stockLow', lang);
}
function PageGallery({ pageName, lang }) {
const [images, setImages] = useState(() => (window.ALLIANCE_DATA.PAGE_IMAGES[pageName] || []).slice());
useEffect(() => {
function onUpdate() { setImages((window.ALLIANCE_DATA.PAGE_IMAGES[pageName] || []).slice()); }
window.addEventListener('alliance-data-updated', onUpdate);
return () => window.removeEventListener('alliance-data-updated', onUpdate);
}, [pageName]);
if (!images.length) return null;
return (
{images.map((img, i) => (
{img.caption && img.caption[lang] &&
{img.caption[lang]}
}
))}
);
}
function Header({ page, setPage, lang, setLang }) {
const nav = [['home','nav.home'],['catalog','nav.catalog'],['about','nav.about'],['production','nav.production'],['careers','nav.careers'],['contacts','nav.contacts']];
return (
);
}
function Hero({ lang, setPage }) {
const [slides, setSlides] = useState(() => window.ALLIANCE_DATA.SLIDES.slice());
const [idx, setIdx] = useState(0);
useEffect(() => {
function onUpdate() { setSlides(window.ALLIANCE_DATA.SLIDES.slice()); setIdx(0); }
window.addEventListener('alliance-data-updated', onUpdate);
return () => window.removeEventListener('alliance-data-updated', onUpdate);
}, []);
useEffect(() => {
const id = setInterval(() => setIdx(i => (i + 1) % slides.length), 4500);
return () => clearInterval(id);
}, []);
return (
{lang === 'ua' ? 'Зі складу в Києві · день-у-день' : 'Ex-stock Kyiv · same day'}
{lang === 'ua' ? <>Плівки CPP, BOPP та PET для гнучкої упаковки > : <>CPP, BOPP & PET films for flexible packaging >}
{t('hero.sub', lang)}
setPage('catalog')}>{t('hero.cta1', lang)} →
setPage('contacts')}>{t('hero.cta2', lang)}
{slides.map((s, i) => (
))}
{lang === 'ua' ? '35+ типів плівок' : '35+ film grades'}
{lang === 'ua' ? 'CPP · BOPP · PET · OPP у наявності' : 'CPP · BOPP · PET · OPP in stock'}
{slides.map((_, i) => (
setIdx(i)} aria-label={'Slide ' + (i + 1)}>
))}
);
}
function Stats({ lang }) {
return (
{window.ALLIANCE_DATA.T.stats.map((s, i) => (
))}
);
}
function Families({ lang, setPage, setFilter }) {
const cpp = FILMS.filter(f => f.family === 'CPP');
const bopp = FILMS.filter(f => f.family === 'BOPP');
const opp = FILMS.filter(f => f.family === 'OPP');
const pet = FILMS.filter(f => f.family === 'PET');
const fams = [
{ k: 'CPP', code: 'CPP', cls: 'cpp',
title: { ua: 'CPP · литий ПП', en: 'CPP · Cast PP' },
lead: { ua: 'Еластична плівка з відмінною термозварюваністю. Внутрішній шар ламінатів.', en: 'Soft film with excellent heat-sealing. Inner laminate layer.' },
films: cpp
},
{ k: 'BOPP', code: 'BOPP', cls: 'bopp',
title: { ua: 'BOPP · двовісно орієнтований ПП', en: 'BOPP · Biaxially Oriented PP' },
lead: { ua: 'Жорстка прозора плівка для друку, snek-пакування та етикеток.', en: 'Stiff clear film for print, snack packaging and labels.' },
films: bopp
},
{ k: 'PET', code: 'PET', cls: 'pet',
title: { ua: 'PET · поліестерова плівка', en: 'PET · Polyester film' },
lead: { ua: 'Термостійка плівка з високою міцністю. Бар\'єр, фольгування, лотки.', en: 'Heat-stable, high-strength film. Barriers, foiling, trays.' },
films: pet
},
{ k: 'OPP', code: 'OPP', cls: 'opp',
title: { ua: 'OPP · спеціальні плівки PP', en: 'OPP · specialty PP films' },
lead: { ua: 'Монооріентовані та литі PP для тютюну, технічних застосувань.', en: 'Mono-oriented and cast PP for tobacco, technical uses.' },
films: opp
}
];
return (
{lang === 'ua' ? 'СІМЕЙСТВА ПЛІВОК' : 'FILM FAMILIES'}
{lang === 'ua' ? 'Чотири технології — повне покриття гнучкої упаковки' : 'Four technologies — complete coverage of flexible packaging'}
{lang === 'ua' ? 'CPP, BOPP, PET та OPP — кожна сімʼя під своє завдання. Разом 35+ грейдів у наявності та під замовлення.' : 'CPP, BOPP, PET and OPP — each family for its task. 35+ grades total, ex-stock or on order.'}
{fams.map(f => (
{ setFilter(f.k); setPage('catalog'); }}>
{f.title[lang]}
{f.lead[lang]}
{f.films.map(x => {x.code} )}
))}
);
}
function Advantages({ lang }) {
const items = window.ALLIANCE_DATA.T.advantages.items;
return (
{lang === 'ua' ? 'ЧОМУ МИ' : 'WHY US'}
{t('advantages.title', lang)}
{items.map((a, i) => (
0{i + 1}
{a.t[lang]}
{a.d[lang]}
))}
);
}
function CatalogPage({ lang, setPage, setProduct, filter, setFilter }) {
const [q, setQ] = useState('');
const list = useMemo(() => FILMS.filter(f => {
if (filter !== 'all' && f.family !== filter) return false;
if (q) {
const lq = q.toLowerCase();
if (!f.name[lang].toLowerCase().includes(lq) && !f.code.toLowerCase().includes(lq)) return false;
}
return true;
}), [filter, q, lang]);
return (
<>
{lang === 'ua' ? 'КАТАЛОГ' : 'CATALOG'}
{t('catalog.title', lang)}
{t('catalog.sub', lang)}
{['all', 'CPP', 'BOPP', 'OPP', 'PET'].map(k => (
setFilter(k)}>
{k === 'all' ? t('catalog.all', lang) : k}
))}
setQ(e.target.value)} />
{list.map(f => (
{ setProduct(f.id); setPage('product'); }}>
{f.family} · {f.code}
● {stockLbl(f.stock, lang)}
{f.code}
{f.name[lang]}
{f.tagline[lang]}
{f.thickness}
{f.applications[lang].length} {lang === 'ua' ? 'застосувань' : 'uses'}
))}
>
);
}
function ProductPage({ lang, productId, setPage }) {
const f = FILMS.find(x => x.id === productId) || FILMS[0];
return (
setPage('catalog')}>← {t('catalog.back', lang)}
{f.code} · {f.family}
{f.name[lang]}
{f.tagline[lang]}
{t('catalog.thickness', lang)}
{f.thickness}
{t('catalog.stock', lang)}
{stockLbl(f.stock, lang)}
{t('catalog.features', lang)}
{f.features[lang].map((x, i) => {x} )}
{t('catalog.applications', lang)}
{f.applications[lang].map((x, i) => {x} )}
setPage('contacts')}>{t('catalog.request', lang)}
setPage('catalog')}>{t('catalog.back', lang)}
);
}
function AboutPage({ lang }) {
return (
{lang === 'ua' ? 'ПРО НАС' : 'ABOUT'}
{t('about.title', lang)}
{t('about.lead', lang)}
{t('about.p1', lang)}
{t('about.p2', lang)}
);
}
function LogisticsPage({ lang }) {
const steps = window.ALLIANCE_DATA.T.logistics.steps;
return (
{lang === 'ua' ? 'ЛОГІСТИКА' : 'LOGISTICS'}
{t('logistics.title', lang)}
{steps.map((s, i) => (
{String(i + 1).padStart(2, '0')}
{s.t[lang]}
{s.d[lang]}
))}
);
}
function ProductionPage({ lang, setPage }) {
const P = window.ALLIANCE_DATA.T.production;
return (
{lang === 'ua' ? 'ВИРОБНИЦТВО ТА ПОСТАЧАННЯ' : 'PRODUCTION & SUPPLY'}
{P.title[lang]}
{P.sub[lang]}
{P.capacity.map((c, i) => (
{c.v} {c.u[lang]}
{c.l[lang]}
))}
{P.ourTitle[lang]}
{P.ourSub[lang]}
{P.services.map((s, i) => (
0{i + 1}
{s.t[lang]}
{s.d[lang]}
{s.stat}
{s.statLabel[lang]}
))}
{P.processTitle[lang]}
{P.processSub[lang]}
{P.steps.map((s, i) => (
{String(i + 1).padStart(2, '0')}
{s.t[lang]}
{s.d[lang]}
))}
{P.partnersTitle[lang]}
{P.partnersSub[lang]}
{P.partners.map((p, i) => (
{p.country[lang]}
{p.spec[lang]}
))}
{lang === 'ua' ? 'Замовте порізку або зразок плівки' : 'Order slitting or a film sample'}
{lang === 'ua' ? 'Надішліть ТЗ — менеджер запропонує найкращий грейд та підготує комерційну.' : 'Send your spec — our manager will suggest the best grade and prepare a quote.'}
setPage('contacts')}>{lang === 'ua' ? 'Звʼязатися →' : 'Get in touch →'}
);
}
function CareersPage({ lang, setPage }) {
const T = window.ALLIANCE_DATA.T;
const careers = T.careers;
const jobs = careers.jobs || [];
const labels = careers.labels;
const [selected, setSelected] = useState(null);
const [filter, setFilter] = useState('all');
const [sent, setSent] = useState(false);
const [position, setPosition] = useState('');
const depts = useMemo(() => {
const set = new Set(jobs.map(j => j.dept[lang]));
return ['all', ...Array.from(set)];
}, [jobs, lang]);
const filtered = useMemo(() => {
if (filter === 'all') return jobs;
return jobs.filter(j => j.dept[lang] === filter);
}, [jobs, filter, lang]);
function openJob(j) {
setSelected(j);
setPosition(j.title[lang]);
window.scrollTo(0, 0);
}
function applyTo(j) {
setPosition(j ? j.title[lang] : '');
setSelected(null);
setTimeout(() => {
const el = document.getElementById('careers-apply');
if (el) el.scrollIntoView({ behavior: 'smooth', block: 'start' });
}, 50);
}
if (selected) {
return (
setSelected(null)}>{labels.back[lang]}
{selected.dept[lang]}
{selected.title[lang]}
{selected.summary[lang]}
{labels.location[lang]} {selected.location[lang]}
{labels.type[lang]} {selected.type[lang]}
{labels.salary[lang]} {selected.salary[lang]}
{labels.published[lang]} {selected.published}
applyTo(selected)}>{labels.apply[lang]} →
{labels.responsibilities[lang]}
{selected.responsibilities[lang].map((r, i) => {r} )}
{labels.requirements[lang]}
{selected.requirements[lang].map((r, i) => {r} )}
);
}
return (
{lang === 'ua' ? 'РОБОТА В КОМАНДІ' : 'WORK WITH US'}
{careers.title[lang]}
{careers.sub[lang]}
{careers.culture.map((c, i) => (
0{i + 1}
{c.t[lang]}
{c.d[lang]}
))}
{labels.open[lang]} · {jobs.length}
{depts.map(d => (
setFilter(d)}
>
{d === 'all' ? labels.all[lang] : d}
))}
{filtered.map(j => (
openJob(j)}>
{j.dept[lang]}
{j.title[lang]}
{j.summary[lang]}
{j.location[lang]}
{j.salary[lang]}
{labels.apply[lang]} →
))}
{careers.applyTitle[lang]}
{careers.applySub[lang]}
);
}
function ContactsPage({ lang }) {
const [fields, setFields] = useState({ company: '', name: '', phone: '', email: '', interest: '', message: '' });
const [errors, setErrors] = useState({});
const [sent, setSent] = useState(false);
const [sending, setSending] = useState(false);
const [sendError, setSendError] = useState('');
const set = k => e => setFields(f => ({ ...f, [k]: e.target.value }));
const validate = () => {
const e = {};
if (!fields.name.trim())
e.name = lang === 'ua' ? 'Введіть контактну особу' : 'Please enter contact name';
if (!fields.phone.trim() && !fields.email.trim())
e.phone = lang === 'ua' ? 'Вкажіть телефон або e-mail' : 'Enter phone or email';
if (!fields.message.trim())
e.message = lang === 'ua' ? 'Напишіть повідомлення' : 'Please enter your message';
return e;
};
const handleSubmit = async e => {
e.preventDefault();
const errs = validate();
setErrors(errs);
if (Object.keys(errs).length) return;
setSending(true);
setSendError('');
try {
const res = await fetch('/api/contact.php', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(fields)
});
const json = await res.json();
if (json.ok) {
setSent(true);
} else {
setSendError(lang === 'ua' ? 'Помилка надсилання. Спробуйте пізніше.' : 'Send error. Please try later.');
}
} catch {
setSendError(lang === 'ua' ? 'Помилка з\'єднання. Спробуйте пізніше.' : 'Connection error. Please try later.');
} finally {
setSending(false);
}
};
const req = * ;
const err = k => errors[k] ? {errors[k]} : null;
return (
{lang === 'ua' ? 'КОНТАКТИ' : 'CONTACTS'}
{t('contacts.title', lang)}
{t('contacts.sub', lang)}
{t('company.legal', lang)}
{lang === 'ua' ? 'Офіс' : 'Office'}
{t('contacts.office.addr', lang)}
{lang === 'ua' ? 'Склад' : 'Warehouse'}
{t('contacts.office.warehouse', lang)}
{lang === 'ua' ? 'Телефон' : 'Phone'}
{(window.ALLIANCE_DATA.COMPANY || {}).phone}
Email
{(window.ALLIANCE_DATA.COMPANY || {}).email}
{lang === 'ua' ? 'Години' : 'Hours'}
{t('contacts.office.hours', lang)}
{sent && ✓ {t('contacts.form.sent', lang)}
}
{sendError && {sendError}
}
{!sent && <>
{t('contacts.form.interest', lang)}
— {FILMS.map(f => {f.code} — {f.name[lang]} )}
{t('contacts.form.message', lang)}{req} {err('message')}
{req} — {lang === 'ua' ? 'обов\'язкові поля' : 'required fields'}
{sending ? '...' : t('contacts.form.submit', lang) + ' →'}
>}
);
}
function Footer({ lang, setPage }) {
return (
{t('company.legal', lang)}
{t('company.role', lang)}
CPP {FILMS.filter(f=>f.family==='CPP').slice(0,5).map(f=>{f.code} )}
BOPP {FILMS.filter(f=>f.family==='BOPP').slice(0,5).map(f=>{f.code} )}
PET {FILMS.filter(f=>f.family==='PET').slice(0,5).map(f=>{f.code} )}
OPP {FILMS.filter(f=>f.family==='OPP').slice(0,5).map(f=>{f.code} )}
{lang==='ua'?'Компанія':'Company'}
{t('footer.rights', lang)}
{t('footer.edrpou', lang)}
);
}
function App() {
const [lang, setLang] = useState('ua');
const [page, setPage] = useState('home');
const [product, setProduct] = useState(null);
const [filter, setFilter] = useState('all');
const [, setTick] = useState(0);
useEffect(() => { window.scrollTo(0, 0); }, [page, product]);
useEffect(() => {
const onUpdate = () => { FILMS = window.ALLIANCE_DATA.FILMS; setTick(t => t + 1); };
window.addEventListener('alliance-data-updated', onUpdate);
window.addEventListener('storage', onUpdate);
return () => {
window.removeEventListener('alliance-data-updated', onUpdate);
window.removeEventListener('storage', onUpdate);
};
}, []);
FILMS = window.ALLIANCE_DATA.FILMS;
return (
{page === 'home' && (<>
>)}
{page === 'catalog' &&
}
{page === 'product' &&
}
{page === 'about' &&
}
{page === 'production' &&
}
{page === 'careers' &&
}
{page === 'contacts' &&
}
);
}
ReactDOM.createRoot(document.getElementById('root')).render( );