Browse Source

Добавлен раздел 'Особенности FDM печати' на основе файла 3d_printing_nuances.txt

- Создан компонент PrintingNuancesSection.vue с карточками для каждой особенности
- Добавлены переводы для всех поддерживаемых языков (en, me, ru, ua)
- Компонент интегрирован на главную страницу после раздела Services
- Использован стиль карточек card-apple в соответствии с дизайном сайта
- Добавлены иконки lucide-vue-next для визуального оформления
- Включено важное примечание о том, что эти особенности являются нормой технологии
unknown 1 week ago
parent
commit
45f1377e84

+ 18 - 0
.continue/agents/deepseek.yaml

@@ -0,0 +1,18 @@
+# This is an example configuration file
+# To learn more, see the full config.yaml reference: https://docs.continue.dev/reference
+
+name: deepseek
+version: 1.0.0
+schema: v1
+
+# Define which models can be used
+# https://docs.continue.dev/customization/models
+models:
+  - name: deepseek
+    provider: deepseek
+    model: deepseek-chat
+    apiKey: sk-c0354e03d88446cc950bee887d55774e
+
+# MCP Servers that Continue can access
+# https://docs.continue.dev/customization/mcp-tools
+

+ 14 - 0
backend/mytest_gen_uplat.py

@@ -0,0 +1,14 @@
+import os
+from services.uplatnica_generator import generate_uplatnica2
+
+# Set up test data
+order_id = 1234567890
+payer_name = "John Doe"
+payer_address = "123 Main St, Anytown USA"
+amount = 101.00
+
+# Generate the invoice PDF
+pdf_path = generate_uplatnica2(order_id, payer_name, payer_address, amount)
+
+# Test that the PDF file is created and saved to the correct location
+assert os.path.exists(pdf_path)

+ 27 - 0
backend/services/3d_printing_nuances.txt

@@ -0,0 +1,27 @@
+
+Изделия, изготовленные методом FDM (послойного наплавления пластика), имеют ряд визуальных и тактильных особенностей, которые являются нормой технологии и не считаются дефектами.
+
+**1. Слоистая структура поверхности**
+Поверхность изделий может иметь заметные линии слоёв. Это естественный результат послойного построения модели.
+
+**2. Небольшие неровности и текстура**
+Допускается лёгкая шероховатость, микронаплывы или незначительные артефакты, особенно на сложной геометрии.
+
+**3. Следы от поддержек**
+В местах, где использовались поддерживающие структуры, могут оставаться небольшие следы или отличия в текстуре поверхности.
+
+**4. Швы (Z-seam)**
+Вертикальная линия или точка, где начинается и заканчивается каждый слой, может быть заметна на поверхности модели.
+
+5. Особенности нависающих элементов
+При печати нависаний (участков без опоры снизу) возможно небольшое провисание, изменение геометрии или менее аккуратная поверхность. Это ограничение технологии и не считается дефектом в разумных пределах.
+
+6. Лёгкие отклонения размеров
+Из-за свойств материалов и особенностей охлаждения возможны небольшие допуски в размерах.
+
+7. Различия в оттенке
+Даже в рамках одного цвета возможны незначительные вариации оттенка или блеска поверхности.
+
+8. Мелкие ниточки (stringing)
+Тонкие пластиковые нити между элементами модели могут иногда присутствовать и легко удаляются.
+Эти особенности обусловлены самой природой технологии FDM-печати и не влияют на функциональность изделия, если иное не оговорено отдельно.

+ 124 - 115
backend/services/uplatnica_generator.py

@@ -2,124 +2,133 @@ import os
 from fpdf import FPDF
 import config
 
-def generate_uplatnica(order_id: int, payer_name: str, payer_address: str, amount: float) -> str:
-    """
-    Generate a PDF 'Nalog za uplatu' (Uplatnica) for Montenegro.
-    Dimensions: 210x99 mm.
-    """
-    pdf = FPDF(orientation='L', unit='mm', format=(99, 210))
-    pdf.set_margins(10, 8, 10)
+from fpdf import FPDF
+import os
+import config
+
+
+def format_amount(amount):
+    return f"{amount:.2f}".replace(".", ",")
+
+
+def generate_uplatnica(order_id, payer_name, payer_address, amount):
+    pdf = FPDF(orientation='P', unit='mm', format='A4')
+    pdf.set_auto_page_break(False)
     pdf.add_page()
-    
-    # Fonts
-    pdf.set_font("helvetica", 'B', 12)
-    
-    # Outer border (optional but helps when printing on A4)
-    pdf.rect(5, 5, 200, 89)
-    
-    # Title
-    pdf.set_font("helvetica", 'B', 11)
-    pdf.cell(0, 5, "NALOG ZA UPLATU", align="C", new_x="LMARGIN", new_y="NEXT")
-    pdf.ln(5)
-    
-    # Left Block (UPLATILAC, SVRHA PLACANJA, PRIMALAC)
-    pdf.set_font("helvetica", 'B', 7)
-    pdf.set_xy(10, 20)
-    pdf.cell(85, 4, "uplatilac")
-    
-    pdf.set_font("helvetica", '', 10)
-    pdf.set_xy(10, 24)
-    pdf.rect(10, 24, 85, 14)
-    pdf.multi_cell(85, 5, f"{payer_name}\n{payer_address}")
-    
-    pdf.set_font("helvetica", 'B', 7)
-    pdf.set_xy(10, 40)
-    pdf.cell(85, 4, "svrha placanja")
-    
-    pdf.set_font("helvetica", '', 10)
-    pdf.set_xy(10, 44)
-    pdf.rect(10, 44, 85, 14)
-    pdf.multi_cell(85, 5, f"Usluge 3D stampe - Narudzba #{order_id}")
-    
-    pdf.set_font("helvetica", 'B', 7)
-    pdf.set_xy(10, 60)
-    pdf.cell(85, 4, "primalac")
-    
-    pdf.set_font("helvetica", '', 10)
-    pdf.set_xy(10, 64)
-    pdf.rect(10, 64, 85, 14)
-    pdf.multi_cell(85, 6, config.COMPANY_NAME)
-    
-    # Right Block
-    # Sifra, Valuta, Iznos
-    pdf.set_font("helvetica", 'B', 7)
-    pdf.set_xy(105, 20)
-    pdf.cell(15, 4, "sifra", align="C")
-    pdf.set_xy(125, 20)
-    pdf.cell(15, 4, "valuta", align="C")
-    pdf.set_xy(150, 20)
-    pdf.cell(45, 4, "iznos", align="C")
-    
-    pdf.set_font("helvetica", 'B', 10)
-    pdf.rect(105, 24, 15, 7)
-    pdf.rect(125, 24, 15, 7)
-    pdf.rect(145, 24, 50, 7)
-    
-    pdf.set_xy(105, 25)
-    pdf.cell(15, 5, "00", align="C") # Sifra placanja
-    pdf.set_xy(125, 25)
-    pdf.cell(15, 5, "EUR", align="C")
-    pdf.set_xy(145, 25)
-    pdf.cell(50, 5, f"= {amount:.2f}", align="R")
-    
-    # Racun posiljaoca (left empty usually for cash deposits)
-    pdf.set_font("helvetica", 'B', 7)
-    pdf.set_xy(105, 34)
-    pdf.cell(60, 4, "racun posiljaoca")
-    pdf.rect(105, 38, 90, 7)
-    
-    # Racun primaoca
-    pdf.set_xy(105, 48)
-    pdf.cell(60, 4, "racun primaoca")
-    pdf.rect(105, 52, 90, 7)
-    pdf.set_font("helvetica", 'B', 11)
-    pdf.set_xy(105, 52)
-    pdf.cell(90, 7, config.ZIRO_RACUN, align="C")
-    
-    # Model and Poziv na broj odobrenja
-    pdf.set_font("helvetica", 'B', 7)
-    pdf.set_xy(105, 62)
-    pdf.cell(15, 4, "model")
-    pdf.set_xy(125, 62)
-    pdf.cell(70, 4, "poziv na broj (odobrenja)")
-    
-    pdf.rect(105, 66, 15, 7)
-    pdf.rect(125, 66, 70, 7)
-    
-    pdf.set_font("helvetica", 'B', 10)
-    pdf.set_xy(105, 67)
-    pdf.cell(15, 5, "00", align="C")
-    pdf.set_xy(125, 67)
-    pdf.cell(70, 5, str(order_id), align="C")
-    
-    # Signature
-    pdf.set_font("helvetica", 'B', 7)
-    pdf.set_xy(10, 84)
-    pdf.cell(40, 4, "potpis uplatioca", align="C")
-    pdf.line(10, 84, 50, 84)
-    
-    # Stamp & signature recipient
-    pdf.set_xy(155, 84)
-    pdf.cell(40, 4, "potpis i pecat primaoca", align="C")
-    pdf.line(155, 84, 195, 84)
-    
-    # Save file
+
+    pdf.set_font("helvetica", size=7)
+
+    # === UPLATNICA AREA (top of A4) ===
+    pdf.rect(0, 0, 210, 99)
+
+    # Vertical divider
+    pdf.line(105, 0, 105, 99)
+
+    # ===== TOP OPTIONS =====
+    labels = ["Hitnost", "Prenos", "Uplata", "Isplata"]
+    start_x = 110
+    step = 22
+
+    for i, label in enumerate(labels):
+        x = start_x + i * step
+        pdf.set_xy(x, 2)
+        pdf.cell(14, 4, label)
+
+        pdf.rect(x + 14, 2, 4, 4)
+
+        if label == "Uplata":
+            pdf.line(x + 14, 2, x + 18, 6)
+            pdf.line(x + 18, 2, x + 14, 6)
+
+    # ===== LEFT =====
+    pdf.set_font("helvetica", "B", 8)
+    pdf.set_xy(5, 5)
+    pdf.cell(95, 5, "NALOG PLATIOCA", align="C")
+
+    pdf.rect(5, 12, 95, 14)
+    pdf.set_font("helvetica", size=9)
+    pdf.set_xy(7, 14)
+    pdf.cell(90, 4, payer_name[:40])
+    pdf.set_xy(7, 18)
+    pdf.cell(90, 4, payer_address[:40])
+
+    pdf.set_font("helvetica", size=6)
+    pdf.set_xy(5, 26)
+    pdf.cell(95, 4, "(Naziv platioca)", align="C")
+
+    pdf.rect(5, 30, 95, 14)
+    pdf.set_font("helvetica", size=9)
+    pdf.set_xy(7, 32)
+    pdf.cell(90, 4, "Usluge 3D stampe")
+    pdf.set_xy(7, 36)
+    pdf.cell(90, 4, f"Narudzba {order_id}")
+
+    pdf.set_font("helvetica", size=6)
+    pdf.set_xy(5, 44)
+    pdf.cell(95, 4, "(Svrha placanja)", align="C")
+
+    pdf.rect(5, 48, 95, 14)
+    pdf.set_font("helvetica", size=9)
+    pdf.set_xy(7, 50)
+    pdf.cell(90, 4, config.COMPANY_NAME[:40])
+
+    pdf.set_font("helvetica", size=6)
+    pdf.set_xy(5, 62)
+    pdf.cell(95, 4, "(Naziv primaoca)", align="C")
+
+    pdf.line(5, 90, 100, 90)
+    pdf.set_xy(5, 90)
+    pdf.cell(95, 4, "(Potpis platioca)", align="C")
+
+    # ===== RIGHT =====
+    pdf.rect(110, 12, 95, 8)
+
+    pdf.set_font("helvetica", size=7)
+    pdf.set_xy(110, 22)
+    pdf.cell(10, 4, "EUR")
+
+    pdf.rect(120, 22, 50, 10)
+    pdf.rect(175, 22, 30, 10)
+
+    pdf.set_font("helvetica", "B", 11)
+    pdf.set_xy(120, 24)
+    pdf.cell(50, 6, format_amount(amount), align="C")
+
+    pdf.set_xy(175, 24)
+    pdf.cell(30, 6, "121", align="C")
+
+    pdf.set_font("helvetica", size=6)
+    pdf.set_xy(120, 32)
+    pdf.cell(50, 4, "(Iznos)", align="C")
+
+    pdf.set_xy(175, 32)
+    pdf.cell(30, 4, "(Sifra)", align="C")
+
+    pdf.rect(110, 36, 95, 10)
+    pdf.set_font("helvetica", size=10)
+    pdf.set_xy(110, 38)
+    pdf.cell(95, 6, config.ZIRO_RACUN, align="C")
+
+    pdf.rect(110, 50, 20, 8)
+    pdf.rect(135, 50, 70, 8)
+
+    pdf.set_xy(110, 52)
+    pdf.cell(20, 4, "00", align="C")
+    pdf.set_xy(135, 52)
+    pdf.cell(70, 4, str(order_id), align="C")
+
+    pdf.line(110, 90, 205, 90)
+    pdf.set_xy(110, 90)
+    pdf.cell(95, 4, "(Potpis primaoca)", align="C")
+
+    # ===== SAVE =====
+
+
     pdf_dir = os.path.join(config.UPLOAD_DIR, "invoices")
-    if not os.path.exists(pdf_dir):
-        os.makedirs(pdf_dir)
-        
+    os.makedirs(pdf_dir, exist_ok=True)
+
     filename = f"uplatnica_order_{order_id}.pdf"
     filepath = os.path.join(pdf_dir, filename)
     pdf.output(filepath)
-    
+
     return os.path.join("uploads", "invoices", filename).replace("\\", "/")

+ 1 - 1
src/components/Footer.vue

@@ -11,7 +11,7 @@
               <Mail class="w-3.5 h-3.5" />hello@radionica3d.com
             </a>
             <div class="flex items-center gap-2 text-xs font-bold text-foreground/40">
-              <MapPin class="w-3.5 h-3.5" />Podgorica, Montenegro
+              <MapPin class="w-3.5 h-3.5" />Herceg Novi, Montenegro
             </div>
           </div>
         </div>

+ 113 - 0
src/components/PrintingNuancesSection.vue

@@ -0,0 +1,113 @@
+<template>
+  <section id="nuances" class="py-12 sm:py-24 relative overflow-hidden bg-white">
+    <div class="container mx-auto px-4">
+      <div class="text-center mb-10 sm:mb-16 animate-slide-up">
+        <span class="text-primary font-display text-sm tracking-widest uppercase">{{ t("nuances.subtitle") }}</span>
+        <h2 class="font-display text-3xl sm:text-4xl lg:text-5xl font-bold mt-4 mb-6">
+          {{ t("nuances.title") }}
+        </h2>
+        <p class="text-muted-foreground max-w-2xl mx-auto">{{ t("nuances.description") }}</p>
+      </div>
+
+      <div class="grid md:grid-cols-2 lg:grid-cols-3 gap-6">
+        <div v-for="(nuance, index) in nuances" :key="index" class="group">
+          <div class="card-apple h-full p-6 hover:translate-y-[-4px] transition-all duration-500">
+            <div class="flex items-start gap-4 mb-4">
+              <div class="w-12 h-12 bg-primary/10 rounded-xl flex items-center justify-center flex-shrink-0 group-hover:bg-primary/20 transition-colors">
+                <component :is="nuance.icon" class="w-6 h-6 text-primary" />
+              </div>
+              <div>
+                <h3 class="font-display text-lg font-semibold mb-2">{{ nuance.title }}</h3>
+                <p class="text-sm text-muted-foreground leading-relaxed">{{ nuance.description }}</p>
+              </div>
+            </div>
+            
+            <div v-if="nuance.details" class="mt-4 pt-4 border-t border-border/30">
+              <p class="text-xs text-muted-foreground/80 leading-relaxed">{{ nuance.details }}</p>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- Disclaimer -->
+      <div class="mt-12 sm:mt-16 p-6 bg-secondary/30 rounded-2xl border border-border/50 animate-fade-in">
+        <div class="flex items-start gap-4">
+          <div class="w-10 h-10 bg-primary/10 rounded-lg flex items-center justify-center flex-shrink-0">
+            <Info class="w-5 h-5 text-primary" />
+          </div>
+          <div>
+            <h4 class="font-display font-semibold mb-2">{{ t("nuances.disclaimer.title") }}</h4>
+            <p class="text-sm text-muted-foreground">{{ t("nuances.disclaimer.text") }}</p>
+          </div>
+        </div>
+      </div>
+    </div>
+  </section>
+</template>
+
+<script setup lang="ts">
+import { useI18n } from "vue-i18n";
+import { 
+  Layers, 
+  FileWarning, 
+  GitCompare, 
+  Zap, 
+  Ruler, 
+  Palette,
+  Droplets,
+  Info
+} from "lucide-vue-next";
+
+const { t } = useI18n();
+
+const nuances = [
+  {
+    icon: Layers,
+    title: t("nuances.items.layerStructure.title"),
+    description: t("nuances.items.layerStructure.description"),
+    details: t("nuances.items.layerStructure.details")
+  },
+  {
+    icon: FileWarning,
+    title: t("nuances.items.surfaceImperfections.title"),
+    description: t("nuances.items.surfaceImperfections.description"),
+    details: t("nuances.items.surfaceImperfections.details")
+  },
+  {
+    icon: GitCompare,
+    title: t("nuances.items.supportMarks.title"),
+    description: t("nuances.items.supportMarks.description"),
+    details: t("nuances.items.supportMarks.details")
+  },
+  {
+    icon: Zap,
+    title: t("nuances.items.zSeam.title"),
+    description: t("nuances.items.zSeam.description"),
+    details: t("nuances.items.zSeam.details")
+  },
+  {
+    icon: Ruler,
+    title: t("nuances.items.overhangs.title"),
+    description: t("nuances.items.overhangs.description"),
+    details: t("nuances.items.overhangs.details")
+  },
+  {
+    icon: Palette,
+    title: t("nuances.items.dimensionalTolerances.title"),
+    description: t("nuances.items.dimensionalTolerances.description"),
+    details: t("nuances.items.dimensionalTolerances.details")
+  },
+  {
+    icon: Droplets,
+    title: t("nuances.items.colorVariations.title"),
+    description: t("nuances.items.colorVariations.description"),
+    details: t("nuances.items.colorVariations.details")
+  },
+  {
+    icon: Zap,
+    title: t("nuances.items.stringing.title"),
+    description: t("nuances.items.stringing.description"),
+    details: t("nuances.items.stringing.details")
+  }
+];
+</script>

+ 2 - 0
src/pages/Index.vue

@@ -4,6 +4,7 @@
     <main>
       <HeroSection />
       <ServicesSection />
+      <PrintingNuancesSection />
       <ModelUploadSection />
       <QuotingSection />
       <ProcessSection />
@@ -17,6 +18,7 @@
 import Header from "@/components/Header.vue";
 import HeroSection from "@/components/HeroSection.vue";
 import ServicesSection from "@/components/ServicesSection.vue";
+import PrintingNuancesSection from "@/components/PrintingNuancesSection.vue";
 import ModelUploadSection from "@/components/ModelUploadSection.vue";
 import QuotingSection from "@/components/QuotingSection.vue";
 import ProcessSection from "@/components/ProcessSection.vue";