diff --git a/TODO.md b/TODO.md index a8890b2..1fa08e6 100644 --- a/TODO.md +++ b/TODO.md @@ -2,3 +2,5 @@ - Slider changable time in UI + +- Fix do košíku button diff --git a/global/templates/globals/script.js b/global/templates/globals/script.js new file mode 100644 index 0000000..e69de29 diff --git a/global/templates/globals/style.css b/global/templates/globals/style.css new file mode 100644 index 0000000..412d835 --- /dev/null +++ b/global/templates/globals/style.css @@ -0,0 +1,7 @@ +body { + padding-top: 0px !important; +} + +html { + font-size: unset !important; +} diff --git a/homepage/templates/articles/replace_selector b/homepage/templates/articles/replace_selector new file mode 100644 index 0000000..3cb1777 --- /dev/null +++ b/homepage/templates/articles/replace_selector @@ -0,0 +1 @@ +main#main #AjaxMainSection .section_guidepost_before diff --git a/homepage/templates/articles/scrape.js b/homepage/templates/articles/scrape.js new file mode 100644 index 0000000..950a92b --- /dev/null +++ b/homepage/templates/articles/scrape.js @@ -0,0 +1,44 @@ +(() => { + const container = document.querySelector('main#main #AjaxMainSection .section_guidepost_before .vc-articlebox'); + if (!container) return null; + + const headerEl = container.querySelector('.header span'); + const sectionTitle = headerEl ? headerEl.textContent.trim() : null; + + const articleNodes = container.querySelectorAll('.content.articles a.article'); + + const articles = Array.from(articleNodes).map(article => { + const link = article.getAttribute('href'); + + const titleEl = article.querySelector('.title'); + const title = titleEl ? titleEl.textContent.trim() : null; + + const dateEl = article.querySelector('.date'); + const date = dateEl ? dateEl.textContent.trim() : null; + + const annotationEl = article.querySelector('.annotation'); + const annotation = annotationEl ? annotationEl.textContent.trim() : null; + + const imgEl = article.querySelector('.img img'); + const imageSrc = imgEl ? (imgEl.getAttribute('data-src') || imgEl.getAttribute('src')) : null; + const imageAlt = imgEl ? imgEl.getAttribute('alt') : null; + + return { + title, + link, + date, + annotation, + imageSrc, + imageAlt + }; + }); + + const moreBtn = container.querySelector('.footer a.moreBtn'); + const moreLink = moreBtn ? moreBtn.getAttribute('href') : null; + + return { + sectionTitle, + articles, + moreLink + }; +})(); diff --git a/homepage/templates/articles/style.css b/homepage/templates/articles/style.css new file mode 100644 index 0000000..caeb7fb --- /dev/null +++ b/homepage/templates/articles/style.css @@ -0,0 +1,201 @@ +:root { + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --card: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --border: oklch(0.922 0 0); + --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif; +} + +.news-section { + font-family: var(--font-sans); + padding: 4rem 0; /* py-16 */ + background-color: color-mix( + in srgb, + var(--secondary) 30%, + transparent + ); /* bg-secondary/30 */ +} +@media (min-width: 1024px) { + .news-section { + padding: 6rem 0; /* lg:py-24 */ + } +} + +.container { + max-width: 80rem; /* max-w-7xl */ + margin: 0 auto; + padding: 0 1rem; /* px-4 */ +} +@media (min-width: 640px) { + .container { + padding: 0 1.5rem; /* sm:px-6 */ + } +} +@media (min-width: 1024px) { + .container { + padding: 0 2rem; /* lg:px-8 */ + } +} + +/* Header Section */ +.news-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 3rem; /* mb-12 */ +} + +.news-title { + font-size: 1.5rem; /* text-2xl */ + font-weight: 700; /* font-bold */ + color: var(--foreground); + margin: 0; +} +@media (min-width: 768px) { + .news-title { + font-size: 1.875rem; /* md:text-3xl */ + } +} + +.news-link { + display: flex; + align-items: center; + gap: 0.5rem; + font-size: 1rem; + font-weight: 500; /* font-medium */ + color: var(--accent); + text-decoration: none; +} +.news-link:hover { + text-decoration: underline; +} +.news-link svg { + width: 1rem; + height: 1rem; +} + +/* Grid Layout */ +.news-grid { + display: grid; + grid-template-columns: 1fr; + gap: 1.5rem; /* gap-6 */ +} +@media (min-width: 768px) { + .news-grid { + grid-template-columns: repeat(2, 1fr); /* md:grid-cols-2 */ + } +} +@media (min-width: 1024px) { + .news-grid { + gap: 2rem; /* lg:gap-8 */ + } +} + +/* News Card */ +.news-card { + display: flex; + flex-direction: column; /* flex-col on mobile */ + gap: 1rem; /* gap-4 */ + background-color: var(--card); + border-radius: 0.75rem; /* rounded-xl */ + border: 1px solid var(--border); + overflow: hidden; + text-decoration: none; + color: inherit; + transition: box-shadow 0.3s ease; +} +@media (min-width: 640px) { + .news-card { + flex-direction: row; /* sm:flex-row */ + } +} +.news-card:hover { + box-shadow: + 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -4px rgba(0, 0, 0, 0.1); /* hover:shadow-lg */ +} + +/* Card Image Area */ +.news-img-wrapper { + flex-shrink: 0; + overflow: hidden; + /* Mobile: 16:9 aspect ratio, full width */ + width: 100%; + aspect-ratio: 16 / 9; +} +@media (min-width: 640px) { + .news-img-wrapper { + /* sm breakpoint: fixed width, 1:1 aspect ratio */ + width: 12rem; /* sm:w-48 */ + aspect-ratio: 1 / 1; + } +} +@media (min-width: 1024px) { + .news-img-wrapper { + width: 16rem; /* lg:w-64 */ + } +} + +.news-img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.5s ease; +} +.news-card:hover .news-img { + transform: scale(1.05); /* group-hover:scale-105 */ +} + +/* Card Content Area */ +.news-content { + display: flex; + flex-direction: column; + justify-content: center; + padding: 1rem; /* p-4 */ +} +@media (min-width: 640px) { + .news-content { + padding: 1.25rem; /* sm:p-5 */ + } +} + +.news-date { + display: flex; + align-items: center; + gap: 0.5rem; /* gap-2 */ + font-size: 0.875rem; /* text-sm */ + color: var(--muted-foreground); + margin-bottom: 0.5rem; /* mb-2 */ +} +.news-date svg { + width: 1rem; + height: 1rem; +} + +.news-card-title { + font-size: 1.125rem; /* text-lg */ + font-weight: 600; /* font-semibold */ + color: var(--foreground); + margin: 0 0 0.5rem 0; /* mb-2 */ + line-height: 1.3; + transition: color 0.2s ease; +} +.news-card:hover .news-card-title { + color: var(--accent); /* group-hover:text-accent */ +} + +.news-excerpt { + font-size: 0.875rem; /* text-sm */ + color: var(--muted-foreground); + margin: 0; + line-height: 1.5; + /* line-clamp-2 */ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; +} diff --git a/homepage/templates/articles/template.html b/homepage/templates/articles/template.html new file mode 100644 index 0000000..9951573 --- /dev/null +++ b/homepage/templates/articles/template.html @@ -0,0 +1,57 @@ + + + + + + + {{#if sectionTitle}}{{sectionTitle}}{{else}}Aktuality{{/if}} + + {{#if moreLink}} + + Více aktualit + + + + + + {{/if}} + + + + + {{#each articles}} + + + {{#if this.imageSrc}} + + {{/if}} + + + + + + + + + + + {{this.date}} + + + {{this.title}} + + {{#if this.annotation}} + {{this.annotation}} + {{/if}} + + + {{/each}} + + + + diff --git a/homepage/templates/brand_images/replace_selector b/homepage/templates/brand_images/replace_selector new file mode 100644 index 0000000..f8d8d6f --- /dev/null +++ b/homepage/templates/brand_images/replace_selector @@ -0,0 +1 @@ +main#main #AjaxMainSection .logo_slider diff --git a/homepage/templates/brand_images/scrape.js b/homepage/templates/brand_images/scrape.js new file mode 100644 index 0000000..c57f139 --- /dev/null +++ b/homepage/templates/brand_images/scrape.js @@ -0,0 +1,9 @@ +(() => { + const imageElements = document.querySelectorAll("main#main #AjaxMainSection .logo_slider .owl-stage .item img"); + + const images = Array.from(imageElements) + .map(img => img.getAttribute('data-src') || img.getAttribute('src')) + .filter(path => path !== null); + + return { images }; +})(); diff --git a/homepage/templates/brand_images/style.css b/homepage/templates/brand_images/style.css new file mode 100644 index 0000000..09d28e1 --- /dev/null +++ b/homepage/templates/brand_images/style.css @@ -0,0 +1,80 @@ +:root { + --card: oklch(1 0 0); + --border: oklch(0.922 0 0); +} + +.brands-section { + padding: 3rem 0; /* py-12 */ + background-color: var(--card); + border-top: 1px solid var(--border); + border-bottom: 1px solid var(--border); +} +@media (min-width: 1024px) { + .brands-section { + padding: 4rem 0; /* lg:py-16 */ + } +} + +.brands-container { + max-width: 80rem; /* max-w-7xl */ + margin: 0 auto; + padding: 0 1rem; /* px-4 */ +} +@media (min-width: 640px) { + .brands-container { + padding: 0 1.5rem; /* sm:px-6 */ + } +} +@media (min-width: 1024px) { + .brands-container { + padding: 0 2rem; /* lg:px-8 */ + } +} + +.brands-grid { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 2rem; /* gap-8 */ +} +@media (min-width: 768px) { + .brands-grid { + gap: 3rem; /* md:gap-12 */ + } +} +@media (min-width: 1024px) { + .brands-grid { + gap: 4rem; /* lg:gap-16 */ + } +} + +.brand-logo { + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; +} + +.brand-logo img { + max-height: 2rem; /* Mobile height */ + width: auto; + object-fit: contain; + /* Mimic the text-muted-foreground/50 look */ + opacity: 0.5; + filter: grayscale(100%); + transition: + opacity 0.3s ease, + filter 0.3s ease; +} +@media (min-width: 768px) { + .brand-logo img { + max-height: 2.5rem; /* md: height */ + } +} + +.brand-logo:hover img { + /* Mimic hover:text-muted-foreground */ + opacity: 1; + filter: grayscale(0%); +} diff --git a/homepage/templates/brand_images/template.html b/homepage/templates/brand_images/template.html new file mode 100644 index 0000000..6391e9a --- /dev/null +++ b/homepage/templates/brand_images/template.html @@ -0,0 +1,11 @@ + + + + {{#each images}} + + + + {{/each}} + + + diff --git a/homepage/templates/section_middle_novinky/replace_selector b/homepage/templates/section_middle_novinky/replace_selector new file mode 100644 index 0000000..5d059f7 --- /dev/null +++ b/homepage/templates/section_middle_novinky/replace_selector @@ -0,0 +1 @@ +main#main #AjaxMainSection section.section_middle .vc-commodity_attribute diff --git a/homepage/templates/section_middle_novinky/scrape.js b/homepage/templates/section_middle_novinky/scrape.js new file mode 100644 index 0000000..cd80c0a --- /dev/null +++ b/homepage/templates/section_middle_novinky/scrape.js @@ -0,0 +1,85 @@ +(() => { + const container = document.querySelector("main#main #AjaxMainSection section.section_middle > .container > .inner > .vc-commodity_attribute"); + + if (!container) return null; + + // Extract the section name (e.g., "Novinky") + const headerEl = container.querySelector(".header > span"); + const sectionName = headerEl ? headerEl.textContent.trim() : "Unknown Section"; + + // Select all products, ignoring carousel initialization wrappers to ensure we get everything + const items = container.querySelectorAll("article.commodityBox"); + + const itemsData = Array.from(items).map((item) => { + const productNode = item.querySelector("a.inner"); + const link = productNode ? productNode.getAttribute("href") : null; + + // Extract flags and their computed styles + const flagsElements = productNode.querySelectorAll(".flags > .flags-inner > .flag"); + const flags = Array.from(flagsElements).map((flag) => { + const styles = window.getComputedStyle(flag); + return { + text: flag.textContent.trim(), + backgroundColor: styles.backgroundColor, + textColor: styles.color + }; + }); + + // Extract image data + const imageEl = productNode.querySelector(".image img.thumb.main"); + const imageSrc = imageEl ? (imageEl.getAttribute("data-src") || imageEl.getAttribute("src")) : null; + const imageAlt = imageEl ? imageEl.getAttribute("alt") : null; + + // Extract description and title + const descriptionEl = productNode.querySelector(".annotation > p"); + const description = descriptionEl ? descriptionEl.textContent.trim() : null; + + const titleEl = productNode.querySelector(".title > span"); + const title = titleEl ? titleEl.textContent.trim() : null; + + // Extract buy form HTML + const buyFormEl = productNode.querySelector(".buyForm"); + const buyFormContent = buyFormEl ? buyFormEl.innerHTML.trim() : null; + + // Extract pricing details + const priceEl = productNode.querySelector(".pricing > .pricing-inner"); + let priceDiscount = null; + let priceBeforeDiscount = null; + let priceHighlight = null; + + if (priceEl) { + const priceDiscountEl = priceEl.querySelector(".discount .number"); + priceDiscount = priceDiscountEl ? parseInt(priceDiscountEl.textContent.trim(), 10) : null; + + const priceBeforeDiscountEl = priceEl.querySelector(".price.beforeDiscount"); + priceBeforeDiscount = priceBeforeDiscountEl ? priceBeforeDiscountEl.textContent.trim().replace(/\s+/g, ' ') : null; + + const priceHighlightEl = priceEl.querySelector(".price.highlight"); + priceHighlight = priceHighlightEl ? priceHighlightEl.textContent.trim().replace(/\s+/g, ' ') : null; + } + + // Extract availability + const availabilityEl = productNode.querySelector(".availability strong"); + const availability = availabilityEl ? availabilityEl.textContent.trim() : null; + + return { + title, + link, + flags, + imageSrc, + imageAlt, + description, + priceHighlight, + priceBeforeDiscount, + priceDiscount, + availability, + buyFormContent + }; + }); + + // Return a clean object with the section name and its respective items + return { + section: sectionName, + items: itemsData + }; +})(); diff --git a/homepage/templates/section_middle_novinky/style.css b/homepage/templates/section_middle_novinky/style.css new file mode 100644 index 0000000..fce4373 --- /dev/null +++ b/homepage/templates/section_middle_novinky/style.css @@ -0,0 +1,262 @@ +:root { + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --card: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --border: oklch(0.922 0 0); + --sale-bg: #ef4444; /* Red for discounts */ + --sale-fg: #ffffff; + --success-text: #10b981; /* Green for in-stock */ + --font-sans: "Geist", "Geist Fallback", ui-sans-serif, system-ui, sans-serif; +} + +.new-products-section { + font-family: var(--font-sans); + padding: 4rem 0; /* py-16 */ + background-color: var(--background); +} +@media (min-width: 1024px) { + .new-products-section { + padding: 6rem 0; /* lg:py-24 */ + } +} + +.container { + max-width: 80rem; /* max-w-7xl */ + margin: 0 auto; + padding: 0 1rem; /* px-4 */ +} +@media (min-width: 640px) { + .container { + padding: 0 1.5rem; /* sm:px-6 */ + } +} +@media (min-width: 1024px) { + .container { + padding: 0 2rem; /* lg:px-8 */ + } +} + +/* Header Section */ +.section-header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 3rem; /* mb-12 */ +} + +.section-title { + font-size: 1.5rem; /* text-2xl */ + font-weight: 700; /* font-bold */ + color: var(--foreground); + margin: 0; +} +@media (min-width: 768px) { + .section-title { + font-size: 1.875rem; /* md:text-3xl */ + } +} + +.section-link { + font-size: 1rem; + font-weight: 500; /* font-medium */ + color: var(--accent); + text-decoration: none; +} +.section-link:hover { + text-decoration: underline; +} + +/* Product Grid */ +.product-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); /* grid-cols-2 */ + gap: 1rem; /* gap-4 */ +} +@media (min-width: 768px) { + .product-grid { + grid-template-columns: repeat(3, 1fr); /* md:grid-cols-3 */ + gap: 1.5rem; /* md:gap-6 */ + } +} +@media (min-width: 1024px) { + .product-grid { + grid-template-columns: repeat(4, 1fr); /* lg:grid-cols-4 */ + } +} + +/* Product Card */ +.product-card { + position: relative; + background-color: var(--card); + border-radius: 0.75rem; /* rounded-xl */ + overflow: hidden; + border: 1px solid var(--border); + transition: all 0.3s ease; + display: flex; + flex-direction: column; +} +.product-card:hover { + box-shadow: + 0 20px 25px -5px rgba(0, 0, 0, 0.1), + 0 8px 10px -6px rgba(0, 0, 0, 0.1); /* hover:shadow-xl */ +} + +/* Badges */ +.product-badges { + position: absolute; + top: 0.75rem; /* top-3 */ + left: 0.75rem; /* left-3 */ + display: flex; + flex-direction: column; + gap: 0.5rem; /* gap-2 */ + z-index: 10; +} + +.badge { + padding: 0.25rem 0.5rem; /* px-2 py-1 */ + font-size: 0.75rem; /* text-xs */ + font-weight: 600; /* font-semibold */ + border-radius: 0.25rem; /* rounded */ + line-height: 1; + text-transform: uppercase; +} +.badge-discount { + background-color: var(--sale-bg); + color: var(--sale-fg); +} + +/* Clickable Area */ +.product-link { + text-decoration: none; + color: inherit; + display: block; + flex: 1; /* Pushes footer to the bottom */ +} + +.product-img-wrapper { + position: relative; + aspect-ratio: 4 / 5; + background-color: var(--secondary); + overflow: hidden; +} + +.product-img { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.5s ease; +} +.product-card:hover .product-img { + transform: scale(1.05); /* group-hover:scale-105 */ +} + +/* Content Area */ +.product-info { + padding: 1rem; /* p-4 */ + display: flex; + flex-direction: column; +} + +.product-desc { + font-size: 0.75rem; /* text-xs */ + color: var(--muted-foreground); + margin: 0 0 0.25rem 0; /* mb-1 */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.product-title { + font-size: 1rem; + font-weight: 600; /* font-semibold */ + color: var(--foreground); + margin: 0 0 0.75rem 0; /* mb-3 */ + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + line-height: 1.3; + transition: color 0.2s; +} +.product-card:hover .product-title { + color: var(--accent); /* group-hover:text-accent */ +} + +.product-pricing { + display: flex; + align-items: baseline; + gap: 0.5rem; /* gap-2 */ + margin-bottom: 0.75rem; /* mb-3 */ +} + +.price-current { + font-size: 1.125rem; /* text-lg */ + font-weight: 700; /* font-bold */ + color: var(--foreground); +} + +.price-original { + font-size: 0.875rem; /* text-sm */ + color: var(--muted-foreground); + text-decoration: line-through; +} + +/* Footer (Availability & Button) */ +.product-footer { + display: flex; + align-items: center; + justify-content: space-between; + margin-top: auto; +} + +.product-availability { + font-size: 0.75rem; /* text-xs */ + font-weight: 500; + color: var(--muted-foreground); +} + +/* Soft-matching text for Skladem to display green */ +.product-availability[data-status*="Skladem"] { + color: var(--success-text); +} + +/* Injected Form Styling */ +.product-buy-form { + margin: 0; +} +.product-buy-form .btn { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.5rem 1rem; /* px-4 py-2 */ + background-color: var(--primary); + color: var(--primary-foreground); + border-radius: 0.5rem; /* rounded-lg */ + font-size: 0.875rem; /* text-sm */ + font-weight: 500; /* font-medium */ + border: none; + cursor: pointer; + transition: background-color 0.2s; + font-family: inherit; + gap: 0.5rem; +} +.product-buy-form .btn:hover { + background-color: color-mix( + in srgb, + var(--primary) 90%, + transparent + ); /* hover:bg-primary/90 */ +} + +/* Hide the text on small screens and just show icon if you insert one via pseudo-element, + but for injected forms, we keep standard padding to make it responsive. */ +@media (max-width: 639px) { + .product-buy-form .btn { + padding: 0.5rem 0.75rem; + font-size: 0.75rem; + } +} diff --git a/homepage/templates/section_middle_novinky/template.html b/homepage/templates/section_middle_novinky/template.html new file mode 100644 index 0000000..c6c9961 --- /dev/null +++ b/homepage/templates/section_middle_novinky/template.html @@ -0,0 +1,76 @@ + + + + + + {{section}} + Zobrazit vše → + + + + + {{#each items}} + + + + + {{#each this.flags}} + + {{this.text}} + + {{/each}} + + {{#if this.priceDiscount}} + + {{this.priceDiscount}}% + + {{/if}} + + + + + + + + + + {{#if this.description}} + {{this.description}} + {{/if}} + + {{this.title}} + + + {{this.priceHighlight}} + {{#if this.priceBeforeDiscount}} + {{this.priceBeforeDiscount}} + {{/if}} + + + + + + + + + + + {{/each}} + + + + diff --git a/homepage/templates/section_middle_tabs/replace_selector b/homepage/templates/section_middle_tabs/replace_selector index 9bf7204..a0685a8 100644 --- a/homepage/templates/section_middle_tabs/replace_selector +++ b/homepage/templates/section_middle_tabs/replace_selector @@ -1 +1 @@ -main#main #AjaxMainSection section.section_middle +main#main #AjaxMainSection section.section_middle .vc-tabstemplate diff --git a/homepage/templates/section_middle_tabs/scrape.js b/homepage/templates/section_middle_tabs/scrape.js index 4b8fd89..7511b4f 100644 --- a/homepage/templates/section_middle_tabs/scrape.js +++ b/homepage/templates/section_middle_tabs/scrape.js @@ -16,14 +16,13 @@ for (let i = 0; i < tabTexts.length; i++) { const idName = `Index_${i + 1}`; - const items = body.querySelectorAll(`#${idName} .owl-item > article.commodityBox`); + const items = body.querySelectorAll(`#${idName} article.commodityBox`); const itemsData = Array.from(items).map((item) => { const productNode = item.querySelector("a.inner"); const link = productNode.getAttribute("href"); - // UPDATED: Extracting background and text colors using getComputedStyle const flagsElements = productNode.querySelectorAll(".flags > .flags-inner > .flag"); const flags = Array.from(flagsElements).map((flag) => { const styles = window.getComputedStyle(flag);
{{this.annotation}}
{{this.description}}