add testimoni

This commit is contained in:
Muzakki Parsaoran Siregar 2025-09-11 14:26:39 +07:00
parent 74840a4fcb
commit d550571217
8 changed files with 248 additions and 49 deletions

View File

@ -12,10 +12,11 @@ class ReviewController extends Controller
public function index()
{
$reviews = Review::all();
return view('admin.reviews.index', compact('reviews'));
return response()->json($reviews, 200);
}
// Simpan ulasan baru
public function store(Request $request)
{

View File

@ -7,7 +7,7 @@
<!-- Layout gambar + teks -->
<div class="about-layout">
<div class="about-image">
<img src="/logo1.png" alt="Tentang Kami - Undangan Digital" />
<img src="/rectangle.png" alt="Tentang Kami - Undangan Digital" />
</div>
<div class="about-text">
<p>

View File

@ -2,7 +2,7 @@
<header class="main-header">
<nav class="container">
<div class="logo">
<NuxtLink to="/" class="logo-link"> <img src="/ABBAUF.png" alt="Abbauf Tech Logo" class="logo-icon">
<NuxtLink to="/" class="logo-link"> <img src="/abbauflogo.png" alt="Abbauf Tech Logo" class="logo-icon">
<span>ABBAUF TECH</span>
</NuxtLink>
</div>
@ -58,7 +58,7 @@ const navLinks = ref([
}
.logo-icon {
width: 40px;
width: 50px;
margin-right: 10px;
}

View File

@ -34,7 +34,7 @@
</div>
<div class="w-full lg:w-1/2">
<img src="/logo2.png" alt="Tampilan Undangan Digital di Ponsel" class="mx-auto max-w-full">
<img src="/iphone.png" alt="Tampilan Undangan Digital di Ponsel" class="mx-auto max-w-full">
<div class="mt-6 text-center italic text-gray-500">
<p>"Abadikan momen indahmu dengan undangan digital yang elegan."</p>
<p>"Satu aplikasi untuk semua momen spesialmu."</p>

View File

@ -4,66 +4,264 @@
<h2 class="text-4xl font-extrabold text-gray-800 mb-2">
Apa Kata Mereka?
</h2>
<p class="text-lg text-gray-600 mb-16">
<p class="text-lg text-gray-600 mb-10">
Kisah sukses dari para pengguna yang telah mempercayakan momen spesialnya kepada kami.
</p>
<div class="grid grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3">
<!-- CSS Marquee Scroll -->
<div class="marquee-container mb-10">
<div class="marquee-content" :style="{ '--total-cards': testimonials?.length || 0 }">
<!-- Render original cards -->
<div
v-for="testimonial in testimonials"
:key="testimonial.id"
class="flex flex-col rounded-xl bg-white p-8 text-left shadow-lg transition-transform duration-300 hover:-translate-y-2 hover:shadow-2xl"
:key="`original-${testimonial.id}`"
class="testimonial-card"
@click="previewModal = testimonial"
>
<!-- Rating -->
<div class="mb-4 flex items-center">
<svg v-for="n in 5" :key="n" class="h-5 w-5" :class="n <= testimonial.rating ? 'text-yellow-400' : 'text-gray-300'" fill="currentColor" viewBox="0 0 20 20"><path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z"></path></svg>
<svg
v-for="n in 5"
:key="n"
class="h-5 w-5"
:class="n <= Number(testimonial.rating) ? 'text-yellow-400' : 'text-gray-300'"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07
3.292a1 1 0 00.95.69h3.462c.969 0 1.371
1.24.588 1.81l-2.8 2.034a1 1 0
00-.364 1.118l1.07 3.292c.3.921-.755
1.688-1.54 1.118l-2.8-2.034a1 1
0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1
1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1
1 0 00.951-.69l1.07-3.292z"
/>
</svg>
</div>
<p class="mb-6 flex-grow text-gray-600 italic">"{{ testimonial.text }}"</p>
<!-- Pesan -->
<p class="mb-6 flex-grow text-gray-600 italic line-clamp-3 min-h-[72px] break-words">
"{{ testimonial.message }}"
</p>
<div class="flex items-center">
<img class="h-12 w-12 rounded-full object-cover" :src="testimonial.avatar" :alt="testimonial.name">
<div class="ml-4">
<!-- User Info -->
<div>
<h4 class="font-bold text-gray-800">{{ testimonial.name }}</h4>
<p class="text-sm text-gray-500">{{ testimonial.role }}</p>
<p class="text-sm text-gray-500">{{ testimonial.city }}</p>
</div>
</div>
<!-- Render clone untuk seamless loop -->
<div
v-for="testimonial in testimonials"
:key="`clone-${testimonial.id}`"
class="testimonial-card"
@click="previewModal = testimonial"
>
<!-- Rating -->
<div class="mb-4 flex items-center">
<svg
v-for="n in 5"
:key="n"
class="h-5 w-5"
:class="n <= Number(testimonial.rating) ? 'text-yellow-400' : 'text-gray-300'"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07
3.292a1 1 0 00.95.69h3.462c.969 0 1.371
1.24.588 1.81l-2.8 2.034a1 1 0
00-.364 1.118l1.07 3.292c.3.921-.755
1.688-1.54 1.118l-2.8-2.034a1 1
0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1
1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1
1 0 00.951-.69l1.07-3.292z"
/>
</svg>
</div>
<!-- Pesan -->
<p class="mb-6 flex-grow text-gray-600 italic line-clamp-3 min-h-[72px] break-words">
"{{ testimonial.message }}"
</p>
<!-- User Info -->
<div>
<h4 class="font-bold text-gray-800">{{ testimonial.name }}</h4>
<p class="text-sm text-gray-500">{{ testimonial.city }}</p>
</div>
</div>
</div>
</div>
<!-- Tombol Berikan Ulasan -->
<button
@click="openModal = true"
class="px-6 py-3 rounded-lg bg-blue-500 text-white font-semibold shadow hover:bg-blue-700 transition"
>
Berikan Ulasan
</button>
</div>
<!-- Modal Form -->
<div
v-if="openModal"
class="fixed inset-0 z-50 flex items-center justify-center bg-gray-800/30"
>
<div class="bg-white rounded-lg shadow-xl w-full max-w-lg p-6 relative">
<button
@click="openModal = false"
class="absolute top-3 right-3 text-gray-500 hover:text-gray-800"
>
</button>
<h3 class="text-xl font-bold mb-4 text-gray-800">Tulis Ulasan</h3>
<form @submit.prevent="submitReview">
<div class="mb-4 text-left">
<label class="block text-sm font-medium mb-1">Nama</label>
<input v-model="form.name" type="text" class="w-full rounded border px-3 py-2" required />
</div>
<div class="mb-4 text-left">
<label class="block text-sm font-medium mb-1">Kota</label>
<input v-model="form.city" type="text" class="w-full rounded border px-3 py-2" required />
</div>
<div class="mb-4 text-left">
<label class="block text-sm font-medium mb-1">Rating</label>
<select v-model="form.rating" class="w-full rounded border px-3 py-2" required>
<option value="">Pilih rating</option>
<option v-for="n in 5" :key="n" :value="n">{{ n }} </option>
</select>
</div>
<div class="mb-4 text-left">
<label class="block text-sm font-medium mb-1">Pesan</label>
<textarea v-model="form.message" class="w-full rounded border px-3 py-2" rows="3" required />
</div>
<button
type="submit"
class="w-full bg-blue-600 text-white py-2 rounded-lg font-semibold hover:bg-blue-700 transition"
>
Kirim Ulasan
</button>
</form>
</div>
</div>
<!-- Modal Preview -->
<div
v-if="previewModal"
class="fixed inset-0 z-50 flex items-center justify-center bg-gray-800/30"
>
<div class="bg-white rounded-lg shadow-xl w-full max-w-lg p-6 relative">
<button
@click="previewModal = null"
class="absolute top-3 right-3 text-gray-500 hover:text-gray-800"
>
</button>
<h3 class="text-xl font-bold mb-4 text-gray-800">Ulasan Lengkap</h3>
<p class="text-gray-600 italic mb-4 whitespace-pre-line break-words">
"{{ previewModal.message }}"
</p>
<h4 class="font-bold text-gray-800">{{ previewModal.name }}</h4>
<p class="text-sm text-gray-500">{{ previewModal.city }}</p>
</div>
</div>
</section>
</template>
<script setup>
import { ref } from 'vue';
import { ref } from 'vue'
const testimonials = ref([
{
id: 1,
name: 'Rizky & Anisa',
role: 'Pengantin Baru',
avatar: 'https://i.pravatar.cc/100?u=rizky',
rating: 5,
text: 'Desainnya elegan dan modern! Proses pembuatannya juga cepat banget. Semua tamu memuji undangannya. Terima kasih Abbauf Tech!'
},
{
id: 2,
name: 'Budi Santoso',
role: 'Event Organizer',
avatar: 'https://i.pravatar.cc/100?u=budi',
rating: 5,
text: 'Sebagai EO, kami butuh platform yang efisien dan hasilnya premium. Abbauf Tech menjawab semua kebutuhan itu. Klien kami sangat puas.'
},
{
id: 3,
name: 'Citra Lestari',
role: 'Ulang Tahun Anak',
avatar: 'https://i.pravatar.cc/100?u=citra',
rating: 4,
text: 'Fitur RSVP dan pengingat sangat membantu. Tema-tema ulang tahunnya juga lucu dan bisa dikustomisasi. Sangat direkomendasikan!'
},
]);
const { data: testimonials, refresh } = await useFetch('http://localhost:8000/api/reviews')
const openModal = ref(false)
const previewModal = ref(null)
const form = ref({
name: '',
city: '',
rating: '',
message: ''
})
// Submit review
const submitReview = async () => {
try {
await $fetch('http://localhost:8000/api/reviews', {
method: 'POST',
body: form.value
})
form.value = { name: '', city: '', rating: '', message: '' }
openModal.value = false
await refresh()
} catch (err) {
console.error('Gagal simpan ulasan:', err)
}
}
</script>
<style scoped>
/* Kosong, semua diatur oleh Tailwind */
/* Marquee Container */
.marquee-container {
overflow: hidden;
padding: 1rem 0;
}
/* Marquee Content - Contains all cards */
.marquee-content {
display: flex;
gap: 1.5rem;
animation: marquee calc(var(--total-cards) * 8s) linear infinite;
width: max-content;
}
/* Individual testimonial card */
.testimonial-card {
flex-shrink: 0;
width: 24rem; /* 384px = w-96 */
border-radius: 0.75rem;
background: white;
padding: 2rem;
text-align: left;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
transition: transform 300ms, box-shadow 300ms;
cursor: pointer;
}
.testimonial-card:hover {
transform: translateY(-0.5rem);
box-shadow: 0 25px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
/* Marquee animation */
@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
/* Pause animation on hover */
.marquee-container:hover .marquee-content {
animation-play-state: paused;
}
/* Responsive adjustments */
@media (max-width: 640px) {
.testimonial-card {
width: 20rem; /* Smaller on mobile */
}
}
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 228 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB