Some checks are pending
NordaBiz Tests / Unit & Integration Tests (push) Waiting to run
NordaBiz Tests / E2E Tests (Playwright) (push) Blocked by required conditions
NordaBiz Tests / Smoke Tests (Production) (push) Blocked by required conditions
NordaBiz Tests / Send Failure Notification (push) Blocked by required conditions
Production moved from on-prem VM 249 (10.22.68.249) to OVH VPS (57.128.200.27, inpi-vps-waw01). Updated ALL documentation, slash commands, memory files, architecture docs, and deploy procedures. Added |local_time Jinja filter (UTC→Europe/Warsaw) and converted 155 .strftime() calls across 71 templates so timestamps display in Polish timezone regardless of server timezone. Also includes: created_by_id tracking, abort import fix, ICS calendar fix for missing end times, Pros Poland data cleanup. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1113 lines
36 KiB
JavaScript
1113 lines
36 KiB
JavaScript
const pptxgen = require("pptxgenjs");
|
|
const React = require("react");
|
|
const ReactDOMServer = require("react-dom/server");
|
|
const sharp = require("sharp");
|
|
const {
|
|
FaBuilding, FaRobot, FaComments, FaCalendarAlt, FaAtom, FaShieldAlt,
|
|
FaRocket, FaQuestion, FaSearch, FaUsers, FaBullhorn, FaLock,
|
|
FaChartLine, FaHandshake, FaIndustry, FaGlobe, FaEnvelope, FaBell,
|
|
FaMobileAlt, FaNewspaper, FaUserShield, FaMoneyBillWave, FaStar,
|
|
FaCheckCircle, FaLightbulb, FaMapMarkedAlt, FaFileAlt
|
|
} = require("react-icons/fa");
|
|
|
|
function renderIconSvg(IconComponent, color = "#000000", size = 256) {
|
|
return ReactDOMServer.renderToStaticMarkup(
|
|
React.createElement(IconComponent, { color, size: String(size) })
|
|
);
|
|
}
|
|
|
|
async function iconToBase64Png(IconComponent, color, size = 256) {
|
|
const svg = renderIconSvg(IconComponent, color, size);
|
|
const pngBuffer = await sharp(Buffer.from(svg)).png().toBuffer();
|
|
return "image/png;base64," + pngBuffer.toString("base64");
|
|
}
|
|
|
|
// Color palette
|
|
const C = {
|
|
darkBg: "0F172A",
|
|
navyBg: "1E293B",
|
|
primary: "0891B2",
|
|
primaryLight: "22D3EE",
|
|
accent: "06B6D4",
|
|
lightBg: "F1F5F9",
|
|
white: "FFFFFF",
|
|
cardBg: "FFFFFF",
|
|
textDark: "1E293B",
|
|
textMuted: "64748B",
|
|
textLight: "CBD5E1",
|
|
green: "10B981",
|
|
amber: "F59E0B",
|
|
red: "EF4444",
|
|
purple: "8B5CF6",
|
|
indigo: "6366F1",
|
|
tealDark: "065F46",
|
|
};
|
|
|
|
const FONT_TITLE = "Georgia";
|
|
const FONT_BODY = "Calibri";
|
|
|
|
const makeShadow = () => ({ type: "outer", blur: 6, offset: 2, angle: 135, color: "000000", opacity: 0.12 });
|
|
|
|
function addSlideNumber(slide, num, total) {
|
|
slide.addText(`${num} / ${total}`, {
|
|
x: 8.8, y: 5.2, w: 1, h: 0.3,
|
|
fontSize: 9, fontFace: FONT_BODY, color: C.textLight, align: "right"
|
|
});
|
|
}
|
|
|
|
function addSectionHeader(slide, icon, title, subtitle, num, total) {
|
|
// Dark left panel
|
|
slide.addShape(slide._slideLayout ? "rect" : "rect", {});
|
|
// We'll just build the full slide manually
|
|
}
|
|
|
|
async function createPresentation() {
|
|
const pres = new pptxgen();
|
|
pres.layout = "LAYOUT_16x9";
|
|
pres.author = "Maciej Pienczyn";
|
|
pres.title = "NordaBiznes.pl — Prezentacja portalu";
|
|
|
|
const TOTAL = 14;
|
|
|
|
// Pre-render icons
|
|
const icons = {};
|
|
const iconMap = {
|
|
building: [FaBuilding, C.white],
|
|
robot: [FaRobot, C.white],
|
|
comments: [FaComments, C.white],
|
|
calendar: [FaCalendarAlt, C.white],
|
|
atom: [FaAtom, C.white],
|
|
shield: [FaShieldAlt, C.white],
|
|
rocket: [FaRocket, C.white],
|
|
question: [FaQuestion, C.white],
|
|
search: [FaSearch, C.primary],
|
|
users: [FaUsers, C.primary],
|
|
bullhorn: [FaBullhorn, C.primary],
|
|
lock: [FaLock, C.primary],
|
|
chart: [FaChartLine, C.primary],
|
|
handshake: [FaHandshake, C.primary],
|
|
industry: [FaIndustry, C.primary],
|
|
globe: [FaGlobe, C.primary],
|
|
envelope: [FaEnvelope, C.primary],
|
|
bell: [FaBell, C.primary],
|
|
mobile: [FaMobileAlt, C.primary],
|
|
newspaper: [FaNewspaper, C.primary],
|
|
userShield: [FaUserShield, C.primary],
|
|
money: [FaMoneyBillWave, C.primary],
|
|
star: [FaStar, C.amber],
|
|
check: [FaCheckCircle, C.green],
|
|
lightbulb: [FaLightbulb, C.amber],
|
|
map: [FaMapMarkedAlt, C.primary],
|
|
file: [FaFileAlt, C.primary],
|
|
// Dark bg versions
|
|
buildingDark: [FaBuilding, C.primaryLight],
|
|
robotDark: [FaRobot, C.primaryLight],
|
|
commentsDark: [FaComments, C.primaryLight],
|
|
calendarDark: [FaCalendarAlt, C.primaryLight],
|
|
atomDark: [FaAtom, C.primaryLight],
|
|
industryDark: [FaIndustry, C.primaryLight],
|
|
shieldDark: [FaShieldAlt, C.primaryLight],
|
|
rocketDark: [FaRocket, C.primaryLight],
|
|
checkWhite: [FaCheckCircle, C.white],
|
|
globeWhite: [FaGlobe, C.white],
|
|
searchWhite: [FaSearch, C.white],
|
|
lightbulbWhite: [FaLightbulb, C.white],
|
|
handshakeWhite: [FaHandshake, C.white],
|
|
};
|
|
|
|
for (const [key, [comp, color]] of Object.entries(iconMap)) {
|
|
icons[key] = await iconToBase64Png(comp, `#${color}`, 256);
|
|
}
|
|
|
|
// Helper: colored icon circle
|
|
function addIconCircle(slide, iconKey, x, y, size, bgColor) {
|
|
slide.addShape(pres.shapes.OVAL, {
|
|
x, y, w: size, h: size,
|
|
fill: { color: bgColor }
|
|
});
|
|
const pad = size * 0.25;
|
|
slide.addImage({
|
|
data: icons[iconKey],
|
|
x: x + pad, y: y + pad,
|
|
w: size - pad * 2, h: size - pad * 2
|
|
});
|
|
}
|
|
|
|
// Helper: content card with left accent
|
|
function addCard(slide, x, y, w, h, accentColor) {
|
|
slide.addShape(pres.shapes.RECTANGLE, {
|
|
x, y, w, h,
|
|
fill: { color: C.white },
|
|
shadow: makeShadow()
|
|
});
|
|
slide.addShape(pres.shapes.RECTANGLE, {
|
|
x, y, w: 0.06, h,
|
|
fill: { color: accentColor || C.primary }
|
|
});
|
|
}
|
|
|
|
// Logo paths
|
|
const nordaLogoPath = "/Users/maciejpi/claude/projects/active/nordabiz/static/img/logo-email.png";
|
|
const inpiLogoPath = "/tmp/inpi-logo.png";
|
|
|
|
// =============================================
|
|
// SLIDE 1: TYTUŁ
|
|
// =============================================
|
|
let s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
|
|
// Accent line top
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0, y: 0, w: 10, h: 0.06,
|
|
fill: { color: C.primary }
|
|
});
|
|
|
|
// Norda compass logo — right side, large
|
|
s.addImage({
|
|
path: nordaLogoPath,
|
|
x: 7.5, y: 1.2, w: 2.0, h: 2.0,
|
|
transparency: 15
|
|
});
|
|
|
|
s.addText("NordaBiznes.pl", {
|
|
x: 0.8, y: 1.2, w: 6.5, h: 1.2,
|
|
fontSize: 48, fontFace: FONT_TITLE, color: C.white, bold: true, margin: 0
|
|
});
|
|
s.addText("Platforma członków Izby NORDA", {
|
|
x: 0.8, y: 2.3, w: 6.5, h: 0.6,
|
|
fontSize: 22, fontFace: FONT_BODY, color: C.primaryLight, margin: 0
|
|
});
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 3.2, w: 2, h: 0.04,
|
|
fill: { color: C.primary }
|
|
});
|
|
|
|
s.addText("Prezentacja dla członków Izby", {
|
|
x: 0.8, y: 3.6, w: 5, h: 0.4,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textLight, margin: 0
|
|
});
|
|
s.addText("9 kwietnia 2026 | Urząd Miasta Wejherowo", {
|
|
x: 0.8, y: 4.0, w: 5, h: 0.4,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textLight, margin: 0
|
|
});
|
|
s.addText("Maciej Pienczyn | InPi sp. z o.o.", {
|
|
x: 0.8, y: 4.6, w: 5, h: 0.4,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textLight, margin: 0
|
|
});
|
|
|
|
// =============================================
|
|
// SLIDE 2: AGENDA
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Co dziś pokażemy", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 32, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.05, w: 1.2, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
const agendaItems = [
|
|
["building", "Twoja firma w Izbie", "Profil, wizytówka, wyszukiwanie"],
|
|
["robot", "NordaGPT", "Asystent AI, który zna wszystkich"],
|
|
["comments", "Komunikacja", "Wiadomości, forum, ogłoszenia B2B"],
|
|
["calendar", "Kalendarz", "Wydarzenia, zapisy, przypomnienia"],
|
|
["atom", "PEJ i Kaszubia", "Elektrownia jądrowa, inwestycje"],
|
|
["shield", "Prywatność", "Kto widzi co, bezpieczeństwo"],
|
|
];
|
|
|
|
const startY = 1.4;
|
|
for (let i = 0; i < agendaItems.length; i++) {
|
|
const [icon, title, desc] = agendaItems[i];
|
|
const row = i;
|
|
const yPos = startY + row * 0.65;
|
|
|
|
addIconCircle(s, icon, 0.8, yPos + 0.05, 0.45, C.primary);
|
|
|
|
s.addText(title, {
|
|
x: 1.45, y: yPos, w: 3, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0, valign: "middle"
|
|
});
|
|
s.addText(desc, {
|
|
x: 1.45, y: yPos + 0.3, w: 5, h: 0.3,
|
|
fontSize: 12, fontFace: FONT_BODY, color: C.textMuted, margin: 0
|
|
});
|
|
}
|
|
|
|
// Right side - stats
|
|
addCard(s, 6.5, 1.4, 3, 3.5, C.primary);
|
|
s.addText("Portal w liczbach", {
|
|
x: 6.8, y: 1.6, w: 2.5, h: 0.35,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
|
|
const stats = [
|
|
["150+", "firm członkowskich"],
|
|
["38", "uczestników spotkania"],
|
|
["57", "wydań od startu"],
|
|
["17", "kategorii branżowych"],
|
|
["6", "poziomów dostępu"],
|
|
];
|
|
for (let i = 0; i < stats.length; i++) {
|
|
s.addText(stats[i][0], {
|
|
x: 6.8, y: 2.15 + i * 0.52, w: 0.8, h: 0.4,
|
|
fontSize: 20, fontFace: FONT_TITLE, color: C.primary, bold: true, margin: 0, valign: "middle"
|
|
});
|
|
s.addText(stats[i][1], {
|
|
x: 7.7, y: 2.15 + i * 0.52, w: 1.6, h: 0.4,
|
|
fontSize: 12, fontFace: FONT_BODY, color: C.textMuted, margin: 0, valign: "middle"
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 2, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 3: PO CO POWSTAŁ PORTAL
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.primary }
|
|
});
|
|
|
|
s.addText("Po co powstał NordaBiznes?", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 32, fontFace: FONT_TITLE, color: C.white, bold: true, margin: 0
|
|
});
|
|
|
|
const whyItems = [
|
|
[icons.handshakeWhite, "Łączyć firmy", "Znajdź partnera biznesowego w Izbie\nbez szukania numeru, maila czy pośredników"],
|
|
[icons.searchWhite, "Pokazywać", "Twoja firma widoczna dla wszystkich\nczłonków — wizytówka, usługi, dane kontaktowe"],
|
|
[icons.globeWhite, "Informować", "Aktualności, wydarzenia, PEJ,\ninwestycje na Kaszubach — wszystko w jednym"],
|
|
[icons.lightbulbWhite, "Wspierać AI", "NordaGPT zna firmy w Izbie\ni pomaga znaleźć właściwego partnera"],
|
|
];
|
|
|
|
for (let i = 0; i < whyItems.length; i++) {
|
|
const col = i % 2;
|
|
const row = Math.floor(i / 2);
|
|
const x = 0.8 + col * 4.5;
|
|
const y = 1.5 + row * 1.8;
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x, y, w: 4, h: 1.5,
|
|
fill: { color: C.navyBg },
|
|
shadow: makeShadow()
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x, y, w: 0.06, h: 1.5,
|
|
fill: { color: C.primary }
|
|
});
|
|
|
|
s.addImage({ data: whyItems[i][0], x: x + 0.3, y: y + 0.25, w: 0.4, h: 0.4 });
|
|
s.addText(whyItems[i][1], {
|
|
x: x + 0.9, y: y + 0.15, w: 2.8, h: 0.4,
|
|
fontSize: 16, fontFace: FONT_BODY, color: C.white, bold: true, margin: 0
|
|
});
|
|
s.addText(whyItems[i][2], {
|
|
x: x + 0.9, y: y + 0.55, w: 2.8, h: 0.8,
|
|
fontSize: 12, fontFace: FONT_BODY, color: C.textLight, margin: 0
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 3, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 4: PROFIL FIRMY
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Twoja firma na portalu", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 32, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.05, w: 1.2, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
// Left column - what's on the profile
|
|
addCard(s, 0.8, 1.4, 4.2, 3.6, C.primary);
|
|
s.addText("Co jest na profilu?", {
|
|
x: 1.1, y: 1.55, w: 3.5, h: 0.35,
|
|
fontSize: 16, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
|
|
const profileItems = [
|
|
"Opis firmy, usługi, technologie",
|
|
"Dane kontaktowe (telefon, email, adres)",
|
|
"Strony internetowe (do 5)",
|
|
"Social media (FB, IG, LinkedIn, YT)",
|
|
"Godziny otwarcia (status na żywo)",
|
|
"Dane z rejestrów: KRS / CEIDG",
|
|
"Rekomendacje od innych firm",
|
|
"Zarząd, wspólnicy, kapitał",
|
|
];
|
|
|
|
s.addText(profileItems.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < profileItems.length - 1, fontSize: 12, color: C.textDark }
|
|
})), {
|
|
x: 1.1, y: 2.0, w: 3.6, h: 2.8,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 4, margin: 0
|
|
});
|
|
|
|
// Right column - what you can do
|
|
addCard(s, 5.4, 1.4, 4.2, 3.6, C.green);
|
|
s.addText("Co możesz zrobić?", {
|
|
x: 5.7, y: 1.55, w: 3.5, h: 0.35,
|
|
fontSize: 16, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
|
|
const actionItems = [
|
|
"Edytuj profil swojej firmy",
|
|
"Zarządzaj zespołem (role, uprawnienia)",
|
|
"Kontroluj co jest widoczne publicznie",
|
|
"Zaproś pracowników do portalu",
|
|
"Sprawdź wskaźnik kompletności",
|
|
"Pobierz dane z urzędowych rejestrów",
|
|
"Zbieraj AI o swojej firmie z internetu",
|
|
"Przeglądaj statystyki odwiedzin",
|
|
];
|
|
|
|
s.addText(actionItems.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < actionItems.length - 1, fontSize: 12, color: C.textDark }
|
|
})), {
|
|
x: 5.7, y: 2.0, w: 3.6, h: 2.8,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 4, margin: 0
|
|
});
|
|
|
|
addSlideNumber(s, 4, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 5: WYSZUKIWARKA I KATEGORIE
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Szukaj i znajdź", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 32, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.05, w: 1.2, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
s.addText("Wyszukiwarka rozumie literówki, synonimy i skróty", {
|
|
x: 0.8, y: 1.3, w: 8, h: 0.4,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textMuted, margin: 0
|
|
});
|
|
|
|
// Category cards
|
|
const categories = [
|
|
["IT i technologie", "Software, hosting,\nautomatyzacja", C.indigo],
|
|
["Budownictwo", "Projekty, wykonawstwo,\ninstalacje", C.amber],
|
|
["Usługi", "Finanse, prawo,\nkonsulting, HR", C.green],
|
|
["Produkcja", "Przemysł, CNC,\nmetalurgia", C.red],
|
|
];
|
|
|
|
for (let i = 0; i < categories.length; i++) {
|
|
const x = 0.8 + i * 2.25;
|
|
const [name, desc, color] = categories[i];
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x, y: 1.9, w: 2.0, h: 1.4,
|
|
fill: { color: C.white }, shadow: makeShadow()
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x, y: 1.9, w: 2.0, h: 0.06,
|
|
fill: { color }
|
|
});
|
|
s.addText(name, {
|
|
x: x + 0.15, y: 2.1, w: 1.7, h: 0.35,
|
|
fontSize: 13, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addText(desc, {
|
|
x: x + 0.15, y: 2.5, w: 1.7, h: 0.6,
|
|
fontSize: 11, fontFace: FONT_BODY, color: C.textMuted, margin: 0
|
|
});
|
|
}
|
|
|
|
// Bottom - search features
|
|
addCard(s, 0.8, 3.6, 8.4, 1.5, C.primary);
|
|
s.addText("Jak szukać?", {
|
|
x: 1.1, y: 3.75, w: 3, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
|
|
const searchFeatures = [
|
|
["Po nazwie firmy", "Wpisz fragment nazwy — znajdzie nawet z literówką"],
|
|
["Po NIP lub REGON", "Dokładne wyszukiwanie po numerach rejestrowych"],
|
|
["Po kategorii", "Filtruj po branży i podkategorii"],
|
|
["Przez NordaGPT", "Opisz czego szukasz swoimi słowami"],
|
|
];
|
|
|
|
for (let i = 0; i < searchFeatures.length; i++) {
|
|
const col = i % 2;
|
|
const row = Math.floor(i / 2);
|
|
const x = 1.1 + col * 4.1;
|
|
const y = 4.15 + row * 0.45;
|
|
|
|
s.addImage({ data: icons.check, x, y: y + 0.02, w: 0.2, h: 0.2 });
|
|
s.addText([
|
|
{ text: searchFeatures[i][0] + " ", options: { bold: true, fontSize: 11 } },
|
|
{ text: searchFeatures[i][1], options: { fontSize: 11, color: C.textMuted } }
|
|
], {
|
|
x: x + 0.3, y, w: 3.5, h: 0.35,
|
|
fontFace: FONT_BODY, color: C.textDark, margin: 0, valign: "middle"
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 5, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 6: NORDAGPT
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.primary }
|
|
});
|
|
|
|
addIconCircle(s, "robot", 0.8, 0.35, 0.55, C.primary);
|
|
s.addText("NordaGPT — asystent AI Izby", {
|
|
x: 1.5, y: 0.35, w: 7, h: 0.6,
|
|
fontSize: 30, fontFace: FONT_TITLE, color: C.white, bold: true, margin: 0, valign: "middle"
|
|
});
|
|
|
|
// What NordaGPT knows
|
|
s.addText("Co wie NordaGPT?", {
|
|
x: 0.8, y: 1.2, w: 4, h: 0.4,
|
|
fontSize: 16, fontFace: FONT_BODY, color: C.primaryLight, bold: true, margin: 0
|
|
});
|
|
|
|
const gptKnows = [
|
|
"Wszystkie firmy członkowskie — usługi, dane",
|
|
"Kto jest kim w Zarządzie i Radzie Izby",
|
|
"Kalendarz wydarzeń i aktualności",
|
|
"Projekt elektrowni jądrowej (PEJ)",
|
|
"Inwestycje na Kaszubach (ZOPK)",
|
|
"Ogłoszenia B2B i tematy na forum",
|
|
];
|
|
|
|
s.addText(gptKnows.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < gptKnows.length - 1, fontSize: 13, color: C.white }
|
|
})), {
|
|
x: 0.8, y: 1.7, w: 4.2, h: 2.5,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 5, margin: 0
|
|
});
|
|
|
|
// Right - example questions
|
|
s.addText("Przykładowe pytania:", {
|
|
x: 5.4, y: 1.2, w: 4, h: 0.4,
|
|
fontSize: 16, fontFace: FONT_BODY, color: C.primaryLight, bold: true, margin: 0
|
|
});
|
|
|
|
const examples = [
|
|
'"Szukam firmy od instalacji elektrycznych"',
|
|
'"Kto w Izbie zajmuje się budownictwem?"',
|
|
'"Jakie są najbliższe wydarzenia?"',
|
|
'"Co nowego w sprawie elektrowni jądrowej?"',
|
|
'"Firma od IT z doświadczeniem w offshore"',
|
|
];
|
|
|
|
for (let i = 0; i < examples.length; i++) {
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 5.4, y: 1.7 + i * 0.5, w: 4.2, h: 0.4,
|
|
fill: { color: C.navyBg }
|
|
});
|
|
s.addText(examples[i], {
|
|
x: 5.6, y: 1.7 + i * 0.5, w: 3.8, h: 0.4,
|
|
fontSize: 11, fontFace: FONT_BODY, color: C.textLight, italic: true, margin: 0, valign: "middle"
|
|
});
|
|
}
|
|
|
|
// Bottom note about AI
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 4.5, w: 8.4, h: 0.7,
|
|
fill: { color: "172554" }
|
|
});
|
|
s.addText([
|
|
{ text: "Odpowiedzi na żywo ", options: { fontSize: 13, color: C.white, bold: true } },
|
|
{ text: " | ", options: { fontSize: 13, color: C.primaryLight } },
|
|
{ text: "Propozycje kolejnych pytań ", options: { fontSize: 13, color: C.white, bold: true } },
|
|
{ text: " | ", options: { fontSize: 13, color: C.primaryLight } },
|
|
{ text: "Klikalne linki do firm", options: { fontSize: 13, color: C.white, bold: true } },
|
|
], {
|
|
x: 1.0, y: 4.5, w: 8, h: 0.7,
|
|
fontFace: FONT_BODY, align: "center", valign: "middle", margin: 0
|
|
});
|
|
|
|
addSlideNumber(s, 6, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 7: KOMUNIKACJA
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Komunikacja w portalu", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 32, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.05, w: 1.2, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
// Three columns
|
|
const commCols = [
|
|
{
|
|
title: "Wiadomości prywatne",
|
|
color: C.primary,
|
|
items: [
|
|
"Napisz do każdego członka Izby",
|
|
"Bąbelki jak w WhatsApp / Teams",
|
|
"Formatowanie tekstu, linki",
|
|
"Załączniki: PDF, zdjęcia",
|
|
"Potwierdzenie odczytania",
|
|
"Wiadomości grupowe",
|
|
]
|
|
},
|
|
{
|
|
title: "Forum dyskusyjne",
|
|
color: C.green,
|
|
items: [
|
|
"Tematy z kategoriami",
|
|
"Odpowiadaj, reaguj emoji",
|
|
"Śledź tematy (powiadomienia)",
|
|
"Wstawiaj zdjęcia i linki",
|
|
"Oznaczaj @użytkowników",
|
|
"Najlepsza odpowiedź",
|
|
]
|
|
},
|
|
{
|
|
title: "Tablica B2B",
|
|
color: C.amber,
|
|
items: [
|
|
"Ogłoszenia z ofertami usług",
|
|
"\"Jestem zainteresowany\"",
|
|
"Publiczne pytania / odpowiedzi",
|
|
"Kto widział ogłoszenie?",
|
|
"Kategorie: Partnerstwo, Okazja...",
|
|
"Wysyłaj wiadomość z ogłoszenia",
|
|
]
|
|
},
|
|
];
|
|
|
|
for (let c = 0; c < commCols.length; c++) {
|
|
const x = 0.8 + c * 3.1;
|
|
const col = commCols[c];
|
|
|
|
addCard(s, x, 1.35, 2.8, 3.7, col.color);
|
|
s.addText(col.title, {
|
|
x: x + 0.25, y: 1.5, w: 2.3, h: 0.35,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
|
|
s.addText(col.items.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < col.items.length - 1, fontSize: 11, color: C.textDark }
|
|
})), {
|
|
x: x + 0.25, y: 2.0, w: 2.3, h: 2.8,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 4, margin: 0
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 7, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 8: KALENDARZ
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Kalendarz wydarzeń", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 32, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.05, w: 1.2, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
// Features grid 2x3
|
|
const calFeatures = [
|
|
[icons.calendar, "Widok miesięczny", "Przejrzysta siatka z kolorowymi\noznaczeniami typów wydarzeń"],
|
|
[icons.check, "Zapis jednym kliknięciem", "Zielony przycisk — zapisz się\nlub zrezygnuj w każdej chwili"],
|
|
[icons.users, "Lista uczestników", "Zobacz kto idzie — klikalne\nplakietki z linkami do profili"],
|
|
[icons.bell, "Przypomnienia", "Email dzień przed wydarzeniem,\npowiadomienie w portalu"],
|
|
[icons.mobile, "Synchronizacja", "Eksport do iPhone (iCal)\ni Google Calendar"],
|
|
[icons.map, "Lokalizacja", "Adresy prowadzą do Google Maps,\nwszystko klikalne"],
|
|
];
|
|
|
|
for (let i = 0; i < calFeatures.length; i++) {
|
|
const col = i % 3;
|
|
const row = Math.floor(i / 3);
|
|
const x = 0.8 + col * 3.1;
|
|
const y = 1.4 + row * 1.9;
|
|
|
|
addCard(s, x, y, 2.8, 1.6, C.primary);
|
|
s.addImage({ data: calFeatures[i][0], x: x + 0.25, y: y + 0.2, w: 0.35, h: 0.35 });
|
|
s.addText(calFeatures[i][1], {
|
|
x: x + 0.25, y: y + 0.65, w: 2.3, h: 0.3,
|
|
fontSize: 13, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addText(calFeatures[i][2], {
|
|
x: x + 0.25, y: y + 0.95, w: 2.3, h: 0.5,
|
|
fontSize: 11, fontFace: FONT_BODY, color: C.textMuted, margin: 0
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 8, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 9: PEJ
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.amber }
|
|
});
|
|
|
|
addIconCircle(s, "atom", 0.8, 0.35, 0.55, C.amber);
|
|
s.addText("PEJ — Elektrownia Jądrowa", {
|
|
x: 1.5, y: 0.35, w: 7, h: 0.6,
|
|
fontSize: 30, fontFace: FONT_TITLE, color: C.white, bold: true, margin: 0, valign: "middle"
|
|
});
|
|
|
|
s.addText("Dedykowana przestrzeń dla firm zainteresowanych największą inwestycją w regionie", {
|
|
x: 0.8, y: 1.1, w: 8.4, h: 0.4,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textLight, margin: 0
|
|
});
|
|
|
|
// Two columns
|
|
// Left - what's there
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.7, w: 4.2, h: 3.2,
|
|
fill: { color: C.navyBg }, shadow: makeShadow()
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.7, w: 0.06, h: 3.2, fill: { color: C.amber }
|
|
});
|
|
|
|
s.addText("Co znajdziesz?", {
|
|
x: 1.1, y: 1.85, w: 3.5, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.amber, bold: true, margin: 0
|
|
});
|
|
|
|
const pejItems = [
|
|
"Aktualności o elektrowni jądrowej (66+ artykułów)",
|
|
"Local Content — lista firm Izby dopasowanych do projektu",
|
|
"Wskaźnik dopasowania firmy do przetargów PEJ",
|
|
"Oś czasu kamieni milowych budowy",
|
|
"Kontakty: PEJ, Bechtel, PAA, Ministerstwo",
|
|
"Link do platformy zakupowej i rejestracji dostawców",
|
|
];
|
|
|
|
s.addText(pejItems.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < pejItems.length - 1, fontSize: 12, color: C.white }
|
|
})), {
|
|
x: 1.1, y: 2.3, w: 3.6, h: 2.4,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 4, margin: 0
|
|
});
|
|
|
|
// Right - Local Content
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 5.4, y: 1.7, w: 4.2, h: 3.2,
|
|
fill: { color: C.navyBg }, shadow: makeShadow()
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 5.4, y: 1.7, w: 0.06, h: 3.2, fill: { color: C.green }
|
|
});
|
|
|
|
s.addText("Local Content — Twoja szansa", {
|
|
x: 5.7, y: 1.85, w: 3.5, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.green, bold: true, margin: 0
|
|
});
|
|
|
|
const lcItems = [
|
|
"System dopasowuje Twoją firmę do projektu PEJ",
|
|
"Widoczne: typ współpracy, branża, opis",
|
|
"Filtruj po: kategorii, typie powiązania",
|
|
"Potencjalny dostawca / partner / beneficjent",
|
|
"Im pełniejszy profil, tym lepsze dopasowanie",
|
|
"NordaGPT odpowiada na pytania o PEJ",
|
|
];
|
|
|
|
s.addText(lcItems.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < lcItems.length - 1, fontSize: 12, color: C.white }
|
|
})), {
|
|
x: 5.7, y: 2.3, w: 3.6, h: 2.4,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 4, margin: 0
|
|
});
|
|
|
|
addSlideNumber(s, 9, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 10: KASZUBIA / ZOPK
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Kaszubia — Inwestycje regionalne", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 30, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addText("Zielony Okręg Przemysłowy Kaszubia — monitoring szans biznesowych", {
|
|
x: 0.8, y: 1.0, w: 8.4, h: 0.4,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textMuted, margin: 0
|
|
});
|
|
|
|
// Project cards - use darker shades for white text readability
|
|
const projects = [
|
|
["Energia", "Elektrownia jądrowa,\noffshore, OZE", "B45309"],
|
|
["Infrastruktura", "Drogi, kolej,\nport Gdynia", C.indigo],
|
|
["Przemysł", "Kongsberg, CNC,\nmetalurgia", C.red],
|
|
["Turystyka", "Żarnowiecki Ring,\nEnergy Velo", "047857"],
|
|
];
|
|
|
|
for (let i = 0; i < projects.length; i++) {
|
|
const x = 0.8 + i * 2.25;
|
|
const [name, desc, color] = projects[i];
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x, y: 1.6, w: 2.0, h: 1.2,
|
|
fill: { color }, shadow: makeShadow()
|
|
});
|
|
s.addText(name, {
|
|
x: x + 0.15, y: 1.7, w: 1.7, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.white, bold: true, margin: 0
|
|
});
|
|
s.addText(desc, {
|
|
x: x + 0.15, y: 2.1, w: 1.7, h: 0.6,
|
|
fontSize: 11, fontFace: FONT_BODY, color: C.white, margin: 0
|
|
});
|
|
}
|
|
|
|
// Bottom - what's tracked
|
|
addCard(s, 0.8, 3.1, 8.4, 2.1, C.primary);
|
|
s.addText("Co monitorujemy?", {
|
|
x: 1.1, y: 3.25, w: 4, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
|
|
const zopkItems = [
|
|
[icons.newspaper, "Aktualności prasowe", "Automatyczne zbieranie i filtrowanie wiadomości przez AI"],
|
|
[icons.chart, "Kamienie milowe", "Oś czasu z postępem kluczowych projektów w regionie"],
|
|
[icons.handshake, "Dopasowanie firm", "System wskazuje które firmy Izby pasują do przetargów"],
|
|
[icons.lightbulb, "Baza wiedzy AI", "NordaGPT odpowiada na pytania o inwestycje kaszubskie"],
|
|
];
|
|
|
|
for (let i = 0; i < zopkItems.length; i++) {
|
|
const col = i % 2;
|
|
const row = Math.floor(i / 2);
|
|
const x = 1.1 + col * 4.1;
|
|
const y = 3.7 + row * 0.65;
|
|
|
|
s.addImage({ data: zopkItems[i][0], x, y: y + 0.05, w: 0.28, h: 0.28 });
|
|
s.addText([
|
|
{ text: zopkItems[i][1] + " ", options: { bold: true, fontSize: 12 } },
|
|
{ text: zopkItems[i][2], options: { fontSize: 11, color: C.textMuted } }
|
|
], {
|
|
x: x + 0.4, y, w: 3.5, h: 0.5,
|
|
fontFace: FONT_BODY, color: C.textDark, margin: 0, valign: "middle"
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 10, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 11: POWIADOMIENIA
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Powiadomienia i aplikacja mobilna", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 30, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.05, w: 1.2, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
// Notification channels
|
|
const notifCols = [
|
|
{
|
|
title: "W portalu",
|
|
icon: "bell",
|
|
color: C.primary,
|
|
items: [
|
|
"Dzwoneczek z licznikiem",
|
|
"Nowe wiadomości",
|
|
"Odpowiedzi na forum",
|
|
"Zapis na wydarzenie",
|
|
"Badge \"Nowe\" na treściach",
|
|
]
|
|
},
|
|
{
|
|
title: "Email",
|
|
icon: "envelope",
|
|
color: C.green,
|
|
items: [
|
|
"Nowa wiadomość prywatna",
|
|
"Odpowiedź na śledzony temat",
|
|
"Przypomnienie o wydarzeniu",
|
|
"Wniosek członkowski",
|
|
"Link do rezygnacji",
|
|
]
|
|
},
|
|
{
|
|
title: "Telefon",
|
|
icon: "mobile",
|
|
color: C.purple,
|
|
items: [
|
|
"Zainstaluj jak aplikację",
|
|
"Powiadomienia na iPhone i Android",
|
|
"Pełna funkcjonalność",
|
|
"Działa bez internetu",
|
|
"Ikona na pulpicie telefonu",
|
|
]
|
|
},
|
|
];
|
|
|
|
for (let c = 0; c < notifCols.length; c++) {
|
|
const x = 0.8 + c * 3.1;
|
|
const col = notifCols[c];
|
|
|
|
addCard(s, x, 1.35, 2.8, 3.5, col.color);
|
|
addIconCircle(s, col.icon, x + 0.25, 1.5, 0.4, col.color);
|
|
s.addText(col.title, {
|
|
x: x + 0.8, y: 1.5, w: 1.7, h: 0.4,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0, valign: "middle"
|
|
});
|
|
|
|
s.addText(col.items.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < col.items.length - 1, fontSize: 11, color: C.textDark }
|
|
})), {
|
|
x: x + 0.25, y: 2.1, w: 2.3, h: 2.5,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 5, margin: 0
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 11, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 12: PRYWATNOŚĆ I BEZPIECZEŃSTWO
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.green }
|
|
});
|
|
|
|
addIconCircle(s, "shield", 0.8, 0.35, 0.55, C.green);
|
|
s.addText("Prywatność i bezpieczeństwo", {
|
|
x: 1.5, y: 0.35, w: 7, h: 0.6,
|
|
fontSize: 30, fontFace: FONT_TITLE, color: C.white, bold: true, margin: 0, valign: "middle"
|
|
});
|
|
|
|
// Privacy features
|
|
const privacyItems = [
|
|
["Twoje dane pod kontrolą", "Decydujesz co jest widoczne — telefon, email,\nsekcje profilu. Ukrywasz jednym kliknięciem.", C.green],
|
|
["6 poziomów dostępu", "Gość → Użytkownik → Członek → Pracownik →\nKierownik → Administrator. Każdy widzi tylko to, co powinien.", C.primary],
|
|
["Tylko dla członków Izby", "NordaGPT, wiadomości, B2B, dane kontaktowe\n— dostępne wyłącznie po zalogowaniu.", C.amber],
|
|
["Ochrona danych osobowych", "PESEL, numery kart, IBAN — automatycznie\nukrywane. Blokowanie niechcianych kontaktów.", C.purple],
|
|
];
|
|
|
|
for (let i = 0; i < privacyItems.length; i++) {
|
|
const col = i % 2;
|
|
const row = Math.floor(i / 2);
|
|
const x = 0.8 + col * 4.5;
|
|
const y = 1.3 + row * 1.8;
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x, y, w: 4.0, h: 1.5,
|
|
fill: { color: C.navyBg }, shadow: makeShadow()
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x, y, w: 0.06, h: 1.5,
|
|
fill: { color: privacyItems[i][2] }
|
|
});
|
|
|
|
s.addText(privacyItems[i][0], {
|
|
x: x + 0.25, y: y + 0.15, w: 3.5, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.white, bold: true, margin: 0
|
|
});
|
|
s.addText(privacyItems[i][1], {
|
|
x: x + 0.25, y: y + 0.55, w: 3.5, h: 0.8,
|
|
fontSize: 12, fontFace: FONT_BODY, color: C.textLight, margin: 0
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 12, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 13: KOSZTY I PRZYSZŁOŚĆ
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.lightBg };
|
|
|
|
s.addText("Koszty, ograniczenia i przyszłość", {
|
|
x: 0.8, y: 0.4, w: 8.4, h: 0.7,
|
|
fontSize: 30, fontFace: FONT_TITLE, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0.8, y: 1.05, w: 1.2, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
// Left - what's included
|
|
addCard(s, 0.8, 1.35, 4.2, 2.0, C.green);
|
|
s.addText("Co jest w cenie członkostwa?", {
|
|
x: 1.1, y: 1.5, w: 3.5, h: 0.35,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
const freeItems = [
|
|
"Profil firmy, katalog, wyszukiwarka",
|
|
"Forum, ogłoszenia B2B, aktualności",
|
|
"Kalendarz wydarzeń z zapisami",
|
|
"Wiadomości prywatne i grupowe",
|
|
"Powiadomienia email i push (PWA)",
|
|
];
|
|
s.addText(freeItems.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < freeItems.length - 1, fontSize: 12, color: C.textDark }
|
|
})), {
|
|
x: 1.1, y: 1.9, w: 3.6, h: 1.3,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 3, margin: 0
|
|
});
|
|
|
|
// Right - AI limits
|
|
addCard(s, 5.4, 1.35, 4.2, 2.0, C.amber);
|
|
s.addText("Ograniczenia AI (NordaGPT)", {
|
|
x: 5.7, y: 1.5, w: 3.5, h: 0.35,
|
|
fontSize: 14, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
const aiLimits = [
|
|
"Silnik AI: Google Gemini (darmowy tier)",
|
|
"Limit dzienny na użytkownika",
|
|
"Limit zbiorczy na cały portal",
|
|
"Odpowiedzi w 2 trybach: szybki / zaawansowany",
|
|
"W przyszłości: płatne plany = więcej AI",
|
|
];
|
|
s.addText(aiLimits.map((item, i) => ({
|
|
text: item,
|
|
options: { bullet: true, breakLine: i < aiLimits.length - 1, fontSize: 12, color: C.textDark }
|
|
})), {
|
|
x: 5.7, y: 1.9, w: 3.6, h: 1.3,
|
|
fontFace: FONT_BODY, paraSpaceAfter: 3, margin: 0
|
|
});
|
|
|
|
// Bottom - future plans
|
|
addCard(s, 0.8, 3.6, 8.4, 1.7, C.indigo);
|
|
s.addText("Co planujemy?", {
|
|
x: 1.1, y: 3.75, w: 4, h: 0.35,
|
|
fontSize: 15, fontFace: FONT_BODY, color: C.textDark, bold: true, margin: 0
|
|
});
|
|
|
|
const futureItems = [
|
|
[icons.robot, "NordaGPT 2.0", "Zaawansowany doradca — analiza rynku, rekomendacje, raporty"],
|
|
[icons.handshake, "Marketplace usług", "Giełda kompetencji — członkowie oferują usługi sobie nawzajem"],
|
|
[icons.chart, "Raporty branżowe", "Automatyczna analiza trendów i szans rynkowych przez AI"],
|
|
[icons.globe, "Integracje", "WhatsApp Business, SMS — komunikacja bez granic"],
|
|
];
|
|
|
|
for (let i = 0; i < futureItems.length; i++) {
|
|
const col = i % 2;
|
|
const row = Math.floor(i / 2);
|
|
const x = 1.1 + col * 4.1;
|
|
const y = 4.2 + row * 0.5;
|
|
|
|
s.addImage({ data: futureItems[i][0], x, y: y + 0.05, w: 0.25, h: 0.25 });
|
|
s.addText([
|
|
{ text: futureItems[i][1] + " ", options: { bold: true, fontSize: 12 } },
|
|
{ text: futureItems[i][2], options: { fontSize: 11, color: C.textMuted } }
|
|
], {
|
|
x: x + 0.35, y, w: 3.5, h: 0.4,
|
|
fontFace: FONT_BODY, color: C.textDark, margin: 0, valign: "middle"
|
|
});
|
|
}
|
|
|
|
addSlideNumber(s, 13, TOTAL);
|
|
|
|
// =============================================
|
|
// SLIDE 14: PYTANIA
|
|
// =============================================
|
|
s = pres.addSlide();
|
|
s.background = { color: C.darkBg };
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 0, y: 0, w: 10, h: 0.06, fill: { color: C.primary }
|
|
});
|
|
|
|
s.addText("Pytania?", {
|
|
x: 0.8, y: 1.0, w: 8.4, h: 1.0,
|
|
fontSize: 48, fontFace: FONT_TITLE, color: C.white, bold: true, align: "center", margin: 0
|
|
});
|
|
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 4.2, y: 2.2, w: 1.6, h: 0.04, fill: { color: C.primary }
|
|
});
|
|
|
|
s.addText("Zaloguj się i sprawdź", {
|
|
x: 0.8, y: 2.6, w: 8.4, h: 0.6,
|
|
fontSize: 22, fontFace: FONT_BODY, color: C.primaryLight, align: "center", margin: 0
|
|
});
|
|
|
|
// Norda compass next to URL
|
|
s.addImage({
|
|
path: nordaLogoPath,
|
|
x: 3.4, y: 3.05, w: 0.7, h: 0.7
|
|
});
|
|
s.addText("nordabiznes.pl", {
|
|
x: 4.2, y: 3.1, w: 4, h: 0.6,
|
|
fontSize: 28, fontFace: FONT_TITLE, color: C.white, bold: true, margin: 0, valign: "middle"
|
|
});
|
|
|
|
// Bottom contact bar
|
|
s.addShape(pres.shapes.RECTANGLE, {
|
|
x: 1.5, y: 4.1, w: 7, h: 1.0,
|
|
fill: { color: C.navyBg }
|
|
});
|
|
s.addText([
|
|
{ text: "Maciej Pienczyn", options: { bold: true, fontSize: 13, color: C.white } },
|
|
{ text: " | maciej.pienczyn@inpi.pl | Izba Gospodarcza NORDA", options: { fontSize: 12, color: C.textLight } }
|
|
], {
|
|
x: 1.5, y: 4.1, w: 7, h: 0.55,
|
|
fontFace: FONT_BODY, align: "center", valign: "middle", margin: 0
|
|
});
|
|
|
|
// INPI logo + "Stworzone przez" — bottom of contact bar
|
|
s.addImage({
|
|
path: inpiLogoPath,
|
|
x: 3.65, y: 4.62, w: 0.55, h: 0.31
|
|
});
|
|
s.addText("Stworzone przez", {
|
|
x: 2.2, y: 4.62, w: 1.4, h: 0.31,
|
|
fontSize: 9, fontFace: FONT_BODY, color: C.textMuted, align: "right", valign: "middle", margin: 0
|
|
});
|
|
s.addText("sp. z o.o.", {
|
|
x: 4.25, y: 4.62, w: 1, h: 0.31,
|
|
fontSize: 9, fontFace: FONT_BODY, color: C.textMuted, valign: "middle", margin: 0
|
|
});
|
|
|
|
// Save
|
|
const outputPath = "/Users/maciejpi/Desktop/NordaBiznes_Prezentacja_2026-04-09.pptx";
|
|
await pres.writeFile({ fileName: outputPath });
|
|
console.log("Presentation saved to:", outputPath);
|
|
}
|
|
|
|
createPresentation().catch(err => {
|
|
console.error("Error:", err);
|
|
process.exit(1);
|
|
});
|