add testimoni
This commit is contained in:
parent
74840a4fcb
commit
d550571217
@ -9,13 +9,14 @@ use App\Models\Review;
|
||||
class ReviewController extends Controller
|
||||
{
|
||||
// Ambil semua ulasan
|
||||
public function index()
|
||||
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)
|
||||
{
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
<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"
|
||||
>
|
||||
<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>
|
||||
<!-- 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="`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 <= 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>
|
||||
|
||||
<p class="mb-6 flex-grow text-gray-600 italic">"{{ testimonial.text }}"</p>
|
||||
<!-- 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>
|
||||
|
||||
<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">
|
||||
<!-- 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.role }}</p>
|
||||
<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>
|
BIN
proyek-frontend/public/Rectangle.png
Normal file
BIN
proyek-frontend/public/Rectangle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 228 KiB |
BIN
proyek-frontend/public/abbauflogo.png
Normal file
BIN
proyek-frontend/public/abbauflogo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
proyek-frontend/public/iphone.png
Normal file
BIN
proyek-frontend/public/iphone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 233 KiB |
Loading…
Reference in New Issue
Block a user