Created
May 11, 2025 19:48
-
-
Save drhema/0717f61d46ea5d13808548db6136f91a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div class="container mx-auto mt-8 px-4 lg:px-0"> | |
<!-- Sticky Add to Cart for Mobile --> | |
<div id="sticky-add-to-cart" class="fixed top-0 left-0 right-0 bg-white shadow-md z-50 py-2 px-3 hidden transform transition-transform duration-300 -translate-y-full"> | |
<div class="flex items-center justify-between"> | |
<!-- Product Thumbnail --> | |
<div class="flex items-center"> | |
<div class="h-12 w-12 bg-gray-100 rounded-md overflow-hidden flex-shrink-0 mr-3"> | |
<%= if @product["base_image"] do %> | |
<img | |
src={@product["base_image"]["small_image_url"]} | |
alt={@product["name"]} | |
class="h-full w-full object-contain" | |
/> | |
<% else %> | |
<div class="flex items-center justify-center h-full w-full text-gray-300"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
</svg> | |
</div> | |
<% end %> | |
</div> | |
<!-- Product Info --> | |
<div class="overflow-hidden"> | |
<h3 class="text-sm font-medium text-gray-900 truncate max-w-[150px]"><%= @product["name"] %></h3> | |
<%= cond do %> | |
<% @product["formatted_special_price"] && @product["special_price"] && | |
(is_number(@product["special_price"]) && @product["special_price"] > 0 || | |
is_binary(@product["special_price"]) && String.trim(@product["special_price"]) != "0") -> %> | |
<p class="text-xs text-red-600 font-medium"><%= @product["formatted_special_price"] %></p> | |
<% @product["formated_special_price"] && @product["special_price"] && | |
(is_number(@product["special_price"]) && @product["special_price"] > 0 || | |
is_binary(@product["special_price"]) && String.trim(@product["special_price"]) != "0") -> %> | |
<p class="text-xs text-red-600 font-medium"><%= @product["formated_special_price"] %></p> | |
<% @product["formatted_price"] -> %> | |
<p class="text-xs text-gray-900"><%= @product["formatted_price"] %></p> | |
<% @product["formated_price"] -> %> | |
<p class="text-xs text-gray-900"><%= @product["formated_price"] %></p> | |
<% @product["price"] -> %> | |
<p class="text-xs text-gray-900"> | |
<%= if @locale == "ar" do %>د.ك<% else %>KD<% end %> | |
<%= try do Float.round(String.to_float(@product["price"]), 3) rescue _ -> @product["price"] end %> | |
</p> | |
<% true -> %> | |
<p class="text-xs text-gray-500"> | |
<%= if @locale == "ar" do %>السعر غير متوفر<% else %>Price not available<% end %> | |
</p> | |
<% end %> | |
</div> | |
</div> | |
<!-- Add to Cart Button --> | |
<button | |
type="button" | |
class="sticky-add-to-cart-btn bg-blue-600 hover:bg-blue-700 text-white text-sm px-3 py-2 rounded-lg font-medium disabled:opacity-50" | |
disabled={!@product["in_stock"]} | |
onclick="addToCartSticky()" | |
> | |
<%= if @product["in_stock"] do %> | |
<%= if @locale == "ar" do %>أضف للسلة<% else %>Add to Cart<% end %> | |
<% else %> | |
<%= if @locale == "ar" do %>غير متوفر<% else %>Sold Out<% end %> | |
<% end %> | |
</button> | |
</div> | |
</div> | |
<%# Helper functions for handling product data %> | |
<% | |
get_slug = fn string -> | |
if is_binary(string) do | |
String.downcase(string) |> String.replace(~r/[^a-z0-9-]/, "-") |> String.replace(~r/-+/, "-") |> String.trim("-") | |
else | |
"" | |
end | |
end | |
# Add this new helper function for product slugs | |
get_product_slug = fn product -> | |
cond do | |
is_map(product) && Map.has_key?(product, "slug") && product["slug"] -> | |
product["slug"] | |
is_map(product) && Map.has_key?(product, "url_key") && product["url_key"] -> | |
product["url_key"] | |
is_map(product) && Map.has_key?(product, "name") && product["name"] -> | |
get_slug.(product["name"]) | |
true -> | |
"product" # Fallback slug to prevent nil errors | |
end | |
end | |
# Construct full product URL for schema | |
product_url = "#{System.get_env("BASE_URL", "https://example.com")}/#{@locale}/products/#{@product["slug"] || @product["url_key"] || get_slug.(@product["name"])}" | |
# Calculate current price for dataLayer (use special_price if available, otherwise regular price) | |
current_price = cond do | |
is_number(@product["special_price"]) && @product["special_price"] > 0 -> @product["special_price"] | |
is_binary(@product["special_price"]) && (try do String.to_float(@product["special_price"]) > 0 rescue _ -> false end) -> | |
(try do String.to_float(@product["special_price"]) rescue _ -> 0 end) | |
is_number(@product["price"]) -> @product["price"] | |
is_binary(@product["price"]) -> (try do String.to_float(@product["price"]) rescue _ -> 0 end) | |
true -> 0 | |
end | |
# Calculate discount amount if applicable | |
discount_amount = cond do | |
is_number(@product["price"]) && is_number(@product["special_price"]) && | |
@product["special_price"] > 0 && @product["special_price"] < @product["price"] -> | |
@product["price"] - @product["special_price"] | |
is_binary(@product["price"]) && is_binary(@product["special_price"]) && | |
(try do String.to_float(@product["special_price"]) > 0 && | |
String.to_float(@product["special_price"]) < String.to_float(@product["price"]) rescue _ -> false end) -> | |
(try do String.to_float(@product["price"]) - String.to_float(@product["special_price"]) rescue _ -> 0 end) | |
true -> 0 | |
end | |
# Get category path | |
category_name = cond do | |
is_map(@product["category"]) && Map.has_key?(@product["category"], "name") -> @product["category"]["name"] | |
is_binary(@product["category"]) -> @product["category"] | |
true -> nil | |
end | |
# Get brand | |
brand_name = cond do | |
is_map(@product["brand"]) && Map.has_key?(@product["brand"], "name") -> @product["brand"]["name"] | |
is_binary(@product["brand"]) -> @product["brand"] | |
true -> nil | |
end | |
# Check if product is a tire | |
is_tire_product = cond do | |
@product["category_id"] == 150 -> true | |
@product["category_id"] == 152 -> true | |
is_binary(@product["category_slug"]) && | |
(String.contains?(@product["category_slug"], "tire") || | |
String.contains?(@product["category_slug"], "tyre")) -> true | |
is_binary(@product["category"]) && | |
(String.contains?(@product["category"], "إطارات") || | |
String.contains?(@product["category"] || "", "Tire") || | |
String.contains?(@product["category"] || "", "Tyre")) -> true | |
Map.has_key?(@product, "tire_width") && @product["tire_width"] != nil -> true | |
Map.has_key?(@product, "rim_size") && @product["rim_size"] != nil -> true | |
true -> false | |
end | |
%> | |
<!-- Google Analytics 4 dataLayer for view_item event --> | |
<script> | |
window.dataLayer = window.dataLayer || []; | |
dataLayer.push({ ecommerce: null }); // Clear the previous ecommerce object | |
dataLayer.push({ | |
event: "view_item", | |
ecommerce: { | |
currency: "KWD", | |
value: <%= current_price %>, | |
items: [ | |
{ | |
item_id: "<%= @product["sku"] || @product["id"] %>", | |
item_name: "<%= String.replace(@product["name"] || "", ~r/["\\]/, "") %>", | |
item_brand: "<%= brand_name && String.replace(brand_name, ~r/["\\]/, "") %>", | |
item_category: "<%= category_name && String.replace(category_name, ~r/["\\]/, "") %>", | |
<%= if @product["type"] do %>item_variant: "<%= String.replace(@product["type"], ~r/["\\]/, "") %>",<% end %> | |
price: <%= @product["price"] || 0 %>, | |
discount: <%= discount_amount %>, | |
quantity: 1, | |
index: 0, | |
item_list_id: "product_detail", | |
item_list_name: "Product Detail" | |
} | |
] | |
} | |
}); | |
</script> | |
<!-- Modern Notification System --> | |
<div id="notification-container" class="fixed top-0 left-0 right-0 z-[60] flex items-center justify-center pointer-events-none transform translate-y-[-100%] transition-transform duration-300"> | |
<div id="notification" class="bg-white border border-gray-100 shadow-lg rounded-md py-3 px-4 m-4 max-w-md w-full pointer-events-auto flex items-center"> | |
<div id="notification-icon" class="flex-shrink-0 w-10 h-10 rounded-full flex items-center justify-center mr-3"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /> | |
</svg> | |
</div> | |
<div class="flex-1"> | |
<h3 id="notification-title" class="text-sm font-medium text-gray-900"> | |
<%= if @locale == "ar", do: "نجاح!", else: "Success!" %> | |
</h3> | |
<p id="notification-message" class="mt-1 text-sm text-gray-500"></p> | |
</div> | |
<button id="notification-close" class="ml-4 flex-shrink-0 text-gray-400 hover:text-gray-500 focus:outline-none"> | |
<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 20 20"> | |
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/> | |
</svg> | |
</button> | |
</div> | |
</div> | |
<!-- Breadcrumbs with Schema.org markup --> | |
<nav class="mb-4 text-sm"> | |
<ol class="list-none p-0 inline-flex" itemscope itemtype="https://schema.org/BreadcrumbList"> | |
<!-- Home --> | |
<li class="flex items-center" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"> | |
<a href={~p"/#{@locale}"} class="text-gray-500 hover:text-blue-600" itemprop="item"> | |
<span itemprop="name"> | |
<%= if @locale == "ar" do %> | |
المتجر | |
<% else %> | |
Home | |
<% end %> | |
</span> | |
</a> | |
<meta itemprop="position" content="1" /> | |
<svg class="h-4 w-4 mx-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> | |
</svg> | |
</li> | |
<!-- Category (if present) --> | |
<%= if @product["category"] do %> | |
<li class="flex items-center" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"> | |
<%= if is_map(@product["category"]) do %> | |
<a href={~p"/#{@locale}/categories/#{@product["category"]["slug"]}"} class="text-gray-500 hover:text-blue-600" itemprop="item"> | |
<span itemprop="name"><%= @product["category"]["name"] %></span> | |
</a> | |
<% else %> | |
<a href={~p"/#{@locale}/categories/#{@product["category_slug"] || get_slug.(@product["category"])}"} class="text-gray-500 hover:text-blue-600" itemprop="item"> | |
<span itemprop="name"><%= @product["category"] %></span> | |
</a> | |
<% end %> | |
<meta itemprop="position" content="2" /> | |
<svg class="h-4 w-4 mx-2 text-gray-400" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" /> | |
</svg> | |
</li> | |
<% end %> | |
<!-- Current Product --> | |
<li class="text-gray-700 font-medium" itemprop="itemListElement" itemscope itemtype="https://schema.org/ListItem"> | |
<span itemprop="name"><%= @product["name"] %></span> | |
<!-- This is a simpler approach to avoid the nil parameter error --> | |
<meta itemprop="item" content={"/" <> @locale <> "/products/" <> ((@product["slug"] || @product["url_key"] || get_slug.(@product["name"])))} /> | |
<meta itemprop="position" content={if @product["category"], do: "3", else: "2"} /> | |
</li> | |
</ol> | |
</nav> | |
<!-- Product Details with Schema.org markup --> | |
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8" itemscope itemtype="https://schema.org/Product"> | |
<!-- Hidden SEO metadata --> | |
<meta itemprop="name" content={@product["name"]} /> | |
<meta itemprop="url" content={product_url} /> | |
<meta itemprop="sku" content={@product["sku"] || ""} /> | |
<%= if @product["brand"] do %> | |
<div itemprop="brand" itemscope itemtype="https://schema.org/Brand"> | |
<meta itemprop="name" content={if is_map(@product["brand"]), do: @product["brand"]["name"], else: @product["brand"]} /> | |
</div> | |
<% end %> | |
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 p-6"> | |
<!-- Product Images --> | |
<div class="col-span-1"> | |
<div id="product-images" class="mb-4"> | |
<!-- Main Image --> | |
<div id="main-image" class="bg-gray-100 rounded-lg h-80 flex items-center justify-center mb-4 relative"> | |
<%= if @product["base_image"] do %> | |
<img | |
itemprop="image" | |
src={@product["base_image"]["large_image_url"]} | |
alt={@product["name"]} | |
class="max-h-80 max-w-full object-contain" | |
/> | |
<% else %> | |
<div class="text-gray-300"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-24 w-24" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
</svg> | |
</div> | |
<% end %> | |
<!-- Discount Badge (if applicable) --> | |
<%= if @product["special_price"] && @product["price"] && is_binary(@product["special_price"]) && is_binary(@product["price"]) && (try do String.to_float(@product["special_price"]) < String.to_float(@product["price"]) rescue _ -> false end) do %> | |
<div class="absolute top-0 left-0 bg-red-600 text-white py-1 px-3 m-4 rounded-md font-bold"> | |
<%= if @product["percentage"] do %> | |
<%= @product["percentage"] %> <%= if @locale == "ar" do %>خصم<% else %>OFF<% end %> | |
<% else %> | |
<%= if @locale == "ar" do %>خصم<% else %>SALE<% end %> | |
<% end %> | |
</div> | |
<% end %> | |
</div> | |
<!-- Thumbnail Gallery --> | |
<%= if @product["images"] && is_list(@product["images"]) && length(@product["images"]) > 0 do %> | |
<div class="flex space-x-2 overflow-x-auto pb-2"> | |
<%= for {image, index} <- Enum.with_index(@product["images"]) do %> | |
<button | |
class="bg-gray-100 rounded-md h-20 w-20 flex items-center justify-center hover:ring-2 hover:ring-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500 overflow-hidden" | |
onclick={"showImage('#{image["large_image_url"]}', '#{@product["name"]}')"}> | |
> | |
<img | |
src={image["small_image_url"]} | |
alt={"#{@product["name"]} - Image #{index + 1}"} | |
class="max-h-20 max-w-full object-contain" | |
/> | |
</button> | |
<% end %> | |
</div> | |
<% end %> | |
</div> | |
</div> | |
<!-- Product Info --> | |
<div class="col-span-1 lg:col-span-2"> | |
<h1 class="text-3xl font-bold text-gray-900 mb-2"><%= @product["name"] %></h1> | |
<!-- Brand --> | |
<%= if @product["brand"] do %> | |
<div class="mb-4"> | |
<%= if is_map(@product["brand"]) do %> | |
<a href={~p"/#{@locale}/brands/#{@product["brand"]["slug"]}"} class="text-blue-600 hover:text-blue-800 font-medium"> | |
<%= @product["brand"]["name"] %> | |
</a> | |
<% else %> | |
<a href={~p"/#{@locale}/brands/#{@product["brand_slug"] || get_slug.(@product["brand"])}"} class="text-blue-600 hover:text-blue-800 font-medium"> | |
<%= @product["brand"] %> | |
</a> | |
<% end %> | |
</div> | |
<% end %> | |
<!-- Stock Status with Schema.org markup --> | |
<div class="mb-4"> | |
<%= if @product["in_stock"] do %> | |
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium bg-green-100 text-green-800"> | |
<span class="h-2 w-2 rounded-full bg-green-500 mr-1"></span> | |
In Stock | |
</span> | |
<% else %> | |
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-sm font-medium bg-red-100 text-red-800"> | |
<span class="h-2 w-2 rounded-full bg-red-500 mr-1"></span> | |
Out of Stock | |
</span> | |
<% end %> | |
</div> | |
<!-- Pricing with Schema.org markup --> | |
<div class="mb-6"> | |
<div itemprop="offers" itemscope itemtype="https://schema.org/Offer"> | |
<%= if @product["in_stock"] do %> | |
<link itemprop="availability" href="https://schema.org/InStock" /> | |
<% else %> | |
<link itemprop="availability" href="https://schema.org/OutOfStock" /> | |
<% end %> | |
<meta itemprop="url" content={product_url} /> | |
<meta itemprop="priceCurrency" content="KWD" /> | |
<%= cond do %> | |
<% @product["formatted_special_price"] && @product["formatted_price"] && @product["special_price"] && | |
(is_number(@product["special_price"]) && @product["special_price"] > 0 || | |
is_binary(@product["special_price"]) && String.trim(@product["special_price"]) != "0" && String.trim(@product["special_price"]) != "0.0000") -> %> | |
<div class="flex flex-wrap items-center gap-2"> | |
<span class="text-3xl font-bold text-red-600"> | |
<meta itemprop="price" content={if is_binary(@product["special_price"]), do: @product["special_price"], else: to_string(@product["special_price"])} /> | |
<%= @product["formatted_special_price"] %> | |
</span> | |
<span class="text-xl text-gray-500 line-through"><%= @product["formatted_price"] %></span> | |
<%= if @product["percentage"] do %> | |
<span class="text-sm font-medium bg-green-100 text-green-800 py-1 px-2 rounded"> | |
<%= @product["percentage"] %> <%= if @locale == "ar" do %>خصم<% else %>OFF<% end %> | |
</span> | |
<% end %> | |
</div> | |
<% @product["formated_special_price"] && @product["formated_price"] && @product["special_price"] && | |
(is_number(@product["special_price"]) && @product["special_price"] > 0 || | |
is_binary(@product["special_price"]) && String.trim(@product["special_price"]) != "0" && String.trim(@product["special_price"]) != "0.0000") -> %> | |
<div class="flex flex-wrap items-center gap-2"> | |
<span class="text-3xl font-bold text-red-600"> | |
<meta itemprop="price" content={if is_binary(@product["special_price"]), do: @product["special_price"], else: to_string(@product["special_price"])} /> | |
<%= @product["formated_special_price"] %> | |
</span> | |
<span class="text-xl text-gray-500 line-through"><%= @product["formated_price"] %></span> | |
<%= if @product["percentage"] do %> | |
<span class="text-sm font-medium bg-green-100 text-green-800 py-1 px-2 rounded"> | |
<%= @product["percentage"] %> <%= if @locale == "ar" do %>خصم<% else %>OFF<% end %> | |
</span> | |
<% end %> | |
</div> | |
<% @product["special_price"] && @product["price"] && | |
((is_number(@product["special_price"]) && @product["special_price"] > 0) || | |
(is_binary(@product["special_price"]) && String.trim(@product["special_price"]) != "0" && String.trim(@product["special_price"]) != "0.0000")) && | |
is_binary(@product["special_price"]) && is_binary(@product["price"]) && | |
(try do String.to_float(@product["special_price"]) < String.to_float(@product["price"]) rescue _ -> false end) -> %> | |
<div class="flex flex-wrap items-center gap-2"> | |
<span class="text-3xl font-bold text-red-600"> | |
<meta itemprop="price" content={@product["special_price"]} /> | |
<%= if @locale == "ar" do %>د.ك<% else %>KD<% end %> | |
<%= try do Float.round(String.to_float(@product["special_price"]), 3) rescue _ -> @product["special_price"] end %> | |
</span> | |
<span class="text-xl text-gray-500 line-through"> | |
<%= if @locale == "ar" do %>د.ك<% else %>KD<% end %> | |
<%= try do Float.round(String.to_float(@product["price"]), 3) rescue _ -> @product["price"] end %> | |
</span> | |
<%= if @product["percentage"] do %> | |
<span class="text-sm font-medium bg-green-100 text-green-800 py-1 px-2 rounded"> | |
<%= @product["percentage"] %> <%= if @locale == "ar" do %>خصم<% else %>OFF<% end %> | |
</span> | |
<% end %> | |
</div> | |
<% @product["formatted_price"] -> %> | |
<span class="text-3xl font-bold text-gray-900"> | |
<meta itemprop="price" content={if is_binary(@product["price"]), do: @product["price"], else: to_string(@product["price"])} /> | |
<%= @product["formatted_price"] %> | |
</span> | |
<% @product["formated_price"] -> %> | |
<span class="text-3xl font-bold text-gray-900"> | |
<meta itemprop="price" content={if is_binary(@product["price"]), do: @product["price"], else: to_string(@product["price"])} /> | |
<%= @product["formated_price"] %> | |
</span> | |
<% @product["price"] -> %> | |
<span class="text-3xl font-bold text-gray-900"> | |
<meta itemprop="price" content={@product["price"]} /> | |
<%= if @locale == "ar" do %>د.ك<% else %>KD<% end %> | |
<%= try do Float.round(String.to_float(@product["price"]), 3) rescue _ -> @product["price"] end %> | |
</span> | |
<% true -> %> | |
<span class="text-xl font-bold text-gray-500"> | |
<%= if @locale == "ar" do %>السعر غير متوفر<% else %>Price not available<% end %> | |
</span> | |
<% end %> | |
</div> | |
<!-- free delivery over 10kwd/Delivery Info --> | |
<p class="text-sm mt-1"> | |
<%= cond do %> | |
<% is_number(@product["price"]) && @product["price"] > 10 || | |
is_binary(@product["price"]) && (try do String.to_float(@product["price"]) > 10 rescue _ -> false end) || | |
is_number(@product["special_price"]) && @product["special_price"] > 10 || | |
is_binary(@product["special_price"]) && (try do String.to_float(@product["special_price"]) > 10 rescue _ -> false end) -> %> | |
<span class="text-green-600 font-medium"> | |
<%= if @locale == "ar" do %>توصيل مجاني<% else %>Free Delivery<% end %> | |
</span> | |
<% true -> %> | |
<% | |
# Calculate how much more needed for free delivery | |
current_price = cond do | |
is_number(@product["special_price"]) && @product["special_price"] > 0 -> @product["special_price"] | |
is_binary(@product["special_price"]) && (try do String.to_float(@product["special_price"]) > 0 rescue _ -> false end) -> (try do String.to_float(@product["special_price"]) rescue _ -> 0 end) | |
is_number(@product["price"]) -> @product["price"] | |
is_binary(@product["price"]) -> (try do String.to_float(@product["price"]) rescue _ -> 0 end) | |
true -> 0 | |
end | |
more_needed = 10 - current_price | |
more_needed = if more_needed < 0, do: 0, else: more_needed | |
more_needed_formatted = :erlang.float_to_binary(more_needed, [decimals: 3]) | |
%> | |
<%= if more_needed > 0 do %> | |
<span class="text-gray-600"> | |
<%= if @locale == "ar" do %> | |
أضف منتجات بقيمة <%= more_needed_formatted %> د.ك للحصول على توصيل مجاني | |
<% else %> | |
Add <%= more_needed_formatted %> KWD more to get free delivery | |
<% end %> | |
</span> | |
<% end %> | |
<% end %> | |
</p> | |
</div> | |
<!-- Modern Collapsible Tire Specifications - Only shown for tire products --> | |
<%= if is_tire_product do %> | |
<div class="mb-6"> | |
<div class="tire-specs-collapsible border border-gray-200 rounded-lg overflow-hidden"> | |
<!-- Header - Always visible --> | |
<button | |
id="tire-specs-toggle" | |
class="w-full py-4 px-5 bg-gray-50 flex justify-between items-center cursor-pointer hover:bg-gray-100 transition-colors duration-200" | |
onclick="toggleTireSpecs()" | |
> | |
<div class="flex items-center space-x-2"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-blue-600" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> | |
</svg> | |
<span class="font-medium text-gray-900 text-lg"> | |
<%= if @locale == "ar" do %>مواصفات الإطار<% else %>Tire Specifications<% end %> | |
</span> | |
</div> | |
<svg id="tire-specs-chevron" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 text-gray-500 transform transition-transform duration-300" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7" /> | |
</svg> | |
</button> | |
<!-- Collapsible Content - Hidden by default --> | |
<div id="tire-specs-content" class="tire-specs-content hidden"> | |
<div class="p-5 bg-white"> | |
<!-- Key Tire Details --> | |
<div class="tire-specs-card flex flex-wrap"> | |
<!-- Tire Size Card --> | |
<div class="tire-spec-box bg-gray-50 rounded-lg p-4 flex flex-col items-center justify-center text-center m-2 flex-1 min-w-[120px] transform transition-all hover:scale-105"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-600 mb-2" viewBox="0 0 20 20" fill="currentColor"> | |
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM10 4a6 6 0 100 12 6 6 0 000-12z" clip-rule="evenodd" /> | |
<path fill-rule="evenodd" d="M10 12a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd" /> | |
</svg> | |
<span class="block text-xs text-gray-500 font-medium"> | |
<%= if @locale == "ar" do %>مقاس الإطار<% else %>Tire Size<% end %> | |
</span> | |
<span class="block text-lg font-bold text-gray-900 mt-1"> | |
<%= if @product["tire_width"] && @product["tire_profile"] && @product["rim_size"] do %> | |
<%= @product["tire_width"] %>/<%= @product["tire_profile"] %>R<%= @product["rim_size"] %> | |
<% else %> | |
- | |
<% end %> | |
</span> | |
</div> | |
<!-- Load & Speed Rating Card --> | |
<div class="tire-spec-box bg-gray-50 rounded-lg p-4 flex flex-col items-center justify-center text-center m-2 flex-1 min-w-[120px] transform transition-all hover:scale-105"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-600 mb-2" viewBox="0 0 20 20" fill="currentColor"> | |
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-13a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V5z" clip-rule="evenodd" /> | |
</svg> | |
<span class="block text-xs text-gray-500 font-medium"> | |
<%= if @locale == "ar" do %>مؤشر الحمل والسرعة<% else %>Load & Speed<% end %> | |
</span> | |
<span class="block text-lg font-bold text-gray-900 mt-1"> | |
<%= if @product["load_index"] && @product["speed_rating"] do %> | |
<%= @product["load_index"] %><%= @product["speed_rating"] %> | |
<% else %> | |
- | |
<% end %> | |
</span> | |
</div> | |
<!-- Tire Type Card --> | |
<div class="tire-spec-box bg-gray-50 rounded-lg p-4 flex flex-col items-center justify-center text-center m-2 flex-1 min-w-[120px] transform transition-all hover:scale-105"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-600 mb-2" viewBox="0 0 20 20" fill="currentColor"> | |
<path d="M10 2a8 8 0 100 16 8 8 0 000-16zm0 2a6 6 0 110 12 6 6 0 010-12z" /> | |
<path d="M10 12a2 2 0 100-4 2 2 0 000 4z" /> | |
</svg> | |
<span class="block text-xs text-gray-500 font-medium"> | |
<%= if @locale == "ar" do %>نوع الإطار<% else %>Tire Type<% end %> | |
</span> | |
<span class="block text-lg font-bold text-gray-900 mt-1"> | |
<%= if @locale == "ar" do %> | |
<%= case @product["tire_type"] do | |
346 -> "صيفي" | |
347 -> "شتوي" | |
348 -> "كل المواسم" | |
_ -> "-" | |
end %> | |
<% else %> | |
<%= case @product["tire_type"] do | |
346 -> "Summer" | |
347 -> "Winter" | |
348 -> "All Season" | |
_ -> "-" | |
end %> | |
<% end %> | |
</span> | |
</div> | |
<!-- Year Card --> | |
<div class="tire-spec-box bg-gray-50 rounded-lg p-4 flex flex-col items-center justify-center text-center m-2 flex-1 min-w-[120px] transform transition-all hover:scale-105"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-8 w-8 text-blue-600 mb-2" viewBox="0 0 20 20" fill="currentColor"> | |
<path fill-rule="evenodd" d="M6 2a1 1 0 00-1 1v1H4a2 2 0 00-2 2v10a2 2 0 002 2h12a2 2 0 002-2V6a2 2 0 00-2-2h-1V3a1 1 0 10-2 0v1H7V3a1 1 0 00-1-1zm0 5a1 1 0 000 2h8a1 1 0 100-2H6z" clip-rule="evenodd" /> | |
</svg> | |
<span class="block text-xs text-gray-500 font-medium"> | |
<%= if @locale == "ar" do %>سنة التصنيع<% else %>Year<% end %> | |
</span> | |
<span class="block text-lg font-bold text-gray-900 mt-1"> | |
<%= @product["year_of_manufacture"] || "-" %> | |
</span> | |
</div> | |
</div> | |
<!-- Additional Specs (table-style) --> | |
<div class="mt-6 border-t border-gray-200 pt-4"> | |
<div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
<!-- Run Flat --> | |
<div class="flex items-center justify-between py-2 border-b border-gray-100"> | |
<span class="text-sm text-gray-600"> | |
<%= if @locale == "ar" do %>إطار مقاوم للثقب<% else %>Run Flat<% end %> | |
</span> | |
<span class="font-medium"> | |
<%= if @product["run_flat"] == 349 do %> | |
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800"> | |
<%= if @locale == "ar" do %>نعم<% else %>Yes<% end %> | |
</span> | |
<% else %> | |
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800"> | |
<%= if @locale == "ar" do %>لا<% else %>No<% end %> | |
</span> | |
<% end %> | |
</span> | |
</div> | |
<!-- Origin Country --> | |
<div class="flex items-center justify-between py-2 border-b border-gray-100"> | |
<span class="text-sm text-gray-600"> | |
<%= if @locale == "ar" do %>بلد المنشأ<% else %>Origin<% end %> | |
</span> | |
<span class="font-medium"> | |
<%= if @locale == "ar" do %> | |
<%= case @product["origin_country"] do | |
350 -> "الصين" | |
351 -> "كوريا" | |
352 -> "اليابان" | |
353 -> "ألمانيا" | |
_ -> "-" | |
end %> | |
<% else %> | |
<%= case @product["origin_country"] do | |
350 -> "China" | |
351 -> "Korea" | |
352 -> "Japan" | |
353 -> "Germany" | |
_ -> "-" | |
end %> | |
<% end %> | |
</span> | |
</div> | |
<!-- Warranty --> | |
<div class="flex items-center justify-between py-2 border-b border-gray-100"> | |
<span class="text-sm text-gray-600"> | |
<%= if @locale == "ar" do %>الضمان<% else %>Warranty<% end %> | |
</span> | |
<span class="font-medium"> | |
<%= if @locale == "ar" do %> | |
<%= case @product["warranty"] do | |
354 -> "12 شهر" | |
355 -> "24 شهر" | |
356 -> "36 شهر" | |
_ -> "-" | |
end %> | |
<% else %> | |
<%= case @product["warranty"] do | |
354 -> "12 Months" | |
355 -> "24 Months" | |
356 -> "36 Months" | |
_ -> "-" | |
end %> | |
<% end %> | |
</span> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
</div> | |
<% end %> | |
<!-- Short Description with Schema.org markup --> | |
<%= if @product["short_description"] do %> | |
<div class="mb-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-2"> | |
<%= if @locale == "ar" do %>الوصف<% else %>Description<% end %> | |
</h3> | |
<div class="text-gray-700 text-sm space-y-2" itemprop="description"> | |
<%= raw(@product["short_description"]) %> | |
</div> | |
</div> | |
<% end %> | |
<!-- Add to Cart Form --> | |
<%= if @product["type"] == "simple" do %> | |
<div class="mb-6"> | |
<div id="add-to-cart-form" class="space-y-4"> | |
<div class="flex items-center"> | |
<label for="quantity" class="block text-sm font-medium text-gray-700 mr-4"> | |
<%= if @locale == "ar" do %>الكمية<% else %>Quantity<% end %> | |
</label> | |
<div class="flex items-center border border-gray-300 rounded-md overflow-hidden"> | |
<button | |
type="button" | |
class="p-2 text-gray-500 hover:text-gray-700 focus:outline-none" | |
onclick="decrementQuantity()" | |
> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4" /> | |
</svg> | |
</button> | |
<input | |
type="number" | |
id="quantity" | |
name="quantity" | |
min="1" | |
value="1" | |
class="w-16 text-center border-0 focus:ring-0" | |
/> | |
<button | |
type="button" | |
class="p-2 text-gray-500 hover:text-gray-700 focus:outline-none" | |
onclick="incrementQuantity()" | |
> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> | |
</svg> | |
</button> | |
</div> | |
</div> | |
<div class="flex space-x-4"> | |
<button | |
type="button" | |
id="add-to-cart-button" | |
class="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-md font-medium flex-1 disabled:opacity-50 disabled:cursor-not-allowed" | |
onclick="addToCart()" | |
disabled={!@product["in_stock"]} | |
> | |
<%= cond do %> | |
<% @product["in_stock"] && @locale == "ar" -> %> | |
اضف المنتج لعربة التسوق | |
<% @product["in_stock"] -> %> | |
Add to Cart | |
<% @locale == "ar" -> %> | |
غير متوفر | |
<% true -> %> | |
Out of Stock | |
<% end %> | |
</button> | |
<button | |
type="button" | |
class="bg-gray-200 hover:bg-gray-300 text-gray-800 px-4 py-3 rounded-md font-medium" | |
> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z" /> | |
</svg> | |
</button> | |
</div> | |
</div> | |
</div> | |
<% else %> | |
<!-- For configurable/other product types, would add variant selectors here --> | |
<div class="mb-6"> | |
<p class="text-gray-700"> | |
<%= if @locale == "ar" do %> | |
يحتوي هذا المنتج على خيارات متعددة. يرجى تحديد تفضيلاتك. | |
<% else %> | |
This product has multiple options. Please select your preferences. | |
<% end %> | |
</p> | |
<!-- Variant selectors would go here --> | |
</div> | |
<% end %> | |
<!-- Product Meta Information --> | |
<div class="border-t border-gray-200 pt-4 space-y-2"> | |
<!-- SKU --> | |
<%= if @product["sku"] do %> | |
<div class="flex text-sm"> | |
<span class="text-gray-500 w-24">SKU:</span> | |
<span class="text-gray-900"><%= @product["sku"] %></span> | |
</div> | |
<% end %> | |
<!-- Category --> | |
<%= if @product["category"] do %> | |
<div class="flex text-sm"> | |
<span class="text-gray-500 w-24"> | |
<%= if @locale == "ar" do %>الفئة:<% else %>Category:<% end %> | |
</span> | |
<%= if is_map(@product["category"]) do %> | |
<a href={~p"/#{@locale}/categories/#{@product["category"]["slug"]}"} class="text-blue-600 hover:text-blue-800"> | |
<%= @product["category"]["name"] %> | |
</a> | |
<% else %> | |
<a href={~p"/#{@locale}/categories/#{@product["category_slug"] || get_slug.(@product["category"])}"} class="text-blue-600 hover:text-blue-800"> | |
<%= @product["category"] %> | |
</a> | |
<% end %> | |
</div> | |
<% end %> | |
<!-- Tags (if available) --> | |
<%= if @product["tags"] && is_list(@product["tags"]) && length(@product["tags"]) > 0 do %> | |
<div class="flex text-sm"> | |
<span class="text-gray-500 w-24"> | |
<%= if @locale == "ar" do %>العلامات:<% else %>Tags:<% end %> | |
</span> | |
<div> | |
<%= for {tag, i} <- Enum.with_index(@product["tags"]) do %> | |
<a href={~p"/#{@locale}/search?tag=#{tag}"} class="text-blue-600 hover:text-blue-800"> | |
<%= tag %><%= if i < length(@product["tags"]) - 1, do: ", " %> | |
</a> | |
<% end %> | |
</div> | |
</div> | |
<% end %> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Tabs for Description, Specifications, Reviews --> | |
<div class="bg-white rounded-lg shadow-md overflow-hidden mb-8"> | |
<div class="border-b border-gray-200"> | |
<nav class="flex -mb-px"> | |
<button class="tab-button active-tab py-4 px-6 text-center border-b-2 border-blue-500 font-medium text-blue-600" onclick="showTab('description')"> | |
<%= if @locale == "ar" do %>الوصف<% else %>Description<% end %> | |
</button> | |
<button class="tab-button py-4 px-6 text-center border-b-2 border-transparent font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300" onclick="showTab('specifications')"> | |
<%= if @locale == "ar" do %>المواصفات<% else %>Specifications<% end %> | |
</button> | |
<button class="tab-button py-4 px-6 text-center border-b-2 border-transparent font-medium text-gray-500 hover:text-gray-700 hover:border-gray-300" onclick="showTab('reviews')"> | |
<%= if @locale == "ar" do %>التقييمات<% else %>Reviews<% end %> | |
</button> | |
</nav> | |
</div> | |
<div class="p-6"> | |
<!-- Description Tab --> | |
<div id="description-tab" class="tab-content active-tab-content"> | |
<%= if @product["description"] do %> | |
<div class="prose prose-blue max-w-none"> | |
<%= raw(@product["description"]) %> | |
</div> | |
<% else %> | |
<p class="text-gray-500 italic"> | |
<%= if @locale == "ar" do %>الوصف غير متوفر.<% else %>No description available.<% end %> | |
</p> | |
<% end %> | |
</div> | |
<!-- Specifications Tab --> | |
<div id="specifications-tab" class="tab-content hidden"> | |
<%= if is_tire_product do %> | |
<!-- Tire Specifications Table --> | |
<div class="mb-6"> | |
<h3 class="text-lg font-medium text-gray-900 mb-4"> | |
<%= if @locale == "ar" do %>مواصفات الإطار<% else %>Tire Specifications<% end %> | |
</h3> | |
<div class="overflow-x-auto"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<tbody class="bg-white divide-y divide-gray-200"> | |
<!-- Tire Size --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>مقاس الإطار<% else %>Tire Size<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= if @product["tire_width"] && @product["tire_profile"] && @product["rim_size"] do %> | |
<%= @product["tire_width"] %>/<%= @product["tire_profile"] %>R<%= @product["rim_size"] %> | |
<% else %> | |
- | |
<% end %> | |
</td> | |
</tr> | |
<!-- Load Index --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>مؤشر الحمل<% else %>Load Index<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= @product["load_index"] || "-" %> | |
</td> | |
</tr> | |
<!-- Speed Rating --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>مؤشر السرعة<% else %>Speed Rating<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= @product["speed_rating"] || "-" %> | |
</td> | |
</tr> | |
<!-- Tire Type --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>نوع الإطار<% else %>Tire Type<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= if @locale == "ar" do %> | |
<%= case @product["tire_type"] do | |
346 -> "إطار صيفي" | |
347 -> "إطار شتوي" | |
348 -> "إطار لكل المواسم" | |
_ -> "-" | |
end %> | |
<% else %> | |
<%= case @product["tire_type"] do | |
346 -> "Summer Tire" | |
347 -> "Winter Tire" | |
348 -> "All Season Tire" | |
_ -> "-" | |
end %> | |
<% end %> | |
</td> | |
</tr> | |
<!-- Run Flat --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>إطار مقاوم للثقب<% else %>Run Flat<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= if @product["run_flat"] == 349 do %> | |
<%= if @locale == "ar" do %>نعم<% else %>Yes<% end %> | |
<% else %> | |
<%= if @locale == "ar" do %>لا<% else %>No<% end %> | |
<% end %> | |
</td> | |
</tr> | |
<!-- Year of Manufacture --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>سنة التصنيع<% else %>Year of Manufacture<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= @product["year_of_manufacture"] || "-" %> | |
</td> | |
</tr> | |
<!-- Country of Origin --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>بلد المنشأ<% else %>Country of Origin<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= if @locale == "ar" do %> | |
<%= case @product["origin_country"] do | |
350 -> "الصين" | |
351 -> "كوريا" | |
352 -> "اليابان" | |
353 -> "ألمانيا" | |
_ -> "-" | |
end %> | |
<% else %> | |
<%= case @product["origin_country"] do | |
350 -> "China" | |
351 -> "Korea" | |
352 -> "Japan" | |
353 -> "Germany" | |
_ -> "-" | |
end %> | |
<% end %> | |
</td> | |
</tr> | |
<!-- Warranty --> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50"> | |
<%= if @locale == "ar" do %>الضمان<% else %>Warranty<% end %> | |
</td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"> | |
<%= if @locale == "ar" do %> | |
<%= case @product["warranty"] do | |
354 -> "12 شهر" | |
355 -> "24 شهر" | |
356 -> "36 شهر" | |
_ -> "-" | |
end %> | |
<% else %> | |
<%= case @product["warranty"] do | |
354 -> "12 Months" | |
355 -> "24 Months" | |
356 -> "36 Months" | |
_ -> "-" | |
end %> | |
<% end %> | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
</div> | |
</div> | |
<% end %> | |
<%= if @product["attributes"] && is_list(@product["attributes"]) && length(@product["attributes"]) > 0 do %> | |
<div class="overflow-hidden"> | |
<table class="min-w-full divide-y divide-gray-200"> | |
<tbody class="bg-white divide-y divide-gray-200"> | |
<%= for attribute <- @product["attributes"] do %> | |
<tr> | |
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900 bg-gray-50 w-1/4"><%= attribute["name"] %></td> | |
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><%= attribute["value"] %></td> | |
</tr> | |
<% end %> | |
</tbody> | |
</table> | |
</div> | |
<% else %> | |
<%= if !is_tire_product do %> | |
<p class="text-gray-500 italic"> | |
<%= if @locale == "ar" do %>المواصفات غير متوفرة.<% else %>No specifications available.<% end %> | |
</p> | |
<% end %> | |
<% end %> | |
</div> | |
<!-- Reviews Tab --> | |
<div id="reviews-tab" class="tab-content hidden"> | |
<p class="text-gray-500 italic"> | |
<%= if @locale == "ar" do %> | |
لا توجد مراجعات حتى الآن. كن أول من يراجع هذا المنتج. | |
<% else %> | |
No reviews yet. Be the first to review this product. | |
<% end %> | |
</p> | |
<!-- Review form would go here --> | |
</div> | |
</div> | |
</div> | |
<!-- Related Products --> | |
<%= if @related_products && is_list(@related_products) && length(@related_products) > 0 do %> | |
<div class="mb-8"> | |
<h3 class="text-2xl font-bold mb-6"> <%= if @locale == "ar" do %> منتجات ذات صلة <% else %> Related Products <% end %> </h3> | |
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6"> | |
<%= for product <- @related_products do %> | |
<div class="bg-white rounded-lg overflow-hidden shadow hover:shadow-md transition-shadow duration-300 relative"> | |
<!-- In Stock Badge --> | |
<div class="absolute top-2 right-2 z-10"> | |
<span class="inline-flex items-center px-2 py-1 rounded-full text-xs font-medium bg-green-100 text-green-800"> | |
<span class="h-1.5 w-1.5 rounded-full bg-green-500 mr-1"></span> | |
<%= if @locale == "ar" do %>متوفر<% else %>In Stock<% end %> | |
</span> | |
</div> | |
<a href={~p"/#{@locale}/products/#{get_product_slug.(product)}"}> | |
<div class="h-48 bg-gray-100 flex items-center justify-center relative"> | |
<%= if product["base_image"] do %> | |
<img src={product["base_image"]["small_image_url"]} alt={product["name"]} class="max-h-48 object-contain" /> | |
<% else %> | |
<div class="text-gray-300"> | |
<svg xmlns="http://www.w3.org/2000/svg" class="h-16 w-16" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" /> | |
</svg> | |
</div> | |
<% end %> | |
<!-- Modern Discount Badge --> | |
<%= if product["special_price"] && product["price"] && | |
((is_binary(product["special_price"]) && is_binary(product["price"]) && | |
(try do String.to_float(product["special_price"]) < String.to_float(product["price"]) rescue _ -> false end)) || | |
(is_number(product["special_price"]) && is_number(product["price"]) && product["special_price"] < product["price"])) do %> | |
<div class="discount-tag"> | |
<span class="discount-text"> | |
<%= if product["percentage"] do %> | |
-<%= product["percentage"] %> | |
<% else %> | |
<%= if @locale == "ar" do %>خصم<% else %>SALE<% end %> | |
<% end %> | |
</span> | |
</div> | |
<% end %> | |
</div> | |
</a> | |
<div class="p-4"> | |
<h3 class="text-lg font-medium text-gray-800"> | |
<a href={~p"/#{@locale}/products/#{get_product_slug.(product)}"} class="hover:text-blue-600"> | |
<%= product["name"] %> | |
</a> | |
</h3> | |
<!-- Enhanced Price Display --> | |
<div class="mt-1"> | |
<%= cond do %> | |
<% product["formatted_special_price"] && product["formatted_price"] && product["special_price"] && | |
(is_number(product["special_price"]) && product["special_price"] > 0 || | |
is_binary(product["special_price"]) && String.trim(product["special_price"]) != "0" && String.trim(product["special_price"]) != "0.0000") -> %> | |
<span class="text-red-600 font-medium"><%= product["formatted_special_price"] %></span> | |
<span class="ml-2 text-gray-500 line-through text-sm"><%= product["formatted_price"] %></span> | |
<% product["formated_special_price"] && product["formated_price"] && product["special_price"] && | |
(is_number(product["special_price"]) && product["special_price"] > 0 || | |
is_binary(product["special_price"]) && String.trim(product["special_price"]) != "0" && String.trim(product["special_price"]) != "0.0000") -> %> | |
<span class="text-red-600 font-medium"><%= product["formated_special_price"] %></span> | |
<span class="ml-2 text-gray-500 line-through text-sm"><%= product["formated_price"] %></span> | |
<% product["special_price"] && product["price"] && | |
((is_number(product["special_price"]) && product["special_price"] > 0) || | |
(is_binary(product["special_price"]) && String.trim(product["special_price"]) != "0" && String.trim(product["special_price"]) != "0.0000")) && | |
is_binary(product["special_price"]) && is_binary(product["price"]) && | |
(try do String.to_float(product["special_price"]) < String.to_float(product["price"]) rescue _ -> false end) -> %> | |
<span class="text-red-600 font-medium"> | |
<%= if @locale == "ar" do %>د.ك<% else %>KD<% end %> | |
<%= try do Float.round(String.to_float(product["special_price"]), 3) rescue _ -> product["special_price"] end %> | |
</span> | |
<span class="ml-2 text-gray-500 line-through text-sm"> | |
<%= if @locale == "ar" do %>د.ك<% else %>KD<% end %> | |
<%= try do Float.round(String.to_float(product["price"]), 3) rescue _ -> product["price"] end %> | |
</span> | |
<% product["formatted_price"] -> %> | |
<span class="text-gray-800 font-medium"><%= product["formatted_price"] %></span> | |
<% product["formated_price"] -> %> | |
<span class="text-gray-800 font-medium"><%= product["formated_price"] %></span> | |
<% product["price"] -> %> | |
<span class="text-gray-800 font-medium"> | |
<%= if @locale == "ar" do %>د.ك<% else %>KD<% end %> | |
<%= try do Float.round(String.to_float(product["price"]), 3) rescue _ -> product["price"] end %> | |
</span> | |
<% true -> %> | |
<span class="text-gray-500 text-sm"> | |
<%= if @locale == "ar" do %>السعر غير متوفر<% else %>Price not available<% end %> | |
</span> | |
<% end %> | |
</div> | |
<!-- Add to | |
<div class="mt-3"> | |
<button | |
class="w-full bg-blue-600 hover:bg-blue-700 text-white py-1.5 px-3 rounded-md text-sm font-medium" | |
onclick={"addToCart('#{product["id"]}', 1, event)"} | |
> | |
<%= if @locale == "ar" do %>أضف للسلة<% else %>Add to Cart<% end %> | |
</button> | |
</div>Cart Button --> | |
</div> | |
</div> | |
<% end %> | |
</div> | |
</div> | |
<% end %> | |
</div> | |
<!-- Add the necessary JavaScript for the tire display and tab functionality --> | |
<script> | |
// Tab switching functionality | |
function showTab(tabName) { | |
// Hide all tabs | |
document.querySelectorAll('.tab-content').forEach(tab => { | |
tab.classList.add('hidden'); | |
tab.classList.remove('active-tab-content'); | |
}); | |
// Remove active class from all tab buttons | |
document.querySelectorAll('.tab-button').forEach(button => { | |
button.classList.remove('active-tab'); | |
button.classList.remove('text-blue-600'); | |
button.classList.remove('border-blue-500'); | |
button.classList.add('text-gray-500'); | |
button.classList.add('border-transparent'); | |
}); | |
// Show selected tab | |
const selectedTab = document.getElementById(tabName + '-tab'); | |
if (selectedTab) { | |
selectedTab.classList.remove('hidden'); | |
selectedTab.classList.add('active-tab-content'); | |
} | |
// Highlight selected tab button | |
const buttons = document.querySelectorAll('.tab-button'); | |
buttons.forEach(button => { | |
if (button.textContent.trim().toLowerCase().includes(tabName.toLowerCase()) || | |
button.onclick.toString().includes(`showTab('${tabName}')`)) { | |
button.classList.add('active-tab'); | |
button.classList.add('text-blue-600'); | |
button.classList.add('border-blue-500'); | |
button.classList.remove('text-gray-500'); | |
button.classList.remove('border-transparent'); | |
} | |
}); | |
} | |
// Image gallery functionality | |
function showImage(url, alt) { | |
const mainImage = document.querySelector('#main-image img'); | |
if (mainImage) { | |
mainImage.src = url; | |
mainImage.alt = alt; | |
} | |
} | |
// Quantity controls | |
function incrementQuantity() { | |
const quantityInput = document.getElementById('quantity'); | |
if (quantityInput) { | |
quantityInput.value = parseInt(quantityInput.value) + 1; | |
} | |
} | |
function decrementQuantity() { | |
const quantityInput = document.getElementById('quantity'); | |
if (quantityInput && parseInt(quantityInput.value) > 1) { | |
quantityInput.value = parseInt(quantityInput.value) - 1; | |
} | |
} | |
// Add to cart functionality | |
function addToCart() { | |
const productId = "<%= @product["id"] %>"; | |
const quantity = parseInt(document.getElementById('quantity').value); | |
fetch(`/${window.locale || '<%= @locale %>'}/cart/add`, { | |
method: 'POST', | |
headers: { | |
'Content-Type': 'application/json', | |
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') | |
}, | |
body: JSON.stringify({ | |
product_id: productId, | |
quantity: quantity | |
}) | |
}) | |
.then(response => response.json()) | |
.then(data => { | |
if (data.success || (data.data && data.data.id)) { | |
// Show the notification | |
const notificationContainer = document.getElementById('notification-container'); | |
const notificationMessage = document.getElementById('notification-message'); | |
const notificationIcon = document.getElementById('notification-icon'); | |
notificationMessage.textContent = window.locale === 'ar' ? | |
'تمت إضافة المنتج إلى سلة التسوق بنجاح!' : | |
'Product added to cart successfully!'; | |
notificationIcon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-green-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" /></svg>'; | |
notificationContainer.classList.remove('translate-y-[-100%]'); | |
notificationContainer.classList.add('translate-y-0'); | |
// Auto-hide the notification after 3 seconds | |
setTimeout(() => { | |
notificationContainer.classList.remove('translate-y-0'); | |
notificationContainer.classList.add('translate-y-[-100%]'); | |
}, 3000); | |
// Update cart count in header (if exists) | |
const cartCountElement = document.getElementById('cart-count'); | |
if (cartCountElement && data.data && data.data.items_count) { | |
cartCountElement.textContent = data.data.items_count; | |
// Add a small animation to the cart count | |
cartCountElement.classList.add('animate-bounce'); | |
setTimeout(() => { | |
cartCountElement.classList.remove('animate-bounce'); | |
}, 1000); | |
} | |
// Update cart total in header (if exists) | |
const cartTotalElement = document.getElementById('cart-total'); | |
if (cartTotalElement && data.data && data.data.formated_grand_total) { | |
cartTotalElement.textContent = data.data.formated_grand_total; | |
} | |
} else { | |
// Show error notification | |
const notificationContainer = document.getElementById('notification-container'); | |
const notificationMessage = document.getElementById('notification-message'); | |
const notificationIcon = document.getElementById('notification-icon'); | |
notificationMessage.textContent = data.message || | |
(window.locale === 'ar' ? 'فشل إضافة المنتج للسلة' : 'Failed to add product to cart'); | |
notificationIcon.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-red-500" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /></svg>'; | |
notificationContainer.classList.remove('translate-y-[-100%]'); | |
notificationContainer.classList.add('translate-y-0'); | |
setTimeout(() => { | |
notificationContainer.classList.remove('translate-y-0'); | |
notificationContainer.classList.add('translate-y-[-100%]'); | |
}, 3000); | |
} | |
}) | |
.catch(error => { | |
console.error('Error adding to cart:', error); | |
alert('An error occurred. Please try again.'); | |
}); | |
} | |
// Function for sticky add to cart button | |
function addToCartSticky() { | |
addToCart(); | |
} | |
// Close notification button | |
document.getElementById('notification-close').addEventListener('click', function() { | |
const notificationContainer = document.getElementById('notification-container'); | |
notificationContainer.classList.remove('translate-y-0'); | |
notificationContainer.classList.add('translate-y-[-100%]'); | |
}); | |
// Sticky add to cart for mobile | |
window.addEventListener('scroll', function() { | |
const stickyAddToCart = document.getElementById('sticky-add-to-cart'); | |
const addToCartForm = document.getElementById('add-to-cart-form'); | |
if (stickyAddToCart && addToCartForm) { | |
const formPosition = addToCartForm.getBoundingClientRect().top; | |
if (formPosition < 0) { | |
stickyAddToCart.classList.remove('hidden', '-translate-y-full'); | |
stickyAddToCart.classList.add('translate-y-0'); | |
} else { | |
stickyAddToCart.classList.remove('translate-y-0'); | |
stickyAddToCart.classList.add('-translate-y-full'); | |
} | |
} | |
}); | |
// Make locale available to JS functions | |
window.locale = '<%= @locale %>'; | |
// Tire specs toggle functionality | |
function toggleTireSpecs() { | |
const content = document.getElementById('tire-specs-content'); | |
const chevron = document.getElementById('tire-specs-chevron'); | |
const toggleIcon = document.querySelector('#tire-specs-toggle svg:first-child'); | |
if (content.classList.contains('hidden')) { | |
// Open the specs | |
content.classList.remove('hidden'); | |
content.style.maxHeight = content.scrollHeight + 'px'; | |
chevron.classList.add('rotate-180'); | |
toggleIcon.innerHTML = ` | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20 12H4" /> | |
`; | |
} else { | |
// Close the specs | |
content.style.maxHeight = '0'; | |
setTimeout(() => content.classList.add('hidden'), 300); | |
chevron.classList.remove('rotate-180'); | |
toggleIcon.innerHTML = ` | |
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6v6m0 0v6m0-6h6m-6 0H6" /> | |
`; | |
} | |
} | |
// Initialize tire specs on page load | |
document.addEventListener('DOMContentLoaded', function() { | |
const tireSpecsToggle = document.getElementById('tire-specs-toggle'); | |
if (tireSpecsToggle) { | |
// Optional: Auto-expand tire specs on page load | |
// Uncomment the next line to have tire specs open by default | |
// toggleTireSpecs(); | |
} | |
}); | |
</script> | |
<style> | |
/* Tire specification styles */ | |
.tire-spec-icon { | |
width: 24px; | |
height: 24px; | |
margin-right: 0.5rem; | |
} | |
/* Discount tag styling */ | |
.discount-tag { | |
position: absolute; | |
top: 0; | |
left: 0; | |
background-color: #ef4444; | |
color: white; | |
padding: 0.25rem 0.75rem; | |
margin: 0.5rem; | |
border-radius: 0.375rem; | |
font-weight: 600; | |
font-size: 0.875rem; | |
} | |
/* Tab content transitions */ | |
.tab-content { | |
transition: opacity 0.3s ease; | |
} | |
.active-tab-content { | |
opacity: 1; | |
} | |
/* Additional responsive fixes */ | |
@media (max-width: 640px) { | |
.tire-specs-grid { | |
grid-template-columns: 1fr; | |
} | |
} | |
/* Tire specs collapsible styling */ | |
.tire-specs-content { | |
max-height: 0; | |
overflow: hidden; | |
transition: max-height 0.3s ease; | |
} | |
.tire-specs-collapsible { | |
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); | |
transition: box-shadow 0.3s ease; | |
} | |
.tire-specs-collapsible:hover { | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
} | |
.tire-specs-card { | |
display: flex; | |
flex-wrap: wrap; | |
margin: -0.5rem; | |
} | |
.tire-spec-box { | |
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05); | |
transition: all 0.3s ease; | |
} | |
.tire-spec-box:hover { | |
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
transform: translateY(-2px); | |
} | |
@media (max-width: 640px) { | |
.tire-spec-box { | |
min-width: calc(50% - 1rem); | |
} | |
} | |
</style> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment