first commit

This commit is contained in:
2026-05-12 12:41:49 +07:00
commit 604fef891f
8368 changed files with 1285228 additions and 0 deletions
+120
View File
@@ -0,0 +1,120 @@
<template>
<section class="container-page py-10">
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
<DashboardSidebar />
<div>
<div class="mb-8">
<h1 class="text-3xl font-extrabold">Barang Saya</h1>
<p class="mt-2 text-slate-600">Kelola barang yang kamu posting ke database.</p>
</div>
<div class="mb-8 grid gap-4 sm:grid-cols-3">
<div class="card p-5">
<p class="text-sm font-semibold text-slate-500">Total Barang</p>
<h2 class="mt-2 text-3xl font-extrabold">{{ userProducts.length }}</h2>
</div>
<div class="card p-5">
<p class="text-sm font-semibold text-slate-500">Tersedia</p>
<h2 class="mt-2 text-3xl font-extrabold">{{ availableCount }}</h2>
</div>
<div class="card p-5">
<p class="text-sm font-semibold text-slate-500">Terjual</p>
<h2 class="mt-2 text-3xl font-extrabold">{{ soldCount }}</h2>
</div>
</div>
<div class="mb-5 flex items-center justify-between">
<h2 class="text-xl font-extrabold">Management Barang</h2>
<RouterLink to="/jual" class="btn-primary !px-4 !py-2">+ Tambah Barang</RouterLink>
</div>
<p v-if="errorMessage" class="mb-5 rounded-xl bg-red-50 px-4 py-3 text-sm font-semibold text-red-700">
{{ errorMessage }}
</p>
<div v-if="loading" class="card p-10 text-center">
<h2 class="text-xl font-extrabold">Memuat barang...</h2>
</div>
<div v-else-if="userProducts.length" class="grid gap-4">
<div v-for="product in userProducts" :key="product.id" class="card grid gap-4 p-4 md:grid-cols-[140px_1fr_auto] md:items-center">
<img :src="getProductImageUrl(product.image)" :alt="product.title" class="aspect-[4/3] w-full rounded-xl object-cover md:w-36" />
<div>
<div class="flex flex-wrap items-center gap-2">
<span class="rounded-full bg-brand-50 px-3 py-1 text-xs font-bold text-brand-700">{{ product.category }}</span>
<span class="rounded-full bg-emerald-50 px-3 py-1 text-xs font-bold text-emerald-700">{{ product.status || "Tersedia" }}</span>
</div>
<h3 class="mt-3 font-extrabold">{{ product.title }}</h3>
<p class="mt-1 text-sm text-slate-500">{{ product.location }} {{ product.condition }}</p>
<p class="mt-2 font-extrabold text-brand-700">{{ formatRupiah(product.price) }}</p>
</div>
<div class="flex flex-wrap gap-2 md:flex-col">
<RouterLink :to="`/produk/${product.id}`" class="btn-secondary !px-3 !py-2">Detail</RouterLink>
<button class="btn-secondary !px-3 !py-2" @click="markSold(product.id)">Tandai Terjual</button>
<button class="rounded-xl bg-red-50 px-3 py-2 text-sm font-bold text-red-700 hover:bg-red-100" @click="deleteProduct(product.id)">Hapus</button>
</div>
</div>
</div>
<div v-else class="card p-10 text-center">
<h2 class="text-xl font-extrabold">Belum ada barang</h2>
<p class="mt-2 text-slate-600">Coba posting barang bekas teknologi pertamamu.</p>
<RouterLink to="/jual" class="btn-primary mt-5">Jual Barang</RouterLink>
</div>
</div>
</div>
</section>
</template>
<script setup>
import { computed, onMounted, ref } from "vue"
import DashboardSidebar from "../components/DashboardSidebar.vue"
import { deleteProductById, fetchMyProducts, formatRupiah, getProductImageUrl, updateProductStatus } from "../utils"
const userProducts = ref([])
const loading = ref(true)
const errorMessage = ref("")
const availableCount = computed(() => userProducts.value.filter((p) => (p.status || "Tersedia") === "Tersedia").length)
const soldCount = computed(() => userProducts.value.filter((p) => p.status === "Terjual").length)
async function loadProducts() {
loading.value = true
errorMessage.value = ""
try {
const data = await fetchMyProducts()
userProducts.value = data.products
} catch (error) {
errorMessage.value = error.message
} finally {
loading.value = false
}
}
async function markSold(id) {
try {
const data = await updateProductStatus(id, "Terjual")
userProducts.value = userProducts.value.map((product) => {
if (product.id === id) return data.product
return product
})
} catch (error) {
errorMessage.value = error.message
}
}
async function deleteProduct(id) {
if (!confirm("Yakin ingin menghapus barang ini?")) return
try {
await deleteProductById(id)
userProducts.value = userProducts.value.filter((product) => product.id !== id)
} catch (error) {
errorMessage.value = error.message
}
}
onMounted(loadProducts)
</script>
+91
View File
@@ -0,0 +1,91 @@
<template>
<section class="bg-gradient-to-br from-brand-50 via-white to-slate-100">
<div class="container-page grid min-h-[560px] items-center gap-10 py-16 lg:grid-cols-2">
<div>
<span class="rounded-full bg-brand-100 px-4 py-2 text-sm font-bold text-brand-700">
Marketplace Barang Bekas Teknologi
</span>
<h1 class="mt-6 text-4xl font-extrabold tracking-tight text-slate-950 sm:text-5xl lg:text-6xl">
Jual beli perangkat IT bekas jadi lebih mudah.
</h1>
<p class="mt-6 max-w-xl text-lg leading-8 text-slate-600">
Temukan laptop bekas, PC, monitor, keyboard, router, switch, access point, dan server bekas untuk kebutuhan belajar SIJA/TKJ.
</p>
<div class="mt-8 flex flex-wrap gap-3">
<RouterLink to="/jual" class="btn-primary">Jual Barang Sekarang</RouterLink>
<RouterLink to="/marketplace" class="btn-secondary">Lihat Marketplace</RouterLink>
</div>
</div>
<div class="relative">
<div class="absolute -inset-4 rounded-[2rem] bg-brand-100 blur-2xl"></div>
<img
class="relative aspect-[4/3] w-full rounded-[2rem] object-cover shadow-2xl"
src="https://images.unsplash.com/photo-1516321318423-f06f85e504b3?q=80&w=1200&auto=format&fit=crop"
alt="Tech marketplace"
/>
</div>
</div>
</section>
<section class="container-page py-14">
<div class="mb-8 flex items-end justify-between gap-4">
<div>
<h2 class="text-2xl font-extrabold">Kategori Populer</h2>
<p class="mt-2 text-slate-600">Fokus ke perangkat teknologi dan jaringan.</p>
</div>
<RouterLink to="/marketplace" class="text-sm font-bold text-brand-700">Lihat semua</RouterLink>
</div>
<div class="grid grid-cols-2 gap-4 sm:grid-cols-3 lg:grid-cols-6">
<div v-for="cat in categoryList" :key="cat" class="card p-5 text-center transition hover:-translate-y-1 hover:shadow-md">
<div class="mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-2xl bg-brand-50 text-brand-700">
<Cpu size="22" />
</div>
<p class="font-bold">{{ cat }}</p>
</div>
</div>
</section>
<section class="bg-white py-14">
<div class="container-page">
<div class="mb-8 flex items-end justify-between gap-4">
<div>
<h2 class="text-2xl font-extrabold">Produk Terbaru</h2>
<p class="mt-2 text-slate-600">Contoh barang bekas teknologi yang tersedia.</p>
</div>
</div>
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<ProductCard
v-for="product in latestProducts"
:key="product.id"
:product="product"
:saved="saved.includes(product.id)"
@toggle-save="toggleSave"
/>
</div>
</div>
</section>
</template>
<script setup>
import { computed, ref } from "vue"
import { Cpu } from "lucide-vue-next"
import ProductCard from "../components/ProductCard.vue"
import { products, categories } from "../data/products"
import { getSavedProducts, setSavedProducts } from "../utils"
const saved = ref(getSavedProducts())
const categoryList = categories.filter((c) => c !== "Semua").slice(0, 6)
const latestProducts = computed(() => products.slice(0, 4))
function toggleSave(id) {
if (saved.value.includes(id)) {
saved.value = saved.value.filter((item) => item !== id)
} else {
saved.value.push(id)
}
setSavedProducts(saved.value)
}
</script>
+60
View File
@@ -0,0 +1,60 @@
<template>
<section class="container-page grid min-h-[70vh] place-items-center py-10">
<div class="card w-full max-w-md p-6">
<h1 class="text-2xl font-extrabold">Login</h1>
<p class="mt-2 text-sm text-slate-600">Masuk untuk mengelola barang jualanmu.</p>
<form class="mt-6 grid gap-4" @submit.prevent="login">
<p v-if="errorMessage" class="rounded-xl bg-red-50 px-4 py-3 text-sm font-semibold text-red-700">
{{ errorMessage }}
</p>
<div>
<label class="label">Email</label>
<input v-model="email" type="email" class="input mt-2" placeholder="nama@email.com" required />
</div>
<div>
<label class="label">Password</label>
<input v-model="password" type="password" class="input mt-2" placeholder="••••••••" required />
</div>
<button class="btn-primary w-full" :disabled="loading">
{{ loading ? "Memproses..." : "Masuk" }}
</button>
</form>
<p class="mt-5 text-center text-sm text-slate-600">
Belum punya akun?
<RouterLink to="/register" class="font-bold text-brand-700">Register</RouterLink>
</p>
</div>
</section>
</template>
<script setup>
import { ref } from "vue"
import { useRouter } from "vue-router"
import { loginUser, setAuth } from "../utils"
const router = useRouter()
const email = ref("")
const password = ref("")
const loading = ref(false)
const errorMessage = ref("")
async function login() {
loading.value = true
errorMessage.value = ""
try {
const data = await loginUser({
email: email.value,
password: password.value
})
setAuth(data)
router.push("/dashboard")
} catch (error) {
errorMessage.value = error.message
} finally {
loading.value = false
}
}
</script>
+112
View File
@@ -0,0 +1,112 @@
<template>
<section class="container-page py-10">
<div class="mb-8">
<h1 class="text-3xl font-extrabold">Marketplace</h1>
<p class="mt-2 text-slate-600">Cari barang bekas teknologi sesuai kebutuhanmu.</p>
</div>
<div class="card mb-8 grid gap-4 p-4 lg:grid-cols-4">
<input v-model="search" class="input lg:col-span-2" placeholder="Cari laptop, router, server..." />
<select v-model="selectedCategory" class="input">
<option v-for="cat in categories" :key="cat" :value="cat">{{ cat }}</option>
</select>
<select v-model="selectedCondition" class="input">
<option value="Semua">Semua Kondisi</option>
<option value="Bekas Normal">Bekas Normal</option>
<option value="Bekas Mulus">Bekas Mulus</option>
<option value="Bekas Server Room">Bekas Server Room</option>
</select>
</div>
<div class="mb-5 flex items-center justify-between">
<p class="text-sm font-semibold text-slate-600">
{{ loading ? "Memuat produk..." : `Menampilkan ${filteredProducts.length} produk` }}
</p>
<RouterLink to="/jual" class="btn-primary !px-4 !py-2">+ Jual Barang</RouterLink>
</div>
<p v-if="errorMessage" class="mb-5 rounded-xl bg-amber-50 px-4 py-3 text-sm font-semibold text-amber-700">
{{ errorMessage }}
</p>
<div v-if="loading" class="card p-10 text-center">
<h2 class="text-xl font-extrabold">Memuat produk...</h2>
</div>
<div v-else-if="filteredProducts.length" class="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
<ProductCard
v-for="product in filteredProducts"
:key="product.id"
:product="product"
:saved="saved.includes(product.id)"
@toggle-save="toggleSave"
/>
</div>
<div v-else class="card p-10 text-center">
<h2 class="text-xl font-extrabold">Produk tidak ditemukan</h2>
<p class="mt-2 text-slate-600">Coba ganti keyword atau filter kategori.</p>
</div>
</section>
</template>
<script setup>
import { computed, onMounted, ref } from "vue"
import ProductCard from "../components/ProductCard.vue"
import { products as defaultProducts, categories } from "../data/products"
import { fetchProducts, getSavedProducts, setSavedProducts } from "../utils"
const search = ref("")
const selectedCategory = ref("Semua")
const selectedCondition = ref("Semua")
const saved = ref(getSavedProducts())
const products = ref([])
const loading = ref(true)
const errorMessage = ref("")
async function loadProducts() {
loading.value = true
errorMessage.value = ""
try {
const data = await fetchProducts()
products.value = data.products
} catch (error) {
products.value = defaultProducts
errorMessage.value = "Backend belum bisa dihubungi, jadi sementara menampilkan data contoh."
} finally {
loading.value = false
}
}
const filteredProducts = computed(() => {
return products.value.filter((product) => {
const keyword = search.value.toLowerCase()
const matchSearch =
product.title.toLowerCase().includes(keyword) ||
product.description.toLowerCase().includes(keyword) ||
product.category.toLowerCase().includes(keyword)
const matchCategory =
selectedCategory.value === "Semua" || product.category === selectedCategory.value
const matchCondition =
selectedCondition.value === "Semua" || product.condition === selectedCondition.value
return matchSearch && matchCategory && matchCondition
})
})
function toggleSave(id) {
if (saved.value.includes(id)) {
saved.value = saved.value.filter((item) => item !== id)
} else {
saved.value.push(id)
}
setSavedProducts(saved.value)
}
onMounted(loadProducts)
</script>
+105
View File
@@ -0,0 +1,105 @@
<template>
<section class="container-page py-10">
<RouterLink to="/marketplace" class="mb-6 inline-flex text-sm font-bold text-brand-700">
Kembali ke Marketplace
</RouterLink>
<div v-if="loading" class="card p-10 text-center">
<h1 class="text-2xl font-extrabold">Memuat produk...</h1>
</div>
<div v-else-if="product" class="grid gap-8 lg:grid-cols-2">
<div class="card overflow-hidden">
<img :src="getProductImageUrl(product.image)" :alt="product.title" class="aspect-[4/3] w-full object-cover" />
</div>
<div>
<span class="rounded-full bg-brand-50 px-3 py-1 text-sm font-bold text-brand-700">
{{ product.category }}
</span>
<h1 class="mt-4 text-3xl font-extrabold text-slate-950">{{ product.title }}</h1>
<p class="mt-3 text-3xl font-extrabold text-brand-700">{{ formatRupiah(product.price) }}</p>
<div class="mt-6 grid gap-3 sm:grid-cols-2">
<div class="card p-4">
<p class="text-xs font-bold uppercase text-slate-500">Kondisi</p>
<p class="mt-1 font-bold">{{ product.condition }}</p>
</div>
<div class="card p-4">
<p class="text-xs font-bold uppercase text-slate-500">Lokasi</p>
<p class="mt-1 font-bold">{{ product.location }}</p>
</div>
<div class="card p-4">
<p class="text-xs font-bold uppercase text-slate-500">Penjual</p>
<p class="mt-1 font-bold">{{ product.seller }}</p>
</div>
<div class="card p-4">
<p class="text-xs font-bold uppercase text-slate-500">WhatsApp</p>
<p class="mt-1 font-bold">{{ product.whatsapp }}</p>
</div>
</div>
<div class="mt-6">
<h2 class="text-lg font-extrabold">Deskripsi</h2>
<p class="mt-2 leading-7 text-slate-600">{{ product.description }}</p>
</div>
<div class="mt-8 flex flex-wrap gap-3">
<a :href="waLink" target="_blank" class="btn-primary">Hubungi via WhatsApp</a>
<button class="btn-secondary" @click="toggleSave">
{{ saved ? "Hapus dari Tersimpan" : "Simpan Barang" }}
</button>
</div>
</div>
</div>
<div v-else class="card p-10 text-center">
<h1 class="text-2xl font-extrabold">Produk tidak ditemukan</h1>
<RouterLink to="/marketplace" class="btn-primary mt-5">Lihat Marketplace</RouterLink>
</div>
</section>
</template>
<script setup>
import { computed, onMounted, ref } from "vue"
import { useRoute } from "vue-router"
import { products as defaultProducts } from "../data/products"
import { fetchProduct, formatRupiah, getProductImageUrl, getSavedProducts, setSavedProducts } from "../utils"
const route = useRoute()
const id = Number(route.params.id)
const product = ref(null)
const loading = ref(true)
const savedIds = ref(getSavedProducts())
const saved = computed(() => savedIds.value.includes(id))
const waLink = computed(() => {
if (!product.value) return "#"
const text = encodeURIComponent(`Halo, saya tertarik dengan ${product.value.title} di SecondTech Market.`)
return `https://wa.me/${product.value.whatsapp}?text=${text}`
})
function toggleSave() {
if (saved.value) {
savedIds.value = savedIds.value.filter((item) => item !== id)
} else {
savedIds.value.push(id)
}
setSavedProducts(savedIds.value)
}
async function loadProduct() {
loading.value = true
try {
const data = await fetchProduct(id)
product.value = data.product
} catch {
product.value = defaultProducts.find((item) => Number(item.id) === id) || null
} finally {
loading.value = false
}
}
onMounted(loadProduct)
</script>
+70
View File
@@ -0,0 +1,70 @@
<template>
<section class="container-page grid min-h-[70vh] place-items-center py-10">
<div class="card w-full max-w-md p-6">
<h1 class="text-2xl font-extrabold">Register</h1>
<p class="mt-2 text-sm text-slate-600">Buat akun untuk mulai menjual barang.</p>
<form class="mt-6 grid gap-4" @submit.prevent="register">
<p v-if="errorMessage" class="rounded-xl bg-red-50 px-4 py-3 text-sm font-semibold text-red-700">
{{ errorMessage }}
</p>
<div>
<label class="label">Nama Lengkap</label>
<input v-model="form.name" class="input mt-2" placeholder="Nama kamu" required />
</div>
<div>
<label class="label">Email</label>
<input v-model="form.email" type="email" class="input mt-2" placeholder="nama@email.com" required />
</div>
<div>
<label class="label">Nomor WhatsApp</label>
<input v-model="form.whatsapp" class="input mt-2" placeholder="6281234567890" required />
</div>
<div>
<label class="label">Password</label>
<input v-model="form.password" type="password" class="input mt-2" placeholder="••••••••" required />
</div>
<button class="btn-primary w-full" :disabled="loading">
{{ loading ? "Memproses..." : "Daftar" }}
</button>
</form>
<p class="mt-5 text-center text-sm text-slate-600">
Sudah punya akun?
<RouterLink to="/login" class="font-bold text-brand-700">Login</RouterLink>
</p>
</div>
</section>
</template>
<script setup>
import { reactive, ref } from "vue"
import { useRouter } from "vue-router"
import { registerUser, setAuth } from "../utils"
const router = useRouter()
const form = reactive({
name: "",
email: "",
whatsapp: "",
password: ""
})
const loading = ref(false)
const errorMessage = ref("")
async function register() {
loading.value = true
errorMessage.value = ""
try {
const data = await registerUser(form)
setAuth(data)
router.push("/dashboard")
} catch (error) {
errorMessage.value = error.message
} finally {
loading.value = false
}
}
</script>
+69
View File
@@ -0,0 +1,69 @@
<template>
<section class="container-page py-10">
<div class="grid gap-8 lg:grid-cols-[280px_1fr]">
<DashboardSidebar />
<div>
<div class="mb-8">
<h1 class="text-3xl font-extrabold">Barang Tersimpan</h1>
<p class="mt-2 text-slate-600">Barang yang kamu simpan dari marketplace.</p>
</div>
<div v-if="loading" class="card p-10 text-center">
<h2 class="text-xl font-extrabold">Memuat barang tersimpan...</h2>
</div>
<div v-else-if="savedProducts.length" class="grid gap-6 sm:grid-cols-2 lg:grid-cols-3">
<ProductCard
v-for="product in savedProducts"
:key="product.id"
:product="product"
:saved="true"
@toggle-save="toggleSave"
/>
</div>
<div v-else class="card p-10 text-center">
<h2 class="text-xl font-extrabold">Belum ada barang tersimpan</h2>
<p class="mt-2 text-slate-600">Simpan barang yang menarik dari marketplace.</p>
<RouterLink to="/marketplace" class="btn-primary mt-5">Lihat Marketplace</RouterLink>
</div>
</div>
</div>
</section>
</template>
<script setup>
import { computed, onMounted, ref } from "vue"
import DashboardSidebar from "../components/DashboardSidebar.vue"
import ProductCard from "../components/ProductCard.vue"
import { products as defaultProducts } from "../data/products"
import { fetchProducts, getSavedProducts, setSavedProducts } from "../utils"
const saved = ref(getSavedProducts())
const products = ref([])
const loading = ref(true)
const savedProducts = computed(() => {
return products.value.filter((product) => saved.value.includes(product.id))
})
function toggleSave(id) {
saved.value = saved.value.filter((item) => item !== id)
setSavedProducts(saved.value)
}
async function loadProducts() {
loading.value = true
try {
const data = await fetchProducts()
products.value = data.products
} catch {
products.value = defaultProducts
} finally {
loading.value = false
}
}
onMounted(loadProducts)
</script>
+149
View File
@@ -0,0 +1,149 @@
<template>
<section class="container-page py-10">
<div class="mb-8">
<h1 class="text-3xl font-extrabold">Jual Barang</h1>
<p class="mt-2 text-slate-600">Posting barang ke marketplace dengan foto asli dari perangkatmu.</p>
</div>
<div class="grid gap-8 lg:grid-cols-[1fr_360px]">
<form class="card grid gap-5 p-6" @submit.prevent="submitProduct">
<p v-if="errorMessage" class="rounded-xl bg-red-50 px-4 py-3 text-sm font-semibold text-red-700">
{{ errorMessage }}
</p>
<div>
<label class="label">Nama Barang</label>
<input v-model="form.title" class="input mt-2" placeholder="Contoh: Laptop ThinkPad T480" required />
</div>
<div class="grid gap-5 sm:grid-cols-2">
<div>
<label class="label">Kategori</label>
<select v-model="form.category" class="input mt-2">
<option v-for="cat in realCategories" :key="cat" :value="cat">{{ cat }}</option>
</select>
</div>
<div>
<label class="label">Harga</label>
<input v-model.number="form.price" type="number" class="input mt-2" placeholder="2500000" required />
</div>
</div>
<div class="grid gap-5 sm:grid-cols-2">
<div>
<label class="label">Kondisi</label>
<select v-model="form.condition" class="input mt-2">
<option>Bekas Normal</option>
<option>Bekas Mulus</option>
<option>Bekas Minus</option>
<option>Bekas Server Room</option>
</select>
</div>
<div>
<label class="label">Lokasi</label>
<input v-model="form.location" class="input mt-2" placeholder="Yogyakarta" required />
</div>
</div>
<div class="grid gap-5 sm:grid-cols-2">
<div>
<label class="label">Nama Penjual</label>
<input v-model="form.seller" class="input mt-2" placeholder="Nama kamu" required />
</div>
<div>
<label class="label">Nomor WhatsApp</label>
<input v-model="form.whatsapp" class="input mt-2" placeholder="6281234567890" required />
</div>
</div>
<div>
<label class="label">Foto Barang</label>
<input type="file" accept="image/*" class="input mt-2" required @change="handleImageChange" />
<img v-if="previewUrl" :src="previewUrl" alt="Preview foto barang" class="mt-4 aspect-[4/3] w-full max-w-sm rounded-xl object-cover" />
<p class="mt-2 text-xs text-slate-500">Upload JPG, PNG, atau WebP. Maksimal 3MB.</p>
</div>
<div>
<label class="label">Deskripsi</label>
<textarea v-model="form.description" class="input mt-2 min-h-32" placeholder="Jelaskan kondisi barang..." required></textarea>
</div>
<button class="btn-primary w-full" :disabled="loading">
{{ loading ? "Memposting..." : "Posting Barang" }}
</button>
</form>
<div class="card h-fit p-6">
<h2 class="text-lg font-extrabold">Tips Isi Produk</h2>
<ul class="mt-4 grid gap-3 text-sm leading-6 text-slate-600">
<li> Pakai judul yang jelas, misal MikroTik RB941 Bekas Normal.</li>
<li> Jelaskan minus barang kalau ada.</li>
<li> Pakai nomor WhatsApp aktif.</li>
<li> Pakai foto yang terang dan jelas.</li>
</ul>
</div>
</div>
</section>
</template>
<script setup>
import { reactive, computed, ref } from "vue"
import { useRouter } from "vue-router"
import { categories } from "../data/products"
import { createProduct, getCurrentUser } from "../utils"
const router = useRouter()
const realCategories = computed(() => categories.filter((cat) => cat !== "Semua"))
const form = reactive({
title: "",
category: "Laptop",
price: "",
condition: "Bekas Normal",
location: "",
seller: "",
whatsapp: "",
description: ""
})
const selectedImage = ref(null)
const previewUrl = ref("")
const loading = ref(false)
const errorMessage = ref("")
const currentUser = getCurrentUser()
if (currentUser) {
form.seller = currentUser.name || ""
form.whatsapp = currentUser.whatsapp || ""
}
function handleImageChange(event) {
const file = event.target.files?.[0]
selectedImage.value = file || null
previewUrl.value = file ? URL.createObjectURL(file) : ""
}
async function submitProduct() {
if (!selectedImage.value) {
errorMessage.value = "Foto barang wajib diupload."
return
}
loading.value = true
errorMessage.value = ""
const payload = new FormData()
Object.entries(form).forEach(([key, value]) => {
payload.append(key, value)
})
payload.append("image", selectedImage.value)
try {
await createProduct(payload)
router.push("/dashboard")
} catch (error) {
errorMessage.value = error.message
} finally {
loading.value = false
}
}
</script>