generated from boboko/starter
Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5482ac07c6 | ||
|
|
7e38f05650 | ||
|
|
df29429257 | ||
|
|
f6bcd31bd8 | ||
|
|
77c9117556 | ||
|
|
8fdb399386 |
@@ -133,6 +133,13 @@ services:
|
||||
|
||||
stoic:
|
||||
image: ghcr.io/lexx27/stoic:latest
|
||||
# Runs as root — the app container's entrypoint.sh chowns the whole
|
||||
# storage/ tree to www-data on every boot (docker/entrypoint.sh),
|
||||
# and Stoic's own image user isn't www-data or in its group, so it
|
||||
# can't write thumbnails into storage/app/public/stoic/ otherwise
|
||||
# (libvips reports this as "Failed to write VipsImage to file").
|
||||
# Same fix already applied in docker-compose.dev.yml.
|
||||
user: root
|
||||
ports:
|
||||
- "127.0.0.1:${STOIC_PORT:-2727}:2727"
|
||||
environment:
|
||||
|
||||
@@ -25,6 +25,17 @@ server {
|
||||
if ($request_method = POST) { set $skip_cache 1; }
|
||||
if ($request_uri ~* "^/(boboko|up)") { set $skip_cache 1; }
|
||||
if ($query_string) { set $skip_cache 1; }
|
||||
# Any logged-in request must never be served a cached response —
|
||||
# the cache key is URL-only ($scheme$request_method$host$request_uri,
|
||||
# see fastcgi_cache_key below), with no per-session variation. Without
|
||||
# this, the first cached hit on an authenticated GET page (e.g.
|
||||
# /account) gets replayed verbatim to every other visitor of that same
|
||||
# URL for up to fastcgi_cache_valid's 10m window — a real account/
|
||||
# session data leak, not just a staleness bug. Laravel's session
|
||||
# cookie name is env('SESSION_COOKIE', Str::slug(APP_NAME).'-session')
|
||||
# (config/session.php) — APP_NAME varies per environment, so this
|
||||
# matches the fixed '-session' suffix rather than a hardcoded name.
|
||||
if ($http_cookie ~* "-session") { set $skip_cache 1; }
|
||||
|
||||
# Vite build assets — content-hashed filenames, safe to cache forever
|
||||
location /build/ {
|
||||
|
||||
@@ -3,20 +3,25 @@ import { Controller } from "@hotwired/stimulus";
|
||||
const SCROLL_STEP = 1;
|
||||
|
||||
export default class extends Controller {
|
||||
static targets = ["main", "thumb", "track", "lightbox", "lightboxImage", "lightboxCounter"];
|
||||
static targets = ["main", "thumb", "track", "arrow", "lightbox", "lightboxImage", "lightboxCounter"];
|
||||
|
||||
connect() {
|
||||
this.offset = 0;
|
||||
this.lightboxIndex = 0;
|
||||
this.#syncHeight();
|
||||
|
||||
this.mainTarget.addEventListener("load", () => this.#syncHeight());
|
||||
// Main image and thumbnails resize as their images load and on viewport
|
||||
// changes; re-sync the track height and arrow visibility each time.
|
||||
this._resizeObserver = new ResizeObserver(() => this.#syncHeight());
|
||||
this._resizeObserver.observe(this.mainTarget);
|
||||
this.thumbTargets.forEach((t) => this._resizeObserver.observe(t));
|
||||
|
||||
this._onKeydown = this.#handleKeydown.bind(this);
|
||||
document.addEventListener("keydown", this._onKeydown);
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this._resizeObserver?.disconnect();
|
||||
document.removeEventListener("keydown", this._onKeydown);
|
||||
}
|
||||
|
||||
@@ -103,6 +108,14 @@ export default class extends Controller {
|
||||
if (h > 0) {
|
||||
this.trackTarget.style.maxHeight = h + "px";
|
||||
}
|
||||
this.#toggleArrows();
|
||||
}
|
||||
|
||||
// Arrows stay in the layout (invisible, not hidden) so the thumbnail
|
||||
// column keeps its alignment with the main image.
|
||||
#toggleArrows() {
|
||||
const overflowing = this.trackTarget.scrollHeight > this.trackTarget.clientHeight;
|
||||
this.arrowTargets.forEach((a) => a.classList.toggle("invisible", !overflowing));
|
||||
}
|
||||
|
||||
#handleKeydown(e) {
|
||||
|
||||
@@ -37,7 +37,8 @@ class="relative flex gap-4 items-start"
|
||||
<button
|
||||
type="button"
|
||||
data-action="click->product-gallery#scrollUp"
|
||||
class="gallery-arrow w-full flex items-center justify-center py-2"
|
||||
data-product-gallery-target="arrow"
|
||||
class="gallery-arrow invisible w-full flex items-center justify-center py-2"
|
||||
aria-label="Scroll thumbnails up"
|
||||
><x-ui.icon name="arrow-up" :size="30" /></button>
|
||||
|
||||
@@ -67,7 +68,8 @@ class="block w-full shrink-0 border border-black overflow-hidden "
|
||||
<button
|
||||
type="button"
|
||||
data-action="click->product-gallery#scrollDown"
|
||||
class="gallery-arrow w-full flex items-center justify-center py-2"
|
||||
data-product-gallery-target="arrow"
|
||||
class="gallery-arrow invisible w-full flex items-center justify-center py-2"
|
||||
aria-label="Scroll thumbnails down"
|
||||
><x-ui.icon name="arrow-down" :size="30" /></button>
|
||||
</div>
|
||||
@@ -231,14 +233,9 @@ class="flex flex-col gap-6"
|
||||
['id' => 'reviews', 'label' => __('storefront.product.reviews') . ' (' . $product['reviews']['count'] . ')'],
|
||||
]">
|
||||
<x-slot name="description">
|
||||
<div class="leading-7 [&_p]:mt-4">
|
||||
<div class="leading-7 [&_p]:mt-4 [&_ul]:flex [&_ul]:flex-col [&_ul]:gap-3 [&_ul]:text-neutral-600 [&_ul]:list-disc [&_ul]:list-outside [&_ul]:pl-5">
|
||||
{!! $product['description'] !!}
|
||||
</div>
|
||||
<ul class="mt-8 flex flex-col gap-3 text-neutral-600 list-disc list-outside pl-5">
|
||||
<li>Όλα τα προϊόντα εκτυπώνονται και προετοιμάζονται κατά παραγγελία. Ο χρόνος προετοιμασίας κυμαίνεται μεταξύ 2 και 7 εργάσιμων ημερών.</li>
|
||||
<li>Όλα τα προϊόντα κατασκευάζονται με τρισδιάστατη εκτύπωση σε ειδικούς εκτυπωτές πλαστικού υλικού. Πιθανώς να έχουν εμφανείς γραμμές ένωσης, στρώσεις εκτύπωσης υλικού και μικρές ατέλειες. Είναι φυσιολογικό για το αποτέλεσμα αυτής της δημιουργικής διαδικασίας.</li>
|
||||
<li>Όλα τα προϊόντα είναι σχεδιασμένα και κατασκευασμένα είτε για διακόσμηση είτε για χρήση σε λογικά, ρεαλιστικά πλαίσια. Υπερβολική ισχύς, υψηλότατες θερμοκρασίες και αποσυναρμολόγηση μπορεί να προκαλέσουν ζημιά στο προϊόν για την οποία δεν ευθύνεται το 3Dealer.</li>
|
||||
</ul>
|
||||
</x-slot>
|
||||
<x-slot name="reviews">
|
||||
@if(session('reviewSubmitted'))
|
||||
|
||||
Reference in New Issue
Block a user