|
@@ -1,32 +1,30 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <section id="upload" class="py-24 bg-background relative">
|
|
|
|
|
|
|
+ <section id="upload" class="py-20 bg-white relative">
|
|
|
<div class="absolute inset-0 bg-gradient-glow opacity-50" />
|
|
<div class="absolute inset-0 bg-gradient-glow opacity-50" />
|
|
|
|
|
|
|
|
<div class="container mx-auto px-4 relative z-10">
|
|
<div class="container mx-auto px-4 relative z-10">
|
|
|
- <div class="text-center mb-16">
|
|
|
|
|
- <span class="text-primary font-display text-sm tracking-widest uppercase">{{ t("upload.badge") }}</span>
|
|
|
|
|
- <h2 class="font-display text-3xl sm:text-4xl lg:text-5xl font-bold mt-4 mb-6">
|
|
|
|
|
- {{ t("upload.title") }} <span class="text-gradient">{{ t("upload.titleGradient") }}</span>
|
|
|
|
|
|
|
+ <div class="text-center mb-12">
|
|
|
|
|
+ <span class="text-primary font-display text-xs font-bold tracking-[0.2em] uppercase">{{ t("upload.badge") }}</span>
|
|
|
|
|
+ <h2 class="font-display text-3xl sm:text-4xl lg:text-5xl font-extrabold mt-4 mb-4 tracking-tight">
|
|
|
|
|
+ {{ t("upload.title") }} <span class="text-primary">{{ t("upload.titleGradient") }}</span>
|
|
|
</h2>
|
|
</h2>
|
|
|
- <p class="text-muted-foreground max-w-2xl mx-auto leading-relaxed">{{ t("upload.description") }}</p>
|
|
|
|
|
|
|
+ <p class="text-foreground/60 max-w-xl mx-auto text-base font-medium">{{ t("upload.description") }}</p>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div class="max-w-3xl mx-auto space-y-8">
|
|
<div class="max-w-3xl mx-auto space-y-8">
|
|
|
<!-- Contact -->
|
|
<!-- Contact -->
|
|
|
<div class="grid sm:grid-cols-2 gap-6">
|
|
<div class="grid sm:grid-cols-2 gap-6">
|
|
|
- <div class="space-y-3">
|
|
|
|
|
- <label class="flex items-center gap-2 text-sm font-medium text-foreground ml-1">
|
|
|
|
|
- <User class="w-4 h-4 text-primary" />{{ t("upload.firstName") }} *
|
|
|
|
|
|
|
+ <label class="flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest text-foreground/40 ml-1">
|
|
|
|
|
+ {{ t("upload.firstName") }} *
|
|
|
</label>
|
|
</label>
|
|
|
<input v-model="firstName" type="text" required :placeholder="t('upload.firstName')"
|
|
<input v-model="firstName" type="text" required :placeholder="t('upload.firstName')"
|
|
|
- class="w-full bg-card/50 border border-border/50 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all backdrop-blur-sm" />
|
|
|
|
|
|
|
+ class="w-full bg-secondary/50 border border-black/[0.03] rounded-2xl px-4 py-2.5 focus:outline-none focus:ring-4 focus:ring-primary/10 focus:border-primary transition-all text-sm font-medium" />
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="space-y-3">
|
|
|
|
|
- <label class="flex items-center gap-2 text-sm font-medium text-foreground ml-1">
|
|
|
|
|
- <User class="w-4 h-4 text-primary" />{{ t("upload.lastName") }} *
|
|
|
|
|
|
|
+ <label class="flex items-center gap-2 text-[10px] font-bold uppercase tracking-widest text-foreground/40 ml-1">
|
|
|
|
|
+ {{ t("upload.lastName") }} *
|
|
|
</label>
|
|
</label>
|
|
|
<input v-model="lastName" type="text" required :placeholder="t('upload.lastName')"
|
|
<input v-model="lastName" type="text" required :placeholder="t('upload.lastName')"
|
|
|
- class="w-full bg-card/50 border border-border/50 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all backdrop-blur-sm" />
|
|
|
|
|
|
|
+ class="w-full bg-secondary/50 border border-black/[0.03] rounded-2xl px-4 py-2.5 focus:outline-none focus:ring-4 focus:ring-primary/10 focus:border-primary transition-all text-sm font-medium" />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -48,9 +46,9 @@
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Material Selection -->
|
|
<!-- Material Selection -->
|
|
|
- <div class="space-y-4 p-6 bg-card/30 rounded-2xl border border-border/50">
|
|
|
|
|
- <h3 class="text-sm font-semibold uppercase tracking-wider text-muted-foreground flex items-center gap-2">
|
|
|
|
|
- <FileBox class="w-4 h-4 text-primary" />{{ t("upload.selectMaterial") || "Select Material" }}
|
|
|
|
|
|
|
+ <div class="space-y-4 p-5 bg-secondary/30 rounded-[2rem] border border-black/[0.02]">
|
|
|
|
|
+ <h3 class="text-[10px] font-bold uppercase tracking-[0.2em] text-foreground/40 flex items-center gap-2 px-1">
|
|
|
|
|
+ {{ t("upload.selectMaterial") || "Select Material" }}
|
|
|
</h3>
|
|
</h3>
|
|
|
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
|
<div class="grid grid-cols-2 sm:grid-cols-3 gap-3">
|
|
|
<button
|
|
<button
|
|
@@ -59,14 +57,14 @@
|
|
|
type="button"
|
|
type="button"
|
|
|
@click="selectedMaterial = String(m.id)"
|
|
@click="selectedMaterial = String(m.id)"
|
|
|
:class="[
|
|
:class="[
|
|
|
- 'p-4 rounded-xl border text-left transition-all hover:scale-[1.02] active:scale-[0.98]',
|
|
|
|
|
|
|
+ 'p-4 rounded-2xl border text-left transition-all duration-300',
|
|
|
selectedMaterial === String(m.id)
|
|
selectedMaterial === String(m.id)
|
|
|
- ? 'bg-primary/20 border-primary shadow-glow ring-1 ring-primary/40'
|
|
|
|
|
- : 'bg-background/50 border-border/50 hover:border-primary/30'
|
|
|
|
|
|
|
+ ? 'bg-white border-primary shadow-lg scale-[1.02] ring-1 ring-primary/20'
|
|
|
|
|
+ : 'bg-white/50 border-black/[0.04] hover:border-primary/20 grayscale opacity-70 hover:opacity-100 hover:grayscale-0'
|
|
|
]"
|
|
]"
|
|
|
>
|
|
>
|
|
|
- <p class="font-bold text-sm mb-1">{{ m['name_' + locale] || m.name_en }}</p>
|
|
|
|
|
- <p class="text-[10px] text-muted-foreground leading-tight">{{ m['desc_' + locale] || m.desc_en }}</p>
|
|
|
|
|
|
|
+ <p class="font-extrabold text-xs mb-0.5 tracking-tight uppercase">{{ m['name_' + locale] || m.name_en }}</p>
|
|
|
|
|
+ <p class="text-[10px] text-foreground/40 leading-tight font-medium line-clamp-1">{{ m['desc_' + locale] || m.desc_en }}</p>
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -79,7 +77,7 @@
|
|
|
<LinkIcon class="w-4 h-4 text-primary" />{{ t("upload.modelLink") }}
|
|
<LinkIcon class="w-4 h-4 text-primary" />{{ t("upload.modelLink") }}
|
|
|
</label>
|
|
</label>
|
|
|
<input v-model="modelLink" type="url" :placeholder="t('upload.modelLinkPlaceholder')"
|
|
<input v-model="modelLink" type="url" :placeholder="t('upload.modelLinkPlaceholder')"
|
|
|
- class="w-full bg-card/50 border border-border/50 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all backdrop-blur-sm" />
|
|
|
|
|
|
|
+ class="w-full bg-secondary/50 border border-black/[0.03] rounded-2xl px-4 py-2.5 focus:outline-none focus:ring-4 focus:ring-primary/10 focus:border-primary transition-all text-sm font-medium" />
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
@@ -89,7 +87,7 @@
|
|
|
<FileText class="w-4 h-4 text-primary" />{{ t("upload.notes") }}
|
|
<FileText class="w-4 h-4 text-primary" />{{ t("upload.notes") }}
|
|
|
</label>
|
|
</label>
|
|
|
<textarea v-model="notes" :placeholder="t('upload.notesPlaceholder')" rows="2"
|
|
<textarea v-model="notes" :placeholder="t('upload.notesPlaceholder')" rows="2"
|
|
|
- class="w-full bg-card/50 border border-border/50 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all backdrop-blur-sm resize-none" />
|
|
|
|
|
|
|
+ class="w-full bg-secondary/50 border border-black/[0.03] rounded-2xl px-4 py-3 focus:outline-none focus:ring-4 focus:ring-primary/10 focus:border-primary transition-all text-sm font-medium resize-none" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- Drop Zone -->
|
|
<!-- Drop Zone -->
|
|
@@ -98,22 +96,22 @@
|
|
|
@dragleave.prevent="isDragging = false"
|
|
@dragleave.prevent="isDragging = false"
|
|
|
@drop.prevent="handleDrop"
|
|
@drop.prevent="handleDrop"
|
|
|
:class="[
|
|
:class="[
|
|
|
- 'relative border-2 border-dashed rounded-2xl p-12 text-center transition-all duration-300',
|
|
|
|
|
- isDragging ? 'border-primary bg-primary/10 shadow-glow' : 'border-border hover:border-primary/50 hover:bg-card/50'
|
|
|
|
|
|
|
+ 'relative border border-dashed rounded-[2rem] p-10 text-center transition-all duration-500',
|
|
|
|
|
+ isDragging ? 'border-primary bg-primary/5 shadow-lg' : 'border-black/10 hover:border-primary/30 hover:bg-secondary/20'
|
|
|
]"
|
|
]"
|
|
|
>
|
|
>
|
|
|
<input type="file" multiple accept=".stl,.obj,.3mf,.step" @change="handleFileSelect"
|
|
<input type="file" multiple accept=".stl,.obj,.3mf,.step" @change="handleFileSelect"
|
|
|
class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" />
|
|
class="absolute inset-0 w-full h-full opacity-0 cursor-pointer" />
|
|
|
- <div class="flex flex-col items-center gap-4">
|
|
|
|
|
- <div :class="['w-20 h-20 rounded-full flex items-center justify-center transition-all duration-300',
|
|
|
|
|
- isDragging ? 'bg-primary text-primary-foreground scale-110' : 'bg-primary/10 text-primary']">
|
|
|
|
|
- <Upload class="w-10 h-10" />
|
|
|
|
|
|
|
+ <div class="flex flex-col items-center gap-3">
|
|
|
|
|
+ <div :class="['w-14 h-14 rounded-2xl flex items-center justify-center transition-all duration-500',
|
|
|
|
|
+ isDragging ? 'bg-primary text-primary-foreground scale-110' : 'bg-primary/5 text-primary']">
|
|
|
|
|
+ <Upload class="w-7 h-7" />
|
|
|
</div>
|
|
</div>
|
|
|
<div>
|
|
<div>
|
|
|
- <p class="font-display text-xl font-semibold mb-2">
|
|
|
|
|
|
|
+ <p class="font-display text-lg font-extrabold tracking-tight mb-1">
|
|
|
{{ isDragging ? t("upload.dropzoneActive") : t("upload.dropzone") }}
|
|
{{ isDragging ? t("upload.dropzoneActive") : t("upload.dropzone") }}
|
|
|
</p>
|
|
</p>
|
|
|
- <p class="text-muted-foreground">
|
|
|
|
|
|
|
+ <p class="text-xs font-medium text-foreground/40">
|
|
|
or <span class="text-primary underline cursor-pointer">{{ t("upload.browse") }}</span>
|
|
or <span class="text-primary underline cursor-pointer">{{ t("upload.browse") }}</span>
|
|
|
</p>
|
|
</p>
|
|
|
</div>
|
|
</div>
|
|
@@ -126,7 +124,7 @@
|
|
|
<MapPin class="w-4 h-4 text-primary" />{{ t("upload.shippingAddress") }} *
|
|
<MapPin class="w-4 h-4 text-primary" />{{ t("upload.shippingAddress") }} *
|
|
|
</label>
|
|
</label>
|
|
|
<textarea v-model="address" rows="3" required :placeholder="t('upload.addressPlaceholder')"
|
|
<textarea v-model="address" rows="3" required :placeholder="t('upload.addressPlaceholder')"
|
|
|
- class="w-full bg-card/50 border border-border/50 rounded-xl px-4 py-3 focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary transition-all backdrop-blur-sm resize-none" />
|
|
|
|
|
|
|
+ class="w-full bg-secondary/50 border border-black/[0.03] rounded-2xl px-4 py-3 focus:outline-none focus:ring-4 focus:ring-primary/10 focus:border-primary transition-all text-sm font-medium resize-none" />
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<!-- File list -->
|
|
<!-- File list -->
|